You've already forked obsidian-visualiser
Fix UI for mobile render
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
<NuxtRouteAnnouncer/>
|
<NuxtRouteAnnouncer/>
|
||||||
<NuxtLoadingIndicator :throttle="50"/>
|
<NuxtLoadingIndicator :throttle="50"/>
|
||||||
<NuxtLayout>
|
<NuxtLayout>
|
||||||
<div class="xl:px-12 xl:py-8 lg:px-8 lg:py-6 px-6 py-3 flex flex-1 justify-center overflow-auto max-h-full max-w-full relative @container/main" id="mainContainer">
|
<div class="xl:px-12 xl:py-8 lg:px-8 lg:py-6 md:px-6 py-3 px-3 flex flex-1 justify-center overflow-auto max-h-full max-w-full relative @container/main box-border" id="mainContainer">
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
</div>
|
</div>
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-row w-full max-w-full h-full max-h-full group/sidebar" :data-active="open || undefined" style="--sidebar-width: 300px">
|
<div class="flex flex-row w-full h-full group/sidebar" :data-active="open || undefined">
|
||||||
<div class="bg-light-0 dark:bg-dark-0 transition-[width] w-0 group-data-[active]/sidebar:w-[var(--sidebar-width)] overflow-hidden border-r border-light-30 dark:border-dark-30 flex flex-col gap-2 absolute top-0 bottom-0 left-0">
|
<div v-if="open" class="fixed inset-0 bg-black/30 z-[5] xl:hidden" @click="open = false" />
|
||||||
<span class="w-full h-14"></span>
|
<div class="bg-light-0 dark:bg-dark-0 z-10 transition-[width] w-0 group-data-[active]/sidebar:w-full group-data-[active]/sidebar:md:w-64 overflow-hidden border-r border-light-30 dark:border-dark-30 flex flex-col gap-2 absolute xl:fixed top-0 bottom-0 left-0" @click.capture="onSidebarClick">
|
||||||
<div class="flex-1 px-2 max-w-full max-h-full overflow-y-auto overflow-x-hidden" ref="treeParent"></div>
|
<span class="w-full h-14"></span>
|
||||||
<div class="flex flex-col my-4 items-center justify-center gap-1 text-xs text-light-60 dark:text-dark-60">
|
<div class="flex-1 px-2 max-w-full max-h-full overflow-y-auto overflow-x-hidden" ref="treeParent"></div>
|
||||||
<NuxtLink class="hover:underline" :to="{ name: 'legal' }">Mentions légales</NuxtLink>
|
<div class="flex flex-col my-4 items-center justify-center gap-1 text-xs text-light-60 dark:text-dark-60">
|
||||||
<NuxtLink class="hover:underline" :to="{ name: 'usage' }">Conditions d'utilisations</NuxtLink>
|
<NuxtLink class="hover:underline" :to="{ name: 'legal' }">Mentions légales</NuxtLink>
|
||||||
Copyright Peaceultime - 2025
|
<NuxtLink class="hover:underline" :to="{ name: 'usage' }">Conditions d'utilisations</NuxtLink>
|
||||||
</div>
|
Copyright Peaceultime - 2025
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col flex-1 h-full w-full">
|
</div>
|
||||||
<div class="flex flex-row w-full border-b border-light-30 dark:border-dark-30">
|
<div class="flex flex-col flex-1 h-full w-full">
|
||||||
<div class="w-[var(--sidebar-width)] z-40 flex flex-row gap-4 items-center justify-between px-4">
|
<div class="flex flex-row w-full border-b border-light-30 dark:border-dark-30">
|
||||||
|
<div class="z-40 flex flex-row gap-4 items-center justify-between px-4 w-64">
|
||||||
<div class="inline-flex justify-center items-center outline-hidden leading-none transition-[box-shadow]
|
<div class="inline-flex justify-center items-center outline-hidden leading-none transition-[box-shadow]
|
||||||
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
|
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
|
||||||
hover:bg-light-25 hover:dark:bg-dark-25 hover:border-light-50 hover:dark:border-dark-50
|
hover:bg-light-25 hover:dark:bg-dark-25 hover:border-light-50 hover:dark:border-dark-50
|
||||||
@@ -19,7 +20,7 @@
|
|||||||
<NuxtLink class="flex flex-row items-center justify-center group gap-2 my-2" aria-label="Accueil" :to="{ name: 'index', force: true }">
|
<NuxtLink class="flex flex-row items-center justify-center group gap-2 my-2" aria-label="Accueil" :to="{ name: 'index', force: true }">
|
||||||
<Avatar src="/logo.dark.svg" class="dark:block hidden" />
|
<Avatar src="/logo.dark.svg" class="dark:block hidden" />
|
||||||
<Avatar src="/logo.light.svg" class="block dark:hidden" />
|
<Avatar src="/logo.light.svg" class="block dark:hidden" />
|
||||||
<span class="text-xl font-semibold group-hover:text-light-70 dark:group-hover:text-dark-70">d[any]</span>
|
<span class="hidden md:inline text-xl font-semibold group-hover:text-light-70 dark:group-hover:text-dark-70">d[any]</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,7 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-1 ms-0 transition-[margin-inline-start] group-data-[active]/sidebar:ms-[var(--sidebar-width)] overflow-auto"><slot></slot></div>
|
<div class="flex flex-1 z-0 overflow-auto group-data-[active]/sidebar:xl:ms-64 transition-[margin]"><slot></slot></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -67,11 +68,19 @@ import { dom, icon } from '~~/shared/dom';
|
|||||||
import { unifySlug } from '~~/shared/general';
|
import { unifySlug } from '~~/shared/general';
|
||||||
import { tooltip } from '~~/shared/floating';
|
import { tooltip } from '~~/shared/floating';
|
||||||
import { link, loading } from '~~/shared/components';
|
import { link, loading } from '~~/shared/components';
|
||||||
|
import { breakpoint } from '~~/shared/breakpoint';
|
||||||
|
|
||||||
const open = useLocalStorage('sidebar', true, { writeDefaults: true });
|
const open = useLocalStorage('sidebar', true, { writeDefaults: true });
|
||||||
let tree: TreeDOM | undefined;
|
let tree: TreeDOM | undefined;
|
||||||
const { loggedIn, user } = useUserSession();
|
const { loggedIn, user } = useUserSession();
|
||||||
|
|
||||||
|
function onSidebarClick(e: MouseEvent)
|
||||||
|
{
|
||||||
|
const link = (e.target as HTMLElement).closest('a');
|
||||||
|
if (link && breakpoint.viewport !== 'xl' && breakpoint.viewport !== '2xl')
|
||||||
|
nextTick(() => { open.value = false; });
|
||||||
|
}
|
||||||
|
|
||||||
const route = useRouter().currentRoute;
|
const route = useRouter().currentRoute;
|
||||||
const path = computed(() => route.value.params.path ? decodeURIComponent(unifySlug(route.value.params.path)) : undefined);
|
const path = computed(() => route.value.params.path ? decodeURIComponent(unifySlug(route.value.params.path)) : undefined);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-1 items-center justify-center">
|
<div class="flex flex-1 items-center justify-center">
|
||||||
<div class="w-full md:w-auto h-full border-e border-light-35 dark:border-dark-35 bg-light-20 dark:bg-dark-20 md:p-8 xl:p-16 flex justify-center items-center">
|
<div class="w-full md:w-[48rem] h-full border-e border-light-35 dark:border-dark-35 bg-light-20 dark:bg-dark-20 md:p-8 xl:p-16 flex justify-center items-center">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden md:block flex-auto h-full"></div>
|
<div class="hidden md:block flex-auto h-full"></div>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
export default defineNuxtPlugin((nuxtApp) => {
|
|
||||||
nuxtApp.vueApp.directive('autofocus', {
|
|
||||||
mounted(el, binding) {
|
|
||||||
el.focus();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const schema = z.object({
|
export const schema = z.object({
|
||||||
usernameOrEmail: z.string({ required_error: "Nom d'utilisateur ou email obligatoire" }),
|
usernameOrEmail: z.string({ error: "Nom d'utilisateur ou email obligatoire" }),
|
||||||
password: z.string({ required_error: "Mot de passe obligatoire" }),
|
password: z.string({ error: "Mot de passe obligatoire" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Login = z.infer<typeof schema>;
|
export type Login = z.infer<typeof schema>;
|
||||||
@@ -2,23 +2,33 @@ import { reactive } from '#shared/reactive';
|
|||||||
|
|
||||||
export type Breakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
export type Breakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
||||||
|
|
||||||
const breakpoints = [
|
const BREAKPOINT_WIDTHS: [Breakpoint, number][] = [
|
||||||
{ name: 'sm', query: '(min-width: 640px)' },
|
['sm', 640],
|
||||||
{ name: 'md', query: '(min-width: 768px)' },
|
['md', 768],
|
||||||
{ name: 'lg', query: '(min-width: 1024px)' },
|
['lg', 1024],
|
||||||
{ name: 'xl', query: '(min-width: 1280px)' },
|
['xl', 1280],
|
||||||
{ name: '2xl', query: '(min-width: 1536px)' },
|
['2xl', 1536],
|
||||||
];
|
];
|
||||||
|
|
||||||
export const breakpoint = reactive({ current: 'lg' as Breakpoint });
|
export const breakpoint = reactive({ container: 'lg' as Breakpoint, viewport: 'lg' as Breakpoint });
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
function updateBreakpoint()
|
||||||
const updateBreakpoint = () => {
|
{
|
||||||
for (let i = breakpoints.length - 1; i >= 0; i--)
|
for (let i = BREAKPOINT_WIDTHS.length - 1; i >= 0; i--)
|
||||||
if (window.matchMedia(breakpoints[i]!.query).matches)
|
{
|
||||||
{ breakpoint.current = breakpoints[i]!.name as Breakpoint; break; }
|
if (window.innerWidth >= BREAKPOINT_WIDTHS[i]![1])
|
||||||
};
|
{
|
||||||
|
const bp = BREAKPOINT_WIDTHS[i]![0];
|
||||||
updateBreakpoint();
|
breakpoint.viewport = bp;
|
||||||
breakpoints.forEach(b => window.matchMedia(b.query).addEventListener('change', updateBreakpoint));
|
breakpoint.container = bp;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined')
|
||||||
|
{
|
||||||
|
updateBreakpoint();
|
||||||
|
|
||||||
|
BREAKPOINT_WIDTHS.forEach(e => window.matchMedia(`(min-width: ${e[1]}px)`).addEventListener('change', updateBreakpoint));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ export class Node extends EventTarget
|
|||||||
const style = this.style;
|
const style = this.style;
|
||||||
|
|
||||||
this.nodeDom = dom('div', { class: ['absolute', {'-z-10': this.properties.type === 'group', 'z-10': this.properties.type !== 'group'}], style: { transform: `translate(${this.properties.x}px, ${this.properties.y}px)`, width: `${this.properties.width}px`, height: `${this.properties.height}px`, '--canvas-color': this.properties.color?.hex } }, [
|
this.nodeDom = dom('div', { class: ['absolute', {'-z-10': this.properties.type === 'group', 'z-10': this.properties.type !== 'group'}], style: { transform: `translate(${this.properties.x}px, ${this.properties.y}px)`, width: `${this.properties.width}px`, height: `${this.properties.height}px`, '--canvas-color': this.properties.color?.hex } }, [
|
||||||
dom('div', { class: ['outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 w-full h-full hover:outline-4', style.border] }, [
|
dom('div', { class: ['outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 w-full h-full', style.border] }, [
|
||||||
dom('div', { class: ['w-full h-full py-2 px-4 flex overflow-auto', style.bg] }, [this.properties.text ? dom('div', { class: 'flex items-center' }, [render(this.properties.text)]) : undefined])
|
dom('div', { class: ['w-full h-full py-2 px-4 flex overflow-auto', style.bg] }, [this.properties.text ? dom('div', { class: 'flex items-center' }, [render(this.properties.text)]) : undefined])
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1478,7 +1478,14 @@ export const craftingText: Record<CraftingType, string> = {
|
|||||||
'armorer': 'Armurerie',
|
'armorer': 'Armurerie',
|
||||||
'enchanter': 'Enchantement',
|
'enchanter': 'Enchantement',
|
||||||
'brewerer': 'Alchimie',
|
'brewerer': 'Alchimie',
|
||||||
}
|
};
|
||||||
|
export const materialText: Record<keyof CharacterVariables['components'], string> = {
|
||||||
|
'money': 'Argent',
|
||||||
|
'natural': 'Naturel',
|
||||||
|
'mineral': 'Minéral',
|
||||||
|
'processed': 'Transformé',
|
||||||
|
'magical': 'Magique',
|
||||||
|
};
|
||||||
export const subnameFactory = (item: ItemConfig, state?: ItemState): string[] => {
|
export const subnameFactory = (item: ItemConfig, state?: ItemState): string[] => {
|
||||||
let result = [];
|
let result = [];
|
||||||
switch(item.category)
|
switch(item.category)
|
||||||
@@ -1600,7 +1607,7 @@ export class CharacterSheet
|
|||||||
const saveNotes = () => { loadableIcon.replaceWith(saveLoading); this.saveNotes().finally(() => { saveLoading.replaceWith(loadableIcon) }); }
|
const saveNotes = () => { loadableIcon.replaceWith(saveLoading); this.saveNotes().finally(() => { saveLoading.replaceWith(loadableIcon) }); }
|
||||||
|
|
||||||
this.tabs = tabgroup([
|
this.tabs = tabgroup([
|
||||||
() => (breakpoint.current === 'sm' || breakpoint.current === 'md') ? { id: 'stats', title: [ text('Stats') ], content: () => this.sidebarTab() } : undefined,
|
() => (breakpoint.viewport === 'sm' || breakpoint.viewport === 'md') ? { id: 'stats', title: [ text('Stats') ], content: () => this.sidebarTab() } : undefined,
|
||||||
|
|
||||||
{ id: 'actions', title: [ text('Actions') ], content: () => this.actionsTab() },
|
{ id: 'actions', title: [ text('Actions') ], content: () => this.actionsTab() },
|
||||||
|
|
||||||
@@ -1626,7 +1633,7 @@ export class CharacterSheet
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
] },
|
] },
|
||||||
], { focused: this.tab, class: { container: 'flex-1 gap-4 px-4 lg:max-w-[960px] h-full', content: 'overflow-auto h-full' }, switch: v => { this.tab = v; localStorage.setItem('this.character.compiled-tab', v); } });
|
], { focused: this.tab, class: { container: 'flex-1 gap-4 lg:px-4 px-2 lg:max-w-[960px] max-w-full h-full', content: 'overflow-auto h-full', title: 'text-sm md:text-base sm: px-1 md:px-2', tabbar: 'overflow-x-auto overflow-y-hidden' }, switch: v => { this.tab = v; localStorage.setItem('this.character.compiled-tab', v); } });
|
||||||
|
|
||||||
this.container.replaceChildren(div('flex flex-col justify-start gap-1 h-full w-full min-w-half', [
|
this.container.replaceChildren(div('flex flex-col justify-start gap-1 h-full w-full min-w-half', [
|
||||||
div("flex lg:flex-row gap-4 lg:gap-6 items-center justify-center", [
|
div("flex lg:flex-row gap-4 lg:gap-6 items-center justify-center", [
|
||||||
@@ -1737,34 +1744,38 @@ export class CharacterSheet
|
|||||||
]),
|
]),
|
||||||
|
|
||||||
div("flex flex-1 flex-row items-stretch justify-center py-2 gap-4 h-0", [
|
div("flex flex-1 flex-row items-stretch justify-center py-2 gap-4 h-0", [
|
||||||
div("hidden lg:flex flex-col gap-4 py-1 w-60", [
|
div("hidden lg:flex flex-col gap-4 py-1 w-48", [
|
||||||
div("flex flex-col py-1 gap-4", [
|
foldable([
|
||||||
|
div("flex flex-1 flex-col gap-1",
|
||||||
|
MAIN_STATS.map((stat) =>
|
||||||
|
div("flex flex-row px-1 justify-between items-center", [
|
||||||
|
span("text-base text-light-100 dark:text-dark-100 font-semibold", mainStatTexts[stat as MainStat] || stat),
|
||||||
|
span("font-bold text-lg text-light-100 dark:text-dark-100", () => !this.character.compiled ? '-' : `+${(this.character.compiled.modifier[stat as MainStat] ?? 0) + ((this.character.compiled.bonus?.defense !== undefined ? this.character.compiled.bonus?.defense[stat as MainStat] : 0) ?? 0)}`),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
),
|
||||||
|
], [
|
||||||
|
div("flex flex-row items-center justify-center gap-4", [
|
||||||
|
dom("div", { class: 'text-xl font-semibold', text: "Résistances" }),
|
||||||
|
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50")
|
||||||
|
]),
|
||||||
|
], { class: { container: 'flex flex-col px-1 gap-2', title: 'ps-2' }, open: true, }),
|
||||||
|
|
||||||
|
foldable([
|
||||||
|
div("flex flex-1 flex-col gap-1",
|
||||||
|
ABILITIES.map((ability) =>
|
||||||
|
div("flex flex-row px-1 justify-between items-center", [
|
||||||
|
proses('a', preview, [ span("text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline", abilityTexts[ability as Ability] || ability) ], { href: `regles/l'entrainement/competences#${abilityTexts[ability as Ability]}`, label: abilityTexts[ability as Ability], navigate: false }),
|
||||||
|
span("font-bold text-lg text-light-100 dark:text-dark-100", () => !this.character.compiled ? '-' : `+${this.character.compiled.abilities[ability as Ability] ?? 0}`),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
),
|
||||||
|
], [
|
||||||
div("flex flex-row items-center justify-center gap-4", [
|
div("flex flex-row items-center justify-center gap-4", [
|
||||||
dom("div", { class: 'text-xl font-semibold', text: "Compétences" }),
|
dom("div", { class: 'text-xl font-semibold', text: "Compétences" }),
|
||||||
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50")
|
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50")
|
||||||
]),
|
]),
|
||||||
|
], { class: { container: 'flex flex-col px-1 gap-2', title: 'ps-2' }, open: true, })
|
||||||
div("grid grid-cols-2 gap-2",
|
|
||||||
ABILITIES.map((ability) =>
|
|
||||||
div("flex flex-row px-1 justify-between items-center", [
|
|
||||||
proses('a', preview, [ span("text-sm text-light-70 dark:text-dark-70 max-w-20 truncate cursor-help decoration-dotted underline", abilityTexts[ability as Ability] || ability) ], { href: `regles/l'entrainement/competences#${abilityTexts[ability as Ability]}`, label: abilityTexts[ability as Ability], navigate: false }),
|
|
||||||
span("font-bold text-base text-light-100 dark:text-dark-100", () => !this.character.compiled ? '-' : `+${this.character.compiled.abilities[ability as Ability] ?? 0}`),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
div("flex flex-row items-center justify-center gap-4", [
|
|
||||||
dom("div", { class: 'text-xl font-semibold', text: "Maitrises" }),
|
|
||||||
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50")
|
|
||||||
]),
|
|
||||||
|
|
||||||
div("grid grid-cols-2 gap-x-3 gap-y-1 text-sm", { list: () => this.character.compiled?.mastery ?? [], render: (e, _c) => proses('a', preview, [ text(masteryTexts[e].text) ], { href: masteryTexts[e].href, label: masteryTexts[e].text, class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline', }) }),
|
|
||||||
div("grid grid-cols-2 gap-x-3 gap-y-1 text-sm", [
|
|
||||||
() => (this.character.compiled?.spellranks?.precision ?? 0) > 0 ? div('flex flex-row items-center gap-2', [ proses('a', preview, [ text('Précision') ], { href: 'regles/la-magie/magie#Les sorts de précision', label: 'Précision', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }), span('font-bold', () => this.character.compiled?.spellranks?.precision ?? 0) ]) : undefined,
|
|
||||||
() => (this.character.compiled?.spellranks?.knowledge ?? 0) > 0 ? div('flex flex-row items-center gap-2', [ proses('a', preview, [ text('Savoir') ], { href: 'regles/la-magie/magie#Les sorts de savoir', label: 'Savoir', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }), span('font-bold', () => this.character.compiled?.spellranks?.knowledge ?? 0) ]) : undefined,
|
|
||||||
() => (this.character.compiled?.spellranks?.instinct ?? 0) > 0 ? div('flex flex-row items-center gap-2', [ proses('a', preview, [ text('Instinct') ], { href: 'regles/la-magie/magie#Les sorts instinctif', label: 'Instinct', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }), span('font-bold', () => this.character.compiled?.spellranks?.instinct ?? 0) ]) : undefined,
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
div('hidden lg:block border-l border-light-35 dark:border-dark-35'),
|
div('hidden lg:block border-l border-light-35 dark:border-dark-35'),
|
||||||
@@ -1813,7 +1824,7 @@ export class CharacterSheet
|
|||||||
},
|
},
|
||||||
mana: 0,
|
mana: 0,
|
||||||
});
|
});
|
||||||
const container = div("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-[480px] flex flex-col gap-4 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]", [
|
const container = div("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-[480px] max-lg:data-[state=active]:w-full flex flex-col gap-4 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', [
|
||||||
div('flex flex-row gap-8 items-center', [ span('text-xl font-bold', 'Edititon de vie'), () => !this.character.compiled ? span('text-xl font-bold', '-') : div('flex flex-row items-center gap-1', [ span('text-xl font-bold', () => (this.character.compiled.health - this.character.compiled.variables.health)), text('/'), text(() => this.character.compiled.health) ]) ]),
|
div('flex flex-row gap-8 items-center', [ span('text-xl font-bold', 'Edititon de vie'), () => !this.character.compiled ? span('text-xl font-bold', '-') : div('flex flex-row items-center gap-1', [ span('text-xl font-bold', () => (this.character.compiled.health - this.character.compiled.variables.health)), text('/'), text(() => this.character.compiled.health) ]) ]),
|
||||||
tooltip(button(icon("radix-icons:cross-1", { width: 24, height: 24 }), () => {
|
tooltip(button(icon("radix-icons:cross-1", { width: 24, height: 24 }), () => {
|
||||||
@@ -1880,7 +1891,7 @@ export class CharacterSheet
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
div('flex flex-col gap-8', [
|
div('flex flex-col gap-8', [
|
||||||
div('flex flex-col gap-2', [
|
div('flex flex-col', [
|
||||||
div("flex flex-row items-center justify-center gap-4", [
|
div("flex flex-row items-center justify-center gap-4", [
|
||||||
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-lg font-semibold', text: "Actions" }) ]),
|
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-lg font-semibold', text: "Actions" }) ]),
|
||||||
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
||||||
@@ -1889,13 +1900,13 @@ export class CharacterSheet
|
|||||||
|
|
||||||
div('flex flex-col gap-2', [
|
div('flex flex-col gap-2', [
|
||||||
div('flex flex-row flex-wrap gap-2', ["Attaquer", "Désarmer", "Saisir", "Faire chuter", "Déplacer", "Courir", "Pas de coté", "Charger", "Lancer un sort", "S'interposer", "Se transformer", "Utiliser un objet", "Anticiper une action", "Improviser"].map(e => proses('a', preview, [ span('cursor-pointer text-sm decoration-dotted underline', e) ], { href: 'regles/le-combat/actions-en-combat#' + e, label: e, trigger: 'hover', navigate: false, class: 'text-light-60 dark:text-dark-60', lowers: false }))),
|
div('flex flex-row flex-wrap gap-2', ["Attaquer", "Désarmer", "Saisir", "Faire chuter", "Déplacer", "Courir", "Pas de coté", "Charger", "Lancer un sort", "S'interposer", "Se transformer", "Utiliser un objet", "Anticiper une action", "Improviser"].map(e => proses('a', preview, [ span('cursor-pointer text-sm decoration-dotted underline', e) ], { href: 'regles/le-combat/actions-en-combat#' + e, label: e, trigger: 'hover', navigate: false, class: 'text-light-60 dark:text-dark-60', lowers: false }))),
|
||||||
div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col gap-1', [
|
div('flex flex-col gap-4', { render: (e, _c) => _c ?? div('flex flex-col', [
|
||||||
div('flex flex-row justify-between', [dom('span', { class: 'text-lg font-semibold', text: config.action[e]?.name }), config.action[e]?.cost ? div('flex flex-row gap-1', [dom('span', { class: 'font-bold', text: config.action[e]?.cost?.toString() }), text(`point${config.action[e]?.cost > 1 ? 's' : ''} d'action`)]) : undefined]),
|
div('flex flex-row justify-between', [dom('span', { class: 'text-base font-bold tracking-tight', text: config.action[e]?.name }), config.action[e]?.cost ? div('flex flex-row gap-1', [dom('span', { class: 'font-bold', text: config.action[e]?.cost?.toString() }), text(`point${config.action[e]?.cost > 1 ? 's' : ''} d'action`)]) : undefined]),
|
||||||
markdown(getText(config.action[e]?.description), undefined, { tags: { a: preview } }),
|
markdown(getText(config.action[e]?.description), undefined, { tags: { a: preview } }),
|
||||||
]), list: () => this.character.compiled ? this.character.compiled.lists.action ?? [] : [] }),
|
]), list: () => this.character.compiled ? this.character.compiled.lists.action ?? [] : [] }),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
div('flex flex-col gap-2', [
|
div('flex flex-col', [
|
||||||
div("flex flex-row items-center justify-center gap-4", [
|
div("flex flex-row items-center justify-center gap-4", [
|
||||||
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-lg font-semibold', text: "Réactions" }) ]),
|
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-lg font-semibold', text: "Réactions" }) ]),
|
||||||
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
||||||
@@ -1904,22 +1915,22 @@ export class CharacterSheet
|
|||||||
|
|
||||||
div('flex flex-col gap-2', [
|
div('flex flex-col gap-2', [
|
||||||
div('flex flex-row flex-wrap gap-2 text-light-60 dark:text-dark-60', ["Parer", "Esquiver", "Saisir une opportunité", "Prendre en tenaille", "Intercepter"].map(e => proses('a', preview, [ span('cursor-pointer text-sm decoration-dotted underline', e) ], { href: 'regles/le-combat/actions-en-combat#' + e, label: e, trigger: 'hover', navigate: false, class: 'text-light-60 dark:text-dark-60', lowers: false }))),
|
div('flex flex-row flex-wrap gap-2 text-light-60 dark:text-dark-60', ["Parer", "Esquiver", "Saisir une opportunité", "Prendre en tenaille", "Intercepter"].map(e => proses('a', preview, [ span('cursor-pointer text-sm decoration-dotted underline', e) ], { href: 'regles/le-combat/actions-en-combat#' + e, label: e, trigger: 'hover', navigate: false, class: 'text-light-60 dark:text-dark-60', lowers: false }))),
|
||||||
div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col gap-1', [
|
div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col', [
|
||||||
div('flex flex-row justify-between', [dom('span', { class: 'text-lg font-semibold', text: config.reaction[e]?.name }), config.reaction[e]?.cost ? div('flex flex-row gap-1', [dom('span', { class: 'font-bold', text: config.reaction[e]?.cost?.toString() }), text(`point${config.reaction[e]?.cost > 1 ? 's' : ''} d'action`)]) : undefined]),
|
div('flex flex-row justify-between', [dom('span', { class: 'text-base font-bold tracking-tight', text: config.reaction[e]?.name }), config.reaction[e]?.cost ? div('flex flex-row gap-1', [dom('span', { class: 'font-bold', text: config.reaction[e]?.cost?.toString() }), text(`point${config.reaction[e]?.cost > 1 ? 's' : ''} d'action`)]) : undefined]),
|
||||||
markdown(getText(config.reaction[e]?.description), undefined, { tags: { a: preview } }),
|
markdown(getText(config.reaction[e]?.description), undefined, { tags: { a: preview } }),
|
||||||
]), list: () => this.character.compiled ? this.character.compiled.lists.reaction ?? [] : [] }),
|
]), list: () => this.character.compiled ? this.character.compiled.lists.reaction ?? [] : [] }),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
div('flex flex-col gap-2', [
|
div('flex flex-col', [
|
||||||
div("flex flex-row items-center justify-center gap-4", [
|
div("flex flex-row items-center justify-center gap-4", [
|
||||||
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-lg font-semibold', text: "Actions libres" }) ]),
|
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-lg font-semibold', text: "Actions libres" }) ]),
|
||||||
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
div('flex flex-col gap-2', [
|
div('flex flex-col gap-2', [
|
||||||
div('flex flex-row flex-wrap gap-2 text-light-60 dark:text-dark-60', ["Analyser une situation", "Communiquer", "Dégainer", "Attraper un objet"].map(e => proses('a', preview, [ span('cursor-pointer text-sm decoration-dotted underline', e) ], { href: 'regles/le-combat/actions-en-combat#' + e, label: e, trigger: 'hover', navigate: false, class: 'text-light-60 dark:text-dark-60', lowers: false }))),
|
div('flex flex-row flex-wrap gap-2 text-light-60 dark:text-dark-60', ["Analyser", "Communiquer", "Dégainer", "Attraper"].map(e => proses('a', preview, [ span('cursor-pointer text-sm decoration-dotted underline', e) ], { href: 'regles/le-combat/actions-en-combat#' + e, label: e, trigger: 'hover', navigate: false, class: 'text-light-60 dark:text-dark-60', lowers: false }))),
|
||||||
div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col gap-1', [
|
div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col', [
|
||||||
div('flex flex-row justify-between', [dom('span', { class: 'text-lg font-semibold', text: config.freeaction[e]?.name }) ]),
|
div('flex flex-row justify-between', [dom('span', { class: 'text-base font-bold tracking-tight', text: config.freeaction[e]?.name }) ]),
|
||||||
markdown(getText(config.freeaction[e]?.description), undefined, { tags: { a: preview } }),
|
markdown(getText(config.freeaction[e]?.description), undefined, { tags: { a: preview } }),
|
||||||
]), list: () => this.character.compiled ? this.character.compiled.lists.freeaction ?? [] : [] })
|
]), list: () => this.character.compiled ? this.character.compiled.lists.freeaction ?? [] : [] })
|
||||||
]),
|
]),
|
||||||
@@ -1930,24 +1941,37 @@ export class CharacterSheet
|
|||||||
abilitiesTab()
|
abilitiesTab()
|
||||||
{
|
{
|
||||||
return [ div('flex flex-col gap-4', [
|
return [ div('flex flex-col gap-4', [
|
||||||
foldable(() => [div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col gap-1', [
|
foldable(() => [div('flex flex-col gap-4 pt-2', { render: (e, _c) => _c ?? div('flex flex-col', [
|
||||||
div('flex flex-row justify-between', [dom('span', { class: 'text-lg font-semibold', text: config.passive[e]?.name }) ]),
|
div('flex flex-row justify-between', [dom('span', { class: 'text-base font-bold tracking-tight', text: config.passive[e]?.name }) ]),
|
||||||
markdown(getText(config.passive[e]?.description), undefined, { tags: { a: preview } }),
|
markdown(getText(config.passive[e]?.description), undefined, { tags: { a: preview } }),
|
||||||
]), list: this.character.compiled.lists.passive })], [
|
]), list: this.character.compiled.lists.passive })], [
|
||||||
div("flex flex-row items-center justify-center gap-4", [
|
div("flex flex-row items-center justify-center gap-4 ps-2", [
|
||||||
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-xl font-bold', text: "Aptitudes" }) ]),
|
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-lg font-semibold', text: "Aptitudes" }) ]),
|
||||||
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
||||||
]),
|
]),
|
||||||
], { open: true }),
|
], { open: true }),
|
||||||
foldable(() => [div('flex flex-col gap-2', { render: (e, _c) => _c ?? div('flex flex-col gap-1', [
|
foldable(() => [div('flex flex-col gap-4 pt-2', { render: (e, _c) => _c ?? div('flex flex-col', [
|
||||||
div('flex flex-row justify-between', [dom('span', { class: 'text-lg font-semibold', text: config.dedication[e]?.name }) ]),
|
div('flex flex-row justify-between', [dom('span', { class: 'text-base font-bold tracking-tight', text: config.dedication[e]?.name }) ]),
|
||||||
markdown(getText(config.features[e]?.description), undefined, { tags: { a: preview } }),
|
markdown(getText(config.features[e]?.description), undefined, { tags: { a: preview } }),
|
||||||
]), list: this.character.compiled.lists.dedication })], [
|
]), list: this.character.compiled.lists.dedication })], [
|
||||||
div("flex flex-row items-center justify-center gap-4", [
|
div("flex flex-row items-center justify-center gap-4 ps-2", [
|
||||||
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-xl font-bold', text: "Spécialisations" }) ]),
|
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-lg font-semibold', text: "Spécialisations" }) ]),
|
||||||
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
||||||
]),
|
]),
|
||||||
], { open: false }),
|
], { open: false }),
|
||||||
|
foldable(() => [
|
||||||
|
div("flex flex-row gap-x-4 gap-y-1 flex-wrap text-sm", { list: () => this.character.compiled.mastery ?? [], render: (e, _c) => proses('a', preview, [ text(masteryTexts[e].text) ], { href: masteryTexts[e].href, label: masteryTexts[e].text, class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline', }) }),
|
||||||
|
div("flex flex-row gap-x-4 gap-y-1 flex-wrap text-sm", [
|
||||||
|
() => (this.character.compiled.spellranks.precision ?? 0) > 0 ? div('flex flex-row items-center gap-2', [ proses('a', preview, [ text('Précision') ], { href: 'regles/la-magie/magie#Les sorts de précision', label: 'Précision', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }), span('font-bold', () => this.character.compiled?.spellranks?.precision ?? 0) ]) : undefined,
|
||||||
|
() => (this.character.compiled.spellranks.knowledge ?? 0) > 0 ? div('flex flex-row items-center gap-2', [ proses('a', preview, [ text('Savoir') ], { href: 'regles/la-magie/magie#Les sorts de savoir', label: 'Savoir', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }), span('font-bold', () => this.character.compiled?.spellranks?.knowledge ?? 0) ]) : undefined,
|
||||||
|
() => (this.character.compiled.spellranks.instinct ?? 0) > 0 ? div('flex flex-row items-center gap-2', [ proses('a', preview, [ text('Instinct') ], { href: 'regles/la-magie/magie#Les sorts instinctif', label: 'Instinct', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }), span('font-bold', () => this.character.compiled?.spellranks?.instinct ?? 0) ]) : undefined,
|
||||||
|
])
|
||||||
|
], [
|
||||||
|
div("flex flex-row items-center justify-center gap-4 ps-2", [
|
||||||
|
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-lg font-semibold', text: "Maitrises" }) ]),
|
||||||
|
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50"),
|
||||||
|
]),
|
||||||
|
], { open: false, class: { content: 'flex flex-col gap-2 py-2' } }),
|
||||||
]) ];
|
]) ];
|
||||||
}
|
}
|
||||||
spellTab()
|
spellTab()
|
||||||
@@ -2010,15 +2034,21 @@ export class CharacterSheet
|
|||||||
return foldable(() => [
|
return foldable(() => [
|
||||||
div('flex flex-row ps-4 p-1 border-l-4 border-light-35 dark:border-dark-35', [ markdown(spell.description) ]),
|
div('flex flex-row ps-4 p-1 border-l-4 border-light-35 dark:border-dark-35', [ markdown(spell.description) ]),
|
||||||
], [
|
], [
|
||||||
div('flex flex-row items-center gap-4', [ dom('span', { class: 'font-semibold text-lg', text: spell.name ?? 'Inconnu' }), div('flex-1 border-b border-dashed border-light-50 dark:border-dark-50'), dom('span', { class: 'text-light-70 dark:text-dark-70', text: `${spell.cost ?? 0} mana` }) ]),
|
div('flex flex-row items-center gap-4', [
|
||||||
|
div('flex flex-row gap-8 items-baseline', [
|
||||||
|
dom('span', { class: 'font-semibold text-lg', text: spell.name ?? 'Inconnu' }),
|
||||||
|
span('text-light-100 dark:text-dark-100 text-sm italic', spell.speed === 'action' ? 'Action' : spell.speed === 'reaction' ? 'Réaction' : spell.speed === 'channeling' ? 'Canalisation' : `${spell.speed} minutes`),
|
||||||
|
]),
|
||||||
|
div('flex-1 border-b border-dashed border-light-50 dark:border-dark-50'),
|
||||||
|
dom('span', { class: 'text-light-70 dark:text-dark-70', text: `${spell.cost ?? 0} mana` })
|
||||||
|
]),
|
||||||
div('flex flex-row flex-wrap items-center gap-x-3 gap-y-1 text-light-70 dark:text-dark-70', [
|
div('flex flex-row flex-wrap items-center gap-x-3 gap-y-1 text-light-70 dark:text-dark-70', [
|
||||||
span('flex flex-row', `Sort ${spell.type === 'instinct' ? 'd\'instinct' : spell.type === 'knowledge' ? 'de savoir' : 'de précision'} ${spell.rank === 4 ? 'unique' :`de rang ${spell.rank}`}`),
|
span('flex flex-row', `Sort ${spell.type === 'instinct' ? 'd\'instinct' : spell.type === 'knowledge' ? 'de savoir' : 'de précision'} ${spell.rank === 4 ? 'unique' :`de rang ${spell.rank}`}`),
|
||||||
div('flex flex-row flex-wrap gap-1', (spell.elements ?? []).map(elementDom)),
|
div('flex flex-row flex-wrap gap-1', (spell.elements ?? []).map(elementDom)),
|
||||||
spell.concentration ? proses('a', preview, [span('italic text-sm', 'concentration')], { href: '' }) : undefined,
|
spell.concentration ? proses('a', preview, [span('italic text-sm', 'concentration')], { href: '' }) : undefined,
|
||||||
span(undefined, typeof spell.range === 'number' && spell.range > 0 ? `${spell.range} case${spell.range > 1 ? 's' : ''}` : spell.range === 0 ? 'toucher' : 'personnel'),
|
span(undefined, typeof spell.range === 'number' && spell.range > 0 ? `${spell.range} case${spell.range > 1 ? 's' : ''}` : spell.range === 0 ? 'Toucher' : 'Personnel'),
|
||||||
span(undefined, typeof spell.speed === 'number' ? `${spell.speed} minute${spell.speed > 1 ? 's' : ''}` : spell.speed)
|
|
||||||
]),
|
]),
|
||||||
], { open: false, class: { container: 'flex flex-col gap-1', icon: 'px-2' } })
|
], { open: false, class: { container: 'flex flex-col gap-1', title: 'ps-2' } })
|
||||||
}, list: () => sort([...(this.character.compiled.lists.spells ?? []), ...this.character.compiled.variables.spells]) }),
|
}, list: () => sort([...(this.character.compiled.lists.spells ?? []), ...this.character.compiled.variables.spells]) }),
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
@@ -2036,7 +2066,7 @@ export class CharacterSheet
|
|||||||
speed: [],
|
speed: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const container = div("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-4 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]", [
|
const container = div("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 max-lg:data-[state=active]:w-full flex flex-col gap-4 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", [
|
||||||
dom("h2", { class: "text-xl font-bold", text: "Ajouter un sort" }),
|
dom("h2", { class: "text-xl font-bold", text: "Ajouter un sort" }),
|
||||||
div('flex flex-row gap-4 items-center', [ dom('span', { class: 'italic text-light-70 dark:text-dark-70 text-sm' }, [ text(() => `${spells.length}/${this.character.compiled.spellslots} sort(s) maitrisé(s)`.replaceAll('(s)', spells.length > 1 ? 's' : '')) ]), tooltip(button(icon("radix-icons:cross-1", { width: 20, height: 20 }), () => {
|
div('flex flex-row gap-4 items-center', [ dom('span', { class: 'italic text-light-70 dark:text-dark-70 text-sm' }, [ text(() => `${spells.length}/${this.character.compiled.spellslots} sort(s) maitrisé(s)`.replaceAll('(s)', spells.length > 1 ? 's' : '')) ]), tooltip(button(icon("radix-icons:cross-1", { width: 20, height: 20 }), () => {
|
||||||
@@ -2119,8 +2149,8 @@ export class CharacterSheet
|
|||||||
return [
|
return [
|
||||||
div('flex flex-col gap-2', [
|
div('flex flex-col gap-2', [
|
||||||
div('flex flex-row justify-between items-center', [
|
div('flex flex-row justify-between items-center', [
|
||||||
div('flex flex-row justify-end items-center gap-8', [
|
div('flex flex-row items-center gap-4', [
|
||||||
div('flex flex-row gap-1 items-center', [ span('italic text-sm', 'Argent'), text("TODO") ]),
|
//TODO: money and components as special foldable with the content replacing the title on fold open.
|
||||||
]),
|
]),
|
||||||
div('flex flex-row justify-end items-center gap-8', [
|
div('flex flex-row justify-end items-center gap-8', [
|
||||||
dom('span', { class: () => ['italic text-sm', { 'text-light-red dark:text-dark-red': this.character.power > this.character.compiled.itempower }], text: () => `Puissance magique: ${this.character.power}/${this.character.compiled.itempower}` }),
|
dom('span', { class: () => ['italic text-sm', { 'text-light-red dark:text-dark-red': this.character.power > this.character.compiled.itempower }], text: () => `Puissance magique: ${this.character.power}/${this.character.compiled.itempower}` }),
|
||||||
@@ -2134,9 +2164,9 @@ export class CharacterSheet
|
|||||||
div('flex-1 min-w-0', [text('Nom')]),
|
div('flex-1 min-w-0', [text('Nom')]),
|
||||||
div('w-32 shrink-0', [text('Stats')]),
|
div('w-32 shrink-0', [text('Stats')]),
|
||||||
div('w-10 shrink-0 text-center', [text('Qté')]),
|
div('w-10 shrink-0 text-center', [text('Qté')]),
|
||||||
div('w-16 shrink-0 text-center hidden lg:block', [text('Puis.')]),
|
div('w-16 shrink-0 text-center hidden md:block', [text('Puis.')]),
|
||||||
div('w-12 shrink-0 text-center hidden lg:block', [text('Poids')]),
|
div('w-12 shrink-0 text-center hidden md:block', [text('Poids')]),
|
||||||
div('w-12 shrink-0 text-center hidden lg:block', [text('Charg.')]),
|
div('w-12 shrink-0 text-center hidden md:block', [text('Charg.')]),
|
||||||
]),
|
]),
|
||||||
div('flex flex-col', { list: this.character.compiled.variables.items, render: (e, _c) => {
|
div('flex flex-col', { list: this.character.compiled.variables.items, render: (e, _c) => {
|
||||||
if(_c) return _c;
|
if(_c) return _c;
|
||||||
@@ -2150,7 +2180,7 @@ export class CharacterSheet
|
|||||||
return foldable(() => [
|
return foldable(() => [
|
||||||
markdown(getText(item.description)),
|
markdown(getText(item.description)),
|
||||||
div('flex flex-row gap-1', { list: () => e.improvements!.map(e => config.improvements[e]).filter(e => !!e), render: (e, _c) => _c ?? floater(div(() => ['flex flex-row gap-2 border px-2 rounded-full py-px', { 'border-accent-blue bg-accent-blue/20': !e.cursed, 'border-light-purple bg-light-purple/20 dark:border-dark-purple dark:bg-dark-purple/20': e.cursed }], [ span('text-sm font-semibold tracking-tight', e.name), div('flex flex-row gap-1 items-center', [icon('game-icons:bolt-drop', { width: 12, height: 12 }), span('text-sm font-light', e.power)]) ]), () => [markdown(getText(e.description), undefined, { tags: { a: preview } })], { class: 'max-w-96 max-h-48 p-2', position: "bottom-start" }) }),
|
div('flex flex-row gap-1', { list: () => e.improvements!.map(e => config.improvements[e]).filter(e => !!e), render: (e, _c) => _c ?? floater(div(() => ['flex flex-row gap-2 border px-2 rounded-full py-px', { 'border-accent-blue bg-accent-blue/20': !e.cursed, 'border-light-purple bg-light-purple/20 dark:border-dark-purple dark:bg-dark-purple/20': e.cursed }], [ span('text-sm font-semibold tracking-tight', e.name), div('flex flex-row gap-1 items-center', [icon('game-icons:bolt-drop', { width: 12, height: 12 }), span('text-sm font-light', e.power)]) ]), () => [markdown(getText(e.description), undefined, { tags: { a: preview } })], { class: 'max-w-96 max-h-48 p-2', position: "bottom-start" }) }),
|
||||||
div('flex flex-row flex-wrap gap-x-3 gap-y-1 text-xs text-light-70 dark:text-dark-70 lg:hidden', [
|
div('flex flex-row flex-wrap gap-x-3 gap-y-1 text-xs text-light-70 dark:text-dark-70 md:hidden', [
|
||||||
item.category === 'armor' ? span('', () => `Armure: ${item.health + ((e.state as ArmorState)?.health ?? 0) - ((e.state as ArmorState)?.loss ?? 0)}/${item.health + ((e.state as ArmorState)?.health ?? 0)} (${[item.absorb.static + ((e.state as ArmorState).absorb?.flat ?? 0) > 0 ? '-' + (item.absorb.static + ((e.state as ArmorState).absorb?.flat ?? 0)) : undefined, item.absorb.percent + ((e.state as ArmorState).absorb?.percent ?? 0) > 0 ? '-' + (item.absorb.percent + ((e.state as ArmorState).absorb?.percent ?? 0)) + '%' : undefined].filter(e => !!e).join('/')})`) : undefined,
|
item.category === 'armor' ? span('', () => `Armure: ${item.health + ((e.state as ArmorState)?.health ?? 0) - ((e.state as ArmorState)?.loss ?? 0)}/${item.health + ((e.state as ArmorState)?.health ?? 0)} (${[item.absorb.static + ((e.state as ArmorState).absorb?.flat ?? 0) > 0 ? '-' + (item.absorb.static + ((e.state as ArmorState).absorb?.flat ?? 0)) : undefined, item.absorb.percent + ((e.state as ArmorState).absorb?.percent ?? 0) > 0 ? '-' + (item.absorb.percent + ((e.state as ArmorState).absorb?.percent ?? 0)) + '%' : undefined].filter(e => !!e).join('/')})`) : undefined,
|
||||||
item.category === 'weapon' ? span('', () => `${stringifyRoll(parseDice(`${item.damage.value}${(e.state as WeaponState)?.attack ? '+' + (e.state as WeaponState).attack : ''}`), this.character.compiled.modifier, true)} ${damageTypeTexts[item.damage.type].toLowerCase()}`) : undefined,
|
item.category === 'weapon' ? span('', () => `${stringifyRoll(parseDice(`${item.damage.value}${(e.state as WeaponState)?.attack ? '+' + (e.state as WeaponState).attack : ''}`), this.character.compiled.modifier, true)} ${damageTypeTexts[item.damage.type].toLowerCase()}`) : undefined,
|
||||||
item.capacity ? span('', () => `Puissance: ${itempower()}/${item.capacity}`) : undefined,
|
item.capacity ? span('', () => `Puissance: ${itempower()}/${item.capacity}`) : undefined,
|
||||||
@@ -2203,13 +2233,13 @@ export class CharacterSheet
|
|||||||
undefined
|
undefined
|
||||||
]),
|
]),
|
||||||
div('w-10 shrink-0 text-center text-sm', [text(() => e.amount.toString())]),
|
div('w-10 shrink-0 text-center text-sm', [text(() => e.amount.toString())]),
|
||||||
div('w-16 shrink-0 text-center text-xs hidden lg:block', [
|
div('w-16 shrink-0 text-center text-xs hidden md:block', [
|
||||||
span(() => ({ 'text-light-red dark:text-dark-red': !!item.capacity && itempower() > item.capacity }), () => item.capacity ? `${itempower()}/${item.capacity}` : '-')
|
span(() => ({ 'text-light-red dark:text-dark-red': !!item.capacity && itempower() > item.capacity }), () => item.capacity ? `${itempower()}/${item.capacity}` : '-')
|
||||||
]),
|
]),
|
||||||
div('w-12 shrink-0 text-center text-xs hidden lg:block', [
|
div('w-12 shrink-0 text-center text-xs hidden md:block', [
|
||||||
e.amount > 1 && !!item.weight ? tooltip(span(() => ({ 'cursor-help underline decoration-1 decoration-dotted underline-offset-2': e.amount > 1 && !!item.weight }), () => item.weight ? `${item.weight * e.amount}` : '-'), `Poids unitaire: ${item.weight}`, 'bottom') : span('', () => item.weight ? `${item.weight * e.amount}` : '-')
|
e.amount > 1 && !!item.weight ? tooltip(span(() => ({ 'cursor-help underline decoration-1 decoration-dotted underline-offset-2': e.amount > 1 && !!item.weight }), () => item.weight ? `${item.weight * e.amount}` : '-'), `Poids unitaire: ${item.weight}`, 'bottom') : span('', () => item.weight ? `${item.weight * e.amount}` : '-')
|
||||||
]),
|
]),
|
||||||
div('w-12 shrink-0 text-center text-xs hidden lg:block', [span('', () => item.charge ? `${item.charge}` : '-')]),
|
div('w-12 shrink-0 text-center text-xs hidden md:block', [span('', () => item.charge ? `${item.charge}` : '-')]),
|
||||||
])
|
])
|
||||||
], { open: false, class: { container: 'border-b border-dashed border-light-35 dark:border-dark-35 py-1', icon: 'px-1', content: 'px-4 pb-2 flex flex-col gap-1' } })
|
], { open: false, class: { container: 'border-b border-dashed border-light-35 dark:border-dark-35 py-1', icon: 'px-1', content: 'px-4 pb-2 flex flex-col gap-1' } })
|
||||||
}})
|
}})
|
||||||
@@ -2226,7 +2256,7 @@ export class CharacterSheet
|
|||||||
power: { min: 0, max: Infinity },
|
power: { min: 0, max: Infinity },
|
||||||
});
|
});
|
||||||
|
|
||||||
const container = div("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-4 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]", [
|
const container = div("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 max-lg:data-[state=active]:w-full flex flex-col gap-4 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]", [
|
||||||
div("flex flex-row justify-between items-center mb-4", [
|
div("flex flex-row justify-between items-center mb-4", [
|
||||||
dom("h2", { class: "text-xl font-bold", text: "Gestion de l'inventaire" }),
|
dom("h2", { class: "text-xl font-bold", text: "Gestion de l'inventaire" }),
|
||||||
div('flex flex-row gap-8 items-center justify-end', [
|
div('flex flex-row gap-8 items-center justify-end', [
|
||||||
@@ -2309,7 +2339,7 @@ export class CharacterSheet
|
|||||||
|
|
||||||
const itempower = () => current.item && config.items[current.item.id] !== undefined ? ((config.items[current.item.id]!.powercost ?? 0) + (current.item.improvements?.reduce((_p, _v) => (config.improvements[_v]?.power ?? 0) + _p, 0) ?? 0)) : 0;
|
const itempower = () => current.item && config.items[current.item.id] !== undefined ? ((config.items[current.item.id]!.powercost ?? 0) + (current.item.improvements?.reduce((_p, _v) => (config.improvements[_v]?.power ?? 0) + _p, 0) ?? 0)) : 0;
|
||||||
|
|
||||||
const container = div("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-4 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]", [
|
const container = div("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 max-lg:data-[state=active]:w-full flex flex-col gap-4 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]", [
|
||||||
div("flex flex-row justify-between items-center mb-4", [
|
div("flex flex-row justify-between items-center mb-4", [
|
||||||
dom("h2", { class: "text-xl font-bold", text: "Améliorations" }),
|
dom("h2", { class: "text-xl font-bold", text: "Améliorations" }),
|
||||||
div('flex flex-row gap-8 items-center justify-end', [
|
div('flex flex-row gap-8 items-center justify-end', [
|
||||||
@@ -2402,7 +2432,7 @@ export class CharacterSheet
|
|||||||
() => this.character.compiled.defense.static + this.character.compiled.defense.passiveparry + this.character.compiled.defense.activedodge,
|
() => this.character.compiled.defense.static + this.character.compiled.defense.passiveparry + this.character.compiled.defense.activedodge,
|
||||||
];
|
];
|
||||||
return div('flex flex-col items-center', [
|
return div('flex flex-col items-center', [
|
||||||
span('text-lg font-bold', () => !this.character.compiled ? '-' : clamp(defs[i](), 0, this.character.compiled.defense.hardcap)),
|
span('text-lg font-bold', () => !this.character.compiled ? '-' : clamp(defs[i]!(), 0, this.character.compiled.defense.hardcap)),
|
||||||
span('text-xs text-light-70 dark:text-dark-70', label),
|
span('text-xs text-light-70 dark:text-dark-70', label),
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ export function async(size: 'small' | 'normal' | 'large' = 'normal', fn: Promise
|
|||||||
}
|
}
|
||||||
export function button(content: Node | NodeChildren, onClick?: (this: HTMLElement) => void, cls?: Class, disabled?: Reactive<boolean>)
|
export function button(content: Node | NodeChildren, onClick?: (this: HTMLElement) => void, cls?: Class, disabled?: Reactive<boolean>)
|
||||||
{
|
{
|
||||||
const btn = dom('button', { class: [`inline-flex justify-center items-center outline-hidden leading-none transition-[box-shadow]
|
const btn = dom('button', { class: [cls, `inline-flex justify-center items-center outline-hidden leading-none transition-[box-shadow]
|
||||||
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
|
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
|
||||||
hover:bg-light-25 hover:dark:bg-dark-25 hover:border-light-50 hover:dark:border-dark-50
|
hover:bg-light-25 hover:dark:bg-dark-25 hover:border-light-50 hover:dark:border-dark-50
|
||||||
focus:bg-light-30 focus:dark:bg-dark-30 focus:border-light-50 focus:dark:border-dark-50 focus:shadow-raw focus:shadow-light-50 focus:dark:shadow-dark-50
|
focus:bg-light-30 focus:dark:bg-dark-30 focus:border-light-50 focus:dark:border-dark-50 focus:shadow-raw focus:shadow-light-50 focus:dark:shadow-dark-50
|
||||||
disabled:text-light-50 dark:disabled:text-dark-50 disabled:bg-light-10 dark:disabled:bg-dark-10 disabled:border-dashed disabled:border-light-40 dark:disabled:border-dark-40`, cls], listeners: { click: () => _disabled || (onClick && onClick.bind(btn)()) } }, Array.isArray(content) ? content : [content]);
|
disabled:text-light-50 dark:disabled:text-dark-50 disabled:bg-light-10 dark:disabled:bg-dark-10 disabled:border-dashed disabled:border-light-40 dark:disabled:border-dark-40`], listeners: { click: () => _disabled || (onClick && onClick.bind(btn)()) } }, Array.isArray(content) ? content : [content]);
|
||||||
let _disabled = false;
|
let _disabled = false;
|
||||||
Object.defineProperty(btn, 'disabled', {
|
Object.defineProperty(btn, 'disabled', {
|
||||||
get: () => _disabled,
|
get: () => _disabled,
|
||||||
@@ -59,10 +59,9 @@ export function button(content: Node | NodeChildren, onClick?: (this: HTMLElemen
|
|||||||
export function buttongroup<T extends any>(options: Array<{ text: string, value: T }>, settings?: { class?: { container?: Class, option?: Class }, value?: T, onChange?: (value: T) => boolean | void })
|
export function buttongroup<T extends any>(options: Array<{ text: string, value: T }>, settings?: { class?: { container?: Class, option?: Class }, value?: T, onChange?: (value: T) => boolean | void })
|
||||||
{
|
{
|
||||||
let currentValue = settings?.value;
|
let currentValue = settings?.value;
|
||||||
const elements = options.map(e => dom('div', { class: [`cursor-pointer text-light-100 dark:text-dark-100 hover:bg-light-30 hover:dark:bg-dark-30 flex items-center justify-center bg-light-20 dark:bg-dark-20 leading-none outline-hidden
|
const elements = options.map(e => dom('div', { class: [settings?.class?.option, `cursor-pointer text-light-100 dark:text-dark-100 hover:bg-light-30 hover:dark:bg-dark-30 flex items-center justify-center bg-light-20 dark:bg-dark-20 leading-none outline-hidden
|
||||||
border border-light-40 dark:border-dark-40 hover:border-light-50 hover:dark:border-dark-50 data-[selected]:z-10 data-[selected]:border-light-50 dark:data-[selected]:border-dark-50
|
border border-light-40 dark:border-dark-40 hover:border-light-50 hover:dark:border-dark-50 data-[selected]:z-10 data-[selected]:border-light-50 dark:data-[selected]:border-dark-50
|
||||||
data-[selected]:shadow-raw transition-[box-shadow] data-[selected]:shadow-light-50 dark:data-[selected]:shadow-dark-50 focus:shadow-raw focus:shadow-light-40 focus:dark:shadow-dark-40`,
|
data-[selected]:shadow-raw transition-[box-shadow] data-[selected]:shadow-light-50 dark:data-[selected]:shadow-dark-50 focus:shadow-raw focus:shadow-light-40 focus:dark:shadow-dark-40`], text: e.text, attributes: { 'data-selected': settings?.value === e.value }, listeners: { click: function() {
|
||||||
settings?.class?.option], text: e.text, attributes: { 'data-selected': settings?.value === e.value }, listeners: { click: function() {
|
|
||||||
if(currentValue !== e.value)
|
if(currentValue !== e.value)
|
||||||
{
|
{
|
||||||
elements.forEach(e => e.toggleAttribute('data-selected', false));
|
elements.forEach(e => e.toggleAttribute('data-selected', false));
|
||||||
@@ -74,12 +73,12 @@ export function buttongroup<T extends any>(options: Array<{ text: string, value:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}}))
|
}}}))
|
||||||
return div(['flex flex-row', settings?.class?.container], elements);
|
return div([settings?.class?.container, 'flex flex-row'], elements);
|
||||||
}
|
}
|
||||||
export function optionmenu(options: Array<{ title: string, click: () => void }>, settings?: { position?: Placement, class?: { container?: Class, option?: Class } }): (target?: HTMLElement) => void
|
export function optionmenu(options: Array<{ title: string, click: () => void }>, settings?: { position?: Placement, class?: { container?: Class, option?: Class } }): (target?: HTMLElement) => void
|
||||||
{
|
{
|
||||||
let close: () => void;
|
let close: () => void;
|
||||||
const element = div(['flex flex-col divide-y divide-light-30 dark:divide-dark-30 text-light-100 dark:text-dark-100', settings?.class?.container], options.map(e => dom('div', { class: ['flex flex-row px-2 py-1 hover:bg-light-35 hover:dark:bg-dark-35 cursor-pointer', settings?.class?.option], text: e.title, listeners: { click: () => { e.click(); close() } } })));
|
const element = div([settings?.class?.container, 'flex flex-col divide-y divide-light-30 dark:divide-dark-30 text-light-100 dark:text-dark-100'], options.map(e => dom('div', { class: [settings?.class?.option, 'flex flex-row px-2 py-1 hover:bg-light-35 hover:dark:bg-dark-35 cursor-pointer'], text: e.title, listeners: { click: () => { e.click(); close() } } })));
|
||||||
return function(this: HTMLElement, target?: HTMLElement) {
|
return function(this: HTMLElement, target?: HTMLElement) {
|
||||||
close = followermenu(target ?? this, [ element ], { arrow: true, placement: settings?.position, offset: 8 }).close;
|
close = followermenu(target ?? this, [ element ], { arrow: true, placement: settings?.position, offset: 8 }).close;
|
||||||
}
|
}
|
||||||
@@ -127,8 +126,8 @@ export function select<T extends NonNullable<any>>(options: Reactive<Array<{ tex
|
|||||||
if(disabled) return;
|
if(disabled) return;
|
||||||
|
|
||||||
window.addEventListener('keydown', handleKeys);
|
window.addEventListener('keydown', handleKeys);
|
||||||
context = followermenu(select, optionElements.filter(e => !!e).length > 0 ? optionElements : [ div('text-light-60 dark:text-dark-60 italic text-center px-2 py-1', [ text('Aucune option') ]) ], { placement: "bottom-start", class: ['flex flex-col max-h-[320px] overflow-auto', settings?.class?.popup], style: { 'min-width': `${select.clientWidth}px` }, blur: () => window.removeEventListener('keydown', handleKeys) });
|
context = followermenu(select, optionElements.filter(e => !!e).length > 0 ? optionElements : [ div('text-light-60 dark:text-dark-60 italic text-center px-2 py-1', [ text('Aucune option') ]) ], { placement: "bottom-start", class: [settings?.class?.popup, 'flex flex-col max-h-[320px] overflow-auto'], style: { 'min-width': `${select.clientWidth}px` }, blur: () => window.removeEventListener('keydown', handleKeys) });
|
||||||
} }, class: ['mx-4 inline-flex items-center justify-between px-3 text-sm font-semibold leading-none outline-hidden cursor-default h-8 gap-1 bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 hover:border-light-50 hover:dark:border-dark-50 data-[disabled]:border-light-25 dark: data-[disabled]:border-dark-25 data-[disabled]:bg-light-20 dark: data-[disabled]:bg-dark-20', settings?.class?.container], attributes: { tabindex: '0' } }, [ span('', valueText), icon('radix-icons:caret-down') ]) as HTMLDivElement & { value: T | undefined, disabled: boolean };
|
} }, class: [settings?.class?.container, 'mx-4 inline-flex items-center justify-between px-3 text-sm font-semibold leading-none outline-hidden cursor-default h-8 gap-1 bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 hover:border-light-50 hover:dark:border-dark-50 data-[disabled]:border-light-25 dark: data-[disabled]:border-dark-25 data-[disabled]:bg-light-20 dark: data-[disabled]:bg-dark-20'], attributes: { tabindex: '0' } }, [ span('', valueText), icon('radix-icons:caret-down') ]) as HTMLDivElement & { value: T | undefined, disabled: boolean };
|
||||||
|
|
||||||
Object.defineProperty(select, 'disabled', {
|
Object.defineProperty(select, 'disabled', {
|
||||||
get: () => disabled,
|
get: () => disabled,
|
||||||
@@ -164,7 +163,7 @@ export function select<T extends NonNullable<any>>(options: Reactive<Array<{ tex
|
|||||||
reactivity(options, (o) => {
|
reactivity(options, (o) => {
|
||||||
_options = o.filter(e => !!e);
|
_options = o.filter(e => !!e);
|
||||||
if(!_options.find(e => e.value === value)) select.value = undefined;
|
if(!_options.find(e => e.value === value)) select.value = undefined;
|
||||||
optionElements = _options.map((e, i) => dom('div', { listeners: { click: (_e) => { select.value = e.value ; 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 }));
|
optionElements = _options.map((e, i) => dom('div', { listeners: { click: (_e) => { select.value = e.value ; context.close(); }, mouseenter: (e) => focus(i) }, class: [settings?.class?.option, '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'], text: e.text }));
|
||||||
});
|
});
|
||||||
|
|
||||||
select.disabled = settings?.disabled ?? false;
|
select.disabled = settings?.disabled ?? false;
|
||||||
@@ -212,8 +211,8 @@ export function multiselect<T extends NonNullable<any>>(options: Option<T>[], se
|
|||||||
if(disabled) return;
|
if(disabled) return;
|
||||||
|
|
||||||
window.addEventListener('keydown', handleKeys);
|
window.addEventListener('keydown', handleKeys);
|
||||||
context = followermenu(select, optionElements.filter(e => !!e).length > 0 ? optionElements : [ div('text-light-60 dark:text-dark-60 italic text-center px-2 py-1', [ text('Aucune option') ]) ], { placement: "bottom-start", class: ['flex flex-col max-h-[320px] overflow-auto', settings?.class?.popup], style: { 'min-width': `${select.clientWidth}px` }, blur: () => window.removeEventListener('keydown', handleKeys) });
|
context = followermenu(select, optionElements.filter(e => !!e).length > 0 ? optionElements : [ div('text-light-60 dark:text-dark-60 italic text-center px-2 py-1', [ text('Aucune option') ]) ], { placement: "bottom-start", class: [settings?.class?.popup, 'flex flex-col max-h-[320px] overflow-auto'], style: { 'min-width': `${select.clientWidth}px` }, blur: () => window.removeEventListener('keydown', handleKeys) });
|
||||||
} }, class: ['mx-4 inline-flex items-center justify-between px-3 text-sm font-semibold leading-none outline-hidden cursor-default h-8 gap-1 bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 hover:border-light-50 hover:dark:border-dark-50 data-[disabled]:border-light-25 dark: data-[disabled]:border-dark-25 data-[disabled]:bg-light-20 dark: data-[disabled]:bg-dark-20', settings?.class?.container], attributes: { tabindex: '0' } }, [ span('', valueText), icon('radix-icons:caret-down') ]) as HTMLDivElement & { value: T[] | undefined, disabled: boolean };
|
} }, class: [settings?.class?.container, 'mx-4 inline-flex items-center justify-between px-3 text-sm font-semibold leading-none outline-hidden cursor-default h-8 gap-1 bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 hover:border-light-50 hover:dark:border-dark-50 data-[disabled]:border-light-25 dark: data-[disabled]:border-dark-25 data-[disabled]:bg-light-20 dark: data-[disabled]:bg-dark-20'], attributes: { tabindex: '0' } }, [ span('', valueText), icon('radix-icons:caret-down') ]) as HTMLDivElement & { value: T[] | undefined, disabled: boolean };
|
||||||
|
|
||||||
Object.defineProperty(select, 'disabled', {
|
Object.defineProperty(select, 'disabled', {
|
||||||
get: () => disabled,
|
get: () => disabled,
|
||||||
@@ -252,7 +251,7 @@ export function multiselect<T extends NonNullable<any>>(options: Option<T>[], se
|
|||||||
this.toggleAttribute('data-selected', idx === -1);
|
this.toggleAttribute('data-selected', idx === -1);
|
||||||
select.value = v;
|
select.value = v;
|
||||||
_e.ctrlKey || 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': settings?.defaultValue?.includes(e.value) ?? false } }, [ text(e.text), icon('radix-icons:check', { class: 'hidden group-data-[selected]:block' }) ]));
|
}, mouseenter: (e) => focus(i) }, class: [settings?.class?.option, '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'], attributes: { 'data-selected': settings?.defaultValue?.includes(e.value) ?? false } }, [ text(e.text), icon('radix-icons:check', { class: 'hidden group-data-[selected]:block' }) ]));
|
||||||
});
|
});
|
||||||
|
|
||||||
select.disabled = settings?.disabled ?? false;
|
select.disabled = settings?.disabled ?? false;
|
||||||
@@ -364,7 +363,7 @@ export function combobox<T extends NonNullable<any>>(options: RecurrentOption<T>
|
|||||||
|
|
||||||
const box = container.getBoundingClientRect();
|
const box = container.getBoundingClientRect();
|
||||||
focus();
|
focus();
|
||||||
context = followermenu(container, currentOptions.length > 0 ? currentOptions.map(e => e.dom) : [ div('text-light-60 dark:text-dark-60 italic text-center px-2 py-1', [ text('Aucune option') ]) ], { placement: "bottom-start", class: ['flex flex-col max-h-[320px] overflow-y-auto overflow-x-hidden', settings?.class?.popup], style: { "min-width": settings?.fill === 'cover' && `${box.width}px`, "max-width": settings?.fill === 'contain' && `${box.width}px` }, blur: hide });
|
context = followermenu(container, currentOptions.length > 0 ? currentOptions.map(e => e.dom) : [ div('text-light-60 dark:text-dark-60 italic text-center px-2 py-1', [ text('Aucune option') ]) ], { placement: "bottom-start", class: [settings?.class?.popup, 'flex flex-col max-h-[320px] overflow-y-auto overflow-x-hidden'], style: { "min-width": settings?.fill === 'cover' && `${box.width}px`, "max-width": settings?.fill === 'contain' && `${box.width}px` }, blur: hide });
|
||||||
if(!selected) container.classList.remove('!border-light-red', 'dark:!border-dark-red');
|
if(!selected) container.classList.remove('!border-light-red', 'dark:!border-dark-red');
|
||||||
};
|
};
|
||||||
const hide = () => {
|
const hide = () => {
|
||||||
@@ -397,12 +396,12 @@ export function combobox<T extends NonNullable<any>>(options: RecurrentOption<T>
|
|||||||
{
|
{
|
||||||
const children = option.value.map(render).filter(e => !!e);
|
const children = option.value.map(render).filter(e => !!e);
|
||||||
|
|
||||||
const stored = { item: option, dom: dom('div', { listeners: { click: () => progress(stored), 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 flex justify-between items-center', settings?.class?.option] }, [ text(option.text), icon('radix-icons:caret-right', { width: 20, height: 20 }) ]), container: div('flex flex-1 flex-col', [div('flex flex-row justify-between items-center text-light-100 dark:text-dark-100 py-1 px-2 text-sm select-none sticky top-0 bg-light-20 dark:bg-dark-20 font-semibold', [button(icon('radix-icons:caret-left', { width: 16, height: 16 }), back, 'p-px'), text(option.text), div()]), div('flex flex-col flex-1', children.map(e => e?.dom))]), children };
|
const stored = { item: option, dom: dom('div', { listeners: { click: () => progress(stored), mouseenter: () => focus(option.value) }, class: [settings?.class?.option, '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 flex justify-between items-center'] }, [ text(option.text), icon('radix-icons:caret-right', { width: 20, height: 20 }) ]), container: div('flex flex-1 flex-col', [div('flex flex-row justify-between items-center text-light-100 dark:text-dark-100 py-1 px-2 text-sm select-none sticky top-0 bg-light-20 dark:bg-dark-20 font-semibold', [button(icon('radix-icons:caret-left', { width: 16, height: 16 }), back, 'p-px'), text(option.text), div()]), div('flex flex-col flex-1', children.map(e => e?.dom))]), children };
|
||||||
return stored;
|
return stored;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return { item: option, dom: dom('div', { listeners: { click: (_e) => { container.value = option.value as T; !_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) ]) };
|
return { item: option, dom: dom('div', { listeners: { click: (_e) => { container.value = option.value as T; !_e.ctrlKey && hide(); }, mouseenter: () => focus(option.value) }, class: [settings?.class?.option, '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'] }, [ option?.render ? option?.render() : text(option.text) ]) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const filter = (value: string, option?: StoredRecurrentOption<T>): StoredRecurrentOption<T>[] => {
|
const filter = (value: string, option?: StoredRecurrentOption<T>): StoredRecurrentOption<T>[] => {
|
||||||
@@ -453,7 +452,7 @@ export function combobox<T extends NonNullable<any>>(options: RecurrentOption<T>
|
|||||||
}
|
}
|
||||||
} }, attributes: { type: 'text', }, class: 'flex-1 outline-hidden px-3 leading-none appearance-none py-1 bg-light-25 dark:bg-dark-25 disabled:bg-light-20 dark:disabled:bg-dark-20 w-full' });
|
} }, attributes: { type: 'text', }, class: 'flex-1 outline-hidden px-3 leading-none appearance-none py-1 bg-light-25 dark:bg-dark-25 disabled:bg-light-20 dark:disabled:bg-dark-20 w-full' });
|
||||||
|
|
||||||
const container = dom('label', { class: ['inline-flex outline-hidden px-3 items-center justify-between text-sm font-semibold leading-none gap-1 bg-light-25 dark:bg-dark-25 border border-light-35 dark:border-dark-35 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 hover:border-light-50 hover:dark:border-dark-50 data-[disabled]:border-light-25 dark:data-[disabled]:border-dark-25 data-[disabled]:bg-light-20 dark: data-[disabled]:bg-dark-20', settings?.class?.container] }, [ select, icon('radix-icons:caret-down') ]) as HTMLLabelElement & { disabled: boolean, value: T | undefined };
|
const container = dom('label', { class: [settings?.class?.container, 'inline-flex outline-hidden px-3 items-center justify-between text-sm font-semibold leading-none gap-1 bg-light-25 dark:bg-dark-25 border border-light-35 dark:border-dark-35 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 hover:border-light-50 hover:dark:border-dark-50 data-[disabled]:border-light-25 dark:data-[disabled]:border-dark-25 data-[disabled]:bg-light-20 dark: data-[disabled]:bg-dark-20'] }, [ select, icon('radix-icons:caret-down') ]) as HTMLLabelElement & { disabled: boolean, value: T | undefined };
|
||||||
let value: T | undefined = undefined;
|
let value: T | undefined = undefined;
|
||||||
|
|
||||||
Object.defineProperty(container, 'disabled', {
|
Object.defineProperty(container, 'disabled', {
|
||||||
@@ -520,7 +519,7 @@ export function tagpicker<T extends NonNullable<any>>(options: Reactive<Option<T
|
|||||||
const box = container.getBoundingClientRect();
|
const box = container.getBoundingClientRect();
|
||||||
focus();
|
focus();
|
||||||
currentOptions = optionElements.map(e => filter(select.value.toLowerCase().trim().normalize(), e)).filter(e => !!e);
|
currentOptions = optionElements.map(e => filter(select.value.toLowerCase().trim().normalize(), e)).filter(e => !!e);
|
||||||
context = followermenu(container, currentOptions.length > 0 ? currentOptions.map(e => e.dom) : [ div('text-light-60 dark:text-dark-60 italic text-center px-2 py-1', [ text('Aucune option') ]) ], { placement: "bottom-start", class: ['flex flex-col max-h-[320px] overflow-y-auto overflow-x-hidden', settings?.class?.popup], style: { "min-width": settings?.fill === 'cover' && `${box.width}px`, "max-width": settings?.fill === 'contain' && `${box.width}px` }, blur: hide });
|
context = followermenu(container, currentOptions.length > 0 ? currentOptions.map(e => e.dom) : [ div('text-light-60 dark:text-dark-60 italic text-center px-2 py-1', [ text('Aucune option') ]) ], { placement: "bottom-start", class: [settings?.class?.popup, 'flex flex-col max-h-[320px] overflow-y-auto overflow-x-hidden'], style: { "min-width": settings?.fill === 'cover' && `${box.width}px`, "max-width": settings?.fill === 'contain' && `${box.width}px` }, blur: hide });
|
||||||
};
|
};
|
||||||
const hide = () => {
|
const hide = () => {
|
||||||
context && context.container.parentElement && context.close();
|
context && context.container.parentElement && context.close();
|
||||||
@@ -537,7 +536,7 @@ export function tagpicker<T extends NonNullable<any>>(options: Reactive<Option<T
|
|||||||
if(option === undefined)
|
if(option === undefined)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
return { item: option, dom: dom('div', { listeners: { click: (_e) => { add(option.value); !_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) ]) };
|
return { item: option, dom: dom('div', { listeners: { click: (_e) => { add(option.value); !_e.ctrlKey && hide(); }, mouseenter: () => focus(option.value) }, class: [settings?.class?.option, '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'] }, [ option?.render ? option?.render() : text(option.text) ]) };
|
||||||
}
|
}
|
||||||
const filter = (_value: string, option?: StoredOption<T>): StoredOption<T> | undefined => {
|
const filter = (_value: string, option?: StoredOption<T>): StoredOption<T> | undefined => {
|
||||||
return option?.item?.text.toLowerCase().normalize().includes(_value) && !value.includes(option.item.value) ? option : undefined;
|
return option?.item?.text.toLowerCase().normalize().includes(_value) && !value.includes(option.item.value) ? option : undefined;
|
||||||
@@ -581,7 +580,7 @@ export function tagpicker<T extends NonNullable<any>>(options: Reactive<Option<T
|
|||||||
} }, attributes: { type: 'text', }, class: 'flex-1 outline-hidden px-3 leading-none appearance-none py-1 bg-light-10 dark:bg-dark-10 disabled:bg-light-20 dark:disabled:bg-dark-20 w-full' });
|
} }, attributes: { type: 'text', }, class: 'flex-1 outline-hidden px-3 leading-none appearance-none py-1 bg-light-10 dark:bg-dark-10 disabled:bg-light-20 dark:disabled:bg-dark-20 w-full' });
|
||||||
|
|
||||||
let value: T[] = reactive([]);
|
let value: T[] = reactive([]);
|
||||||
const container = dom('label', { class: ['inline-flex h-10 w-full outline-hidden px-1 items-center justify-between text-sm font-semibold leading-none border border-light-35 dark:border-dark-35 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 hover:border-light-50 hover:dark:border-dark-50 data-[disabled]:border-light-25 dark:data-[disabled]:border-dark-25 data-[disabled]:bg-light-20 dark: data-[disabled]:bg-dark-20 overflow-y-hidden overflow-x-auto no-scroll', settings?.class?.container] }, [ div('flex flex-row gap-2 item-center select-none py-1 shrink-0', { list: () => value, render: (v, _c) => _c ?? div('flex flex-row gap-2 items-center border border-light-35 dark:border-dark-35 py-1 ps-2 pe-1', [ span('text-sm', currentOptions.find(e => e.item?.value === v)?.item?.text), dom('span', { listeners: { click: () => remove(v) } }, [ icon('radix-icons:cross-1', { width: 10, height: 10, class: 'cursor-pointer text-light-60 dark:text-dark-60 hover:text-light-100 hover:dark:text-dark-100' }) ]) ]) }), select ]) as HTMLLabelElement & { disabled: boolean, value: T[] };
|
const container = dom('label', { class: [settings?.class?.container, 'inline-flex h-10 w-full outline-hidden px-1 items-center justify-between text-sm font-semibold leading-none border border-light-35 dark:border-dark-35 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 hover:border-light-50 hover:dark:border-dark-50 data-[disabled]:border-light-25 dark:data-[disabled]:border-dark-25 data-[disabled]:bg-light-20 dark: data-[disabled]:bg-dark-20 overflow-y-hidden overflow-x-auto no-scroll'] }, [ div('flex flex-row gap-2 item-center select-none py-1 shrink-0', { list: () => value, render: (v, _c) => _c ?? div('flex flex-row gap-2 items-center border border-light-35 dark:border-dark-35 py-1 ps-2 pe-1', [ span('text-sm', currentOptions.find(e => e.item?.value === v)?.item?.text), dom('span', { listeners: { click: () => remove(v) } }, [ icon('radix-icons:cross-1', { width: 10, height: 10, class: 'cursor-pointer text-light-60 dark:text-dark-60 hover:text-light-100 hover:dark:text-dark-100' }) ]) ]) }), select ]) as HTMLLabelElement & { disabled: boolean, value: T[] };
|
||||||
|
|
||||||
Object.defineProperty(container, 'disabled', {
|
Object.defineProperty(container, 'disabled', {
|
||||||
get: () => disabled,
|
get: () => disabled,
|
||||||
@@ -603,9 +602,9 @@ export function tagpicker<T extends NonNullable<any>>(options: Reactive<Option<T
|
|||||||
}
|
}
|
||||||
export function input(type: 'text' | 'number' | 'email' | 'password' | 'tel', settings?: { defaultValue?: string, change?: (value: string) => void, input?: (value: string) => void | boolean, focus?: () => void, blur?: () => void, class?: Class, disabled?: boolean, placeholder?: string }): HTMLInputElement
|
export function input(type: 'text' | 'number' | 'email' | 'password' | 'tel', settings?: { defaultValue?: string, change?: (value: string) => void, input?: (value: string) => void | boolean, focus?: () => void, blur?: () => void, class?: Class, disabled?: boolean, placeholder?: string }): HTMLInputElement
|
||||||
{
|
{
|
||||||
const input = dom("input", { attributes: { disabled: settings?.disabled, placeholder: settings?.placeholder, type: type }, class: [`mx-4 caret-light-50 dark:caret-dark-50 text-light-100 dark:text-dark-100 placeholder:text-light-50 dark:placeholder:text-dark-50
|
const input = dom("input", { attributes: { disabled: settings?.disabled, placeholder: settings?.placeholder, type: type }, class: [settings?.class, `mx-4 caret-light-50 dark:caret-dark-50 text-light-100 dark:text-dark-100 placeholder:text-light-50 dark:placeholder:text-dark-50
|
||||||
bg-light-20 dark:bg-dark-20 appearance-none outline-hidden px-3 py-1 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40
|
bg-light-20 dark:bg-dark-20 appearance-none outline-hidden px-3 py-1 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40
|
||||||
border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50 data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20`, settings?.class], listeners: {
|
border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50 data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20`], listeners: {
|
||||||
input: (e) => { if(settings?.input && settings.input(input.value) === false) input.value = value; else value = input.value; },
|
input: (e) => { if(settings?.input && settings.input(input.value) === false) input.value = value; else value = input.value; },
|
||||||
change: () => settings?.change && settings.change(input.value),
|
change: () => settings?.change && settings.change(input.value),
|
||||||
focus: settings?.focus,
|
focus: settings?.focus,
|
||||||
@@ -619,13 +618,19 @@ export function input(type: 'text' | 'number' | 'email' | 'password' | 'tel', se
|
|||||||
export function numberpicker(settings?: { defaultValue?: Reactive<number>, change?: (value: number) => void, input?: (value: number) => void, focus?: (value: number) => void, blur?: (value: number) => void, class?: Class, min?: number, max?: number, disabled?: Reactive<boolean> }): HTMLInputElement
|
export function numberpicker(settings?: { defaultValue?: Reactive<number>, change?: (value: number) => void, input?: (value: number) => void, focus?: (value: number) => void, blur?: (value: number) => void, class?: Class, min?: number, max?: number, disabled?: Reactive<boolean> }): HTMLInputElement
|
||||||
{
|
{
|
||||||
let storedValue = settings?.defaultValue ? typeof settings.defaultValue === 'function' ? settings.defaultValue() : settings.defaultValue : 0;
|
let storedValue = settings?.defaultValue ? typeof settings.defaultValue === 'function' ? settings.defaultValue() : settings.defaultValue : 0;
|
||||||
const validateAndChange = (value: number) => {
|
const parseRaw = () => field.value.trim().toLowerCase().normalize().replace(/[a-z]/g, "").replace(/,/g, ".");
|
||||||
|
const validateAndChange = (value: number, fromInput = false) => {
|
||||||
|
const raw = parseRaw();
|
||||||
if(isNaN(value))
|
if(isNaN(value))
|
||||||
field.value = '';
|
{
|
||||||
|
if(!fromInput || (raw !== '' && raw !== '.'))
|
||||||
|
field.value = '';
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
value = clamp(value, settings?.min ?? -Infinity, settings?.max ?? Infinity);
|
value = clamp(value, settings?.min ?? -Infinity, settings?.max ?? Infinity);
|
||||||
field.value = value.toString(10);
|
if(!fromInput || !raw.endsWith('.'))
|
||||||
|
field.value = value.toString(10);
|
||||||
if(storedValue !== value)
|
if(storedValue !== value)
|
||||||
{
|
{
|
||||||
storedValue = value;
|
storedValue = value;
|
||||||
@@ -634,8 +639,8 @@ export function numberpicker(settings?: { defaultValue?: Reactive<number>, chang
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const field = dom("input", { attributes: { disabled: settings?.disabled }, class: [`w-14 caret-light-50 dark:caret-dark-50 text-light-100 dark:text-dark-100 placeholder:text-light-50 dark:placeholder:text-dark-50 bg-light-20 dark:bg-dark-20 outline-hidden px-3 py-1 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50 disabled:shadow-none disabled:bg-light-20 dark:disabled:bg-dark-20 disabled:border-dashed disabled:border-light-35 dark:disabled:border-dark-35 disabled:border-2`, settings?.class], listeners: {
|
const field = dom("input", { attributes: { disabled: settings?.disabled }, class: [settings?.class, `w-14 caret-light-50 dark:caret-dark-50 text-light-100 dark:text-dark-100 placeholder:text-light-50 dark:placeholder:text-dark-50 bg-light-20 dark:bg-dark-20 outline-hidden px-3 py-1 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50 disabled:shadow-none disabled:bg-light-20 dark:disabled:bg-dark-20 disabled:border-dashed disabled:border-light-35 dark:disabled:border-dark-35 disabled:border-2`], listeners: {
|
||||||
input: () => validateAndChange(parseInt(field.value.trim().toLowerCase().normalize().replace(/[a-z,.]/g, ""), 10)) && settings?.input && settings.input(storedValue),
|
input: () => validateAndChange(parseFloat(parseRaw()), true) && settings?.input && settings.input(storedValue),
|
||||||
keydown: (e: KeyboardEvent) => {
|
keydown: (e: KeyboardEvent) => {
|
||||||
if(field.disabled)
|
if(field.disabled)
|
||||||
return;
|
return;
|
||||||
@@ -655,15 +660,16 @@ export function numberpicker(settings?: { defaultValue?: Reactive<number>, chang
|
|||||||
settings?.min && validateAndChange(settings.min) && settings?.input && settings.input(storedValue);
|
settings?.min && validateAndChange(settings.min) && settings?.input && settings.input(storedValue);
|
||||||
break;
|
break;
|
||||||
case "Enter":
|
case "Enter":
|
||||||
|
validateAndChange(parseFloat(parseRaw()));
|
||||||
settings?.change && settings.change(storedValue);
|
settings?.change && settings.change(storedValue);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
change: () => settings?.change && settings.change(storedValue),
|
change: () => { validateAndChange(parseFloat(parseRaw())); settings?.change && settings.change(storedValue); },
|
||||||
focus: () => settings?.focus && settings.focus(storedValue),
|
focus: () => settings?.focus && settings.focus(storedValue),
|
||||||
blur: () => settings?.blur && settings.blur(storedValue),
|
blur: () => { validateAndChange(parseFloat(parseRaw())); settings?.blur && settings.blur(storedValue); },
|
||||||
}});
|
}});
|
||||||
if(settings?.defaultValue !== undefined)
|
if(settings?.defaultValue !== undefined)
|
||||||
{
|
{
|
||||||
@@ -690,27 +696,27 @@ export function foldable(content: Reactive<NodeChildren>, title: NodeChildren, s
|
|||||||
}
|
}
|
||||||
settings?.onFold && settings.onFold(state);
|
settings?.onFold && settings.onFold(state);
|
||||||
}
|
}
|
||||||
const contentContainer = div(['hidden group-data-[active]:flex', settings?.class?.content]);
|
const contentContainer = div([settings?.class?.content, 'hidden group-data-[active]:flex']);
|
||||||
const fold = div(['group flex w-full flex-col', settings?.class?.container], [
|
const fold = div([settings?.class?.container, 'group flex w-full flex-col'], [
|
||||||
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' }) ]), div(['flex-1', settings?.class?.title], title) ]),
|
div('flex', [ dom('div', { listeners: { click: () => { display(fold.toggleAttribute('data-active')) } }, class: [settings?.class?.icon, 'flex justify-center items-center'] }, [ icon('radix-icons:caret-right', { class: 'group-data-[active]:rotate-90 origin-center' }) ]), div([settings?.class?.title, 'flex-1'], title) ]),
|
||||||
contentContainer
|
contentContainer
|
||||||
]);
|
]);
|
||||||
display(settings?.open ?? true);
|
display(settings?.open ?? true);
|
||||||
fold.toggleAttribute('data-active', settings?.open ?? true);
|
fold.toggleAttribute('data-active', settings?.open ?? true);
|
||||||
return fold;
|
return fold;
|
||||||
}
|
}
|
||||||
type TableRow = Record<string, (() => HTMLElement) | HTMLElement | string>;
|
type TableRow<T extends string> = Record<T, (() => HTMLElement) | HTMLElement | string>;
|
||||||
export function table(content: TableRow[], headers: TableRow, properties?: { class?: { table?: Class, header?: Class, body?: Class, row?: Class, cell?: Class } })
|
export function table<T extends string>(content: TableRow<T>[], headers: TableRow<T>, properties?: { class?: { table?: Class, header?: Class, body?: Class, row?: Class, cell?: Class } })
|
||||||
{
|
{
|
||||||
const render = (item: (() => HTMLElement) | HTMLElement | string) => typeof item === 'string' ? text(item) : typeof item === 'function' ? item() : item;
|
const render = (item: (() => HTMLElement) | HTMLElement | string) => typeof item === 'string' ? text(item) : typeof item === 'function' ? item() : item;
|
||||||
return dom('table', { class: ['', properties?.class?.table] }, [ dom('thead', { class: ['', properties?.class?.header] }, [ dom('tr', { class: '' }, Object.values(headers).map(e => dom('th', {}, [ render(e) ]))) ]), dom('tbody', { class: ['', properties?.class?.body] }, content.map(e => dom('tr', { class: ['', properties?.class?.row] }, Object.keys(headers).map(f => e.hasOwnProperty(f) ? dom('td', { class: ['', properties?.class?.cell] }, [ render(e[f]!) ]) : undefined)))) ]);
|
return dom('table', { class: [properties?.class?.table] }, [ dom('thead', { class: [properties?.class?.header] }, [ dom('tr', { class: '' }, Object.values(headers).map(e => dom('th', {}, [ render(e) ]))) ]), dom('tbody', { class: [properties?.class?.body] }, content.map(e => dom('tr', { class: [properties?.class?.row] }, Object.keys(headers).map(f => e.hasOwnProperty(f) ? dom('td', { class: [properties?.class?.cell] }, [ render(e[f]!) ]) : undefined)))) ]);
|
||||||
}
|
}
|
||||||
export function toggle(settings?: { defaultValue?: boolean, change?: (value: boolean) => void, disabled?: Reactive<boolean>, class?: { container?: Class } })
|
export function toggle(settings?: { defaultValue?: boolean, change?: (value: boolean) => void, disabled?: Reactive<boolean>, class?: { container?: Class } })
|
||||||
{
|
{
|
||||||
let state = settings?.defaultValue ?? false;
|
let state = settings?.defaultValue ?? false;
|
||||||
const element = dom("div", { class: [`group mx-3 w-12 h-6 select-none transition-all border border-light-35 dark:border-dark-35 bg-light-20 dark:bg-dark-20 outline-hidden
|
const element = dom("div", { class: [settings?.class?.container, `group mx-3 w-12 h-6 select-none transition-all border border-light-35 dark:border-dark-35 bg-light-20 dark:bg-dark-20 outline-hidden
|
||||||
data-[state=checked]:bg-light-35 dark:data-[state=checked]:bg-dark-35 hover:border-light-50 hover:dark:border-dark-50 focus:shadow-raw focus:shadow-light-40 focus:dark:shadow-dark-40
|
data-[state=checked]:bg-light-35 dark:data-[state=checked]:bg-dark-35 hover:border-light-50 hover:dark:border-dark-50 focus:shadow-raw focus:shadow-light-40 focus:dark:shadow-dark-40
|
||||||
data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20 relative py-[2px]`, settings?.class?.container], attributes: { "data-state": state ? "checked" : "unchecked", "data-disabled": settings?.disabled }, listeners: {
|
data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20 relative py-[2px]`], attributes: { "data-state": state ? "checked" : "unchecked", "data-disabled": settings?.disabled }, listeners: {
|
||||||
click: function(e: Event) {
|
click: function(e: Event) {
|
||||||
if(this.hasAttribute('data-disabled'))
|
if(this.hasAttribute('data-disabled'))
|
||||||
return;
|
return;
|
||||||
@@ -726,9 +732,9 @@ export function toggle(settings?: { defaultValue?: boolean, change?: (value: boo
|
|||||||
export function checkbox(settings?: { defaultValue?: boolean, change?: (this: HTMLElement, value: boolean) => void | boolean, disabled?: Reactive<boolean>, class?: { container?: Class, icon?: Class } })
|
export function checkbox(settings?: { defaultValue?: boolean, change?: (this: HTMLElement, value: boolean) => void | boolean, disabled?: Reactive<boolean>, class?: { container?: Class, icon?: Class } })
|
||||||
{
|
{
|
||||||
let state = settings?.defaultValue ?? false;
|
let state = settings?.defaultValue ?? false;
|
||||||
const element = dom("div", { class: [`group w-6 h-6 box-content flex items-center justify-center border border-light-50 dark:border-dark-50 bg-light-20 dark:bg-dark-20
|
const element = dom("div", { class: [settings?.class?.container, `group w-6 h-6 box-content flex items-center justify-center border border-light-50 dark:border-dark-50 bg-light-20 dark:bg-dark-20
|
||||||
cursor-pointer hover:bg-light-30 hover:dark:bg-dark-30 hover:border-light-60 hover:dark:border-dark-60
|
cursor-pointer hover:bg-light-30 hover:dark:bg-dark-30 hover:border-light-60 hover:dark:border-dark-60
|
||||||
data-[disabled]:cursor-default data-[disabled]:border-dashed data-[disabled]:border-light-40 dark:data-[disabled]:border-dark-40 data-[disabled]:bg-0 dark:data-[disabled]:bg-0 hover:data-[disabled]:bg-0 hover:dark:data-[disabled]:bg-0`, settings?.class?.container], attributes: { "data-state": state ? "checked" : "unchecked", "data-disabled": settings?.disabled }, listeners: {
|
data-[disabled]:cursor-default data-[disabled]:border-dashed data-[disabled]:border-light-40 dark:data-[disabled]:border-dark-40 data-[disabled]:bg-0 dark:data-[disabled]:bg-0 hover:data-[disabled]:bg-0 hover:dark:data-[disabled]:bg-0`], attributes: { "data-state": state ? "checked" : "unchecked", "data-disabled": settings?.disabled }, listeners: {
|
||||||
click: function(e: Event) {
|
click: function(e: Event) {
|
||||||
if(this.hasAttribute('data-disabled'))
|
if(this.hasAttribute('data-disabled'))
|
||||||
return;
|
return;
|
||||||
@@ -740,7 +746,7 @@ export function checkbox(settings?: { defaultValue?: boolean, change?: (this: HT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [ icon('radix-icons:check', { width: 14, height: 14, class: ['hidden group-data-[state="checked"]:block data-[disabled]:text-light-50 dark:data-[disabled]:text-dark-50', settings?.class?.icon] }), ]);
|
}, [ icon('radix-icons:check', { width: 14, height: 14, class: [settings?.class?.icon, 'hidden group-data-[state="checked"]:block data-[disabled]:text-light-50 dark:data-[disabled]:text-dark-50'] }), ]);
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
export function tabgroup(tabs: Array<Reactive<{ id: string, title: NodeChildren, content: Reactive<NodeChildren> } | undefined>>, settings?: { focused?: string, class?: { container?: Class, tabbar?: Class, title?: Class, content?: Class }, switch?: (tab: string) => void | boolean }): HTMLElement
|
export function tabgroup(tabs: Array<Reactive<{ id: string, title: NodeChildren, content: Reactive<NodeChildren> } | undefined>>, settings?: { focused?: string, class?: { container?: Class, tabbar?: Class, title?: Class, content?: Class }, switch?: (tab: string) => void | boolean }): HTMLElement
|
||||||
@@ -749,9 +755,9 @@ export function tabgroup(tabs: Array<Reactive<{ id: string, title: NodeChildren,
|
|||||||
|
|
||||||
const resolveTab = (t: typeof tabs[number]) => typeof t === 'function' ? t() : t;
|
const resolveTab = (t: typeof tabs[number]) => typeof t === 'function' ? t() : t;
|
||||||
|
|
||||||
const tabbar = div(['flex flex-row items-center gap-1', settings?.class?.tabbar]);
|
const tabbar = div([settings?.class?.tabbar, 'flex flex-row items-center gap-1']);
|
||||||
const content = div(['', settings?.class?.content]);
|
const content = div([settings?.class?.content]);
|
||||||
const container = div(['flex flex-col', settings?.class?.container], [tabbar, content]);
|
const container = div([settings?.class?.container, 'flex flex-col'], [tabbar, content]);
|
||||||
|
|
||||||
const render = () => {
|
const render = () => {
|
||||||
const resolved = tabs.map(resolveTab).filter((t): t is NonNullable<typeof t> => !!t);
|
const resolved = tabs.map(resolveTab).filter((t): t is NonNullable<typeof t> => !!t);
|
||||||
@@ -760,7 +766,7 @@ export function tabgroup(tabs: Array<Reactive<{ id: string, title: NodeChildren,
|
|||||||
focus = resolved[0]?.id ?? '';
|
focus = resolved[0]?.id ?? '';
|
||||||
|
|
||||||
const titles = resolved.map(t => dom('div', {
|
const titles = resolved.map(t => dom('div', {
|
||||||
class: ['px-2 py-1 border-b border-transparent hover:border-accent-blue data-[focus]:border-accent-blue data-[focus]:border-b-[3px] cursor-pointer', settings?.class?.title],
|
class: [settings?.class?.title, 'px-2 py-1 border-b border-transparent hover:border-accent-blue data-[focus]:border-accent-blue data-[focus]:border-b-[3px] cursor-pointer'],
|
||||||
attributes: focus === t.id ? { 'data-focus': '' } : {},
|
attributes: focus === t.id ? { 'data-focus': '' } : {},
|
||||||
listeners: { click: function() {
|
listeners: { click: function() {
|
||||||
if (this.hasAttribute('data-focus'))
|
if (this.hasAttribute('data-focus'))
|
||||||
@@ -952,7 +958,7 @@ export function floater(container: HTMLElement, content: NodeChildren | (() => N
|
|||||||
});
|
});
|
||||||
minimized = false;
|
minimized = false;
|
||||||
} } }, [icon('radix-icons:cross-1', { width: 12, height: 12, class: 'p-1' })]), 'Fermer', 'top') ]) : undefined,
|
} } }, [icon('radix-icons:cross-1', { width: 12, height: 12, class: 'p-1' })]), 'Fermer', 'top') ]) : undefined,
|
||||||
div(['group-data-[minimized]:hidden h-full group-data-[pinned]:h-[calc(100%-21px)] w-full group-data-[pinned]:min-h-[initial] group-data-[pinned]:min-w-[initial] group-data-[pinned]:max-h-[initial] group-data-[pinned]:max-w-[initial] overflow-auto box-content', settings?.class], typeof content === 'function' ? content() : content), dom('span', { class: 'hidden group-data-[pinned]:flex group-data-[minimized]:hidden absolute bottom-0 right-0 cursor-nw-resize z-50', listeners: { mousedown: resizestart } }, [ icon('ph:notches', { width: 12, height: 12 }) ])
|
div([settings?.class, 'group-data-[minimized]:hidden h-full group-data-[pinned]:h-[calc(100%-21px)] w-full group-data-[pinned]:min-h-[initial] group-data-[pinned]:min-w-[initial] group-data-[pinned]:max-h-[initial] group-data-[pinned]:max-w-[initial] overflow-auto box-content'], typeof content === 'function' ? content() : content), dom('span', { class: 'hidden group-data-[pinned]:flex group-data-[minimized]:hidden absolute bottom-0 right-0 cursor-nw-resize z-50', listeners: { mousedown: resizestart } }, [ icon('ph:notches', { width: 12, height: 12 }) ])
|
||||||
],
|
],
|
||||||
viewport,
|
viewport,
|
||||||
events: events
|
events: events
|
||||||
|
|||||||
@@ -184,7 +184,30 @@ export class Content
|
|||||||
if(!overview)
|
if(!overview)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
return { ...overview, content: Content.fromString(overview, (await Content.read(id, { create: true }))!) };
|
let content = await Content.read(id);
|
||||||
|
if(content === undefined)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const apiContent = await useRequestFetch()(`/api/file/content/${id}`, { cache: 'no-cache' });
|
||||||
|
if(apiContent)
|
||||||
|
{
|
||||||
|
content = apiContent;
|
||||||
|
if(overview.type !== 'folder')
|
||||||
|
Content.queue.queue(() => Content.write(id, content!, { create: true }));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
overview.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
overview.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...overview, content: Content.fromString(overview, content!) };
|
||||||
}
|
}
|
||||||
static set(id: string, overview?: Omit<LocalContent, 'content'> | Recursive<Omit<LocalContent, 'content'>>)
|
static set(id: string, overview?: Omit<LocalContent, 'content'> | Recursive<Omit<LocalContent, 'content'>>)
|
||||||
{
|
{
|
||||||
@@ -251,20 +274,6 @@ export class Content
|
|||||||
{
|
{
|
||||||
Content._overview[file.id] = file;
|
Content._overview[file.id] = file;
|
||||||
Content._reverseMapping[file.path] = file.id;
|
Content._reverseMapping[file.path] = file.id;
|
||||||
|
|
||||||
Content.queue.queue(() => {
|
|
||||||
return useRequestFetch()(`/api/file/content/${file.id}`, { cache: 'no-cache' }).then(async (content: string | undefined | null) => {
|
|
||||||
if(content)
|
|
||||||
{
|
|
||||||
if(file.type !== 'folder')
|
|
||||||
Content.queue.queue(() => Content.write(file.id, content, { create: true }));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Content._overview[file.id]!.error = true;
|
|
||||||
}).catch(e => {
|
|
||||||
Content._overview[file.id]!.error = true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deletable.splice(deletable.findIndex(e => e === file.id), 1);
|
deletable.splice(deletable.findIndex(e => e === file.id), 1);
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ export class MarkdownEditor
|
|||||||
{
|
{
|
||||||
this._dom = div('flex h-full relative', [ div('absolute -top-1 -left-1 -translate-x-px -translate-y-px z-10 group/editor', [
|
this._dom = div('flex h-full relative', [ div('absolute -top-1 -left-1 -translate-x-px -translate-y-px z-10 group/editor', [
|
||||||
div('group-hover/editor:hidden group-data-[focused]/editor:hidden w-0 h-0 border-8 border-transparent border-l-light-40 dark:border-l-dark-40 border-t-light-40 dark:border-t-dark-40'),
|
div('group-hover/editor:hidden group-data-[focused]/editor:hidden w-0 h-0 border-8 border-transparent border-l-light-40 dark:border-l-dark-40 border-t-light-40 dark:border-t-dark-40'),
|
||||||
button([icon('radix-icons:gear')], MarkdownEditor.settings, 'p-1 hidden group-data-[focused]/editor:block group-hover/editor:block') ]),
|
button([icon('radix-icons:gear')], MarkdownEditor.settings, 'p-1 hidden! group-data-[focused]/editor:block! group-hover/editor:block!') ]),
|
||||||
]);
|
]);
|
||||||
this._decoratorVisible = ViewPlugin.fromClass(Decorator, {
|
this._decoratorVisible = ViewPlugin.fromClass(Decorator, {
|
||||||
decorations: undefined,
|
decorations: undefined,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const a: Prose = {
|
|||||||
|
|
||||||
const link = overview ? { name: 'explore-path', params: { path: overview.path }, hash: hash } : href, nav = router.resolve(link);
|
const link = overview ? { name: 'explore-path', params: { path: overview.path }, hash: hash } : href, nav = router.resolve(link);
|
||||||
|
|
||||||
const element = properties?.navigate ?? true ? dom('a', { class: ['text-accent-blue inline-flex items-center', properties?.class], attributes: { href: nav.href }, listeners: {
|
const element = properties?.navigate ?? true ? dom('a', { class: [properties?.class, 'text-accent-blue inline-flex items-center'], attributes: { href: nav.href }, listeners: {
|
||||||
'click': (e) => {
|
'click': (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
router.push(link);
|
router.push(link);
|
||||||
@@ -29,7 +29,7 @@ export const a: Prose = {
|
|||||||
...(children ?? []),
|
...(children ?? []),
|
||||||
overview && overview.type !== 'markdown' ? icon(iconByType[overview.type], { class: 'w-4 h-4 inline-block', inline: true }) : undefined
|
overview && overview.type !== 'markdown' ? icon(iconByType[overview.type], { class: 'w-4 h-4 inline-block', inline: true }) : undefined
|
||||||
])
|
])
|
||||||
]) : dom('span', { class: ['cursor-pointer text-accent-blue inline-flex items-center', properties?.class] }, [
|
]) : dom('span', { class: [properties?.class, 'cursor-pointer text-accent-blue inline-flex items-center'] }, [
|
||||||
...(children ?? []),
|
...(children ?? []),
|
||||||
overview && overview.type !== 'markdown' ? icon(iconByType[overview.type], { class: 'w-4 h-4 inline-block', inline: true }) : undefined
|
overview && overview.type !== 'markdown' ? icon(iconByType[overview.type], { class: 'w-4 h-4 inline-block', inline: true }) : undefined
|
||||||
]);
|
]);
|
||||||
@@ -57,7 +57,7 @@ export const preview: Prose = {
|
|||||||
|
|
||||||
const overview = Content.getFromPath(pathname === '' && hash.length > 0 ? unifySlug(router.currentRoute.value.params.path ?? '') : pathname);
|
const overview = Content.getFromPath(pathname === '' && hash.length > 0 ? unifySlug(router.currentRoute.value.params.path ?? '') : pathname);
|
||||||
|
|
||||||
const element = dom('span', { class: ['cursor-pointer text-accent-blue inline-flex items-center', properties?.class] }, [
|
const element = dom('span', { class: [properties?.class, 'cursor-pointer text-accent-blue inline-flex items-center'] }, [
|
||||||
...(children ?? []),
|
...(children ?? []),
|
||||||
overview && overview.type !== 'markdown' ? icon(iconByType[overview.type], { class: 'w-4 h-4 inline-block', inline: true }) : undefined
|
overview && overview.type !== 'markdown' ? icon(iconByType[overview.type], { class: 'w-4 h-4 inline-block', inline: true }) : undefined
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user