feat(fend): adds record cards to each record section
This commit is contained in:
parent
973541e24f
commit
85b87f8140
3 changed files with 81 additions and 24 deletions
58
src/lib/components/RecordCard.svelte
Normal file
58
src/lib/components/RecordCard.svelte
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<script lang="ts">
|
||||
import { ChevronDown } from 'lucide-svelte';
|
||||
import type { RecordDef } from '$lib/types';
|
||||
import DynamicField from './fields/DynamicField.svelte';
|
||||
import { slugify } from '$lib/utils/slugify';
|
||||
|
||||
let { record, data, onFieldChange }: {
|
||||
record: RecordDef;
|
||||
data: Record<string, unknown>;
|
||||
onFieldChange: (key: string, value: any) => void;
|
||||
} = $props();
|
||||
|
||||
let expanded = $state(false);
|
||||
|
||||
let filled = $derived(
|
||||
record.fields.filter((f) => {
|
||||
const v = data[slugify(f.label)];
|
||||
if (v === undefined || v === null || v === '' || v === 0) return false;
|
||||
if (Array.isArray(v) && v.length === 0) return false;
|
||||
return true;
|
||||
}).length
|
||||
);
|
||||
</script>
|
||||
|
||||
<section class="rounded border" style="border-color: var(--border); background: var(--bg-card);">
|
||||
<button
|
||||
onclick={() => { expanded = !expanded; }}
|
||||
class="flex items-center w-full px-4 py-3 text-left gap-3"
|
||||
>
|
||||
<ChevronDown
|
||||
size={16}
|
||||
class="transition-transform shrink-0"
|
||||
style="transform: rotate({expanded ? '0' : '-90'}deg); color: var(--text-muted);"
|
||||
/>
|
||||
<div class="flex-1 min-w-0">
|
||||
<span class="font-medium capitalize">{record.type}</span>
|
||||
{#if record.preamble}
|
||||
<span class="block text-xs truncate" style="color: var(--text-muted);">{record.preamble}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<span class="text-sm tabular-nums" style="color: var(--text-muted);">
|
||||
{filled}/{record.fields.length}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{#if expanded}
|
||||
<div class="px-4 pb-4 flex flex-col gap-4">
|
||||
{#each record.fields as field}
|
||||
<DynamicField
|
||||
{field}
|
||||
value={data[slugify(field.label)]}
|
||||
{data}
|
||||
onChange={(v) => onFieldChange(slugify(field.label), v)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
20
src/lib/components/SchemaForm.svelte
Normal file
20
src/lib/components/SchemaForm.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import type { Character } from '$lib/types';
|
||||
import { roster } from '$lib/state.svelte';
|
||||
import RecordCard from './RecordCard.svelte';
|
||||
|
||||
let { character }: { character: Character } = $props();
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
{#each character.template.records as record}
|
||||
<RecordCard
|
||||
{record}
|
||||
data={character.data}
|
||||
onFieldChange={(key, value) => {
|
||||
character.data[key] = value;
|
||||
roster.scheduleSave(character);
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
<script lang="ts">
|
||||
import Header from '$lib/components/Header.svelte';
|
||||
import DynamicField from '$lib/components/fields/DynamicField.svelte';
|
||||
import SchemaForm from '$lib/components/SchemaForm.svelte';
|
||||
import { roster } from '$lib/state.svelte';
|
||||
import { presets } from '$lib/presets';
|
||||
import { slugify } from '$lib/utils/slugify';
|
||||
</script>
|
||||
|
||||
<div class="min-h-screen flex flex-col">
|
||||
|
|
@ -11,28 +10,8 @@
|
|||
|
||||
<main class="flex-1 p-4">
|
||||
{#if roster.active}
|
||||
{@const char = roster.active}
|
||||
<div class="max-w-xl mx-auto flex flex-col gap-6">
|
||||
{#each char.template.records as record}
|
||||
<section>
|
||||
<h2 class="text-sm font-semibold uppercase tracking-wide mb-3" style="color: var(--text-muted);">
|
||||
{record.type}
|
||||
</h2>
|
||||
<div class="flex flex-col gap-4">
|
||||
{#each record.fields as field}
|
||||
<DynamicField
|
||||
{field}
|
||||
value={char.data[slugify(field.label)]}
|
||||
data={char.data}
|
||||
onChange={(v) => {
|
||||
char.data[slugify(field.label)] = v;
|
||||
roster.scheduleSave(char);
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
{/each}
|
||||
<div class="max-w-xl mx-auto">
|
||||
<SchemaForm character={roster.active} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col items-center justify-center gap-4 h-full min-h-[60vh]">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue