Rename RedrawableHTML, remove File API rate limite and fix pull job transaction.
This commit is contained in:
parent
e9a892076d
commit
a412116b9c
|
|
@ -16,6 +16,7 @@ const schemaList: Record<string, z.ZodObject<any> | null> = {
|
||||||
import { z } from 'zod/v4';
|
import { z } from 'zod/v4';
|
||||||
import { Icon } from '@iconify/vue';
|
import { Icon } from '@iconify/vue';
|
||||||
import { Toaster } from '~~/shared/components';
|
import { Toaster } from '~~/shared/components';
|
||||||
|
import { Content } from '~~/shared/content';
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
rights: ['admin'],
|
rights: ['admin'],
|
||||||
|
|
@ -51,6 +52,9 @@ async function fetch()
|
||||||
error.value = null;
|
error.value = null;
|
||||||
success.value = true;
|
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, });
|
Toaster.add({ duration: 10000, content: data.value ?? 'Job executé avec succès', type: 'success', timer: true, });
|
||||||
}
|
}
|
||||||
catch(e)
|
catch(e)
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,11 @@ export default defineNuxtConfig({
|
||||||
tokensPerInterval: 1
|
tokensPerInterval: 1
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'/api/file/**': {
|
||||||
|
security: {
|
||||||
|
rateLimiter: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,10 @@ export default defineTask({
|
||||||
|
|
||||||
const db = useDatabase();
|
const db = useDatabase();
|
||||||
db.transaction(tx => {
|
db.transaction(tx => {
|
||||||
db.delete(projectFilesTable).run();
|
tx.delete(projectFilesTable).run();
|
||||||
db.insert(projectFilesTable).values(files.map(e => {const { content, ...rest } = e; return rest; })).run();
|
tx.insert(projectFilesTable).values(files.map(e => {const { content, ...rest } = e; return rest; })).run();
|
||||||
db.delete(projectContentTable).run();
|
tx.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.insert(projectContentTable).values(files.map(e => ({ content: e.content ? Buffer.from(e.content as string) : null, id: e.id }))).run();
|
||||||
});
|
});
|
||||||
|
|
||||||
return { result: true };
|
return { result: true };
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { z } from "zod/v4";
|
||||||
import type { User } from "~/types/auth";
|
import type { User } from "~/types/auth";
|
||||||
import characterConfig from '#shared/character-config.json';
|
import characterConfig from '#shared/character-config.json';
|
||||||
import type { Campaign } from "~/types/campaign";
|
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 { button, foldable, loading, numberpicker, tabgroup, Toaster } from "#shared/components";
|
||||||
import { CharacterCompiler, colorByRarity, stateFactory, subnameFactory } from "#shared/character";
|
import { CharacterCompiler, colorByRarity, stateFactory, subnameFactory } from "#shared/character";
|
||||||
import { modal, tooltip } from "#shared/floating";
|
import { modal, tooltip } from "#shared/floating";
|
||||||
|
|
@ -27,7 +27,7 @@ export const CampaignValidation = z.object({
|
||||||
class CharacterPrinter
|
class CharacterPrinter
|
||||||
{
|
{
|
||||||
compiler?: CharacterCompiler;
|
compiler?: CharacterCompiler;
|
||||||
container: RedrawableHTML;
|
container: HTMLElement;
|
||||||
name: string;
|
name: string;
|
||||||
id: number;
|
id: number;
|
||||||
constructor(character: number, name: string)
|
constructor(character: number, name: string)
|
||||||
|
|
@ -64,7 +64,7 @@ export class CampaignSheet
|
||||||
private campaign?: Campaign;
|
private campaign?: Campaign;
|
||||||
private characters!: Array<CharacterPrinter>;
|
private characters!: Array<CharacterPrinter>;
|
||||||
|
|
||||||
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;
|
ws?: Socket;
|
||||||
|
|
||||||
constructor(id: string, user: ComputedRef<User | null>)
|
constructor(id: string, user: ComputedRef<User | null>)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { CanvasContent, CanvasEdge, CanvasNode } from "~/types/canvas";
|
import type { CanvasContent, CanvasEdge, CanvasNode } from "~/types/canvas";
|
||||||
import { clamp, lerp } from "#shared/general";
|
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 render from "#shared/markdown";
|
||||||
import { tooltip } from "#shared/floating";
|
import { tooltip } from "#shared/floating";
|
||||||
import { History } from "#shared/history";
|
import { History } from "#shared/history";
|
||||||
|
|
@ -200,7 +200,7 @@ export class Node extends EventTarget
|
||||||
{
|
{
|
||||||
properties: CanvasNode;
|
properties: CanvasNode;
|
||||||
|
|
||||||
nodeDom?: RedrawableHTML;
|
nodeDom?: HTMLElement;
|
||||||
|
|
||||||
constructor(properties: CanvasNode)
|
constructor(properties: CanvasNode)
|
||||||
{
|
{
|
||||||
|
|
@ -334,7 +334,7 @@ export class Edge extends EventTarget
|
||||||
{
|
{
|
||||||
properties: CanvasEdge;
|
properties: CanvasEdge;
|
||||||
|
|
||||||
edgeDom?: RedrawableHTML;
|
edgeDom?: HTMLElement;
|
||||||
protected from: Node;
|
protected from: Node;
|
||||||
protected to: Node;
|
protected to: Node;
|
||||||
protected path: Path;
|
protected path: Path;
|
||||||
|
|
@ -388,7 +388,7 @@ export class EdgeEditable extends Edge
|
||||||
private editing: boolean = false;
|
private editing: boolean = false;
|
||||||
|
|
||||||
private pathDom?: SVGPathElement;
|
private pathDom?: SVGPathElement;
|
||||||
private inputDom?: RedrawableHTML;
|
private inputDom?: HTMLElement;
|
||||||
constructor(properties: CanvasEdge, from: NodeEditable, to: NodeEditable)
|
constructor(properties: CanvasEdge, from: NodeEditable, to: NodeEditable)
|
||||||
{
|
{
|
||||||
super(properties, from, to);
|
super(properties, from, to);
|
||||||
|
|
@ -441,8 +441,8 @@ export class Canvas
|
||||||
protected tweener: Tweener = new Tweener();
|
protected tweener: Tweener = new Tweener();
|
||||||
private debouncedTimeout: Timer = setTimeout(() => {}, 0);
|
private debouncedTimeout: Timer = setTimeout(() => {}, 0);
|
||||||
|
|
||||||
protected transform!: RedrawableHTML;
|
protected transform!: HTMLElement;
|
||||||
container!: RedrawableHTML;
|
container!: HTMLElement;
|
||||||
|
|
||||||
protected firstX = 0;
|
protected firstX = 0;
|
||||||
protected firstY = 0;
|
protected firstY = 0;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { z } from "zod/v4";
|
||||||
import characterConfig from '#shared/character-config.json';
|
import characterConfig from '#shared/character-config.json';
|
||||||
import proses, { preview } from "#shared/proses";
|
import proses, { preview } from "#shared/proses";
|
||||||
import { button, checkbox, floater, foldable, input, loading, multiselect, numberpicker, select, tabgroup, Toaster, toggle } from "#shared/components";
|
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 { followermenu, fullblocker, tooltip } from "#shared/floating";
|
||||||
import { clamp } from "#shared/general";
|
import { clamp } from "#shared/general";
|
||||||
import markdown from "#shared/markdown";
|
import markdown from "#shared/markdown";
|
||||||
|
|
@ -701,16 +701,16 @@ function setProperty<T>(root: any, path: string, value: T | ((old: T) => T), for
|
||||||
}
|
}
|
||||||
export class CharacterBuilder extends CharacterCompiler
|
export class CharacterBuilder extends CharacterCompiler
|
||||||
{
|
{
|
||||||
private _container: RedrawableHTML;
|
private _container: HTMLElement;
|
||||||
private _content?: RedrawableHTML;
|
private _content?: HTMLElement;
|
||||||
private _stepsHeader: RedrawableHTML[] = [];
|
private _stepsHeader: HTMLElement[] = [];
|
||||||
private _steps: Array<BuilderTabConstructor> = [];
|
private _steps: Array<BuilderTabConstructor> = [];
|
||||||
private _stepContent: Array<BuilderTab> = [];
|
private _stepContent: Array<BuilderTab> = [];
|
||||||
private _currentStep: number = 0;
|
private _currentStep: number = 0;
|
||||||
private _helperText!: Text;
|
private _helperText!: Text;
|
||||||
private id?: string;
|
private id?: string;
|
||||||
|
|
||||||
constructor(container: RedrawableHTML, id?: string)
|
constructor(container: HTMLElement, id?: string)
|
||||||
{
|
{
|
||||||
super(Object.assign({}, defaultCharacter));
|
super(Object.assign({}, defaultCharacter));
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|
@ -944,7 +944,7 @@ export class CharacterBuilder extends CharacterCompiler
|
||||||
this.add(config.training[stat][level][choice]);
|
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');
|
const choices = config.features[feature]!.effect.filter(e => e.category === 'choice');
|
||||||
if(choices.length === 0)
|
if(choices.length === 0)
|
||||||
|
|
@ -981,8 +981,8 @@ type BuilderTabConstructor = {
|
||||||
class PeoplePicker extends BuilderTab
|
class PeoplePicker extends BuilderTab
|
||||||
{
|
{
|
||||||
private _nameInput: HTMLInputElement;
|
private _nameInput: HTMLInputElement;
|
||||||
private _visibilityInput: RedrawableHTML;
|
private _visibilityInput: HTMLElement;
|
||||||
private _options: RedrawableHTML[];
|
private _options: HTMLElement[];
|
||||||
|
|
||||||
static override header = 'Peuple';
|
static override header = 'Peuple';
|
||||||
static override description = 'Choisissez un peuple afin de définir la progression de votre personnage au fil des niveaux.';
|
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 _levelInput: HTMLInputElement;
|
||||||
private _pointsInput: HTMLInputElement;
|
private _pointsInput: HTMLInputElement;
|
||||||
|
|
||||||
private _options: RedrawableHTML[][];
|
private _options: HTMLElement[][];
|
||||||
|
|
||||||
static override header = 'Niveaux';
|
static override header = 'Niveaux';
|
||||||
static override description = 'Déterminez la progression de votre personnage en choisissant une option par niveau disponible.';
|
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[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]?.classList.toggle("opacity-30", ((i + 1) as Level) > this._builder.character.level);
|
||||||
e[1]?.childNodes.forEach((option, j) => {
|
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));
|
'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 RedrawableHTML).classList.toggle(_e, this._builder.character.leveling[((i + 1) as Level)] === j));
|
'!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
|
class TrainingPicker extends BuilderTab
|
||||||
{
|
{
|
||||||
private _pointsInput: HTMLInputElement;
|
private _pointsInput: HTMLInputElement;
|
||||||
private _options: Record<MainStat, RedrawableHTML[][]>;
|
private _options: Record<MainStat, HTMLElement[][]>;
|
||||||
|
|
||||||
private _tab: number = 0;
|
private _tab: number = 0;
|
||||||
private _statIndicator: RedrawableHTML;
|
private _statIndicator: HTMLElement;
|
||||||
private _statContainer: RedrawableHTML;
|
private _statContainer: HTMLElement;
|
||||||
|
|
||||||
static override header = 'Entrainement';
|
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.';
|
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._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<MainStat, RedrawableHTML[][]>);
|
this._options = MAIN_STATS.reduce((p, v) => { p[v] = statRenderBlock(v); return p; }, {} as Record<MainStat, HTMLElement[][]>);
|
||||||
|
|
||||||
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._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]))));
|
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[0]?.classList.toggle("opacity-30", (i as TrainingLevel) > max);
|
||||||
e[1]?.classList.toggle("opacity-30", (i as TrainingLevel) > max);
|
e[1]?.classList.toggle("opacity-30", (i as TrainingLevel) > max);
|
||||||
e[1]?.childNodes.forEach((option, j) => {
|
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
|
class AbilityPicker extends BuilderTab
|
||||||
{
|
{
|
||||||
private _pointsInput: HTMLInputElement;
|
private _pointsInput: HTMLInputElement;
|
||||||
private _options: RedrawableHTML[];
|
private _options: HTMLElement[];
|
||||||
|
|
||||||
private _maxs: RedrawableHTML[] = [];
|
private _maxs: HTMLElement[] = [];
|
||||||
|
|
||||||
static override header = 'Compétences';
|
static override header = 'Compétences';
|
||||||
static override description = 'Diversifiez vos possibilités en affectant vos points dans les différentes compétences disponibles.';
|
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) => {
|
ABILITIES.forEach((e, i) => {
|
||||||
const max = (values[`bonus/abilities/${e}`] ?? 0);
|
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;
|
const valid = (compiled.abilities[e] ?? 0) <= max;
|
||||||
if(load)
|
if(load)
|
||||||
{
|
{
|
||||||
|
|
@ -1293,7 +1293,7 @@ class AspectPicker extends BuilderTab
|
||||||
|
|
||||||
private _filter: boolean = true;
|
private _filter: boolean = true;
|
||||||
|
|
||||||
private _options: RedrawableHTML[];
|
private _options: HTMLElement[];
|
||||||
|
|
||||||
static override header = 'Aspect';
|
static override header = 'Aspect';
|
||||||
static override description = 'Déterminez l\'Aspect qui vous corresponds et benéficiez de puissants bonus.';
|
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._mentalInput.value = mental.toString();
|
||||||
this._personalityInput.value = personality.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 id = e.getAttribute('data-aspect')!;
|
||||||
const aspect = config.aspects[id]!;
|
const aspect = config.aspects[id]!;
|
||||||
|
|
||||||
|
|
@ -1498,8 +1498,8 @@ export class CharacterSheet
|
||||||
{
|
{
|
||||||
private user: ComputedRef<User | null>;
|
private user: ComputedRef<User | null>;
|
||||||
private character?: CharacterCompiler;
|
private character?: CharacterCompiler;
|
||||||
container: RedrawableHTML = div('flex flex-1 h-full w-full items-start justify-center');
|
container: HTMLElement = div('flex flex-1 h-full w-full items-start justify-center');
|
||||||
private tabs?: RedrawableHTML;
|
private tabs?: HTMLElement;
|
||||||
private tab: string = 'actions';
|
private tab: string = 'actions';
|
||||||
|
|
||||||
ws?: Socket;
|
ws?: Socket;
|
||||||
|
|
@ -1582,7 +1582,7 @@ export class CharacterSheet
|
||||||
publicNotes.content = this.character!.character.notes!.public!;
|
publicNotes.content = this.character!.character.notes!.public!;
|
||||||
privateNotes.content = this.character!.character.notes!.private!;
|
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);
|
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.value = (character[property] - character.variables[property]).toString();
|
||||||
obj.edit.replaceWith(obj.readonly);
|
obj.edit.replaceWith(obj.readonly);
|
||||||
|
|
@ -1624,12 +1624,19 @@ export class CharacterSheet
|
||||||
{ id: 'effects', title: [ text('Afflictions') ], content: () => this.effectsTab(character) },
|
{ id: 'effects', title: [ text('Afflictions') ], content: () => this.effectsTab(character) },
|
||||||
|
|
||||||
{ id: 'notes', title: [ text('Notes') ], content: () => [
|
{ id: 'notes', title: [ text('Notes') ], content: () => [
|
||||||
div('flex flex-col gap-2', [
|
div('flex flex-col h-full divide-y divide-light-30 dark:divide-dark-30', [
|
||||||
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 ]) ]),
|
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-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-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', [
|
this.container.replaceChildren(div('flex flex-col justify-start gap-1 h-full', [
|
||||||
div("flex flex-row gap-4 justify-between", [
|
div("flex flex-row gap-4 justify-between", [
|
||||||
|
|
@ -1791,7 +1798,7 @@ export class CharacterSheet
|
||||||
div('flex flex-col gap-2', [
|
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-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-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 } }),
|
markdown(getText(config.action[e]?.description), undefined, { tags: { a: preview } }),
|
||||||
]), list: character.lists.action }),
|
]), list: character.lists.action }),
|
||||||
]),
|
]),
|
||||||
|
|
@ -1806,7 +1813,7 @@ export class CharacterSheet
|
||||||
div('flex flex-col gap-2', [
|
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-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-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 } }),
|
markdown(getText(config.reaction[e]?.description), undefined, { tags: { a: preview } }),
|
||||||
]), list: character.lists.reaction }),
|
]), list: character.lists.reaction }),
|
||||||
]),
|
]),
|
||||||
|
|
@ -1820,7 +1827,7 @@ export class CharacterSheet
|
||||||
div('flex flex-col gap-2', [
|
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-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-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 } }),
|
markdown(getText(config.freeaction[e]?.description), undefined, { tags: { a: preview } }),
|
||||||
]), list: character.lists.reaction })
|
]), list: character.lists.reaction })
|
||||||
]),
|
]),
|
||||||
|
|
@ -1832,7 +1839,7 @@ export class CharacterSheet
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col gap-1', [
|
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 } }),
|
markdown(getText(config.passive[e]?.description), undefined, { tags: { a: preview } }),
|
||||||
]), list: character.lists.passive }),
|
]), list: character.lists.passive }),
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { RouteLocationAsRelativeTyped, RouteLocationRaw, RouteMapGeneric } from "vue-router";
|
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 { contextmenu, followermenu, minimizeBox, popper, teleport, tooltip, type FloatState } from "#shared/floating";
|
||||||
import { clamp } from "#shared/general";
|
import { clamp } from "#shared/general";
|
||||||
import { Tree } from "#shared/tree";
|
import { Tree } from "#shared/tree";
|
||||||
|
|
@ -18,11 +18,11 @@ export function link(children: NodeChildren, properties?: NodeProperties & { act
|
||||||
}
|
}
|
||||||
} : undefined }, children);
|
} : 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'}] })
|
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<RedrawableHTML>)
|
export function async(size: 'small' | 'normal' | 'large' = 'normal', fn: Promise<HTMLElement>)
|
||||||
{
|
{
|
||||||
let state = { current: loading(size) };
|
let state = { current: loading(size) };
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ export function async(size: 'small' | 'normal' | 'large' = 'normal', fn: Promise
|
||||||
|
|
||||||
return state;
|
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]
|
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
|
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<T extends any>(options: Array<{ text: string, value:
|
||||||
}}}))
|
}}}))
|
||||||
return div(['flex flex-row', settings?.class?.container], elements);
|
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;
|
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() } } })));
|
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;
|
close = followermenu(target ?? this, [ element ], { arrow: true, placement: settings?.position, offset: 8 }).close;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export type Option<T> = { text: string, render?: () => RedrawableHTML, value: T | Option<T>[] } | undefined;
|
export type Option<T> = { text: string, render?: () => HTMLElement, value: T | Option<T>[] } | undefined;
|
||||||
type StoredOption<T> = { item: Option<T>, dom: RedrawableHTML, container?: RedrawableHTML, children?: Array<StoredOption<T>> };
|
type StoredOption<T> = { item: Option<T>, dom: HTMLElement, container?: HTMLElement, children?: Array<StoredOption<T>> };
|
||||||
export function select<T extends NonNullable<any>>(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 select<T extends NonNullable<any>>(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 context: { close: Function };
|
||||||
let focused: number | undefined;
|
let focused: number | undefined;
|
||||||
|
|
@ -151,7 +151,7 @@ export function select<T extends NonNullable<any>>(options: Array<{ text: string
|
||||||
})
|
})
|
||||||
return select;
|
return select;
|
||||||
}
|
}
|
||||||
export function multiselect<T extends NonNullable<any>>(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<T extends NonNullable<any>>(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 context: { close: Function };
|
||||||
let focused: number | undefined;
|
let focused: number | undefined;
|
||||||
|
|
@ -225,7 +225,7 @@ export function multiselect<T extends NonNullable<any>>(options: Array<{ text: s
|
||||||
}
|
}
|
||||||
export function combobox<T extends NonNullable<any>>(options: Option<T>[], settings?: { defaultValue?: T, change?: (value: T) => void, class?: { container?: Class, popup?: Class, option?: Class }, disabled?: boolean, fill?: 'contain' | 'cover' })
|
export function combobox<T extends NonNullable<any>>(options: Option<T>[], 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<T>[] = [];
|
let selected = true, tree: StoredOption<T>[] = [];
|
||||||
let focused: number | undefined;
|
let focused: number | undefined;
|
||||||
let currentOptions: StoredOption<T>[] = [];
|
let currentOptions: StoredOption<T>[] = [];
|
||||||
|
|
@ -470,10 +470,10 @@ export function foldable(content: Reactive<NodeChildren>, title: NodeChildren, s
|
||||||
fold.toggleAttribute('data-active', settings?.open ?? true);
|
fold.toggleAttribute('data-active', settings?.open ?? true);
|
||||||
return fold;
|
return fold;
|
||||||
}
|
}
|
||||||
type TableRow = Record<string, (() => RedrawableHTML) | RedrawableHTML | string>;
|
type TableRow = Record<string, (() => HTMLElement) | HTMLElement | string>;
|
||||||
export function table(content: TableRow[], headers: TableRow, properties?: { class?: { table?: Class, header?: Class, body?: Class, row?: Class, cell?: Class } })
|
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)))) ]);
|
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 } })
|
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') ]);
|
}, [ 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;
|
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;
|
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
|
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] }), ]);
|
}, [ 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;
|
return element;
|
||||||
}
|
}
|
||||||
export function tabgroup(tabs: Array<{ id: string, title: NodeChildren, content: Reactive<NodeChildren> }>, 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<NodeChildren> }>, 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;
|
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() {
|
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),
|
div(['flex flex-row items-center gap-1', settings?.class?.tabbar], titles),
|
||||||
content
|
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, string | undefined | boolean | number> | string, position?: Placement, pinned?: boolean | { width: number, height: number }, minimizable?: boolean, cover?: 'width' | 'height' | 'all' | 'none', events?: { show: Array<keyof HTMLElementEventMap>, hide: Array<keyof HTMLElementEventMap>, 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, string | undefined | boolean | number> | string, position?: Placement, pinned?: boolean | { width: number, height: number }, minimizable?: boolean, cover?: 'width' | 'height' | 'all' | 'none', events?: { show: Array<keyof HTMLElementEventMap>, hide: Array<keyof HTMLElementEventMap>, onshow?: (state: FloatState) => boolean, onhide?: (state: FloatState) => boolean }, title?: string })
|
||||||
{
|
{
|
||||||
let viewport = document.getElementById('mainContainer') ?? undefined;
|
let viewport = document.getElementById('mainContainer') ?? undefined;
|
||||||
let diffX, diffY;
|
let diffX, diffY;
|
||||||
let minimizeRect: DOMRect, minimized = false;
|
let minimizeRect: DOMRect, minimized = false;
|
||||||
|
|
||||||
const events: { show: Array<keyof HTMLElementEventMap>, hide: Array<keyof HTMLElementEventMap>, onshow?: (this: RedrawableHTML, state: FloatState) => boolean, onhide?: (this: RedrawableHTML, state: FloatState) => boolean } = Object.assign({
|
const events: { show: Array<keyof HTMLElementEventMap>, hide: Array<keyof HTMLElementEventMap>, onshow?: (this: HTMLElement, state: FloatState) => boolean, onhide?: (this: HTMLElement, state: FloatState) => boolean } = Object.assign({
|
||||||
show: ['mouseenter', 'mousemove', 'focus'],
|
show: ['mouseenter', 'mousemove', 'focus'],
|
||||||
hide: ['mouseleave', 'blur'],
|
hide: ['mouseleave', 'blur'],
|
||||||
} as { show: Array<keyof HTMLElementEventMap>, hide: Array<keyof HTMLElementEventMap> }, settings?.events ?? {});
|
} as { show: Array<keyof HTMLElementEventMap>, hide: Array<keyof HTMLElementEventMap> }, settings?.events ?? {});
|
||||||
|
|
@ -634,7 +634,7 @@ export function floater(container: RedrawableHTML, content: NodeChildren | (() =
|
||||||
if(!floating.content.hasAttribute('data-pinned'))
|
if(!floating.content.hasAttribute('data-pinned'))
|
||||||
return;
|
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);
|
this.attributeStyleMap.set('z-index', 0);
|
||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
}
|
}
|
||||||
|
|
@ -717,13 +717,13 @@ export interface ToastConfig
|
||||||
timer?: boolean
|
timer?: boolean
|
||||||
type?: ToastType
|
type?: ToastType
|
||||||
}
|
}
|
||||||
type ToastDom = ToastConfig & { dom: RedrawableHTML };
|
type ToastDom = ToastConfig & { dom: HTMLElement };
|
||||||
export type ToastType = 'info' | 'success' | 'error';
|
export type ToastType = 'info' | 'success' | 'error';
|
||||||
export class Toaster
|
export class Toaster
|
||||||
{
|
{
|
||||||
private static _MAX_DRAG = 150;
|
private static _MAX_DRAG = 150;
|
||||||
private static _list: Array<ToastDom> = [];
|
private static _list: Array<ToastDom> = [];
|
||||||
private static _container: RedrawableHTML;
|
private static _container: HTMLElement;
|
||||||
|
|
||||||
static init()
|
static init()
|
||||||
{
|
{
|
||||||
|
|
@ -794,3 +794,22 @@ export class Toaster
|
||||||
Toaster._list = Toaster._list.filter(e => e !== config);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ import { safeDestr as parse } from 'destr';
|
||||||
import { Canvas, CanvasEditor } from "#shared/canvas";
|
import { Canvas, CanvasEditor } from "#shared/canvas";
|
||||||
import render, { renderMDAsText } from "#shared/markdown";
|
import render, { renderMDAsText } from "#shared/markdown";
|
||||||
import { confirm, contextmenu, tooltip } from "#shared/floating";
|
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 { loading } from "#shared/components";
|
||||||
import prose, { h1, h2 } from "#shared/proses";
|
import prose, { h1, h2 } from "#shared/proses";
|
||||||
import { getID, lerp, parsePath } from '~~/shared/general';
|
import { getID, lerp, parsePath } from '~~/shared/general';
|
||||||
|
|
@ -381,7 +381,7 @@ export class Content
|
||||||
return handlers[overview.type].fromString(content);
|
return handlers[overview.type].fromString(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async render(parent: RedrawableHTML, path: string): Promise<Omit<LocalContent, 'content'> | undefined>
|
static async render(parent: HTMLElement, path: string): Promise<Omit<LocalContent, 'content'> | undefined>
|
||||||
{
|
{
|
||||||
parent.appendChild(dom('div', { class: 'flex, flex-1 justify-center items-center' }, [loading('normal')]))
|
parent.appendChild(dom('div', { class: 'flex, flex-1 justify-center items-center' }, [loading('normal')]))
|
||||||
|
|
||||||
|
|
@ -489,7 +489,7 @@ const handlers: { [K in FileType]: ContentTypeHandler<K> } = {
|
||||||
return c.container;
|
return c.container;
|
||||||
},
|
},
|
||||||
renderEditor: (content) => {
|
renderEditor: (content) => {
|
||||||
let element: RedrawableHTML;
|
let element: HTMLElement;
|
||||||
if(content.hasOwnProperty('content'))
|
if(content.hasOwnProperty('content'))
|
||||||
{
|
{
|
||||||
const c = new CanvasEditor(content.content);
|
const c = new CanvasEditor(content.content);
|
||||||
|
|
@ -527,7 +527,7 @@ const handlers: { [K in FileType]: ContentTypeHandler<K> } = {
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
renderEditor: (content) => {
|
renderEditor: (content) => {
|
||||||
let element: RedrawableHTML;
|
let element: HTMLElement;
|
||||||
if(content.hasOwnProperty('content'))
|
if(content.hasOwnProperty('content'))
|
||||||
{
|
{
|
||||||
MarkdownEditor.singleton.content = content.content;
|
MarkdownEditor.singleton.content = content.content;
|
||||||
|
|
@ -582,11 +582,11 @@ export const iconByType: Record<FileType, string> = {
|
||||||
export class Editor
|
export class Editor
|
||||||
{
|
{
|
||||||
tree!: TreeDOM;
|
tree!: TreeDOM;
|
||||||
container: RedrawableHTML;
|
container: HTMLElement;
|
||||||
|
|
||||||
selected?: Recursive<LocalContent & { element?: RedrawableHTML }>;
|
selected?: Recursive<LocalContent & { element?: HTMLElement }>;
|
||||||
|
|
||||||
private instruction: RedrawableHTML;
|
private instruction: HTMLElement;
|
||||||
private cleanup?: CleanupFn;
|
private cleanup?: CleanupFn;
|
||||||
|
|
||||||
private history: History;
|
private history: History;
|
||||||
|
|
@ -638,7 +638,7 @@ export class Editor
|
||||||
if(!action.element)
|
if(!action.element)
|
||||||
{
|
{
|
||||||
const depth = getPath(action.element as LocalContent).split('/').length;
|
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<LocalContent>).parent);
|
this.dragndrop(action.element as LocalContent, depth, (action.element as Recursive<LocalContent>).parent);
|
||||||
}
|
}
|
||||||
this.tree.tree.insertAt(action.element as Recursive<LocalContent>, action.to as number);
|
this.tree.tree.insertAt(action.element as Recursive<LocalContent>, 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<LocalContent & { element?: RedrawableHTML }> | undefined);
|
this.select(this.tree.tree.find(useRouter().currentRoute.value.hash.substring(1)) as Recursive<LocalContent & { element?: HTMLElement }> | undefined);
|
||||||
|
|
||||||
this.cleanup = this.setupDnD();
|
this.cleanup = this.setupDnD();
|
||||||
});
|
});
|
||||||
|
|
@ -740,14 +740,14 @@ export class Editor
|
||||||
private add(type: FileType, nextTo: Recursive<LocalContent>)
|
private add(type: FileType, nextTo: Recursive<LocalContent>)
|
||||||
{
|
{
|
||||||
const count = Object.values(Content.files).filter(e => e.title.match(/^Nouveau( \(\d+\))?$/)).length;
|
const count = Object.values(Content.files).filter(e => e.title.match(/^Nouveau( \(\d+\))?$/)).length;
|
||||||
const item: Recursive<Omit<LocalContent, 'path' | 'content'> & { 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<Omit<LocalContent, 'path' | 'content'> & { 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 }]);
|
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);
|
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;
|
let exists = true;
|
||||||
const change = () =>
|
const change = () =>
|
||||||
|
|
@ -769,13 +769,13 @@ export class Editor
|
||||||
text?.parentElement?.replaceChild(input, text);
|
text?.parentElement?.replaceChild(input, text);
|
||||||
input.focus();
|
input.focus();
|
||||||
}
|
}
|
||||||
private toggleNavigable(e: Event, item: LocalContent & { element?: RedrawableHTML })
|
private toggleNavigable(e: Event, item: LocalContent & { element?: HTMLElement })
|
||||||
{
|
{
|
||||||
cancelPropagation(e);
|
cancelPropagation(e);
|
||||||
|
|
||||||
this.history.add('overview', 'navigable', [{ element: item, from: item.navigable, to: !item.navigable }], true);
|
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);
|
cancelPropagation(e);
|
||||||
|
|
||||||
|
|
@ -800,7 +800,7 @@ export class Editor
|
||||||
element: this.tree.container,
|
element: this.tree.container,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
private dragndrop(item: Omit<LocalContent & { element?: RedrawableHTML, cleanup?: () => void }, "content">, depth: number, parent?: Omit<LocalContent & { element?: RedrawableHTML }, "content">): CleanupFn
|
private dragndrop(item: Omit<LocalContent & { element?: HTMLElement, cleanup?: () => void }, "content">, depth: number, parent?: Omit<LocalContent & { element?: HTMLElement }, "content">): CleanupFn
|
||||||
{
|
{
|
||||||
item.cleanup && item.cleanup();
|
item.cleanup && item.cleanup();
|
||||||
|
|
||||||
|
|
@ -896,7 +896,7 @@ export class Editor
|
||||||
{
|
{
|
||||||
return handlers[item.type].renderEditor(item);
|
return handlers[item.type].renderEditor(item);
|
||||||
}
|
}
|
||||||
private select(item?: LocalContent & { element?: RedrawableHTML })
|
private select(item?: LocalContent & { element?: HTMLElement })
|
||||||
{
|
{
|
||||||
if(this.selected && item)
|
if(this.selected && item)
|
||||||
{
|
{
|
||||||
|
|
@ -917,7 +917,7 @@ export class Editor
|
||||||
useRouter().push({ hash: this.selected ? '#' + this.selected.id : '' })
|
useRouter().push({ hash: this.selected ? '#' + this.selected.id : '' })
|
||||||
|
|
||||||
this.container.firstElementChild!.replaceChildren();
|
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()
|
unmount()
|
||||||
{
|
{
|
||||||
|
|
@ -937,8 +937,9 @@ export function getPath(item: any): string
|
||||||
return parsePath(item.title) ?? item.path;
|
return parsePath(item.title) ?? item.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import characterConfig from '#shared/character-config.json';
|
||||||
/* export function buildSpellMD()
|
const config = characterConfig as CharacterConfig;
|
||||||
|
export function buildSpellMD()
|
||||||
{
|
{
|
||||||
const SPELL_ELEMENTS = ["fire","ice","thunder","earth","arcana","air","nature","light","psyche"];
|
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" };
|
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 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');
|
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');
|
}).join('\n');
|
||||||
} */
|
}
|
||||||
|
|
@ -2,14 +2,14 @@ import { buildIcon, getIcon, iconLoaded, loadIcon, type IconifyIcon } from 'icon
|
||||||
import { loading } from './components';
|
import { loading } from './components';
|
||||||
import { _defer, raw, reactivity, type Proxy, type Reactive } from './reactive';
|
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 Node = HTMLElement | SVGElement | Text | undefined;
|
||||||
export type NodeChildren = Array<Reactive<Node>> | undefined;
|
export type NodeChildren = Array<Reactive<Node>> | undefined;
|
||||||
|
|
||||||
export type Class = string | Array<Class> | Record<string, boolean> | undefined;
|
export type Class = string | Array<Class> | Record<string, boolean> | undefined;
|
||||||
type Listener<K extends keyof HTMLElementEventMap> = | ((this: RedrawableHTML, ev: HTMLElementEventMap[K]) => any) | {
|
type Listener<K extends keyof HTMLElementEventMap> = | ((this: HTMLElement, ev: HTMLElementEventMap[K]) => any) | {
|
||||||
options?: boolean | AddEventListenerOptions;
|
options?: boolean | AddEventListenerOptions;
|
||||||
listener: (this: RedrawableHTML, ev: HTMLElementEventMap[K]) => any;
|
listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any;
|
||||||
} | undefined;
|
} | undefined;
|
||||||
|
|
||||||
export interface DOMList<T> extends Array<T>{
|
export interface DOMList<T> extends Array<T>{
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { acceptCompletion, autocompletion, closeBrackets, closeBracketsKeymap, c
|
||||||
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
|
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
|
||||||
import { IterMode, Tree, type SyntaxNodeRef } from '@lezer/common';
|
import { IterMode, Tree, type SyntaxNodeRef } from '@lezer/common';
|
||||||
import { tags } from '@lezer/highlight';
|
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 { callout as calloutExtension, calloutKeymap } from '#shared/grammar/callout.extension';
|
||||||
import { wikilink as wikilinkExtension, autocompletion as wikilinkAutocompletion } from '#shared/grammar/wikilink.extension';
|
import { wikilink as wikilinkExtension, autocompletion as wikilinkAutocompletion } from '#shared/grammar/wikilink.extension';
|
||||||
import renderMarkdown from '~~/shared/markdown';
|
import renderMarkdown from '~~/shared/markdown';
|
||||||
|
|
@ -56,7 +56,7 @@ class CalloutWidget extends WidgetType
|
||||||
foldable?: boolean;
|
foldable?: boolean;
|
||||||
content: string;
|
content: string;
|
||||||
|
|
||||||
contentMD: RedrawableHTML;
|
contentMD: HTMLElement;
|
||||||
|
|
||||||
static create(node: SyntaxNodeRef, state: EditorState): CalloutWidget | undefined
|
static create(node: SyntaxNodeRef, state: EditorState): CalloutWidget | undefined
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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 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 { MarkdownEditor } from "#shared/editor";
|
||||||
import { preview } from "#shared/proses";
|
import { preview } from "#shared/proses";
|
||||||
import { button, checkbox, combobox, foldable, input, multiselect, numberpicker, optionmenu, select, tabgroup, table, toggle, type Option } from "#shared/components";
|
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);
|
const config = reactive(characterConfig as CharacterConfig);
|
||||||
export class HomebrewBuilder
|
export class HomebrewBuilder
|
||||||
{
|
{
|
||||||
private _container: RedrawableHTML;
|
private _container: HTMLElement;
|
||||||
private _tabs: RedrawableHTML;
|
private _tabs: HTMLElement;
|
||||||
|
|
||||||
constructor(container: RedrawableHTML)
|
constructor(container: HTMLElement)
|
||||||
{
|
{
|
||||||
this._container = container;
|
this._container = container;
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ export class HomebrewBuilder
|
||||||
}).reduce((p, v) => { p[v[0]] = v[1]; return p }, {} as Record<Level, string[]>)
|
}).reduce((p, v) => { p[v[0]] = v[1]; return p }, {} as Record<Level, string[]>)
|
||||||
};
|
};
|
||||||
config.peoples[people.id] = people;
|
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) => {
|
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 => {
|
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<MainStat, RedrawableHTML[][]>);
|
const _options = MAIN_STATS.reduce((p, v) => { p[v] = statRenderBlock(v); return p; }, {} as Record<MainStat, HTMLElement[][]>);
|
||||||
|
|
||||||
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 _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]))));
|
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 _arr: boolean;
|
||||||
private option!: FeatureOption;
|
private option!: FeatureOption;
|
||||||
|
|
||||||
container!: RedrawableHTML;
|
container!: HTMLElement;
|
||||||
|
|
||||||
constructor(list: Record<string, FeatureOption> | FeatureOption[], id: string, draft: boolean)
|
constructor(list: Record<string, FeatureOption> | FeatureOption[], id: string, draft: boolean)
|
||||||
{
|
{
|
||||||
|
|
@ -795,7 +795,7 @@ export class FeaturePanel
|
||||||
}
|
}
|
||||||
static edit(feature: Feature): Promise<Feature>
|
static edit(feature: Feature): Promise<Feature>
|
||||||
{
|
{
|
||||||
let container: RedrawableHTML, close: Function;
|
let container: HTMLElement, close: Function;
|
||||||
return new Promise<Feature>((success, failure) => {
|
return new Promise<Feature>((success, failure) => {
|
||||||
container = FeaturePanel.render(feature, success, failure);
|
container = FeaturePanel.render(feature, success, failure);
|
||||||
close = fullblocker([container], {
|
close = fullblocker([container], {
|
||||||
|
|
@ -869,7 +869,7 @@ export class ItemPanel
|
||||||
}
|
}
|
||||||
static edit(item: ItemConfig): Promise<ItemConfig>
|
static edit(item: ItemConfig): Promise<ItemConfig>
|
||||||
{
|
{
|
||||||
let container: RedrawableHTML, close: Function;
|
let container: HTMLElement, close: Function;
|
||||||
return new Promise<ItemConfig>((success, failure) => {
|
return new Promise<ItemConfig>((success, failure) => {
|
||||||
container = ItemPanel.render(item, success, failure);
|
container = ItemPanel.render(item, success, failure);
|
||||||
close = fullblocker([container], {
|
close = fullblocker([container], {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import * as FloatingUI from "@floating-ui/dom";
|
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 { button } from "./components";
|
||||||
import type { Reactive } from "./reactive";
|
import type { Reactive } from "./reactive";
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@ export interface FloatingProperties
|
||||||
arrow?: boolean;
|
arrow?: boolean;
|
||||||
class?: Class;
|
class?: Class;
|
||||||
style?: Record<string, string | undefined | boolean | number> | string;
|
style?: Record<string, string | undefined | boolean | number> | string;
|
||||||
viewport?: RedrawableHTML;
|
viewport?: HTMLElement;
|
||||||
cover?: 'width' | 'height' | 'all' | 'none';
|
cover?: 'width' | 'height' | 'all' | 'none';
|
||||||
persistant?: boolean;
|
persistant?: boolean;
|
||||||
}
|
}
|
||||||
|
|
@ -41,8 +41,8 @@ export interface ModalProperties
|
||||||
onClose?: () => boolean | void;
|
onClose?: () => boolean | void;
|
||||||
}
|
}
|
||||||
type ModalInternals = {
|
type ModalInternals = {
|
||||||
container: RedrawableHTML;
|
container: HTMLElement;
|
||||||
content: RedrawableHTML;
|
content: HTMLElement;
|
||||||
stop: Function;
|
stop: Function;
|
||||||
start: Function;
|
start: Function;
|
||||||
show: Function;
|
show: Function;
|
||||||
|
|
@ -50,7 +50,7 @@ type ModalInternals = {
|
||||||
persistant: boolean;
|
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()
|
export function init()
|
||||||
{
|
{
|
||||||
dispose();
|
dispose();
|
||||||
|
|
@ -76,7 +76,7 @@ function clear()
|
||||||
cache = cache.filter(e => !(!e.persistant && e.content.remove()));
|
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;
|
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" } })]);
|
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);
|
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?.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));
|
(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);
|
}, 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, {
|
return popper(container, {
|
||||||
arrow: true,
|
arrow: true,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Content, type LocalContent } from "./content";
|
import { Content, type LocalContent } from "./content";
|
||||||
import { dom, type RedrawableHTML } from "./dom";
|
import { dom, type HTMLElement } from "./dom";
|
||||||
import { clamp } from "./general";
|
import { clamp } from "./general";
|
||||||
|
|
||||||
export type Recursive<T> = T & {
|
export type Recursive<T> = T & {
|
||||||
|
|
@ -138,14 +138,14 @@ export class Tree<T extends Omit<LocalContent, 'content'>>
|
||||||
}
|
}
|
||||||
export class TreeDOM
|
export class TreeDOM
|
||||||
{
|
{
|
||||||
container: RedrawableHTML;
|
container: HTMLElement;
|
||||||
tree: Tree<Omit<LocalContent & { element?: RedrawableHTML }, "content">>;
|
tree: Tree<Omit<LocalContent & { element?: HTMLElement }, "content">>;
|
||||||
|
|
||||||
private filter?: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => boolean | undefined;
|
private filter?: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => boolean | undefined;
|
||||||
private folder: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => RedrawableHTML;
|
private folder: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => HTMLElement;
|
||||||
private leaf: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => RedrawableHTML;
|
private leaf: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => HTMLElement;
|
||||||
|
|
||||||
constructor(folder: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => RedrawableHTML, leaf: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => RedrawableHTML, filter?: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => boolean | undefined)
|
constructor(folder: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => HTMLElement, leaf: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => HTMLElement, filter?: (item: Recursive<Omit<LocalContent, 'content'>>, depth: number) => boolean | undefined)
|
||||||
{
|
{
|
||||||
this.tree = new Tree(Content.tree);
|
this.tree = new Tree(Content.tree);
|
||||||
|
|
||||||
|
|
@ -156,7 +156,7 @@ export class TreeDOM
|
||||||
const elements = this.tree.accumulate(this.render.bind(this));
|
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);
|
this.container = dom('div', { class: 'list-none select-none text-light-100 dark:text-dark-100 text-sm ps-2' }, elements);
|
||||||
}
|
}
|
||||||
render(item: Recursive<Omit<LocalContent & { element?: RedrawableHTML }, "content">>, depth: number): RedrawableHTML | undefined
|
render(item: Recursive<Omit<LocalContent & { element?: HTMLElement }, "content">>, depth: number): HTMLElement | undefined
|
||||||
{
|
{
|
||||||
if(this.filter && !(this.filter(item, depth) ?? true))
|
if(this.filter && !(this.filter(item, depth) ?? true))
|
||||||
return;
|
return;
|
||||||
|
|
@ -187,7 +187,7 @@ export class TreeDOM
|
||||||
{
|
{
|
||||||
this.container.replaceChildren(...this.tree.flatten.map(e => e.element!));
|
this.container.replaceChildren(...this.tree.flatten.map(e => e.element!));
|
||||||
}
|
}
|
||||||
toggle(item?: Omit<LocalContent & { element?: RedrawableHTML }, 'content'>, state?: boolean)
|
toggle(item?: Omit<LocalContent & { element?: HTMLElement }, 'content'>, state?: boolean)
|
||||||
{
|
{
|
||||||
if(item && item.type === 'folder')
|
if(item && item.type === 'folder')
|
||||||
{
|
{
|
||||||
|
|
@ -202,7 +202,7 @@ export class TreeDOM
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
opened(item?: Omit<LocalContent & { element?: RedrawableHTML }, 'content'>): boolean | undefined
|
opened(item?: Omit<LocalContent & { element?: HTMLElement }, 'content'>): boolean | undefined
|
||||||
{
|
{
|
||||||
return item ? item.element!.getAttribute('data-state') === 'open' : undefined;
|
return item ? item.element!.getAttribute('data-state') === 'open' : undefined;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue