Persistant item/spell panel to avoid filing the reactive tracker.
This commit is contained in:
parent
78a101b79d
commit
e9ffdd58a5
|
|
@ -3,7 +3,7 @@ import { z } from "zod/v4";
|
|||
import characterConfig from '#shared/character-config.json';
|
||||
import proses, { preview } from "#shared/proses";
|
||||
import { button, buttongroup, checkbox, floater, foldable, input, loading, multiselect, numberpicker, select, tabgroup, Toaster, toggle } from "#shared/components.util";
|
||||
import { div, dom, icon, span, text, reactive, type DOMList, type RedrawableHTML } from "#shared/dom.util";
|
||||
import { div, dom, icon, span, text, type RedrawableHTML } from "#shared/dom.util";
|
||||
import { followermenu, fullblocker, tooltip } from "#shared/floating.util";
|
||||
import { clamp, deepEquals } from "#shared/general.util";
|
||||
import markdown from "#shared/markdown.util";
|
||||
|
|
@ -11,7 +11,7 @@ import { getText } from "#shared/i18n";
|
|||
import type { User } from "~/types/auth";
|
||||
import { MarkdownEditor } from "#shared/editor.util";
|
||||
import { Socket } from "#shared/websocket.util";
|
||||
import { raw, reactive, reactivity, type Reactive } from '#shared/reactive';
|
||||
import { raw, reactive } from '#shared/reactive';
|
||||
|
||||
const config = characterConfig as CharacterConfig;
|
||||
|
||||
|
|
@ -286,7 +286,6 @@ export class CharacterCompiler
|
|||
'modifier/charisma': { value: 0, _dirty: false, min: -Infinity, list: [] },
|
||||
'modifier/psyche': { value: 0, _dirty: false, min: -Infinity, list: [] },
|
||||
};
|
||||
private _variableDirty: boolean = false;
|
||||
private _variableDebounce: NodeJS.Timeout = setTimeout(() => {});
|
||||
|
||||
constructor(character: Character)
|
||||
|
|
@ -307,11 +306,6 @@ export class CharacterCompiler
|
|||
'modifier/charisma': { value: 0, _dirty: false, min: -Infinity, list: [] },
|
||||
'modifier/psyche': { value: 0, _dirty: false, min: -Infinity, list: [] },
|
||||
};
|
||||
reactivity(() => this.character.variables, () => {
|
||||
console.log("Saving variables");
|
||||
clearTimeout(this._variableDebounce);
|
||||
this._variableDebounce = setTimeout(() => this.saveVariables(), 2000);
|
||||
})
|
||||
|
||||
if(value.people !== undefined)
|
||||
{
|
||||
|
|
@ -350,7 +344,7 @@ export class CharacterCompiler
|
|||
get armor()
|
||||
{
|
||||
const armors = this._character.variables.items.filter(e => e.equipped && config.items[e.id]?.category === 'armor');
|
||||
return armors.length > 0 ? armors.map(e => ({ max: (config.items[e.id] as ArmorConfig).health, current: (config.items[e.id] as ArmorConfig).health - e.state.health })).reduce((p, v) => { p.max += v.max; p.current += v.current; return p; }, { max: 0, current: 0 }) : undefined;
|
||||
return armors.length > 0 ? armors.map(e => ({ max: (config.items[e.id] as ArmorConfig).health, current: (config.items[e.id] as ArmorConfig).health - e.state })).reduce((p, v) => { p.max += v.max; p.current += v.current; return p; }, { max: 0, current: 0 }) : undefined;
|
||||
}
|
||||
|
||||
parse(text: string): string
|
||||
|
|
@ -362,12 +356,15 @@ export class CharacterCompiler
|
|||
}
|
||||
saveVariables()
|
||||
{
|
||||
clearTimeout(this._variableDebounce);
|
||||
this._variableDebounce = setTimeout(() => {
|
||||
useRequestFetch()(`/api/character/${this.character.id}/variables`, {
|
||||
method: 'POST',
|
||||
body: raw(this._character.variables),
|
||||
}).then(() => {}).catch(() => {
|
||||
Toaster.add({ type: 'error', content: 'Impossible de mettre à jour les données', duration: 5000, timer: true });
|
||||
})
|
||||
}, 2000);
|
||||
}
|
||||
saveNotes()
|
||||
{
|
||||
|
|
@ -1284,6 +1281,17 @@ const subnameFactory = (item: ItemConfig, state?: ItemState): string[] => {
|
|||
|
||||
return result;
|
||||
}
|
||||
const stateFactory = (item: ItemConfig) => {
|
||||
const state = { id: item.id, amount: 1, charges: item.charge, enchantments: [], equipped: item.equippable ? false : undefined } as ItemState;
|
||||
switch(item.category)
|
||||
{
|
||||
case 'armor':
|
||||
state.state = 0;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
export class CharacterSheet
|
||||
{
|
||||
private user: ComputedRef<User | null>;
|
||||
|
|
@ -1661,6 +1669,8 @@ export class CharacterSheet
|
|||
}
|
||||
};
|
||||
|
||||
const panel = this.spellPanel(character);
|
||||
|
||||
return [
|
||||
div('flex flex-col gap-2', [
|
||||
div('flex flex-row justify-between items-center', [
|
||||
|
|
@ -1670,7 +1680,7 @@ export class CharacterSheet
|
|||
]),
|
||||
div('flex flex-row gap-2 items-center', [
|
||||
dom('span', { class: () => ['italic text-sm', { 'text-light-red dark:text-dark-red': character.variables.spells.length + (character.lists.spells?.length ?? 0) !== character.spellslots }], text: () => `${character.variables.spells.length + (character.lists.spells?.length ?? 0)}/${character.spellslots} sort(s) maitrisé(s)`.replaceAll('(s)', character.variables.spells.length + (character.lists.spells?.length ?? 0) > 1 ? 's' : '') }),
|
||||
button(text('Modifier'), () => this.spellPanel(character), 'py-1 px-4'),
|
||||
button(text('Modifier'), () => panel.show(), 'py-1 px-4'),
|
||||
])
|
||||
]),
|
||||
div('flex flex-col gap-2', { render: e => {
|
||||
|
|
@ -1736,13 +1746,22 @@ export class CharacterSheet
|
|||
const idx = spells.findIndex(e => e === spell.id);
|
||||
if(idx !== -1) spells.splice(idx, 1);
|
||||
else spells.push(spell.id);
|
||||
|
||||
this.character?.saveVariables();
|
||||
}, "px-2 py-1 text-sm font-normal"),
|
||||
]),
|
||||
]) ], { open: false, class: { container: "px-2 flex flex-col border-light-35 dark:border-dark-35", content: 'py-2' } })
|
||||
})
|
||||
]);
|
||||
const blocker = fullblocker([ container ], { closeWhenOutside: true, onClose: () => this.character?.saveVariables() });
|
||||
const blocker = fullblocker([ container ], { closeWhenOutside: true, open: false });
|
||||
|
||||
return { show: () => {
|
||||
setTimeout(() => container.setAttribute('data-state', 'active'), 1);
|
||||
blocker.open();
|
||||
}, hide: () => {
|
||||
setTimeout(blocker.close, 150);
|
||||
container.setAttribute('data-state', 'inactive');
|
||||
}};
|
||||
}
|
||||
itemsTab(character: CompiledCharacter)
|
||||
{
|
||||
|
|
@ -1750,12 +1769,14 @@ export class CharacterSheet
|
|||
const power = () => items.filter(e => config.items[e.id]?.equippable && e.equipped).reduce((p, v) => p + ((config.items[v.id]?.powercost ?? 0) + (v.enchantments?.reduce((_p, _v) => (config.enchantments[_v]?.power ?? 0) + _p, 0) ?? 0) * v.amount), 0);
|
||||
const weight = () => items.reduce((p, v) => p + (config.items[v.id]?.weight ?? 0) * v.amount, 0);
|
||||
|
||||
const panel = this.itemsPanel(character);
|
||||
|
||||
return [
|
||||
div('flex flex-col gap-2', [
|
||||
div('flex flex-row justify-end items-center gap-8', [
|
||||
dom('span', { class: () => ['italic text-sm', { 'text-light-red dark:text-dark-red': weight() > character.itempower }], text: () => `Poids total: ${weight()}/${character.itempower}` }),
|
||||
dom('span', { class: () => ['italic text-sm', { 'text-light-red dark:text-dark-red': power() > (character.capacity === false ? 0 : character.capacity) }], text: () => `Puissance magique: ${power()}/${character.capacity}` }),
|
||||
button(text('Modifier'), () => this.itemsPanel(character), 'py-1 px-4'),
|
||||
button(text('Modifier'), () => panel.show(), 'py-1 px-4'),
|
||||
]),
|
||||
div('flex flex-col flex-1 divide-y divide-light-35 dark:divide-dark-35', { list: character.variables.items, render: e => {
|
||||
const item = config.items[e.id];
|
||||
|
|
@ -1776,19 +1797,25 @@ export class CharacterSheet
|
|||
|
||||
items[idx]!.amount--;
|
||||
if(items[idx]!.amount <= 0) items.splice(idx, 1);
|
||||
|
||||
this.character?.saveVariables();
|
||||
}, 'p-1'),
|
||||
button(icon('radix-icons:plus', { width: 12, height: 12 }), () => {
|
||||
const idx = items.findIndex(_e => _e === e);
|
||||
if(idx === -1) return;
|
||||
|
||||
if(item.equippable) items.push({ id: item.id, amount: 1, charges: item.charge, enchantments: [], equipped: false });
|
||||
if(item.equippable) items.push(stateFactory(item));
|
||||
else if(items.find(_e => _e === e)) items.find(_e => _e === e)!.amount++;
|
||||
else items.push({ id: item.id, amount: 1, charges: item.charge, enchantments: [] });
|
||||
else items.push(stateFactory(item));
|
||||
|
||||
this.character?.saveVariables();
|
||||
}, 'p-1'),
|
||||
]) ], [div('flex flex-row justify-between', [
|
||||
div('flex flex-row items-center gap-4', [
|
||||
item.equippable ? checkbox({ defaultValue: e.equipped, change: v => {
|
||||
e.equipped = v;
|
||||
|
||||
this.character?.saveVariables();
|
||||
}, class: { container: '!w-5 !h-5' } }) : undefined,
|
||||
div('flex flex-row items-center gap-4', [ span([colorByRarity[item.rarity], 'text-lg'], item.name), div('flex flex-row gap-2 text-light-60 dark:text-dark-60 text-sm italic', subnameFactory(item).map(e => span('', e))) ]),
|
||||
]),
|
||||
|
|
@ -1847,15 +1874,24 @@ export class CharacterSheet
|
|||
div('flex flex-row w-16 gap-2 justify-between items-center px-2', [ icon('ph:coin', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }), span('', item.price ? `${item.price}` : '-') ]),
|
||||
button(icon('radix-icons:plus', { width: 16, height: 16 }), () => {
|
||||
const list = this.character!.character.variables.items;
|
||||
if(item.equippable) list.push({ id: item.id, amount: 1, charges: item.charge, enchantments: [], equipped: false });
|
||||
if(item.equippable) list.push(stateFactory(item));
|
||||
else if(list.find(e => e.id === item.id)) list.find(e => e.id === item.id)!.amount++;
|
||||
else list.push({ id: item.id, amount: 1, charges: item.charge, enchantments: [] });
|
||||
else list.push(stateFactory(item));
|
||||
|
||||
this.character?.saveVariables();
|
||||
}, 'p-1 !border-solid !border-r'),
|
||||
]),
|
||||
])], { open: false, class: { icon: 'px-2', container: 'border border-light-35 dark:border-dark-35 p-1 gap-2', content: 'px-2 pb-1' } })
|
||||
} }),
|
||||
]);
|
||||
const blocker = fullblocker([ container ], { closeWhenOutside: true, onClose: () => this.character?.saveVariables() });
|
||||
const blocker = fullblocker([ container ], { closeWhenOutside: true, open: false });
|
||||
|
||||
return { show: () => {
|
||||
setTimeout(() => container.setAttribute('data-state', 'active'), 1);
|
||||
blocker.open();
|
||||
}, hide: () => {
|
||||
setTimeout(blocker.close, 150);
|
||||
container.setAttribute('data-state', 'inactive');
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ export interface PopperProperties extends FloatingProperties
|
|||
export interface ModalProperties
|
||||
{
|
||||
priority?: boolean;
|
||||
open?: boolean;
|
||||
class?: { blocker?: Class, popup?: Class },
|
||||
closeWhenOutside?: boolean;
|
||||
onClose?: () => boolean | void;
|
||||
|
|
@ -390,15 +391,16 @@ export function tooltip(container: RedrawableHTML, txt: string | Text, placement
|
|||
export function fullblocker(content: NodeChildren, properties?: ModalProperties)
|
||||
{
|
||||
if(!content)
|
||||
return { close: () => {} };
|
||||
return { close: () => {}, open: () => {} };
|
||||
|
||||
const close = () => (!properties?.onClose || properties.onClose() !== false) && _modal.remove();
|
||||
const open = () => { _modal.parentElement === null && teleport.appendChild(_modal) };
|
||||
const close = () => { _modal.parentElement !== null && (!properties?.onClose || properties.onClose() !== false) && _modal.remove() };
|
||||
const _modalBlocker = dom('div', { class: [' absolute top-0 left-0 bottom-0 right-0 z-0', { 'bg-light-0 dark:bg-dark-0 opacity-70': properties?.priority ?? false }, properties?.class?.blocker], listeners: { click: properties?.closeWhenOutside ? (close) : undefined } });
|
||||
const _modal = dom('div', { class: ['fixed flex justify-center items-center top-0 left-0 bottom-0 right-0 inset-0 z-40', properties?.class?.blocker] }, [ _modalBlocker, ...content]);
|
||||
|
||||
teleport.appendChild(_modal);
|
||||
(properties?.open ?? true) && open();
|
||||
|
||||
return { close };
|
||||
return { close, open };
|
||||
}
|
||||
export function modal(content: NodeChildren, properties?: ModalProperties & { class?: { container?: Class } })
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue