From a412116b9c15f4b6e187f0c079473680adbfbedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pons?= Date: Tue, 27 Jan 2026 17:13:40 +0100 Subject: [PATCH] Rename RedrawableHTML, remove File API rate limite and fix pull job transaction. --- app/pages/admin/jobs.vue | 4 +++ db.sqlite | Bin 712704 -> 733184 bytes nuxt.config.ts | 5 +++ server/tasks/pull.ts | 8 ++--- shared/campaign.ts | 6 ++-- shared/canvas.ts | 12 +++---- shared/character.ts | 71 +++++++++++++++++++++------------------ shared/components.ts | 61 +++++++++++++++++++++------------ shared/content.ts | 41 +++++++++++----------- shared/dom.ts | 6 ++-- shared/editor.ts | 4 +-- shared/feature.ts | 18 +++++----- shared/floating.ts | 16 ++++----- shared/tree.ts | 18 +++++----- 14 files changed, 153 insertions(+), 117 deletions(-) diff --git a/app/pages/admin/jobs.vue b/app/pages/admin/jobs.vue index dd8b423..0332f3b 100644 --- a/app/pages/admin/jobs.vue +++ b/app/pages/admin/jobs.vue @@ -16,6 +16,7 @@ const schemaList: Record | null> = { import { z } from 'zod/v4'; import { Icon } from '@iconify/vue'; import { Toaster } from '~~/shared/components'; +import { Content } from '~~/shared/content'; definePageMeta({ rights: ['admin'], @@ -51,6 +52,9 @@ async function fetch() error.value = null; success.value = true; + if(job.value === 'pull') + await Content.pull(true); + Toaster.add({ duration: 10000, content: data.value ?? 'Job executé avec succès', type: 'success', timer: true, }); } catch(e) diff --git a/db.sqlite b/db.sqlite index d3cd7aced3f45c77cfd5e1f50f2fdf529bb8f73d..94a82ff83c46844ce6f46eba32a53a4cc50b8a55 100644 GIT binary patch delta 82926 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
4BfR%a0etm$4QhOC4!cZES&k| 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 delta 70282 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 { - db.delete(projectFilesTable).run(); - db.insert(projectFilesTable).values(files.map(e => {const { content, ...rest } = e; return rest; })).run(); - db.delete(projectContentTable).run(); - db.insert(projectContentTable).values(files.map(e => ({ content: e.content ? Buffer.from(e.content as string) : null, id: e.id }))).run(); + tx.delete(projectFilesTable).run(); + tx.insert(projectFilesTable).values(files.map(e => {const { content, ...rest } = e; return rest; })).run(); + tx.delete(projectContentTable).run(); + tx.insert(projectContentTable).values(files.map(e => ({ content: e.content ? Buffer.from(e.content as string) : null, id: e.id }))).run(); }); return { result: true }; diff --git a/shared/campaign.ts b/shared/campaign.ts index 0a4c52f..0ff70a6 100644 --- a/shared/campaign.ts +++ b/shared/campaign.ts @@ -2,7 +2,7 @@ import { z } from "zod/v4"; import type { User } from "~/types/auth"; import characterConfig from '#shared/character-config.json'; import type { Campaign } from "~/types/campaign"; -import { div, dom, icon, span, text, type RedrawableHTML } from "#shared/dom"; +import { div, dom, icon, span, text, type HTMLElement } from "#shared/dom"; import { button, foldable, loading, numberpicker, tabgroup, Toaster } from "#shared/components"; import { CharacterCompiler, colorByRarity, stateFactory, subnameFactory } from "#shared/character"; import { modal, tooltip } from "#shared/floating"; @@ -27,7 +27,7 @@ export const CampaignValidation = z.object({ class CharacterPrinter { compiler?: CharacterCompiler; - container: RedrawableHTML; + container: HTMLElement; name: string; id: number; constructor(character: number, name: string) @@ -64,7 +64,7 @@ export class CampaignSheet private campaign?: Campaign; private characters!: Array; - container: RedrawableHTML = div('flex flex-col flex-1 h-full w-full items-center justify-start gap-6'); + container: HTMLElement = div('flex flex-col flex-1 h-full w-full items-center justify-start gap-6'); ws?: Socket; constructor(id: string, user: ComputedRef) diff --git a/shared/canvas.ts b/shared/canvas.ts index e32d084..f20d61c 100644 --- a/shared/canvas.ts +++ b/shared/canvas.ts @@ -1,6 +1,6 @@ import type { CanvasContent, CanvasEdge, CanvasNode } from "~/types/canvas"; import { clamp, lerp } from "#shared/general"; -import { dom, icon, svg, type RedrawableHTML } from "#shared/dom"; +import { dom, icon, svg, type HTMLElement } from "#shared/dom"; import render from "#shared/markdown"; import { tooltip } from "#shared/floating"; import { History } from "#shared/history"; @@ -200,7 +200,7 @@ export class Node extends EventTarget { properties: CanvasNode; - nodeDom?: RedrawableHTML; + nodeDom?: HTMLElement; constructor(properties: CanvasNode) { @@ -334,7 +334,7 @@ export class Edge extends EventTarget { properties: CanvasEdge; - edgeDom?: RedrawableHTML; + edgeDom?: HTMLElement; protected from: Node; protected to: Node; protected path: Path; @@ -388,7 +388,7 @@ export class EdgeEditable extends Edge private editing: boolean = false; private pathDom?: SVGPathElement; - private inputDom?: RedrawableHTML; + private inputDom?: HTMLElement; constructor(properties: CanvasEdge, from: NodeEditable, to: NodeEditable) { super(properties, from, to); @@ -441,8 +441,8 @@ export class Canvas protected tweener: Tweener = new Tweener(); private debouncedTimeout: Timer = setTimeout(() => {}, 0); - protected transform!: RedrawableHTML; - container!: RedrawableHTML; + protected transform!: HTMLElement; + container!: HTMLElement; protected firstX = 0; protected firstY = 0; diff --git a/shared/character.ts b/shared/character.ts index d5ff240..50ccddd 100644 --- a/shared/character.ts +++ b/shared/character.ts @@ -3,7 +3,7 @@ import { z } from "zod/v4"; import characterConfig from '#shared/character-config.json'; import proses, { preview } from "#shared/proses"; import { button, checkbox, floater, foldable, input, loading, multiselect, numberpicker, select, tabgroup, Toaster, toggle } from "#shared/components"; -import { div, dom, icon, span, text, type RedrawableHTML } from "#shared/dom"; +import { div, dom, icon, span, text, type HTMLElement } from "#shared/dom"; import { followermenu, fullblocker, tooltip } from "#shared/floating"; import { clamp } from "#shared/general"; import markdown from "#shared/markdown"; @@ -701,16 +701,16 @@ function setProperty(root: any, path: string, value: T | ((old: T) => T), for } export class CharacterBuilder extends CharacterCompiler { - private _container: RedrawableHTML; - private _content?: RedrawableHTML; - private _stepsHeader: RedrawableHTML[] = []; + private _container: HTMLElement; + private _content?: HTMLElement; + private _stepsHeader: HTMLElement[] = []; private _steps: Array = []; private _stepContent: Array = []; private _currentStep: number = 0; private _helperText!: Text; private id?: string; - constructor(container: RedrawableHTML, id?: string) + constructor(container: HTMLElement, id?: string) { super(Object.assign({}, defaultCharacter)); this.id = id; @@ -944,7 +944,7 @@ export class CharacterBuilder extends CharacterCompiler this.add(config.training[stat][level][choice]); } } - handleChoice(element: RedrawableHTML, feature: string) + handleChoice(element: HTMLElement, feature: string) { const choices = config.features[feature]!.effect.filter(e => e.category === 'choice'); if(choices.length === 0) @@ -981,8 +981,8 @@ type BuilderTabConstructor = { class PeoplePicker extends BuilderTab { private _nameInput: HTMLInputElement; - private _visibilityInput: RedrawableHTML; - private _options: RedrawableHTML[]; + private _visibilityInput: HTMLElement; + private _options: HTMLElement[]; static override header = 'Peuple'; static override description = 'Choisissez un peuple afin de définir la progression de votre personnage au fil des niveaux.'; @@ -1043,7 +1043,7 @@ class LevelPicker extends BuilderTab private _levelInput: HTMLInputElement; private _pointsInput: HTMLInputElement; - private _options: RedrawableHTML[][]; + private _options: HTMLElement[][]; static override header = 'Niveaux'; static override description = 'Déterminez la progression de votre personnage en choisissant une option par niveau disponible.'; @@ -1112,8 +1112,8 @@ class LevelPicker extends BuilderTab e[0]?.classList.toggle("opacity-30", ((i + 1) as Level) > this._builder.character.level); e[1]?.classList.toggle("opacity-30", ((i + 1) as Level) > this._builder.character.level); e[1]?.childNodes.forEach((option, j) => { - 'hover:border-light-70 dark:hover:border-dark-70 cursor-pointer'.split(" ").forEach(_e => (option as RedrawableHTML).classList.toggle(_e, ((i + 1) as Level) <= this._builder.character.level)); - '!border-accent-blue bg-accent-blue bg-opacity-20'.split(" ").forEach(_e => (option as RedrawableHTML).classList.toggle(_e, this._builder.character.leveling[((i + 1) as Level)] === j)); + 'hover:border-light-70 dark:hover:border-dark-70 cursor-pointer'.split(" ").forEach(_e => (option as HTMLElement).classList.toggle(_e, ((i + 1) as Level) <= this._builder.character.level)); + '!border-accent-blue bg-accent-blue bg-opacity-20'.split(" ").forEach(_e => (option as HTMLElement).classList.toggle(_e, this._builder.character.leveling[((i + 1) as Level)] === j)); }); }); } @@ -1125,11 +1125,11 @@ class LevelPicker extends BuilderTab class TrainingPicker extends BuilderTab { private _pointsInput: HTMLInputElement; - private _options: Record; + private _options: Record; private _tab: number = 0; - private _statIndicator: RedrawableHTML; - private _statContainer: RedrawableHTML; + private _statIndicator: HTMLElement; + private _statContainer: HTMLElement; static override header = 'Entrainement'; static override description = 'Spécialisez votre personnage en attribuant vos points d\'entrainement parmi les 7 branches disponibles.\nChaque paliers de 3 points augmentent votre modifieur.'; @@ -1156,7 +1156,7 @@ class TrainingPicker extends BuilderTab this._pointsInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-none ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }}); - this._options = MAIN_STATS.reduce((p, v) => { p[v] = statRenderBlock(v); return p; }, {} as Record); + this._options = MAIN_STATS.reduce((p, v) => { p[v] = statRenderBlock(v); return p; }, {} as Record); this._statIndicator = dom('span', { class: 'rounded-full w-3 h-3 bg-accent-blue absolute transition-[left] after:content-[attr(data-text)] after:absolute after:-translate-x-1/2 after:top-4 after:p-px after:bg-light-0 dark:after:bg-dark-0 after:text-center' }); this._statContainer = div('relative select-none transition-[left] flex flex-1 flex-row max-w-full', Object.values(this._options).map(e => div('flex flex-shrink-0 flex-col gap-4 relative w-full overflow-y-auto px-8', e.flatMap(_e => [..._e])))); @@ -1207,7 +1207,7 @@ class TrainingPicker extends BuilderTab e[0]?.classList.toggle("opacity-30", (i as TrainingLevel) > max); e[1]?.classList.toggle("opacity-30", (i as TrainingLevel) > max); e[1]?.childNodes.forEach((option, j) => { - '!border-accent-blue bg-accent-blue bg-opacity-20'.split(" ").forEach(_e => (option as RedrawableHTML).classList.toggle(_e, i == 0 || (this._builder.character.training[stat as MainStat][i as TrainingLevel] === j))); + '!border-accent-blue bg-accent-blue bg-opacity-20'.split(" ").forEach(_e => (option as HTMLElement).classList.toggle(_e, i == 0 || (this._builder.character.training[stat as MainStat][i as TrainingLevel] === j))); }) }) }); @@ -1223,9 +1223,9 @@ class TrainingPicker extends BuilderTab class AbilityPicker extends BuilderTab { private _pointsInput: HTMLInputElement; - private _options: RedrawableHTML[]; + private _options: HTMLElement[]; - private _maxs: RedrawableHTML[] = []; + private _maxs: HTMLElement[] = []; static override header = 'Compétences'; static override description = 'Diversifiez vos possibilités en affectant vos points dans les différentes compétences disponibles.'; @@ -1266,7 +1266,7 @@ class AbilityPicker extends BuilderTab ABILITIES.forEach((e, i) => { const max = (values[`bonus/abilities/${e}`] ?? 0); - const load = this._options[i]?.lastElementChild as RedrawableHTML | undefined; + const load = this._options[i]?.lastElementChild as HTMLElement | undefined; const valid = (compiled.abilities[e] ?? 0) <= max; if(load) { @@ -1293,7 +1293,7 @@ class AspectPicker extends BuilderTab private _filter: boolean = true; - private _options: RedrawableHTML[]; + private _options: HTMLElement[]; static override header = 'Aspect'; static override description = 'Déterminez l\'Aspect qui vous corresponds et benéficiez de puissants bonus.'; @@ -1370,7 +1370,7 @@ class AspectPicker extends BuilderTab this._mentalInput.value = mental.toString(); this._personalityInput.value = personality.toString(); - (this._content[1] as RedrawableHTML).replaceChildren(...this._options.filter(e => { + (this._content[1] as HTMLElement).replaceChildren(...this._options.filter(e => { const id = e.getAttribute('data-aspect')!; const aspect = config.aspects[id]!; @@ -1498,8 +1498,8 @@ export class CharacterSheet { private user: ComputedRef; private character?: CharacterCompiler; - container: RedrawableHTML = div('flex flex-1 h-full w-full items-start justify-center'); - private tabs?: RedrawableHTML; + container: HTMLElement = div('flex flex-1 h-full w-full items-start justify-center'); + private tabs?: HTMLElement; private tab: string = 'actions'; ws?: Socket; @@ -1582,7 +1582,7 @@ export class CharacterSheet publicNotes.content = this.character!.character.notes!.public!; privateNotes.content = this.character!.character.notes!.private!; - const validateProperty = (v: string, property: 'health' | 'mana', obj: { edit: HTMLInputElement, readonly: RedrawableHTML }) => { + const validateProperty = (v: string, property: 'health' | 'mana', obj: { edit: HTMLInputElement, readonly: HTMLElement }) => { character.variables[property] = v.startsWith('-') ? character.variables[property] + parseInt(v.substring(1), 10) : v.startsWith('+') ? character.variables[property] - parseInt(v.substring(1), 10) : character[property] - parseInt(v, 10); obj.edit.value = (character[property] - character.variables[property]).toString(); obj.edit.replaceWith(obj.readonly); @@ -1624,12 +1624,19 @@ export class CharacterSheet { id: 'effects', title: [ text('Afflictions') ], content: () => this.effectsTab(character) }, { id: 'notes', title: [ text('Notes') ], content: () => [ - div('flex flex-col gap-2', [ - div('flex flex-col gap-2 border-b border-light-35 dark:border-dark-35 pb-4', [ div('flex flex-row w-full items-center justify-between', [ span('text-lg font-bold', 'Notes publics'), tooltip(button(loadableIcon, saveNotes, 'p-1 items-center justify-center'), 'Enregistrer', 'right') ]), div('border border-light-35 dark:border-dark-35 bg-light20 dark:bg-dark-20 p-1 h-64', [ publicNotes.dom ]) ]), - div('flex flex-col gap-2', [ span('text-lg font-bold', 'Notes privés'), div('border border-light-35 dark:border-dark-35 bg-light20 dark:bg-dark-20 p-1 h-64', [ privateNotes.dom ]) ]), + div('flex flex-col h-full divide-y divide-light-30 dark:divide-dark-30', [ + foldable([ div('border border-light-35 dark:border-dark-35 bg-light20 dark:bg-dark-20 p-1 flex-1', [ publicNotes.dom ]) ], + [ div('flex flex-row w-full items-center justify-between', [ span('text-lg font-bold', 'Notes publics'), tooltip(button(loadableIcon, saveNotes, 'p-1 items-center justify-center'), 'Enregistrer', 'right') ]), ], { + class: { container: 'flex flex-col gap-2 data-[active]:flex-1 py-2', content: 'h-full' }, open: true + }), + foldable([ div('border border-light-35 dark:border-dark-35 bg-light20 dark:bg-dark-20 p-1 flex-1', [ privateNotes.dom ]) ], + [ span('text-lg font-bold', 'Notes privés'), ], { + class: { container: 'flex flex-col gap-2 data-[active]:flex-1 py-2', content: 'h-full' }, open: false + }), + //div('flex flex-col gap-2', [ span('text-lg font-bold', 'Notes privés'), div('border border-light-35 dark:border-dark-35 bg-light20 dark:bg-dark-20 p-1 h-64', [ privateNotes.dom ]) ]), ]) ] }, - ], { focused: this.tab, class: { container: 'flex-1 gap-4 px-4 max-w-[960px] h-full', content: 'overflow-auto' }, switch: v => { this.tab = v; } }); + ], { focused: this.tab, class: { container: 'flex-1 gap-4 px-4 max-w-[960px] h-full', content: 'overflow-auto h-full' }, switch: v => { this.tab = v; } }); this.container.replaceChildren(div('flex flex-col justify-start gap-1 h-full', [ div("flex flex-row gap-4 justify-between", [ @@ -1791,7 +1798,7 @@ export class CharacterSheet div('flex flex-col gap-2', [ div('flex flex-row flex-wrap gap-2', ["Attaquer", "Désarmer", "Saisir", "Faire chuter", "Déplacer", "Courir", "Pas de coté", "Charger", "Lancer un sort", "S'interposer", "Se transformer", "Utiliser un objet", "Anticiper une action", "Improviser"].map(e => proses('a', preview, [ span('cursor-pointer text-sm decoration-dotted underline', e) ], { href: 'regles/le-combat/actions-en-combat#' + e, label: e, trigger: 'hover', navigate: false, class: 'text-light-60 dark:text-dark-60', lowers: false }))), div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col gap-1', [ - div('flex flex-row justify-between', [dom('span', { class: 'text-lg', text: config.action[e]?.name }), config.action[e]?.cost ? div('flex flex-row gap-1', [dom('span', { class: 'font-bold', text: config.action[e]?.cost?.toString() }), text(`point${config.action[e]?.cost > 1 ? 's' : ''} d'action`)]) : undefined]), + div('flex flex-row justify-between', [dom('span', { class: 'text-lg font-semibold', text: config.action[e]?.name }), config.action[e]?.cost ? div('flex flex-row gap-1', [dom('span', { class: 'font-bold', text: config.action[e]?.cost?.toString() }), text(`point${config.action[e]?.cost > 1 ? 's' : ''} d'action`)]) : undefined]), markdown(getText(config.action[e]?.description), undefined, { tags: { a: preview } }), ]), list: character.lists.action }), ]), @@ -1806,7 +1813,7 @@ export class CharacterSheet div('flex flex-col gap-2', [ div('flex flex-row flex-wrap gap-2 text-light-60 dark:text-dark-60', ["Parer", "Esquiver", "Saisir une opportunité", "Prendre en tenaille", "Intercepter"].map(e => proses('a', preview, [ span('cursor-pointer text-sm decoration-dotted underline', e) ], { href: 'regles/le-combat/actions-en-combat#' + e, label: e, trigger: 'hover', navigate: false, class: 'text-light-60 dark:text-dark-60', lowers: false }))), div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col gap-1', [ - div('flex flex-row justify-between', [dom('span', { class: 'text-lg', text: config.reaction[e]?.name }), config.reaction[e]?.cost ? div('flex flex-row gap-1', [dom('span', { class: 'font-bold', text: config.reaction[e]?.cost?.toString() }), text(`point${config.reaction[e]?.cost > 1 ? 's' : ''} d'action`)]) : undefined]), + div('flex flex-row justify-between', [dom('span', { class: 'text-lg font-semibold', text: config.reaction[e]?.name }), config.reaction[e]?.cost ? div('flex flex-row gap-1', [dom('span', { class: 'font-bold', text: config.reaction[e]?.cost?.toString() }), text(`point${config.reaction[e]?.cost > 1 ? 's' : ''} d'action`)]) : undefined]), markdown(getText(config.reaction[e]?.description), undefined, { tags: { a: preview } }), ]), list: character.lists.reaction }), ]), @@ -1820,7 +1827,7 @@ export class CharacterSheet div('flex flex-col gap-2', [ div('flex flex-row flex-wrap gap-2 text-light-60 dark:text-dark-60', ["Analyser une situation", "Communiquer", "Dégainer", "Attraper un objet"].map(e => proses('a', preview, [ span('cursor-pointer text-sm decoration-dotted underline', e) ], { href: 'regles/le-combat/actions-en-combat#' + e, label: e, trigger: 'hover', navigate: false, class: 'text-light-60 dark:text-dark-60', lowers: false }))), div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col gap-1', [ - div('flex flex-row justify-between', [dom('span', { class: 'text-lg', text: config.freeaction[e]?.name }) ]), + div('flex flex-row justify-between', [dom('span', { class: 'text-lg font-semibold', text: config.freeaction[e]?.name }) ]), markdown(getText(config.freeaction[e]?.description), undefined, { tags: { a: preview } }), ]), list: character.lists.reaction }) ]), @@ -1832,7 +1839,7 @@ export class CharacterSheet { return [ div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col gap-1', [ - div('flex flex-row justify-between', [dom('span', { class: 'text-lg', text: config.passive[e]?.name }) ]), + div('flex flex-row justify-between', [dom('span', { class: 'text-lg font-semibold', text: config.passive[e]?.name }) ]), markdown(getText(config.passive[e]?.description), undefined, { tags: { a: preview } }), ]), list: character.lists.passive }), ]; diff --git a/shared/components.ts b/shared/components.ts index e653f3b..621329b 100644 --- a/shared/components.ts +++ b/shared/components.ts @@ -1,5 +1,5 @@ import type { RouteLocationAsRelativeTyped, RouteLocationRaw, RouteMapGeneric } from "vue-router"; -import { type NodeProperties, type Class, type NodeChildren, dom, mergeClasses, text, div, icon, type Node, type RedrawableHTML } from "#shared/dom"; +import { type NodeProperties, type Class, type NodeChildren, dom, mergeClasses, text, div, icon, type Node, type HTMLElement } from "#shared/dom"; import { contextmenu, followermenu, minimizeBox, popper, teleport, tooltip, type FloatState } from "#shared/floating"; import { clamp } from "#shared/general"; import { Tree } from "#shared/tree"; @@ -18,11 +18,11 @@ export function link(children: NodeChildren, properties?: NodeProperties & { act } } : undefined }, children); } -export function loading(size: 'small' | 'normal' | 'large' = 'normal'): RedrawableHTML +export function loading(size: 'small' | 'normal' | 'large' = 'normal'): HTMLElement { return dom('span', { class: ["after:block after:relative after:rounded-full after:border-transparent after:border-t-accent-purple after:animate-spin", {'w-6 h-6 border-4 border-transparent after:-top-[4px] after:-left-[4px] after:w-6 after:h-6 after:border-4': size === 'normal', 'w-4 h-4 after:-top-[2px] after:-left-[2px] after:w-4 after:h-4 after:border-2': size === 'small', 'w-12 h-12 after:-top-[6px] after:-left-[6px] after:w-12 after:h-12 after:border-[6px]': size === 'large'}] }) } -export function async(size: 'small' | 'normal' | 'large' = 'normal', fn: Promise) +export function async(size: 'small' | 'normal' | 'large' = 'normal', fn: Promise) { let state = { current: loading(size) }; @@ -36,7 +36,7 @@ export function async(size: 'small' | 'normal' | 'large' = 'normal', fn: Promise return state; } -export function button(content: Node | NodeChildren, onClick?: (this: RedrawableHTML) => void, cls?: Class) +export function button(content: Node | NodeChildren, onClick?: (this: HTMLElement) => void, cls?: Class) { const btn = dom('button', { class: [`inline-flex justify-center items-center outline-none leading-none transition-[box-shadow] text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40 @@ -73,17 +73,17 @@ export function buttongroup(options: Array<{ text: string, value: }}})) return div(['flex flex-row', settings?.class?.container], elements); } -export function optionmenu(options: Array<{ title: string, click: () => void }>, settings?: { position?: Placement, class?: { container?: Class, option?: Class } }): (target?: RedrawableHTML) => void +export function optionmenu(options: Array<{ title: string, click: () => void }>, settings?: { position?: Placement, class?: { container?: Class, option?: Class } }): (target?: HTMLElement) => void { let close: () => void; const element = div(['flex flex-col divide-y divide-light-30 dark:divide-dark-30 text-light-100 dark:text-dark-100', settings?.class?.container], options.map(e => dom('div', { class: ['flex flex-row px-2 py-1 hover:bg-light-35 dark:hover:bg-dark-35 cursor-pointer', settings?.class?.option], text: e.title, listeners: { click: () => { e.click(); close() } } }))); - return function(this: RedrawableHTML, target?: RedrawableHTML) { + return function(this: HTMLElement, target?: HTMLElement) { close = followermenu(target ?? this, [ element ], { arrow: true, placement: settings?.position, offset: 8 }).close; } } -export type Option = { text: string, render?: () => RedrawableHTML, value: T | Option[] } | undefined; -type StoredOption = { item: Option, dom: RedrawableHTML, container?: RedrawableHTML, children?: Array> }; -export function select>(options: Array<{ text: string, value: T } | undefined>, settings?: { defaultValue?: T, change?: (value: T) => void, class?: { container?: Class, popup?: Class, option?: Class }, disabled?: boolean }): RedrawableHTML +export type Option = { text: string, render?: () => HTMLElement, value: T | Option[] } | undefined; +type StoredOption = { item: Option, dom: HTMLElement, container?: HTMLElement, children?: Array> }; +export function select>(options: Array<{ text: string, value: T } | undefined>, settings?: { defaultValue?: T, change?: (value: T) => void, class?: { container?: Class, popup?: Class, option?: Class }, disabled?: boolean }): HTMLElement { let context: { close: Function }; let focused: number | undefined; @@ -151,7 +151,7 @@ export function select>(options: Array<{ text: string }) return select; } -export function multiselect>(options: Array<{ text: string, value: T } | undefined>, settings?: { defaultValue?: T[], change?: (value: T[]) => void, class?: { container?: Class, popup?: Class, option?: Class }, disabled?: boolean }): RedrawableHTML +export function multiselect>(options: Array<{ text: string, value: T } | undefined>, settings?: { defaultValue?: T[], change?: (value: T[]) => void, class?: { container?: Class, popup?: Class, option?: Class }, disabled?: boolean }): HTMLElement { let context: { close: Function }; let focused: number | undefined; @@ -225,7 +225,7 @@ export function multiselect>(options: Array<{ text: s } export function combobox>(options: Option[], settings?: { defaultValue?: T, change?: (value: T) => void, class?: { container?: Class, popup?: Class, option?: Class }, disabled?: boolean, fill?: 'contain' | 'cover' }) { - let context: { container: RedrawableHTML, content: NodeChildren, close: () => void }; + let context: { container: HTMLElement, content: NodeChildren, close: () => void }; let selected = true, tree: StoredOption[] = []; let focused: number | undefined; let currentOptions: StoredOption[] = []; @@ -470,10 +470,10 @@ export function foldable(content: Reactive, title: NodeChildren, s fold.toggleAttribute('data-active', settings?.open ?? true); return fold; } -type TableRow = Record RedrawableHTML) | RedrawableHTML | string>; +type TableRow = Record HTMLElement) | HTMLElement | string>; export function table(content: TableRow[], headers: TableRow, properties?: { class?: { table?: Class, header?: Class, body?: Class, row?: Class, cell?: Class } }) { - const render = (item: (() => RedrawableHTML) | RedrawableHTML | string) => typeof item === 'string' ? text(item) : typeof item === 'function' ? item() : item; + const render = (item: (() => HTMLElement) | HTMLElement | string) => typeof item === 'string' ? text(item) : typeof item === 'function' ? item() : item; return dom('table', { class: ['', properties?.class?.table] }, [ dom('thead', { class: ['', properties?.class?.header] }, [ dom('tr', { class: '' }, Object.values(headers).map(e => dom('th', {}, [ render(e) ]))) ]), dom('tbody', { class: ['', properties?.class?.body] }, content.map(e => dom('tr', { class: ['', properties?.class?.row] }, Object.keys(headers).map(f => e.hasOwnProperty(f) ? dom('td', { class: ['', properties?.class?.cell] }, [ render(e[f]!) ]) : undefined)))) ]); } export function toggle(settings?: { defaultValue?: boolean, change?: (value: boolean) => void, disabled?: boolean, class?: { container?: Class } }) @@ -494,7 +494,7 @@ export function toggle(settings?: { defaultValue?: boolean, change?: (value: boo }, [ div('block w-[18px] h-[18px] translate-x-[2px] will-change-transform transition-transform bg-light-50 dark:bg-dark-50 group-data-[state=checked]:translate-x-[26px] group-data-[disabled]:bg-light-30 dark:group-data-[disabled]:bg-dark-30 group-data-[disabled]:border-light-30 dark:group-data-[disabled]:border-dark-30') ]); return element; } -export function checkbox(settings?: { defaultValue?: boolean, change?: (this: RedrawableHTML, value: boolean) => void, disabled?: boolean, class?: { container?: Class, icon?: Class } }) +export function checkbox(settings?: { defaultValue?: boolean, change?: (this: HTMLElement, value: boolean) => void, disabled?: boolean, class?: { container?: Class, icon?: Class } }) { let state = settings?.defaultValue ?? false; const element = dom("div", { class: [`group w-6 h-6 box-content flex items-center justify-center border border-light-50 dark:border-dark-50 bg-light-20 dark:bg-dark-20 @@ -512,7 +512,7 @@ export function checkbox(settings?: { defaultValue?: boolean, change?: (this: Re }, [ icon('radix-icons:check', { width: 14, height: 14, class: ['hidden group-data-[state="checked"]:block data-[disabled]:text-light-50 dark:data-[disabled]:text-dark-50', settings?.class?.icon] }), ]); return element; } -export function tabgroup(tabs: Array<{ id: string, title: NodeChildren, content: Reactive }>, settings?: { focused?: string, class?: { container?: Class, tabbar?: Class, title?: Class, content?: Class }, switch?: (tab: string) => void | boolean }): RedrawableHTML +export function tabgroup(tabs: Array<{ id: string, title: NodeChildren, content: Reactive }>, settings?: { focused?: string, class?: { container?: Class, tabbar?: Class, title?: Class, content?: Class }, switch?: (tab: string) => void | boolean }): HTMLElement { let focus = settings?.focused ?? tabs[0]?.id; const titles = tabs.map(e => dom('div', { class: ['px-2 py-1 border-b border-transparent hover:border-accent-blue data-[focus]:border-accent-blue data-[focus]:border-b-[3px] cursor-pointer', settings?.class?.title], attributes: { 'data-focus': e.id === focus }, listeners: { click: function() { @@ -535,15 +535,15 @@ export function tabgroup(tabs: Array<{ id: string, title: NodeChildren, content: div(['flex flex-row items-center gap-1', settings?.class?.tabbar], titles), content ]); - return container as RedrawableHTML; + return container as HTMLElement; } -export function floater(container: RedrawableHTML, content: NodeChildren | (() => NodeChildren), settings?: { delay?: number, href?: RouteLocationRaw, class?: Class, style?: Record | string, position?: Placement, pinned?: boolean | { width: number, height: number }, minimizable?: boolean, cover?: 'width' | 'height' | 'all' | 'none', events?: { show: Array, hide: Array, onshow?: (state: FloatState) => boolean, onhide?: (state: FloatState) => boolean }, title?: string }) +export function floater(container: HTMLElement, content: NodeChildren | (() => NodeChildren), settings?: { delay?: number, href?: RouteLocationRaw, class?: Class, style?: Record | string, position?: Placement, pinned?: boolean | { width: number, height: number }, minimizable?: boolean, cover?: 'width' | 'height' | 'all' | 'none', events?: { show: Array, hide: Array, onshow?: (state: FloatState) => boolean, onhide?: (state: FloatState) => boolean }, title?: string }) { let viewport = document.getElementById('mainContainer') ?? undefined; let diffX, diffY; let minimizeRect: DOMRect, minimized = false; - const events: { show: Array, hide: Array, onshow?: (this: RedrawableHTML, state: FloatState) => boolean, onhide?: (this: RedrawableHTML, state: FloatState) => boolean } = Object.assign({ + const events: { show: Array, hide: Array, onshow?: (this: HTMLElement, state: FloatState) => boolean, onhide?: (this: HTMLElement, state: FloatState) => boolean } = Object.assign({ show: ['mouseenter', 'mousemove', 'focus'], hide: ['mouseleave', 'blur'], } as { show: Array, hide: Array }, settings?.events ?? {}); @@ -634,7 +634,7 @@ export function floater(container: RedrawableHTML, content: NodeChildren | (() = if(!floating.content.hasAttribute('data-pinned')) return; - [...this.parentElement?.children ?? []].forEach(e => (e as any as RedrawableHTML).attributeStyleMap.set('z-index', -1)); + [...this.parentElement?.children ?? []].forEach(e => (e as any as HTMLElement).attributeStyleMap.set('z-index', -1)); this.attributeStyleMap.set('z-index', 0); }, { passive: true }); } @@ -717,13 +717,13 @@ export interface ToastConfig timer?: boolean type?: ToastType } -type ToastDom = ToastConfig & { dom: RedrawableHTML }; +type ToastDom = ToastConfig & { dom: HTMLElement }; export type ToastType = 'info' | 'success' | 'error'; export class Toaster { private static _MAX_DRAG = 150; private static _list: Array = []; - private static _container: RedrawableHTML; + private static _container: HTMLElement; static init() { @@ -793,4 +793,23 @@ export class Toaster ], { easing: 'ease-in', duration: 100 }).onfinish = () => config.dom.remove(); Toaster._list = Toaster._list.filter(e => e !== config); } +} +export class DiceRoller +{ + private static _dom: HTMLElement; + static init() + { + DiceRoller.dispose(); + + DiceRoller._dom = dom('div', { attributes: { id: 'dice-roller' }, class: 'fixed bottom-0 right-0 flex flex-col p-6 gap-2 max-w-[512px] z-50 outline-none min-w-72 empty:hidden' }); + document.body.appendChild(DiceRoller._dom); + } + static dispose() + { + DiceRoller._dom?.remove(); + } + static get dom() + { + return DiceRoller._dom; + } } \ No newline at end of file diff --git a/shared/content.ts b/shared/content.ts index 47cdb40..b73bf3d 100644 --- a/shared/content.ts +++ b/shared/content.ts @@ -2,7 +2,7 @@ import { safeDestr as parse } from 'destr'; import { Canvas, CanvasEditor } from "#shared/canvas"; import render, { renderMDAsText } from "#shared/markdown"; import { confirm, contextmenu, tooltip } from "#shared/floating"; -import { cancelPropagation, dom, icon, text, type Node, type RedrawableHTML } from "#shared/dom"; +import { cancelPropagation, dom, icon, text, type Node, type HTMLElement } from "#shared/dom"; import { loading } from "#shared/components"; import prose, { h1, h2 } from "#shared/proses"; import { getID, lerp, parsePath } from '~~/shared/general'; @@ -381,7 +381,7 @@ export class Content return handlers[overview.type].fromString(content); } - static async render(parent: RedrawableHTML, path: string): Promise | undefined> + static async render(parent: HTMLElement, path: string): Promise | undefined> { parent.appendChild(dom('div', { class: 'flex, flex-1 justify-center items-center' }, [loading('normal')])) @@ -489,7 +489,7 @@ const handlers: { [K in FileType]: ContentTypeHandler } = { return c.container; }, renderEditor: (content) => { - let element: RedrawableHTML; + let element: HTMLElement; if(content.hasOwnProperty('content')) { const c = new CanvasEditor(content.content); @@ -527,7 +527,7 @@ const handlers: { [K in FileType]: ContentTypeHandler } = { ]) }, renderEditor: (content) => { - let element: RedrawableHTML; + let element: HTMLElement; if(content.hasOwnProperty('content')) { MarkdownEditor.singleton.content = content.content; @@ -582,11 +582,11 @@ export const iconByType: Record = { export class Editor { tree!: TreeDOM; - container: RedrawableHTML; + container: HTMLElement; - selected?: Recursive; + selected?: Recursive; - private instruction: RedrawableHTML; + private instruction: HTMLElement; private cleanup?: CleanupFn; private history: History; @@ -638,7 +638,7 @@ export class Editor if(!action.element) { const depth = getPath(action.element as LocalContent).split('/').length; - action.element.element = this.tree.render(action.element as LocalContent, depth) as RedrawableHTML; + action.element.element = this.tree.render(action.element as LocalContent, depth) as HTMLElement; this.dragndrop(action.element as LocalContent, depth, (action.element as Recursive).parent); } this.tree.tree.insertAt(action.element as Recursive, action.to as number); @@ -718,7 +718,7 @@ export class Editor ])]); }); - this.select(this.tree.tree.find(useRouter().currentRoute.value.hash.substring(1)) as Recursive | undefined); + this.select(this.tree.tree.find(useRouter().currentRoute.value.hash.substring(1)) as Recursive | undefined); this.cleanup = this.setupDnD(); }); @@ -740,14 +740,14 @@ export class Editor private add(type: FileType, nextTo: Recursive) { const count = Object.values(Content.files).filter(e => e.title.match(/^Nouveau( \(\d+\))?$/)).length; - const item: Recursive & { element?: RedrawableHTML }> = { id: getID(), navigable: true, private: false, owner: 0, order: nextTo.order + 1, timestamp: new Date(), title: count === 0 ? 'Nouveau' : `Nouveau (${count})`, type: type, parent: nextTo.parent }; + const item: Recursive & { element?: HTMLElement }> = { id: getID(), navigable: true, private: false, owner: 0, order: nextTo.order + 1, timestamp: new Date(), title: count === 0 ? 'Nouveau' : `Nouveau (${count})`, type: type, parent: nextTo.parent }; this.history.add('overview', 'add', [{ element: item, from: undefined, to: nextTo.order + 1 }]); } - private remove(item: LocalContent & { element?: RedrawableHTML }) + private remove(item: LocalContent & { element?: HTMLElement }) { this.history.add('overview', 'remove', [{ element: item, from: item.order, to: undefined }], true); } - private rename(item: LocalContent & { element?: RedrawableHTML }) + private rename(item: LocalContent & { element?: HTMLElement }) { let exists = true; const change = () => @@ -769,13 +769,13 @@ export class Editor text?.parentElement?.replaceChild(input, text); input.focus(); } - private toggleNavigable(e: Event, item: LocalContent & { element?: RedrawableHTML }) + private toggleNavigable(e: Event, item: LocalContent & { element?: HTMLElement }) { cancelPropagation(e); this.history.add('overview', 'navigable', [{ element: item, from: item.navigable, to: !item.navigable }], true); } - private togglePrivate(e: Event, item: LocalContent & { element?: RedrawableHTML }) + private togglePrivate(e: Event, item: LocalContent & { element?: HTMLElement }) { cancelPropagation(e); @@ -800,7 +800,7 @@ export class Editor element: this.tree.container, })); } - private dragndrop(item: Omit void }, "content">, depth: number, parent?: Omit): CleanupFn + private dragndrop(item: Omit void }, "content">, depth: number, parent?: Omit): CleanupFn { item.cleanup && item.cleanup(); @@ -896,7 +896,7 @@ export class Editor { return handlers[item.type].renderEditor(item); } - private select(item?: LocalContent & { element?: RedrawableHTML }) + private select(item?: LocalContent & { element?: HTMLElement }) { if(this.selected && item) { @@ -917,7 +917,7 @@ export class Editor useRouter().push({ hash: this.selected ? '#' + this.selected.id : '' }) this.container.firstElementChild!.replaceChildren(); - this.selected && this.container.firstElementChild!.appendChild(this.render(this.selected) as RedrawableHTML); + this.selected && this.container.firstElementChild!.appendChild(this.render(this.selected) as HTMLElement); } unmount() { @@ -937,8 +937,9 @@ export function getPath(item: any): string return parsePath(item.title) ?? item.path; } - -/* export function buildSpellMD() +import characterConfig from '#shared/character-config.json'; +const config = characterConfig as CharacterConfig; +export function buildSpellMD() { const SPELL_ELEMENTS = ["fire","ice","thunder","earth","arcana","air","nature","light","psyche"]; const SPELL_TYPE_TEXTS = { "instinct": "Instinct", "knowledge": "Savoir", "precision": "Précision", "arts": "Oeuvres" }; @@ -981,4 +982,4 @@ export function buildTrainingFile() return Object.entries(config.training).map(e => { return `# ${mainStatTexts[e[0] as MainStat]}\n` + Object.entries(e[1]).map(_e => `## Niveau ${_e[0]}\n` + _e[1].map(feature => renderMDAsText(getText(config.features[feature]!.description))).join('\nou\n')).join('\n'); }).join('\n'); -} */ \ No newline at end of file +} \ No newline at end of file diff --git a/shared/dom.ts b/shared/dom.ts index bc00ae2..cfcc579 100644 --- a/shared/dom.ts +++ b/shared/dom.ts @@ -2,14 +2,14 @@ import { buildIcon, getIcon, iconLoaded, loadIcon, type IconifyIcon } from 'icon import { loading } from './components'; import { _defer, raw, reactivity, type Proxy, type Reactive } from './reactive'; -export type RedrawableHTML = HTMLElement; +export type HTMLElement = HTMLElement; export type Node = HTMLElement | SVGElement | Text | undefined; export type NodeChildren = Array> | undefined; export type Class = string | Array | Record | undefined; -type Listener = | ((this: RedrawableHTML, ev: HTMLElementEventMap[K]) => any) | { +type Listener = | ((this: HTMLElement, ev: HTMLElementEventMap[K]) => any) | { options?: boolean | AddEventListenerOptions; - listener: (this: RedrawableHTML, ev: HTMLElementEventMap[K]) => any; + listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any; } | undefined; export interface DOMList extends Array{ diff --git a/shared/editor.ts b/shared/editor.ts index 15f4901..9e8b8d3 100644 --- a/shared/editor.ts +++ b/shared/editor.ts @@ -7,7 +7,7 @@ import { acceptCompletion, autocompletion, closeBrackets, closeBracketsKeymap, c import { markdown, markdownLanguage } from '@codemirror/lang-markdown'; import { IterMode, Tree, type SyntaxNodeRef } from '@lezer/common'; import { tags } from '@lezer/highlight'; -import { div, dom, icon, span, type RedrawableHTML } from '~~/shared/dom'; +import { div, dom, icon, span, type HTMLElement } from '~~/shared/dom'; import { callout as calloutExtension, calloutKeymap } from '#shared/grammar/callout.extension'; import { wikilink as wikilinkExtension, autocompletion as wikilinkAutocompletion } from '#shared/grammar/wikilink.extension'; import renderMarkdown from '~~/shared/markdown'; @@ -56,7 +56,7 @@ class CalloutWidget extends WidgetType foldable?: boolean; content: string; - contentMD: RedrawableHTML; + contentMD: HTMLElement; static create(node: SyntaxNodeRef, state: EditorState): CalloutWidget | undefined { diff --git a/shared/feature.ts b/shared/feature.ts index 4801b3d..75fe0bd 100644 --- a/shared/feature.ts +++ b/shared/feature.ts @@ -1,5 +1,5 @@ import type { Ability, ArmorConfig, AspectConfig, CharacterConfig, CommonItemConfig, DamageType, Feature, FeatureChoice, FeatureEquipment, FeatureItem, FeatureList, FeatureTree, FeatureValue, ItemConfig, Level, MainStat, MundaneConfig, RaceConfig, Resistance, SpellConfig, TrainingLevel, WeaponConfig, WeaponType, WondrousConfig } from "~/types/character"; -import { div, dom, icon, span, text, type NodeChildren, type RedrawableHTML } from "#shared/dom"; +import { div, dom, icon, span, text, type NodeChildren, type HTMLElement } from "#shared/dom"; import { MarkdownEditor } from "#shared/editor"; import { preview } from "#shared/proses"; import { button, checkbox, combobox, foldable, input, multiselect, numberpicker, optionmenu, select, tabgroup, table, toggle, type Option } from "#shared/components"; @@ -18,10 +18,10 @@ type Rarity = ItemConfig['rarity']; const config = reactive(characterConfig as CharacterConfig); export class HomebrewBuilder { - private _container: RedrawableHTML; - private _tabs: RedrawableHTML; + private _container: HTMLElement; + private _tabs: HTMLElement; - constructor(container: RedrawableHTML) + constructor(container: HTMLElement) { this._container = container; @@ -58,7 +58,7 @@ export class HomebrewBuilder }).reduce((p, v) => { p[v[0]] = v[1]; return p }, {} as Record) }; config.peoples[people.id] = people; - (content[0] as RedrawableHTML).appendChild(peopleRender(people)); + (content[0] as HTMLElement).appendChild(peopleRender(people)); } const render = (people: string, level: Level, feature: string) => { let element = dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: e => { @@ -144,7 +144,7 @@ export class HomebrewBuilder ]) } - const _options = MAIN_STATS.reduce((p, v) => { p[v] = statRenderBlock(v); return p; }, {} as Record); + const _options = MAIN_STATS.reduce((p, v) => { p[v] = statRenderBlock(v); return p; }, {} as Record); const _statIndicator = dom('span', { class: 'rounded-full w-3 h-3 bg-accent-blue absolute transition-[left] after:content-[attr(data-text)] after:absolute after:-translate-x-1/2 after:top-4 after:p-px after:bg-light-0 dark:after:bg-dark-0 after:text-center' }); const _statContainer = div('relative select-none transition-[left] flex flex-1 flex-row max-w-full', Object.values(_options).map(e => div('flex flex-shrink-0 flex-col gap-4 relative w-full overflow-y-auto px-8', e.flatMap(_e => [..._e])))); @@ -507,7 +507,7 @@ class FeatureEditor private _arr: boolean; private option!: FeatureOption; - container!: RedrawableHTML; + container!: HTMLElement; constructor(list: Record | FeatureOption[], id: string, draft: boolean) { @@ -795,7 +795,7 @@ export class FeaturePanel } static edit(feature: Feature): Promise { - let container: RedrawableHTML, close: Function; + let container: HTMLElement, close: Function; return new Promise((success, failure) => { container = FeaturePanel.render(feature, success, failure); close = fullblocker([container], { @@ -869,7 +869,7 @@ export class ItemPanel } static edit(item: ItemConfig): Promise { - let container: RedrawableHTML, close: Function; + let container: HTMLElement, close: Function; return new Promise((success, failure) => { container = ItemPanel.render(item, success, failure); close = fullblocker([container], { diff --git a/shared/floating.ts b/shared/floating.ts index e565853..a1a8beb 100644 --- a/shared/floating.ts +++ b/shared/floating.ts @@ -1,5 +1,5 @@ import * as FloatingUI from "@floating-ui/dom"; -import { cancelPropagation, dom, svg, text, type Class, type NodeChildren, type RedrawableHTML } from "./dom"; +import { cancelPropagation, dom, svg, text, type Class, type NodeChildren, type HTMLElement } from "./dom"; import { button } from "./components"; import type { Reactive } from "./reactive"; @@ -10,7 +10,7 @@ export interface FloatingProperties arrow?: boolean; class?: Class; style?: Record | string; - viewport?: RedrawableHTML; + viewport?: HTMLElement; cover?: 'width' | 'height' | 'all' | 'none'; persistant?: boolean; } @@ -41,8 +41,8 @@ export interface ModalProperties onClose?: () => boolean | void; } type ModalInternals = { - container: RedrawableHTML; - content: RedrawableHTML; + container: HTMLElement; + content: HTMLElement; stop: Function; start: Function; show: Function; @@ -50,7 +50,7 @@ type ModalInternals = { persistant: boolean; }; -export let teleport: RedrawableHTML, minimizeBox: RedrawableHTML, cache: ModalInternals[] = [], hook: VoidFunction = () => {}; +export let teleport: HTMLElement, minimizeBox: HTMLElement, cache: ModalInternals[] = [], hook: VoidFunction = () => {}; export function init() { dispose(); @@ -76,7 +76,7 @@ function clear() cache = cache.filter(e => !(!e.persistant && e.content.remove())); } -export function popper(container: RedrawableHTML, properties?: PopperProperties) +export function popper(container: HTMLElement, properties?: PopperProperties) { let state: FloatState = 'hidden', timeout: Timer; const arrow = svg('svg', { class: ' group-data-[pinned]:hidden absolute fill-light-35 dark:fill-dark-35', attributes: { width: "12", height: "8", viewBox: "0 0 20 10" } }, [svg('polygon', { attributes: { points: "0,0 20,0 10,10" } })]); @@ -233,7 +233,7 @@ export function popper(container: RedrawableHTML, properties?: PopperProperties) clearTimeout(timeout); } - function link(element: RedrawableHTML) { + function link(element: HTMLElement) { (properties?.events?.show ?? ['mouseenter', 'mousemove', 'focus']).forEach((e: keyof HTMLElementEventMap) => element.addEventListener(e, show)); (properties?.events?.hide ?? ['mouseleave', 'blur']).forEach((e: keyof HTMLElementEventMap) => element.addEventListener(e, hide)); } @@ -384,7 +384,7 @@ export function contextmenu(x: number, y: number, content: NodeChildren, propert }, }, content, properties); } -export function tooltip(container: RedrawableHTML, txt: string | Text, placement: FloatingUI.Placement, delay?: number): RedrawableHTML +export function tooltip(container: HTMLElement, txt: string | Text, placement: FloatingUI.Placement, delay?: number): HTMLElement { return popper(container, { arrow: true, diff --git a/shared/tree.ts b/shared/tree.ts index 7d17998..3c4ec60 100644 --- a/shared/tree.ts +++ b/shared/tree.ts @@ -1,5 +1,5 @@ import { Content, type LocalContent } from "./content"; -import { dom, type RedrawableHTML } from "./dom"; +import { dom, type HTMLElement } from "./dom"; import { clamp } from "./general"; export type Recursive = T & { @@ -138,14 +138,14 @@ export class Tree> } export class TreeDOM { - container: RedrawableHTML; - tree: Tree>; + container: HTMLElement; + tree: Tree>; private filter?: (item: Recursive>, depth: number) => boolean | undefined; - private folder: (item: Recursive>, depth: number) => RedrawableHTML; - private leaf: (item: Recursive>, depth: number) => RedrawableHTML; + private folder: (item: Recursive>, depth: number) => HTMLElement; + private leaf: (item: Recursive>, depth: number) => HTMLElement; - constructor(folder: (item: Recursive>, depth: number) => RedrawableHTML, leaf: (item: Recursive>, depth: number) => RedrawableHTML, filter?: (item: Recursive>, depth: number) => boolean | undefined) + constructor(folder: (item: Recursive>, depth: number) => HTMLElement, leaf: (item: Recursive>, depth: number) => HTMLElement, filter?: (item: Recursive>, depth: number) => boolean | undefined) { this.tree = new Tree(Content.tree); @@ -156,7 +156,7 @@ export class TreeDOM const elements = this.tree.accumulate(this.render.bind(this)); this.container = dom('div', { class: 'list-none select-none text-light-100 dark:text-dark-100 text-sm ps-2' }, elements); } - render(item: Recursive>, depth: number): RedrawableHTML | undefined + render(item: Recursive>, depth: number): HTMLElement | undefined { if(this.filter && !(this.filter(item, depth) ?? true)) return; @@ -187,7 +187,7 @@ export class TreeDOM { this.container.replaceChildren(...this.tree.flatten.map(e => e.element!)); } - toggle(item?: Omit, state?: boolean) + toggle(item?: Omit, state?: boolean) { if(item && item.type === 'folder') { @@ -202,7 +202,7 @@ export class TreeDOM }); } } - opened(item?: Omit): boolean | undefined + opened(item?: Omit): boolean | undefined { return item ? item.element!.getAttribute('data-state') === 'open' : undefined; }