Nearly finished FeatureEditor for choices
This commit is contained in:
parent
06276b3fbc
commit
658499749d
BIN
db.sqlite-shm
BIN
db.sqlite-shm
Binary file not shown.
BIN
db.sqlite-wal
BIN
db.sqlite-wal
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -63,18 +63,19 @@ export class HomebrewBuilder
|
||||||
|
|
||||||
this._content?.replaceChildren(...this._tabsContent[tab]!.dom);
|
this._content?.replaceChildren(...this._tabsContent[tab]!.dom);
|
||||||
}
|
}
|
||||||
edit(feature: Feature)
|
edit(feature: Feature): Promise<Feature>
|
||||||
{
|
{
|
||||||
const promise = this._editor.edit(feature).then(f => {
|
const promise: Promise<Feature> = this._editor.edit(feature).then(f => {
|
||||||
this._config.features[feature.id] = f;
|
this._config.features[feature.id] = f;
|
||||||
}).finally(() => {
|
return f;
|
||||||
|
}).catch(() => feature).finally(() => {
|
||||||
setTimeout(popup.close, 150);
|
setTimeout(popup.close, 150);
|
||||||
this._editor.container.setAttribute('data-state', 'inactive');
|
this._editor.container.setAttribute('data-state', 'inactive');
|
||||||
});
|
});
|
||||||
const popup = fullblocker([this._editor.container], {
|
const popup = fullblocker([this._editor.container], {
|
||||||
priority: true, closeWhenOutside: false,
|
priority: true, closeWhenOutside: false,
|
||||||
});
|
});
|
||||||
this._editor.container.setAttribute('data-state', 'active');
|
setTimeout(() => this._editor.container.setAttribute('data-state', 'active'), 1);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
private save()
|
private save()
|
||||||
|
|
@ -133,9 +134,14 @@ class TrainingEditor extends BuilderTab
|
||||||
const statRenderBlock = (stat: MainStat) => {
|
const statRenderBlock = (stat: MainStat) => {
|
||||||
return Object.entries(config.training[stat]).map(
|
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]) ])]),
|
(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) => 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 => {
|
div("flex flex-row gap-4 justify-center", level[1].map((option, j) => {
|
||||||
this._builder.edit(config.features[option]!);
|
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 => {
|
||||||
}}}, [ markdownUtil(config.features[option]!.description, undefined, { tags: { a: fakeA } }) ])))
|
this._builder.edit(config.features[option]!).then(e => {
|
||||||
|
element.replaceChildren(markdownUtil(config.features[option]!.description, undefined, { tags: { a: fakeA } }));
|
||||||
|
});
|
||||||
|
}}}, [ markdownUtil(config.features[option]!.description, undefined, { tags: { a: fakeA } }) ]);
|
||||||
|
return element;
|
||||||
|
})),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -357,48 +363,50 @@ export class FeatureEditor
|
||||||
switch(effect.category)
|
switch(effect.category)
|
||||||
{
|
{
|
||||||
case 'value':
|
case 'value':
|
||||||
return flattenFeatureChoices.find(e => e.category === 'value' && e.property === effect.property);
|
return flattenFeatureChoices.findLast(e => e.category === 'value' && e.property === effect.property);
|
||||||
case 'choice':
|
case 'choice':
|
||||||
return flattenFeatureChoices.find(e => e.category === 'choice');
|
return flattenFeatureChoices.findLast(e => e.category === 'choice');
|
||||||
case 'list':
|
case 'list':
|
||||||
return flattenFeatureChoices.find(e => e.category === 'list' && e.list === effect.list);
|
return flattenFeatureChoices.findLast(e => e.category === 'list' && e.list === effect.list);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const approve = () => {
|
const approve = () => {
|
||||||
const idx = this._feature!.effect.findIndex(e => e.id === buffer.id);
|
const idx = this._feature!.effect.findIndex(e => e.id === _buffer.id);
|
||||||
|
|
||||||
if(idx === -1)
|
if(idx === -1)
|
||||||
this._feature!.effect.push(buffer);
|
this._feature!.effect.push(_buffer);
|
||||||
else
|
else
|
||||||
this._feature!.effect[idx] = buffer;
|
this._feature!.effect[idx] = _buffer;
|
||||||
|
|
||||||
this._table.replaceChild(this._renderEffect(buffer), content);
|
this._table.replaceChild(this._renderEffect(_buffer), content);
|
||||||
}, reject = () => {
|
}, reject = () => {
|
||||||
const idx = this._feature!.effect.findIndex(e => e.id === buffer.id);
|
const idx = this._feature!.effect.findIndex(e => e.id === _buffer.id);
|
||||||
|
|
||||||
if(idx === -1)
|
if(idx === -1)
|
||||||
content.remove();
|
content.remove();
|
||||||
else
|
else
|
||||||
this._table.replaceChild(this._renderEffect(effect), content);
|
this._table.replaceChild(this._renderEffect(effect), content);
|
||||||
}
|
}
|
||||||
let buffer = JSON.parse(JSON.stringify(effect)) as FeatureItem;
|
let _buffer = JSON.parse(JSON.stringify(effect)) as FeatureItem;
|
||||||
|
|
||||||
const drawByCategory = (buffer: Partial<FeatureItem>) => {
|
const drawByCategory = (buffer: Partial<FeatureItem>) => {
|
||||||
let top: NodeChildren = [], bottom: NodeChildren = [];
|
let top: NodeChildren = [], bottom: NodeChildren = [];
|
||||||
switch(buffer.category)
|
switch(buffer.category)
|
||||||
{
|
{
|
||||||
case 'value':
|
case 'value':
|
||||||
|
const valueVariable = () => typeof buffer.value === 'number' ? numberpicker({ defaultValue: buffer.value, input: (value) => { (buffer as Extract<FeatureEffect, { category: "value" }>).value = value; summaryText.textContent = textFromEffect(buffer); }, class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-[80px]' }) : select<`modifier/${MainStat}` | false>([...Object.entries(mainStatShortTexts).map(e => ({ text: 'Mod. de ' + e[1], value: `modifier/${e[0]}` as `modifier/${MainStat}` })), buffer.operation === 'add' ? undefined : { text: 'Interdit', value: false }], { class: { container: 'w-[160px] bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px]' }, defaultValue: buffer.value, change: (value) => { (buffer as Extract<FeatureEffect, { category: "value" }>).value = value; summaryText.textContent = textFromEffect(buffer); } });
|
||||||
const summaryText = text(textFromEffect(buffer));
|
const summaryText = text(textFromEffect(buffer));
|
||||||
|
let valueSelection = valueVariable();
|
||||||
top = [
|
top = [
|
||||||
select([ { text: '+', value: 'add' }, (['speed', 'capacity'].includes(buffer.property ?? '') || ['defense/'].some(e => (buffer as Extract<FeatureEffect, { category: "value" }>).property.startsWith(e))) ? { text: '=', value: 'set' } : undefined ], { defaultValue: buffer.operation, change: (value) => { (buffer as Extract<FeatureEffect, { category: "value" }>).operation = value as 'add' | 'set'; summaryText.textContent = textFromEffect(buffer); }, class: { container: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-[80px]' } }),
|
select([ { text: '+', value: 'add' }, (['speed', 'capacity'].includes(buffer.property ?? '') || ['defense/'].some(e => (buffer as Extract<FeatureEffect, { category: "value" }>).property.startsWith(e))) ? { text: '=', value: 'set' } : undefined ], { defaultValue: buffer.operation, change: (value) => { (buffer as Extract<FeatureEffect, { category: "value" }>).operation = value as 'add' | 'set'; summaryText.textContent = textFromEffect(buffer); }, class: { container: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-[80px]' } }),
|
||||||
typeof buffer.value === 'number' ? numberpicker({ defaultValue: buffer.value, input: (value) => { (buffer as Extract<FeatureEffect, { category: "value" }>).value = value; summaryText.textContent = textFromEffect(buffer); }, class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-[80px]' }) : select<`modifier/${MainStat}` | false>([...Object.entries(mainStatShortTexts).map(e => ({ text: 'Mod. de ' + e[1], value: `modifier/${e[0]}` as `modifier/${MainStat}` })), buffer.operation === 'add' ? undefined : { text: 'Interdit', value: false }], { class: { container: 'w-[160px] bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px]' }, defaultValue: buffer.value, change: (value) => { (buffer as Extract<FeatureEffect, { category: "value" }>).value = value; summaryText.textContent = textFromEffect(buffer); } }),
|
valueSelection,
|
||||||
button(icon('radix-icons:update'), () => {
|
tooltip(button(icon('radix-icons:update'), () => {
|
||||||
(buffer as Extract<FeatureEffect, { category: "value" }>).value = (typeof (buffer as Extract<FeatureEffect, { category: "value" }>).value === 'number' ? '' as any as false : 0);
|
(buffer as Extract<FeatureEffect, { category: "value" }>).value = (typeof (buffer as Extract<FeatureEffect, { category: "value" }>).value === 'number' ? '' as any as false : 0);
|
||||||
const element = redraw();
|
const newValueSelection = valueVariable();
|
||||||
this._table.replaceChild(element, content);
|
valueSelection?.parentElement?.replaceChild(newValueSelection, valueSelection);
|
||||||
content = element;
|
valueSelection = newValueSelection;
|
||||||
summaryText.textContent = textFromEffect(buffer);
|
summaryText.textContent = textFromEffect(buffer);
|
||||||
}, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'),
|
}, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Changer d\'editeur', 'bottom'),
|
||||||
];
|
];
|
||||||
bottom = [ div('px-2 py-1 flex items-center flex-1', [summaryText]) ];
|
bottom = [ div('px-2 py-1 flex items-center flex-1', [summaryText]) ];
|
||||||
break;
|
break;
|
||||||
|
|
@ -424,23 +432,23 @@ export class FeatureEditor
|
||||||
const add = () => {
|
const add = () => {
|
||||||
const option: Extract<FeatureItem, { category: 'choice' }>["options"][number] = { id: getID(ID_SIZE), category: 'value', text: '', operation: 'add', property: '', value: 0 };
|
const option: Extract<FeatureItem, { category: 'choice' }>["options"][number] = { id: getID(ID_SIZE), category: 'value', text: '', operation: 'add', property: '', value: 0 };
|
||||||
(buffer as Extract<FeatureItem, { category: 'choice' }>).options.push(option);
|
(buffer as Extract<FeatureItem, { category: 'choice' }>).options.push(option);
|
||||||
list.appendChild(render(option));
|
list.appendChild(render(option, true));
|
||||||
};
|
};
|
||||||
const remove = (option: FeatureEffect) => {
|
const render = (option: FeatureEffect & { text: string }, state: boolean): HTMLElement => {
|
||||||
|
|
||||||
};
|
|
||||||
const render = (option: FeatureEffect): HTMLElement => {
|
|
||||||
const { top: _top, bottom: _bottom } = drawByCategory(option);
|
const { top: _top, bottom: _bottom } = drawByCategory(option);
|
||||||
const combo = combobox(featureChoices.filter(e => (e?.value as FeatureItem)?.category !== 'choice'), { defaultValue: match(option), class: { container: 'bg-light-25 dark:bg-dark-25 w-[300px] -m-px hover:z-10 h-[36px]' }, change: (e) => {
|
const combo = combobox([...featureChoices].filter(e => (e?.value as FeatureItem)?.category !== 'choice').map(e => { if(e) e.value = Array.isArray(e.value) ? e.value.filter(f => (f?.value as FeatureItem)?.category !== 'choice') : e.value; return e; }), { defaultValue: match(option), class: { container: 'bg-light-25 dark:bg-dark-25 w-[300px] -m-px hover:z-10 h-[36px]' }, change: (e) => {
|
||||||
option = { id: option.id, ...e } as FeatureEffect;
|
option = { id: option.id, ...e } as FeatureEffect & { text: string };
|
||||||
const element = render(option);
|
const element = render(option, true);
|
||||||
_content?.parentElement?.replaceChild(element, _content);
|
_content?.parentElement?.replaceChild(element, _content);
|
||||||
_content = element;
|
_content = element;
|
||||||
} });
|
} });
|
||||||
let _content: HTMLElement = foldable(_bottom, [ div('flex flex-1 justify-between', [ div('flex flex-row',[ combo, ..._top ]), tooltip(button(icon('radix-icons:trash'), () => remove(option), 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Supprimer', 'bottom') ]) ], { class: { title: 'border-b border-light-35 dark:border-dark-35', icon: 'w-[34px] h-[34px]', content: 'border-b border-light-35 dark:border-dark-35' } });
|
let _content: HTMLElement = foldable(_bottom, [ div('flex flex-1 justify-between', [ div('flex flex-1 flex-row',[ combo, ..._top, input('text', { defaultValue: option.text, input: (value) => option.text = value, class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] flex-shrink-1', placeholder: 'Description' }) ]), tooltip(button(icon('radix-icons:trash'), () => {
|
||||||
|
_content.remove();
|
||||||
|
(buffer as Extract<FeatureItem, { category: 'choice' }>).options = (buffer as Extract<FeatureItem, { category: 'choice' }>).options.filter(e => e.id !== option.id);
|
||||||
|
}, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Supprimer', 'bottom') ]) ], { class: { title: 'border-b border-light-35 dark:border-dark-35', icon: 'w-[34px] h-[34px]', content: 'border-b border-light-35 dark:border-dark-35' }, open: state });
|
||||||
return _content;
|
return _content;
|
||||||
}
|
}
|
||||||
const list = div('flex flex-col flex-1 divide-y divide-light-35 dark:divide-dark-35 gap-2', buffer.options?.map(e => render(e)) ?? []);
|
const list = div('flex flex-col flex-1 divide-y divide-light-35 dark:divide-dark-35 gap-2', buffer.options?.map(e => render(e, false)) ?? []);
|
||||||
top = [ input('text', { defaultValue: buffer.text, input: (value) => (buffer as Extract<FeatureItem, { category: 'choice' }>).text = value, class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-full', placeholder: 'Description' }), tooltip(button(icon('radix-icons:plus'), () => add(), 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Ajouter une option', 'bottom') ];
|
top = [ input('text', { defaultValue: buffer.text, input: (value) => (buffer as Extract<FeatureItem, { category: 'choice' }>).text = value, class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-full', placeholder: 'Description' }), tooltip(button(icon('radix-icons:plus'), () => add(), 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Ajouter une option', 'bottom') ];
|
||||||
bottom = [ list ];
|
bottom = [ list ];
|
||||||
break;
|
break;
|
||||||
|
|
@ -449,13 +457,13 @@ export class FeatureEditor
|
||||||
return { top, bottom };
|
return { top, bottom };
|
||||||
}
|
}
|
||||||
const redraw = () => {
|
const redraw = () => {
|
||||||
const { top, bottom } = drawByCategory(buffer);
|
const { top, bottom } = drawByCategory(_buffer);
|
||||||
return div('border border-light-30 dark:border-dark-30 col-span-2 row-span-2', [ div('flex justify-between items-stretch', [
|
return div('border border-light-30 dark:border-dark-30 col-span-2 row-span-2', [ div('flex justify-between items-stretch', [
|
||||||
div('flex flex-row flex-1', [
|
div('flex flex-row flex-1', [
|
||||||
combobox(featureChoices, { defaultValue: match(buffer), class: { container: 'bg-light-25 dark:bg-dark-25 w-[300px] -m-px hover:z-10 h-[36px]' }, change: (e) => {
|
combobox(featureChoices, { defaultValue: match(_buffer), class: { container: 'bg-light-25 dark:bg-dark-25 w-[300px] -m-px hover:z-10 h-[36px]' }, change: (e) => {
|
||||||
buffer = { id: buffer.id, ...e } as FeatureItem;
|
_buffer = { id: _buffer.id, ...e } as FeatureItem;
|
||||||
const element = redraw();
|
const element = redraw();
|
||||||
this._table.replaceChild(element, content);
|
content?.parentElement?.replaceChild(element, content);
|
||||||
content = element;
|
content = element;
|
||||||
} }),
|
} }),
|
||||||
...top,
|
...top,
|
||||||
|
|
@ -511,7 +519,36 @@ const featureChoices: Option<Partial<FeatureItem>>[] = [
|
||||||
{ text: 'Modifier d\'intelligence', value: { category: 'value', property: 'modifier/intelligence', operation: 'add', value: 0 } },
|
{ text: 'Modifier d\'intelligence', value: { category: 'value', property: 'modifier/intelligence', operation: 'add', value: 0 } },
|
||||||
{ text: 'Modifier de curiosité', value: { category: 'value', property: 'modifier/curiosity', operation: 'add', value: 0 } },
|
{ text: 'Modifier de curiosité', value: { category: 'value', property: 'modifier/curiosity', operation: 'add', value: 0 } },
|
||||||
{ text: 'Modifier de charisme', value: { category: 'value', property: 'modifier/charisma', operation: 'add', value: 0 } },
|
{ text: 'Modifier de charisme', value: { category: 'value', property: 'modifier/charisma', operation: 'add', value: 0 } },
|
||||||
{ text: 'Modifier de psyché', value: { category: 'value', property: 'modifier/psyche', operation: 'add', value: 0 } }
|
{ text: 'Modifier de psyché', value: { category: 'value', property: 'modifier/psyche', operation: 'add', value: 0 } },
|
||||||
|
//@ts-ignore
|
||||||
|
{ text: 'Modifier au choix', value: { category: 'choice', text: '+1 au modifier de ', options: [
|
||||||
|
{ text: 'Modifier de force', category: 'value', property: 'modifier/strength', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Modifier de dextérité', category: 'value', property: 'modifier/dexterity', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Modifier de constitution', category: 'value', property: 'modifier/constitution', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Modifier d\'intelligence', category: 'value', property: 'modifier/intelligence', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Modifier de curiosité', category: 'value', property: 'modifier/curiosity', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Modifier de charisme', category: 'value', property: 'modifier/charisma', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Modifier de psyché', egory: 'value', property: 'modifier/psyche', operation: 'add', value: 1 }
|
||||||
|
]}}
|
||||||
|
] },
|
||||||
|
{ text: 'Jet de résistance', value: [
|
||||||
|
{ text: 'Force', value: { category: 'value', property: 'bonus/defense/strength', operation: 'add', value: 0 } },
|
||||||
|
{ text: 'Dextérité', value: { category: 'value', property: 'bonus/defense/dexterity', operation: 'add', value: 0 } },
|
||||||
|
{ text: 'Constitution', value: { category: 'value', property: 'bonus/defense/constitution', operation: 'add', value: 0 } },
|
||||||
|
{ text: 'Intelligence', value: { category: 'value', property: 'bonus/defense/intelligence', operation: 'add', value: 0 } },
|
||||||
|
{ text: 'Curiosité', value: { category: 'value', property: 'bonus/defense/curiosity', operation: 'add', value: 0 } },
|
||||||
|
{ text: 'Charisme', value: { category: 'value', property: 'bonus/defense/charisma', operation: 'add', value: 0 } },
|
||||||
|
{ text: 'Psyché', value: { category: 'value', property: 'bonus/defense/psyche', operation: 'add', value: 0 } },
|
||||||
|
//@ts-ignore
|
||||||
|
{ text: 'Résistance au choix', value: { category: 'choice', text: '+1 au jet de résistance de ', options: [
|
||||||
|
{ text: 'Force', category: 'value', property: 'bonus/defense/strength', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Dextérité', category: 'value', property: 'bonus/defense/dexterity', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Constitution', category: 'value', property: 'bonus/defense/constitution', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Intelligence', category: 'value', property: 'bonus/defense/intelligence', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Curiosité', category: 'value', property: 'bonus/defense/curiosity', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Charisme', category: 'value', property: 'bonus/defense/charisma', operation: 'add', value: 1 },
|
||||||
|
{ text: 'Psyché', egory: 'value', property: 'bonus/defense/psyche', operation: 'add', value: 1 }
|
||||||
|
]}}
|
||||||
] },
|
] },
|
||||||
{ text: 'Action', value: { category: 'list', list: 'action', action: 'add' }, },
|
{ text: 'Action', value: { category: 'list', list: 'action', action: 'add' }, },
|
||||||
{ text: 'Réaction', value: { category: 'list', list: 'reaction', action: 'add' }, },
|
{ text: 'Réaction', value: { category: 'list', list: 'reaction', action: 'add' }, },
|
||||||
|
|
@ -623,7 +660,7 @@ function textFromEffect(effect: Partial<FeatureItem>): string
|
||||||
}
|
}
|
||||||
else if(effect.category === 'choice')
|
else if(effect.category === 'choice')
|
||||||
{
|
{
|
||||||
return `${effect.text} (${effect.options?.length ?? 0} options)`;
|
return `${effect.text} (${effect.options?.length ?? 0} options).`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Inconnu`;
|
return `Inconnu`;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue