You've already forked obsidian-visualiser
Homebrew manager completed !
This commit is contained in:
@@ -745,8 +745,9 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"peoples": [
|
||||
{
|
||||
"peoples": {
|
||||
"e662m19q590kn4dowvssowi1qf8ia7sk": {
|
||||
"id": "e662m19q590kn4dowvssowi1qf8ia7sk",
|
||||
"name": "Humain",
|
||||
"description": "Les humains, originaire d'un tout autre monde, ont subit un cataclysme qui les a projeté dans les terres d'Erina. En tant que civilisation dépourvue de magie, ils sont plus specialisés, gagnant moins de statistiques mais pouvant plus tôt ou plus fréquemment obtenir certains bonus.",
|
||||
"options": {
|
||||
@@ -833,8 +834,75 @@
|
||||
"9q8mf0u06oxxwqltyv58kbavs7qtoouw"
|
||||
]
|
||||
}
|
||||
},
|
||||
"3v3rwsn9bimpyd2fc95ml8wdrrmfsqb0": {
|
||||
"id": "3v3rwsn9bimpyd2fc95ml8wdrrmfsqb0",
|
||||
"name": "Quplothien",
|
||||
"description": "Quploth est la région du monde abritant le plus de marchands et charlatans. Dû à la sur-désertification de leurs terres, ils ont appris à vivre en troquant les richesses. Leurs cités, denses et prospères, sont peu nombreuses et suscitent un tourisme culturel croissant.",
|
||||
"options": {
|
||||
"1": [
|
||||
"bbuzw6awn2mkb05imdoecxfkc5zuj98i"
|
||||
],
|
||||
"2": [
|
||||
"ub2ws6q8xdbngeouip02umvw2oox9r68"
|
||||
],
|
||||
"3": [
|
||||
"5d7u2jvi4u0nnrzesderha3uo8kb3zjq"
|
||||
],
|
||||
"4": [
|
||||
"8w4jthjrn3l8u4trmj46z6t6ab5rbgk3"
|
||||
],
|
||||
"5": [
|
||||
"z9lux6nlhl8pjhcwst6bnhpn6cq6c77w"
|
||||
],
|
||||
"6": [
|
||||
"dx5khvrhwkhhn8fv4b8pecuh8i5wtwij"
|
||||
],
|
||||
"7": [
|
||||
"pfzopr4oyrsgxg0cbva16zzzly3kke9z"
|
||||
],
|
||||
"8": [
|
||||
"fk0wmg94tlq78khq8zot2o5u4nnxr2gb"
|
||||
],
|
||||
"9": [
|
||||
"u9vv3z280jgzab7pjwe9kexqjlpoxvax"
|
||||
],
|
||||
"10": [
|
||||
"fuxn9ndabr5yl0rrdtilldssmjxso24p"
|
||||
],
|
||||
"11": [
|
||||
"7kdxs6b6j9pqhgm3c8ydp8f9o074vp8q"
|
||||
],
|
||||
"12": [
|
||||
"dwvjqspm8l0gnks6y7u9vty0563u20kd"
|
||||
],
|
||||
"13": [
|
||||
"bmh55yfypfw8rezd16m2cuocrx0kkfl4"
|
||||
],
|
||||
"14": [
|
||||
"hatuas1yl3armteqwjwm1gjpsjp3v97x"
|
||||
],
|
||||
"15": [
|
||||
"0fg543b25uppvollu9oxtowyxjjw1x5a"
|
||||
],
|
||||
"16": [
|
||||
"l770gvirwzfbtfgfl29dxhvdh95f1m71"
|
||||
],
|
||||
"17": [
|
||||
"m1zkviiz3ow1g7rwpkyygmyggphvoz8b"
|
||||
],
|
||||
"18": [
|
||||
"uuc8vci5bk5kkx23a7ks1gu778fmu9w1"
|
||||
],
|
||||
"19": [
|
||||
"fd076hnyjagipaez166p9xp3wtlf5sgw"
|
||||
],
|
||||
"20": [
|
||||
"qcp28eysi3l3n438v41kowisdpq4ht61"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"training": {
|
||||
"strength": {
|
||||
"0": [
|
||||
@@ -9452,6 +9520,106 @@
|
||||
}
|
||||
],
|
||||
"id": "9q8mf0u06oxxwqltyv58kbavs7qtoouw"
|
||||
},
|
||||
"bbuzw6awn2mkb05imdoecxfkc5zuj98i": {
|
||||
"id": "bbuzw6awn2mkb05imdoecxfkc5zuj98i",
|
||||
"description": "Bonjour",
|
||||
"effect": []
|
||||
},
|
||||
"ub2ws6q8xdbngeouip02umvw2oox9r68": {
|
||||
"id": "ub2ws6q8xdbngeouip02umvw2oox9r68",
|
||||
"description": "je",
|
||||
"effect": []
|
||||
},
|
||||
"5d7u2jvi4u0nnrzesderha3uo8kb3zjq": {
|
||||
"id": "5d7u2jvi4u0nnrzesderha3uo8kb3zjq",
|
||||
"description": "suis",
|
||||
"effect": []
|
||||
},
|
||||
"8w4jthjrn3l8u4trmj46z6t6ab5rbgk3": {
|
||||
"id": "8w4jthjrn3l8u4trmj46z6t6ab5rbgk3",
|
||||
"description": "Nicolas",
|
||||
"effect": []
|
||||
},
|
||||
"z9lux6nlhl8pjhcwst6bnhpn6cq6c77w": {
|
||||
"id": "z9lux6nlhl8pjhcwst6bnhpn6cq6c77w",
|
||||
"description": "Sarkozy",
|
||||
"effect": []
|
||||
},
|
||||
"dx5khvrhwkhhn8fv4b8pecuh8i5wtwij": {
|
||||
"id": "dx5khvrhwkhhn8fv4b8pecuh8i5wtwij",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"pfzopr4oyrsgxg0cbva16zzzly3kke9z": {
|
||||
"id": "pfzopr4oyrsgxg0cbva16zzzly3kke9z",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"fk0wmg94tlq78khq8zot2o5u4nnxr2gb": {
|
||||
"id": "fk0wmg94tlq78khq8zot2o5u4nnxr2gb",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"u9vv3z280jgzab7pjwe9kexqjlpoxvax": {
|
||||
"id": "u9vv3z280jgzab7pjwe9kexqjlpoxvax",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"fuxn9ndabr5yl0rrdtilldssmjxso24p": {
|
||||
"id": "fuxn9ndabr5yl0rrdtilldssmjxso24p",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"7kdxs6b6j9pqhgm3c8ydp8f9o074vp8q": {
|
||||
"id": "7kdxs6b6j9pqhgm3c8ydp8f9o074vp8q",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"dwvjqspm8l0gnks6y7u9vty0563u20kd": {
|
||||
"id": "dwvjqspm8l0gnks6y7u9vty0563u20kd",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"bmh55yfypfw8rezd16m2cuocrx0kkfl4": {
|
||||
"id": "bmh55yfypfw8rezd16m2cuocrx0kkfl4",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"hatuas1yl3armteqwjwm1gjpsjp3v97x": {
|
||||
"id": "hatuas1yl3armteqwjwm1gjpsjp3v97x",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"0fg543b25uppvollu9oxtowyxjjw1x5a": {
|
||||
"id": "0fg543b25uppvollu9oxtowyxjjw1x5a",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"l770gvirwzfbtfgfl29dxhvdh95f1m71": {
|
||||
"id": "l770gvirwzfbtfgfl29dxhvdh95f1m71",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"m1zkviiz3ow1g7rwpkyygmyggphvoz8b": {
|
||||
"id": "m1zkviiz3ow1g7rwpkyygmyggphvoz8b",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"uuc8vci5bk5kkx23a7ks1gu778fmu9w1": {
|
||||
"id": "uuc8vci5bk5kkx23a7ks1gu778fmu9w1",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"fd076hnyjagipaez166p9xp3wtlf5sgw": {
|
||||
"id": "fd076hnyjagipaez166p9xp3wtlf5sgw",
|
||||
"description": "",
|
||||
"effect": []
|
||||
},
|
||||
"qcp28eysi3l3n438v41kowisdpq4ht61": {
|
||||
"id": "qcp28eysi3l3n438v41kowisdpq4ht61",
|
||||
"description": "",
|
||||
"effect": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,7 +428,7 @@ export class CharacterBuilder extends CharacterCompiler
|
||||
}
|
||||
private render()
|
||||
{
|
||||
/*this._steps = [
|
||||
this._steps = [
|
||||
PeoplePicker,
|
||||
LevelPicker,
|
||||
TrainingPicker,
|
||||
@@ -439,7 +439,7 @@ export class CharacterBuilder extends CharacterCompiler
|
||||
dom("div", { class: "group flex items-center", }, [
|
||||
dom("div", { class: "px-2 py-1 border-b border-transparent hover:border-accent-blue disabled:text-light-50 dark:disabled:text-dark-50 disabled:hover:border-transparent group-data-[state=active]:text-accent-blue cursor-pointer", listeners: { click: () => this.display(i) } }, [text(e.header)]),
|
||||
])
|
||||
);*/
|
||||
);
|
||||
this._helperText = text("Choisissez un peuple afin de définir la progression de votre personnage au fil des niveaux.")
|
||||
this._content = dom('div', { class: 'flex-1 outline-none max-w-full w-full overflow-y-auto', attributes: { id: 'characterEditorContainer' } });
|
||||
this._container.appendChild(div('flex flex-1 flex-col justify-start items-center px-8 w-full h-full overflow-y-hidden', [
|
||||
@@ -460,15 +460,15 @@ export class CharacterBuilder extends CharacterCompiler
|
||||
if(step < 0 || step >= this._stepsHeader.length)
|
||||
return;
|
||||
|
||||
//if(step !== 0 && this._steps.slice(0, step).some(e => !e.validate(this)))
|
||||
// return;
|
||||
if(step !== 0 && this._steps.slice(0, step).some(e => !e.validate(this)))
|
||||
return;
|
||||
|
||||
//this._stepsHeader.forEach(e => e.setAttribute('data-state', 'inactive'));
|
||||
//this._stepsHeader[step]!.setAttribute('data-state', 'active');
|
||||
this._stepsHeader.forEach(e => e.setAttribute('data-state', 'inactive'));
|
||||
this._stepsHeader[step]!.setAttribute('data-state', 'active');
|
||||
|
||||
//this._content?.replaceChildren(...(new this._steps[step]!(this)).dom);
|
||||
this._content?.replaceChildren(...(new this._steps[step]!(this)).dom);
|
||||
|
||||
//this._helperText.textContent = this._steps[step]!.description;
|
||||
this._helperText.textContent = this._steps[step]!.description;
|
||||
}
|
||||
async save(leave: boolean = true)
|
||||
{
|
||||
@@ -681,7 +681,7 @@ abstract class BuilderTab {
|
||||
};
|
||||
type BuilderTabConstructor = {
|
||||
new (builder: CharacterBuilder): BuilderTab;
|
||||
name: string;
|
||||
header: string;
|
||||
description: string;
|
||||
validate(builder: CharacterBuilder): boolean;
|
||||
}
|
||||
@@ -691,8 +691,6 @@ class PeoplePicker extends BuilderTab
|
||||
private _visibilityInput: HTMLDivElement;
|
||||
private _options: HTMLDivElement[];
|
||||
|
||||
private _activeOption?: HTMLDivElement;
|
||||
|
||||
static override header = 'Peuple';
|
||||
static override description = 'Choisissez un peuple afin de définir la progression de votre personnage au fil des niveaux.';
|
||||
|
||||
@@ -704,16 +702,15 @@ class PeoplePicker extends BuilderTab
|
||||
input: (value) => {
|
||||
this._builder.character.name = value ?? '';
|
||||
document.title = `d[any] - Edition de ${this._builder.character.name || 'nouveau personnage'}`;
|
||||
}
|
||||
}, defaultValue: this._builder.character.name
|
||||
});
|
||||
this._visibilityInput = toggle({ defaultValue: this._builder.character.visibility === "private", change: (value) => this._builder.character.visibility = value ? "private" : "public" });
|
||||
|
||||
this._options = config.peoples.map(
|
||||
this._options = Object.values(config.peoples).map(
|
||||
(people, i) => dom("div", { class: "flex flex-col flex-nowrap gap-2 p-2 border border-light-35 dark:border-dark-35 cursor-pointer hover:border-light-70 dark:hover:border-dark-70 w-[320px]", listeners: { click: () => {
|
||||
this._builder.character.people = i;
|
||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, false));
|
||||
this._activeOption = this._options[i]!;
|
||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, true));
|
||||
this._builder.character.people = people.id;
|
||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._options.forEach(f => f?.classList.toggle(e, false)));
|
||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._options[i]?.classList.toggle(e, true));
|
||||
}
|
||||
} }, [div("h-[320px]"), div("text-xl font-bold text-center", [text(people.name)]), div("w-full border-b border-light-50 dark:border-dark-50"), div("text-wrap word-break", [text(people.description)])]),
|
||||
);
|
||||
@@ -734,13 +731,6 @@ class PeoplePicker extends BuilderTab
|
||||
{
|
||||
this._nameInput.value = this._builder.character.name;
|
||||
this._visibilityInput.setAttribute('data-state', this._builder.character.visibility === "private" ? "checked" : "unchecked");
|
||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, false));
|
||||
|
||||
if(this._builder.character.people !== undefined)
|
||||
{
|
||||
this._activeOption = this._options[this._builder.character.people]!;
|
||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, true));
|
||||
}
|
||||
}
|
||||
static override validate(builder: CharacterBuilder): boolean
|
||||
{
|
||||
|
||||
@@ -47,10 +47,10 @@ export function select<T extends NonNullable<any>>(options: Array<{ text: string
|
||||
if(e === undefined)
|
||||
return;
|
||||
|
||||
return dom('div', { listeners: { click: () => {
|
||||
return dom('div', { listeners: { click: (_e) => {
|
||||
textValue.textContent = e.text;
|
||||
settings?.change && settings?.change(e.value);
|
||||
context && context.close && context.close();
|
||||
context && context.close && !_e.ctrlKey && context.close();
|
||||
}, mouseenter: (e) => focus(i) }, class: ['data-[focused]:bg-light-30 dark:data-[focused]:bg-dark-30 text-light-70 dark:text-dark-70 data-[focused]:text-light-100 dark:data-[focused]:text-dark-100 py-1 px-2 cursor-pointer', settings?.class?.option] }, [ text(e.text) ]);
|
||||
});
|
||||
const select = dom('div', { listeners: { click: () => {
|
||||
@@ -116,12 +116,12 @@ export function multiselect<T extends NonNullable<any>>(options: Array<{ text: s
|
||||
if(e === undefined)
|
||||
return;
|
||||
|
||||
const element = dom('div', { listeners: { click: () => {
|
||||
const element = dom('div', { listeners: { click: (_e) => {
|
||||
selection = selection.includes(e.value) ? selection.filter(f => f !== e.value) : [...selection, e.value];
|
||||
textValue.textContent = selection.length > 0 ? ((options.find(f => f?.value === selection[0])?.text ?? '') + (selection.length > 1 ? ` +${selection.length - 1}` : '')) : '';
|
||||
element.toggleAttribute('data-selected', selection.includes(e.value));
|
||||
settings?.change && settings?.change(selection);
|
||||
context && context.close && context.close();
|
||||
context && context.close && !_e.ctrlKey && context.close();
|
||||
}, mouseenter: (e) => focus(i) }, class: ['group flex flex-row justify-between items-center data-[focused]:bg-light-30 dark:data-[focused]:bg-dark-30 text-light-70 dark:text-dark-70 data-[focused]:text-light-100 dark:data-[focused]:text-dark-100 py-1 px-2 cursor-pointer', settings?.class?.option], attributes: { 'data-selected': selection.includes(e.value) } }, [ text(e.text), icon('radix-icons:check', { class: 'hidden group-data-[selected]:block', noobserver: true }) ]);
|
||||
return element;
|
||||
});
|
||||
@@ -240,11 +240,11 @@ export function combobox<T extends NonNullable<any>>(options: Option<T>[], setti
|
||||
}
|
||||
else
|
||||
{
|
||||
return { item: option, dom: dom('div', { listeners: { click: () => {
|
||||
return { item: option, dom: dom('div', { listeners: { click: (_e) => {
|
||||
select.value = option.text;
|
||||
settings?.change && settings?.change(option.value as T);
|
||||
selected = true;
|
||||
hide();
|
||||
!_e.ctrlKey && hide();
|
||||
}, mouseenter: () => focus(option.value) }, class: ['data-[focused]:bg-light-30 dark:data-[focused]:bg-dark-30 text-light-70 dark:text-dark-70 data-[focused]:text-light-100 dark:data-[focused]:text-dark-100 py-1 px-2 cursor-pointer', settings?.class?.option] }, [ option?.render ? option?.render() : text(option.text) ]) };
|
||||
}
|
||||
}
|
||||
@@ -347,10 +347,10 @@ export function numberpicker(settings?: { defaultValue?: number, change?: (value
|
||||
switch(e.key)
|
||||
{
|
||||
case "ArrowUp":
|
||||
validateAndChange(storedValue + (e.shiftKey ? 10 : 1)) && settings?.input && settings.input(storedValue);
|
||||
validateAndChange(storedValue + (e.ctrlKey ? 10 : 1)) && settings?.input && settings.input(storedValue);
|
||||
break;
|
||||
case "ArrowDown":
|
||||
validateAndChange(storedValue - (e.shiftKey ? 10 : 1)) && settings?.input && settings.input(storedValue);
|
||||
validateAndChange(storedValue - (e.ctrlKey ? 10 : 1)) && settings?.input && settings.input(storedValue);
|
||||
break;
|
||||
case "PageUp":
|
||||
settings?.max && validateAndChange(settings.max) && settings?.input && settings.input(storedValue);
|
||||
@@ -371,12 +371,23 @@ export function numberpicker(settings?: { defaultValue?: number, change?: (value
|
||||
return field;
|
||||
}
|
||||
// Open by default
|
||||
export function foldable(content: NodeChildren, title: NodeChildren, settings?: { open?: boolean, class?: { container?: Class, title?: Class, content?: Class, icon?: Class } })
|
||||
export function foldable(content: NodeChildren | (() => NodeChildren), title: NodeChildren, settings?: { open?: boolean, class?: { container?: Class, title?: Class, content?: Class, icon?: Class } })
|
||||
{
|
||||
let _content: NodeChildren;
|
||||
const display = (state: boolean) => {
|
||||
if(state && !_content)
|
||||
{
|
||||
_content = typeof content === 'function' ? content() : content;
|
||||
//@ts-ignore
|
||||
contentContainer.replaceChildren(..._content);
|
||||
}
|
||||
}
|
||||
const contentContainer = div(['hidden group-data-[active]:flex', settings?.class?.content]);
|
||||
const fold = div(['group flex flex-1 w-full flex-col', settings?.class?.container], [
|
||||
div('flex', [ dom('div', { listeners: { click: () => fold.toggleAttribute('data-active') }, class: ['flex justify-center items-center', settings?.class?.icon] }, [ icon('radix-icons:caret-right', { class: 'group-data-[active]:rotate-90 origin-center' }) ]), div(['flex-1', settings?.class?.title], title) ]),
|
||||
div(['hidden group-data-[active]:flex', settings?.class?.content], content),
|
||||
div('flex', [ dom('div', { listeners: { click: () => { display(fold.toggleAttribute('data-active')) } }, class: ['flex justify-center items-center', settings?.class?.icon] }, [ icon('radix-icons:caret-right', { class: 'group-data-[active]:rotate-90 origin-center', noobserver: true }) ]), div(['flex-1', settings?.class?.title], title) ]),
|
||||
contentContainer
|
||||
]);
|
||||
display(settings?.open ?? true);
|
||||
fold.toggleAttribute('data-active', settings?.open ?? true);
|
||||
return fold;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Ability, AspectConfig, CharacterConfig, Feature, FeatureEffect, FeatureItem, MainStat, Resistance, SpellConfig, TrainingLevel } from "~/types/character";
|
||||
import type { Ability, AspectConfig, CharacterConfig, Feature, FeatureEffect, FeatureItem, Level, MainStat, RaceConfig, Resistance, SpellConfig, TrainingLevel } from "~/types/character";
|
||||
import { div, dom, icon, text, type NodeChildren } from "#shared/dom.util";
|
||||
import { MarkdownEditor } from "#shared/editor.util";
|
||||
import { fakeA } from "#shared/proses";
|
||||
import { button, combobox, foldable, input, multiselect, numberpicker, select, table, toggle, type Option } from "#shared/components.util";
|
||||
import { confirm, contextmenu, fullblocker, tooltip } from "#shared/floating.util";
|
||||
import { ALIGNMENTS, alignmentTexts, elementTexts, MAIN_STATS, mainStatShortTexts, mainStatTexts, SPELL_ELEMENTS, SPELL_TYPES, spellTypeTexts } from "#shared/character.util";
|
||||
import { ALIGNMENTS, alignmentTexts, elementTexts, LEVELS, MAIN_STATS, mainStatShortTexts, mainStatTexts, SPELL_ELEMENTS, SPELL_TYPES, spellTypeTexts } from "#shared/character.util";
|
||||
import characterConfig from "#shared/character-config.json";
|
||||
import { getID, ID_SIZE } from "#shared/general.util";
|
||||
import renderMarkdown, { renderText } from "#shared/markdown.util";
|
||||
@@ -100,24 +100,73 @@ abstract class BuilderTab {
|
||||
};
|
||||
class PeopleEditor extends BuilderTab
|
||||
{
|
||||
private _options: HTMLDivElement[];
|
||||
|
||||
private _activeOption?: HTMLDivElement;
|
||||
|
||||
constructor(builder: HomebrewBuilder, config: CharacterConfig)
|
||||
{
|
||||
super(builder, config);
|
||||
|
||||
this._options = config.peoples.map(
|
||||
(people, i) => dom("div", { class: "flex flex-col flex-nowrap gap-2 p-2 border border-light-35 dark:border-dark-35 cursor-pointer hover:border-light-70 dark:hover:border-dark-70 w-[320px]", listeners: { click: () => {
|
||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, false));
|
||||
this._activeOption = this._options[i]!;
|
||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, true));
|
||||
const add = () => {
|
||||
const people: RaceConfig = {
|
||||
id: getID(ID_SIZE),
|
||||
name: '',
|
||||
description: '',
|
||||
options: LEVELS.map(e => {
|
||||
const feature: Feature = {
|
||||
id: getID(ID_SIZE),
|
||||
description: '',
|
||||
effect: [],
|
||||
}
|
||||
config.features[feature.id] = feature;
|
||||
return [e, [feature.id]] as [Level, string[]];
|
||||
}).reduce((p, v) => { p[v[0]] = v[1]; return p }, {} as Record<Level, string[]>)
|
||||
};
|
||||
config.peoples[people.id] = people;
|
||||
(this._content[0] as HTMLDivElement).appendChild(peopleRender(people));
|
||||
}
|
||||
const remove = (people: RaceConfig) => {
|
||||
confirm('Voulez vous vraiment supprimer cet aspect ?').then(e => {
|
||||
if(e)
|
||||
{
|
||||
Object.values(people.options).forEach(e => e.forEach(id => delete config.features[id]));
|
||||
delete config.peoples[people.id];
|
||||
|
||||
|
||||
}
|
||||
} }, [div("h-[320px]"), div("text-xl font-bold text-center", [text(people.name)]), div("w-full border-b border-light-50 dark:border-dark-50"), div("text-wrap word-break", [text(people.description)])]),
|
||||
);
|
||||
|
||||
this._content = [ div('flex flex-1 gap-4 p-2 overflow-x-auto justify-center', this._options) ];
|
||||
})
|
||||
}
|
||||
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 => {
|
||||
this._builder.edit(config.features[feature]!).then(e => {
|
||||
element.replaceChildren(markdownUtil(config.features[feature]!.description, undefined, { tags: { a: fakeA } }));
|
||||
});
|
||||
}, contextmenu: (e) => {
|
||||
e.preventDefault();
|
||||
const context = contextmenu(e.clientX, e.clientY, [
|
||||
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
|
||||
context.close();
|
||||
const _feature: Feature = { id: getID(ID_SIZE), description: '', effect: [] };
|
||||
config.features[_feature.id] = _feature;
|
||||
config.peoples[people]!.options[level]!.push(_feature.id);
|
||||
element.parentElement?.appendChild(render(people, level, _feature.id));
|
||||
} } }, [ text('Nouveau') ]),
|
||||
config.peoples[people]!.options[level].length > 1 ? dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
|
||||
context.close();
|
||||
confirm('Voulez-vous vraiment supprimer cet element ?').then(e => { if(e) {
|
||||
config.peoples[people]!.options[level] = config.peoples[people]!.options[level].filter(e => e !== feature);
|
||||
delete config.features[feature];
|
||||
element.remove();
|
||||
}
|
||||
}) } } }, [ text('Supprimer') ]) : undefined,
|
||||
], { placement: "right-start", priority: false });
|
||||
}}}, [ markdownUtil(config.features[feature]!.description, undefined, { tags: { a: fakeA } }) ]);
|
||||
return element;
|
||||
}
|
||||
const peopleRender = (people: RaceConfig) => {
|
||||
return foldable(() => Object.entries(people.options).flatMap(level => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative" }, [ text(level[0]) ])]),
|
||||
div("flex flex-row gap-4 justify-center", level[1].map((option) => render(people.id, parseInt(level[0], 10) as Level, option))),
|
||||
]), [ input('text', { defaultValue: people.name, input: (value) => people.name = value, class: 'w-32' }), input('text', { defaultValue: people.description, input: (value) => people.description = value, class: 'w-full' }) ], { class: { container: 'gap-2 max-h-full', title: 'flex flex-row', content: 'flex flex-shrink-0 flex-col gap-4 relative w-full overflow-y-auto px-8' }, open: false })
|
||||
}
|
||||
const container = div('flex flex-col gap-2', Object.values(config.peoples).map(peopleRender));
|
||||
this._content = [ div('flex flex-col py-2 gap-2', [ div('w-full flex flex-row-reverse', [ button(icon('radix-icons:plus'), add, 'p-1') ]), container ]) ];
|
||||
}
|
||||
}
|
||||
class TrainingEditor extends BuilderTab
|
||||
@@ -131,24 +180,37 @@ class TrainingEditor extends BuilderTab
|
||||
constructor(builder: HomebrewBuilder, config: CharacterConfig)
|
||||
{
|
||||
super(builder, config);
|
||||
const render = (stat: MainStat, level: TrainingLevel, 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 => {
|
||||
this._builder.edit(config.features[feature]!).then(e => {
|
||||
element.replaceChildren(markdownUtil(config.features[feature]!.description, undefined, { tags: { a: fakeA } }));
|
||||
});
|
||||
}, contextmenu: (e) => {
|
||||
e.preventDefault();
|
||||
const context = contextmenu(e.clientX, e.clientY, [
|
||||
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
|
||||
context.close();
|
||||
const _feature: Feature = { id: getID(ID_SIZE), description: '', effect: [] };
|
||||
config.features[_feature.id] = _feature;
|
||||
config.training[stat][level].push(_feature.id);
|
||||
element.parentElement?.appendChild(render(stat, level, _feature.id));
|
||||
} } }, [ text('Nouveau') ]),
|
||||
config.training[stat][level].length > 1 ? dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
|
||||
context.close();
|
||||
confirm('Voulez-vous vraiment supprimer cet element ?').then(e => { if(e) {
|
||||
config.training[stat][level as any as TrainingLevel] = config.training[stat][level as any as TrainingLevel].filter(e => e !== feature);
|
||||
delete config.features[feature];
|
||||
element.remove();
|
||||
}
|
||||
}) } } }, [ text('Supprimer') ]) : undefined,
|
||||
], { placement: "right-start", priority: false });
|
||||
}}}, [ markdownUtil(config.features[feature]!.description, undefined, { tags: { a: fakeA } }) ]);
|
||||
return element;
|
||||
};
|
||||
const statRenderBlock = (stat: MainStat) => {
|
||||
return Object.entries(config.training[stat]).map(
|
||||
(level) => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative" }, [ text(level[0]) ])]),
|
||||
div("flex flex-row gap-4 justify-center", level[1].map((option, j) => {
|
||||
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 => {
|
||||
this._builder.edit(config.features[option]!).then(e => {
|
||||
element.replaceChildren(markdownUtil(config.features[option]!.description, undefined, { tags: { a: fakeA } }));
|
||||
});
|
||||
}, contextmenu: (e) => {
|
||||
e.preventDefault();
|
||||
const context = contextmenu(e.clientX, e.clientY, [
|
||||
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => { context.close(); } } }, [ text('Nouveau avant') ]),
|
||||
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => { context.close(); } } }, [ text('Nouveau après') ]),
|
||||
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => { context.close(); confirm('Voulez-vous vraiment supprimer cet element ?').then(e => { if(e) { delete config.training[stat][level[0] as any as TrainingLevel]; /* redraw */ } }) } } }, [ text('Supprimer') ])
|
||||
], { placement: "right-start", priority: false });
|
||||
}}}, [ markdownUtil(config.features[option]!.description, undefined, { tags: { a: fakeA } }) ]);
|
||||
return element;
|
||||
})),
|
||||
div("flex flex-row gap-4 justify-center", level[1].map((option) => render(stat, parseInt(level[0], 10) as TrainingLevel, option))),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -229,11 +291,16 @@ class AspectEditor extends BuilderTab
|
||||
content = element;
|
||||
};
|
||||
const remove = (aspect: AspectConfig) => {
|
||||
config.aspects = config.aspects.filter(e => e !== aspect);
|
||||
confirm('Voulez vous vraiment supprimer cet aspect ?').then(e => {
|
||||
if(e)
|
||||
{
|
||||
config.aspects = config.aspects.filter(e => e !== aspect);
|
||||
|
||||
const element = redraw();
|
||||
content.parentElement?.replaceChild(element, content);
|
||||
content = element;
|
||||
const element = redraw();
|
||||
content.parentElement?.replaceChild(element, content);
|
||||
content = element;
|
||||
}
|
||||
})
|
||||
}
|
||||
const redraw = () => table(config.aspects.map(render), { name: 'Nom', description: 'Description', stat: 'Buff de stat', alignment: 'Alignement', magic: 'Magie', difficulty: 'Difficulté', physic: 'Physique', mental: 'Mental', personality: 'Caractère', action: 'Actions' }, { class: { table: 'flex-1' } });
|
||||
let content = redraw();
|
||||
@@ -247,19 +314,15 @@ class SpellEditor extends BuilderTab
|
||||
super(builder, config);
|
||||
|
||||
const render = (spell: SpellConfig) => {
|
||||
return {
|
||||
id: spell.id,
|
||||
name: input('text', { input: (value) => spell.name = value, defaultValue: spell.name, class: '!m-0 w-full' }),
|
||||
rank: select([{ text: 'Rang 1', value: 1 }, { text: 'Rang 2', value: 2 }, { text: 'Rang 3', value: 3 }, { text: 'Spécial', value: 4 }], { change: (value: 1 | 2 | 3 | 4) => spell.rank = value, defaultValue: spell.rank, class: { container: '!m-0 w-full' } }),
|
||||
type: select(SPELL_TYPES.map(f => ({ text: spellTypeTexts[f], value: f })), { change: (value) => spell.type = value, defaultValue: spell.type, class: { container: '!m-0 w-full' } }),
|
||||
cost: numberpicker({ defaultValue: spell.cost, input: (value) => spell.cost = value, class: '!m-0 w-full' }),
|
||||
speed: select<'action' | 'reaction' | number>([{ text: 'Action', value: 'action' }, { text: 'Reaction', value: 'reaction' }, { text: '1 minute', value: 1 }, { text: '10 minutes', value: 10 }], { change: (value) => spell.speed = value, defaultValue: spell.speed, class: { container: '!m-0 w-full' } }),
|
||||
elements: multiselect(SPELL_ELEMENTS.map(f => ({ text: elementTexts[f].text, value: f })), { change: (value) => spell.elements = value, defaultValue: spell.elements, class: { container: '!m-0 w-full' } }),
|
||||
effect: input('text', { input: (value) => spell.effect = value, defaultValue: spell.effect, class: '!m-0 w-full' }),
|
||||
tags: multiselect([{ text: 'Dégâts', value: 'damage' }, { text: 'Buff', value: 'buff' }, { text: 'Debuff', value: 'debuff' }, { text: 'Support', value: 'support' }, { text: 'Tank', value: 'tank' }, { text: 'Mouvement', value: 'movement' }, { text: 'Utilitaire', value: 'utilitary' }], { change: (value) => spell.tags = value, defaultValue: spell.tags, class: { container: '!m-0 w-full' } }),
|
||||
concentration: toggle({ change: (value) => spell.concentration = value, defaultValue: spell.concentration, class: { container: '!m-0' } }),
|
||||
action: div('flex flex-row justify-center gap-2', [ button(icon('radix-icons:trash'), () => remove(spell), 'p-1') ])
|
||||
};
|
||||
return foldable([
|
||||
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Rang'), select([{ text: 'Rang 1', value: 1 }, { text: 'Rang 2', value: 2 }, { text: 'Rang 3', value: 3 }, { text: 'Spécial', value: 4 }], { change: (value: 1 | 2 | 3 | 4) => spell.rank = value, defaultValue: spell.rank, class: { container: '!m-0 !h-9 w-full' } }), ]),
|
||||
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Type'), select(SPELL_TYPES.map(f => ({ text: spellTypeTexts[f], value: f })), { change: (value) => spell.type = value, defaultValue: spell.type, class: { container: '!m-0 !h-9 w-full' } }), ]),
|
||||
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Coût'), numberpicker({ defaultValue: spell.cost, input: (value) => spell.cost = value, class: '!m-0 w-full' }), ]),
|
||||
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Incantation'), select<'action' | 'reaction' | number>([{ text: 'Action', value: 'action' }, { text: 'Reaction', value: 'reaction' }, { text: '1 minute', value: 1 }, { text: '10 minutes', value: 10 }], { change: (value) => spell.speed = value, defaultValue: spell.speed, class: { container: '!m-0 !h-9 w-full' } }), ]),
|
||||
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Elements'), multiselect(SPELL_ELEMENTS.map(f => ({ text: elementTexts[f].text, value: f })), { change: (value) => spell.elements = value, defaultValue: spell.elements, class: { container: '!m-0 !h-9 w-full' } }), ]),
|
||||
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Tags'), multiselect([{ text: 'Dégâts', value: 'damage' }, { text: 'Buff', value: 'buff' }, { text: 'Debuff', value: 'debuff' }, { text: 'Support', value: 'support' }, { text: 'Tank', value: 'tank' }, { text: 'Mouvement', value: 'movement' }, { text: 'Utilitaire', value: 'utilitary' }], { change: (value) => spell.tags = value, defaultValue: spell.tags, class: { container: '!m-0 !h-9 w-full' } }), ]),
|
||||
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Concentration'), toggle({ change: (value) => spell.concentration = value, defaultValue: spell.concentration, class: { container: '!m-0 !flex-none' } }), ]),
|
||||
], [ div('gap-4 px-4 flex', [ input('text', { input: (value) => spell.name = value, defaultValue: spell.name, class: '!m-0 w-64' }), input('text', { input: (value) => spell.effect = value, defaultValue: spell.effect, class: '!m-0 w-full' }),div('flex flex-row justify-center gap-2', [ button(icon('radix-icons:trash', { noobserver: true }), () => remove(spell), 'p-1') ]) ]) ], { class: { container: 'border-light-35 dark:border-dark-35 py-1', content: 'gap-2 px-4 py-1 flex items-center *:flex-1' }, open: false });
|
||||
}
|
||||
const add = () => {
|
||||
config.spells.push({
|
||||
@@ -280,13 +343,19 @@ class SpellEditor extends BuilderTab
|
||||
content = element;
|
||||
};
|
||||
const remove = (spell: SpellConfig) => {
|
||||
config.spells = config.spells.filter(e => e !== spell);
|
||||
confirm('Voulez vous vraiment supprimer ce sort ?').then(e => {
|
||||
if(e)
|
||||
{
|
||||
config.spells = config.spells.filter(e => e !== spell);
|
||||
|
||||
const element = redraw();
|
||||
content.parentElement?.replaceChild(element, content);
|
||||
content = element;
|
||||
const element = redraw();
|
||||
content.parentElement?.replaceChild(element, content);
|
||||
content = element;
|
||||
}
|
||||
});
|
||||
}
|
||||
const redraw = () => table(config.spells.map(render), { id: 'ID', name: 'Nom', rank: 'Rang', type: 'Type', cost: 'Coût', speed: 'Incantation', elements: 'Elements', effect: 'Effet', tags: 'Tag', concentration: 'Concentration', action: 'Actions' }, { class: { table: 'flex-1' } });
|
||||
const redraw = () => div('flex flex-col divide-y', config.spells.map(render));
|
||||
//, { class: { table: 'flex-1' } });
|
||||
let content = redraw();
|
||||
this._content = [ div('flex px-8 py-4 flex-col gap-4', [ div('flex flex-row-reverse', [ button(icon('radix-icons:plus'), add, 'p-1') ]), content ] ) ];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user