Style guide improvement

This commit is contained in:
AlacrisDevs
2026-02-09 15:23:03 +02:00
parent bfeb33906e
commit 046d4bd098

View File

@@ -11,6 +11,7 @@
Spinner, Spinner,
Toggle, Toggle,
Toast, Toast,
ToastContainer,
Chip, Chip,
ListItem, ListItem,
OrgHeader, OrgHeader,
@@ -27,6 +28,22 @@
StatCard, StatCard,
StatusBadge, StatusBadge,
TabBar, TabBar,
Icon,
ContextMenu,
KanbanColumn,
AssigneePicker,
MemberList,
ActivityFeed,
EventCard,
ModuleCard,
QuickLinkGrid,
PageSkeleton,
ContentSkeleton,
Twemoji,
EmojiPicker,
ImagePreviewModal,
VirtualList,
PersonContactModal,
} from "$lib/components/ui"; } from "$lib/components/ui";
let inputValue = $state(""); let inputValue = $state("");
@@ -34,9 +51,96 @@
let selectValue = $state(""); let selectValue = $state("");
let toggleChecked = $state(false); let toggleChecked = $state(false);
let modalOpen = $state(false); let modalOpen = $state(false);
let imagePreviewOpen = $state(false);
let emojiPickerOpen = $state(false);
let personModalProfile = $state<any>(null);
let assigneeValue = $state<string | null>(null);
let activeTab = $state("tab1"); let activeTab = $state("tab1");
let searchFilter = $state(""); let searchFilter = $state("");
const demoMembers = [
{
id: "1",
user_id: "u1",
role: "owner",
profiles: {
id: "u1",
email: "alice@example.com",
full_name: "Alice Johnson",
avatar_url: null,
},
},
{
id: "2",
user_id: "u2",
role: "admin",
profiles: {
id: "u2",
email: "bob@example.com",
full_name: "Bob Smith",
avatar_url: null,
},
},
{
id: "3",
user_id: "u3",
role: "member",
profiles: {
id: "u3",
email: "charlie@example.com",
full_name: "Charlie Brown",
avatar_url: null,
},
},
{
id: "4",
user_id: "u4",
role: "member",
profiles: {
id: "u4",
email: "diana@example.com",
full_name: "Diana Prince",
avatar_url: null,
},
},
];
const demoActivity = [
{
id: "a1",
action: "create",
entity_type: "document",
entity_id: "d1",
entity_name: "Project Brief",
created_at: new Date(Date.now() - 300000).toISOString(),
profiles: {
full_name: "Alice Johnson",
email: "alice@example.com",
},
},
{
id: "a2",
action: "update",
entity_type: "kanban_card",
entity_id: "k1",
entity_name: "Design Review",
created_at: new Date(Date.now() - 3600000).toISOString(),
profiles: { full_name: "Bob Smith", email: "bob@example.com" },
},
{
id: "a3",
action: "delete",
entity_type: "folder",
entity_id: "f1",
entity_name: "Old Files",
created_at: new Date(Date.now() - 86400000).toISOString(),
profiles: {
full_name: "Charlie Brown",
email: "charlie@example.com",
},
},
];
const selectOptions = [ const selectOptions = [
{ value: "option1", label: "Option 1" }, { value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" }, { value: "option2", label: "Option 2" },
@@ -47,7 +151,7 @@
// Component Registry - add new components here and they render automatically // Component Registry - add new components here and they render automatically
// Each entry: { name, description, sections: [{ label, ... }] } // Each entry: { name, description, sections: [{ label, ... }] }
// ============================================================ // ============================================================
const componentCount = 25; const componentCount = 42;
const colors = [ const colors = [
{ {
@@ -97,9 +201,13 @@
"Colors", "Colors",
"Typography", "Typography",
"Button", "Button",
"Icon",
"IconButton",
"Input", "Input",
"Textarea", "Textarea",
"Select", "Select",
"Toggle",
"AssigneePicker",
"Avatar", "Avatar",
"Badge", "Badge",
"Chip", "Chip",
@@ -107,21 +215,33 @@
"Card", "Card",
"SectionCard", "SectionCard",
"StatCard", "StatCard",
"Toggle", "ModuleCard",
"EventCard",
"KanbanColumn",
"ListItem",
"MemberList",
"ActivityFeed",
"QuickLinkGrid",
"Modal",
"ImagePreviewModal",
"PersonContactModal",
"ContextMenu",
"Dropdown",
"Toast",
"Spinner", "Spinner",
"Skeleton", "Skeleton",
"Modal", "PageSkeleton",
"Toast", "ContentSkeleton",
"Logo", "Logo",
"ListItem",
"OrgHeader", "OrgHeader",
"IconButton",
"ContentHeader", "ContentHeader",
"PageHeader", "PageHeader",
"CalendarDay", "CalendarDay",
"TabBar", "TabBar",
"EmptyState", "EmptyState",
"Dropdown", "Twemoji",
"EmojiPicker",
"VirtualList",
]; ];
const filteredSections = $derived( const filteredSections = $derived(
@@ -339,6 +459,54 @@
</div> </div>
</section> </section>
<!-- Icon -->
<section id="icon" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
Icon
</h2>
<div class="space-y-4">
<div>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
Sizes
</h3>
<div class="flex items-end gap-4">
{#each [16, 20, 24, 32, 48] as size}
<div class="flex flex-col items-center gap-1">
<Icon name="star" {size} />
<span class="text-[10px] text-light/30"
>{size}px</span
>
</div>
{/each}
</div>
</div>
<div>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
Various Icons
</h3>
<div class="flex flex-wrap gap-4">
{#each ["home", "settings", "search", "notifications", "person", "edit", "delete", "add", "check_circle", "error", "info", "warning", "folder", "description", "calendar_today", "group"] as name}
<div
class="flex flex-col items-center gap-1 w-14"
>
<Icon {name} size={24} />
<span
class="text-[9px] text-light/30 truncate w-full text-center"
>{name}</span
>
</div>
{/each}
</div>
</div>
</div>
</section>
<!-- Input --> <!-- Input -->
<section id="input" class="space-y-4"> <section id="input" class="space-y-4">
<h2 <h2
@@ -976,6 +1144,578 @@
</Dropdown> </Dropdown>
</section> </section>
<!-- AssigneePicker -->
<section id="assigneepicker" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
AssigneePicker
</h2>
<div class="max-w-sm">
<AssigneePicker
value={assigneeValue}
members={demoMembers}
label="Assigned to"
onchange={(v) => (assigneeValue = v)}
/>
</div>
<p class="text-body-sm text-light/40">
Selected: {assigneeValue ?? "none"}
</p>
</section>
<!-- ContextMenu -->
<section id="contextmenu" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
ContextMenu
</h2>
<p class="text-body-sm text-light/40">
A three-dot menu that opens a dropdown with actions. Click
the icon to toggle.
</p>
<div class="flex items-center gap-6">
<div class="flex flex-col items-center gap-1">
<ContextMenu
items={[
{
label: "Edit",
icon: "edit",
onclick: () => {},
},
{
label: "Duplicate",
icon: "content_copy",
onclick: () => {},
},
{
label: "",
icon: "",
onclick: () => {},
divider: true,
},
{
label: "Delete",
icon: "delete",
onclick: () => {},
danger: true,
},
]}
/>
<span class="text-[10px] text-light/30">right</span>
</div>
<div class="flex flex-col items-center gap-1">
<ContextMenu
align="left"
items={[
{
label: "Share",
icon: "share",
onclick: () => {},
},
{
label: "Export",
icon: "download",
onclick: () => {},
},
]}
/>
<span class="text-[10px] text-light/30">left</span>
</div>
</div>
</section>
<!-- KanbanColumn -->
<section id="kanbancolumn" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
KanbanColumn
</h2>
<div
class="flex gap-3 overflow-x-auto pb-2"
style="height: 320px"
>
<KanbanColumn title="To Do" count={2} onAddCard={() => {}}>
<div
class="bg-surface border border-light/5 rounded-xl p-3"
>
<p class="text-body-sm text-white">
Design mockups
</p>
<p class="text-[11px] text-light/40 mt-1">
Due tomorrow
</p>
</div>
<div
class="bg-surface border border-light/5 rounded-xl p-3"
>
<p class="text-body-sm text-white">Write specs</p>
</div>
</KanbanColumn>
<KanbanColumn
title="In Progress"
count={1}
onAddCard={() => {}}
onMore={() => {}}
>
<div
class="bg-surface border border-light/5 rounded-xl p-3"
>
<p class="text-body-sm text-white">
API integration
</p>
<p class="text-[11px] text-light/40 mt-1">
Started 2h ago
</p>
</div>
</KanbanColumn>
<KanbanColumn title="Done" count={0} onAddCard={() => {}} />
</div>
</section>
<!-- MemberList -->
<section id="memberlist" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
MemberList
</h2>
<div class="grid md:grid-cols-2 gap-6">
<div
class="bg-dark/30 border border-light/5 p-4 rounded-xl"
>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
With Members
</h3>
<MemberList
members={demoMembers}
max={3}
moreHref="#"
moreLabel="+1 more"
/>
</div>
<div
class="bg-dark/30 border border-light/5 p-4 rounded-xl"
>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
Empty
</h3>
<MemberList members={[]} emptyLabel="No members yet" />
</div>
</div>
</section>
<!-- ActivityFeed -->
<section id="activityfeed" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
ActivityFeed
</h2>
<div class="grid md:grid-cols-2 gap-6">
<div
class="bg-dark/30 border border-light/5 p-4 rounded-xl"
>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
With Entries
</h3>
<ActivityFeed entries={demoActivity} />
</div>
<div
class="bg-dark/30 border border-light/5 p-4 rounded-xl"
>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
Empty
</h3>
<ActivityFeed entries={[]} />
</div>
</div>
</section>
<!-- EventCard -->
<section id="eventcard" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
EventCard
</h2>
<div class="space-y-4">
<div>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
Full Card
</h3>
<div class="grid md:grid-cols-2 gap-4">
<EventCard
name="Summer Hackathon"
slug="summer-hackathon"
status="active"
startDate="2026-06-15"
endDate="2026-06-17"
color="#33b679"
venueName="Tech Hub"
href="#"
/>
<EventCard
name="Design Sprint"
slug="design-sprint"
status="planning"
startDate="2026-07-01"
endDate={null}
color="#7986cb"
venueName={null}
href="#"
/>
</div>
</div>
<div>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
Compact
</h3>
<div
class="max-w-md bg-dark/30 border border-light/5 rounded-xl overflow-hidden"
>
<EventCard
name="Team Retreat"
slug="team-retreat"
status="completed"
startDate="2026-03-10"
endDate="2026-03-12"
color="#e67c73"
venueName="Mountain Lodge"
href="#"
compact
/>
<EventCard
name="Q2 Planning"
slug="q2-planning"
status="draft"
startDate="2026-04-01"
endDate={null}
color="#f6bf26"
venueName={null}
href="#"
compact
/>
</div>
</div>
</div>
</section>
<!-- ModuleCard -->
<section id="modulecard" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
ModuleCard
</h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<ModuleCard
label="Documents"
description="Shared files and folders"
icon="description"
href="#"
/>
<ModuleCard
label="Calendar"
description="Events and scheduling"
icon="calendar_today"
href="#"
color="text-success"
bg="bg-success/10"
/>
<ModuleCard
label="Kanban"
description="Task boards"
icon="view_kanban"
href="#"
color="text-warning"
bg="bg-warning/10"
/>
<ModuleCard
label="Finances"
description="Budget tracking"
icon="payments"
href="#"
color="text-error"
bg="bg-error/10"
/>
</div>
</section>
<!-- QuickLinkGrid -->
<section id="quicklinkgrid" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
QuickLinkGrid
</h2>
<div class="max-w-xs">
<QuickLinkGrid
links={[
{
label: "Documents",
icon: "description",
href: "#",
color: "text-primary",
},
{
label: "Calendar",
icon: "calendar_today",
href: "#",
color: "text-success",
},
{
label: "Team",
icon: "group",
href: "#",
color: "text-warning",
},
{
label: "Settings",
icon: "settings",
href: "#",
color: "text-light/50",
},
]}
/>
</div>
</section>
<!-- ImagePreviewModal -->
<section id="imagepreviewmodal" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
ImagePreviewModal
</h2>
<Button onclick={() => (imagePreviewOpen = true)}
>Open Image Preview</Button
>
</section>
{#if imagePreviewOpen}
<ImagePreviewModal
src="https://placehold.co/800x600/0F1B2E/00A3E0?text=Preview"
alt="Demo image"
onClose={() => (imagePreviewOpen = false)}
/>
{/if}
<!-- PersonContactModal -->
<section id="personcontactmodal" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
PersonContactModal
</h2>
<Button
onclick={() =>
(personModalProfile = {
id: "demo-1",
full_name: "Alice Johnson",
email: "alice@example.com",
phone: "+1 555-0123",
avatar_url: null,
discord_handle: "alice#1234",
shirt_size: "M",
hoodie_size: "M",
})}
>
Open Person Modal
</Button>
</section>
<PersonContactModal
profile={personModalProfile}
role="admin"
roleName="Admin"
isOpen={personModalProfile !== null}
onClose={() => (personModalProfile = null)}
/>
<!-- PageSkeleton -->
<section id="pageskeleton" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
PageSkeleton
</h2>
<div class="space-y-6">
{#each ["default", "kanban", "files", "calendar", "settings"] as variant}
<div>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
{variant}
</h3>
<div
class="bg-dark/30 border border-light/5 rounded-xl overflow-hidden h-[200px]"
>
<PageSkeleton
variant={variant as
| "default"
| "kanban"
| "files"
| "calendar"
| "settings"}
/>
</div>
</div>
{/each}
</div>
</section>
<!-- ContentSkeleton -->
<section id="contentskeleton" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
ContentSkeleton
</h2>
<div class="space-y-6">
{#each ["default", "kanban", "files", "list", "detail"] as variant}
<div>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
{variant}
</h3>
<div
class="bg-dark/30 border border-light/5 rounded-xl overflow-hidden h-[200px]"
>
<ContentSkeleton
variant={variant as
| "default"
| "kanban"
| "files"
| "list"
| "detail"}
/>
</div>
</div>
{/each}
</div>
</section>
<!-- Twemoji -->
<section id="twemoji" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
Twemoji
</h2>
<div class="space-y-4">
<div>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
Sizes
</h3>
<div class="flex items-end gap-4">
{#each [16, 24, 32, 48] as size}
<div class="flex flex-col items-center gap-1">
<Twemoji emoji="🚀" {size} />
<span class="text-[10px] text-light/30"
>{size}px</span
>
</div>
{/each}
</div>
</div>
<div>
<h3
class="text-body-sm font-heading text-light/60 mb-2"
>
Various Emojis
</h3>
<div class="flex flex-wrap gap-3">
{#each ["😀", "🎉", "🔥", "💡", "⭐", "🎯", "📦", "🛠️", "✅", "❌", "🌍", "🎨"] as emoji}
<Twemoji {emoji} size={32} />
{/each}
</div>
</div>
</div>
</section>
<!-- EmojiPicker -->
<section id="emojipicker" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
EmojiPicker
</h2>
<p class="text-body-sm text-light/40">
A full emoji picker with category tabs and search. Click to
open.
</p>
<div class="relative">
<Button onclick={() => (emojiPickerOpen = !emojiPickerOpen)}
>{emojiPickerOpen
? "Close Picker"
: "Open Emoji Picker"}</Button
>
{#if emojiPickerOpen}
<div class="absolute top-12 left-0 z-50">
<EmojiPicker
onSelect={(emoji) => {
emojiPickerOpen = false;
}}
onClose={() => (emojiPickerOpen = false)}
/>
</div>
{/if}
</div>
</section>
<!-- VirtualList -->
<section id="virtuallist" class="space-y-4">
<h2
class="font-heading text-body text-white border-b border-light/5 pb-2"
>
VirtualList
</h2>
<p class="text-body-sm text-light/40">
Renders only visible items for large lists. Scroll to see
virtualization in action.
</p>
<div
class="bg-dark/30 border border-light/5 rounded-xl overflow-hidden"
style="height: 240px"
>
<VirtualList
items={Array.from({ length: 1000 }, (_, i) => ({
id: i,
label: `Item ${i + 1}`,
}))}
itemHeight={40}
getKey={(item) => String(item.id)}
>
{#snippet children(
item: { id: number; label: string },
_index: number,
)}
<div
class="flex items-center px-4 h-[40px] border-b border-light/5 text-body-sm text-light/70"
>
{item.label}
</div>
{/snippet}
</VirtualList>
</div>
</section>
<!-- Footer --> <!-- Footer -->
<footer class="text-center py-8 border-t border-light/5"> <footer class="text-center py-8 border-t border-light/5">
<p class="text-[11px] text-light/30 font-body"> <p class="text-[11px] text-light/30 font-body">