Progress on ItemEditor interface and rendering
This commit is contained in:
parent
d187957915
commit
48e767944a
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
|
|
@ -11,6 +11,21 @@ import markdown, { markdownReference, renderMDAsText } from "#shared/markdown.ut
|
|||
import { Tree } from "#shared/tree";
|
||||
import { getText } from "#shared/i18n";
|
||||
|
||||
type Category = ItemConfig['category'];
|
||||
type Rarity = ItemConfig['rarity'];
|
||||
const categoryText: Record<Category, string> = {
|
||||
'mundane': 'Objet inerte',
|
||||
'armor': 'Armure',
|
||||
'weapon': 'Arme',
|
||||
'wondrous': 'Objet magique'
|
||||
};
|
||||
const rarityText: Record<Rarity, string> = {
|
||||
'common': 'Commun',
|
||||
'uncommon': 'Peu commun',
|
||||
'rare': 'Rare',
|
||||
'legendary': 'Légendaire'
|
||||
};
|
||||
|
||||
const config = characterConfig as CharacterConfig;
|
||||
export class HomebrewBuilder
|
||||
{
|
||||
|
|
@ -19,13 +34,12 @@ export class HomebrewBuilder
|
|||
|
||||
private _config: CharacterConfig;
|
||||
private _featureEditor: FeatureEditor;
|
||||
private _itemEditor: ItemEditor;
|
||||
|
||||
constructor(container: HTMLDivElement)
|
||||
{
|
||||
this._config = config as CharacterConfig;
|
||||
this._featureEditor = new FeatureEditor();
|
||||
this._itemEditor = new ItemEditor();
|
||||
ItemEditor.config = this._config;
|
||||
this._container = container;
|
||||
|
||||
this._tabs = tabgroup([
|
||||
|
|
@ -317,8 +331,8 @@ export class HomebrewBuilder
|
|||
editing = { id, type };
|
||||
|
||||
const buttons = div('flex flex-row items-center gap-2', [ span('text-sm text-light-70 dark:text-dark-70', type), tooltip(button(icon('radix-icons:check'), () => {
|
||||
this._config.texts[feature.description].default = editor.content;
|
||||
this._config.texts[feature.description]['fr_FR'] = editor.content;
|
||||
this._config.texts[feature.description]!.default = editor.content;
|
||||
this._config.texts[feature.description]!['fr_FR'] = editor.content;
|
||||
const rerender = render(type, feature);
|
||||
|
||||
option!.buttons.replaceWith(rerender.buttons);
|
||||
|
|
@ -357,21 +371,6 @@ export class HomebrewBuilder
|
|||
}
|
||||
items()
|
||||
{
|
||||
type Category = ItemConfig['category'];
|
||||
type Rarity = ItemConfig['rarity'];
|
||||
const categoryText: Record<Category, string> = {
|
||||
'mundane': 'Objet inerte',
|
||||
'armor': 'Armure',
|
||||
'weapon': 'Arme',
|
||||
'wondrous': 'Objet magique'
|
||||
};
|
||||
const rarityText: Record<Rarity, string> = {
|
||||
'common': 'Commun',
|
||||
'uncommon': 'Peu commun',
|
||||
'rare': 'Rare',
|
||||
'legendary': 'Légendaire'
|
||||
};
|
||||
|
||||
const defaultItem = (category: Category): ItemConfig => {
|
||||
const common: CommonItemConfig = {
|
||||
id: getID(),
|
||||
|
|
@ -384,19 +383,19 @@ export class HomebrewBuilder
|
|||
{
|
||||
case 'armor':
|
||||
return { ...common, category: category, health: 0, absorb: { percent: 0, static: 0 }, type: 'light' };
|
||||
case 'mundane':
|
||||
return { ...common, category: category };
|
||||
case 'weapon':
|
||||
return { ...common, category: category, damage: '0', type: ['classic'] };
|
||||
case 'wondrous':
|
||||
case 'mundane':
|
||||
return { ...common, category: category };
|
||||
}
|
||||
};
|
||||
const render = (item: ItemConfig) => {
|
||||
return {
|
||||
dom: div('flex flex-col gap-2 border border-light-35 dark:border-dark-35 p-1', [
|
||||
div('flex flex-row justify-between', [ span('text-xl font-bold px-4', item.name), div('flex flex-row gap-2 items-center', [ div('flex flex-row items-center gap-2', [ span('text-sm text-light-70 dark:text-dark-70', categoryText[item.category]), text('-'), span('text-sm text-light-70 dark:text-dark-70', rarityText[item.rarity]), tooltip(button(icon('radix-icons:pencil-1'), () => edit(item), 'p-1'), 'Modifier', 'top'), tooltip(button(icon('radix-icons:trash'), () => remove(item), 'p-1'), 'Supprimer', 'top') ]) ])]),
|
||||
markdown(getText(item.description), undefined, { tags: { a: preview }, class: 'ms-2 px-2 py-1 border-l-4 border-light-30 dark:border-dark-30' }),
|
||||
div('flex flex-row justify-between', [ span('text-xl font-bold ps-2', item.name), div('flex flex-row gap-2 items-center', [ div('flex flex-row items-center gap-2', [ tooltip(button(icon('radix-icons:pencil-1'), () => edit(item), 'p-1'), 'Modifier', 'top'), tooltip(button(icon('radix-icons:trash'), () => remove(item), 'p-1'), 'Supprimer', 'top') ]) ])]),
|
||||
div('flex flex-row gap-2 px-4 items-center', [ span('text-sm text-light-70 dark:text-dark-70', categoryText[item.category]), text('-'), span('text-sm text-light-70 dark:text-dark-70', rarityText[item.rarity]), ]),
|
||||
markdown(getText(item.description), undefined, { tags: { a: preview }, class: 'px-2 py-1 border-l-4 border-light-30 dark:border-dark-30 h-full' }),
|
||||
]),
|
||||
item,
|
||||
};
|
||||
|
|
@ -423,18 +422,13 @@ export class HomebrewBuilder
|
|||
});
|
||||
};
|
||||
const edit = (item: ItemConfig) => {
|
||||
const idx = options.findIndex(e => e.item === item);
|
||||
this._itemEditor.edit(item).then(f => {
|
||||
ItemEditor.edit(item).then(f => {
|
||||
const idx = options.findIndex(e => e.item === item);
|
||||
this._config.items[item.id] = f;
|
||||
options[idx] = render(f);
|
||||
}).catch((e) => {}).finally(() => {
|
||||
setTimeout(popup.close, 150);
|
||||
this._itemEditor.container.setAttribute('data-state', 'inactive');
|
||||
});
|
||||
const popup = fullblocker([this._itemEditor.container], {
|
||||
priority: true, closeWhenOutside: false,
|
||||
});
|
||||
setTimeout(() => this._itemEditor.container.setAttribute('data-state', 'active'), 1);
|
||||
const element = render(f);
|
||||
options[idx]?.dom.replaceWith(element.dom);
|
||||
options[idx] = element;
|
||||
}).catch((e) => {});
|
||||
}
|
||||
const options = Object.values(this._config.items).map(e => render(e));
|
||||
const optionHolder = div('grid grid-cols-3 gap-2', options.map(e => e.dom));
|
||||
|
|
@ -688,31 +682,24 @@ export class FeatureEditor
|
|||
}
|
||||
export class ItemEditor
|
||||
{
|
||||
private _container: HTMLDivElement;
|
||||
|
||||
private _success?: Function;
|
||||
private _failure?: Function;
|
||||
private _item?: ItemConfig;
|
||||
|
||||
private _idInput: HTMLInputElement;
|
||||
private _table: HTMLDivElement;
|
||||
|
||||
constructor()
|
||||
static config: CharacterConfig;
|
||||
static render(item: ItemConfig, success: (item: ItemConfig) => void, failure: (item: ItemConfig) => void)
|
||||
{
|
||||
this._idInput = dom("input", { attributes: { 'disabled': true }, class: `mx-4 text-light-70 dark:text-dark-70 appearance-none outline-none px-3 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-25 dark:bg-dark-25 border-light-30 dark:border-dark-30` });
|
||||
this._table = div('grid grid-cols-2 gap-4 px-2');
|
||||
this._container = dom('div', { attributes: { 'data-state': 'inactive' }, class: 'border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10 border-l absolute top-0 bottom-0 right-0 w-[10%] data-[state=active]:w-1/2 flex flex-col gap-2 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]' }, [
|
||||
const _item = JSON.parse(JSON.stringify(item)) as ItemConfig;
|
||||
MarkdownEditor.singleton.content = getText(_item.description);
|
||||
MarkdownEditor.singleton.onChange = (value) => ItemEditor.config.texts[_item.description]!.default = value;
|
||||
return dom('div', { attributes: { 'data-state': 'inactive' }, class: 'border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10 border-l absolute top-0 bottom-0 right-0 w-[10%] data-[state=active]:w-1/2 flex flex-col gap-2 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]' }, [
|
||||
div('flex flex-row justify-between items-center', [
|
||||
tooltip(button(icon('radix-icons:check', { width: 20, height: 20 }), () => {
|
||||
this._success!(this._item);
|
||||
success!(_item);
|
||||
MarkdownEditor.singleton.onChange = undefined;
|
||||
}, 'p-1'), 'Valider', 'left'),
|
||||
dom('label', { class: 'flex justify-center items-center my-2' }, [
|
||||
dom('span', { class: 'pb-1 md:p-0', text: "ID" }),
|
||||
this._idInput
|
||||
dom('span', { class: 'pb-1 md:p-0', text: "Nom" }),
|
||||
input('text', { defaultValue: _item.name, input: (v) => _item.name = v })
|
||||
]),
|
||||
tooltip(button(icon('radix-icons:cross-1', { width: 20, height: 20 }), () => {
|
||||
this._failure!(this._item);
|
||||
failure!(item);
|
||||
MarkdownEditor.singleton.onChange = undefined;
|
||||
}, 'p-1'), 'Annuler', 'left'),
|
||||
]),
|
||||
|
|
@ -720,6 +707,17 @@ export class ItemEditor
|
|||
dom('span', { class: 'pb-1 md:p-0 w-full', text: "Description" }),
|
||||
div('p-1 border border-light-40 dark:border-dark-40 w-full bg-light-25 dark:bg-dark-25 min-h-48 max-h-[32rem]', [ MarkdownEditor.singleton.dom ]),
|
||||
]),
|
||||
dom('div', { class: 'flex flex-col justify-start items-start my-2 gap-4' }, [
|
||||
dom('span', { class: 'pb-1 md:p-0 w-full', text: "Propriétés" }),
|
||||
div('flex flex-row gap-4 px-2 py-1 items-center justify-center w-full', [
|
||||
div('flex flex-col gap-2 items-center px-1', [ span('', 'Rareté'), select(Object.keys(rarityText).map(e => ({ text: rarityText[e as Rarity], value: e as Rarity })), { defaultValue: _item.rarity, change: (v) => _item.rarity = v }), ]),
|
||||
div('flex flex-col gap-2 items-center px-1', [ span('', 'Poids'), numberpicker({ defaultValue: _item.weight, input: (v) => _item.weight = v }), ]),
|
||||
div('flex flex-col gap-2 items-center px-1', [ span('', 'Prix'), numberpicker({ defaultValue: _item.price, input: (v) => _item.price = v }), ]),
|
||||
div('flex flex-col gap-2 items-center px-1', [ span('', 'Puissance magique'), numberpicker({ defaultValue: _item.power, input: (v) => _item.power = v }), ]),
|
||||
div('flex flex-col gap-2 items-center px-1', [ span('', 'Charges'), numberpicker({ defaultValue: _item.charge, input: (v) => _item.charge = v }), ]),
|
||||
div('flex flex-col gap-2 items-center px-1', [ span('', 'Equippable ?'), toggle({ defaultValue: _item.equippable, change: (v) => _item.equippable = v }), ]),
|
||||
]),
|
||||
]),
|
||||
div('flex flex-col gap-2 w-full', [
|
||||
div('flex flex-row justify-between', [
|
||||
dom('h3', { class: 'text-lg font-bold', text: 'Effets' }),
|
||||
|
|
@ -727,22 +725,22 @@ export class ItemEditor
|
|||
//this._table.appendChild(this._edit({ id: getID() }));
|
||||
}, 'p-1'), 'Ajouter', 'left'),
|
||||
]),
|
||||
this._table,
|
||||
div('grid grid-cols-2 gap-4 px-2'),
|
||||
])
|
||||
]);
|
||||
])
|
||||
}
|
||||
edit(item: ItemConfig): Promise<ItemConfig>
|
||||
static edit(item: ItemConfig): Promise<ItemConfig>
|
||||
{
|
||||
return new Promise((success, failure) => {
|
||||
this._success = success;
|
||||
this._failure = failure;
|
||||
|
||||
this._item = JSON.parse(JSON.stringify(item)) as ItemConfig;
|
||||
|
||||
//this._table.replaceChildren(...this._item.effect.map(this._renderEffect.bind(this)));
|
||||
this._idInput.value = this._item.id;
|
||||
MarkdownEditor.singleton.onChange = (e) => this._item!.description = e;
|
||||
MarkdownEditor.singleton.content = this._item.description;
|
||||
let container: HTMLElement, close: Function;
|
||||
return new Promise<ItemConfig>((success, failure) => {
|
||||
container = ItemEditor.render(item, success, failure);
|
||||
close = fullblocker([container], {
|
||||
priority: true, closeWhenOutside: false,
|
||||
}).close;
|
||||
setTimeout(() => container.setAttribute('data-state', 'active'), 1);
|
||||
}).finally(() => {
|
||||
setTimeout(close, 150);
|
||||
container.setAttribute('data-state', 'inactive');
|
||||
});
|
||||
}
|
||||
/* private _renderEffect(effect: Partial<FeatureItem>): HTMLDivElement
|
||||
|
|
@ -900,10 +898,6 @@ export class ItemEditor
|
|||
let content = redraw();
|
||||
return content;
|
||||
} */
|
||||
get container()
|
||||
{
|
||||
return this._container;
|
||||
}
|
||||
}
|
||||
|
||||
const featureChoices: Option<Partial<FeatureItem>>[] = [
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { MAIN_STATS, ABILITIES, LEVELS, TRAINING_LEVELS, SPELL_TYPES, CATEGORIES, SPELL_ELEMENTS, ALIGNMENTS, RESISTANCES } from "#shared/character.util";
|
||||
import type { Localized } from "#shared/general";
|
||||
import type { Localized } from "../types/general";
|
||||
|
||||
export type MainStat = typeof MAIN_STATS[number];
|
||||
export type Ability = typeof ABILITIES[number];
|
||||
|
|
|
|||
Loading…
Reference in New Issue