zabi-components 5.0.22 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -1478
- package/THEME.md +473 -0
- package/THEMING.md +248 -0
- package/dist/atoms/ActionPanel.svelte +116 -0
- package/dist/atoms/ActionPanel.svelte.d.ts +19 -0
- package/dist/atoms/Badge.svelte +1 -1
- package/dist/atoms/Badge.svelte.d.ts +1 -1
- package/dist/atoms/Button.svelte +35 -16
- package/dist/atoms/Button.svelte.d.ts +3 -1
- package/dist/atoms/Card.svelte +49 -67
- package/dist/atoms/Card.svelte.d.ts +4 -4
- package/dist/atoms/CardHeader.svelte +15 -2
- package/dist/atoms/CardHeader.svelte.d.ts +5 -0
- package/dist/atoms/Checkbox.svelte +50 -76
- package/dist/atoms/Checkbox.svelte.d.ts +7 -2
- package/dist/atoms/CodeBlock.svelte +20 -34
- package/dist/atoms/CodeBlock.svelte.d.ts +1 -0
- package/dist/atoms/ColorPicker.svelte +3 -7
- package/dist/atoms/Container.svelte +41 -0
- package/dist/atoms/Container.svelte.d.ts +12 -0
- package/dist/atoms/Divider.svelte +68 -0
- package/dist/atoms/Divider.svelte.d.ts +13 -0
- package/dist/atoms/FeatureCard.svelte +35 -50
- package/dist/atoms/FeatureCard.svelte.d.ts +9 -4
- package/dist/atoms/IconButton.svelte +33 -24
- package/dist/atoms/IconButton.svelte.d.ts +4 -8
- package/dist/atoms/Input.svelte +58 -26
- package/dist/atoms/Input.svelte.d.ts +6 -1
- package/dist/atoms/List.svelte +2 -7
- package/dist/atoms/List.svelte.d.ts +1 -6
- package/dist/atoms/ListItem.svelte +54 -65
- package/dist/atoms/ListItem.svelte.d.ts +7 -13
- package/dist/atoms/ListItemLeading.svelte +19 -0
- package/dist/atoms/ListItemLeading.svelte.d.ts +9 -0
- package/dist/atoms/Progress.svelte +2 -2
- package/dist/atoms/Radio.svelte +51 -0
- package/dist/atoms/Radio.svelte.d.ts +14 -0
- package/dist/atoms/Select.svelte +28 -33
- package/dist/atoms/Select.svelte.d.ts +0 -10
- package/dist/atoms/SelectionControl.svelte +98 -0
- package/dist/atoms/SelectionControl.svelte.d.ts +26 -0
- package/dist/atoms/Skeleton.svelte +39 -15
- package/dist/atoms/Skeleton.svelte.d.ts +7 -3
- package/dist/atoms/Table.svelte +27 -0
- package/dist/atoms/Table.svelte.d.ts +10 -0
- package/dist/atoms/Text.svelte +46 -0
- package/dist/atoms/Text.svelte.d.ts +13 -0
- package/dist/atoms/Textarea.svelte +74 -53
- package/dist/atoms/Textarea.svelte.d.ts +6 -2
- package/dist/atoms/ThemeToggle.svelte +35 -24
- package/dist/atoms/ThemeToggle.svelte.d.ts +1 -0
- package/dist/atoms/Toast.svelte +55 -31
- package/dist/atoms/Toast.svelte.d.ts +4 -1
- package/dist/atoms/Toggle.svelte +37 -22
- package/dist/atoms/Toggle.svelte.d.ts +2 -1
- package/dist/atoms/Tooltip.svelte +90 -11
- package/dist/atoms/index.d.ts +6 -0
- package/dist/atoms/index.js +6 -0
- package/dist/atoms/selection-control.styles.d.ts +25 -0
- package/dist/atoms/selection-control.styles.js +30 -0
- package/dist/components/atoms/index.d.ts +6 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +26 -0
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -0
- package/dist/components/index.d.ts +42 -7
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts +10 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/navigation-menu-context.d.ts +11 -0
- package/dist/components/molecules/navigation-menu-context.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +39 -0
- package/dist/components/molecules/toast-store.d.ts.map +1 -0
- package/dist/components/organisms/index.d.ts +3 -3
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/page.types.d.ts +69 -0
- package/dist/components/types/page.types.d.ts.map +1 -0
- package/dist/components/types/page.types.js +1 -0
- package/dist/components/types/variants.d.ts +64 -0
- package/dist/components/types/variants.d.ts.map +1 -0
- package/dist/components/types/variants.js +44 -0
- package/dist/components/util/fixed-sidebar-flyout.d.ts +23 -0
- package/dist/components/util/fixed-sidebar-flyout.d.ts.map +1 -0
- package/dist/components/util/focus-utils.d.ts +12 -0
- package/dist/components/util/focus-utils.d.ts.map +1 -0
- package/dist/components/util/ssr-safe.d.ts +17 -0
- package/dist/components/util/ssr-safe.d.ts.map +1 -0
- package/dist/index.d.ts +42 -7
- package/dist/index.js +45 -10
- package/dist/lib/layout-width-tokens.d.ts +17 -0
- package/dist/lib/layout-width-tokens.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Button.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Button.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Input.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Input.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/List.d.ts +3 -0
- package/dist/lib/showcase/component-docs/List.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Modal.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Modal.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Radio.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Radio.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +3 -0
- package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Skeleton.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/_shared.d.ts +12 -0
- package/dist/lib/showcase/component-docs/_shared.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/index.d.ts +4 -0
- package/dist/lib/showcase/component-docs/index.d.ts.map +1 -0
- package/dist/lib/showcase/components-catalog.d.ts +3 -0
- package/dist/lib/showcase/components-catalog.d.ts.map +1 -0
- package/dist/lib/showcase/components-showcase-constants.d.ts +89 -0
- package/dist/lib/showcase/components-showcase-constants.d.ts.map +1 -0
- package/dist/lib/showcase/docs-sidebar-helpers.d.ts +15 -0
- package/dist/lib/showcase/docs-sidebar-helpers.d.ts.map +1 -0
- package/dist/molecules/Alert.svelte +50 -23
- package/dist/molecules/Alert.svelte.d.ts +1 -1
- package/dist/molecules/ComponentDemo.svelte +54 -51
- package/dist/molecules/ContactForm.svelte +70 -65
- package/dist/molecules/Dropdown.svelte +99 -108
- package/dist/molecules/Dropdown.svelte.d.ts +12 -6
- package/dist/molecules/EmptyState.svelte +44 -0
- package/dist/molecules/EmptyState.svelte.d.ts +11 -0
- package/dist/molecules/FormField.svelte +89 -0
- package/dist/molecules/FormField.svelte.d.ts +23 -0
- package/dist/molecules/Header.svelte +41 -0
- package/dist/molecules/Header.svelte.d.ts +10 -0
- package/dist/molecules/ImageUpload.svelte +1 -1
- package/dist/molecules/Modal.svelte +39 -26
- package/dist/molecules/Modal.svelte.d.ts +4 -2
- package/dist/molecules/NavigationMenu.svelte +16 -0
- package/dist/molecules/NavigationMenu.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuContent.svelte +9 -1
- package/dist/molecules/NavigationMenuLink.svelte +1 -1
- package/dist/molecules/NavigationMenuTrigger.svelte +9 -4
- package/dist/molecules/Page.svelte +16 -0
- package/dist/molecules/Page.svelte.d.ts +8 -0
- package/dist/molecules/PropsTable.svelte +61 -0
- package/dist/molecules/PropsTable.svelte.d.ts +8 -0
- package/dist/molecules/RadioGroup.svelte +197 -0
- package/dist/molecules/RadioGroup.svelte.d.ts +20 -0
- package/dist/molecules/Section.svelte +14 -23
- package/dist/molecules/Section.svelte.d.ts +2 -11
- package/dist/molecules/SidebarBrandHeader.svelte +68 -0
- package/dist/molecules/SidebarBrandHeader.svelte.d.ts +10 -0
- package/dist/molecules/SidebarFooter.svelte +119 -0
- package/dist/molecules/SidebarFooter.svelte.d.ts +23 -0
- package/dist/molecules/SidebarNavSection.svelte +49 -0
- package/dist/molecules/SidebarNavSection.svelte.d.ts +14 -0
- package/dist/molecules/SlideUp.svelte +21 -16
- package/dist/molecules/SlideUp.svelte.d.ts +1 -1
- package/dist/molecules/Tabs.svelte +6 -11
- package/dist/molecules/Toaster.svelte +21 -0
- package/dist/molecules/Toaster.svelte.d.ts +6 -0
- package/dist/molecules/ToasterToast.svelte +217 -0
- package/dist/molecules/ToasterToast.svelte.d.ts +7 -0
- package/dist/molecules/index.d.ts +10 -0
- package/dist/molecules/index.js +10 -0
- package/dist/molecules/navigation-menu-context.d.ts +11 -0
- package/dist/molecules/navigation-menu-context.js +18 -0
- package/dist/molecules/toast-store.d.ts +38 -0
- package/dist/molecules/toast-store.js +34 -0
- package/dist/organisms/SidebarAccountPanel.svelte +118 -0
- package/dist/organisms/SidebarAccountPanel.svelte.d.ts +20 -0
- package/dist/organisms/SidebarNavigation.svelte +286 -212
- package/dist/organisms/SidebarNavigation.svelte.d.ts +22 -1
- package/dist/organisms/{SidebarProjectPanel.svelte → SidebarPanel.svelte} +67 -34
- package/dist/organisms/{SidebarProjectPanel.svelte.d.ts → SidebarPanel.svelte.d.ts} +9 -6
- package/dist/organisms/TopNavbar.svelte +214 -0
- package/dist/organisms/TopNavbar.svelte.d.ts +35 -0
- package/dist/organisms/index.d.ts +3 -3
- package/dist/organisms/index.js +3 -3
- package/dist/routes/lib/focus-utils.d.ts +7 -18
- package/dist/routes/lib/focus-utils.ts +32 -34
- package/dist/routes/lib/ssr-safe.ts +5 -4
- package/dist/routes/lib/variant-utils.ts +48 -39
- package/dist/types/components.d.ts +187 -0
- package/dist/types/components.d.ts.map +1 -0
- package/dist/types/events.d.ts +200 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/index.d.ts +171 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/page.types.d.ts +3 -0
- package/dist/types/page.types.d.ts.map +1 -0
- package/dist/types/page.types.js +1 -0
- package/dist/types/page.types.ts +79 -0
- package/dist/types/variants.d.ts +3 -0
- package/dist/types/variants.d.ts.map +1 -0
- package/dist/types/variants.js +1 -0
- package/dist/types/variants.ts +75 -0
- package/dist/util/fixed-sidebar-flyout.d.ts +22 -0
- package/dist/util/fixed-sidebar-flyout.js +148 -0
- package/dist/util/focus-utils.d.ts +11 -0
- package/dist/util/focus-utils.js +94 -0
- package/dist/util/ssr-safe.d.ts +16 -0
- package/dist/util/ssr-safe.js +48 -0
- package/dist/zabi-components-colors.css +360 -324
- package/dist/zabi-components-theme-dark-only.css +164 -151
- package/dist/zabi-components-theme-dark.css +164 -151
- package/dist/zabi-components-theme-only.css +253 -230
- package/dist/zabi-components-theme.css +253 -230
- package/dist/zabi-components.css +1262 -241
- package/docs/theme-imports.md +112 -0
- package/package.json +32 -10
- package/dist/organisms/Navbar.svelte +0 -92
- package/dist/organisms/Navbar.svelte.d.ts +0 -14
- package/dist/organisms/Navigation.svelte +0 -119
- package/dist/organisms/Navigation.svelte.d.ts +0 -21
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
import { cubicOut } from 'svelte/easing';
|
|
4
|
+
import { slide, fade, fly } from 'svelte/transition';
|
|
5
|
+
import {
|
|
6
|
+
AlertCircle,
|
|
7
|
+
AlertTriangle,
|
|
8
|
+
CheckCircle,
|
|
9
|
+
ChevronDown,
|
|
10
|
+
Info,
|
|
11
|
+
X,
|
|
12
|
+
} from '@lucide/svelte';
|
|
13
|
+
import Button from '../atoms/Button.svelte';
|
|
14
|
+
import { toastStore, type ToastItem } from './toast-store.js';
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
toast: ToastItem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let { toast }: Props = $props();
|
|
21
|
+
|
|
22
|
+
/** Seconds until auto-dismiss; `0` means no countdown (manual dismiss only). */
|
|
23
|
+
function autoDismissSeconds(duration: number | undefined): number {
|
|
24
|
+
if (duration === undefined) return 14;
|
|
25
|
+
if (duration <= 0) return 0;
|
|
26
|
+
return Math.max(1, Math.ceil(duration / 1000));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const maxSeconds = $derived(autoDismissSeconds(toast.duration));
|
|
30
|
+
|
|
31
|
+
let count = $state(0);
|
|
32
|
+
let timerActive = $state(false);
|
|
33
|
+
let isExpanded = $state(false);
|
|
34
|
+
|
|
35
|
+
let intervalRef: ReturnType<typeof setInterval> | undefined;
|
|
36
|
+
|
|
37
|
+
const expandableText = $derived((toast.detail ?? toast.message).trim());
|
|
38
|
+
|
|
39
|
+
const hasExpandable = $derived(expandableText.length > 0);
|
|
40
|
+
|
|
41
|
+
const headerTitle = $derived.by(() => {
|
|
42
|
+
const t = toast.title?.trim();
|
|
43
|
+
if (t) return t;
|
|
44
|
+
switch (toast.type) {
|
|
45
|
+
case 'success':
|
|
46
|
+
return 'Changes saved';
|
|
47
|
+
case 'error':
|
|
48
|
+
return 'Something went wrong';
|
|
49
|
+
case 'warning':
|
|
50
|
+
return 'Please review';
|
|
51
|
+
default:
|
|
52
|
+
return 'Notice';
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const statusIconClass = $derived(
|
|
57
|
+
toast.type === 'success'
|
|
58
|
+
? 'text-success'
|
|
59
|
+
: toast.type === 'error'
|
|
60
|
+
? 'text-error'
|
|
61
|
+
: toast.type === 'warning'
|
|
62
|
+
? 'text-warning'
|
|
63
|
+
: 'text-info',
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const progressFillClass = $derived(
|
|
67
|
+
toast.type === 'success'
|
|
68
|
+
? 'bg-success'
|
|
69
|
+
: toast.type === 'error'
|
|
70
|
+
? 'bg-error'
|
|
71
|
+
: toast.type === 'warning'
|
|
72
|
+
? 'bg-warning'
|
|
73
|
+
: 'bg-info',
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
function stopTimer() {
|
|
77
|
+
if (intervalRef !== undefined) {
|
|
78
|
+
clearInterval(intervalRef);
|
|
79
|
+
intervalRef = undefined;
|
|
80
|
+
}
|
|
81
|
+
timerActive = false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
onMount(() => {
|
|
85
|
+
const max = maxSeconds;
|
|
86
|
+
if (max <= 0) {
|
|
87
|
+
return () => {};
|
|
88
|
+
}
|
|
89
|
+
count = max;
|
|
90
|
+
timerActive = true;
|
|
91
|
+
|
|
92
|
+
intervalRef = setInterval(() => {
|
|
93
|
+
count -= 1;
|
|
94
|
+
if (count <= 0) {
|
|
95
|
+
stopTimer();
|
|
96
|
+
toastStore.dismiss(toast.id);
|
|
97
|
+
}
|
|
98
|
+
}, 1000);
|
|
99
|
+
|
|
100
|
+
return () => {
|
|
101
|
+
if (intervalRef !== undefined) {
|
|
102
|
+
clearInterval(intervalRef);
|
|
103
|
+
intervalRef = undefined;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
function handleDismiss() {
|
|
109
|
+
toastStore.dismiss(toast.id);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function handleOkay() {
|
|
113
|
+
isExpanded = false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const toastEnter = { y: 18, duration: 260, opacity: 0, easing: cubicOut };
|
|
117
|
+
const toastLeave = { y: 14, duration: 300, opacity: 0, easing: cubicOut };
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<div
|
|
121
|
+
class="pointer-events-auto relative w-full min-w-[18rem] overflow-hidden rounded-2xl border border-border bg-surface-1 shadow-xl"
|
|
122
|
+
in:fly={toastEnter}
|
|
123
|
+
out:fly={toastLeave}
|
|
124
|
+
role={toast.type === 'error' ? 'alert' : 'status'}
|
|
125
|
+
>
|
|
126
|
+
<div class="p-4">
|
|
127
|
+
<div class="flex items-start gap-3">
|
|
128
|
+
<div class="shrink-0 pt-0.5">
|
|
129
|
+
{#if toast.type === 'success'}
|
|
130
|
+
<CheckCircle class="size-5 {statusIconClass}" aria-hidden="true" />
|
|
131
|
+
{:else if toast.type === 'error'}
|
|
132
|
+
<AlertCircle class="size-5 {statusIconClass}" aria-hidden="true" />
|
|
133
|
+
{:else if toast.type === 'warning'}
|
|
134
|
+
<AlertTriangle class="size-5 {statusIconClass}" aria-hidden="true" />
|
|
135
|
+
{:else}
|
|
136
|
+
<Info class="size-5 {statusIconClass}" aria-hidden="true" />
|
|
137
|
+
{/if}
|
|
138
|
+
</div>
|
|
139
|
+
<h4 class="min-w-0 flex-1 text-base font-semibold text-headline">
|
|
140
|
+
{headerTitle}
|
|
141
|
+
</h4>
|
|
142
|
+
<div class="flex shrink-0 items-center gap-0.5">
|
|
143
|
+
{#if hasExpandable}
|
|
144
|
+
<button
|
|
145
|
+
type="button"
|
|
146
|
+
class="focus-ring cursor-pointer rounded-md p-1 text-description transition-colors hover:bg-base-100 hover:text-headline focus:outline-none"
|
|
147
|
+
aria-expanded={isExpanded}
|
|
148
|
+
aria-controls="toaster-expand-{toast.id}"
|
|
149
|
+
aria-label={isExpanded ? 'Collapse details' : 'Expand details'}
|
|
150
|
+
onclick={() => (isExpanded = !isExpanded)}
|
|
151
|
+
>
|
|
152
|
+
<ChevronDown
|
|
153
|
+
class="size-5 transition-transform duration-200 {isExpanded
|
|
154
|
+
? 'rotate-180'
|
|
155
|
+
: ''}"
|
|
156
|
+
aria-hidden="true"
|
|
157
|
+
/>
|
|
158
|
+
</button>
|
|
159
|
+
{/if}
|
|
160
|
+
<button
|
|
161
|
+
type="button"
|
|
162
|
+
class="focus-ring cursor-pointer rounded-md p-1 text-description transition-colors hover:bg-base-100 hover:text-headline focus:outline-none"
|
|
163
|
+
onclick={handleDismiss}
|
|
164
|
+
aria-label="Dismiss notification"
|
|
165
|
+
>
|
|
166
|
+
<X class="size-5" aria-hidden="true" />
|
|
167
|
+
</button>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
{#if hasExpandable && isExpanded}
|
|
172
|
+
<div
|
|
173
|
+
id="toaster-expand-{toast.id}"
|
|
174
|
+
class="mt-3"
|
|
175
|
+
transition:slide={{ duration: 200 }}
|
|
176
|
+
>
|
|
177
|
+
<p class="text-sm text-description">{expandableText}</p>
|
|
178
|
+
<Button
|
|
179
|
+
variant="primary"
|
|
180
|
+
size="sm"
|
|
181
|
+
text="Okay"
|
|
182
|
+
type="button"
|
|
183
|
+
class="mt-3"
|
|
184
|
+
onclick={handleOkay}
|
|
185
|
+
/>
|
|
186
|
+
</div>
|
|
187
|
+
{/if}
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
{#if timerActive}
|
|
191
|
+
<div class="px-4 pb-3" transition:fade={{ duration: 180 }}>
|
|
192
|
+
<p class="text-xs text-description">
|
|
193
|
+
This message will close in {count} seconds.
|
|
194
|
+
<button
|
|
195
|
+
type="button"
|
|
196
|
+
class="focus-ring cursor-pointer rounded-sm text-link underline-offset-2 hover:underline focus:outline-none"
|
|
197
|
+
onclick={stopTimer}
|
|
198
|
+
>
|
|
199
|
+
Click to stop
|
|
200
|
+
</button>
|
|
201
|
+
</p>
|
|
202
|
+
</div>
|
|
203
|
+
{/if}
|
|
204
|
+
|
|
205
|
+
{#if timerActive}
|
|
206
|
+
<div
|
|
207
|
+
class="pointer-events-none absolute bottom-0 left-0 right-0 h-1 bg-border/70"
|
|
208
|
+
transition:fade={{ duration: 180 }}
|
|
209
|
+
aria-hidden="true"
|
|
210
|
+
>
|
|
211
|
+
<div
|
|
212
|
+
class="h-full {progressFillClass} transition-all duration-1000 ease-linear"
|
|
213
|
+
style="width: {(count / maxSeconds) * 100}%"
|
|
214
|
+
></div>
|
|
215
|
+
</div>
|
|
216
|
+
{/if}
|
|
217
|
+
</div>
|
|
@@ -11,6 +11,16 @@ export { default as NavigationMenuItem } from './NavigationMenuItem.svelte';
|
|
|
11
11
|
export { default as NavigationMenuTrigger } from './NavigationMenuTrigger.svelte';
|
|
12
12
|
export { default as NavigationMenuContent } from './NavigationMenuContent.svelte';
|
|
13
13
|
export { default as NavigationMenuLink } from './NavigationMenuLink.svelte';
|
|
14
|
+
export { default as Page } from './Page.svelte';
|
|
14
15
|
export { default as Section } from './Section.svelte';
|
|
16
|
+
export { default as SidebarBrandHeader } from './SidebarBrandHeader.svelte';
|
|
17
|
+
export { default as SidebarFooter } from './SidebarFooter.svelte';
|
|
18
|
+
export { default as SidebarNavSection } from './SidebarNavSection.svelte';
|
|
15
19
|
export { default as SlideUp } from './SlideUp.svelte';
|
|
16
20
|
export { default as Tabs } from './Tabs.svelte';
|
|
21
|
+
export { default as FormField } from './FormField.svelte';
|
|
22
|
+
export { default as Header } from './Header.svelte';
|
|
23
|
+
export { default as EmptyState } from './EmptyState.svelte';
|
|
24
|
+
export { default as Toaster } from './Toaster.svelte';
|
|
25
|
+
export { default as RadioGroup } from './RadioGroup.svelte';
|
|
26
|
+
export * from './toast-store.js';
|
package/dist/molecules/index.js
CHANGED
|
@@ -11,6 +11,16 @@ export { default as NavigationMenuItem } from './NavigationMenuItem.svelte';
|
|
|
11
11
|
export { default as NavigationMenuTrigger } from './NavigationMenuTrigger.svelte';
|
|
12
12
|
export { default as NavigationMenuContent } from './NavigationMenuContent.svelte';
|
|
13
13
|
export { default as NavigationMenuLink } from './NavigationMenuLink.svelte';
|
|
14
|
+
export { default as Page } from './Page.svelte';
|
|
14
15
|
export { default as Section } from './Section.svelte';
|
|
16
|
+
export { default as SidebarBrandHeader } from './SidebarBrandHeader.svelte';
|
|
17
|
+
export { default as SidebarFooter } from './SidebarFooter.svelte';
|
|
18
|
+
export { default as SidebarNavSection } from './SidebarNavSection.svelte';
|
|
15
19
|
export { default as SlideUp } from './SlideUp.svelte';
|
|
16
20
|
export { default as Tabs } from './Tabs.svelte';
|
|
21
|
+
export { default as FormField } from './FormField.svelte';
|
|
22
|
+
export { default as Header } from './Header.svelte';
|
|
23
|
+
export { default as EmptyState } from './EmptyState.svelte';
|
|
24
|
+
export { default as Toaster } from './Toaster.svelte';
|
|
25
|
+
export { default as RadioGroup } from './RadioGroup.svelte';
|
|
26
|
+
export * from './toast-store.js';
|
|
@@ -2,5 +2,16 @@ export interface NavigationMenuContextValue {
|
|
|
2
2
|
activeItem: string | null;
|
|
3
3
|
setActiveItem: (item: string | null) => void;
|
|
4
4
|
isMobile: boolean;
|
|
5
|
+
/** Prefix for panel `id` / trigger `aria-controls` (one menu instance). */
|
|
6
|
+
menuInstanceId: string;
|
|
5
7
|
}
|
|
6
8
|
export declare const NAVIGATION_MENU_CONTEXT_KEY: unique symbol;
|
|
9
|
+
/** Panel element id; `itemValue` is sanitized for HTML id rules. */
|
|
10
|
+
export declare function navigationMenuPanelId(menuInstanceId: string, itemValue: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Deterministic menu id from item order + values (FNV-1a).
|
|
13
|
+
* SSR and client must agree — do not use `generateId()` for this pairing.
|
|
14
|
+
*/
|
|
15
|
+
export declare function navigationMenuStableInstanceId(items: ReadonlyArray<{
|
|
16
|
+
value: string;
|
|
17
|
+
}>): string;
|
|
@@ -1 +1,19 @@
|
|
|
1
1
|
export const NAVIGATION_MENU_CONTEXT_KEY = Symbol("navigation-menu");
|
|
2
|
+
/** Panel element id; `itemValue` is sanitized for HTML id rules. */
|
|
3
|
+
export function navigationMenuPanelId(menuInstanceId, itemValue) {
|
|
4
|
+
const safe = itemValue.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
5
|
+
return `${menuInstanceId}-${safe}-panel`;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Deterministic menu id from item order + values (FNV-1a).
|
|
9
|
+
* SSR and client must agree — do not use `generateId()` for this pairing.
|
|
10
|
+
*/
|
|
11
|
+
export function navigationMenuStableInstanceId(items) {
|
|
12
|
+
const raw = items.map((i) => i.value).join("\u001f");
|
|
13
|
+
let h = 2166136261;
|
|
14
|
+
for (let i = 0; i < raw.length; i++) {
|
|
15
|
+
h ^= raw.charCodeAt(i);
|
|
16
|
+
h = Math.imul(h, 16777619);
|
|
17
|
+
}
|
|
18
|
+
return `zabi-nav-${(h >>> 0).toString(36)}`;
|
|
19
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type ToastLevel = 'success' | 'error' | 'warning' | 'info';
|
|
2
|
+
export interface ToastItem {
|
|
3
|
+
id: string;
|
|
4
|
+
/** Primary line (always shown). */
|
|
5
|
+
message: string;
|
|
6
|
+
type: ToastLevel;
|
|
7
|
+
title?: string;
|
|
8
|
+
/** Expandable body; UI can fall back to `message`. */
|
|
9
|
+
detail?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Auto-dismiss duration in milliseconds.
|
|
12
|
+
* When omitted, the toaster uses 14s. Use `0` to keep the toast until dismissed manually.
|
|
13
|
+
*/
|
|
14
|
+
duration?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const toastStore: {
|
|
17
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<ToastItem[]>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
|
|
18
|
+
push(options: {
|
|
19
|
+
message: string;
|
|
20
|
+
type?: ToastLevel;
|
|
21
|
+
/** Same semantics as {@link ToastItem.duration}. */
|
|
22
|
+
duration?: number;
|
|
23
|
+
id?: string;
|
|
24
|
+
title?: string;
|
|
25
|
+
detail?: string;
|
|
26
|
+
}): string;
|
|
27
|
+
dismiss: (id: string) => void;
|
|
28
|
+
clear(): void;
|
|
29
|
+
};
|
|
30
|
+
export declare function pushToast(options: {
|
|
31
|
+
message: string;
|
|
32
|
+
type?: ToastLevel;
|
|
33
|
+
/** Same semantics as {@link ToastItem.duration}. */
|
|
34
|
+
duration?: number;
|
|
35
|
+
title?: string;
|
|
36
|
+
detail?: string;
|
|
37
|
+
}): string;
|
|
38
|
+
export declare function dismissToast(id: string): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { writable } from 'svelte/store';
|
|
2
|
+
function createToastStore() {
|
|
3
|
+
const { subscribe, update } = writable([]);
|
|
4
|
+
function dismiss(id) {
|
|
5
|
+
update((list) => list.filter((t) => t.id !== id));
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
subscribe,
|
|
9
|
+
push(options) {
|
|
10
|
+
const id = options.id ?? `toast-${Math.random().toString(36).slice(2, 11)}`;
|
|
11
|
+
const item = {
|
|
12
|
+
id,
|
|
13
|
+
message: options.message,
|
|
14
|
+
type: options.type ?? 'info',
|
|
15
|
+
duration: options.duration,
|
|
16
|
+
title: options.title,
|
|
17
|
+
detail: options.detail,
|
|
18
|
+
};
|
|
19
|
+
update((list) => [...list, item]);
|
|
20
|
+
return id;
|
|
21
|
+
},
|
|
22
|
+
dismiss,
|
|
23
|
+
clear() {
|
|
24
|
+
update(() => []);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export const toastStore = createToastStore();
|
|
29
|
+
export function pushToast(options) {
|
|
30
|
+
return toastStore.push(options);
|
|
31
|
+
}
|
|
32
|
+
export function dismissToast(id) {
|
|
33
|
+
toastStore.dismiss(id);
|
|
34
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import SidebarPanel, {
|
|
3
|
+
type SidebarPanelItem,
|
|
4
|
+
} from "./SidebarPanel.svelte";
|
|
5
|
+
import { generateId } from "../util/ssr-safe.js";
|
|
6
|
+
import { LogOut, Moon, Sun, User } from "@lucide/svelte";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
/** Target id for `aria-controls` from the sidebar footer/nav. */
|
|
10
|
+
panelId?: string;
|
|
11
|
+
style?: string;
|
|
12
|
+
profileName?: string;
|
|
13
|
+
profileEmail?: string;
|
|
14
|
+
logoutLabel?: string;
|
|
15
|
+
showThemeToggle?: boolean;
|
|
16
|
+
showLogout?: boolean;
|
|
17
|
+
isLightMode?: boolean;
|
|
18
|
+
onThemeToggle?: (nextIsLightMode: boolean) => void;
|
|
19
|
+
onLogout?: () => void;
|
|
20
|
+
onAccount?: () => void;
|
|
21
|
+
onClose?: () => void;
|
|
22
|
+
widthClass?: string;
|
|
23
|
+
variant?: "plain" | "elevated";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
panelId = generateId("account-panel"),
|
|
28
|
+
style = "",
|
|
29
|
+
profileName = "Account",
|
|
30
|
+
profileEmail = "",
|
|
31
|
+
logoutLabel = "Log out",
|
|
32
|
+
showThemeToggle = true,
|
|
33
|
+
showLogout = true,
|
|
34
|
+
isLightMode = $bindable(false),
|
|
35
|
+
onThemeToggle,
|
|
36
|
+
onLogout,
|
|
37
|
+
onAccount,
|
|
38
|
+
onClose,
|
|
39
|
+
widthClass = "w-[320px]",
|
|
40
|
+
variant = "plain",
|
|
41
|
+
...restProps
|
|
42
|
+
}: Props = $props();
|
|
43
|
+
|
|
44
|
+
let selectedItemId = $state("");
|
|
45
|
+
|
|
46
|
+
const items = $derived.by((): SidebarPanelItem[] => {
|
|
47
|
+
const list: SidebarPanelItem[] = [
|
|
48
|
+
{
|
|
49
|
+
id: "account",
|
|
50
|
+
label: "Account",
|
|
51
|
+
description: profileName,
|
|
52
|
+
icon: User,
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
if (showThemeToggle) {
|
|
57
|
+
list.push({
|
|
58
|
+
id: "theme",
|
|
59
|
+
label: "Theme",
|
|
60
|
+
description: isLightMode ? "Light mode" : "Dark mode",
|
|
61
|
+
badgeText: isLightMode ? "Light" : "Dark",
|
|
62
|
+
icon: isLightMode ? Sun : Moon,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (showLogout) {
|
|
67
|
+
list.push({
|
|
68
|
+
id: "logout",
|
|
69
|
+
label: logoutLabel,
|
|
70
|
+
description: "Sign out of this account",
|
|
71
|
+
icon: LogOut,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return list;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
function toggleTheme(): void {
|
|
79
|
+
isLightMode = !isLightMode;
|
|
80
|
+
onThemeToggle?.(isLightMode);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function handleSelect(item: SidebarPanelItem): void {
|
|
84
|
+
if (item.id === "theme") {
|
|
85
|
+
toggleTheme();
|
|
86
|
+
} else if (item.id === "logout") {
|
|
87
|
+
onLogout?.();
|
|
88
|
+
} else if (item.id === "account") {
|
|
89
|
+
onAccount?.();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
onClose?.();
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<div
|
|
97
|
+
id={panelId}
|
|
98
|
+
{style}
|
|
99
|
+
role="dialog"
|
|
100
|
+
aria-modal="false"
|
|
101
|
+
aria-label="Account panel"
|
|
102
|
+
{...restProps}
|
|
103
|
+
>
|
|
104
|
+
<SidebarPanel
|
|
105
|
+
title="Account"
|
|
106
|
+
subtitle={profileEmail}
|
|
107
|
+
ariaLabel="Account panel"
|
|
108
|
+
closeLabel="Close account panel"
|
|
109
|
+
{widthClass}
|
|
110
|
+
{variant}
|
|
111
|
+
showSearch={false}
|
|
112
|
+
items={items}
|
|
113
|
+
bind:selectedItemId={selectedItemId}
|
|
114
|
+
onSelect={handleSelect}
|
|
115
|
+
onClose={onClose}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Target id for `aria-controls` from the sidebar footer/nav. */
|
|
3
|
+
panelId?: string;
|
|
4
|
+
style?: string;
|
|
5
|
+
profileName?: string;
|
|
6
|
+
profileEmail?: string;
|
|
7
|
+
logoutLabel?: string;
|
|
8
|
+
showThemeToggle?: boolean;
|
|
9
|
+
showLogout?: boolean;
|
|
10
|
+
isLightMode?: boolean;
|
|
11
|
+
onThemeToggle?: (nextIsLightMode: boolean) => void;
|
|
12
|
+
onLogout?: () => void;
|
|
13
|
+
onAccount?: () => void;
|
|
14
|
+
onClose?: () => void;
|
|
15
|
+
widthClass?: string;
|
|
16
|
+
variant?: "plain" | "elevated";
|
|
17
|
+
}
|
|
18
|
+
declare const SidebarAccountPanel: import("svelte").Component<Props, {}, "isLightMode">;
|
|
19
|
+
type SidebarAccountPanel = ReturnType<typeof SidebarAccountPanel>;
|
|
20
|
+
export default SidebarAccountPanel;
|