Custom-styled primitives (no headless dep): LumotiaButton — primary/secondary/tertiary/destructive × sm/md/lg LumotiaIconButton — square icon-only; ghost/filled/destructive LumotiaNotice — info/caution/danger/success inline notice LumotiaProgress — native <progress> + token theming LumotiaField — plain + Formsnap modes share the same markup Bits UI 2.18.1 wrappers (warm-brutalist styling): LumotiaSelect — single-select, options=[] LumotiaCombobox — searchable; one-way inputValue + oninput LumotiaDialog — controlled open; closable + footer snippet LumotiaTabs — orchestrates List/Trigger/Content from a tabs array LumotiaTooltip — wraps Provider + Root + Trigger + Content LumotiaMenu — DropdownMenu items=[] with destructive variant design-system-v2 preview route: src/routes/design-system-v2/+page.ts gates with VITE_LUMOTIA_DESIGN_SYSTEM_V2=1. Without the flag the load() throws 404 — route-level gate, not nav- hidden. Run via VITE_LUMOTIA_DESIGN_SYSTEM_V2=1 npm run dev:frontend to see the showcase. Browser-mode component test: src/lib/ui/LumotiaButton.browser.test.ts. Covers render, click, and disabled-blocks-click. Validates that vitest-browser-svelte + the @vitest/browser-playwright provider land Phase 1's tooling contract end-to-end. 3/3 passing in Chromium. Type fix: LumotiaIconButton and LumotiaMenu accept icon: any so lucide-svelte's legacy SvelteComponentTyped shape composes with our Svelte 5 wrappers without forcing a // @ts-nocheck escape hatch on every call site. Tightens to Component<…> once lucide-svelte ships a Svelte 5 build. Type fix: LumotiaCombobox honours Bits UI 2.x Combobox.Root's one-way inputValue contract. The wrapper drops bind:inputValue and exposes an oninput callback so caller-owned filter pipelines (HistoryPage FTS5, ModelDownloader) can drive options upstream. Phase 5 per-page gate green: npm run check (0/0/5700 files), npm test, npm run test:browser (3/3 in Chromium), npm run test:e2e (16/16), guard-no-skeleton clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
2.2 KiB
Svelte
71 lines
2.2 KiB
Svelte
<script lang="ts">
|
|
// v0.2 Phase 5 primitive — Lumotia-tinted Bits UI Dialog.
|
|
// Consumers control open via bind:open. Trigger is rendered by the
|
|
// caller (a button + LumotiaIconButton + anchor — anything that
|
|
// can flip open). Keeping the trigger out of the wrapper means
|
|
// /preview can omit the dialog entirely (Wayland-hardened) without
|
|
// pulling in the Bits UI tree.
|
|
import { Dialog } from "bits-ui";
|
|
import { X } from "lucide-svelte";
|
|
import type { Snippet } from "svelte";
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
onOpenChange?: (open: boolean) => void;
|
|
title?: string;
|
|
description?: string;
|
|
closeLabel?: string;
|
|
children?: Snippet;
|
|
footer?: Snippet;
|
|
}
|
|
|
|
let {
|
|
open = $bindable(false),
|
|
onOpenChange,
|
|
title,
|
|
description,
|
|
closeLabel = "Close",
|
|
children,
|
|
footer,
|
|
}: Props = $props();
|
|
</script>
|
|
|
|
<Dialog.Root bind:open {onOpenChange}>
|
|
<Dialog.Portal>
|
|
<Dialog.Overlay
|
|
class="fixed inset-0 z-40"
|
|
style="background: var(--color-overlay-dim)"
|
|
/>
|
|
<Dialog.Content
|
|
class="fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2 w-[min(90vw,520px)] max-h-[85vh] overflow-auto rounded-2xl bg-bg-card border border-border-subtle shadow-2xl p-6"
|
|
>
|
|
{#if title || description}
|
|
<div class="mb-4">
|
|
{#if title}
|
|
<Dialog.Title class="text-[16px] font-semibold text-text">{title}</Dialog.Title>
|
|
{/if}
|
|
{#if description}
|
|
<Dialog.Description class="text-[13px] text-text-secondary mt-1">{description}</Dialog.Description>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
{#if children}
|
|
<div class="text-[13px] text-text">
|
|
{@render children()}
|
|
</div>
|
|
{/if}
|
|
{#if footer}
|
|
<div class="mt-5 flex justify-end gap-2 pt-4 border-t border-border-subtle">
|
|
{@render footer()}
|
|
</div>
|
|
{/if}
|
|
<Dialog.Close
|
|
class="absolute top-3 right-3 size-7 rounded-md flex items-center justify-center text-text-tertiary hover:text-text hover:bg-hover"
|
|
aria-label={closeLabel}
|
|
>
|
|
<X size={16} />
|
|
</Dialog.Close>
|
|
</Dialog.Content>
|
|
</Dialog.Portal>
|
|
</Dialog.Root>
|