Migration from tailwindcss v3 to v4. Deletion of nuxt/tailwindcss.

This commit is contained in:
2026-06-16 15:54:38 +02:00
parent a5317d6156
commit 8cbc25a601
44 changed files with 633 additions and 1145 deletions

View File

@@ -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" id="mainContainer"> <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">
<NuxtPage /> <NuxtPage />
</div> </div>
</NuxtLayout> </NuxtLayout>
@@ -36,6 +36,7 @@ onBeforeMount(() => {
</script> </script>
<style> <style>
@reference "./assets/css/main.css";
iconify-icon iconify-icon
{ {
display: inline-block; display: inline-block;
@@ -46,24 +47,22 @@ iconify-icon
.ToastRoot[data-type='error'] { .ToastRoot[data-type='error'] {
@apply border-light-red; @apply border-light-red;
@apply dark:border-dark-red; @apply dark:border-dark-red;
@apply bg-light-red; @apply bg-light-red/50;
@apply dark:bg-dark-red; @apply dark:bg-dark-red/50;
@apply !bg-opacity-50;
} }
.ToastRoot[data-type='success'] { .ToastRoot[data-type='success'] {
@apply border-light-green; @apply border-light-green;
@apply dark:border-dark-green; @apply dark:border-dark-green;
@apply bg-light-green; @apply bg-light-green/50;
@apply dark:bg-dark-green; @apply dark:bg-dark-green/50;
@apply !bg-opacity-50;
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 12px; width: 12px;
height: 12px; height: 12px;
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
@apply bg-light-40; @apply bg-light-40!;
@apply dark:bg-dark-40; @apply dark:bg-dark-40!;
@apply rounded-md; @apply rounded-md;
@apply border-2; @apply border-2;
@apply border-solid; @apply border-solid;
@@ -87,37 +86,37 @@ iconify-icon
.callout[data-type="summary"], .callout[data-type="summary"],
.callout[data-type="tldr"] .callout[data-type="tldr"]
{ {
@apply bg-light-cyan; @apply bg-light-cyan/25;
@apply dark:bg-dark-cyan; @apply dark:bg-dark-cyan/25;
@apply text-light-cyan; @apply text-light-cyan;
@apply dark:text-dark-cyan; @apply dark:text-dark-cyan;
} }
.callout[data-type="info"] .callout[data-type="info"]
{ {
@apply bg-light-blue; @apply bg-light-blue/25;
@apply dark:bg-dark-blue; @apply dark:bg-dark-blue/25;
@apply text-light-blue; @apply text-light-blue;
@apply dark:text-dark-blue; @apply dark:text-dark-blue;
} }
.callout[data-type="todo"] .callout[data-type="todo"]
{ {
@apply bg-light-blue; @apply bg-light-blue/25;
@apply dark:bg-dark-blue; @apply dark:bg-dark-blue/25;
@apply text-light-blue; @apply text-light-blue;
@apply dark:text-dark-blue; @apply dark:text-dark-blue;
} }
.callout[data-type="important"] .callout[data-type="important"]
{ {
@apply bg-light-cyan; @apply bg-light-cyan/25;
@apply dark:bg-dark-cyan; @apply dark:bg-dark-cyan/25;
@apply text-light-cyan; @apply text-light-cyan;
@apply dark:text-dark-cyan; @apply dark:text-dark-cyan;
} }
.callout[data-type="tip"], .callout[data-type="tip"],
.callout[data-type="hint"] .callout[data-type="hint"]
{ {
@apply bg-light-cyan; @apply bg-light-cyan/25;
@apply dark:bg-dark-cyan; @apply dark:bg-dark-cyan/25;
@apply text-light-cyan; @apply text-light-cyan;
@apply dark:text-dark-cyan; @apply dark:text-dark-cyan;
} }
@@ -125,8 +124,8 @@ iconify-icon
.callout[data-type="check"], .callout[data-type="check"],
.callout[data-type="done"] .callout[data-type="done"]
{ {
@apply bg-light-green; @apply bg-light-green/25;
@apply dark:bg-dark-green; @apply dark:bg-dark-green/25;
@apply text-light-green; @apply text-light-green;
@apply dark:text-dark-green; @apply dark:text-dark-green;
} }
@@ -134,8 +133,8 @@ iconify-icon
.callout[data-type="help"], .callout[data-type="help"],
.callout[data-type="faq"] .callout[data-type="faq"]
{ {
@apply bg-light-orange; @apply bg-light-orange/25;
@apply dark:bg-dark-orange; @apply dark:bg-dark-orange/25;
@apply text-light-orange; @apply text-light-orange;
@apply dark:text-dark-orange; @apply dark:text-dark-orange;
} }
@@ -143,8 +142,8 @@ iconify-icon
.callout[data-type="caution"], .callout[data-type="caution"],
.callout[data-type="attention"] .callout[data-type="attention"]
{ {
@apply bg-light-orange; @apply bg-light-orange/25;
@apply dark:bg-dark-orange; @apply dark:bg-dark-orange/25;
@apply text-light-orange; @apply text-light-orange;
@apply dark:text-dark-orange; @apply dark:text-dark-orange;
} }
@@ -152,30 +151,30 @@ iconify-icon
.callout[data-type="fail"], .callout[data-type="fail"],
.callout[data-type="missing"] .callout[data-type="missing"]
{ {
@apply bg-light-red; @apply bg-light-red/25;
@apply dark:bg-dark-red; @apply dark:bg-dark-red/25;
@apply text-light-red; @apply text-light-red;
@apply dark:text-dark-red; @apply dark:text-dark-red;
} }
.callout[data-type="danger"], .callout[data-type="danger"],
.callout[data-type="error"] .callout[data-type="error"]
{ {
@apply bg-light-red; @apply bg-light-red/25;
@apply dark:bg-dark-red; @apply dark:bg-dark-red/25;
@apply text-light-red; @apply text-light-red;
@apply dark:text-dark-red; @apply dark:text-dark-red;
} }
.callout[data-type="bug"] .callout[data-type="bug"]
{ {
@apply bg-light-red; @apply bg-light-red/25;
@apply dark:bg-dark-red; @apply dark:bg-dark-red/25;
@apply text-light-red; @apply text-light-red;
@apply dark:text-dark-red; @apply dark:text-dark-red;
} }
.callout[data-type="example"] .callout[data-type="example"]
{ {
@apply bg-light-purple; @apply bg-light-purple/25;
@apply dark:bg-dark-purple; @apply dark:bg-dark-purple/25;
@apply text-light-purple; @apply text-light-purple;
@apply dark:text-dark-purple; @apply dark:text-dark-purple;
} }
@@ -193,7 +192,7 @@ iconify-icon
} }
.cm-focused .cm-focused
{ {
@apply outline-none; @apply outline-hidden;
} }
.cm-editor .cm-content .cm-editor .cm-content
{ {
@@ -207,10 +206,10 @@ iconify-icon
.cm-tooltip-autocomplete { .cm-tooltip-autocomplete {
@apply max-w-[400px]; @apply max-w-[400px];
@apply !bg-light-20; @apply bg-light-20;
@apply dark:!bg-dark-20; @apply dark:bg-dark-20;
@apply !border-light-40; @apply border-light-40;
@apply dark:!border-dark-40; @apply dark:border-dark-40;
} }
/* .cm-tooltip-autocomplete > ul { /* .cm-tooltip-autocomplete > ul {
@@ -220,18 +219,18 @@ iconify-icon
.cm-tooltip-autocomplete > ul > li { .cm-tooltip-autocomplete > ul > li {
@apply flex; @apply flex;
@apply flex-col; @apply flex-col;
@apply !py-1; @apply py-1!;
@apply hover:bg-light-30; @apply hover:bg-light-30;
@apply dark:hover:bg-dark-30; @apply hover:dark:bg-dark-30;
} }
.cm-tooltip-autocomplete > ul > li[aria-selected] { .cm-tooltip-autocomplete > ul > li[aria-selected] {
@apply !bg-light-35; @apply bg-light-35!;
@apply dark:!bg-dark-35; @apply dark:bg-dark-35!;
} }
.cm-completionIcon { .cm-completionIcon {
@apply !hidden; @apply hidden!;
} }
.cm-completionLabel { .cm-completionLabel {
@@ -245,7 +244,7 @@ iconify-icon
.cm-completionMatchedText { .cm-completionMatchedText {
@apply font-bold; @apply font-bold;
@apply !no-underline; @apply no-underline!;
} }
.cm-completionDetail { .cm-completionDetail {

92
app/assets/css/main.css Normal file
View File

@@ -0,0 +1,92 @@
@import "tailwindcss";
@variant dark (&:where(.dark, .dark *));
@source "../../../shared";
@theme {
--color-transparent: transparent;
--color-current: currentColor;
--color-light-red: #e93147;
--color-light-orange: #FF9800;
--color-light-yellow: #FFEB3B;
--color-light-green: #388E3C;
--color-light-indigo: #7986CB;
--color-light-cyan: #00bfbc;
--color-light-lime: #8BC34A;
--color-light-blue: #086ddd;
--color-light-purple: #AB47BC;
--color-light-pink: #d53984;
--color-light-0: #ffffff;
--color-light-5: #fcfcfc;
--color-light-10: #fafafa;
--color-light-20: #f7f7f7;
--color-light-25: #e4e4e4;
--color-light-30: #dfdfdf;
--color-light-35: #d2d2d2;
--color-light-40: #bdbdbd;
--color-light-50: #ababab;
--color-light-60: #707070;
--color-light-70: #5c5c5c;
--color-light-100: #202020;
--color-dark-red: #fb464c;
--color-dark-redBack: #5A292B;
--color-dark-orange: #e9973f;
--color-dark-yellow: #e0de71;
--color-dark-green: #44cf6e;
--color-dark-greenBack: #284E34;
--color-dark-cyan: #53dfdd;
--color-dark-blue: #027aff;
--color-dark-purple: #a882ff;
--color-dark-pink: #fa99cd;
--color-dark-0: #1e1e1e;
--color-dark-5: #212121;
--color-dark-10: #242424;
--color-dark-20: #262626;
--color-dark-25: #2a2a2a;
--color-dark-30: #363636;
--color-dark-35: #3f3f3f;
--color-dark-40: #555555;
--color-dark-50: #666666;
--color-dark-60: #999999;
--color-dark-70: #b3b3b3;
--color-dark-100: #dadada;
--color-accent-purple: #43A047;
--color-accent-blue: #26C6DA;
--shadow-raw: 0 0 0 2px var(--tw-shadow-color);
--animate-slideDownAndFade: slideDownAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1);
--animate-slideLeftAndFade: slideLeftAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1);
--animate-slideUpAndFade: slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1);
--animate-slideRightAndFade: slideRightAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1);
--animate-contentShow: contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes slideDownAndFade {
from { opacity: 0; transform: translateY(-2px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideLeftAndFade {
from { opacity: 0; transform: translateX(2px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideUpAndFade {
from { opacity: 0; transform: translateY(2px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideRightAndFade {
from { opacity: 0; transform: translateX(-2px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes contentShow {
from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); }
to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

View File

@@ -1,6 +1,6 @@
<template> <template>
<button :disabled="disabled" class="text-light-100 dark:text-dark-100 font-semibold hover:bg-light-30 dark:hover:bg-dark-30 inline-flex items-center justify-center bg-light-25 dark:bg-dark-25 leading-none outline-none <button :disabled="disabled" class="text-light-100 dark:text-dark-100 font-semibold hover:bg-light-30 hover:dark:bg-dark-30 inline-flex items-center justify-center bg-light-25 dark:bg-dark-25 leading-none outline-hidden
border border-light-25 dark:border-dark-25 hover:border-light-30 dark:hover:border-dark-30 active:border-light-40 dark:active:border-dark-40 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 dark:focus:shadow-dark-40 border border-light-25 dark:border-dark-25 hover:border-light-30 hover:dark:border-dark-30 active:border-light-40 dark:active:border-dark-40 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40
disabled:bg-light-10 dark:disabled:bg-dark-10 disabled:border-none disabled:text-light-50 dark:disabled:text-dark-50" disabled:bg-light-10 dark:disabled:bg-dark-10 disabled:border-none disabled:text-light-50 dark:disabled:text-dark-50"
:class="{'p-1': loading || icon, 'h-[35px] px-[15px]': !loading && !icon}" @click="!loading && emit('click')"> :class="{'p-1': loading || icon, 'h-[35px] px-[15px]': !loading && !icon}" @click="!loading && emit('click')">
<Loading v-if="loading" /> <Loading v-if="loading" />

View File

@@ -3,9 +3,9 @@
<span class="pb-1 md:p-0">{{ label }}</span> <span class="pb-1 md:p-0">{{ label }}</span>
<ComboboxRoot v-model:model-value="model" v-model:open="open" :multiple="multiple"> <ComboboxRoot v-model:model-value="model" v-model:open="open" :multiple="multiple">
<ComboboxAnchor :disabled="disabled" class="mx-4 inline-flex min-w-[150px] items-center justify-between px-3 text-sm font-semibold leading-none h-8 gap-1 <ComboboxAnchor :disabled="disabled" class="mx-4 inline-flex min-w-[150px] items-center justify-between px-3 text-sm font-semibold leading-none h-8 gap-1
bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 outline-none data-[placeholder]:font-normal bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 outline-hidden data-[placeholder]:font-normal
data-[placeholder]:text-light-50 dark:data-[placeholder]:text-dark-50 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 dark:focus:shadow-dark-40 data-[placeholder]:text-light-50 dark:data-[placeholder]:text-dark-50 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40
hover:border-light-50 dark:hover:border-dark-50"> hover:border-light-50 hover:dark:border-dark-50">
<ComboboxTrigger class="flex flex-1 justify-between !cursor-pointer"> <ComboboxTrigger class="flex flex-1 justify-between !cursor-pointer">
<span v-if="!multiple">{{ model !== undefined ? options.find(e => e[1] === model)![0] : "" }}</span> <span v-if="!multiple">{{ model !== undefined ? options.find(e => e[1] === model)![0] : "" }}</span>
<span class="flex gap-2" v-else><span v-if="model !== undefined">{{ options.find(e => e[1] === (model as T[])[0]) !== undefined ? options.find(e => e[1] === (model as T[])[0])![0] : undefined }}</span><span v-if="model !== undefined && (model as T[]).length > 1">{{((model as T[]).length > 1 ? `+${(model as T[]).length - 1}` : "") }}</span></span> <span class="flex gap-2" v-else><span v-if="model !== undefined">{{ options.find(e => e[1] === (model as T[])[0]) !== undefined ? options.find(e => e[1] === (model as T[])[0])![0] : undefined }}</span><span v-if="model !== undefined && (model as T[]).length > 1">{{((model as T[]).length > 1 ? `+${(model as T[]).length - 1}` : "") }}</span></span>
@@ -16,7 +16,7 @@
<ComboboxPortal :disabled="disabled"> <ComboboxPortal :disabled="disabled">
<ComboboxContent :position="position" align="start" class="min-w-[150px] bg-light-20 dark:bg-dark-20 will-change-[opacity,transform] z-50"> <ComboboxContent :position="position" align="start" class="min-w-[150px] bg-light-20 dark:bg-dark-20 will-change-[opacity,transform] z-50">
<ComboboxViewport> <ComboboxViewport>
<ComboboxItem v-for="[label, value] of options" :value="value" :disabled="disabled" class="text-base py-2 leading-none text-light-60 dark:text-dark-60 flex items-center px-6 relative Combobox-none data-[disabled]:text-light-50 dark:data-[disabled]:text-dark-50 data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-light-30 dark:data-[highlighted]:bg-dark-30 data-[highlighted]:text-light-100 dark:data-[highlighted]:text-dark-100"> <ComboboxItem v-for="[label, value] of options" :value="value" :disabled="disabled" class="text-base py-2 leading-none text-light-60 dark:text-dark-60 flex items-center px-6 relative Combobox-none data-[disabled]:text-light-50 dark:data-[disabled]:text-dark-50 data-[disabled]:pointer-events-none data-[highlighted]:outline-hidden data-[highlighted]:bg-light-30 dark:data-[highlighted]:bg-dark-30 data-[highlighted]:text-light-100 dark:data-[highlighted]:text-dark-100">
<span class="">{{ label }}</span> <span class="">{{ label }}</span>
<ComboboxItemIndicator class="absolute left-1 w-4 inline-flex items-center justify-center"> <ComboboxItemIndicator class="absolute left-1 w-4 inline-flex items-center justify-center">
<Icon icon="radix-icons:check" /> <Icon icon="radix-icons:check" />

View File

@@ -7,7 +7,7 @@
<DialogTitle v-if="!!title" class="text-3xl font-light relative -top-2">{{ title }}</DialogTitle> <DialogTitle v-if="!!title" class="text-3xl font-light relative -top-2">{{ title }}</DialogTitle>
<DialogDescription v-if="!!description" class="text-base pb-2">{{ description }}</DialogDescription> <DialogDescription v-if="!!description" class="text-base pb-2">{{ description }}</DialogDescription>
<slot /> <slot />
<DialogClose v-if="iconClose" class="text-light-100 dark:text-dark-100 absolute top-2 right-2 inline-flex h-6 w-6 appearance-none items-center justify-center outline-none text-xl" aria-label="Close"> <DialogClose v-if="iconClose" class="text-light-100 dark:text-dark-100 absolute top-2 right-2 inline-flex h-6 w-6 appearance-none items-center justify-center outline-hidden text-xl" aria-label="Close">
<span aria-hidden>×</span> <span aria-hidden>×</span>
</DialogClose> </DialogClose>
</DialogContent> </DialogContent>

View File

@@ -1,7 +1,7 @@
<template> <template>
<template v-for="(item, idx) of options"> <template v-for="(item, idx) of options">
<template v-if="item.type === 'item'"> <template v-if="item.type === 'item'">
<DropdownMenuItem :disabled="item.disabled" :textValue="item.label" @select="item.select" :class="{'!pe-1': item.kbd}" class="cursor-pointer text-base text-light-100 dark:text-dark-100 leading-none flex items-center py-1.5 relative ps-7 pe-4 select-none outline-none data-[disabled]:text-light-60 dark:data-[disabled]:text-dark-60 data-[disabled]:pointer-events-none data-[highlighted]:bg-light-35 dark:data-[highlighted]:bg-dark-35"> <DropdownMenuItem :disabled="item.disabled" :textValue="item.label" @select="item.select" :class="{'!pe-1': item.kbd}" class="cursor-pointer text-base text-light-100 dark:text-dark-100 leading-none flex items-center py-1.5 relative ps-7 pe-4 select-none outline-hidden data-[disabled]:text-light-60 dark:data-[disabled]:text-dark-60 data-[disabled]:pointer-events-none data-[highlighted]:bg-light-35 dark:data-[highlighted]:bg-dark-35">
<Icon v-if="item.icon" :icon="item.icon" class="absolute left-1.5" /> <Icon v-if="item.icon" :icon="item.icon" class="absolute left-1.5" />
<div class="flex flex-1 justify-between"> <div class="flex flex-1 justify-between">
<span>{{ item.label }}</span> <span>{{ item.label }}</span>
@@ -11,7 +11,7 @@
</template> </template>
<template v-else-if="item.type === 'checkbox'"> <template v-else-if="item.type === 'checkbox'">
<DropdownMenuCheckboxItem :disabled="item.disabled" :textValue="item.label" v-model:checked="item.checked" @update:checked="item.select" class="cursor-pointer text-base text-light-100 dark:text-dark-100 leading-none flex items-center py-1.5 relative pe-4 select-none outline-none data-[disabled]:text-light-60 dark:data-[disabled]:text-dark-60 data-[disabled]:pointer-events-none data-[highlighted]:bg-light-35 dark:data-[highlighted]:bg-dark-35"> <DropdownMenuCheckboxItem :disabled="item.disabled" :textValue="item.label" v-model:checked="item.checked" @update:checked="item.select" class="cursor-pointer text-base text-light-100 dark:text-dark-100 leading-none flex items-center py-1.5 relative pe-4 select-none outline-hidden data-[disabled]:text-light-60 dark:data-[disabled]:text-dark-60 data-[disabled]:pointer-events-none data-[highlighted]:bg-light-35 dark:data-[highlighted]:bg-dark-35">
<span class="w-6 flex items-center justify-center"> <span class="w-6 flex items-center justify-center">
<DropdownMenuItemIndicator> <DropdownMenuItemIndicator>
<Icon icon="radix-icons:check" /> <Icon icon="radix-icons:check" />
@@ -41,13 +41,13 @@
<template v-if="item.type === 'submenu'"> <template v-if="item.type === 'submenu'">
<DropdownMenuSub> <DropdownMenuSub>
<DropdownMenuSubTrigger class="group cursor-pointer text-base text-light-100 dark:text-dark-100 leading-none flex items-center py-1.5 relative ps-7 select-none outline-none data-[disabled]:text-light-60 dark:data-[disabled]:text-dark-60 data-[disabled]:pointer-events-none data-[highlighted]:bg-light-35 dark:data-[highlighted]:bg-dark-35"> <DropdownMenuSubTrigger class="group cursor-pointer text-base text-light-100 dark:text-dark-100 leading-none flex items-center py-1.5 relative ps-7 select-none outline-hidden data-[disabled]:text-light-60 dark:data-[disabled]:text-dark-60 data-[disabled]:pointer-events-none data-[highlighted]:bg-light-35 dark:data-[highlighted]:bg-dark-35">
<Icon v-if="item.icon" :icon="item.icon" /> <Icon v-if="item.icon" :icon="item.icon" />
<span>{{ item.label }}</span> <span>{{ item.label }}</span>
<Icon icon="radix-icons:chevron-right" class="absolute right-1" /> <Icon icon="radix-icons:chevron-right" class="absolute right-1" />
</DropdownMenuSubTrigger> </DropdownMenuSubTrigger>
<DropdownMenuPortal> <DropdownMenuPortal>
<DropdownMenuSubContent class="z-50 outline-none bg-light-20 dark:bg-dark-20 will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade border border-light-35 dark:border-dark-35"> <DropdownMenuSubContent class="z-50 outline-hidden bg-light-20 dark:bg-dark-20 will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade border border-light-35 dark:border-dark-35">
<DropdownContentRender :options="item.items" /> <DropdownContentRender :options="item.items" />
</DropdownMenuSubContent> </DropdownMenuSubContent>
</DropdownMenuPortal> </DropdownMenuPortal>

View File

@@ -3,7 +3,7 @@
<DropdownMenuTrigger :disabled="disabled"><slot /></DropdownMenuTrigger> <DropdownMenuTrigger :disabled="disabled"><slot /></DropdownMenuTrigger>
<DropdownMenuPortal> <DropdownMenuPortal>
<DropdownMenuContent :align="align" :side="side" class="z-50 outline-none bg-light-20 dark:bg-dark-20 will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade border border-light-35 dark:border-dark-35"> <DropdownMenuContent :align="align" :side="side" class="z-50 outline-hidden bg-light-20 dark:bg-dark-20 will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade border border-light-35 dark:border-dark-35">
<DropdownContentRender :options="options" /> <DropdownContentRender :options="options" />
<DropdownMenuArrow class="fill-light-35 dark:fill-dark-35" /> <DropdownMenuArrow class="fill-light-35 dark:fill-dark-35" />

View File

@@ -1,6 +1,6 @@
<template> <template>
<HoverCardRoot :open-delay="delay" @update:open="(...args) => emits('open', ...args)"> <HoverCardRoot :open-delay="delay" @update:open="(...args) => emits('open', ...args)">
<HoverCardTrigger class="inline-block cursor-help outline-none"> <HoverCardTrigger class="inline-block cursor-help outline-hidden">
<slot></slot> <slot></slot>
</HoverCardTrigger> </HoverCardTrigger>
<HoverCardPortal v-if="!disabled"> <HoverCardPortal v-if="!disabled">

View File

@@ -1,3 +1,3 @@
<template> <template>
<span class="rounded bg-light-35 dark:bg-dark-35 font-mono text-sm px-1 py-0 select-none" style="box-shadow: black 0 2px 0 1px;"><slot /></span> <span class="rounded-sm bg-light-35 dark:bg-dark-35 font-mono text-sm px-1 py-0 select-none" style="box-shadow: black 0 2px 0 1px;"><slot /></span>
</template> </template>

View File

@@ -1,9 +1,9 @@
<template> <template>
<Label class="my-2 flex">{{ label }} <Label class="my-2 flex">{{ label }}
<NumberFieldRoot :min="min" :max="max" v-model="model" :disabled="disabled" :step="step" class="ms-4 flex justify-center border border-light-35 dark:border-dark-35 bg-light-20 dark:bg-dark-20 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20 data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20 <NumberFieldRoot :min="min" :max="max" v-model="model" :disabled="disabled" :step="step" class="ms-4 flex justify-center border border-light-35 dark:border-dark-35 bg-light-20 dark:bg-dark-20 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20 data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20
data-[disabled]:text-light-70 dark:data-[disabled]:text-dark-70 hover:border-light-50 dark:hover:border-dark-50 has-[:focus]:shadow-raw transition-[box-shadow] has-[:focus]:shadow-light-40 dark:has-[:focus]:shadow-dark-40"> data-[disabled]:text-light-70 dark:data-[disabled]:text-dark-70 hover:border-light-50 hover:dark:border-dark-50 has-[:focus]:shadow-raw transition-[box-shadow] has-[:focus]:shadow-light-40 dark:has-[:focus]:shadow-dark-40">
<NumberFieldDecrement class="data-[disabled]:opacity-50 px-1"><Icon icon="radix-icons:minus" :inline="true" class="w-6 text-light-100 dark:text-dark-100 opacity-100" /></NumberFieldDecrement> <NumberFieldDecrement class="data-[disabled]:opacity-50 px-1"><Icon icon="radix-icons:minus" :inline="true" class="w-6 text-light-100 dark:text-dark-100 opacity-100" /></NumberFieldDecrement>
<NumberFieldInput class="text-sm tabular-nums w-20 appearance-none bg-transparent px-2 py-1 outline-none caret-light-50 dark:caret-dark-50" /> <NumberFieldInput class="text-sm tabular-nums w-20 appearance-none bg-transparent px-2 py-1 outline-hidden caret-light-50 dark:caret-dark-50" />
<NumberFieldIncrement class="data-[disabled]:opacity-50 px-1"><Icon icon="radix-icons:plus" :inline="true" class="w-6 text-light-100 dark:text-dark-100 opacity-100" /></NumberFieldIncrement> <NumberFieldIncrement class="data-[disabled]:opacity-50 px-1"><Icon icon="radix-icons:plus" :inline="true" class="w-6 text-light-100 dark:text-dark-100 opacity-100" /></NumberFieldIncrement>
</NumberFieldRoot> </NumberFieldRoot>
</Label> </Label>

View File

@@ -1,9 +1,9 @@
<template> <template>
<Label class="my-2">{{ label }} <Label class="my-2">{{ label }}
<PinInputRoot :disabled="disabled" :default-value="model?.split('')" @update:model-value="(v) => model = v.join('')" @complete="() => emit('complete')" class="flex gap-2 items-center mt-1"> <PinInputRoot :disabled="disabled" :default-value="model?.split('')" @update:model-value="(v) => model = v.join('')" @complete="() => emit('complete')" class="flex gap-2 items-center mt-1">
<PinInputInput :type="hidden ? 'password' : undefined" v-for="(id, index) in amount" :key="id" :index="index" class="border border-light-35 dark:border-dark-35 w-10 h-10 outline-none <PinInputInput :type="hidden ? 'password' : undefined" v-for="(id, index) in amount" :key="id" :index="index" class="border border-light-35 dark:border-dark-35 w-10 h-10 outline-hidden
bg-light-20 dark:bg-dark-20 text-center text-light-100 dark:text-dark-100 placeholder:text-light-60 dark:placeholder:text-dark-60 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20 data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20 bg-light-20 dark:bg-dark-20 text-center text-light-100 dark:text-dark-100 placeholder:text-light-60 dark:placeholder:text-dark-60 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20 data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20
data-[disabled]:text-light-70 dark:data-[disabled]:text-dark-70 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 dark:focus:shadow-dark-40 caret-light-50 dark:caret-dark-50" /> data-[disabled]:text-light-70 dark:data-[disabled]:text-dark-70 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 caret-light-50 dark:caret-dark-50" />
</PinInputRoot> </PinInputRoot>
</Label> </Label>
</template> </template>

View File

@@ -3,8 +3,8 @@
<Label v-for="option in options" class="flex items-center gap-2"> <Label v-for="option in options" class="flex items-center gap-2">
<RadioGroupItem :disabled="(option as RadioOption).disabled ?? false" <RadioGroupItem :disabled="(option as RadioOption).disabled ?? false"
:value="(option as RadioOption).value ?? option" :value="(option as RadioOption).value ?? option"
class="border border-light-60 dark:border-dark-35 bg-light-20 dark:bg-dark-25 w-5 h-5 outline-none cursor-default hover:border-light-70 dark:hover:border-dark-40 class="border border-light-60 dark:border-dark-35 bg-light-20 dark:bg-dark-25 w-5 h-5 outline-hidden cursor-default hover:border-light-70 hover:dark:border-dark-40
focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 dark:focus:shadow-dark-40 data-[disabled]:bg-light-10 dark:data-[disabled]:bg-dark-10 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20"> focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40 data-[disabled]:bg-light-10 dark:data-[disabled]:bg-dark-10 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20">
<RadioGroupIndicator> <RadioGroupIndicator>
<Icon icon="radix-icons:check" class="relative w-5 h-5 -top-px -left-px" /> <Icon icon="radix-icons:check" class="relative w-5 h-5 -top-px -left-px" />
</RadioGroupIndicator> </RadioGroupIndicator>

View File

@@ -3,9 +3,9 @@
<span class="pb-1 md:p-0">{{ label }}</span> <span class="pb-1 md:p-0">{{ label }}</span>
<SelectRoot v-model="model" :default-value="defaultValue"> <SelectRoot v-model="model" :default-value="defaultValue">
<SelectTrigger :disabled="disabled" class="mx-4 inline-flex min-w-[160px] items-center justify-between px-3 text-sm font-semibold leading-none h-8 gap-1 <SelectTrigger :disabled="disabled" class="mx-4 inline-flex min-w-[160px] items-center justify-between px-3 text-sm font-semibold leading-none h-8 gap-1
bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 outline-none data-[placeholder]:font-normal bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 outline-hidden data-[placeholder]:font-normal
data-[placeholder]:text-light-50 dark:data-[placeholder]:text-dark-50 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 dark:focus:shadow-dark-40 data-[placeholder]:text-light-50 dark:data-[placeholder]:text-dark-50 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 focus:dark:shadow-dark-40
hover:border-light-50 dark:hover:border-dark-50"> hover:border-light-50 hover:dark:border-dark-50">
<SelectValue :placeholder="placeholder" /> <SelectValue :placeholder="placeholder" />
<Icon icon="radix-icons:caret-down" class="h-4 w-4" /> <Icon icon="radix-icons:caret-down" class="h-4 w-4" />
</SelectTrigger> </SelectTrigger>

View File

@@ -1,5 +1,5 @@
<template> <template>
<SelectItem :value="value" :disabled="disabled" class="text-base py-2 leading-none text-light-60 dark:text-dark-60 flex items-center px-6 relative select-none data-[disabled]:text-light-50 dark:data-[disabled]:text-dark-50 data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-light-30 dark:data-[highlighted]:bg-dark-30 data-[highlighted]:text-light-100 dark:data-[highlighted]:text-dark-100"> <SelectItem :value="value" :disabled="disabled" class="text-base py-2 leading-none text-light-60 dark:text-dark-60 flex items-center px-6 relative select-none data-[disabled]:text-light-50 dark:data-[disabled]:text-dark-50 data-[disabled]:pointer-events-none data-[highlighted]:outline-hidden data-[highlighted]:bg-light-30 dark:data-[highlighted]:bg-dark-30 data-[highlighted]:text-light-100 dark:data-[highlighted]:text-dark-100">
<SelectItemText class="">{{ label }}</SelectItemText> <SelectItemText class="">{{ label }}</SelectItemText>
<SelectItemIndicator class="absolute left-1 w-4 inline-flex items-center justify-center"> <SelectItemIndicator class="absolute left-1 w-4 inline-flex items-center justify-center">
<Icon icon="radix-icons:check" /> <Icon icon="radix-icons:check" />

View File

@@ -7,8 +7,8 @@
<SliderRange class="absolute bg-light-40 dark:bg-dark-40 h-full data-[disabled]:bg-light-30 dark:data-[disabled]:bg-dark-30" /> <SliderRange class="absolute bg-light-40 dark:bg-dark-40 h-full data-[disabled]:bg-light-30 dark:data-[disabled]:bg-dark-30" />
</SliderTrack> </SliderTrack>
<SliderThumb <SliderThumb
class="block w-5 h-5 bg-light-50 dark:bg-dark-50 outline-none focus:shadow-raw transition-[box-shadow] focus:shadow-light-60 dark:focus:shadow-dark-60 border border-light-50 dark:border-dark-50 class="block w-5 h-5 bg-light-50 dark:bg-dark-50 outline-hidden focus:shadow-raw transition-[box-shadow] focus:shadow-light-60 focus:dark:shadow-dark-60 border border-light-50 dark:border-dark-50
hover:border-light-60 dark:hover:border-dark-60 data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20" /> hover:border-light-60 hover:dark:border-dark-60 data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20" />
</SliderRoot> </SliderRoot>
</Label> </Label>
</template> </template>

View File

@@ -2,8 +2,8 @@
<Label class="flex justify-center items-center my-2"> <Label class="flex justify-center items-center my-2">
<span class="md:text-base text-sm">{{ label }}</span> <span class="md:text-base text-sm">{{ label }}</span>
<SwitchRoot v-model:checked="model" :disabled="disabled" :default-checked="defaultValue" <SwitchRoot v-model:checked="model" :disabled="disabled" :default-checked="defaultValue"
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-none 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
data-[state=checked]:bg-light-35 dark:data-[state=checked]:bg-dark-35 hover:border-light-50 dark:hover:border-dark-50 focus:shadow-raw focus:shadow-light-40 dark:focus: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"> data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20 relative">
<SwitchThumb <SwitchThumb
class="block w-[18px] h-[18px] translate-x-[2px] will-change-transform transition-transform bg-light-50 dark:bg-dark-50 data-[state=checked]:translate-x-[26px] class="block w-[18px] h-[18px] translate-x-[2px] will-change-transform transition-transform bg-light-50 dark:bg-dark-50 data-[state=checked]:translate-x-[26px]

View File

@@ -1,13 +1,13 @@
<template> <template>
<TagsInputRoot v-model="model" addOnPaste class="flex gap-2 items-center border p-2 w-full flex-wrap border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10" > <TagsInputRoot v-model="model" addOnPaste class="flex gap-2 items-center border p-2 w-full flex-wrap border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10" >
<TagsInputItem v-for="item in model" :key="item" :value="item" class="text-light-100 dark:text-dark-100 flex items-center justify-center gap-2 bg-light-20 dark:bg-dark-20 hover:bg-light-35 dark:hover:bg-dark-35 p-1 border border-light-35 dark:border-dark-35"> <TagsInputItem v-for="item in model" :key="item" :value="item" class="text-light-100 dark:text-dark-100 flex items-center justify-center gap-2 bg-light-20 dark:bg-dark-20 hover:bg-light-35 hover:dark:bg-dark-35 p-1 border border-light-35 dark:border-dark-35">
<TagsInputItemText class="text-sm pl-1" /> <TagsInputItemText class="text-sm pl-1" />
<TagsInputItemDelete asChild> <TagsInputItemDelete asChild>
<Icon icon="radix-icons:cross-2" class="w-4 h-4 cursor-pointer" /> <Icon icon="radix-icons:cross-2" class="w-4 h-4 cursor-pointer" />
</TagsInputItemDelete> </TagsInputItemDelete>
</TagsInputItem> </TagsInputItem>
<TagsInputInput :placeholder="placeholder" class="text-sm focus:outline-none flex-1 rounded text-green9 bg-transparent placeholder:text-mauve9 px-1" /> <TagsInputInput :placeholder="placeholder" class="text-sm focus:outline-hidden flex-1 rounded-sm text-green9 bg-transparent placeholder:text-mauve9 px-1" />
</TagsInputRoot> </TagsInputRoot>
</template> </template>

View File

@@ -3,8 +3,8 @@
<span class="pb-1 md:p-0">{{ label }}</span> <span class="pb-1 md:p-0">{{ label }}</span>
<input :placeholder="placeholder" :disabled="disabled" <input :placeholder="placeholder" :disabled="disabled"
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 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-none px-3 py-1 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 dark:focus: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 dark:hover: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" 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"
:type="type" v-model="model" :data-disabled="disabled || undefined" v-bind="$attrs" @change="(e) => emits('change', e)" @input="(e) => emits('input', e)"> :type="type" v-model="model" :data-disabled="disabled || undefined" v-bind="$attrs" @change="(e) => emits('change', e)" @input="(e) => emits('input', e)">
</Label> </Label>
</template> </template>

View File

@@ -1,6 +1,6 @@
<template> <template>
<TreeRoot v-slot="{ flattenItems }" class="list-none select-none text-light-100 dark:text-dark-100 text-sm" :items="model" :get-key="getKey" :defaultExpanded="flatten(model)"> <TreeRoot v-slot="{ flattenItems }" class="list-none select-none text-light-100 dark:text-dark-100 text-sm" :items="model" :get-key="getKey" :defaultExpanded="flatten(model)">
<TreeItem v-for="item in flattenItems" v-slot="{ isExpanded }" :key="item._id" :style="{ 'padding-left': `${item.level / 2 - 0.5}em` }" v-bind="item.bind" class="flex items-center ps-2 outline-none relative cursor-pointer"> <TreeItem v-for="item in flattenItems" v-slot="{ isExpanded }" :key="item._id" :style="{ 'padding-left': `${item.level / 2 - 0.5}em` }" v-bind="item.bind" class="flex items-center ps-2 outline-hidden relative cursor-pointer">
<slot :isExpanded="isExpanded" :item="item" /> <slot :isExpanded="isExpanded" :item="item" />
</TreeItem> </TreeItem>
</TreeRoot> </TreeRoot>

View File

@@ -9,10 +9,10 @@
<div class="text-3xl">Une erreur est survenue.</div> <div class="text-3xl">Une erreur est survenue.</div>
</div> </div>
<pre class="text-center text-wrap">Erreur {{ error?.statusCode }}: {{ error?.message }}</pre> <pre class="text-center text-wrap">Erreur {{ error?.statusCode }}: {{ error?.message }}</pre>
<button class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow] <button 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 dark:hover:bg-dark-25 hover:border-light-50 dark:hover: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 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 p-2" @click="handleError">Revenir en lieu sûr</button> 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 p-2" @click="handleError">Revenir en lieu sûr</button>
</div> </div>
</template> </template>

View File

@@ -12,10 +12,10 @@
<div class="flex flex-col flex-1 h-full w-full"> <div class="flex flex-col flex-1 h-full w-full">
<div class="flex flex-row w-full border-b border-light-30 dark:border-dark-30"> <div class="flex flex-row w-full border-b border-light-30 dark:border-dark-30">
<div class="w-[var(--sidebar-width)] z-40 flex flex-row gap-4 items-center justify-between px-4"> <div class="w-[var(--sidebar-width)] z-40 flex flex-row gap-4 items-center justify-between px-4">
<div class="inline-flex justify-center items-center outline-none 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 dark:hover:bg-dark-25 hover:border-light-50 dark:hover: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 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 p-1" @click="() => open = !open"><Icon :icon="open ? 'radix-icons:pin-left' : 'radix-icons:pin-right'" height="12" width="12"/></div> 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 p-1" @click="() => open = !open"><Icon :icon="open ? 'radix-icons:pin-left' : 'radix-icons:pin-right'" height="12" width="12"/></div>
<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" />
@@ -29,11 +29,11 @@
<NavigationMenuList class="flex items-center gap-8 max-md:hidden"> <NavigationMenuList class="flex items-center gap-8 max-md:hidden">
<NavigationMenuItem> <NavigationMenuItem>
<NavigationMenuTrigger> <NavigationMenuTrigger>
<NuxtLink :href="{ name: 'character' }" class="flex flex-row gap-2 items-center border-b-2 border-transparent hover:border-accent-blue py-4 select-none" active-class="!text-accent-blue"><span class="px-3 flex-1 truncate">Personnages</span><Icon icon="radix-icons:caret-down" /></NuxtLink> <NuxtLink :href="{ name: 'character' }" class="flex flex-row gap-2 items-center border-b-2 border-transparent hover:border-accent-blue py-4 select-none" active-class="text-accent-blue"><span class="px-3 flex-1 truncate">Personnages</span><Icon icon="radix-icons:caret-down" /></NuxtLink>
</NavigationMenuTrigger> </NavigationMenuTrigger>
<NavigationMenuContent class="absolute top-0 w-full sm:w-auto bg-light-0 dark:bg-dark-0 border border-light-30 dark:border-dark-30 py-2 z-20 flex flex-col"> <NavigationMenuContent class="absolute top-0 w-full sm:w-auto bg-light-0 dark:bg-dark-0 border border-light-30 dark:border-dark-30 py-2 z-20 flex flex-col">
<NuxtLink :href="{ name: 'character-list' }" class="hover:bg-light-30 dark:hover:bg-dark-30 px-4 py-2 select-none" active-class="!text-accent-blue"><span class="flex-1 truncate">Personnages publics</span></NuxtLink> <NuxtLink :href="{ name: 'character-list' }" class="hover:bg-light-30 hover:dark:bg-dark-30 px-4 py-2 select-none" active-class="text-accent-blue"><span class="flex-1 truncate">Personnages publics</span></NuxtLink>
<NuxtLink :href="{ name: 'character-id-edit', params: { id: 'new' } }" class="hover:bg-light-30 dark:hover:bg-dark-30 px-4 py-2 select-none" active-class="!text-accent-blue"><span class="flex-1 truncate">Nouveau personnage</span></NuxtLink> <NuxtLink :href="{ name: 'character-id-edit', params: { id: 'new' } }" class="hover:bg-light-30 hover:dark:bg-dark-30 px-4 py-2 select-none" active-class="text-accent-blue"><span class="flex-1 truncate">Nouveau personnage</span></NuxtLink>
</NavigationMenuContent> </NavigationMenuContent>
</NavigationMenuItem> </NavigationMenuItem>
</NavigationMenuList> </NavigationMenuList>
@@ -41,15 +41,15 @@
<NavigationMenuViewport class="h-[var(--radix-navigation-menu-viewport-height)] w-full origin-[top_center] flex justify-center overflow-hidden sm:w-[var(--radix-navigation-menu-viewport-width)]" /> <NavigationMenuViewport class="h-[var(--radix-navigation-menu-viewport-height)] w-full origin-[top_center] flex justify-center overflow-hidden sm:w-[var(--radix-navigation-menu-viewport-width)]" />
</div> </div>
</NavigationMenuRoot> </NavigationMenuRoot>
<NuxtLink :href="{ name: 'campaign' }" class="flex flex-row gap-2 items-center border-b-2 border-transparent hover:border-accent-blue py-4 select-none" active-class="!text-accent-blue"><span class="px-3 flex-1 truncate">Campagnes</span></NuxtLink> <NuxtLink :href="{ name: 'campaign' }" class="flex flex-row gap-2 items-center border-b-2 border-transparent hover:border-accent-blue py-4 select-none" active-class="text-accent-blue"><span class="px-3 flex-1 truncate">Campagnes</span></NuxtLink>
</div> </div>
<div class="flex flex-row gap-16 items-center"> <div class="flex flex-row gap-16 items-center">
<template v-if="!loggedIn"> <template v-if="!loggedIn">
<NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 dark:hover:text-dark-70" :to="{ name: 'user-login' }">Se connecter</NuxtLink> <NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 hover:dark:text-dark-70" :to="{ name: 'user-login' }">Se connecter</NuxtLink>
<NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 dark:hover:text-dark-70 max-md:hidden" :to="{ name: 'user-register' }">Créer un compte</NuxtLink> <NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 hover:dark:text-dark-70 max-md:hidden" :to="{ name: 'user-register' }">Créer un compte</NuxtLink>
</template> </template>
<template v-else> <template v-else>
<NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 dark:hover:text-dark-70" :to="{ name: 'user-login' }">{{ user!.username }}</NuxtLink> <NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 hover:dark:text-dark-70" :to="{ name: 'user-login' }">{{ user!.username }}</NuxtLink>
</template> </template>
</div> </div>
</div> </div>
@@ -89,13 +89,13 @@ onMounted(() => {
treeParent.value.replaceChildren(loading('normal')); treeParent.value.replaceChildren(loading('normal'));
Content.ready.then(() => { Content.ready.then(() => {
tree = new TreeDOM((item, depth) => { tree = new TreeDOM((item, depth) => {
return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-inline-start': `${depth / 1.5}em` } }, [dom('div', { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full cursor-pointer font-medium'], attributes: { 'data-private': item.private } }, [ return dom('div', { class: 'group flex items-center ps-2 outline-hidden relative cursor-pointer', style: { 'padding-inline-start': `${depth / 1.5}em` } }, [dom('div', { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full cursor-pointer font-medium'], attributes: { 'data-private': item.private } }, [
icon('radix-icons:chevron-right', { class: 'h-4 w-4 transition-transform absolute group-data-[state=open]:rotate-90', style: { 'left': `${depth / 1.5 - 1}em` } }), icon('radix-icons:chevron-right', { class: 'h-4 w-4 transition-transform absolute group-data-[state=open]:rotate-90', style: { 'left': `${depth / 1.5 - 1}em` } }),
dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }), dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }),
item.private ? tooltip(icon('radix-icons:lock-closed', { class: 'mx-1' }), 'Privé', 'right') : undefined, item.private ? tooltip(icon('radix-icons:lock-closed', { class: 'mx-1' }), 'Privé', 'right') : undefined,
])]); ])]);
}, (item, depth) => { }, (item, depth) => {
return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-inline-start': `${depth / 1.5}em` } }, [link([ return dom('div', { class: 'group flex items-center ps-2 outline-hidden relative cursor-pointer', style: { 'padding-inline-start': `${depth / 1.5}em` } }, [link([
icon(iconByType[item.type], { class: 'w-5 h-5', width: 20, height: 20 }), icon(iconByType[item.type], { class: 'w-5 h-5', width: 20, height: 20 }),
dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }), dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }),
item.private ? tooltip(icon('radix-icons:lock-closed', { class: 'mx-1' }), 'Privé', 'right') : undefined, item.private ? tooltip(icon('radix-icons:lock-closed', { class: 'mx-1' }), 'Privé', 'right') : undefined,

View File

@@ -199,7 +199,7 @@ async function logout(user: User)
</DialogTitle> </DialogTitle>
<div class="flex flex-1 justify-end gap-4"> <div class="flex flex-1 justify-end gap-4">
<DialogClose asChild><Button>Non</Button></DialogClose> <DialogClose asChild><Button>Non</Button></DialogClose>
<DialogClose asChild><Button @click="() => logout(user)" class="border-light-green dark:border-dark-green hover:border-light-green dark:hover:border-dark-green hover:bg-light-greenBack dark:hover:bg-dark-greenBack text-light-green dark:text-dark-green focus:shadow-light-green dark:focus:shadow-dark-green">Oui</Button></DialogClose> <DialogClose asChild><Button @click="() => logout(user)" class="border-light-green dark:border-dark-green hover:border-light-green hover:dark:border-dark-green hover:bg-light-greenBack hover:dark:bg-dark-greenBack text-light-green dark:text-dark-green focus:shadow-light-green focus:dark:shadow-dark-green">Oui</Button></DialogClose>
</div> </div>
</DialogContent> </DialogContent>
</DialogPortal> </DialogPortal>
@@ -216,7 +216,7 @@ async function logout(user: User)
<AlertDialogDescription><TagsInput v-model="permissionCopy" /></AlertDialogDescription> <AlertDialogDescription><TagsInput v-model="permissionCopy" /></AlertDialogDescription>
<div class="flex flex-1 justify-end gap-4"> <div class="flex flex-1 justify-end gap-4">
<AlertDialogCancel asChild><Button>Annuler</Button></AlertDialogCancel> <AlertDialogCancel asChild><Button>Annuler</Button></AlertDialogCancel>
<AlertDialogAction asChild><Button @click="() => editPermissions(user)" class="border-light-green dark:border-dark-green hover:border-light-green dark:hover:border-dark-green hover:bg-light-greenBack dark:hover:bg-dark-greenBack text-light-green dark:text-dark-green focus:shadow-light-green dark:focus:shadow-dark-green">Modifier</Button></AlertDialogAction> <AlertDialogAction asChild><Button @click="() => editPermissions(user)" class="border-light-green dark:border-dark-green hover:border-light-green hover:dark:border-dark-green hover:bg-light-greenBack hover:dark:bg-dark-greenBack text-light-green dark:text-dark-green focus:shadow-light-green focus:dark:shadow-dark-green">Modifier</Button></AlertDialogAction>
</div> </div>
</AlertDialogContent> </AlertDialogContent>
</AlertDialogPortal> </AlertDialogPortal>

View File

@@ -74,7 +74,7 @@ async function fetch()
</Head> </Head>
<div class="flex flex-col justify-start items-center p-4"> <div class="flex flex-col justify-start items-center p-4">
<div class="flex flex-row justify-between items-center gap-8"> <div class="flex flex-row justify-between items-center gap-8">
<span class="border border-transparent hover:border-light-35 dark:hover:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span> <span class="border border-transparent hover:border-light-35 hover:dark:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span>
<h2 class="text-center flex-1 text-2xl font-bold">Administration</h2> <h2 class="text-center flex-1 text-2xl font-bold">Administration</h2>
</div> </div>
<div class="flex flex-row w-full gap-8"> <div class="flex flex-row w-full gap-8">
@@ -87,7 +87,7 @@ async function fetch()
</div> </div>
<div v-if="job === 'mail'" class="flex justify-center items-center flex-col"> <div v-if="job === 'mail'" class="flex justify-center items-center flex-col">
<TextInput label="Destinataire" class="w-full" v-model="payload.to" /> <TextInput label="Destinataire" class="w-full" v-model="payload.to" />
<textarea v-model="payload.data" class="w-[640px] bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 outline-none m-2 px-2"></textarea> <textarea v-model="payload.data" class="w-[640px] bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 outline-hidden m-2 px-2"></textarea>
</div> </div>
<Button class="self-center" @click="() => !!job && fetch()" :loading="status === 'pending'"> <Button class="self-center" @click="() => !!job && fetch()" :loading="status === 'pending'">
<span>Executer</span> <span>Executer</span>

View File

@@ -65,7 +65,7 @@ function create()
<span class="text-sm">{{ campaign.members.length }} joueur{{ campaign.members.length === 1 ? '' : 's' }}</span> <span class="text-sm">{{ campaign.members.length }} joueur{{ campaign.members.length === 1 ? '' : 's' }}</span>
</div> </div>
</div> </div>
<div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100 dark:bg-dark-100 !bg-opacity-10"></div> <div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100/10 dark:bg-dark-100/10"></div>
</div> </div>
</NuxtLink> </NuxtLink>
<div class="flex justify-around items-center py-2 px-4 gap-4"> <div class="flex justify-around items-center py-2 px-4 gap-4">
@@ -80,7 +80,7 @@ function create()
<AlertDialogTitle class="text-3xl font-light relative -top-2">Vous vous appretez à {{ user.id !== campaign.owner.id ? 'quitter' : 'supprimer' }} "{{ campaign.name }}". Etes vous sûr ?</AlertDialogTitle> <AlertDialogTitle class="text-3xl font-light relative -top-2">Vous vous appretez à {{ user.id !== campaign.owner.id ? 'quitter' : 'supprimer' }} "{{ campaign.name }}". Etes vous sûr ?</AlertDialogTitle>
<div class="flex flex-1 justify-end gap-4"> <div class="flex flex-1 justify-end gap-4">
<AlertDialogCancel asChild><Button>Non</Button></AlertDialogCancel> <AlertDialogCancel asChild><Button>Non</Button></AlertDialogCancel>
<AlertDialogAction asChild><Button @click="() => user?.id !== campaign.owner.id ? leaveCampaign(campaign.id) : removeCampaign(campaign.id)" class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red">Oui</Button></AlertDialogAction> <AlertDialogAction asChild><Button @click="() => user?.id !== campaign.owner.id ? leaveCampaign(campaign.id) : removeCampaign(campaign.id)" class="border-light-red dark:border-dark-red hover:border-light-red hover:dark:border-dark-red hover:bg-light-redBack hover:dark:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red focus:dark:shadow-dark-red">Oui</Button></AlertDialogAction>
</div> </div>
</AlertDialogContent> </AlertDialogContent>
</AlertDialogPortal> </AlertDialogPortal>
@@ -100,7 +100,7 @@ function create()
<span class="text-sm">{{ campaign.members.length }} joueur{{ campaign.members.length === 1 ? '' : 's' }}</span> <span class="text-sm">{{ campaign.members.length }} joueur{{ campaign.members.length === 1 ? '' : 's' }}</span>
</div> </div>
</div> </div>
<div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100 dark:bg-dark-100 !bg-opacity-10"></div> <div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100/10 dark:bg-dark-100/10"></div>
</div> </div>
</NuxtLink> </NuxtLink>
<div class="flex justify-around items-center py-2 px-4 gap-4"> <div class="flex justify-around items-center py-2 px-4 gap-4">
@@ -115,7 +115,7 @@ function create()
<AlertDialogTitle class="text-3xl font-light relative -top-2">Vous vous appretez à {{ user.id !== campaign.owner.id ? 'quitter' : 'supprimer' }} "{{ campaign.name }}". Etes vous sûr ?</AlertDialogTitle> <AlertDialogTitle class="text-3xl font-light relative -top-2">Vous vous appretez à {{ user.id !== campaign.owner.id ? 'quitter' : 'supprimer' }} "{{ campaign.name }}". Etes vous sûr ?</AlertDialogTitle>
<div class="flex flex-1 justify-end gap-4"> <div class="flex flex-1 justify-end gap-4">
<AlertDialogCancel asChild><Button>Non</Button></AlertDialogCancel> <AlertDialogCancel asChild><Button>Non</Button></AlertDialogCancel>
<AlertDialogAction asChild><Button @click="() => user?.id !== campaign.owner.id ? leaveCampaign(campaign.id) : removeCampaign(campaign.id)" class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red">Oui</Button></AlertDialogAction> <AlertDialogAction asChild><Button @click="() => user?.id !== campaign.owner.id ? leaveCampaign(campaign.id) : removeCampaign(campaign.id)" class="border-light-red dark:border-dark-red hover:border-light-red hover:dark:border-dark-red hover:bg-light-redBack hover:dark:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red focus:dark:shadow-dark-red">Oui</Button></AlertDialogAction>
</div> </div>
</AlertDialogContent> </AlertDialogContent>
</AlertDialogPortal> </AlertDialogPortal>
@@ -126,14 +126,14 @@ function create()
</div> </div>
<div v-else class="flex flex-col gap-2 items-center flex-1"> <div v-else class="flex flex-col gap-2 items-center flex-1">
<span class="text-lg font-bold">Vous n'avez pas encore rejoint de campagne</span> <span class="text-lg font-bold">Vous n'avez pas encore rejoint de campagne</span>
<div class="inline-flex justify-center items-center outline-none 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 dark:hover:bg-dark-25 hover:border-light-50 dark:hover: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 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" @click="create">Créer ma campagne</div> 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 py-2 px-4" @click="create">Créer ma campagne</div>
<!-- <NuxtLink class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow] <!-- <NuxtLink 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 dark:hover:bg-dark-25 hover:border-light-50 dark:hover: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 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" :to="{ name: 'campaign-id-edit', params: { id: 'new' } }">Créer ma campagne</NuxtLink> --> 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 py-2 px-4" :to="{ name: 'campaign-id-edit', params: { id: 'new' } }">Créer ma campagne</NuxtLink> -->
</div> </div>
</template> </template>
<div v-else> <div v-else>

View File

@@ -51,7 +51,7 @@ async function duplicateCharacter(id: number)
<span class="text-sm italic">{{ config.peoples[character.people!]?.name }}</span> <span class="text-sm italic">{{ config.peoples[character.people!]?.name }}</span>
</div> </div>
</div> </div>
<div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100 dark:bg-dark-100 !bg-opacity-10"></div> <div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100/10 dark:bg-dark-100/10"></div>
</div> </div>
</NuxtLink> </NuxtLink>
<div class="flex justify-around items-center py-2 px-4 gap-4"> <div class="flex justify-around items-center py-2 px-4 gap-4">
@@ -70,7 +70,7 @@ async function duplicateCharacter(id: number)
<AlertDialogTitle class="text-3xl font-light relative -top-2">Supprimer {{ character.name }} ?</AlertDialogTitle> <AlertDialogTitle class="text-3xl font-light relative -top-2">Supprimer {{ character.name }} ?</AlertDialogTitle>
<div class="flex flex-1 justify-end gap-4"> <div class="flex flex-1 justify-end gap-4">
<AlertDialogCancel asChild><Button>Non</Button></AlertDialogCancel> <AlertDialogCancel asChild><Button>Non</Button></AlertDialogCancel>
<AlertDialogAction asChild><Button @click="() => deleteCharacter(character.id)" class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red">Oui</Button></AlertDialogAction> <AlertDialogAction asChild><Button @click="() => deleteCharacter(character.id)" class="border-light-red dark:border-dark-red hover:border-light-red hover:dark:border-dark-red hover:bg-light-redBack hover:dark:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red focus:dark:shadow-dark-red">Oui</Button></AlertDialogAction>
</div> </div>
</AlertDialogContent> </AlertDialogContent>
</AlertDialogPortal> </AlertDialogPortal>
@@ -80,15 +80,15 @@ async function duplicateCharacter(id: number)
</div> </div>
<div v-else class="flex flex-col gap-2 items-center flex-1"> <div v-else class="flex flex-col gap-2 items-center flex-1">
<span class="text-lg font-bold">Vous n'avez pas encore de personnage</span> <span class="text-lg font-bold">Vous n'avez pas encore de personnage</span>
<NuxtLink v-if="user && user.state === 1" class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow] <NuxtLink v-if="user && user.state === 1" 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 dark:hover:bg-dark-25 hover:border-light-50 dark:hover: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 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" :to="{ name: 'character-id-edit', params: { id: 'new' } }">Nouveau personnage</NuxtLink> 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 py-2 px-4" :to="{ name: 'character-id-edit', params: { id: 'new' } }">Nouveau personnage</NuxtLink>
<div v-else>Veuillez validez votre adresse mail pour pouvoir créer des personnages.</div> <div v-else>Veuillez validez votre adresse mail pour pouvoir créer des personnages.</div>
<NuxtLink class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow] <NuxtLink 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 dark:hover:bg-dark-25 hover:border-light-50 dark:hover: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 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" :to="{ name: 'character-list' }">Qu'ont fait les autres ?</NuxtLink> 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 py-2 px-4" :to="{ name: 'character-list' }">Qu'ont fait les autres ?</NuxtLink>
</div> </div>
</template> </template>
<div v-else> <div v-else>

View File

@@ -29,7 +29,7 @@ const { user } = useUserSession();
<span class="text-sm italic">{{ config.peoples[character.people!]?.name }}</span> <span class="text-sm italic">{{ config.peoples[character.people!]?.name }}</span>
</div> </div>
</div> </div>
<div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100 dark:bg-dark-100 !bg-opacity-10"></div> <div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100/10 dark:bg-dark-100/10"></div>
</div> </div>
</NuxtLink> </NuxtLink>
</div> </div>
@@ -38,10 +38,10 @@ const { user } = useUserSession();
<span class="text-lg font-bold">Il n'existe pas encore de personnage public</span> <span class="text-lg font-bold">Il n'existe pas encore de personnage public</span>
<template v-if="user && user.state === 1"> <template v-if="user && user.state === 1">
Soyez le premier à partager vos créations ! Soyez le premier à partager vos créations !
<NuxtLink class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow] <NuxtLink 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 dark:hover:bg-dark-25 hover:border-light-50 dark:hover: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 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" :to="{ name: 'character-id-edit', params: { id: 'new' } }">Nouveau personnage</NuxtLink> 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 py-2 px-4" :to="{ name: 'character-id-edit', params: { id: 'new' } }">Nouveau personnage</NuxtLink>
</template> </template>
<div v-else>Veuillez valider votre adresse mail pour pouvoir créer des personnages.</div> <div v-else>Veuillez valider votre adresse mail pour pouvoir créer des personnages.</div>
</div> </div>

View File

@@ -23,11 +23,11 @@
<NavigationMenuList class="flex items-center gap-8 max-md:hidden"> <NavigationMenuList class="flex items-center gap-8 max-md:hidden">
<NavigationMenuItem> <NavigationMenuItem>
<NavigationMenuTrigger> <NavigationMenuTrigger>
<NuxtLink :href="{ name: 'character' }" class="flex flex-row gap-2 items-center border-b-2 border-transparent hover:border-accent-blue py-4 select-none" active-class="!text-accent-blue"><span class="px-3 flex-1 truncate">Personnages</span><Icon icon="radix-icons:caret-down" /></NuxtLink> <NuxtLink :href="{ name: 'character' }" class="flex flex-row gap-2 items-center border-b-2 border-transparent hover:border-accent-blue py-4 select-none" active-class="text-accent-blue"><span class="px-3 flex-1 truncate">Personnages</span><Icon icon="radix-icons:caret-down" /></NuxtLink>
</NavigationMenuTrigger> </NavigationMenuTrigger>
<NavigationMenuContent class="absolute top-0 w-full sm:w-auto bg-light-0 dark:bg-dark-0 border border-light-30 dark:border-dark-30 py-2 z-20 flex flex-col"> <NavigationMenuContent class="absolute top-0 w-full sm:w-auto bg-light-0 dark:bg-dark-0 border border-light-30 dark:border-dark-30 py-2 z-20 flex flex-col">
<NuxtLink :href="{ name: 'character-list' }" class="hover:bg-light-30 dark:hover:bg-dark-30 px-4 py-2 select-none" active-class="!text-accent-blue"><span class="flex-1 truncate">Personnages publics</span></NuxtLink> <NuxtLink :href="{ name: 'character-list' }" class="hover:bg-light-30 hover:dark:bg-dark-30 px-4 py-2 select-none" active-class="text-accent-blue"><span class="flex-1 truncate">Personnages publics</span></NuxtLink>
<NuxtLink :href="{ name: 'character-id-edit', params: { id: 'new' } }" class="hover:bg-light-30 dark:hover:bg-dark-30 px-4 py-2 select-none" active-class="!text-accent-blue"><span class="flex-1 truncate">Nouveau personnage</span></NuxtLink> <NuxtLink :href="{ name: 'character-id-edit', params: { id: 'new' } }" class="hover:bg-light-30 hover:dark:bg-dark-30 px-4 py-2 select-none" active-class="text-accent-blue"><span class="flex-1 truncate">Nouveau personnage</span></NuxtLink>
</NavigationMenuContent> </NavigationMenuContent>
</NavigationMenuItem> </NavigationMenuItem>
</NavigationMenuList> </NavigationMenuList>
@@ -35,10 +35,10 @@
<NavigationMenuViewport class="h-[var(--radix-navigation-menu-viewport-height)] w-full origin-[top_center] flex justify-center overflow-hidden sm:w-[var(--radix-navigation-menu-viewport-width)]" /> <NavigationMenuViewport class="h-[var(--radix-navigation-menu-viewport-height)] w-full origin-[top_center] flex justify-center overflow-hidden sm:w-[var(--radix-navigation-menu-viewport-width)]" />
</div> </div>
</NavigationMenuRoot> </NavigationMenuRoot>
<NuxtLink :href="{ name: 'character' }" class="flex flex-row gap-2 items-center border-b-2 border-transparent hover:border-accent-blue py-4 select-none" active-class="!text-accent-blue"><span class="px-3 flex-1 truncate">Campagnes</span></NuxtLink> <NuxtLink :href="{ name: 'character' }" class="flex flex-row gap-2 items-center border-b-2 border-transparent hover:border-accent-blue py-4 select-none" active-class="text-accent-blue"><span class="px-3 flex-1 truncate">Campagnes</span></NuxtLink>
</div> </div>
<div class="flex flex-row gap-16 items-center"> <div class="flex flex-row gap-16 items-center">
<NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 dark:hover:text-dark-70" :to="{ name: 'user-login' }">{{ user!.username }}</NuxtLink> <NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 hover:dark:text-dark-70" :to="{ name: 'user-login' }">{{ user!.username }}</NuxtLink>
</div> </div>
</div> </div>
<div class="flex flex-1 flex-row max-h-full overflow-hidden" ref="container"></div> <div class="flex flex-1 flex-row max-h-full overflow-hidden" ref="container"></div>

View File

@@ -4,7 +4,7 @@
</Head> </Head>
<div class="flex flex-1 flex-col justify-center items-center"> <div class="flex flex-1 flex-col justify-center items-center">
<div class="flex gap-8 items-center"> <div class="flex gap-8 items-center">
<span class="border border-transparent hover:border-light-35 dark:hover:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span> <span class="border border-transparent hover:border-light-35 hover:dark:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span>
<h4 class="text-xl font-bold">Reinitialisation de mon mot de passe</h4> <h4 class="text-xl font-bold">Reinitialisation de mon mot de passe</h4>
</div> </div>
<form @submit.prevent="() => submit()" class="flex flex-1 flex-col justify-center items-stretch"> <form @submit.prevent="() => submit()" class="flex flex-1 flex-col justify-center items-stretch">

View File

@@ -4,7 +4,7 @@
</Head> </Head>
<div class="flex flex-1 flex-col justify-center items-center"> <div class="flex flex-1 flex-col justify-center items-center">
<div class="flex gap-8 items-center"> <div class="flex gap-8 items-center">
<span class="border border-transparent hover:border-light-35 dark:hover:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span> <span class="border border-transparent hover:border-light-35 hover:dark:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span>
<h4 class="text-center flex-1 text-xl font-bold">Reinitialisation de mon mot de passe</h4> <h4 class="text-center flex-1 text-xl font-bold">Reinitialisation de mon mot de passe</h4>
</div> </div>
<form @submit.prevent="submit" class="flex flex-1 flex-col justify-center items-stretch"> <form @submit.prevent="submit" class="flex flex-1 flex-col justify-center items-stretch">

View File

@@ -4,7 +4,7 @@
</Head> </Head>
<div class="flex flex-1 flex-col justify-center items-center"> <div class="flex flex-1 flex-col justify-center items-center">
<div class="flex gap-8 items-center"> <div class="flex gap-8 items-center">
<span class="border border-transparent hover:border-light-35 dark:hover:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span> <span class="border border-transparent hover:border-light-35 hover:dark:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span>
<h4 class="text-center flex-1 text-xl font-bold">Modification de mon mot de passe</h4> <h4 class="text-center flex-1 text-xl font-bold">Modification de mon mot de passe</h4>
</div> </div>
<form @submit.prevent="submit" class="flex flex-1 flex-col justify-center items-stretch"> <form @submit.prevent="submit" class="flex flex-1 flex-col justify-center items-stretch">

View File

@@ -4,7 +4,7 @@
</Head> </Head>
<div class="flex flex-1 flex-col justify-center items-center"> <div class="flex flex-1 flex-col justify-center items-center">
<div class="flex gap-8 items-center"> <div class="flex gap-8 items-center">
<span class="border border-transparent hover:border-light-35 dark:hover:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span> <span class="border border-transparent hover:border-light-35 hover:dark:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span>
<h4 class="text-xl font-bold">Connexion</h4> <h4 class="text-xl font-bold">Connexion</h4>
</div> </div>
<form @submit.prevent="submit" class="flex flex-1 flex-col justify-center items-stretch"> <form @submit.prevent="submit" class="flex flex-1 flex-col justify-center items-stretch">

View File

@@ -57,7 +57,7 @@ async function deleteUser()
<NuxtLink :to="{ name: 'user-changing-password' }" class="flex flex-1"><Button>Modifier mon mot de passe</Button></NuxtLink> <NuxtLink :to="{ name: 'user-changing-password' }" class="flex flex-1"><Button>Modifier mon mot de passe</Button></NuxtLink>
<AlertDialogRoot> <AlertDialogRoot>
<AlertDialogTrigger asChild><Button :loading="loading" <AlertDialogTrigger asChild><Button :loading="loading"
class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red">Supprimer class="border-light-red dark:border-dark-red hover:border-light-red hover:dark:border-dark-red hover:bg-light-redBack hover:dark:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red focus:dark:shadow-dark-red">Supprimer
mon compte</Button></AlertDialogTrigger> mon compte</Button></AlertDialogTrigger>
<AlertDialogPortal> <AlertDialogPortal>
<AlertDialogOverlay class="bg-light-0 dark:bg-dark-0 opacity-70 fixed inset-0 z-40" /> <AlertDialogOverlay class="bg-light-0 dark:bg-dark-0 opacity-70 fixed inset-0 z-40" />
@@ -72,7 +72,7 @@ async function deleteUser()
</AlertDialogDescription> </AlertDialogDescription>
<div class="flex flex-1 justify-end gap-4"> <div class="flex flex-1 justify-end gap-4">
<AlertDialogCancel asChild><Button>Annuler</Button></AlertDialogCancel> <AlertDialogCancel asChild><Button>Annuler</Button></AlertDialogCancel>
<AlertDialogAction asChild><Button @click="() => deleteUser()" class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red">Supprimer</Button></AlertDialogAction> <AlertDialogAction asChild><Button @click="() => deleteUser()" class="border-light-red dark:border-dark-red hover:border-light-red hover:dark:border-dark-red hover:bg-light-redBack hover:dark:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red focus:dark:shadow-dark-red">Supprimer</Button></AlertDialogAction>
</div> </div>
</AlertDialogContent> </AlertDialogContent>
</AlertDialogPortal> </AlertDialogPortal>

View File

@@ -4,7 +4,7 @@
</Head> </Head>
<div class="flex flex-1 flex-col justify-center items-center"> <div class="flex flex-1 flex-col justify-center items-center">
<div class="flex gap-8 items-center"> <div class="flex gap-8 items-center">
<span class="border border-transparent hover:border-light-35 dark:hover:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span> <span class="border border-transparent hover:border-light-35 hover:dark:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span>
<h4 class="text-xl font-bold">Inscription</h4> <h4 class="text-xl font-bold">Inscription</h4>
</div> </div>
<form @submit.prevent="() => submit()" class="grid flex-1 p-4 grid-cols-2 md:grid-cols-1 gap-4 md:gap-0"> <form @submit.prevent="() => submit()" class="grid flex-1 p-4 grid-cols-2 md:grid-cols-1 gap-4 md:gap-0">
@@ -20,7 +20,7 @@
<span class="ps-4 flex items-center gap-2" :class="{'text-light-red dark:text-dark-red': !checkedSymbol}"><Icon v-show="!checkedSymbol" icon="radix-icons:cross-2" />Un caractère special</span> <span class="ps-4 flex items-center gap-2" :class="{'text-light-red dark:text-dark-red': !checkedSymbol}"><Icon v-show="!checkedSymbol" icon="radix-icons:cross-2" />Un caractère special</span>
</div> </div>
<TextInput type="password" label="Confirmation du mot de passe" autocomplete="new-password" v-model="confirmPassword" class="w-full md:w-auto"/> <TextInput type="password" label="Confirmation du mot de passe" autocomplete="new-password" v-model="confirmPassword" class="w-full md:w-auto"/>
<Label class="pb-2 col-span-2 md:col-span-1 flex flex-row gap-2 items-center"><CheckboxRoot v-model:checked="agreeOnRules" class="border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50 w-5 h-5" ><CheckboxIndicator ><Icon icon="radix-icons:check" /></CheckboxIndicator></CheckboxRoot><span>J'ai lu et j'accepte les <NuxtLink class="text-accent-blue cursor-pointer" :to="{ name: 'usage' }" target="_blank">conditions d'utilisation</NuxtLink></span></Label> <Label class="pb-2 col-span-2 md:col-span-1 flex flex-row gap-2 items-center"><CheckboxRoot v-model:checked="agreeOnRules" class="border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50 w-5 h-5" ><CheckboxIndicator ><Icon icon="radix-icons:check" /></CheckboxIndicator></CheckboxRoot><span>J'ai lu et j'accepte les <NuxtLink class="text-accent-blue cursor-pointer" :to="{ name: 'usage' }" target="_blank">conditions d'utilisation</NuxtLink></span></Label>
<Button type="submit" class="border border-light-35 dark:border-dark-35 max-w-48 w-full order-9 col-span-2 md:col-span-1 m-auto" :loading="status === 'pending'">S'inscrire</Button> <Button type="submit" class="border border-light-35 dark:border-dark-35 max-w-48 w-full order-9 col-span-2 md:col-span-1 m-auto" :loading="status === 'pending'">S'inscrire</Button>
<span class="mt-4 order-10 flex justify-center items-center gap-4 col-span-2 md:col-span-1 m-auto">Vous avez déjà un compte ?<NuxtLink class="text-center block text-sm font-semibold tracking-wide hover:text-accent-blue" :to="{ name: 'user-login' }">Se connecter</NuxtLink></span> <span class="mt-4 order-10 flex justify-center items-center gap-4 col-span-2 md:col-span-1 m-auto">Vous avez déjà un compte ?<NuxtLink class="text-center block text-sm font-semibold tracking-wide hover:text-accent-blue" :to="{ name: 'user-login' }">Se connecter</NuxtLink></span>
</form> </form>

1054
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
import fs from 'node:fs' import fs from 'node:fs'
import path from 'node:path' import path from 'node:path'
import tailwindcss from '@tailwindcss/vite'
export default defineNuxtConfig({ export default defineNuxtConfig({
compatibilityDate: '2024-04-03', compatibilityDate: '2024-04-03',
@@ -8,113 +9,11 @@ export default defineNuxtConfig({
modules: [ modules: [
'@nuxtjs/color-mode', '@nuxtjs/color-mode',
'nuxt-security', 'nuxt-security',
'@nuxtjs/tailwindcss',
'@vueuse/nuxt', '@vueuse/nuxt',
'radix-vue/nuxt', 'radix-vue/nuxt',
'@nuxtjs/sitemap', '@nuxtjs/sitemap',
], ],
tailwindcss: { css: ['~/assets/css/main.css'],
viewer: false,
config: {
content: {
files: [
"./shared/**/*.{vue,js,jsx,mjs,ts,tsx}"
]
},
theme: {
extend: {
boxShadow: {
raw: '0 0 0 2px var(--tw-shadow-color)'
},
keyframes: {
slideDownAndFade: {
from: { opacity: '0', transform: 'translateY(-2px)' },
to: { opacity: '1', transform: 'translateY(0)' },
},
slideLeftAndFade: {
from: { opacity: '0', transform: 'translateX(2px)' },
to: { opacity: '1', transform: 'translateX(0)' },
},
slideUpAndFade: {
from: { opacity: '0', transform: 'translateY(2px)' },
to: { opacity: '1', transform: 'translateY(0)' },
},
slideRightAndFade: {
from: { opacity: '0', transform: 'translateX(-2px)' },
to: { opacity: '1', transform: 'translateX(0)' },
},
contentShow: {
from: { opacity: '0', transform: 'translate(-50%, -48%) scale(0.96)' },
to: { opacity: '1', transform: 'translate(-50%, -50%) scale(1)' },
},
},
animation: {
slideDownAndFade: 'slideDownAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
slideLeftAndFade: 'slideLeftAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
slideUpAndFade: 'slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
slideRightAndFade: 'slideRightAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
contentShow: 'contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1)',
},
},
colors: {
transparent: 'transparent',
current: 'currentColor',
light: {
red: '#e93147',
orange: '#FF9800',
yellow: '#FFEB3B',
green: '#388E3C',
indigo: '#7986CB',
cyan: '#00bfbc',
lime: '#8BC34A',
blue: '#086ddd',
purple: '#AB47BC',
pink: '#d53984',
0: "#ffffff",
5: "#fcfcfc",
10: "#fafafa",
20: "#f7f7f7",
25: "#e4e4e4",
30: "#dfdfdf",
35: "#d2d2d2",
40: "#bdbdbd",
50: "#ababab",
60: "#707070",
70: "#5c5c5c",
100: "#202020",
},
dark: {
red: '#fb464c',
redBack: '#5A292B',
orange: '#e9973f',
yellow: '#e0de71',
green: '#44cf6e',
greenBack: '#284E34',
cyan: '#53dfdd',
blue: '#027aff',
purple: '#a882ff',
pink: '#fa99cd',
0: '#1e1e1e',
5: '#212121',
10: '#242424',
20: '#262626',
25: '#2a2a2a',
30: '#363636',
35: '#3f3f3f',
40: '#555555',
50: '#666666',
60: '#999999',
70: '#b3b3b3',
100: '#dadada',
},
accent: {
purple: '#43A047',
blue: '#26C6DA',
},
}
}
}
},
app: { app: {
pageTransition: false, pageTransition: false,
layoutTransition: false layoutTransition: false
@@ -225,6 +124,9 @@ export default defineNuxtConfig({
} }
}, },
vite: { vite: {
plugins: [
tailwindcss(),
],
server: { server: {
hmr: { hmr: {
protocol: 'wss', protocol: 'wss',

View File

@@ -19,7 +19,7 @@
"@markdoc/markdoc": "^0.5.7", "@markdoc/markdoc": "^0.5.7",
"@nuxtjs/color-mode": "^4.0.0", "@nuxtjs/color-mode": "^4.0.0",
"@nuxtjs/sitemap": "^8.1.0", "@nuxtjs/sitemap": "^8.1.0",
"@nuxtjs/tailwindcss": "^6.14.0", "@tailwindcss/vite": "^4.1.18",
"@vueuse/gesture": "^2.0.0", "@vueuse/gesture": "^2.0.0",
"@vueuse/math": "^14.3.0", "@vueuse/math": "^14.3.0",
"@vueuse/nuxt": "^14.3.0", "@vueuse/nuxt": "^14.3.0",
@@ -43,7 +43,7 @@
"remark-ofm": "link:remark-ofm", "remark-ofm": "link:remark-ofm",
"remark-parse": "^11.0.0", "remark-parse": "^11.0.0",
"remark-rehype": "^11.1.2", "remark-rehype": "^11.1.2",
"rollup-plugin-postcss": "^4.0.2", "tailwindcss": "^4.1.18",
"rollup-plugin-vue": "^6.0.0", "rollup-plugin-vue": "^6.0.0",
"strip-markdown": "^6.0.0", "strip-markdown": "^6.0.0",
"unified": "^11.0.5", "unified": "^11.0.5",

View File

@@ -246,7 +246,7 @@ export class CampaignSheet
const items = this.campaign.items; const items = this.campaign.items;
/* const money = { /* const money = {
readonly: dom('div', { listeners: { click: () => { money.readonly.replaceWith(money.edit); money.edit.focus(); } }, class: 'cursor-pointer border border-transparent hover:border-light-40 dark:hover:border-dark-40 px-2 py-px flex flex-row gap-1 items-center' }, [ span('text-lg font-bold', () => this.campaign!.money.toLocaleString(undefined, { useGrouping: true })), icon('ph:coin', { width: 16, height: 16 }) ]), readonly: dom('div', { listeners: { click: () => { money.readonly.replaceWith(money.edit); money.edit.focus(); } }, class: 'cursor-pointer border border-transparent hover:border-light-40 hover:dark:border-dark-40 px-2 py-px flex flex-row gap-1 items-center' }, [ span('text-lg font-bold', () => this.campaign!.money.toLocaleString(undefined, { useGrouping: true })), icon('ph:coin', { width: 16, height: 16 }) ]),
edit: numberpicker({ defaultValue: this.campaign.money, change: v => { this.campaign!.money = v; this.saveVariables(); money.edit.replaceWith(money.readonly); }, blur: v => { this.campaign!.money = v; this.saveVariables(); money.edit.replaceWith(money.readonly); }, min: 0, class: 'w-24' }), edit: numberpicker({ defaultValue: this.campaign.money, change: v => { this.campaign!.money = v; this.saveVariables(); money.edit.replaceWith(money.readonly); }, blur: v => { this.campaign!.money = v; this.saveVariables(); money.edit.replaceWith(money.readonly); }, min: 0, class: 'w-24' }),
}; */ }; */

View File

@@ -215,7 +215,7 @@ export class Node extends EventTarget
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 hover:outline-4', style.border] }, [
dom('div', { class: ['w-full h-full py-2 px-4 flex !bg-opacity-[0.07] 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])
]) ])
]); ]);
@@ -238,9 +238,9 @@ export class Node extends EventTarget
get style() get style()
{ {
return this.properties.color ? this.properties.color?.class ? return this.properties.color ? this.properties.color?.class ?
{ bg: `bg-light-${this.properties.color?.class} dark:bg-dark-${this.properties.color?.class}`, border: `border-light-${this.properties.color?.class} dark:border-dark-${this.properties.color?.class}` } : { bg: `bg-light-${this.properties.color?.class}/10 dark:bg-dark-${this.properties.color?.class}/10`, border: `border-light-${this.properties.color?.class} dark:border-dark-${this.properties.color?.class}` } :
{ bg: `bg-colored`, border: `border-[color:var(--canvas-color)]` } : { bg: `bg-colored`, border: `border-[color:var(--canvas-color)]` } :
{ border: `border-light-40 dark:border-dark-40`, bg: `bg-light-40 dark:bg-dark-40` } { border: `border-light-40 dark:border-dark-40`, bg: `bg-light-40/10 dark:bg-dark-40/10` }
} }
} }
export class NodeEditable extends Node export class NodeEditable extends Node
@@ -258,7 +258,7 @@ export class NodeEditable extends Node
this.nodeDom = dom('div', { class: ['absolute group', {'-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 group', {'-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 group-hover:outline-4', style.border, style.outline] }, [ dom('div', { class: ['outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 w-full h-full group-hover:outline-4', style.border, style.outline] }, [
dom('div', { class: ['w-full h-full py-2 px-4 flex !bg-opacity-[0.07] overflow-auto', style.bg], listeners: this.properties.type === 'text' ? { mouseenter: e => this.dispatchEvent(new CustomEvent('focus', { detail: this })), click: e => this.dispatchEvent(new CustomEvent('select', { detail: this })), dblclick: e => this.dispatchEvent(new CustomEvent('edit', { detail: this })) } : undefined }, [this.properties.text ? dom('div', { class: 'flex items-center' }, [render(this.properties.text, undefined, { tags: { a: preview } })]) : undefined]) dom('div', { class: ['w-full h-full py-2 px-4 flex overflow-auto', style.bg], listeners: this.properties.type === 'text' ? { mouseenter: e => this.dispatchEvent(new CustomEvent('focus', { detail: this })), click: e => this.dispatchEvent(new CustomEvent('select', { detail: this })), dblclick: e => this.dispatchEvent(new CustomEvent('edit', { detail: this })) } : undefined }, [this.properties.text ? dom('div', { class: 'flex items-center' }, [render(this.properties.text, undefined, { tags: { a: preview } })]) : undefined])
]) ])
]); ]);
@@ -287,9 +287,9 @@ export class NodeEditable extends Node
override get style() override get style()
{ {
return this.properties.color ? this.properties.color?.class ? return this.properties.color ? this.properties.color?.class ?
{ bg: `bg-light-${this.properties.color?.class} dark:bg-dark-${this.properties.color?.class}`, border: `border-light-${this.properties.color?.class} dark:border-dark-${this.properties.color?.class}`, outline: `outline-light-${this.properties.color?.class} dark:outline-dark-${this.properties.color?.class}` } : { bg: `bg-light-${this.properties.color?.class}/10 dark:bg-dark-${this.properties.color?.class}/10`, border: `border-light-${this.properties.color?.class} dark:border-dark-${this.properties.color?.class}`, outline: `outline-light-${this.properties.color?.class} dark:outline-dark-${this.properties.color?.class}` } :
{ bg: `bg-colored`, border: `border-[color:var(--canvas-color)]`, outline: `outline-[color:var(--canvas-color)]` } : { bg: `bg-colored`, border: `border-[color:var(--canvas-color)]`, outline: `outline-[color:var(--canvas-color)]` } :
{ border: `border-light-40 dark:border-dark-40`, bg: `bg-light-40 dark:bg-dark-40`, outline: `outline-light-40 dark:outline-dark-40` } { border: `border-light-40 dark:border-dark-40`, bg: `bg-light-40/10 dark:bg-dark-40/10`, outline: `outline-light-40 dark:outline-dark-40` }
} }
get x() get x()
@@ -486,9 +486,9 @@ export class Canvas
this.container = dom('div', { class: 'absolute top-0 left-0 overflow-hidden w-full h-full touch-none' }, [ this.container = dom('div', { class: 'absolute top-0 left-0 overflow-hidden w-full h-full touch-none' }, [
dom('div', { class: 'flex flex-col absolute sm:top-2 top-10 left-2 z-[35] overflow-hidden gap-4' }, [ dom('div', { class: 'flex flex-col absolute sm:top-2 top-10 left-2 z-[35] overflow-hidden gap-4' }, [
dom('div', { class: 'border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10' }, [ dom('div', { class: 'border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10' }, [
tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer', listeners: { click: () => this.zoomTo(this._x, this._y, clamp(this._zoom * 1.1, this.containZoom, Canvas.maxZoom)) } }, [icon('radix-icons:plus')]), 'Zoom avant', 'right'), tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 hover:dark:bg-dark-30 cursor-pointer', listeners: { click: () => this.zoomTo(this._x, this._y, clamp(this._zoom * 1.1, this.containZoom, Canvas.maxZoom)) } }, [icon('radix-icons:plus')]), 'Zoom avant', 'right'),
tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer', listeners: { click: (e: MouseEvent) => { this.reset(); } } }, [icon('radix-icons:corners')]), 'Tout contenir', 'right'), tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 hover:dark:bg-dark-30 cursor-pointer', listeners: { click: (e: MouseEvent) => { this.reset(); } } }, [icon('radix-icons:corners')]), 'Tout contenir', 'right'),
tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer', listeners: { click: () => this.zoomTo(this._x, this._y, clamp(this._zoom / 1.1, this.containZoom, Canvas.maxZoom)) } }, [icon('radix-icons:minus')]), 'Zoom arrière', 'right'), tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 hover:dark:bg-dark-30 cursor-pointer', listeners: { click: () => this.zoomTo(this._x, this._y, clamp(this._zoom / 1.1, this.containZoom, Canvas.maxZoom)) } }, [icon('radix-icons:minus')]), 'Zoom arrière', 'right'),
]), ]),
]), this.transform, ]), this.transform,
]); ]);
@@ -780,17 +780,17 @@ export class CanvasEditor extends Canvas
this.container = dom('div', { class: 'absolute top-0 left-0 overflow-hidden w-full h-full touch-none', listeners: { mousedown: () => { this.selection.clear(); this.updateSelection() } } }, [ this.container = dom('div', { class: 'absolute top-0 left-0 overflow-hidden w-full h-full touch-none', listeners: { mousedown: () => { this.selection.clear(); this.updateSelection() } } }, [
dom('div', { class: 'flex flex-col absolute sm:top-2 top-10 left-2 z-[35] overflow-hidden gap-4' }, [ dom('div', { class: 'flex flex-col absolute sm:top-2 top-10 left-2 z-[35] overflow-hidden gap-4' }, [
dom('div', { class: 'border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10' }, [ dom('div', { class: 'border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10' }, [
tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer', listeners: { click: () => this.zoomTo(this._x, this._y, clamp(this._zoom * 1.1, this.containZoom, Canvas.maxZoom)) } }, [icon('radix-icons:plus')]), 'Zoom avant', 'right'), tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 hover:dark:bg-dark-30 cursor-pointer', listeners: { click: () => this.zoomTo(this._x, this._y, clamp(this._zoom * 1.1, this.containZoom, Canvas.maxZoom)) } }, [icon('radix-icons:plus')]), 'Zoom avant', 'right'),
tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer', listeners: { click: () => this.reset() } }, [icon('radix-icons:corners')]), 'Tout contenir', 'right'), tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 hover:dark:bg-dark-30 cursor-pointer', listeners: { click: () => this.reset() } }, [icon('radix-icons:corners')]), 'Tout contenir', 'right'),
tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer', listeners: { click: () => this.zoomTo(this._x, this._y, clamp(this._zoom / 1.1, this.containZoom, Canvas.maxZoom)) } }, [icon('radix-icons:minus')]), 'Zoom arrière', 'right'), tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 hover:dark:bg-dark-30 cursor-pointer', listeners: { click: () => this.zoomTo(this._x, this._y, clamp(this._zoom / 1.1, this.containZoom, Canvas.maxZoom)) } }, [icon('radix-icons:minus')]), 'Zoom arrière', 'right'),
]), ]),
dom('div', { class: 'border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10' }, [ dom('div', { class: 'border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10' }, [
tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer', listeners: { click: () => this.history.undo() } }, [icon('ph:arrow-bend-up-left')]), 'Annuler (Ctrl+Z)', 'right'), tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 hover:dark:bg-dark-30 cursor-pointer', listeners: { click: () => this.history.undo() } }, [icon('ph:arrow-bend-up-left')]), 'Annuler (Ctrl+Z)', 'right'),
tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer', listeners: { click: () => this.history.redo() } }, [icon('ph:arrow-bend-up-right')]), 'Rétablir (Ctrl+Y)', 'right'), tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 hover:dark:bg-dark-30 cursor-pointer', listeners: { click: () => this.history.redo() } }, [icon('ph:arrow-bend-up-right')]), 'Rétablir (Ctrl+Y)', 'right'),
]), ]),
dom('div', { class: 'border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10' }, [ dom('div', { class: 'border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10' }, [
tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer', listeners: { click: () => {} } }, [icon('radix-icons:gear')]), 'Préférences', 'right'), tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 hover:dark:bg-dark-30 cursor-pointer', listeners: { click: () => {} } }, [icon('radix-icons:gear')]), 'Préférences', 'right'),
tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer', listeners: { click: () => {} } }, [icon('radix-icons:question-mark-circled')]), 'Aide', 'right'), tooltip(dom('span', { class: 'w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 hover:dark:bg-dark-30 cursor-pointer', listeners: { click: () => {} } }, [icon('radix-icons:question-mark-circled')]), 'Aide', 'right'),
]), ]),
]), this.pattern, this.transform ]), this.pattern, this.transform
]); ]);

View File

@@ -169,18 +169,18 @@ export const mainStatShortTexts: Record<MainStat, string> = {
"psyche": "PSY", "psyche": "PSY",
}; };
export const elementTexts: Record<SpellElement, { class: string, text: string }> = { export const elementTexts: Record<SpellElement, { class: string, text: string }> = {
fire: { class: 'text-light-red dark:text-dark-red border-light-red dark:border-dark-red bg-light-red dark:bg-dark-red', text: 'Feu' }, fire: { class: 'text-light-red dark:text-dark-red border-light-red/50 dark:border-dark-red/50 bg-light-red/20 dark:bg-dark-red/20', text: 'Feu' },
ice: { class: 'text-light-blue dark:text-dark-blue border-light-blue dark:border-dark-blue bg-light-blue dark:bg-dark-blue', text: 'Glace' }, ice: { class: 'text-light-blue dark:text-dark-blue border-light-blue/50 dark:border-dark-blue/50 bg-light-blue/20 dark:bg-dark-blue/20', text: 'Glace' },
thunder: { class: 'text-light-yellow dark:text-dark-yellow border-light-yellow dark:border-dark-yellow bg-light-yellow dark:bg-dark-yellow', text: 'Foudre' }, thunder: { class: 'text-light-yellow dark:text-dark-yellow border-light-yellow/50 dark:border-dark-yellow/50 bg-light-yellow/20 dark:bg-dark-yellow/20', text: 'Foudre' },
earth: { class: 'text-light-orange dark:text-dark-orange border-light-orange dark:border-dark-orange bg-light-orange dark:bg-dark-orange', text: 'Terre' }, earth: { class: 'text-light-orange dark:text-dark-orange border-light-orange/50 dark:border-dark-orange/50 bg-light-orange/20 dark:bg-dark-orange/20', text: 'Terre' },
arcana: { class: 'text-light-indigo dark:text-dark-indigo border-light-indigo dark:border-dark-indigo bg-light-indigo dark:bg-dark-indigo', text: 'Arcane' }, arcana: { class: 'text-light-indigo dark:text-dark-indigo border-light-indigo/50 dark:border-dark-indigo/50 bg-light-indigo/20 dark:bg-dark-indigo/20', text: 'Arcane' },
air: { class: 'text-light-lime dark:text-dark-lime border-light-lime dark:border-dark-lime bg-light-lime dark:bg-dark-lime', text: 'Air' }, air: { class: 'text-light-lime dark:text-dark-lime border-light-lime/50 dark:border-dark-lime/50 bg-light-lime/20 dark:bg-dark-lime/20', text: 'Air' },
nature: { class: 'text-light-green dark:text-dark-green border-light-green dark:border-dark-green bg-light-green dark:bg-dark-green', text: 'Nature' }, nature: { class: 'text-light-green dark:text-dark-green border-light-green/50 dark:border-dark-green/50 bg-light-green/20 dark:bg-dark-green/20', text: 'Nature' },
light: { class: 'text-light-yellow dark:text-dark-yellow border-light-yellow dark:border-dark-yellow bg-light-yellow dark:bg-dark-yellow', text: 'Lumière' }, light: { class: 'text-light-yellow dark:text-dark-yellow border-light-yellow/50 dark:border-dark-yellow/50 bg-light-yellow/20 dark:bg-dark-yellow/20', text: 'Lumière' },
psyche: { class: 'text-light-purple dark:text-dark-purple border-light-purple dark:border-dark-purple bg-light-purple dark:bg-dark-purple', text: 'Psy' }, psyche: { class: 'text-light-purple dark:text-dark-purple border-light-purple/50 dark:border-dark-purple/50 bg-light-purple/20 dark:bg-dark-purple/20', text: 'Psy' },
}; };
export const elementDom = (element: SpellElement) => dom("span", { export const elementDom = (element: SpellElement) => dom("span", {
class: [`border !border-opacity-50 rounded-full !bg-opacity-20 px-1 py-px text-sm font-semibold`, elementTexts[element].class], class: [`border rounded-full px-1 py-px text-sm font-semibold`, elementTexts[element].class],
text: elementTexts[element].text text: elementTexts[element].text
}); });
export const alignmentTexts: Record<Alignment, string> = { export const alignmentTexts: Record<Alignment, string> = {
@@ -789,7 +789,7 @@ export class CharacterBuilder extends CharacterCompiler
]) ])
); );
this._helperText = text("Choisissez un peuple afin de définir la progression de votre personnage au fil des niveaux.") this._helperText = text("Choisissez un peuple afin de définir la progression de votre personnage au fil des niveaux.")
this._content = dom('div', { class: 'flex-1 outline-none max-w-full w-full overflow-y-auto', attributes: { id: 'characterEditorContainer' } }); this._content = dom('div', { class: 'flex-1 outline-hidden max-w-full w-full overflow-y-auto', attributes: { id: 'characterEditorContainer' } });
this._container.appendChild(div('flex flex-1 flex-col justify-start items-center px-8 w-full h-full overflow-y-hidden', [ this._container.appendChild(div('flex flex-1 flex-col justify-start items-center px-8 w-full h-full overflow-y-hidden', [
div("flex w-full flex-row gap-4 items-center justify-between px-4 bg-light-0 dark:bg-dark-0 z-20", [ div("flex w-full flex-row gap-4 items-center justify-between px-4 bg-light-0 dark:bg-dark-0 z-20", [
div('flex flex-row gap-2', [ floater(tooltip(button(icon('radix-icons:pencil-2', { width: 16, height: 16 }), undefined, 'p-1'), 'Notes publics', 'left'), [ publicNotes.dom ], { pinned: true, events: { show: ['click'], hide: [] }, class: 'min-w-[200px] min-h-[150px] max-w-[600px] max-h-[600px]', title: 'Notes publics', position: 'bottom-start' }), floater(tooltip(button(icon('radix-icons:eye-none', { width: 16, height: 16 }), undefined, 'p-1'), 'Notes privés', 'right'), [ privateNotes.dom ], { pinned: true, events: { show: ['click'], hide: [] }, class: 'min-w-[200px] min-h-[150px] max-w-[600px] max-h-[600px]', title: 'Notes privés', position: 'bottom-start' }) ]), div("flex w-full flex-row gap-4 items-center justify-center relative", this._stepsHeader), div('flex flex-row gap-2', [ tooltip(button(icon("radix-icons:chevron-right", { height: 16, width: 16 }), () => this.next(), 'p-1'), 'Suivant', "bottom"), tooltip(button(icon("radix-icons:paper-plane", { height: 16, width: 16 }), () => this.save(), 'p-1'), 'Enregistrer', "bottom"), tooltip(icon("radix-icons:question-mark-circled", { height: 20, width: 20 }), this._helperText, "bottom-end") ]), div('flex flex-row gap-2', [ floater(tooltip(button(icon('radix-icons:pencil-2', { width: 16, height: 16 }), undefined, 'p-1'), 'Notes publics', 'left'), [ publicNotes.dom ], { pinned: true, events: { show: ['click'], hide: [] }, class: 'min-w-[200px] min-h-[150px] max-w-[600px] max-h-[600px]', title: 'Notes publics', position: 'bottom-start' }), floater(tooltip(button(icon('radix-icons:eye-none', { width: 16, height: 16 }), undefined, 'p-1'), 'Notes privés', 'right'), [ privateNotes.dom ], { pinned: true, events: { show: ['click'], hide: [] }, class: 'min-w-[200px] min-h-[150px] max-w-[600px] max-h-[600px]', title: 'Notes privés', position: 'bottom-start' }) ]), div("flex w-full flex-row gap-4 items-center justify-center relative", this._stepsHeader), div('flex flex-row gap-2', [ tooltip(button(icon("radix-icons:chevron-right", { height: 16, width: 16 }), () => this.next(), 'p-1'), 'Suivant', "bottom"), tooltip(button(icon("radix-icons:paper-plane", { height: 16, width: 16 }), () => this.save(), 'p-1'), 'Enregistrer', "bottom"), tooltip(icon("radix-icons:question-mark-circled", { height: 20, width: 20 }), this._helperText, "bottom-end") ]),
@@ -1023,7 +1023,7 @@ class PeoplePicker extends BuilderTab
this._visibilityInput = toggle({ defaultValue: this._builder.character.visibility === "private", change: (value) => this._builder.character.visibility = value ? "private" : "public" }); this._visibilityInput = toggle({ defaultValue: this._builder.character.visibility === "private", change: (value) => this._builder.character.visibility = value ? "private" : "public" });
this._options = Object.values(config.peoples).map( this._options = Object.values(config.peoples).map(
(people, i) => dom("div", { class: "flex flex-col flex-nowrap gap-2 p-2 border border-light-35 dark:border-dark-35 cursor-pointer hover:border-light-70 dark:hover:border-dark-70 w-[320px]", listeners: { click: () => { (people, i) => dom("div", { class: "flex flex-col flex-nowrap gap-2 p-2 border border-light-35 dark:border-dark-35 cursor-pointer hover:border-light-70 hover:dark:border-dark-70 w-[320px]", listeners: { click: () => {
this._builder.character.people = people.id; this._builder.character.people = people.id;
this._builder.character = { ...this._builder.character, people: people.id }; this._builder.character = { ...this._builder.character, people: people.id };
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._options.forEach(f => f?.classList.toggle(e, false))); "border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._options.forEach(f => f?.classList.toggle(e, false)));
@@ -1075,17 +1075,17 @@ class LevelPicker extends BuilderTab
{ {
super(builder); super(builder);
this._levelInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-none ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }}); this._levelInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-hidden ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }});
this._pointsInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-none ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }}); this._pointsInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-hidden ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }});
this._options = Object.entries(config.peoples[this._builder.character.people!]!.options).map( this._options = Object.entries(config.peoples[this._builder.character.people!]!.options).map(
(level) => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative left-4" }, [ text(level[0]) ])]), (level) => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative left-4" }, [ text(level[0]) ])]),
div("flex flex-row gap-4 justify-center", level[1].map((option, j) => { div("flex flex-row gap-4 justify-center", level[1].map((option, j) => {
const choice = config.features[option]!.effect.some(e => e.category === 'choice') ? dom('div', { class: 'absolute -bottom-px -right-px border border-light-50 dark:border-dark-50 bg-light-10 dark:bg-dark-10 hover:border-light-70 dark:hover:border-dark-70 flex p-1 justify-center items-center', listeners: { click: (e) => { const choice = config.features[option]!.effect.some(e => e.category === 'choice') ? dom('div', { class: 'absolute -bottom-px -right-px border border-light-50 dark:border-dark-50 bg-light-10 dark:bg-dark-10 hover:border-light-70 hover:dark:border-dark-70 flex p-1 justify-center items-center', listeners: { click: (e) => {
e.stopImmediatePropagation(); e.stopImmediatePropagation();
this._builder.character.leveling[level[0] as any as Level] === j && this._builder.handleChoice(choice!, config.features[option]!.id); this._builder.character.leveling[level[0] as any as Level] === j && this._builder.handleChoice(choice!, config.features[option]!.id);
} } }, [ icon('radix-icons:gear') ]) : undefined; } } }, [ icon('radix-icons:gear') ]) : undefined;
return dom("div", { class: ["flex border border-light-50 dark:border-dark-50 px-4 py-2 w-[400px] relative", { 'hover:border-light-70 dark:hover:border-dark-70 cursor-pointer': (level[0] as any as Level) <= this._builder.character.level, '!border-accent-blue bg-accent-blue bg-opacity-20': this._builder.character.leveling[level[0] as any as Level] === j }], listeners: { click: e => { return dom("div", { class: ["flex border border-light-50 dark:border-dark-50 px-4 py-2 w-[400px] relative", { 'hover:border-light-70 hover:dark:border-dark-70 cursor-pointer': (level[0] as any as Level) <= this._builder.character.level, '!border-accent-blue bg-accent-blue/20': this._builder.character.leveling[level[0] as any as Level] === j }], listeners: { click: e => {
this._builder.toggleLevelOption(parseInt(level[0]) as Level, j); this._builder.toggleLevelOption(parseInt(level[0]) as Level, j);
this.update(); this.update();
}}}, [ dom('span', { class: "text-wrap whitespace-pre", text: getText(config.features[option]!.description) }), choice ]); }}}, [ dom('span', { class: "text-wrap whitespace-pre", text: getText(config.features[option]!.description) }), choice ]);
@@ -1131,8 +1131,8 @@ class LevelPicker extends BuilderTab
e[0]?.classList.toggle("opacity-30", ((i + 1) as Level) > this._builder.character.level); e[0]?.classList.toggle("opacity-30", ((i + 1) as Level) > this._builder.character.level);
e[1]?.classList.toggle("opacity-30", ((i + 1) as Level) > this._builder.character.level); e[1]?.classList.toggle("opacity-30", ((i + 1) as Level) > this._builder.character.level);
e[1]?.childNodes.forEach((option, j) => { e[1]?.childNodes.forEach((option, j) => {
'hover:border-light-70 dark:hover:border-dark-70 cursor-pointer'.split(" ").forEach(_e => (option as HTMLElement).classList.toggle(_e, ((i + 1) as Level) <= this._builder.character.level)); 'hover:border-light-70 hover:dark:border-dark-70 cursor-pointer'.split(" ").forEach(_e => (option as HTMLElement).classList.toggle(_e, ((i + 1) as Level) <= this._builder.character.level));
'!border-accent-blue bg-accent-blue bg-opacity-20'.split(" ").forEach(_e => (option as HTMLElement).classList.toggle(_e, this._builder.character.leveling[((i + 1) as Level)] === j)); '!border-accent-blue bg-accent-blue/20'.split(" ").forEach(_e => (option as HTMLElement).classList.toggle(_e, this._builder.character.leveling[((i + 1) as Level)] === j));
}); });
}); });
} }
@@ -1161,11 +1161,11 @@ class TrainingPicker extends BuilderTab
return Object.entries(config.training[stat]).map( return Object.entries(config.training[stat]).map(
(level) => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative" }, [ text(level[0]) ])]), (level) => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative" }, [ text(level[0]) ])]),
div("flex flex-row gap-4 justify-center", level[1].map((option, j) => { div("flex flex-row gap-4 justify-center", level[1].map((option, j) => {
const choice = config.features[option]!.effect.some(e => e.category === 'choice') ? dom('div', { class: 'absolute -bottom-px -right-px border border-light-50 dark:border-dark-50 bg-light-10 dark:bg-dark-10 hover:border-light-70 dark:hover:border-dark-70 flex p-1 justify-center items-center', listeners: { click: (e) => { const choice = config.features[option]!.effect.some(e => e.category === 'choice') ? dom('div', { class: 'absolute -bottom-px -right-px border border-light-50 dark:border-dark-50 bg-light-10 dark:bg-dark-10 hover:border-light-70 hover:dark:border-dark-70 flex p-1 justify-center items-center', listeners: { click: (e) => {
e.stopImmediatePropagation(); e.stopImmediatePropagation();
this._builder.character.training[stat as MainStat][parseInt(level[0], 10) as TrainingLevel] === j && this._builder.handleChoice(choice!, config.features[option]!.id); this._builder.character.training[stat as MainStat][parseInt(level[0], 10) as TrainingLevel] === j && this._builder.handleChoice(choice!, config.features[option]!.id);
} } }, [ icon('radix-icons:gear') ]) : undefined; } } }, [ icon('radix-icons:gear') ]) : undefined;
return dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50 relative"], listeners: { click: e => { return dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 hover:dark:border-dark-50 relative"], listeners: { click: e => {
this._builder.toggleTrainingOption(stat, parseInt(level[0]) as TrainingLevel, j); this._builder.toggleTrainingOption(stat, parseInt(level[0]) as TrainingLevel, j);
this.update(); this.update();
}}}, [ markdown(getText(config.features[option]!.description), undefined, { tags: { a: preview } }), choice ]); }}}, [ markdown(getText(config.features[option]!.description), undefined, { tags: { a: preview } }), choice ]);
@@ -1173,15 +1173,15 @@ class TrainingPicker extends BuilderTab
]); ]);
} }
this._pointsInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-none ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }}); this._pointsInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-hidden ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }});
this._options = MAIN_STATS.reduce((p, v) => { p[v] = statRenderBlock(v); return p; }, {} as Record<MainStat, HTMLElement[][]>); this._options = MAIN_STATS.reduce((p, v) => { p[v] = statRenderBlock(v); return p; }, {} as Record<MainStat, HTMLElement[][]>);
this._statIndicator = dom('span', { class: 'rounded-full w-3 h-3 bg-accent-blue absolute transition-[left] after:content-[attr(data-text)] after:absolute after:-translate-x-1/2 after:top-4 after:p-px after:bg-light-0 dark:after:bg-dark-0 after:text-center' }); this._statIndicator = dom('span', { class: 'rounded-full w-3 h-3 bg-accent-blue absolute transition-[left] after:content-[attr(data-text)] after:absolute after:-translate-x-1/2 after:top-4 after:p-px after:bg-light-0 dark:after:bg-dark-0 after:text-center' });
this._statContainer = div('relative select-none transition-[left] flex flex-1 flex-row max-w-full', Object.values(this._options).map(e => div('flex flex-shrink-0 flex-col gap-4 relative w-full overflow-y-auto px-8', e.flatMap(_e => [..._e])))); this._statContainer = div('relative select-none transition-[left] flex flex-1 flex-row max-w-full', Object.values(this._options).map(e => div('flex shrink-0 flex-col gap-4 relative w-full overflow-y-auto px-8', e.flatMap(_e => [..._e]))));
this._content = [ div("flex flex-1 gap-12 px-2 py-4 justify-center items-center sticky top-0 bg-light-0 dark:bg-dark-0 w-full z-10 min-h-20", [ this._content = [ div("flex flex-1 gap-12 px-2 py-4 justify-center items-center sticky top-0 bg-light-0 dark:bg-dark-0 w-full z-10 min-h-20", [
div('flex flex-shrink gap-3 items-center relative w-48 ms-12', [ div('flex flex-shrink gap-3 items-center relative w-48 ms-12', [
...MAIN_STATS.map((e, i) => dom('span', { listeners: { click: () => this.switchTab(i) }, class: 'block w-2.5 h-2.5 m-px outline outline-1 outline-transparent hover:outline-light-70 dark:hover:outline-dark-70 rounded-full bg-light-40 dark:bg-dark-40 cursor-pointer' })), ...MAIN_STATS.map((e, i) => dom('span', { listeners: { click: () => this.switchTab(i) }, class: 'block w-2.5 h-2.5 m-px outline outline-1 outline-transparent hover:outline-light-70 hover:dark:outline-dark-70 rounded-full bg-light-40 dark:bg-dark-40 cursor-pointer' })),
this._statIndicator, this._statIndicator,
]), ]),
div('flex flex-1 gap-12 px-2 py-4 justify-center items-center sticky top-0 bg-light-0 dark:bg-dark-0 w-full z-10', [ div('flex flex-1 gap-12 px-2 py-4 justify-center items-center sticky top-0 bg-light-0 dark:bg-dark-0 w-full z-10', [
@@ -1226,7 +1226,7 @@ class TrainingPicker extends BuilderTab
e[0]?.classList.toggle("opacity-30", (i as TrainingLevel) > max); e[0]?.classList.toggle("opacity-30", (i as TrainingLevel) > max);
e[1]?.classList.toggle("opacity-30", (i as TrainingLevel) > max); e[1]?.classList.toggle("opacity-30", (i as TrainingLevel) > max);
e[1]?.childNodes.forEach((option, j) => { e[1]?.childNodes.forEach((option, j) => {
'!border-accent-blue bg-accent-blue bg-opacity-20'.split(" ").forEach(_e => (option as HTMLElement).classList.toggle(_e, i == 0 || (this._builder.character.training[stat as MainStat][i as TrainingLevel] === j))); '!border-accent-blue bg-accent-blue/20'.split(" ").forEach(_e => (option as HTMLElement).classList.toggle(_e, i == 0 || (this._builder.character.training[stat as MainStat][i as TrainingLevel] === j)));
}) })
}) })
}); });
@@ -1254,7 +1254,7 @@ class AbilityPicker extends BuilderTab
{ {
super(builder); super(builder);
this._pointsInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-none ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }}); this._pointsInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-hidden ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }});
this._options = ABILITIES.map((e, i) => { this._options = ABILITIES.map((e, i) => {
const max = dom('span', { class: 'text-lg text-end font-bold' }); const max = dom('span', { class: 'text-lg text-end font-bold' });
@@ -1322,19 +1322,19 @@ class AspectPicker extends BuilderTab
{ {
super(builder); super(builder);
this._physicInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-none ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }}); this._physicInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-hidden ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }});
this._mentalInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-none ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }}); this._mentalInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-hidden ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }});
this._personalityInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-none ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }}); this._personalityInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-hidden ps-3 pe-1 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }});
this._options = Object.values(config.aspects).map((e, i) => dom('div', { attributes: { "data-aspect": e.id }, listeners: { click: () => { this._options = Object.values(config.aspects).map((e, i) => dom('div', { attributes: { "data-aspect": e.id }, listeners: { click: () => {
this._builder.character.aspect = e.id; this._builder.character.aspect = e.id;
this._options.forEach(_e => _e.setAttribute('data-state', 'inactive')); this._options.forEach(_e => _e.setAttribute('data-state', 'inactive'));
this._options[i]?.setAttribute('data-state', 'active'); this._options[i]?.setAttribute('data-state', 'active');
}}, class: 'group flex flex-col w-[360px] border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50 cursor-pointer' }, [ }}, class: 'group flex flex-col w-[360px] border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50 cursor-pointer' }, [
div('bg-light-10 dark:bg-dark-10 border-b border-light-35 dark:border-dark-35 p-2 flex flex-col gap-2 group-data-[state=active]:bg-accent-blue group-data-[state=active]:bg-opacity-10', [ div('bg-light-10 dark:bg-dark-10 border-b border-light-35 dark:border-dark-35 p-2 flex flex-col gap-2 group-data-[state=active]:bg-accent-blue/10', [
div('flex flex-row gap-8 ps-4 items-center', [ div('flex flex-row gap-8 ps-4 items-center', [
div("flex flex-1 flex-col gap-2 justify-center", [ div('text-lg font-bold', [ text(e.name) ]), dom('span', { class: 'border-b w-full border-light-50 dark:border-dark-50 group-data-[state=active]:border-b-[4px] group-data-[state=active]:border-accent-blue' }) ]), div("flex flex-1 flex-col gap-2 justify-center", [ div('text-lg font-bold', [ text(e.name) ]), dom('span', { class: 'border-b w-full border-light-50 dark:border-dark-50 group-data-[state=active]:border-b-[4px] group-data-[state=active]:border-accent-blue' }) ]),
div('rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100 dark:bg-dark-100 !bg-opacity-10') div('rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100/10 dark:bg-dark-100/10')
]) ])
]), ]),
div('flex justify-stretch items-stretch py-2 px-4 gap-4', [ div('flex justify-stretch items-stretch py-2 px-4 gap-4', [
@@ -1350,7 +1350,7 @@ class AspectPicker extends BuilderTab
]) ])
])); ]));
const filterSwitch = 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-none data-[state=checked]:bg-light-35 dark:data-[state=checked]:bg-dark-35 hover:border-light-50 dark:hover:border-dark-50 focus:shadow-raw focus:shadow-light-40 dark:focus: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]`, attributes: { "data-state": this._filter ? "checked" : "unchecked" }, listeners: { const filterSwitch = 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 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]`, attributes: { "data-state": this._filter ? "checked" : "unchecked" }, listeners: {
click: (e: Event) => { click: (e: Event) => {
this._filter = !this._filter; this._filter = !this._filter;
filterSwitch.setAttribute('data-state', this._filter ? "checked" : "unchecked"); filterSwitch.setAttribute('data-state', this._filter ? "checked" : "unchecked");
@@ -1652,7 +1652,7 @@ export class CharacterSheet
div("flex flex-row items-center gap-1 lg:gap-2 text-xl sm:text-2xl lg:text-3xl font-light", [ div("flex flex-row items-center gap-1 lg:gap-2 text-xl sm:text-2xl lg:text-3xl font-light", [
text("PV: "), text("PV: "),
() => this.character.compiled ? dom("span", { () => this.character.compiled ? dom("span", {
class: "font-bold px-1 lg:px-2 border-transparent border cursor-pointer hover:border-light-35 dark:hover:border-dark-35", class: "font-bold px-1 lg:px-2 border-transparent border cursor-pointer hover:border-light-35 hover:dark:border-dark-35",
text: () => `${this.character.compiled.health - this.character.compiled.variables.health}`, text: () => `${this.character.compiled.health - this.character.compiled.variables.health}`,
listeners: { click: healthPanel.show }, listeners: { click: healthPanel.show },
}) : undefined, }) : undefined,
@@ -1662,7 +1662,7 @@ export class CharacterSheet
div("flex flex-row items-center gap-1 lg:gap-2 text-xl sm:text-2xl lg:text-3xl font-light", [ div("flex flex-row items-center gap-1 lg:gap-2 text-xl sm:text-2xl lg:text-3xl font-light", [
text("Mana: "), text("Mana: "),
() => this.character.compiled ? dom("span", { () => this.character.compiled ? dom("span", {
class: "font-bold px-1 lg:px-2 border-transparent border cursor-pointer hover:border-light-35 dark:hover:border-dark-35", class: "font-bold px-1 lg:px-2 border-transparent border cursor-pointer hover:border-light-35 hover:dark:border-dark-35",
text: () => `${this.character.compiled.mana - this.character.compiled.variables.mana}`, text: () => `${this.character.compiled.mana - this.character.compiled.variables.mana}`,
listeners: { click: healthPanel.show }, listeners: { click: healthPanel.show },
}) : undefined, }) : undefined,
@@ -1832,20 +1832,20 @@ export class CharacterSheet
const value = clamp(this.character.armor.percent > 0 ? Math.floor(inputs.health.sum * clamp(this.character.armor.percent / 100, 0, 1)) : clamp(inputs.health.sum, 0, this.character.armor.flat), 0, this.character.armor.current); const value = clamp(this.character.armor.percent > 0 ? Math.floor(inputs.health.sum * clamp(this.character.armor.percent / 100, 0, 1)) : clamp(inputs.health.sum, 0, this.character.armor.flat), 0, this.character.armor.current);
(this.character.armor.state!.state as ArmorState).loss += value; (this.character.armor.state!.state as ArmorState).loss += value;
inputs.health.sum -= value; inputs.health.sum -= value;
}, 'px-2 h-8 border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red focus:border-light-red dark:focus:border-dark-red focus:shadow-light-red dark:focus:shadow-dark-red', () => this.character.armor.current === 0), 'Dégats', 'left') : undefined, }, 'px-2 h-8 border-light-red dark:border-dark-red hover:border-light-red hover:dark:border-dark-red focus:border-light-red focus:dark:border-dark-red focus:shadow-light-red focus:dark:shadow-dark-red', () => this.character.armor.current === 0), 'Dégats', 'left') : undefined,
tooltip(button(icon('radix-icons:minus', { width: 16, height: 16 }), () => { tooltip(button(icon('radix-icons:minus', { width: 16, height: 16 }), () => {
this.character.compiled.variables.health += inputs.health.sum; this.character.compiled.variables.health += inputs.health.sum;
inputs.health.sum = 0; inputs.health.sum = 0;
DAMAGE_TYPES.forEach(e => inputs.health[e] = 0); DAMAGE_TYPES.forEach(e => inputs.health[e] = 0);
this.saveVariables(); this.saveVariables();
}, 'w-8 h-8 border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red focus:border-light-red dark:focus:border-dark-red focus:shadow-light-red dark:focus:shadow-dark-red'), 'Dégats', 'left'), }, 'w-8 h-8 border-light-red dark:border-dark-red hover:border-light-red hover:dark:border-dark-red focus:border-light-red focus:dark:border-dark-red focus:shadow-light-red focus:dark:shadow-dark-red'), 'Dégats', 'left'),
numberpicker({ defaultValue: () => inputs.health.sum, input: v => { inputs.health.sum = v }, min: 0, disabled: () => inputs.health.open, class: 'h-8 !m-0' }), numberpicker({ defaultValue: () => inputs.health.sum, input: v => { inputs.health.sum = v }, min: 0, disabled: () => inputs.health.open, class: 'h-8 !m-0' }),
tooltip(button(icon('radix-icons:plus', { width: 16, height: 16 }), () => { tooltip(button(icon('radix-icons:plus', { width: 16, height: 16 }), () => {
this.character.compiled.variables.health = Math.max(this.character.compiled.variables.health - inputs.health.sum, 0); this.character.compiled.variables.health = Math.max(this.character.compiled.variables.health - inputs.health.sum, 0);
inputs.health.sum = 0; inputs.health.sum = 0;
DAMAGE_TYPES.forEach(e => inputs.health[e] = 0); DAMAGE_TYPES.forEach(e => inputs.health[e] = 0);
this.saveVariables(); this.saveVariables();
}, 'w-8 h-8 border-light-green dark:border-dark-green hover:border-light-green dark:hover:border-dark-green focus:border-light-green dark:focus:border-dark-green focus:shadow-light-green dark:focus:shadow-dark-green'), 'Soin', 'left'), }, 'w-8 h-8 border-light-green dark:border-dark-green hover:border-light-green hover:dark:border-dark-green focus:border-light-green focus:dark:border-dark-green focus:shadow-light-green focus:dark:shadow-dark-green'), 'Soin', 'left'),
]) ])
]) ])
], { class: { container: 'gap-2', title: 'ps-2' }, open: false, onFold: v => { inputs.health.open = v; if(v) { inputs.health.sum = 0; }} }), ], { class: { container: 'gap-2', title: 'ps-2' }, open: false, onFold: v => { inputs.health.open = v; if(v) { inputs.health.sum = 0; }} }),
@@ -1856,13 +1856,13 @@ export class CharacterSheet
this.character.compiled.variables.mana += inputs.mana; this.character.compiled.variables.mana += inputs.mana;
inputs.mana = 0; inputs.mana = 0;
this.saveVariables(); this.saveVariables();
}, 'w-8 h-8 border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red focus:border-light-red dark:focus:border-dark-red focus:shadow-light-red dark:focus:shadow-dark-red'), 'Dégats', 'left'), }, 'w-8 h-8 border-light-red dark:border-dark-red hover:border-light-red hover:dark:border-dark-red focus:border-light-red focus:dark:border-dark-red focus:shadow-light-red focus:dark:shadow-dark-red'), 'Dégats', 'left'),
numberpicker({ defaultValue: () => inputs.mana, input: v => { inputs.mana = v }, min: 0, class: 'h-8 !m-0' }), numberpicker({ defaultValue: () => inputs.mana, input: v => { inputs.mana = v }, min: 0, class: 'h-8 !m-0' }),
tooltip(button(icon('radix-icons:plus', { width: 16, height: 16 }), () => { tooltip(button(icon('radix-icons:plus', { width: 16, height: 16 }), () => {
this.character.compiled.variables.mana = Math.max(this.character.compiled.variables.mana - inputs.mana, 0); this.character.compiled.variables.mana = Math.max(this.character.compiled.variables.mana - inputs.mana, 0);
inputs.mana = 0; inputs.mana = 0;
this.saveVariables(); this.saveVariables();
}, 'w-8 h-8 border-light-green dark:border-dark-green hover:border-light-green dark:hover:border-dark-green focus:border-light-green dark:focus:border-dark-green focus:shadow-light-green dark:focus:shadow-dark-green'), 'Soin', 'left'), }, 'w-8 h-8 border-light-green dark:border-dark-green hover:border-light-green hover:dark:border-dark-green focus:border-light-green focus:dark:border-dark-green focus:shadow-light-green focus:dark:shadow-dark-green'), 'Soin', 'left'),
]) ])
]), ]),
]); ]);
@@ -1983,12 +1983,12 @@ export class CharacterSheet
const panel = this.spellPanel(); const panel = this.spellPanel();
const sorter = function(this: HTMLElement) { return followermenu(this, [ const sorter = function(this: HTMLElement) { return followermenu(this, [
() => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 dark:hover:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'rank-asc' ? 'rank-desc' : preference.sort === 'rank-desc' ? '' : 'rank-asc') } }, [text('Rang'), () => preference.sort.startsWith('rank') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]), () => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 hover:dark:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'rank-asc' ? 'rank-desc' : preference.sort === 'rank-desc' ? '' : 'rank-asc') } }, [text('Rang'), () => preference.sort.startsWith('rank') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]),
() => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 dark:hover:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'type-asc' ? 'type-desc' : preference.sort === 'type-desc' ? '' : 'type-asc') } }, [text('Type'), () => preference.sort.startsWith('type') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]), () => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 hover:dark:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'type-asc' ? 'type-desc' : preference.sort === 'type-desc' ? '' : 'type-asc') } }, [text('Type'), () => preference.sort.startsWith('type') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]),
() => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 dark:hover:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'element-asc' ? 'element-desc' : preference.sort === 'element-desc' ? '' : 'element-asc') } }, [text('Element'), () => preference.sort.startsWith('element') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]), () => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 hover:dark:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'element-asc' ? 'element-desc' : preference.sort === 'element-desc' ? '' : 'element-asc') } }, [text('Element'), () => preference.sort.startsWith('element') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]),
() => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 dark:hover:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'cost-asc' ? 'cost-desc' : preference.sort === 'cost-desc' ? '' : 'cost-asc') } }, [text('Coût'), () => preference.sort.startsWith('cost') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]), () => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 hover:dark:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'cost-asc' ? 'cost-desc' : preference.sort === 'cost-desc' ? '' : 'cost-asc') } }, [text('Coût'), () => preference.sort.startsWith('cost') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]),
() => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 dark:hover:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'range-asc' ? 'range-desc' : preference.sort === 'range-desc' ? '' : 'range-asc') } }, [text('Portée'), () => preference.sort.startsWith('range') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]), () => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 hover:dark:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'range-asc' ? 'range-desc' : preference.sort === 'range-desc' ? '' : 'range-asc') } }, [text('Portée'), () => preference.sort.startsWith('range') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]),
() => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 dark:hover:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'speed-asc' ? 'speed-desc' : preference.sort === 'speed-desc' ? '' : 'speed-asc') } }, [text('Incantation'), () => preference.sort.startsWith('speed') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]), () => dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 hover:dark:bg-dark-40 cursor-pointer', listeners: { click: () => preference.sort = (preference.sort === 'speed-asc' ? 'speed-desc' : preference.sort === 'speed-desc' ? '' : 'speed-asc') } }, [text('Incantation'), () => preference.sort.startsWith('speed') ? icon(preference.sort.endsWith('asc') ? 'ph:sort-ascending' : 'ph:sort-descending', { width: 16, height: 16 }) : undefined ]),
], { class: 'text-light-100 dark:text-dark-100 w-32', offset: 8, placement: 'bottom-end', arrow: true }); ], { class: 'text-light-100 dark:text-dark-100 w-32', offset: 8, placement: 'bottom-end', arrow: true });
} }
@@ -2073,7 +2073,7 @@ export class CharacterSheet
div("flex flex-row text-sm gap-2", div("flex flex-row text-sm gap-2",
spell.elements.map(el => spell.elements.map(el =>
dom("span", { dom("span", {
class: [`border !border-opacity-50 rounded-full !bg-opacity-20 px-2 py-px`, elementTexts[el].class], class: [`border rounded-full px-2 py-px`, elementTexts[el].class],
text: elementTexts[el].text text: elementTexts[el].text
}) })
) )
@@ -2149,7 +2149,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 !bg-opacity-20', { 'border-accent-blue bg-accent-blue': !e.cursed, 'border-light-purple bg-light-purple dark:border-dark-purple dark:bg-dark-purple': 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 lg: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,

View File

@@ -10,7 +10,7 @@ export function link(children: NodeChildren, properties?: NodeProperties & { act
{ {
const router = useRouter(); const router = useRouter();
const nav = link ? router.resolve(link) : undefined; const nav = link ? router.resolve(link) : undefined;
return dom('a', { ...properties, class: [properties?.class, properties?.active && router.currentRoute.value.fullPath === nav?.fullPath ? properties.active : undefined], attributes: { href: nav?.href, 'data-active': !!properties?.active }, listeners: link ? { return dom('a', { ...properties, class: [properties?.class, properties?.active && router.currentRoute.value.fullPath === nav?.fullPath ? properties.active : undefined], attributes: { href: nav?.href, 'data-active': properties?.active }, listeners: link ? {
click: function(e) click: function(e)
{ {
e.preventDefault(); e.preventDefault();
@@ -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-none leading-none transition-[box-shadow] const btn = dom('button', { 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 dark:hover:bg-dark-25 hover:border-light-50 dark:hover: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 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus: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`, cls], 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,9 +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 dark:hover:bg-dark-30 flex items-center justify-center bg-light-20 dark:bg-dark-20 leading-none outline-none 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
border border-light-40 dark:border-dark-40 hover:border-light-50 dark:hover: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 dark:focus: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`,
settings?.class?.option], 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)
{ {
@@ -79,7 +79,7 @@ export function buttongroup<T extends any>(options: Array<{ text: string, value:
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 dark:hover:bg-dark-35 cursor-pointer', settings?.class?.option], text: e.title, listeners: { click: () => { e.click(); close() } } }))); 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() } } })));
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;
} }
@@ -128,7 +128,7 @@ export function select<T extends NonNullable<any>>(options: Reactive<Array<{ tex
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: ['flex flex-col max-h-[320px] overflow-auto', settings?.class?.popup], 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-none 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 dark:focus:shadow-dark-40 hover:border-light-50 dark:hover: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: ['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 };
Object.defineProperty(select, 'disabled', { Object.defineProperty(select, 'disabled', {
get: () => disabled, get: () => disabled,
@@ -213,7 +213,7 @@ export function multiselect<T extends NonNullable<any>>(options: Option<T>[], se
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: ['flex flex-col max-h-[320px] overflow-auto', settings?.class?.popup], 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-none 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 dark:focus:shadow-dark-40 hover:border-light-50 dark:hover: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: ['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 };
Object.defineProperty(select, 'disabled', { Object.defineProperty(select, 'disabled', {
get: () => disabled, get: () => disabled,
@@ -319,7 +319,7 @@ export function multiselect<T extends NonNullable<any>>(options: Option<T>[], se
const box = select.getBoundingClientRect(); const box = select.getBoundingClientRect();
context = contextmenu(box.x, box.y + box.height, 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": `${box.width}px` }, blur: () => window.removeEventListener('keydown', handleKeys) }); context = contextmenu(box.x, box.y + box.height, 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": `${box.width}px` }, blur: () => window.removeEventListener('keydown', handleKeys) });
} }, class: ['mx-4 inline-flex items-center justify-between px-3 text-sm font-semibold leading-none 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 dark:focus:shadow-dark-40 hover:border-light-50 dark:hover: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] }, [ dom('span', {}, [ textValue ]), icon('radix-icons:caret-down') ]); } }, class: ['mx-4 inline-flex items-center justify-between px-3 text-sm font-semibold leading-none 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] }, [ dom('span', {}, [ textValue ]), icon('radix-icons:caret-down') ]);
Object.defineProperty(select, 'disabled', { Object.defineProperty(select, 'disabled', {
get: () => disabled, get: () => disabled,
@@ -451,9 +451,9 @@ export function combobox<T extends NonNullable<any>>(options: RecurrentOption<T>
return; return;
default: return; default: return;
} }
} }, attributes: { type: 'text', }, class: 'flex-1 outline-none 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-none 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 dark:focus:shadow-dark-40 hover:border-light-50 dark:hover: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: ['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 };
let value: T | undefined = undefined; let value: T | undefined = undefined;
Object.defineProperty(container, 'disabled', { Object.defineProperty(container, 'disabled', {
@@ -578,10 +578,10 @@ export function tagpicker<T extends NonNullable<any>>(options: Reactive<Option<T
return; return;
default: return; default: return;
} }
} }, attributes: { type: 'text', }, class: 'flex-1 outline-none 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-none 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 dark:focus:shadow-dark-40 hover:border-light-50 dark:hover: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 flex-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 dark:hover:text-dark-100' }) ]) ]) }), select ]) as HTMLLabelElement & { disabled: boolean, value: T[] }; 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[] };
Object.defineProperty(container, 'disabled', { Object.defineProperty(container, 'disabled', {
get: () => disabled, get: () => disabled,
@@ -604,8 +604,8 @@ 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: [`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-none px-3 py-1 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 dark:focus: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 dark:hover: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`, settings?.class], 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,
@@ -634,7 +634,7 @@ 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-none px-3 py-1 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 dark:focus:shadow-dark-40 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover: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: [`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: {
input: () => validateAndChange(parseInt(field.value.trim().toLowerCase().normalize().replace(/[a-z,.]/g, ""), 10)) && settings?.input && settings.input(storedValue), input: () => validateAndChange(parseInt(field.value.trim().toLowerCase().normalize().replace(/[a-z,.]/g, ""), 10)) && settings?.input && settings.input(storedValue),
keydown: (e: KeyboardEvent) => { keydown: (e: KeyboardEvent) => {
if(field.disabled) if(field.disabled)
@@ -708,8 +708,8 @@ export function table(content: TableRow[], headers: TableRow, properties?: { cla
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-none 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
data-[state=checked]:bg-light-35 dark:data-[state=checked]:bg-dark-35 hover:border-light-50 dark:hover:border-dark-50 focus:shadow-raw focus:shadow-light-40 dark:focus: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]`, settings?.class?.container], 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'))
@@ -727,8 +727,8 @@ export function checkbox(settings?: { defaultValue?: boolean, change?: (this: HT
{ {
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: [`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 dark:hover:bg-dark-30 hover:border-light-60 dark:hover: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 dark:hover: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`, settings?.class?.container], 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;
@@ -985,7 +985,7 @@ export class Toaster
{ {
Toaster.dispose(); Toaster.dispose();
Toaster._container = dom('div', { attributes: { id: 'toaster' }, class: 'fixed bottom-0 right-0 flex flex-col p-6 gap-2 max-w-[512px] z-50 outline-none min-w-72 empty:hidden' }); Toaster._container = dom('div', { attributes: { id: 'toaster' }, class: 'fixed bottom-0 right-0 flex flex-col p-6 gap-2 max-w-[512px] z-50 outline-hidden min-w-72 empty:hidden' });
document.body.appendChild(Toaster._container); document.body.appendChild(Toaster._container);
} }
static dispose() static dispose()
@@ -1029,7 +1029,7 @@ export class Toaster
config.closeable ? dom('span', { class: 'translate-x-4 text-light-100 dark:text-dark-100', listeners: { click: () => Toaster.close(config), } }, [ icon('radix-icons:cross-1', { width: 12, height: 12, class: 'cursor-pointer' }) ]) : undefined, config.closeable ? dom('span', { class: 'translate-x-4 text-light-100 dark:text-dark-100', listeners: { click: () => Toaster.close(config), } }, [ icon('radix-icons:cross-1', { width: 12, height: 12, class: 'cursor-pointer' }) ]) : undefined,
config.content ? dom('span', { class: 'text-sm col-span-8 text-light-100 dark:text-dark-100', text: config.content }) : undefined, config.content ? dom('span', { class: 'text-sm col-span-8 text-light-100 dark:text-dark-100', text: config.content }) : undefined,
]), ]),
config.timer ? dom('div', { class: 'relative overflow-hidden bg-light-25 dark:bg-dark-25 h-1 mb-0 mt-0 w-full group-data-[type=error]:*:bg-light-red dark:group-data-[type=error]:*:bg-dark-red group-data-[type=success]:*:bg-light-green dark:group-data-[type=success]:*:bg-dark-green group-data-[type=error]:bg-light-red dark:group-data-[type=error]:bg-dark-red group-data-[type=success]:bg-light-green dark:group-data-[type=success]:bg-dark-green !bg-opacity-50' }, [ loader ]) : undefined config.timer ? dom('div', { class: 'relative overflow-hidden bg-light-25/50 dark:bg-dark-25/50 h-1 mb-0 mt-0 w-full group-data-[type=error]:*:bg-light-red/50 dark:group-data-[type=error]:*:bg-dark-red/50 group-data-[type=success]:*:bg-light-green/50 dark:group-data-[type=success]:*:bg-dark-green/50 group-data-[type=error]:bg-light-red/50 dark:group-data-[type=error]:bg-dark-red/50 group-data-[type=success]:bg-light-green/50 dark:group-data-[type=success]:bg-dark-green/50' }, [ loader ]) : undefined
]); ]);
config.dom.addEventListener('mousedown', dragstart); config.dom.addEventListener('mousedown', dragstart);
config.dom.animate([{ transform: 'translateX(100%)' }, { transform: 'translateX(0)' }], { duration: 150, easing: 'cubic-bezier(0.16, 1, 0.3, 1)' }); config.dom.animate([{ transform: 'translateX(100%)' }, { transform: 'translateX(0)' }], { duration: 150, easing: 'cubic-bezier(0.16, 1, 0.3, 1)' });
@@ -1057,7 +1057,7 @@ export class DiceRoller
{ {
DiceRoller.dispose(); DiceRoller.dispose();
DiceRoller._dom = dom('div', { attributes: { id: 'dice-roller' }, class: 'fixed bottom-0 right-0 flex flex-col p-6 gap-2 max-w-[512px] z-50 outline-none min-w-72 empty:hidden' }); DiceRoller._dom = dom('div', { attributes: { id: 'dice-roller' }, class: 'fixed bottom-0 right-0 flex flex-col p-6 gap-2 max-w-[512px] z-50 outline-hidden min-w-72 empty:hidden' });
document.body.appendChild(DiceRoller._dom); document.body.appendChild(DiceRoller._dom);
} }
static dispose() static dispose()

View File

@@ -705,14 +705,14 @@ export class Editor
Content.ready.then(() => { Content.ready.then(() => {
this.tree = new TreeDOM((item, depth) => { this.tree = new TreeDOM((item, depth) => {
return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-left': `${depth / 2 - 0.5}em` } }, [dom('div', { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full cursor-pointer font-medium'], attributes: { 'data-private': item.private }, listeners: { contextmenu: (e) => this.contextmenu(e, item as LocalContent)} }, [ return dom('div', { class: 'group flex items-center ps-2 outline-hidden relative cursor-pointer', style: { 'padding-left': `${depth / 2 - 0.5}em` } }, [dom('div', { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full cursor-pointer font-medium'], attributes: { 'data-private': item.private }, listeners: { contextmenu: (e) => this.contextmenu(e, item as LocalContent)} }, [
icon('radix-icons:chevron-right', { class: 'h-4 w-4 transition-transform absolute group-data-[state=open]:rotate-90', style: { 'left': `${depth / 2 - 1.5}em` } }), icon('radix-icons:chevron-right', { class: 'h-4 w-4 transition-transform absolute group-data-[state=open]:rotate-90', style: { 'left': `${depth / 2 - 1.5}em` } }),
dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }), dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }),
tooltip(dom('span', { class: 'flex', listeners: { click: e => this.toggleNavigable(e, item as LocalContent) } }, [icon(item.navigable ? 'radix-icons:eye-open' : 'radix-icons:eye-none', { class: ['mx-1', { 'opacity-50': !item.navigable }] })]), 'Navigable', 'left'), tooltip(dom('span', { class: 'flex', listeners: { click: e => this.toggleNavigable(e, item as LocalContent) } }, [icon(item.navigable ? 'radix-icons:eye-open' : 'radix-icons:eye-none', { class: ['mx-1', { 'opacity-50': !item.navigable }] })]), 'Navigable', 'left'),
tooltip(dom('span', { class: 'flex', listeners: { click: e => this.togglePrivate(e, item as LocalContent) } }, [icon(item.private ? 'radix-icons:lock-closed' : 'radix-icons:lock-open-2', { class: ['mx-1', { 'opacity-50': !item.private }] })]), 'Privé', 'right'), tooltip(dom('span', { class: 'flex', listeners: { click: e => this.togglePrivate(e, item as LocalContent) } }, [icon(item.private ? 'radix-icons:lock-closed' : 'radix-icons:lock-open-2', { class: ['mx-1', { 'opacity-50': !item.private }] })]), 'Privé', 'right'),
])]); ])]);
}, (item, depth) => { }, (item, depth) => {
return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-left': `${depth / 2 - 0.5}em` } }, [dom('div', { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full'], attributes: { 'data-private': item.private }, listeners: { contextmenu: (e) => this.contextmenu(e, item as LocalContent), click: () => this.select(item as LocalContent) } }, [ return dom('div', { class: 'group flex items-center ps-2 outline-hidden relative cursor-pointer', style: { 'padding-left': `${depth / 2 - 0.5}em` } }, [dom('div', { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full'], attributes: { 'data-private': item.private }, listeners: { contextmenu: (e) => this.contextmenu(e, item as LocalContent), click: () => this.select(item as LocalContent) } }, [
icon(iconByType[item.type], { class: 'w-5 h-5', width: 20, height: 20 }), icon(iconByType[item.type], { class: 'w-5 h-5', width: 20, height: 20 }),
dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }), dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }),
tooltip(dom('span', { class: 'flex', listeners: { click: e => this.toggleNavigable(e, item as LocalContent) } }, [icon(item.navigable ? 'radix-icons:eye-open' : 'radix-icons:eye-none', { class: ['mx-1', { 'opacity-50': !item.navigable }] })]), 'Navigable', 'left'), tooltip(dom('span', { class: 'flex', listeners: { click: e => this.toggleNavigable(e, item as LocalContent) } }, [icon(item.navigable ? 'radix-icons:eye-open' : 'radix-icons:eye-none', { class: ['mx-1', { 'opacity-50': !item.navigable }] })]), 'Navigable', 'left'),
@@ -734,9 +734,9 @@ export class Editor
e.preventDefault(); e.preventDefault();
const { close } = contextmenu(e.clientX, e.clientY, [ const { close } = contextmenu(e.clientX, e.clientY, [
dom('div', { class: 'hover:bg-light-35 dark:hover:bg-dark-35 px-2 gap-2 flex py-1 items-center cursor-pointer text-light-100 dark:text-dark-100', listeners: { click: (e) => { this.add("markdown", item); close() }} }, [icon('radix-icons:plus'), text('Ajouter')]), dom('div', { class: 'hover:bg-light-35 hover:dark:bg-dark-35 px-2 gap-2 flex py-1 items-center cursor-pointer text-light-100 dark:text-dark-100', listeners: { click: (e) => { this.add("markdown", item); close() }} }, [icon('radix-icons:plus'), text('Ajouter')]),
dom('div', { class: 'hover:bg-light-35 dark:hover:bg-dark-35 px-2 gap-2 flex py-1 items-center cursor-pointer text-light-100 dark:text-dark-100', listeners: { click: (e) => { this.rename(item); close() }} }, [icon('radix-icons:input'), text('Renommer')]), dom('div', { class: 'hover:bg-light-35 hover:dark:bg-dark-35 px-2 gap-2 flex py-1 items-center cursor-pointer text-light-100 dark:text-dark-100', listeners: { click: (e) => { this.rename(item); close() }} }, [icon('radix-icons:input'), text('Renommer')]),
dom('div', { class: 'hover:bg-light-35 dark:hover:bg-dark-35 px-2 gap-2 flex py-1 items-center cursor-pointer text-light-red dark:text-dark-red', listeners: { click: (e) => { close(); confirm(`Confirmer la suppression de ${item.title}${item.children ? ' et de ses enfants' : ''} ?`).then(e => { if(e) this.remove(item)}) }} }, [icon('radix-icons:trash'), text('Supprimer')]), dom('div', { class: 'hover:bg-light-35 hover:dark:bg-dark-35 px-2 gap-2 flex py-1 items-center cursor-pointer text-light-red dark:text-dark-red', listeners: { click: (e) => { close(); confirm(`Confirmer la suppression de ${item.title}${item.children ? ' et de ses enfants' : ''} ?`).then(e => { if(e) this.remove(item)}) }} }, [icon('radix-icons:trash'), text('Supprimer')]),
], { placement: 'right-start', offset: 8 }); ], { placement: 'right-start', offset: 8 });
} }
private add(type: FileType, nextTo: Recursive<LocalContent>) private add(type: FileType, nextTo: Recursive<LocalContent>)

View File

@@ -19,7 +19,7 @@ import { followermenu } from './floating';
const External = Annotation.define<boolean>(); const External = Annotation.define<boolean>();
const Hidden = Decoration.mark({ class: 'hidden' }); const Hidden = Decoration.mark({ class: 'hidden' });
const Bullet = Decoration.mark({ class: '*:hidden before:absolute before:top-2 before:left-0 before:inline-block before:w-2 before:h-2 before:rounded before:bg-light-40 dark:before:bg-dark-40 relative ps-4' }); const Bullet = Decoration.mark({ class: '*:hidden before:absolute before:top-2 before:left-0 before:inline-block before:w-2 before:h-2 before:rounded-sm before:bg-light-40 dark:before:bg-dark-40 relative ps-4' });
const Blockquote = Decoration.line({ class: '*:hidden before:block !ps-4 relative before:absolute before:top-0 before:bottom-0 before:left-0 before:w-1 before:bg-none before:bg-light-30 dark:before:bg-dark-30' }); const Blockquote = Decoration.line({ class: '*:hidden before:block !ps-4 relative before:absolute before:top-0 before:bottom-0 before:left-0 before:w-1 before:bg-none before:bg-light-30 dark:before:bg-dark-30' });
const intersects = (a: { const intersects = (a: {
@@ -43,8 +43,8 @@ const highlight = HighlightStyle.define([
{ tag: tags.strong, fontWeight: "bold" }, { tag: tags.strong, fontWeight: "bold" },
{ tag: tags.strikethrough, textDecoration: "line-through" }, { tag: tags.strikethrough, textDecoration: "line-through" },
{ tag: tags.keyword, class: "text-accent-blue" }, { tag: tags.keyword, class: "text-accent-blue" },
{ tag: tags.monospace, class: "border border-light-35 dark:border-dark-35 px-2 py-px rounded-sm bg-light-20 dark:bg-dark-20" }, { tag: tags.monospace, class: "border border-light-35 dark:border-dark-35 px-2 py-px rounded-xs bg-light-20 dark:bg-dark-20" },
{ tag: tagTag, class: "cursor-default bg-accent-blue bg-opacity-10 text-accent-blue text-sm px-1 ms-1 pb-0.5 rounded-full rounded-se-none border border-accent-blue border-opacity-30" }, { tag: tagTag, class: "cursor-default bg-accent-blue/10 text-accent-blue text-sm px-1 ms-1 pb-0.5 rounded-full rounded-se-none border border-accent-blue/30" },
]); ]);
class CalloutWidget extends WidgetType class CalloutWidget extends WidgetType
@@ -240,16 +240,19 @@ export class MarkdownEditor
const viewer = MarkdownEditor.viewer; const viewer = MarkdownEditor.viewer;
this.parentElement?.toggleAttribute('data-focused', true); this.parentElement?.toggleAttribute('data-focused', true);
const follower = followermenu(this, [ const follower = followermenu(this, [
dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 dark:hover:bg-dark-40 cursor-pointer', listeners: { click: () => { MarkdownEditor.viewer = 'edit'; follower.close(); } } }, [span('', 'Modif. source'), viewer === 'edit' ? icon('radix-icons:check', { width: 16, height: 16 }) : undefined ]), dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 hover:dark:bg-dark-40 cursor-pointer', listeners: { click: () => { MarkdownEditor.viewer = 'edit'; follower.close(); } } }, [span('', 'Modif. source'), viewer === 'edit' ? icon('radix-icons:check', { width: 16, height: 16 }) : undefined ]),
dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 dark:hover:bg-dark-40 cursor-pointer', listeners: { click: () => { MarkdownEditor.viewer = 'live'; follower.close(); } } }, [span('', 'Modifi. live'), viewer === 'live' ? icon('radix-icons:check', { width: 16, height: 16 }) : undefined ]), dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 hover:dark:bg-dark-40 cursor-pointer', listeners: { click: () => { MarkdownEditor.viewer = 'live'; follower.close(); } } }, [span('', 'Modifi. live'), viewer === 'live' ? icon('radix-icons:check', { width: 16, height: 16 }) : undefined ]),
dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 dark:hover:bg-dark-40 cursor-pointer', listeners: { click: () => { MarkdownEditor.viewer = 'read'; follower.close(); } } }, [span('', 'Lecture seule'), viewer === 'read' ? icon('radix-icons:check', { width: 16, height: 16 }) : undefined ]), dom('div', { class: 'flex flex-row justify-between items-center gap-4 py-1 px-2 hover:bg-light-40 hover:dark:bg-dark-40 cursor-pointer', listeners: { click: () => { MarkdownEditor.viewer = 'read'; follower.close(); } } }, [span('', 'Lecture seule'), viewer === 'read' ? icon('radix-icons:check', { width: 16, height: 16 }) : undefined ]),
], { class: 'text-light-100 dark:text-dark-100', offset: 0, placement: 'right-start', blur: () => this.parentElement?.toggleAttribute('data-focused', false) }); ], { class: 'text-light-100 dark:text-dark-100', offset: 0, placement: 'right-start', blur: () => this.parentElement?.toggleAttribute('data-focused', false) });
return follower; return follower;
} }
constructor() constructor()
{ {
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'), button([icon('radix-icons:gear')], MarkdownEditor.settings, 'p-1 hidden group-data-[focused]/editor:block group-hover/editor:block') ]), ]); 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'),
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,
}).of(undefined); }).of(undefined);

View File

@@ -34,7 +34,7 @@ export class HomebrewBuilder
{ id: 'items', title: [ text("Objets") ], content: () => this.items() }, { id: 'items', title: [ text("Objets") ], content: () => this.items() },
{ id: 'improvements', title: [ text("Améliorations") ], content: () => this.improvements() }, { id: 'improvements', title: [ text("Améliorations") ], content: () => this.improvements() },
{ id: 'trees', title: [ text("Arbres") ], content: () => this.trees() }, { id: 'trees', title: [ text("Arbres") ], content: () => this.trees() },
], { focused: 'training', class: { container: 'flex-1 outline-none max-w-full w-full overflow-y-auto', tabbar: 'flex w-full flex-row gap-4 items-center justify-center relative' } }); ], { focused: 'training', class: { container: 'flex-1 outline-hidden max-w-full w-full overflow-y-auto', tabbar: 'flex w-full flex-row gap-4 items-center justify-center relative' } });
this._tabs.children[0]?.appendChild(tooltip(button(icon('radix-icons:clipboard'), () => this.save(), 'p-1'), 'Copier', 'bottom')); this._tabs.children[0]?.appendChild(tooltip(button(icon('radix-icons:clipboard'), () => this.save(), 'p-1'), 'Copier', 'bottom'));
this._container.appendChild(div('flex flex-1 flex-col justify-start items-center px-8 w-full h-full overflow-y-hidden', [ this._container.appendChild(div('flex flex-1 flex-col justify-start items-center px-8 w-full h-full overflow-y-hidden', [
@@ -62,7 +62,7 @@ export class HomebrewBuilder
(content[0] as HTMLElement).appendChild(peopleRender(people)); (content[0] as HTMLElement).appendChild(peopleRender(people));
} }
const render = (people: string, level: Level, feature: string) => { const render = (people: string, level: Level, feature: string) => {
let element = dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: e => { let element = dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 hover:dark:border-dark-50"], listeners: { click: e => {
FeaturePanel.edit(config.features[feature]!).then(e => { FeaturePanel.edit(config.features[feature]!).then(e => {
config.features[feature] = e; config.features[feature] = e;
element.replaceChildren(markdown(getText(config.features[feature]!.description), undefined, { tags: { a: preview } })); element.replaceChildren(markdown(getText(config.features[feature]!.description), undefined, { tags: { a: preview } }));
@@ -70,14 +70,14 @@ export class HomebrewBuilder
}, contextmenu: (e) => { }, contextmenu: (e) => {
e.preventDefault(); e.preventDefault();
const context = contextmenu(e.clientX, e.clientY, [ const context = contextmenu(e.clientX, e.clientY, [
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => { dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 hover:dark:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
context.close(); context.close();
const _feature: Feature = { id: getID(), description: getID(), effect: [] }; const _feature: Feature = { id: getID(), description: getID(), effect: [] };
config.features[_feature.id] = _feature; config.features[_feature.id] = _feature;
config.peoples[people]!.options[level]!.push(_feature.id); config.peoples[people]!.options[level]!.push(_feature.id);
element.parentElement?.appendChild(render(people, level, _feature.id)); element.parentElement?.appendChild(render(people, level, _feature.id));
} } }, [ text('Nouveau') ]), } } }, [ text('Nouveau') ]),
config.peoples[people]!.options[level].length > 1 ? dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => { config.peoples[people]!.options[level].length > 1 ? dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 hover:dark:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
context.close(); context.close();
confirm('Voulez-vous vraiment supprimer cet element ?').then(e => { if(e) { confirm('Voulez-vous vraiment supprimer cet element ?').then(e => { if(e) {
config.peoples[people]!.options[level] = config.peoples[people]!.options[level].filter(e => e !== feature); config.peoples[people]!.options[level] = config.peoples[people]!.options[level].filter(e => e !== feature);
@@ -92,7 +92,7 @@ export class HomebrewBuilder
const peopleRender = (people: RaceConfig) => { const peopleRender = (people: RaceConfig) => {
return foldable(() => Object.entries(people.options).flatMap(level => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative" }, [ text(level[0]) ])]), return foldable(() => Object.entries(people.options).flatMap(level => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative" }, [ text(level[0]) ])]),
div("flex flex-row gap-4 justify-center", level[1].map((option) => render(people.id, parseInt(level[0], 10) as Level, option))), div("flex flex-row gap-4 justify-center", level[1].map((option) => render(people.id, parseInt(level[0], 10) as Level, option))),
]), [ input('text', { defaultValue: people.name, input: (value) => { people.name = value }, class: 'w-32' }), input('text', { defaultValue: people.description, input: (value) => { people.description = value }, class: 'w-full' }) ], { class: { container: 'gap-2 max-h-full', title: 'flex flex-row', content: 'flex flex-shrink-0 flex-col gap-4 relative w-full overflow-y-auto px-8' }, open: false }) ]), [ input('text', { defaultValue: people.name, input: (value) => { people.name = value }, class: 'w-32' }), input('text', { defaultValue: people.description, input: (value) => { people.description = value }, class: 'w-full' }) ], { class: { container: 'gap-2 max-h-full', title: 'flex flex-row', content: 'flex shrink-0 flex-col gap-4 relative w-full overflow-y-auto px-8' }, open: false })
} }
const container = div('flex flex-col gap-2', Object.values(config.peoples).map(peopleRender)); const container = div('flex flex-col gap-2', Object.values(config.peoples).map(peopleRender));
const content = [ div('flex flex-col py-2 gap-2', [ div('w-full flex flex-row-reverse', [ button(icon('radix-icons:plus'), add, 'p-1') ]), container ]) ]; const content = [ div('flex flex-col py-2 gap-2', [ div('w-full flex flex-row-reverse', [ button(icon('radix-icons:plus'), add, 'p-1') ]), container ]) ];
@@ -111,7 +111,7 @@ export class HomebrewBuilder
_statContainer.style.left = `-${tab * 100}%`; _statContainer.style.left = `-${tab * 100}%`;
} }
const render = (stat: MainStat, level: TrainingLevel, feature: string) => { const render = (stat: MainStat, level: TrainingLevel, feature: string) => {
let element = dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: e => { let element = dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 hover:dark:border-dark-50"], listeners: { click: e => {
FeaturePanel.edit(config.features[feature]!).then(e => { FeaturePanel.edit(config.features[feature]!).then(e => {
config.features[feature] = e; config.features[feature] = e;
element.replaceChildren(markdown(getText(config.features[feature]!.description), undefined, { tags: { a: preview } })); element.replaceChildren(markdown(getText(config.features[feature]!.description), undefined, { tags: { a: preview } }));
@@ -119,14 +119,14 @@ export class HomebrewBuilder
}, contextmenu: (e) => { }, contextmenu: (e) => {
e.preventDefault(); e.preventDefault();
const context = contextmenu(e.clientX, e.clientY, [ const context = contextmenu(e.clientX, e.clientY, [
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => { dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 hover:dark:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
context.close(); context.close();
const _feature: Feature = { id: getID(), description: getID(), effect: [] }; const _feature: Feature = { id: getID(), description: getID(), effect: [] };
config.features[_feature.id] = _feature; config.features[_feature.id] = _feature;
config.training[stat][level].push(_feature.id); config.training[stat][level].push(_feature.id);
element.parentElement?.appendChild(render(stat, level, _feature.id)); element.parentElement?.appendChild(render(stat, level, _feature.id));
} } }, [ text('Nouveau') ]), } } }, [ text('Nouveau') ]),
config.training[stat][level].length > 1 ? dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => { config.training[stat][level].length > 1 ? dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 hover:dark:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
context.close(); context.close();
confirm('Voulez-vous vraiment supprimer cet element ?').then(e => { if(e) { confirm('Voulez-vous vraiment supprimer cet element ?').then(e => { if(e) {
config.training[stat][level as any as TrainingLevel] = config.training[stat][level as any as TrainingLevel].filter(e => e !== feature); config.training[stat][level as any as TrainingLevel] = config.training[stat][level as any as TrainingLevel].filter(e => e !== feature);
@@ -148,10 +148,10 @@ export class HomebrewBuilder
const _options = MAIN_STATS.reduce((p, v) => { p[v] = statRenderBlock(v); return p; }, {} as Record<MainStat, HTMLElement[][]>); const _options = MAIN_STATS.reduce((p, v) => { p[v] = statRenderBlock(v); return p; }, {} as Record<MainStat, HTMLElement[][]>);
const _statIndicator = dom('span', { class: 'rounded-full w-3 h-3 bg-accent-blue absolute transition-[left] after:content-[attr(data-text)] after:absolute after:-translate-x-1/2 after:top-4 after:p-px after:bg-light-0 dark:after:bg-dark-0 after:text-center' }); const _statIndicator = dom('span', { class: 'rounded-full w-3 h-3 bg-accent-blue absolute transition-[left] after:content-[attr(data-text)] after:absolute after:-translate-x-1/2 after:top-4 after:p-px after:bg-light-0 dark:after:bg-dark-0 after:text-center' });
const _statContainer = div('relative select-none transition-[left] flex flex-1 flex-row max-w-full', Object.values(_options).map(e => div('flex flex-shrink-0 flex-col gap-4 relative w-full overflow-y-auto px-8', e.flatMap(_e => [..._e])))); const _statContainer = div('relative select-none transition-[left] flex flex-1 flex-row max-w-full', Object.values(_options).map(e => div('flex shrink-0 flex-col gap-4 relative w-full overflow-y-auto px-8', e.flatMap(_e => [..._e]))));
const content = [ div("flex flex-1 gap-12 px-2 py-4 justify-center items-center sticky top-0 bg-light-0 dark:bg-dark-0 w-full z-10 min-h-20", [ const content = [ div("flex flex-1 gap-12 px-2 py-4 justify-center items-center sticky top-0 bg-light-0 dark:bg-dark-0 w-full z-10 min-h-20", [
div('flex flex-shrink gap-3 items-center relative w-48 ms-12', [ div('flex flex-shrink gap-3 items-center relative w-48 ms-12', [
...MAIN_STATS.map((e, i) => dom('span', { listeners: { click: () => switchTab(i) }, class: 'block w-2.5 h-2.5 m-px outline outline-1 outline-transparent hover:outline-light-70 dark:hover:outline-dark-70 rounded-full bg-light-40 dark:bg-dark-40 cursor-pointer' })), ...MAIN_STATS.map((e, i) => dom('span', { listeners: { click: () => switchTab(i) }, class: 'block w-2.5 h-2.5 m-px outline outline-1 outline-transparent hover:outline-light-70 hover:dark:outline-dark-70 rounded-full bg-light-40 dark:bg-dark-40 cursor-pointer' })),
_statIndicator, _statIndicator,
]), ]),
]), div('flex flex-1 px-6 overflow-hidden max-w-full', [ _statContainer ])]; ]), div('flex flex-1 px-6 overflow-hidden max-w-full', [ _statContainer ])];
@@ -641,7 +641,7 @@ export class HomebrewBuilder
() => editing.tree !== undefined ? undefined : div('flex flex-row gap-1 justify-start overflow-x-auto max-w-full', { list: Object.keys(config.trees), render: (e, _c) => _c ?? div('grid grid-cols-2 gap-2 items-baseline w-64 border border-light-35 dark:border-dark-35 p-2', [ span('text-lg font-semibold tracking-thigh', config.trees[e]!.name), div('flex flex-row justify-end', [ tooltip(button(icon('radix-icons:pencil-1', { width: 16, height: 16 }), () => editing.tree = e, 'p-1'), 'Modifier', 'left') ]), span('italic', `${Object.keys(config.trees[e]!.nodes).length} nodes`) ]) }), () => editing.tree !== undefined ? undefined : div('flex flex-row gap-1 justify-start overflow-x-auto max-w-full', { list: Object.keys(config.trees), render: (e, _c) => _c ?? div('grid grid-cols-2 gap-2 items-baseline w-64 border border-light-35 dark:border-dark-35 p-2', [ span('text-lg font-semibold tracking-thigh', config.trees[e]!.name), div('flex flex-row justify-end', [ tooltip(button(icon('radix-icons:pencil-1', { width: 16, height: 16 }), () => editing.tree = e, 'p-1'), 'Modifier', 'left') ]), span('italic', `${Object.keys(config.trees[e]!.nodes).length} nodes`) ]) }),
() => editing.tree === undefined ? undefined : div('flex flex-col', [ () => editing.tree === undefined ? undefined : div('flex flex-col', [
, ,
foldable([ div('flex flex-col gap-2', { list: Object.keys(config.trees[editing.tree]!.nodes), render: (e, _c) => _c ?? dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: () => { foldable([ div('flex flex-col gap-2', { list: Object.keys(config.trees[editing.tree]!.nodes), render: (e, _c) => _c ?? dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 hover:dark:border-dark-50"], listeners: { click: () => {
FeaturePanel.edit(config.features[e]!).then(feature => { FeaturePanel.edit(config.features[e]!).then(feature => {
config.features[e] = feature; config.features[e] = feature;
}).catch(e => {}); }).catch(e => {});
@@ -726,10 +726,10 @@ class FeatureEditor
{ {
const content = div('border border-light-30 dark:border-dark-30 col-span-1', [ div('flex justify-between items-center', [ const content = div('border border-light-30 dark:border-dark-30 col-span-1', [ div('flex justify-between items-center', [
div('px-4 flex items-center h-full', [ markdown(textFromEffect(this.option), undefined, { tags: { a: preview } }) ]), div('px-4 flex items-center h-full', [ markdown(textFromEffect(this.option), undefined, { tags: { a: preview } }) ]),
div('flex', [ tooltip(button(icon('radix-icons:pencil-1'), () => this.edit(), 'p-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), "Modifieur", "bottom"), tooltip(button(icon('radix-icons:trash'), () => { div('flex', [ tooltip(button(icon('radix-icons:pencil-1'), () => this.edit(), 'p-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), "Modifieur", "bottom"), tooltip(button(icon('radix-icons:trash'), () => {
this.delete(); this.delete();
this.container.remove(); this.container.remove();
}, 'p-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), "Supprimer", "bottom") ]) }, 'p-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), "Supprimer", "bottom") ])
]) ]); ]) ]);
this.container.replaceWith(content); this.container.replaceWith(content);
@@ -783,7 +783,7 @@ class FeatureEditor
valueSelection.replaceWith(newValueSelection); valueSelection.replaceWith(newValueSelection);
valueSelection = newValueSelection; valueSelection = newValueSelection;
summaryText.textContent = textFromEffect(buffer); summaryText.textContent = textFromEffect(buffer);
}, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Changer d\'editeur', 'bottom'), }, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), 'Changer d\'editeur', 'bottom'),
], bottom: [ ], bottom: [
div('px-2 py-1 flex items-center flex-1', [summaryText]) div('px-2 py-1 flex items-center flex-1', [summaryText])
] }; ] };
@@ -854,12 +854,12 @@ class FeatureEditor
path.value = ""; path.value = "";
this.parentNode?.insertBefore(path, this); this.parentNode?.insertBefore(path, this);
this.replaceWith(remove); this.replaceWith(remove);
}, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Option fixe', 'right'), remove = tooltip(button(icon('radix-icons:cross-2', { width: 16, height: 16 }), function() { }, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), 'Option fixe', 'right'), remove = tooltip(button(icon('radix-icons:cross-2', { width: 16, height: 16 }), function() {
delete buffer.option; delete buffer.option;
path.value = ""; path.value = "";
path.remove(); path.remove();
this.replaceWith(edit); this.replaceWith(edit);
}, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Option fixe', 'right'); }, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), 'Option fixe', 'right');
return { return {
top: [ combobox(Object.values(config.trees).map(e => ({ text: e.name, value: e.name })), { defaultValue: buffer.tree, change: v => buffer.tree = v, class: { container: 'bg-light-25 dark:bg-dark-25 w-48 -m-px hover:z-10 h-[36px]' }, fill: 'cover' }), ...(buffer.option === undefined ? [edit] : [path, remove]) ], top: [ combobox(Object.values(config.trees).map(e => ({ text: e.name, value: e.name })), { defaultValue: buffer.tree, change: v => buffer.tree = v, class: { container: 'bg-light-25 dark:bg-dark-25 w-48 -m-px hover:z-10 h-[36px]' }, fill: 'cover' }), ...(buffer.option === undefined ? [edit] : [path, remove]) ],
@@ -894,23 +894,23 @@ class FeatureEditor
} }), } }),
..._top, ..._top,
]), ]),
div('flex', [ tooltip(button(icon('radix-icons:trash'), () => { option.effects = option.effects.filter(e => e === effect); element.remove(); }, 'p-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), "Supprimer", "bottom") ]) div('flex', [ tooltip(button(icon('radix-icons:trash'), () => { option.effects = option.effects.filter(e => e === effect); element.remove(); }, 'p-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), "Supprimer", "bottom") ])
]), div('flex border-t border-light-35 dark:border-dark-35 max-h-[300px] min-h-[36px] overflow-y-auto overflow-x-hidden', _bottom) ]); ]), div('flex border-t border-light-35 dark:border-dark-35 max-h-[300px] min-h-[36px] overflow-y-auto overflow-x-hidden', _bottom) ]);
return element; return element;
} }
const renderOption = (option: { text: string; effects: (Partial<FeatureValue | FeatureList | FeatureTree>)[] }, state: boolean) => { const renderOption = (option: { text: string; effects: (Partial<FeatureValue | FeatureList | FeatureTree>)[] }, state: boolean) => {
const effects = div('flex flex-col -m-px flex flex-col ms-px ps-8 w-full', option.effects.map(e => renderEffect(option, e))), effectLength = text(option.effects.length); const effects = div('flex flex-col -m-px flex flex-col ms-px ps-8 w-full', option.effects.map(e => renderEffect(option, e))), effectLength = text(option.effects.length);
let _content = foldable([ effects ], [ div('flex flex-row flex-1 justify-between', [ div('flex flex-row items-center', [ input('text', { defaultValue: option.text, input: (value) => { option.text = value }, placeholder: 'Nom de l\'option', class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] flex-shrink-1' }), span('italic ps-8 pe-2', 'Effets: '), span('font-bold', effectLength) ]), div('flex flex-row flex-shrink-1', [ tooltip(button(icon('radix-icons:plus'), () => effects.appendChild(renderEffect(option, addEffect(option))), 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Nouvel effet', 'bottom'), , tooltip(button(icon('radix-icons:trash'), () => { let _content = foldable([ effects ], [ div('flex flex-row flex-1 justify-between', [ div('flex flex-row items-center', [ input('text', { defaultValue: option.text, input: (value) => { option.text = value }, placeholder: 'Nom de l\'option', class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] shrink-1' }), span('italic ps-8 pe-2', 'Effets: '), span('font-bold', effectLength) ]), div('flex flex-row shrink-1', [ tooltip(button(icon('radix-icons:plus'), () => effects.appendChild(renderEffect(option, addEffect(option))), 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), 'Nouvel effet', 'bottom'), , tooltip(button(icon('radix-icons:trash'), () => {
_content.remove(); _content.remove();
buffer.options?.splice(buffer.options.findIndex(e => e !== option), 1); buffer.options?.splice(buffer.options.findIndex(e => e !== option), 1);
}, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Supprimer', 'bottom') ]) ]) ], { class: { title: 'border-b border-light-35 dark:border-dark-35', icon: 'w-[34px] h-[34px]', content: 'border-b border-light-35 dark:border-dark-35' }, open: state }); }, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), 'Supprimer', 'bottom') ]) ]) ], { class: { title: 'border-b border-light-35 dark:border-dark-35', icon: 'w-[34px] h-[34px]', content: 'border-b border-light-35 dark:border-dark-35' }, open: state });
return _content; return _content;
} }
const list = div('flex flex-col flex-1 divide-y divide-light-35 dark:divide-dark-35 gap-2', buffer.options?.map(e => renderOption(e, false)) ?? []); const list = div('flex flex-col flex-1 divide-y divide-light-35 dark:divide-dark-35 gap-2', buffer.options?.map(e => renderOption(e, false)) ?? []);
return { return {
top: [ input('text', { defaultValue: buffer.text, input: (value) => { (buffer as FeatureChoice).text = value }, class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-full', placeholder: 'Description' }), tooltip(button(icon('radix-icons:plus'), () => list.appendChild(renderOption(addChoice(), true)), 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Nouvelle option', 'bottom') ], top: [ input('text', { defaultValue: buffer.text, input: (value) => { (buffer as FeatureChoice).text = value }, class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-full', placeholder: 'Description' }), tooltip(button(icon('radix-icons:plus'), () => list.appendChild(renderOption(addChoice(), true)), 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), 'Nouvelle option', 'bottom') ],
bottom: [ list ], bottom: [ list ],
} }
} }
@@ -929,7 +929,7 @@ class FeatureEditor
} }), } }),
...top, ...top,
]), ]),
div('flex', [ tooltip(button(icon('radix-icons:check'), () => { this.update(); this.read(); this.show(); this._draft = false; }, 'p-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), "Valider", "bottom"), tooltip(button(icon('radix-icons:cross-1'), () => { if(this._draft) { this.delete(); this.container.remove(); } else { this.read(); this.show(); } }, 'p-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), "Annuler", "bottom") ]) div('flex', [ tooltip(button(icon('radix-icons:check'), () => { this.update(); this.read(); this.show(); this._draft = false; }, 'p-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), "Valider", "bottom"), tooltip(button(icon('radix-icons:cross-1'), () => { if(this._draft) { this.delete(); this.container.remove(); } else { this.read(); this.show(); } }, 'p-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 hover:dark:border-dark-50'), "Annuler", "bottom") ])
]), bottom.length > 0 ? div('flex border-t border-light-35 dark:border-dark-35 max-h-[300px] min-h-[36px] overflow-y-auto overflow-x-hidden', bottom) : undefined ]); ]), bottom.length > 0 ? div('flex border-t border-light-35 dark:border-dark-35 max-h-[300px] min-h-[36px] overflow-y-auto overflow-x-hidden', bottom) : undefined ]);
} }
@@ -955,7 +955,7 @@ export class FeaturePanel
}, 'p-1'), 'Valider', 'left'), }, 'p-1'), 'Valider', 'left'),
dom('label', { class: 'flex justify-center items-center my-2' }, [ dom('label', { class: 'flex justify-center items-center my-2' }, [
dom('span', { class: 'pb-1 md:p-0', text: "ID" }), dom('span', { class: 'pb-1 md:p-0', text: "ID" }),
input("text", { defaultValue: _feature.id, disabled: true, class: `mx-4 text-light-70 dark:text-dark-70 appearance-none outline-none px-3 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-25 dark:bg-dark-25 border-light-30 dark:border-dark-30` }) input("text", { defaultValue: _feature.id, disabled: true, class: `mx-4 text-light-70 dark:text-dark-70 appearance-none outline-hidden px-3 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-25 dark:bg-dark-25 border-light-30 dark:border-dark-30` })
]), ]),
tooltip(button(icon('radix-icons:cross-1', { width: 20, height: 20 }), () => { tooltip(button(icon('radix-icons:cross-1', { width: 20, height: 20 }), () => {
failure!(feature); failure!(feature);

View File

@@ -104,12 +104,12 @@ export const callout: Prose = {
} = properties; } = properties;
let open = fold; let open = fold;
const container = dom('div', { class: ['callout group overflow-hidden my-4 p-3 ps-4 bg-blend-lighten !bg-opacity-25 border-l-4 inline-block pe-8 bg-light-blue dark:bg-dark-blue', properties?.class], attributes: { 'data-state': fold !== false ? 'closed' : 'open', 'data-type': type } }, [ const container = dom('div', { class: ['callout group overflow-hidden my-4 p-3 ps-4 bg-blend-lighten border-l-4 inline-block pe-8 bg-light-blue/25 dark:bg-dark-blue/25', properties?.class], attributes: { 'data-state': fold !== false ? 'closed' : 'open', 'data-type': type } }, [
dom('div', { class: [{'cursor-pointer': fold !== undefined}, 'flex flex-row items-center justify-start ps-2'], listeners: { click: e => { dom('div', { class: [{'cursor-pointer': fold !== undefined}, 'flex flex-row items-center justify-start ps-2'], listeners: { click: e => {
container.setAttribute('data-state', open ? 'open' : 'closed'); container.setAttribute('data-state', open ? 'open' : 'closed');
open = !open; open = !open;
}}}, }}},
[icon(calloutIconByType[type] ?? defaultCalloutIcon, { inline: true, width: 24, height: 24, class: 'w-6 h-6 stroke-2 float-start me-2 flex-shrink-0' }), !!title ? dom('span', { class: 'block font-bold text-start', text: title }) : undefined, fold !== undefined ? icon('radix-icons:caret-right', { height: 24, width: 24, class: 'transition-transform group-data-[state=open]:rotate-90 w-6 h-6 mx-6' }) : undefined [icon(calloutIconByType[type] ?? defaultCalloutIcon, { inline: true, width: 24, height: 24, class: 'w-6 h-6 stroke-2 float-start me-2 shrink-0' }), !!title ? dom('span', { class: 'block font-bold text-start', text: title }) : undefined, fold !== undefined ? icon('radix-icons:caret-right', { height: 24, width: 24, class: 'transition-transform group-data-[state=open]:rotate-90 w-6 h-6 mx-6' }) : undefined
]), ]),
dom('div', { class: {'overflow-hidden': true, 'group-data-[state=closed]:animate-[collapseClose_0.2s_ease-in-out] group-data-[state=open]:animate-[collapseOpen_0.2s_ease-in-out] group-data-[state=closed]:h-0': fold !== undefined } }, [ dom('div', { class: {'overflow-hidden': true, 'group-data-[state=closed]:animate-[collapseClose_0.2s_ease-in-out] group-data-[state=open]:animate-[collapseOpen_0.2s_ease-in-out] group-data-[state=closed]:h-0': fold !== undefined } }, [
dom('div', { class: 'px-2' }, children), dom('div', { class: 'px-2' }, children),
@@ -120,7 +120,7 @@ export const callout: Prose = {
}, },
} }
export const tag: Prose = { export const tag: Prose = {
class: "before:content-['#'] cursor-default bg-accent-blue bg-opacity-10 hover:bg-opacity-20 text-accent-blue text-sm px-1 ms-1 pb-0.5 rounded-full rounded-se-none border border-accent-blue border-opacity-30", class: "before:content-['#'] cursor-default bg-accent-blue/10 hover:bg-accent-blue/20 text-accent-blue text-sm px-1 ms-1 pb-0.5 rounded-full rounded-se-none border border-accent-blue/30",
} }
export const blockquote: Prose = { export const blockquote: Prose = {
class: 'empty:before:hidden ps-4 my-4 relative before:absolute before:-top-1 before:-bottom-1 before:left-0 before:w-1 before:bg-light-30 dark:before:bg-dark-30', class: 'empty:before:hidden ps-4 my-4 relative before:absolute before:-top-1 before:-bottom-1 before:left-0 before:w-1 before:bg-light-30 dark:before:bg-dark-30',
@@ -144,7 +144,7 @@ export const hr: Prose = {
class: 'border-b border-light-35 dark:border-dark-35 m-4', class: 'border-b border-light-35 dark:border-dark-35 m-4',
} }
export const li: Prose = { export const li: Prose = {
class: 'before:absolute before:top-2 before:left-0 before:inline-block before:w-2 before:h-2 before:rounded before:bg-light-40 dark:before:bg-dark-40 relative ps-4', class: 'before:absolute before:top-2 before:left-0 before:inline-block before:w-2 before:h-2 before:rounded-sm before:bg-light-40 dark:before:bg-dark-40 relative ps-4',
} }
export const small: Prose = { export const small: Prose = {
class: 'text-light-60 dark:text-dark-60 text-sm italic', class: 'text-light-60 dark:text-dark-60 text-sm italic',