Spell UI, variables saving and mail server fixes (finally working in prod !!!)
This commit is contained in:
parent
1642cd513f
commit
61d2d144b7
BIN
db.sqlite-shm
BIN
db.sqlite-shm
Binary file not shown.
BIN
db.sqlite-wal
BIN
db.sqlite-wal
Binary file not shown.
|
|
@ -55,7 +55,7 @@ export const characterTable = table("character", {
|
|||
owner: int().notNull().references(() => usersTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
|
||||
people: text().notNull(),
|
||||
level: int().notNull().default(1),
|
||||
variables: text({ mode: 'json' }).notNull().default('{"health": 0,"mana": 0,"spells": [],"equipment": [],"exhaustion": 0,"sickness": []}'),
|
||||
variables: text({ mode: 'json' }).notNull().default('{"health": 0,"mana": 0,"spells": [],"equipment": [],"exhaustion": 0,"sickness": [],"poisons": []}'),
|
||||
aspect: int(),
|
||||
notes: text(),
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ declare module 'nitropack'
|
|||
{
|
||||
interface TaskPayload
|
||||
{
|
||||
type: string
|
||||
type: string;
|
||||
}
|
||||
interface TaskResult<RT = unknown>
|
||||
{
|
||||
error?: Error | string;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +21,7 @@ export default defineEventHandler(async (e) => {
|
|||
return;
|
||||
}
|
||||
const id = getRouterParam(e, 'id');
|
||||
const payload: Record<string, any> = await readBody(e);
|
||||
const body: Record<string, any> = await readBody(e);
|
||||
|
||||
if(!id)
|
||||
{
|
||||
|
|
@ -25,8 +29,11 @@ export default defineEventHandler(async (e) => {
|
|||
return;
|
||||
}
|
||||
|
||||
payload.type = id;
|
||||
payload.data = JSON.parse(payload.data);
|
||||
body.data = JSON.parse(body.data);
|
||||
const payload = {
|
||||
type: id,
|
||||
data: body,
|
||||
}
|
||||
|
||||
const result = await runTask(id, {
|
||||
payload: payload
|
||||
|
|
@ -36,7 +43,7 @@ export default defineEventHandler(async (e) => {
|
|||
{
|
||||
setResponseStatus(e, 500);
|
||||
|
||||
if(result.error && (result.error as Error).message)
|
||||
if(result.error && result.error.message)
|
||||
throw result.error;
|
||||
else if(result.error)
|
||||
throw new Error(result.error);
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
import { and, eq, sql } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterTable } from '~/db/schema';
|
||||
import type { CharacterVariables } from '~/types/character';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const id = getRouterParam(e, "id");
|
||||
|
||||
if(!id)
|
||||
{
|
||||
setResponseStatus(e, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const session = await getUserSession(e);
|
||||
if(!session.user)
|
||||
{
|
||||
setResponseStatus(e, 401);
|
||||
return;
|
||||
}
|
||||
|
||||
const db = useDatabase();
|
||||
const character = db.select({
|
||||
health: characterTable.health,
|
||||
mana: characterTable.mana,
|
||||
}).from(characterTable).where(and(eq(characterTable.id, parseInt(id, 10)), eq(characterTable.owner, session.user.id))).get();
|
||||
|
||||
if(character !== undefined)
|
||||
{
|
||||
return character as CharacterVariables;
|
||||
}
|
||||
|
||||
setResponseStatus(e, 404);
|
||||
return;
|
||||
});
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import { eq } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterTable } from '~/db/schema';
|
||||
import type { CharacterValues } from '~/types/character';
|
||||
import { CharacterVariablesValidation } from '~/shared/character.util';
|
||||
import type { CharacterVariables } from '~/types/character';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const id = getRouterParam(e, "id");
|
||||
|
|
@ -11,11 +12,12 @@ export default defineEventHandler(async (e) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const body = await readBody(e) as CharacterValues;
|
||||
if(!body)
|
||||
const body = await readValidatedBody(e, CharacterVariablesValidation.safeParse);
|
||||
if(!body.success)
|
||||
{
|
||||
console.error(body.error);
|
||||
setResponseStatus(e, 400);
|
||||
return;
|
||||
throw body.error;
|
||||
}
|
||||
|
||||
const db = useDatabase();
|
||||
|
|
@ -35,8 +37,7 @@ export default defineEventHandler(async (e) => {
|
|||
}
|
||||
|
||||
db.update(characterTable).set({
|
||||
health: body.health,
|
||||
mana: body.mana,
|
||||
variables: body.data
|
||||
}).where(eq(characterTable.id, parseInt(id, 10))).run();
|
||||
|
||||
setResponseStatus(e, 200);
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div style='margin-left: auto; margin-right: auto; width: 75%; font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 1rem; line-height: 1.5rem; color: #171717;'>
|
||||
<div style="margin-left: auto; margin-right: auto; text-align: center;">
|
||||
<a href="https://d-any.com">
|
||||
<a style="display: inline-block;" href="https://d-any.com">
|
||||
<img src="https://d-any.com/logo.light.png" alt="Logo" title="d[any] logo" width="64" height="64" style="display: block; height: 4rem; width: 4rem; margin-left: auto; margin-right: auto;" />
|
||||
<span style="margin-inline-end: 1rem; font-size: 1.5rem; line-height: 2rem; font-weight: 700; font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;">d[any]</span>
|
||||
<span style="margin-inline-end: 1rem; font-size: 1.5rem; color: black; text-decoration: none; line-height: 2rem; font-weight: 700; font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;">d[any]</span>
|
||||
</a>
|
||||
</div>
|
||||
<div style="padding: 1rem;">
|
||||
|
|
@ -11,6 +11,6 @@
|
|||
</div>
|
||||
</div>
|
||||
<div style="background-color: #171717;">
|
||||
<p style="padding-top: 1rem; padding-bottom: 1rem; text-align: center; font-size: 0.75rem; line-height: 1rem; color: #fff;">Copyright Peaceultime - d[any] - 2024</p>
|
||||
<p style="padding-top: 1rem; padding-bottom: 1rem; text-align: center; font-size: 0.75rem; line-height: 1rem; color: #fff;">Copyright Peaceultime / d[any] - 2024 / 2025</p>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -17,10 +17,9 @@ export const templates: Record<string, { component: any, subject: string }> = {
|
|||
import type Mail from 'nodemailer/lib/mailer';
|
||||
interface MailPayload
|
||||
{
|
||||
type: 'mail'
|
||||
to: string[]
|
||||
template: string
|
||||
data: Record<string, any>
|
||||
to: string[];
|
||||
template: string;
|
||||
data: Record<string, any>;
|
||||
}
|
||||
|
||||
const transport = nodemailer.createTransport({
|
||||
|
|
@ -55,26 +54,33 @@ if(process.env.NODE_ENV === 'production')
|
|||
});
|
||||
}
|
||||
|
||||
export default async function(e: TaskEvent) {
|
||||
export default defineTask({
|
||||
meta: {
|
||||
name: 'mail',
|
||||
description: ''
|
||||
},
|
||||
run: async ({ payload, context }) => {
|
||||
try {
|
||||
if(e.payload.type !== 'mail')
|
||||
if(payload.type !== 'mail')
|
||||
{
|
||||
throw new Error(`Données inconnues`);
|
||||
}
|
||||
|
||||
const payload = e.payload as MailPayload;
|
||||
const template = templates[payload.template];
|
||||
const mailPayload = payload.data as MailPayload;
|
||||
const template = templates[mailPayload.template];
|
||||
|
||||
console.log(mailPayload);
|
||||
|
||||
if(!template)
|
||||
{
|
||||
throw new Error(`Modèle de mail ${payload.template} inconnu`);
|
||||
throw new Error(`Modèle de mail ${mailPayload.template} inconnu`);
|
||||
}
|
||||
|
||||
console.time('Generating HTML');
|
||||
const mail: Mail.Options = {
|
||||
from: 'd[any] - Ne pas répondre <no-reply@peaceultime.com>',
|
||||
to: payload.to,
|
||||
html: await render(template.component, payload.data),
|
||||
to: mailPayload.to,
|
||||
html: await render(template.component, mailPayload.data),
|
||||
subject: template.subject,
|
||||
textEncoding: 'quoted-printable',
|
||||
};
|
||||
|
|
@ -100,6 +106,7 @@ export default async function(e: TaskEvent) {
|
|||
return { result: false, error: e };
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function render(component: any, data: Record<string, any>): Promise<string>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -558,12 +558,18 @@
|
|||
}
|
||||
},
|
||||
"lists": {
|
||||
"sickness": [
|
||||
{
|
||||
"id": "",
|
||||
"sickness": {
|
||||
"id": "sickness",
|
||||
"config": {
|
||||
|
||||
},
|
||||
"values": {
|
||||
"rotted": {
|
||||
"id": "rotted",
|
||||
"name": "Pourriture mortelle"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"peoples": {
|
||||
"e662m19q590kn4dowvssowi1qf8ia7sk": {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { Ability, Alignment, Character, CharacterConfig, CharacterVariables, CompiledCharacter, FeatureItem, Level, MainStat, Resistance, SpellElement, SpellType, TrainingLevel } from "~/types/character";
|
||||
import type { Ability, Alignment, Character, CharacterConfig, CharacterVariables, CompiledCharacter, FeatureItem, Level, MainStat, Resistance, SpellConfig, SpellElement, SpellType, TrainingLevel } from "~/types/character";
|
||||
import { z } from "zod/v4";
|
||||
import characterConfig from '#shared/character-config.json';
|
||||
import proses, { preview } from "#shared/proses";
|
||||
import { button, buttongroup, foldable, input, loading, numberpicker, select, tabgroup, Toaster, toggle } from "#shared/components.util";
|
||||
import { div, dom, icon, text } from "#shared/dom.util";
|
||||
import { div, dom, icon, span, text } from "#shared/dom.util";
|
||||
import { followermenu, fullblocker, tooltip } from "#shared/floating.util";
|
||||
import { clamp } from "#shared/general.util";
|
||||
import markdown from "#shared/markdown.util";
|
||||
|
|
@ -40,6 +40,7 @@ export const defaultCharacter: Character = {
|
|||
items: [],
|
||||
exhaustion: 0,
|
||||
sickness: [],
|
||||
poisons: [],
|
||||
},
|
||||
|
||||
owner: -1,
|
||||
|
|
@ -113,7 +114,10 @@ const defaultCompiledCharacter: (character: Character) => CompiledCharacter = (c
|
|||
magicelement: 0,
|
||||
magicinstinct: 0,
|
||||
},
|
||||
bonus: {},
|
||||
bonus: {
|
||||
abilities: {},
|
||||
defense: {},
|
||||
},
|
||||
resistance: {},
|
||||
initiative: 0,
|
||||
capacity: 0,
|
||||
|
|
@ -125,7 +129,7 @@ const defaultCompiledCharacter: (character: Character) => CompiledCharacter = (c
|
|||
spells: [],
|
||||
},
|
||||
aspect: "",
|
||||
notes: character.notes ?? "",
|
||||
notes: Object.assign({ public: '', private: '' }, character.notes),
|
||||
});
|
||||
|
||||
export const mainStatTexts: Record<MainStat, string> = {
|
||||
|
|
@ -158,6 +162,10 @@ export const elementTexts: Record<SpellElement, { class: string, text: string }>
|
|||
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' },
|
||||
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' },
|
||||
};
|
||||
export const elementDom = (element: SpellElement) => dom("span", {
|
||||
class: [`border !border-opacity-50 rounded-full !bg-opacity-20 px-2 py-px`, elementTexts[element].class],
|
||||
text: elementTexts[element].text
|
||||
});
|
||||
|
||||
export const alignmentTexts: Record<Alignment, string> = {
|
||||
'loyal_good': 'Loyal bon',
|
||||
|
|
@ -205,6 +213,22 @@ export const resistanceTexts: Record<Resistance, string> = {
|
|||
'instinct': 'Sorts d\'instinct',
|
||||
};
|
||||
|
||||
export const CharacterVariablesValidation = z.object({
|
||||
health: z.number(),
|
||||
mana: z.number(),
|
||||
exhaustion: z.number(),
|
||||
|
||||
sickness: z.array(z.object({
|
||||
id: z.string(),
|
||||
state: z.number().min(1).max(7).or(z.literal(true)),
|
||||
})),
|
||||
poisons: z.array(z.object({
|
||||
id: z.string(),
|
||||
state: z.number().min(1).max(7).or(z.literal(true)),
|
||||
})),
|
||||
spells: z.array(z.string()),
|
||||
equipment: z.array(z.string()),
|
||||
});
|
||||
export const CharacterValidation = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
|
|
@ -216,18 +240,7 @@ export const CharacterValidation = z.object({
|
|||
leveling: z.record(z.enum(LEVELS.map(String)), z.number().optional()),
|
||||
abilities: z.record(z.enum(ABILITIES), z.number().optional()),
|
||||
choices: z.record(z.string(), z.array(z.number())),
|
||||
variables: z.object({
|
||||
health: z.number(),
|
||||
mana: z.number(),
|
||||
exhaustion: z.number(),
|
||||
|
||||
sickness: z.array(z.object({
|
||||
id: z.string(),
|
||||
state: z.number().min(1).max(7).or(z.literal(true)),
|
||||
})),
|
||||
spells: z.array(z.string()),
|
||||
equipment: z.array(z.string()),
|
||||
}),
|
||||
variables: CharacterVariablesValidation,
|
||||
owner: z.number(),
|
||||
username: z.string().optional(),
|
||||
visibility: z.enum(["public", "private"]),
|
||||
|
|
@ -241,6 +254,7 @@ export class CharacterCompiler
|
|||
protected _character!: Character;
|
||||
protected _result!: CompiledCharacter;
|
||||
protected _buffer: Record<string, PropertySum> = {};
|
||||
private _variableDirty: boolean = false;
|
||||
|
||||
constructor(character: Character)
|
||||
{
|
||||
|
|
@ -299,6 +313,20 @@ export class CharacterCompiler
|
|||
{
|
||||
this._character.variables[prop] = value;
|
||||
this._result.variables[prop] = value;
|
||||
this._variableDirty = true;
|
||||
}
|
||||
saveVariables()
|
||||
{
|
||||
if(this._variableDirty)
|
||||
{
|
||||
this._variableDirty = false;
|
||||
useRequestFetch()(`/api/character/${this.character.id}/variables`, {
|
||||
method: 'POST',
|
||||
body: this._character.variables,
|
||||
}).then(() => {}).catch(() => {
|
||||
Toaster.add({ type: 'error', content: 'Impossible de mettre à jour les données', duration: 5000, timer: true });
|
||||
})
|
||||
}
|
||||
}
|
||||
protected add(feature?: string)
|
||||
{
|
||||
|
|
@ -1142,10 +1170,13 @@ class AspectPicker extends BuilderTab
|
|||
|
||||
export class CharacterSheet
|
||||
{
|
||||
user: ComputedRef<User | null>;
|
||||
character?: CharacterCompiler;
|
||||
container: HTMLElement = div();
|
||||
tabs?: HTMLDivElement & { refresh: () => void };
|
||||
constructor(id: string, user: ComputedRef<User | null>)
|
||||
{
|
||||
this.user = user;
|
||||
const load = div("flex justify-center items-center w-full h-full", [ loading('large') ]);
|
||||
this.container.replaceChildren(load);
|
||||
useRequestFetch()(`/api/character/${id}`).then(character => {
|
||||
|
|
@ -1159,9 +1190,16 @@ export class CharacterSheet
|
|||
this.render();
|
||||
}
|
||||
else
|
||||
{
|
||||
//ERROR
|
||||
}
|
||||
throw new Error();
|
||||
}).catch(() => {
|
||||
this.container.replaceChildren(div('flex flex-col items-center justify-center flex-1 h-full gap-4', [
|
||||
span('text-2xl font-bold tracking-wider', 'Personnage introuvable'),
|
||||
span(undefined, 'Ce personnage n\'existe pas ou est privé.'),
|
||||
div('flex flex-row gap-4 justify-center items-center', [
|
||||
button(text('Personnages publics'), () => useRouter().push({ name: 'character-list' }), 'px-2 py-1'),
|
||||
button(text('Créer un personange'), () => useRouter().push({ name: 'character-id-edit', params: { id: 'new' } }), 'px-2 py-1')
|
||||
])
|
||||
]))
|
||||
});
|
||||
}
|
||||
render()
|
||||
|
|
@ -1170,7 +1208,22 @@ export class CharacterSheet
|
|||
return;
|
||||
|
||||
const character = this.character.compiled;
|
||||
console.log(character);
|
||||
|
||||
this.tabs = tabgroup([
|
||||
{ id: 'actions', title: [ text('Actions') ], content: () => this.actionsTab(character) },
|
||||
|
||||
{ id: 'abilities', title: [ text('Aptitudes') ], content: () => this.abilitiesTab(character) },
|
||||
|
||||
{ id: 'spells', title: [ text('Sorts') ], content: () => this.spellTab(character) },
|
||||
|
||||
{ id: 'inventory', title: [ text('Inventaire') ], content: () => [
|
||||
|
||||
] },
|
||||
|
||||
{ id: 'notes', title: [ text('Notes') ], content: () => [
|
||||
|
||||
] },
|
||||
], { focused: 'abilities', class: { container: 'flex-1 gap-4 px-4 w-[960px]' } });
|
||||
this.container.replaceChildren(div('flex flex-col justify-center gap-1', [
|
||||
div("flex flex-row gap-4 justify-between", [
|
||||
div(),
|
||||
|
|
@ -1215,10 +1268,9 @@ export class CharacterSheet
|
|||
]),
|
||||
|
||||
div("self-center", [
|
||||
/* user && user.id === character.owner ?
|
||||
button(icon("radix-icons:pencil-2"), () => {
|
||||
}, "icon")
|
||||
: div() */
|
||||
this.user.value && this.user.value.id === character.owner ?
|
||||
button(icon("radix-icons:pencil-2"), () => useRouter().push({ name: 'character-id-edit', params: { id: this.character?.character.id } }), "p-1")
|
||||
: div()
|
||||
])
|
||||
]),
|
||||
|
||||
|
|
@ -1299,7 +1351,7 @@ export class CharacterSheet
|
|||
div("flex flex-col gap-4 py-1 w-80", [
|
||||
div("flex flex-col py-1 gap-4", [
|
||||
div("flex flex-row items-center justify-center gap-4", [
|
||||
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-xl font-semibold', text: "Compétences" }), proses('a', preview, [ icon('radix-icons:question-mark-circled', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }) ], { href: 'regles/l\'entrainement/competences', class: 'h-4' }) ]),
|
||||
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-xl font-semibold', text: "Compétences" }), proses('a', preview, [ icon('radix-icons:question-mark-circled', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }) ], { href: 'regles/l\'entrainement/competences', size: 'small', class: 'h-4' }) ]),
|
||||
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50")
|
||||
]),
|
||||
|
||||
|
|
@ -1314,44 +1366,49 @@ export class CharacterSheet
|
|||
|
||||
|
||||
div("flex flex-row items-center justify-center gap-4", [
|
||||
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-xl font-semibold', text: "Maitrises" }), proses('a', preview, [ icon('radix-icons:question-mark-circled', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }) ], { href: 'regles/l\'entrainement/competences', class: 'h-4' }) ]),
|
||||
div("flex flex-row items-center justify-center gap-2", [ dom("div", { class: 'text-xl font-semibold', text: "Maitrises" }), proses('a', preview, [ icon('radix-icons:question-mark-circled', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }) ], { href: 'regles/l\'entrainement/competences', size: 'small', class: 'h-4' }) ]),
|
||||
div("flex flex-1 border-t border-dashed border-light-50 dark:border-dark-50")
|
||||
]),
|
||||
|
||||
character.mastery.strength + character.mastery.dexterity > 0 ? div("grid grid-cols-2 gap-x-3 gap-y-1 text-sm", [
|
||||
character.mastery.strength + character.mastery.dexterity > 0 ? proses('a', preview, [ text('Arme légère') ], { href: 'regles/annexes/equipement#Les armes légères' }) : undefined,
|
||||
character.mastery.strength + character.mastery.dexterity > 0 ? proses('a', preview, [ text('Arme de jet') ], { href: 'regles/annexes/equipement#Les armes de jet' }) : undefined,
|
||||
character.mastery.strength + character.mastery.dexterity > 0 ? proses('a', preview, [ text('Arme naturelle') ], { href: 'regles/annexes/equipement#Les armes naturelles' }) : undefined,
|
||||
character.mastery.strength > 1 ? proses('a', preview, [ text('Arme standard') ], { href: 'regles/annexes/equipement#Les armes' }) : undefined,
|
||||
character.mastery.strength > 1 ? proses('a', preview, [ text('Arme improvisée') ], { href: 'regles/annexes/equipement#Les armes improvisées' }) : undefined,
|
||||
character.mastery.strength > 2 ? proses('a', preview, [ text('Arme lourde') ], { href: 'regles/annexes/equipement#Les armes lourdes' }) : undefined,
|
||||
character.mastery.strength > 3 ? proses('a', preview, [ text('Arme à deux mains') ], { href: 'regles/annexes/equipement#Les armes à deux mains' }) : undefined,
|
||||
character.mastery.dexterity > 0 && character.mastery.strength > 1 ? proses('a', preview, [ text('Arme maniable') ], { href: 'regles/annexes/equipement#Les armes maniables' }) : undefined,
|
||||
character.mastery.dexterity > 1 && character.mastery.strength > 1 ? proses('a', preview, [ text('Arme à projectiles') ], { href: 'regles/annexes/equipement#Les armes à projectiles' }) : undefined,
|
||||
character.mastery.dexterity > 1 && character.mastery.strength > 2 ? proses('a', preview, [ text('Arme longue') ], { href: 'regles/annexes/equipement#Les armes longues' }) : undefined,
|
||||
character.mastery.shield > 0 ? proses('a', preview, [ text('Bouclier') ], { href: 'regles/annexes/equipement#Les boucliers' }) : undefined,
|
||||
character.mastery.shield > 0 && character.mastery.strength > 3 ? proses('a', preview, [ text('Bouclier à deux mains') ], { href: 'regles/annexes/equipement#Les boucliers à deux mains' }) : undefined,
|
||||
character.mastery.strength + character.mastery.dexterity > 0 ? proses('a', preview, [ text('Arme légère') ], { href: 'regles/annexes/equipement#Les armes légères', size: 'small' }) : undefined,
|
||||
character.mastery.strength + character.mastery.dexterity > 0 ? proses('a', preview, [ text('Arme de jet') ], { href: 'regles/annexes/equipement#Les armes de jet', size: 'small' }) : undefined,
|
||||
character.mastery.strength + character.mastery.dexterity > 0 ? proses('a', preview, [ text('Arme naturelle') ], { href: 'regles/annexes/equipement#Les armes naturelles', size: 'small' }) : undefined,
|
||||
character.mastery.strength > 1 ? proses('a', preview, [ text('Arme standard') ], { href: 'regles/annexes/equipement#Les armes', size: 'small' }) : undefined,
|
||||
character.mastery.strength > 1 ? proses('a', preview, [ text('Arme improvisée') ], { href: 'regles/annexes/equipement#Les armes improvisées', size: 'small' }) : undefined,
|
||||
character.mastery.strength > 2 ? proses('a', preview, [ text('Arme lourde') ], { href: 'regles/annexes/equipement#Les armes lourdes', size: 'small' }) : undefined,
|
||||
character.mastery.strength > 3 ? proses('a', preview, [ text('Arme à deux mains') ], { href: 'regles/annexes/equipement#Les armes à deux mains', size: 'small' }) : undefined,
|
||||
character.mastery.dexterity > 0 && character.mastery.strength > 1 ? proses('a', preview, [ text('Arme maniable') ], { href: 'regles/annexes/equipement#Les armes maniables', size: 'small' }) : undefined,
|
||||
character.mastery.dexterity > 1 && character.mastery.strength > 1 ? proses('a', preview, [ text('Arme à projectiles') ], { href: 'regles/annexes/equipement#Les armes à projectiles', size: 'small' }) : undefined,
|
||||
character.mastery.dexterity > 1 && character.mastery.strength > 2 ? proses('a', preview, [ text('Arme longue') ], { href: 'regles/annexes/equipement#Les armes longues', size: 'small' }) : undefined,
|
||||
character.mastery.shield > 0 ? proses('a', preview, [ text('Bouclier') ], { href: 'regles/annexes/equipement#Les boucliers', size: 'small' }) : undefined,
|
||||
character.mastery.shield > 0 && character.mastery.strength > 3 ? proses('a', preview, [ text('Bouclier à deux mains') ], { href: 'regles/annexes/equipement#Les boucliers à deux mains', size: 'small' }) : undefined,
|
||||
]) : undefined,
|
||||
|
||||
character.mastery.armor > 0 ? div("grid grid-cols-2 gap-x-3 gap-y-1 text-sm", [
|
||||
character.mastery.armor > 0 ? proses('a', preview, [ text('Armure légère') ], { href: 'regles/annexes/equipement#Les armures légères' }) : undefined,
|
||||
character.mastery.armor > 1 ? proses('a', preview, [ text('Armure standard') ], { href: 'regles/annexes/equipement#Les armures' }) : undefined,
|
||||
character.mastery.armor > 2 ? proses('a', preview, [ text('Armure lourde') ], { href: 'regles/annexes/equipement#Les armures lourdes' }) : undefined,
|
||||
character.mastery.armor > 0 ? proses('a', preview, [ text('Armure légère') ], { href: 'regles/annexes/equipement#Les armures légères', size: 'small' }) : undefined,
|
||||
character.mastery.armor > 1 ? proses('a', preview, [ text('Armure standard') ], { href: 'regles/annexes/equipement#Les armures', size: 'small' }) : undefined,
|
||||
character.mastery.armor > 2 ? proses('a', preview, [ text('Armure lourde') ], { href: 'regles/annexes/equipement#Les armures lourdes', size: 'small' }) : undefined,
|
||||
]) : undefined,
|
||||
|
||||
div("grid grid-cols-2 gap-x-3 gap-y-1 text-sm", [
|
||||
div('flex flex-row items-center gap-2', [ text('Précision'), dom('span', { text: character.spellranks.precision.toString(), class: 'font-bold' }) ]),
|
||||
div('flex flex-row items-center gap-2', [ text('Savoir'), dom('span', { text: character.spellranks.knowledge.toString(), class: 'font-bold' }) ]),
|
||||
div('flex flex-row items-center gap-2', [ text('Instinct'), dom('span', { text: character.spellranks.instinct.toString(), class: 'font-bold' }) ]),
|
||||
div('flex flex-row items-center gap-2', [ text('Oeuvres'), dom('span', { text: character.spellranks.arts.toString(), class: 'font-bold' }) ]),
|
||||
div('flex flex-row items-center gap-2', [ text('Précision'), span('font-bold', character.spellranks.precision.toString()) ]),
|
||||
div('flex flex-row items-center gap-2', [ text('Savoir'), span('font-bold', character.spellranks.knowledge.toString()) ]),
|
||||
div('flex flex-row items-center gap-2', [ text('Instinct'), span('font-bold', character.spellranks.instinct.toString()) ]),
|
||||
div('flex flex-row items-center gap-2', [ text('Oeuvres'), span('font-bold', character.spellranks.arts.toString()) ]),
|
||||
])
|
||||
])
|
||||
]),
|
||||
|
||||
div('border-l border-light-35 dark:border-dark-35'),
|
||||
|
||||
tabgroup([
|
||||
{ id: 'actions', title: [ text('Actions') ], content: () => [
|
||||
this.tabs,
|
||||
])
|
||||
]));
|
||||
}
|
||||
actionsTab(character: CompiledCharacter)
|
||||
{
|
||||
return [
|
||||
div('flex flex-col gap-8', [
|
||||
div('flex flex-col gap-2', [
|
||||
div("flex flex-row items-center justify-center gap-4", [
|
||||
|
|
@ -1398,22 +1455,7 @@ export class CharacterSheet
|
|||
]),
|
||||
]),
|
||||
]),
|
||||
] },
|
||||
|
||||
{ id: 'abilities', title: [ text('Aptitudes') ], content: () => this.abilitiesTab(character) },
|
||||
|
||||
{ id: 'spells', title: [ text('Sorts') ], content: () => this.spellTab(character) },
|
||||
|
||||
{ id: 'inventory', title: [ text('Inventaire') ], content: () => [
|
||||
|
||||
] },
|
||||
|
||||
{ id: 'notes', title: [ text('Notes') ], content: () => [
|
||||
|
||||
] },
|
||||
], { focused: 'abilities', class: { container: 'flex-1 gap-4 px-4 w-[960px]' } }),
|
||||
])
|
||||
]));
|
||||
]
|
||||
}
|
||||
abilitiesTab(character: CompiledCharacter)
|
||||
{
|
||||
|
|
@ -1428,23 +1470,45 @@ export class CharacterSheet
|
|||
}
|
||||
spellTab(character: CompiledCharacter)
|
||||
{
|
||||
let sortPreference = (localStorage.getItem('character-sort') ?? 'rank') as 'rank' | 'type' | 'element';
|
||||
|
||||
const sort = (spells: Array<{ id: string, spell?: SpellConfig, source: string }>) => {
|
||||
spells = spells.filter(e => !!e.spell);
|
||||
switch(sortPreference)
|
||||
{
|
||||
case 'rank': return spells.sort((a, b) => a.spell!.rank - b.spell!.rank || SPELL_ELEMENTS.indexOf(a.spell!.elements[0]!) - SPELL_ELEMENTS.indexOf(b.spell!.elements[0]!));
|
||||
case 'type': return spells.sort((a, b) => a.spell!.type.localeCompare(b.spell!.type) || a.spell!.rank - b.spell!.rank);
|
||||
case 'element': return spells.sort((a, b) => SPELL_ELEMENTS.indexOf(a.spell!.elements[0]!) - SPELL_ELEMENTS.indexOf(b.spell!.elements[0]!) || a.spell!.rank - b.spell!.rank);
|
||||
default: return spells;
|
||||
}
|
||||
};
|
||||
const spells = sort([...(character.lists.spells ?? []).map(e => ({ id: e, spell: config.spells.find(_e => _e.id === e), source: 'feature' })), ...character.variables.spells.map(e => ({ id: e, spell: config.spells.find(_e => _e.id === e), source: 'player' }))]).map(e => ({...e, dom:
|
||||
e.spell ? div('flex flex-col gap-2', [
|
||||
div('flex flex-row items-center gap-4', [ dom('span', { class: 'font-semibold text-lg', text: e.spell.name ?? 'Inconnu' }), div('flex-1 border-b border-dashed border-light-50 dark:border-dark-50'), dom('span', { class: 'text-light-70 dark:text-dark-70', text: `${e.spell.cost ?? 0} mana` }) ]),
|
||||
div('flex flex-row justify-between items-center gap-2 text-light-70 dark:text-dark-70', [
|
||||
div('flex flex-row gap-2', [ span('flex flex-row', e.spell.rank === 4 ? 'Sort unique' : `Sort ${e.spell.type === 'instinct' ? 'd\'instinct' : e.spell.type === 'knowledge' ? 'de savoir' : 'de précision'} de rang ${e.spell.rank}`), ...(e.spell.elements ?? []).map(elementDom) ]),
|
||||
div('flex flex-row gap-2', [ e.spell.concentration ? proses('a', preview, [span('italic text-sm', 'concentration')], { href: '' }) : undefined, span(undefined, typeof e.spell.speed === 'number' ? `${e.spell.speed} minute${e.spell.speed > 1 ? 's' : ''}` : e.spell.speed) ])
|
||||
]),
|
||||
div('flex flex-row ps-4 p-1 border-l-4 border-light-35 dark:border-dark-35', [ markdown(e.spell.effect) ]),
|
||||
]) : undefined }));
|
||||
return [
|
||||
div('flex flex-col gap-2', [
|
||||
div('flex flex-row justify-between items-center', [
|
||||
div('flex flex-row gap-2 items-center', [
|
||||
dom('span', { class: 'italic tracking-tight text-sm', text: 'Trier par' }),
|
||||
buttongroup([{ text: 'Rang', value: 'rank' }, { text: 'Type', value: 'type' }, { text: 'Element', value: 'element' }], { value: 'rank', class: { option: 'px-2 py-1 text-sm' } }),
|
||||
])
|
||||
buttongroup<'rank' | 'type' | 'element'>([{ text: 'Rang', value: 'rank' }, { text: 'Type', value: 'type' }, { text: 'Element', value: 'element' }], { value: sortPreference, class: { option: 'px-2 py-1 text-sm' }, onChange: (value) => { localStorage.setItem('character-sort', value); sortPreference = value; this.tabs?.refresh(); } }),
|
||||
]),
|
||||
div('flex flex-row gap-2 items-center', [
|
||||
dom('span', { class: ['italic text-sm', { 'text-light-red dark:text-dark-red': character.variables.spells.length !== character.spellslots }], text: `${character.variables.spells.length}/${character.spellslots} sort${character.variables.spells.length > 1 ? 's' : ''} maitrisé${character.variables.spells.length > 1 ? 's' : ''}` }),
|
||||
button(text('Modifier'), () => this.spellPanel(character, spells), 'py-1 px-4'),
|
||||
])
|
||||
]),
|
||||
div('flex flex-col gap-2', spells.map(e => e.dom))
|
||||
])
|
||||
]
|
||||
}
|
||||
spellPanel()
|
||||
spellPanel(character: CompiledCharacter, spelllist: Array<{ id: string, spell?: SpellConfig, source: string }>)
|
||||
{
|
||||
if(!this.character)
|
||||
return;
|
||||
|
||||
const character = this.character.compiled;
|
||||
const availableSpells = Object.values(config.spells).filter(spell => {
|
||||
if (spell.rank === 4) return false;
|
||||
if (character.spellranks[spell.type] < spell.rank) return false;
|
||||
|
|
@ -1465,17 +1529,17 @@ export class CharacterSheet
|
|||
const toggleText = text(state === 'choosen' ? 'Supprimer' : state === 'given' ? 'Inné' : 'Ajouter'), toggleButton = button(toggleText, () => {
|
||||
if(state === 'choosen')
|
||||
{
|
||||
//this.character.variable('spells', character.variables.spells.filter(e => e !== spell.id)); //TO REWORK
|
||||
this.character!.variable('spells', character.variables.spells.filter(e => e !== spell.id));
|
||||
state = 'empty';
|
||||
}
|
||||
else if(state === 'empty')
|
||||
{
|
||||
//this.character.variable('spells', [...character.variables.spells, spell.id]); //TO REWORK
|
||||
this.character!.variable('spells', [...character.variables.spells, spell.id]); //TO REWORK
|
||||
state = 'choosen';
|
||||
}
|
||||
//character = compiler.compiled; //TO REWORK
|
||||
toggleText.textContent = state === 'choosen' ? 'Supprimer' : state === 'given' ? 'Inné' : 'Ajouter';
|
||||
textAmount.textContent = character.variables.spells.length.toString();
|
||||
this.tabs?.refresh();
|
||||
}, "px-2 py-1 text-sm font-normal");
|
||||
toggleButton.disabled = state === 'given';
|
||||
return foldable(() => [
|
||||
|
|
@ -1507,7 +1571,7 @@ export class CharacterSheet
|
|||
]) ], { open: false, class: { container: "px-2 flex flex-col border-light-35 dark:border-dark-35", content: 'py-2' } });
|
||||
}))
|
||||
]);
|
||||
const blocker = fullblocker([ container ], { closeWhenOutside: true });
|
||||
const blocker = fullblocker([ container ], { closeWhenOutside: true, onClose: () => this.character?.saveVariables() });
|
||||
setTimeout(() => container.setAttribute('data-state', 'active'), 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -35,9 +35,16 @@ export function async(size: 'small' | 'normal' | 'large' = 'normal', fn: Promise
|
|||
}
|
||||
export function button(content: Node, onClick?: () => void, cls?: Class)
|
||||
{
|
||||
const btn = dom('button', { 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
|
||||
/*
|
||||
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
|
||||
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
|
||||
disabled:bg-light-10 dark:disabled:bg-dark-10 disabled:border-none disabled:text-light-50 dark:disabled:text-dark-50`, cls], listeners: { click: () => disabled || (onClick && onClick()) } }, [ content ]);
|
||||
disabled:bg-light-10 dark:disabled:bg-dark-10 disabled:border-none disabled:text-light-50 dark:disabled:text-dark-50
|
||||
*/
|
||||
const btn = dom('button', { class: [`inline-flex justify-center items-center outline-none 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
|
||||
hover:bg-light-25 dark:hover:bg-dark-25 hover:border-light-50 dark:hover: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
|
||||
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()) } }, [ content ]);
|
||||
let disabled = false;
|
||||
Object.defineProperty(btn, 'disabled', {
|
||||
get: () => disabled,
|
||||
|
|
@ -48,7 +55,7 @@ export function button(content: Node, onClick?: () => void, cls?: Class)
|
|||
})
|
||||
return btn;
|
||||
}
|
||||
export function buttongroup<T extends any>(options: Array<{ text: string, value: T }>, settings?: { class?: { container?: Class, option?: Class }, value?: T, onChange?: (value: T) => boolean })
|
||||
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;
|
||||
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
|
||||
|
|
@ -60,7 +67,7 @@ export function buttongroup<T extends any>(options: Array<{ text: string, value:
|
|||
elements.forEach(e => e.toggleAttribute('data-selected', false));
|
||||
this.toggleAttribute('data-selected', true);
|
||||
|
||||
if(!settings?.onChange || settings?.onChange(e.value))
|
||||
if(!settings?.onChange || settings?.onChange(e.value) !== false)
|
||||
{
|
||||
currentValue = e.value;
|
||||
}
|
||||
|
|
@ -454,25 +461,38 @@ export function toggle(settings?: { defaultValue?: boolean, change?: (value: boo
|
|||
}, [ div('block w-[18px] h-[18px] translate-x-[2px] will-change-transform transition-transform bg-light-50 dark:bg-dark-50 group-data-[state=checked]:translate-x-[26px] group-data-[disabled]:bg-light-30 dark:group-data-[disabled]:bg-dark-30 group-data-[disabled]:border-light-30 dark:group-data-[disabled]:border-dark-30') ]);
|
||||
return element;
|
||||
}
|
||||
export function tabgroup(tabs: Array<{ id: string, title: NodeChildren, content: NodeChildren | (() => NodeChildren) }>, settings?: { focused?: string, class?: { container?: Class, tabbar?: Class, title?: Class, content?: Class } })
|
||||
export function tabgroup(tabs: Array<{ id: string, title: NodeChildren, content: NodeChildren | (() => NodeChildren) }>, settings?: { focused?: string, class?: { container?: Class, tabbar?: Class, title?: Class, content?: Class } }): HTMLDivElement & { refresh: () => void }
|
||||
{
|
||||
const focus = settings?.focused ?? tabs[0]?.id;
|
||||
let focus = settings?.focused ?? tabs[0]?.id;
|
||||
const titles = tabs.map((e, i) => dom('div', { class: ['px-2 py-1 border-b border-transparent hover:border-accent-blue data-[focus]:border-accent-blue data-[focus]:border-b-[3px] cursor-pointer', settings?.class?.title], attributes: { 'data-focus': e.id === focus }, listeners: { click: function() {
|
||||
if(this.hasAttribute('data-focus'))
|
||||
return;
|
||||
|
||||
titles.forEach(e => e.toggleAttribute('data-focus', false));
|
||||
this.toggleAttribute('data-focus', true);
|
||||
focus = e.id;
|
||||
const _content = typeof e.content === 'function' ? e.content() : e.content;
|
||||
//@ts-expect-error
|
||||
content.replaceChildren(..._content);
|
||||
}}}, e.title));
|
||||
const _content = tabs.find(e => e.id === focus)?.content;
|
||||
const content = div(['', settings?.class?.content], typeof _content === 'function' ? _content() : _content);
|
||||
return div(['flex flex-col', settings?.class?.container], [
|
||||
|
||||
const container = div(['flex flex-col', settings?.class?.container], [
|
||||
div(['flex flex-row items-center gap-1', settings?.class?.tabbar], titles),
|
||||
content
|
||||
]);
|
||||
Object.defineProperty(container, 'refresh', {
|
||||
writable: false,
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: () => {
|
||||
const _content = tabs.find(e => e.id === focus)?.content;
|
||||
//@ts-expect-error
|
||||
_content && content.replaceChildren(...(typeof _content === 'function' ? _content() : _content))
|
||||
}
|
||||
})
|
||||
return container as HTMLDivElement & { refresh: () => void };
|
||||
}
|
||||
|
||||
export interface ToastConfig
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ export function div(cls?: Class, children?: NodeChildren): HTMLDivElement
|
|||
{
|
||||
return dom("div", { class: cls }, children);
|
||||
}
|
||||
export function span(cls?: Class, children?: NodeChildren): HTMLSpanElement
|
||||
export function span(cls?: Class, text?: string): HTMLSpanElement
|
||||
{
|
||||
return dom("span", { class: cls }, children);
|
||||
return dom("span", { class: cls, text: text });
|
||||
}
|
||||
export function svg<K extends keyof SVGElementTagNameMap>(tag: K, properties?: NodeProperties, children?: Omit<NodeChildren, 'HTMLElement' | 'Text'>): SVGElementTagNameMap[K]
|
||||
{
|
||||
|
|
@ -138,7 +138,7 @@ export function icon(name: string, properties?: IconProperties): HTMLElement
|
|||
|
||||
properties?.mode && el.setAttribute('mode', properties?.mode.toString());
|
||||
properties?.inline && el.toggleAttribute('inline', properties?.inline);
|
||||
properties?.noobserver && el.toggleAttribute('noobserver', properties?.noobserver);
|
||||
el.toggleAttribute('noobserver', properties?.noobserver ?? true);
|
||||
properties?.width && el.setAttribute('width', properties?.width.toString());
|
||||
properties?.height && el.setAttribute('height', properties?.height.toString());
|
||||
properties?.flip && el.setAttribute('flip', properties?.flip.toString());
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export interface ModalProperties
|
|||
{
|
||||
priority?: boolean;
|
||||
closeWhenOutside?: boolean;
|
||||
onClose?: () => boolean | void;
|
||||
}
|
||||
|
||||
let teleport: HTMLDivElement;
|
||||
|
|
@ -161,6 +162,7 @@ export function popper(container: HTMLElement, properties?: PopperProperties): H
|
|||
function link(element: HTMLElement) {
|
||||
Object.entries({
|
||||
'mouseenter': show,
|
||||
'mousemove': show,
|
||||
'mouseleave': hide,
|
||||
'focus': show,
|
||||
'blur': hide,
|
||||
|
|
@ -296,14 +298,13 @@ export function tooltip(container: HTMLElement, txt: string | Text, placement: F
|
|||
|
||||
export function fullblocker(content: NodeChildren, properties?: ModalProperties)
|
||||
{
|
||||
const _modalBlocker = dom('div', { class: [' absolute top-0 left-0 bottom-0 right-0 z-0', { 'bg-light-0 dark:bg-dark-0 opacity-70': properties?.priority ?? false }], listeners: { click: properties?.closeWhenOutside ? (() => _modal.remove()) : undefined } });
|
||||
const close = () => (!properties?.onClose || properties.onClose() !== false) && _modal.remove();
|
||||
const _modalBlocker = dom('div', { class: [' absolute top-0 left-0 bottom-0 right-0 z-0', { 'bg-light-0 dark:bg-dark-0 opacity-70': properties?.priority ?? false }], listeners: { click: properties?.closeWhenOutside ? (close) : undefined } });
|
||||
const _modal = dom('div', { class: 'fixed flex justify-center items-center top-0 left-0 bottom-0 right-0 inset-0 z-40' }, [ _modalBlocker, ...content]);
|
||||
|
||||
teleport.appendChild(_modal);
|
||||
|
||||
return {
|
||||
close: () => _modal.remove(),
|
||||
}
|
||||
return { close };
|
||||
}
|
||||
export function modal(content: NodeChildren, properties?: ModalProperties)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { dom, icon, type NodeChildren, type Node, div } from "#shared/dom.util";
|
||||
import { dom, icon, type NodeChildren, type Node, div, type Class } from "#shared/dom.util";
|
||||
import { parseURL } from 'ufo';
|
||||
import render from "#shared/markdown.util";
|
||||
import { popper } from "#shared/floating.util";
|
||||
|
|
@ -64,7 +64,7 @@ export const a: Prose = {
|
|||
}
|
||||
}
|
||||
export const preview: Prose = {
|
||||
custom(properties, children) {
|
||||
custom(properties: { href: string, class?: Class, size?: 'small' | 'large' }, children) {
|
||||
const href = properties.href as string;
|
||||
const { hash, pathname } = parseURL(href);
|
||||
const router = useRouter();
|
||||
|
|
@ -76,17 +76,16 @@ export const preview: Prose = {
|
|||
overview && overview.type !== 'markdown' ? icon(iconByType[overview.type], { class: 'w-4 h-4 inline-block', inline: true }) : undefined
|
||||
]);
|
||||
|
||||
|
||||
if(!!overview)
|
||||
{
|
||||
const magicKeys = useMagicKeys();
|
||||
popper(el, {
|
||||
return overview ? popper(el, {
|
||||
arrow: true,
|
||||
delay: 150,
|
||||
offset: 12,
|
||||
cover: "height",
|
||||
placement: 'bottom-start',
|
||||
class: 'data-[side=bottom]:animate-slideUpAndFade data-[side=right]:animate-slideLeftAndFade data-[side=left]:animate-slideRightAndFade data-[side=top]:animate-slideDownAndFade w-[300px] bg-light-10 dark:bg-dark-10 border border-light-35 dark:border-dark-35 data-[state=open]:transition-transform text-light-100 dark:text-dark-100 min-w-[200px] min-h-[150px] max-w-[600px] max-h-[600px] w-full z-[45]',
|
||||
class: ['data-[side=bottom]:animate-slideUpAndFade data-[side=right]:animate-slideLeftAndFade data-[side=left]:animate-slideRightAndFade data-[side=top]:animate-slideDownAndFade w-[300px] bg-light-10 dark:bg-dark-10 border border-light-35 dark:border-dark-35 data-[state=open]:transition-transform text-light-100 dark:text-dark-100 w-full z-[45]',
|
||||
{ 'min-w-[200px] min-h-[150px] max-w-[600px] max-h-[600px]': !properties?.size || properties.size === 'large', 'max-w-[400px] max-h-[250px]': properties.size === 'small' }
|
||||
],
|
||||
content: () => {
|
||||
return [async('large', Content.getContent(overview.id).then((_content) => {
|
||||
if(_content?.type === 'markdown')
|
||||
|
|
@ -106,10 +105,7 @@ export const preview: Prose = {
|
|||
if(!magicKeys.current.has('control') || magicKeys.current.has('meta'))
|
||||
return false;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return el;
|
||||
}) : el;
|
||||
}
|
||||
}
|
||||
export const callout: Prose = {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export type CharacterConfig = {
|
|||
features: Record<FeatureID, Feature>;
|
||||
enchantments: Record<string, EnchantementConfig>; //TODO
|
||||
items: Record<string, ItemConfig>;
|
||||
lists: Record<string, { id: string, config: ListConfig, values: Record<string, any> }>;
|
||||
lists: Record<string, { id: string, config: Record<string, any>, values: Record<string, any> }>;
|
||||
texts: Record<i18nID, Localized>;
|
||||
};
|
||||
export type EnchantementConfig = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue