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 { Tree } from "#shared/tree";
|
||||||
import { getText } from "#shared/i18n";
|
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;
|
const config = characterConfig as CharacterConfig;
|
||||||
export class HomebrewBuilder
|
export class HomebrewBuilder
|
||||||
{
|
{
|
||||||
|
|
@ -19,13 +34,12 @@ export class HomebrewBuilder
|
||||||
|
|
||||||
private _config: CharacterConfig;
|
private _config: CharacterConfig;
|
||||||
private _featureEditor: FeatureEditor;
|
private _featureEditor: FeatureEditor;
|
||||||
private _itemEditor: ItemEditor;
|
|
||||||
|
|
||||||
constructor(container: HTMLDivElement)
|
constructor(container: HTMLDivElement)
|
||||||
{
|
{
|
||||||
this._config = config as CharacterConfig;
|
this._config = config as CharacterConfig;
|
||||||
this._featureEditor = new FeatureEditor();
|
this._featureEditor = new FeatureEditor();
|
||||||
this._itemEditor = new ItemEditor();
|
ItemEditor.config = this._config;
|
||||||
this._container = container;
|
this._container = container;
|
||||||
|
|
||||||
this._tabs = tabgroup([
|
this._tabs = tabgroup([
|
||||||
|
|
@ -317,8 +331,8 @@ export class HomebrewBuilder
|
||||||
editing = { id, type };
|
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'), () => {
|
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]!.default = editor.content;
|
||||||
this._config.texts[feature.description]['fr_FR'] = editor.content;
|
this._config.texts[feature.description]!['fr_FR'] = editor.content;
|
||||||
const rerender = render(type, feature);
|
const rerender = render(type, feature);
|
||||||
|
|
||||||
option!.buttons.replaceWith(rerender.buttons);
|
option!.buttons.replaceWith(rerender.buttons);
|
||||||
|
|
@ -357,21 +371,6 @@ export class HomebrewBuilder
|
||||||
}
|
}
|
||||||
items()
|
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 defaultItem = (category: Category): ItemConfig => {
|
||||||
const common: CommonItemConfig = {
|
const common: CommonItemConfig = {
|
||||||
id: getID(),
|
id: getID(),
|
||||||
|
|
@ -384,19 +383,19 @@ export class HomebrewBuilder
|
||||||
{
|
{
|
||||||
case 'armor':
|
case 'armor':
|
||||||
return { ...common, category: category, health: 0, absorb: { percent: 0, static: 0 }, type: 'light' };
|
return { ...common, category: category, health: 0, absorb: { percent: 0, static: 0 }, type: 'light' };
|
||||||
case 'mundane':
|
|
||||||
return { ...common, category: category };
|
|
||||||
case 'weapon':
|
case 'weapon':
|
||||||
return { ...common, category: category, damage: '0', type: ['classic'] };
|
return { ...common, category: category, damage: '0', type: ['classic'] };
|
||||||
case 'wondrous':
|
case 'wondrous':
|
||||||
|
case 'mundane':
|
||||||
return { ...common, category: category };
|
return { ...common, category: category };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const render = (item: ItemConfig) => {
|
const render = (item: ItemConfig) => {
|
||||||
return {
|
return {
|
||||||
dom: div('flex flex-col gap-2 border border-light-35 dark:border-dark-35 p-1', [
|
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') ]) ])]),
|
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') ]) ])]),
|
||||||
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 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,
|
item,
|
||||||
};
|
};
|
||||||
|
|
@ -423,18 +422,13 @@ export class HomebrewBuilder
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const edit = (item: ItemConfig) => {
|
const edit = (item: ItemConfig) => {
|
||||||
const idx = options.findIndex(e => e.item === item);
|
ItemEditor.edit(item).then(f => {
|
||||||
this._itemEditor.edit(item).then(f => {
|
const idx = options.findIndex(e => e.item === item);
|
||||||
this._config.items[item.id] = f;
|
this._config.items[item.id] = f;
|
||||||
options[idx] = render(f);
|
const element = render(f);
|
||||||
}).catch((e) => {}).finally(() => {
|
options[idx]?.dom.replaceWith(element.dom);
|
||||||
setTimeout(popup.close, 150);
|
options[idx] = element;
|
||||||
this._itemEditor.container.setAttribute('data-state', 'inactive');
|
}).catch((e) => {});
|
||||||
});
|
|
||||||
const popup = fullblocker([this._itemEditor.container], {
|
|
||||||
priority: true, closeWhenOutside: false,
|
|
||||||
});
|
|
||||||
setTimeout(() => this._itemEditor.container.setAttribute('data-state', 'active'), 1);
|
|
||||||
}
|
}
|
||||||
const options = Object.values(this._config.items).map(e => render(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));
|
const optionHolder = div('grid grid-cols-3 gap-2', options.map(e => e.dom));
|
||||||
|
|
@ -688,31 +682,24 @@ export class FeatureEditor
|
||||||
}
|
}
|
||||||
export class ItemEditor
|
export class ItemEditor
|
||||||
{
|
{
|
||||||
private _container: HTMLDivElement;
|
static config: CharacterConfig;
|
||||||
|
static render(item: ItemConfig, success: (item: ItemConfig) => void, failure: (item: ItemConfig) => void)
|
||||||
private _success?: Function;
|
|
||||||
private _failure?: Function;
|
|
||||||
private _item?: ItemConfig;
|
|
||||||
|
|
||||||
private _idInput: HTMLInputElement;
|
|
||||||
private _table: HTMLDivElement;
|
|
||||||
|
|
||||||
constructor()
|
|
||||||
{
|
{
|
||||||
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` });
|
const _item = JSON.parse(JSON.stringify(item)) as ItemConfig;
|
||||||
this._table = div('grid grid-cols-2 gap-4 px-2');
|
MarkdownEditor.singleton.content = getText(_item.description);
|
||||||
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]' }, [
|
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', [
|
div('flex flex-row justify-between items-center', [
|
||||||
tooltip(button(icon('radix-icons:check', { width: 20, height: 20 }), () => {
|
tooltip(button(icon('radix-icons:check', { width: 20, height: 20 }), () => {
|
||||||
this._success!(this._item);
|
success!(_item);
|
||||||
MarkdownEditor.singleton.onChange = undefined;
|
MarkdownEditor.singleton.onChange = undefined;
|
||||||
}, 'p-1'), 'Valider', 'left'),
|
}, 'p-1'), 'Valider', 'left'),
|
||||||
dom('label', { class: 'flex justify-center items-center my-2' }, [
|
dom('label', { class: 'flex justify-center items-center my-2' }, [
|
||||||
dom('span', { class: 'pb-1 md:p-0', text: "ID" }),
|
dom('span', { class: 'pb-1 md:p-0', text: "Nom" }),
|
||||||
this._idInput
|
input('text', { defaultValue: _item.name, input: (v) => _item.name = v })
|
||||||
]),
|
]),
|
||||||
tooltip(button(icon('radix-icons:cross-1', { width: 20, height: 20 }), () => {
|
tooltip(button(icon('radix-icons:cross-1', { width: 20, height: 20 }), () => {
|
||||||
this._failure!(this._item);
|
failure!(item);
|
||||||
MarkdownEditor.singleton.onChange = undefined;
|
MarkdownEditor.singleton.onChange = undefined;
|
||||||
}, 'p-1'), 'Annuler', 'left'),
|
}, 'p-1'), 'Annuler', 'left'),
|
||||||
]),
|
]),
|
||||||
|
|
@ -720,6 +707,17 @@ export class ItemEditor
|
||||||
dom('span', { class: 'pb-1 md:p-0 w-full', text: "Description" }),
|
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 ]),
|
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-col gap-2 w-full', [
|
||||||
div('flex flex-row justify-between', [
|
div('flex flex-row justify-between', [
|
||||||
dom('h3', { class: 'text-lg font-bold', text: 'Effets' }),
|
dom('h3', { class: 'text-lg font-bold', text: 'Effets' }),
|
||||||
|
|
@ -727,22 +725,22 @@ export class ItemEditor
|
||||||
//this._table.appendChild(this._edit({ id: getID() }));
|
//this._table.appendChild(this._edit({ id: getID() }));
|
||||||
}, 'p-1'), 'Ajouter', 'left'),
|
}, '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) => {
|
let container: HTMLElement, close: Function;
|
||||||
this._success = success;
|
return new Promise<ItemConfig>((success, failure) => {
|
||||||
this._failure = failure;
|
container = ItemEditor.render(item, success, failure);
|
||||||
|
close = fullblocker([container], {
|
||||||
this._item = JSON.parse(JSON.stringify(item)) as ItemConfig;
|
priority: true, closeWhenOutside: false,
|
||||||
|
}).close;
|
||||||
//this._table.replaceChildren(...this._item.effect.map(this._renderEffect.bind(this)));
|
setTimeout(() => container.setAttribute('data-state', 'active'), 1);
|
||||||
this._idInput.value = this._item.id;
|
}).finally(() => {
|
||||||
MarkdownEditor.singleton.onChange = (e) => this._item!.description = e;
|
setTimeout(close, 150);
|
||||||
MarkdownEditor.singleton.content = this._item.description;
|
container.setAttribute('data-state', 'inactive');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/* private _renderEffect(effect: Partial<FeatureItem>): HTMLDivElement
|
/* private _renderEffect(effect: Partial<FeatureItem>): HTMLDivElement
|
||||||
|
|
@ -900,10 +898,6 @@ export class ItemEditor
|
||||||
let content = redraw();
|
let content = redraw();
|
||||||
return content;
|
return content;
|
||||||
} */
|
} */
|
||||||
get container()
|
|
||||||
{
|
|
||||||
return this._container;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const featureChoices: Option<Partial<FeatureItem>>[] = [
|
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 { 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 MainStat = typeof MAIN_STATS[number];
|
||||||
export type Ability = typeof ABILITIES[number];
|
export type Ability = typeof ABILITIES[number];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue