zabi-components 5.0.19 → 5.0.21
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/LICENSE +21 -0
- package/README.md +354 -81
- package/dist/atoms/Button.svelte +6 -3
- package/dist/atoms/Button.svelte.d.ts +1 -0
- package/dist/atoms/Card.svelte +23 -47
- package/dist/atoms/CardContent.svelte +12 -5
- package/dist/atoms/CardContent.svelte.d.ts +2 -0
- package/dist/atoms/CardHeader.svelte +22 -5
- package/dist/atoms/CardHeader.svelte.d.ts +3 -0
- package/dist/atoms/ColorPicker.svelte +271 -7
- package/dist/atoms/IconButton.svelte +88 -0
- package/dist/atoms/IconButton.svelte.d.ts +21 -0
- package/dist/atoms/Input.svelte +7 -6
- package/dist/atoms/Input.svelte.d.ts +3 -1
- package/dist/atoms/Progress.svelte +1 -3
- package/dist/atoms/Select.svelte +109 -24
- package/dist/atoms/Select.svelte.d.ts +10 -0
- package/dist/atoms/Skeleton.svelte +12 -2
- package/dist/atoms/Skeleton.svelte.d.ts +1 -0
- package/dist/atoms/Tooltip.svelte +15 -17
- package/dist/atoms/index.d.ts +1 -2
- package/dist/atoms/index.js +1 -2
- package/dist/components/atoms/index.d.ts +1 -2
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts +8 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/molecules/Dropdown.svelte +101 -14
- package/dist/molecules/Dropdown.svelte.d.ts +7 -0
- package/dist/molecules/ImageUpload.svelte +4 -4
- package/dist/molecules/Modal.svelte +42 -26
- package/dist/molecules/Modal.svelte.d.ts +1 -0
- package/dist/molecules/NavigationMenu.svelte +158 -0
- package/dist/molecules/NavigationMenu.svelte.d.ts +25 -0
- package/dist/molecules/NavigationMenuContent.svelte +83 -0
- package/dist/molecules/NavigationMenuContent.svelte.d.ts +9 -0
- package/dist/molecules/NavigationMenuItem.svelte +36 -0
- package/dist/molecules/NavigationMenuItem.svelte.d.ts +9 -0
- package/dist/molecules/NavigationMenuLink.svelte +53 -0
- package/dist/molecules/NavigationMenuLink.svelte.d.ts +10 -0
- package/dist/molecules/NavigationMenuList.svelte +31 -0
- package/dist/molecules/NavigationMenuList.svelte.d.ts +8 -0
- package/dist/molecules/NavigationMenuTrigger.svelte +81 -0
- package/dist/molecules/NavigationMenuTrigger.svelte.d.ts +9 -0
- package/dist/molecules/Section.svelte +158 -0
- package/dist/molecules/Section.svelte.d.ts +27 -0
- package/dist/molecules/Sidebar.svelte +324 -0
- package/dist/molecules/Sidebar.svelte.d.ts +30 -0
- package/dist/molecules/SlideUp.svelte +2 -2
- package/dist/molecules/index.d.ts +8 -0
- package/dist/molecules/index.js +8 -0
- package/dist/organisms/Navigation.svelte +2 -2
- package/dist/zabi-components-colors.css +2 -2
- package/dist/zabi-components-theme-only.css +2 -2
- package/dist/zabi-components-theme.css +2 -2
- package/dist/zabi-components.css +299 -24
- package/package.json +3 -5
- package/dist/atoms/CardDescription.svelte +0 -24
- package/dist/atoms/CardDescription.svelte.d.ts +0 -8
- package/dist/atoms/CardTitle.svelte +0 -33
- package/dist/atoms/CardTitle.svelte.d.ts +0 -9
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { SizeVariant } from "../../types/variants.js";
|
|
4
|
+
import Heading from "../atoms/Heading.svelte";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
/** Section title */
|
|
8
|
+
title?: string;
|
|
9
|
+
/** Section description/subtitle */
|
|
10
|
+
description?: string;
|
|
11
|
+
/** Heading level for title (1-6) */
|
|
12
|
+
headingLevel?: number;
|
|
13
|
+
/** Section size variant */
|
|
14
|
+
size?: SizeVariant;
|
|
15
|
+
/** Maximum width of the section container */
|
|
16
|
+
maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "full" | "none";
|
|
17
|
+
/** Background variant */
|
|
18
|
+
background?: "default" | "muted" | "accent" | "transparent";
|
|
19
|
+
/** Padding variant */
|
|
20
|
+
padding?: "none" | "sm" | "md" | "lg" | "xl";
|
|
21
|
+
/** Whether to center the content */
|
|
22
|
+
centered?: boolean;
|
|
23
|
+
/** Additional CSS classes */
|
|
24
|
+
className?: string;
|
|
25
|
+
/** Section content */
|
|
26
|
+
children?: Snippet;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let {
|
|
30
|
+
title = "",
|
|
31
|
+
description = "",
|
|
32
|
+
headingLevel = 2,
|
|
33
|
+
size = "md",
|
|
34
|
+
maxWidth = "xl",
|
|
35
|
+
background = "default",
|
|
36
|
+
padding = "lg",
|
|
37
|
+
centered = false,
|
|
38
|
+
className = "",
|
|
39
|
+
children,
|
|
40
|
+
...restProps
|
|
41
|
+
}: Props = $props();
|
|
42
|
+
|
|
43
|
+
const sizeClasses = $derived(() => {
|
|
44
|
+
switch (size) {
|
|
45
|
+
case "sm":
|
|
46
|
+
return "text-sm";
|
|
47
|
+
case "lg":
|
|
48
|
+
return "text-lg";
|
|
49
|
+
default:
|
|
50
|
+
return "text-base";
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const maxWidthClasses = $derived(() => {
|
|
55
|
+
switch (maxWidth) {
|
|
56
|
+
case "sm":
|
|
57
|
+
return "max-w-screen-sm";
|
|
58
|
+
case "md":
|
|
59
|
+
return "max-w-screen-md";
|
|
60
|
+
case "lg":
|
|
61
|
+
return "max-w-screen-lg";
|
|
62
|
+
case "xl":
|
|
63
|
+
return "max-w-screen-xl";
|
|
64
|
+
case "2xl":
|
|
65
|
+
return "max-w-screen-2xl";
|
|
66
|
+
case "full":
|
|
67
|
+
return "max-w-full";
|
|
68
|
+
case "none":
|
|
69
|
+
return "";
|
|
70
|
+
default:
|
|
71
|
+
return "max-w-screen-xl";
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const backgroundClasses = $derived(() => {
|
|
76
|
+
switch (background) {
|
|
77
|
+
case "muted":
|
|
78
|
+
return "bg-base-50";
|
|
79
|
+
case "accent":
|
|
80
|
+
return "bg-brand-50 dark:bg-brand-950";
|
|
81
|
+
case "transparent":
|
|
82
|
+
return "bg-transparent";
|
|
83
|
+
default:
|
|
84
|
+
return "bg-background";
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const paddingClasses = $derived(() => {
|
|
89
|
+
switch (padding) {
|
|
90
|
+
case "none":
|
|
91
|
+
return "";
|
|
92
|
+
case "sm":
|
|
93
|
+
return "py-8 sm:py-12";
|
|
94
|
+
case "md":
|
|
95
|
+
return "py-12 sm:py-16";
|
|
96
|
+
case "lg":
|
|
97
|
+
return "py-16 sm:py-24";
|
|
98
|
+
case "xl":
|
|
99
|
+
return "py-24 sm:py-32";
|
|
100
|
+
default:
|
|
101
|
+
return "py-16 sm:py-24";
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const containerClasses = $derived(() => {
|
|
106
|
+
const base = "w-full";
|
|
107
|
+
const maxWidthClass = maxWidthClasses();
|
|
108
|
+
const centeredClass = centered ? "mx-auto" : "";
|
|
109
|
+
return `${base} ${maxWidthClass} ${centeredClass}`.trim();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const contentClasses = $derived(() => {
|
|
113
|
+
const base = "px-4 sm:px-6 lg:px-8";
|
|
114
|
+
const centeredClass = centered ? "text-center" : "";
|
|
115
|
+
return `${base} ${centeredClass}`.trim();
|
|
116
|
+
});
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<section
|
|
120
|
+
class="section {backgroundClasses()} {paddingClasses()} {className}"
|
|
121
|
+
{...restProps}
|
|
122
|
+
>
|
|
123
|
+
<div class={containerClasses()}>
|
|
124
|
+
<div class={contentClasses()}>
|
|
125
|
+
{#if title}
|
|
126
|
+
<div class="mb-6 {centered ? 'mx-auto' : ''}">
|
|
127
|
+
<Heading level={headingLevel} text={title} />
|
|
128
|
+
{#if description}
|
|
129
|
+
<p
|
|
130
|
+
class="mt-4 {sizeClasses()} text-description {centered
|
|
131
|
+
? 'mx-auto max-w-2xl'
|
|
132
|
+
: ''}"
|
|
133
|
+
>
|
|
134
|
+
{description}
|
|
135
|
+
</p>
|
|
136
|
+
{/if}
|
|
137
|
+
</div>
|
|
138
|
+
{/if}
|
|
139
|
+
|
|
140
|
+
{#if children}
|
|
141
|
+
<div class="section-content {centered ? 'mx-auto' : ''}">
|
|
142
|
+
{@render children()}
|
|
143
|
+
</div>
|
|
144
|
+
{/if}
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</section>
|
|
148
|
+
|
|
149
|
+
<style>
|
|
150
|
+
.section {
|
|
151
|
+
position: relative;
|
|
152
|
+
width: 100%;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.section-content {
|
|
156
|
+
width: 100%;
|
|
157
|
+
}
|
|
158
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { SizeVariant } from "../../types/variants.js";
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Section title */
|
|
5
|
+
title?: string;
|
|
6
|
+
/** Section description/subtitle */
|
|
7
|
+
description?: string;
|
|
8
|
+
/** Heading level for title (1-6) */
|
|
9
|
+
headingLevel?: number;
|
|
10
|
+
/** Section size variant */
|
|
11
|
+
size?: SizeVariant;
|
|
12
|
+
/** Maximum width of the section container */
|
|
13
|
+
maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "full" | "none";
|
|
14
|
+
/** Background variant */
|
|
15
|
+
background?: "default" | "muted" | "accent" | "transparent";
|
|
16
|
+
/** Padding variant */
|
|
17
|
+
padding?: "none" | "sm" | "md" | "lg" | "xl";
|
|
18
|
+
/** Whether to center the content */
|
|
19
|
+
centered?: boolean;
|
|
20
|
+
/** Additional CSS classes */
|
|
21
|
+
className?: string;
|
|
22
|
+
/** Section content */
|
|
23
|
+
children?: Snippet;
|
|
24
|
+
}
|
|
25
|
+
declare const Section: import("svelte").Component<Props, {}, "">;
|
|
26
|
+
type Section = ReturnType<typeof Section>;
|
|
27
|
+
export default Section;
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import { onMount, onDestroy } from "svelte";
|
|
4
|
+
|
|
5
|
+
interface SidebarItem {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
icon?: any;
|
|
10
|
+
onClick?: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface SidebarSection {
|
|
14
|
+
title?: string;
|
|
15
|
+
items: SidebarItem[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
title?: string;
|
|
20
|
+
sections?: SidebarSection[];
|
|
21
|
+
items?: SidebarItem[];
|
|
22
|
+
selectedItemId?: string;
|
|
23
|
+
selectedSectionId?: string;
|
|
24
|
+
onItemClick?: (item: SidebarItem) => void;
|
|
25
|
+
onSectionClick?: (sectionId: string) => void;
|
|
26
|
+
className?: string;
|
|
27
|
+
header?: Snippet;
|
|
28
|
+
footer?: Snippet;
|
|
29
|
+
isOpen?: boolean;
|
|
30
|
+
onClose?: () => void;
|
|
31
|
+
mobileResponsive?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let {
|
|
35
|
+
title = "",
|
|
36
|
+
sections = [],
|
|
37
|
+
items = [],
|
|
38
|
+
selectedItemId = "",
|
|
39
|
+
selectedSectionId = "",
|
|
40
|
+
onItemClick,
|
|
41
|
+
onSectionClick,
|
|
42
|
+
className = "",
|
|
43
|
+
header,
|
|
44
|
+
footer,
|
|
45
|
+
isOpen = true,
|
|
46
|
+
onClose,
|
|
47
|
+
mobileResponsive = true,
|
|
48
|
+
...restProps
|
|
49
|
+
}: Props = $props();
|
|
50
|
+
|
|
51
|
+
let isMobile = $state(false);
|
|
52
|
+
let sidebarElement = $state<HTMLElement | null>(null);
|
|
53
|
+
|
|
54
|
+
function checkMobile() {
|
|
55
|
+
if (mobileResponsive) {
|
|
56
|
+
isMobile = window.innerWidth < 768;
|
|
57
|
+
} else {
|
|
58
|
+
isMobile = false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function handleItemClick(item: SidebarItem) {
|
|
63
|
+
if (item.onClick) {
|
|
64
|
+
item.onClick();
|
|
65
|
+
}
|
|
66
|
+
if (onItemClick) {
|
|
67
|
+
onItemClick(item);
|
|
68
|
+
}
|
|
69
|
+
// Close sidebar on mobile when item is clicked
|
|
70
|
+
if (isMobile && onClose) {
|
|
71
|
+
onClose();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function handleSectionClick(sectionId: string) {
|
|
76
|
+
if (onSectionClick) {
|
|
77
|
+
onSectionClick(sectionId);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function handleBackdropClick(event: MouseEvent) {
|
|
82
|
+
if (event.target === event.currentTarget && onClose) {
|
|
83
|
+
onClose();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
88
|
+
if (event.key === "Escape" && isOpen && isMobile && onClose) {
|
|
89
|
+
onClose();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
onMount(() => {
|
|
94
|
+
checkMobile();
|
|
95
|
+
if (mobileResponsive) {
|
|
96
|
+
window.addEventListener("resize", checkMobile);
|
|
97
|
+
return () => window.removeEventListener("resize", checkMobile);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const hasSections = $derived(sections.length > 0);
|
|
102
|
+
const hasItems = $derived(items.length > 0);
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
{#if isMobile && mobileResponsive}
|
|
106
|
+
<!-- Mobile: Overlay and Drawer -->
|
|
107
|
+
{#if isOpen}
|
|
108
|
+
<div
|
|
109
|
+
class="fixed inset-0 bg-overlay z-modal md:hidden"
|
|
110
|
+
onclick={handleBackdropClick}
|
|
111
|
+
onkeydown={handleKeydown}
|
|
112
|
+
role="dialog"
|
|
113
|
+
aria-modal="true"
|
|
114
|
+
aria-label="Sidebar navigation"
|
|
115
|
+
tabindex="-1"
|
|
116
|
+
>
|
|
117
|
+
<aside
|
|
118
|
+
bind:this={sidebarElement}
|
|
119
|
+
class="fixed left-0 top-0 bottom-0 w-64 bg-base-100 border-r border-border p-6 h-full overflow-y-auto transform transition-transform duration-300 ease-in-out {isOpen ? 'translate-x-0' : '-translate-x-full'} {className}"
|
|
120
|
+
role="complementary"
|
|
121
|
+
aria-label="Sidebar navigation"
|
|
122
|
+
onclick={(e) => e.stopPropagation()}
|
|
123
|
+
{...restProps}
|
|
124
|
+
>
|
|
125
|
+
<div class="flex items-center justify-between mb-4 md:hidden">
|
|
126
|
+
{#if title}
|
|
127
|
+
<h2 class="text-lg font-semibold text-headline">{title}</h2>
|
|
128
|
+
{/if}
|
|
129
|
+
{#if onClose}
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
onclick={onClose}
|
|
133
|
+
class="text-description hover:text-headline text-2xl cursor-pointer transition-colors w-8 h-8 flex items-center justify-center rounded-full hover:bg-base-50"
|
|
134
|
+
aria-label="Close sidebar"
|
|
135
|
+
>
|
|
136
|
+
×
|
|
137
|
+
</button>
|
|
138
|
+
{/if}
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
{#if header}
|
|
142
|
+
<div class="mb-6">
|
|
143
|
+
{@render header()}
|
|
144
|
+
</div>
|
|
145
|
+
{:else if title && !isMobile}
|
|
146
|
+
<h2 class="text-lg font-semibold text-headline mb-4">{title}</h2>
|
|
147
|
+
{/if}
|
|
148
|
+
|
|
149
|
+
{#if hasSections}
|
|
150
|
+
{#each sections as section}
|
|
151
|
+
{#if section.title}
|
|
152
|
+
<div class="mb-4">
|
|
153
|
+
<h3 class="text-sm font-medium text-headline mb-2 uppercase tracking-wide">
|
|
154
|
+
{section.title}
|
|
155
|
+
</h3>
|
|
156
|
+
</div>
|
|
157
|
+
{/if}
|
|
158
|
+
|
|
159
|
+
<nav class="space-y-2 mb-8">
|
|
160
|
+
{#each section.items as item}
|
|
161
|
+
<button
|
|
162
|
+
type="button"
|
|
163
|
+
onclick={() => handleItemClick(item)}
|
|
164
|
+
class="w-full text-left px-3 py-2 rounded-md transition-colors duration-200 {selectedItemId ===
|
|
165
|
+
item.id
|
|
166
|
+
? 'bg-action-primary-subtle text-headline border-l-2 border-action-primary'
|
|
167
|
+
: 'text-secondary hover:text-headline hover:bg-base-50'}"
|
|
168
|
+
aria-current={selectedItemId === item.id ? "page" : undefined}
|
|
169
|
+
>
|
|
170
|
+
<div class="flex items-center gap-2">
|
|
171
|
+
{#if item.icon}
|
|
172
|
+
<div class="shrink-0">
|
|
173
|
+
<svelte:component this={item.icon} class="h-4 w-4" />
|
|
174
|
+
</div>
|
|
175
|
+
{/if}
|
|
176
|
+
<div class="flex-1">
|
|
177
|
+
<div class="font-medium">{item.label}</div>
|
|
178
|
+
{#if item.description}
|
|
179
|
+
<div class="text-xs text-description">
|
|
180
|
+
{item.description}
|
|
181
|
+
</div>
|
|
182
|
+
{/if}
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</button>
|
|
186
|
+
{/each}
|
|
187
|
+
</nav>
|
|
188
|
+
{/each}
|
|
189
|
+
{:else if hasItems}
|
|
190
|
+
<nav class="space-y-1">
|
|
191
|
+
{#each items as item}
|
|
192
|
+
<button
|
|
193
|
+
type="button"
|
|
194
|
+
onclick={() => handleItemClick(item)}
|
|
195
|
+
class="w-full text-left px-3 py-2 rounded-md transition-colors duration-200 {selectedItemId ===
|
|
196
|
+
item.id
|
|
197
|
+
? 'bg-action-primary-subtle text-headline border-l-2 border-action-primary'
|
|
198
|
+
: 'text-secondary hover:text-headline hover:bg-base-50'}"
|
|
199
|
+
aria-current={selectedItemId === item.id ? "page" : undefined}
|
|
200
|
+
>
|
|
201
|
+
<div class="flex items-center gap-2">
|
|
202
|
+
{#if item.icon}
|
|
203
|
+
<div class="shrink-0">
|
|
204
|
+
<svelte:component this={item.icon} class="h-4 w-4" />
|
|
205
|
+
</div>
|
|
206
|
+
{/if}
|
|
207
|
+
<div class="flex-1">
|
|
208
|
+
<div class="font-medium">{item.label}</div>
|
|
209
|
+
{#if item.description}
|
|
210
|
+
<div class="text-xs text-description">
|
|
211
|
+
{item.description}
|
|
212
|
+
</div>
|
|
213
|
+
{/if}
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</button>
|
|
217
|
+
{/each}
|
|
218
|
+
</nav>
|
|
219
|
+
{/if}
|
|
220
|
+
|
|
221
|
+
{#if footer}
|
|
222
|
+
<div class="mt-auto pt-6 border-t border-border">
|
|
223
|
+
{@render footer()}
|
|
224
|
+
</div>
|
|
225
|
+
{/if}
|
|
226
|
+
</aside>
|
|
227
|
+
</div>
|
|
228
|
+
{/if}
|
|
229
|
+
{:else}
|
|
230
|
+
<!-- Desktop: Always visible sidebar -->
|
|
231
|
+
<aside
|
|
232
|
+
bind:this={sidebarElement}
|
|
233
|
+
class="w-64 bg-base-100 border-r border-border p-6 h-full overflow-y-auto {className}"
|
|
234
|
+
role="complementary"
|
|
235
|
+
aria-label="Sidebar navigation"
|
|
236
|
+
{...restProps}
|
|
237
|
+
>
|
|
238
|
+
{#if header}
|
|
239
|
+
<div class="mb-6">
|
|
240
|
+
{@render header()}
|
|
241
|
+
</div>
|
|
242
|
+
{:else if title}
|
|
243
|
+
<h2 class="text-lg font-semibold text-headline mb-4">{title}</h2>
|
|
244
|
+
{/if}
|
|
245
|
+
|
|
246
|
+
{#if hasSections}
|
|
247
|
+
{#each sections as section}
|
|
248
|
+
{#if section.title}
|
|
249
|
+
<div class="mb-4">
|
|
250
|
+
<h3 class="text-sm font-medium text-headline mb-2 uppercase tracking-wide">
|
|
251
|
+
{section.title}
|
|
252
|
+
</h3>
|
|
253
|
+
</div>
|
|
254
|
+
{/if}
|
|
255
|
+
|
|
256
|
+
<nav class="space-y-2 mb-8">
|
|
257
|
+
{#each section.items as item}
|
|
258
|
+
<button
|
|
259
|
+
type="button"
|
|
260
|
+
onclick={() => handleItemClick(item)}
|
|
261
|
+
class="w-full text-left px-3 py-2 rounded-md transition-colors duration-200 {selectedItemId ===
|
|
262
|
+
item.id
|
|
263
|
+
? 'bg-action-primary-subtle text-headline border-l-2 border-action-primary'
|
|
264
|
+
: 'text-secondary hover:text-headline hover:bg-base-50'}"
|
|
265
|
+
aria-current={selectedItemId === item.id ? "page" : undefined}
|
|
266
|
+
>
|
|
267
|
+
<div class="flex items-center gap-2">
|
|
268
|
+
{#if item.icon}
|
|
269
|
+
<div class="shrink-0">
|
|
270
|
+
<svelte:component this={item.icon} class="h-4 w-4" />
|
|
271
|
+
</div>
|
|
272
|
+
{/if}
|
|
273
|
+
<div class="flex-1">
|
|
274
|
+
<div class="font-medium">{item.label}</div>
|
|
275
|
+
{#if item.description}
|
|
276
|
+
<div class="text-xs text-description">
|
|
277
|
+
{item.description}
|
|
278
|
+
</div>
|
|
279
|
+
{/if}
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
</button>
|
|
283
|
+
{/each}
|
|
284
|
+
</nav>
|
|
285
|
+
{/each}
|
|
286
|
+
{:else if hasItems}
|
|
287
|
+
<nav class="space-y-1">
|
|
288
|
+
{#each items as item}
|
|
289
|
+
<button
|
|
290
|
+
type="button"
|
|
291
|
+
onclick={() => handleItemClick(item)}
|
|
292
|
+
class="w-full text-left px-3 py-2 rounded-md transition-colors duration-200 {selectedItemId ===
|
|
293
|
+
item.id
|
|
294
|
+
? 'bg-action-primary-subtle text-headline border-l-2 border-action-primary'
|
|
295
|
+
: 'text-secondary hover:text-headline hover:bg-base-50'}"
|
|
296
|
+
aria-current={selectedItemId === item.id ? "page" : undefined}
|
|
297
|
+
>
|
|
298
|
+
<div class="flex items-center gap-2">
|
|
299
|
+
{#if item.icon}
|
|
300
|
+
<div class="shrink-0">
|
|
301
|
+
<svelte:component this={item.icon} class="h-4 w-4" />
|
|
302
|
+
</div>
|
|
303
|
+
{/if}
|
|
304
|
+
<div class="flex-1">
|
|
305
|
+
<div class="font-medium">{item.label}</div>
|
|
306
|
+
{#if item.description}
|
|
307
|
+
<div class="text-xs text-description">
|
|
308
|
+
{item.description}
|
|
309
|
+
</div>
|
|
310
|
+
{/if}
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
</button>
|
|
314
|
+
{/each}
|
|
315
|
+
</nav>
|
|
316
|
+
{/if}
|
|
317
|
+
|
|
318
|
+
{#if footer}
|
|
319
|
+
<div class="mt-auto pt-6 border-t border-border">
|
|
320
|
+
{@render footer()}
|
|
321
|
+
</div>
|
|
322
|
+
{/if}
|
|
323
|
+
</aside>
|
|
324
|
+
{/if}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface SidebarItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
icon?: any;
|
|
7
|
+
onClick?: () => void;
|
|
8
|
+
}
|
|
9
|
+
interface SidebarSection {
|
|
10
|
+
title?: string;
|
|
11
|
+
items: SidebarItem[];
|
|
12
|
+
}
|
|
13
|
+
interface Props {
|
|
14
|
+
title?: string;
|
|
15
|
+
sections?: SidebarSection[];
|
|
16
|
+
items?: SidebarItem[];
|
|
17
|
+
selectedItemId?: string;
|
|
18
|
+
selectedSectionId?: string;
|
|
19
|
+
onItemClick?: (item: SidebarItem) => void;
|
|
20
|
+
onSectionClick?: (sectionId: string) => void;
|
|
21
|
+
className?: string;
|
|
22
|
+
header?: Snippet;
|
|
23
|
+
footer?: Snippet;
|
|
24
|
+
isOpen?: boolean;
|
|
25
|
+
onClose?: () => void;
|
|
26
|
+
mobileResponsive?: boolean;
|
|
27
|
+
}
|
|
28
|
+
declare const Sidebar: import("svelte").Component<Props, {}, "">;
|
|
29
|
+
type Sidebar = ReturnType<typeof Sidebar>;
|
|
30
|
+
export default Sidebar;
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
|
|
40
40
|
{#if isOpen}
|
|
41
41
|
<div
|
|
42
|
-
class="fixed inset-0 bg-
|
|
42
|
+
class="fixed inset-0 bg-overlay z-modal"
|
|
43
43
|
onclick={handleBackdropClick}
|
|
44
44
|
onkeydown={handleKeydown}
|
|
45
45
|
role="dialog"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
tabindex="-1"
|
|
49
49
|
>
|
|
50
50
|
<div
|
|
51
|
-
class="fixed bottom-0 left-0 right-0 bg-
|
|
51
|
+
class="fixed bottom-0 left-0 right-0 bg-card rounded-t-3xl shadow-xl z-modal max-h-[90vh] overflow-y-auto animate-[slideUp_0.3s_ease-out] flex flex-col"
|
|
52
52
|
>
|
|
53
53
|
{#if title}
|
|
54
54
|
<div class="flex items-center justify-between px-6 pt-6 pb-4">
|
|
@@ -5,5 +5,13 @@ export { default as Dropdown } from './Dropdown.svelte';
|
|
|
5
5
|
export { default as Form } from './Form.svelte';
|
|
6
6
|
export { default as ImageUpload } from './ImageUpload.svelte';
|
|
7
7
|
export { default as Modal } from './Modal.svelte';
|
|
8
|
+
export { default as NavigationMenu } from './NavigationMenu.svelte';
|
|
9
|
+
export { default as NavigationMenuList } from './NavigationMenuList.svelte';
|
|
10
|
+
export { default as NavigationMenuItem } from './NavigationMenuItem.svelte';
|
|
11
|
+
export { default as NavigationMenuTrigger } from './NavigationMenuTrigger.svelte';
|
|
12
|
+
export { default as NavigationMenuContent } from './NavigationMenuContent.svelte';
|
|
13
|
+
export { default as NavigationMenuLink } from './NavigationMenuLink.svelte';
|
|
14
|
+
export { default as Section } from './Section.svelte';
|
|
15
|
+
export { default as Sidebar } from './Sidebar.svelte';
|
|
8
16
|
export { default as SlideUp } from './SlideUp.svelte';
|
|
9
17
|
export { default as Tabs } from './Tabs.svelte';
|
package/dist/molecules/index.js
CHANGED
|
@@ -5,5 +5,13 @@ export { default as Dropdown } from './Dropdown.svelte';
|
|
|
5
5
|
export { default as Form } from './Form.svelte';
|
|
6
6
|
export { default as ImageUpload } from './ImageUpload.svelte';
|
|
7
7
|
export { default as Modal } from './Modal.svelte';
|
|
8
|
+
export { default as NavigationMenu } from './NavigationMenu.svelte';
|
|
9
|
+
export { default as NavigationMenuList } from './NavigationMenuList.svelte';
|
|
10
|
+
export { default as NavigationMenuItem } from './NavigationMenuItem.svelte';
|
|
11
|
+
export { default as NavigationMenuTrigger } from './NavigationMenuTrigger.svelte';
|
|
12
|
+
export { default as NavigationMenuContent } from './NavigationMenuContent.svelte';
|
|
13
|
+
export { default as NavigationMenuLink } from './NavigationMenuLink.svelte';
|
|
14
|
+
export { default as Section } from './Section.svelte';
|
|
15
|
+
export { default as Sidebar } from './Sidebar.svelte';
|
|
8
16
|
export { default as SlideUp } from './SlideUp.svelte';
|
|
9
17
|
export { default as Tabs } from './Tabs.svelte';
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
const ulClasses = $derived(() => {
|
|
36
36
|
return variant === "sidebar"
|
|
37
37
|
? "flex flex-col gap-1"
|
|
38
|
-
: "flex items-center justify-between
|
|
38
|
+
: "flex flex-col gap-1 md:flex-row md:items-center md:justify-between";
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
function getNavItemClasses(itemHref: string): string {
|
|
42
|
-
const baseClasses = "flex flex-col gap-1 grow h-full items-center justify-center min-h-0 min-w-0 relative shrink-0 cursor-pointer";
|
|
42
|
+
const baseClasses = "flex flex-col gap-1 grow h-full items-center justify-center min-h-0 min-w-0 relative shrink-0 cursor-pointer w-full md:w-auto";
|
|
43
43
|
|
|
44
44
|
return baseClasses;
|
|
45
45
|
}
|
|
@@ -194,8 +194,8 @@
|
|
|
194
194
|
--color-action-primary-text: var(--color-brand-50);
|
|
195
195
|
|
|
196
196
|
/* Secondary Actions - Used by bg-action-secondary utility class */
|
|
197
|
-
--color-action-secondary: var(--color-brand-
|
|
198
|
-
--color-action-secondary-hover: var(--color-brand-
|
|
197
|
+
--color-action-secondary: color-mix(in srgb, var(--color-brand-800) 10%, transparent);
|
|
198
|
+
--color-action-secondary-hover: color-mix(in srgb, var(--color-brand-800) 20%, transparent);
|
|
199
199
|
--color-action-secondary-active: var(--color-brand-400);
|
|
200
200
|
--color-action-secondary-disabled: var(--color-brand-500);
|
|
201
201
|
--color-action-secondary-subtle: var(--color-brand-600);
|
|
@@ -186,8 +186,8 @@
|
|
|
186
186
|
--color-action-primary-text: var(--color-brand-50);
|
|
187
187
|
|
|
188
188
|
/* Secondary Actions - Used by bg-action-secondary utility class */
|
|
189
|
-
--color-action-secondary: var(--color-brand-
|
|
190
|
-
--color-action-secondary-hover: var(--color-brand-
|
|
189
|
+
--color-action-secondary: color-mix(in srgb, var(--color-brand-800) 10%, transparent);
|
|
190
|
+
--color-action-secondary-hover: color-mix(in srgb, var(--color-brand-800) 20%, transparent);
|
|
191
191
|
--color-action-secondary-active: var(--color-brand-400);
|
|
192
192
|
--color-action-secondary-disabled: var(--color-brand-500);
|
|
193
193
|
--color-action-secondary-subtle: var(--color-brand-600);
|
|
@@ -188,8 +188,8 @@
|
|
|
188
188
|
--color-action-primary-text: var(--color-brand-50);
|
|
189
189
|
|
|
190
190
|
/* Secondary Actions - Used by bg-action-secondary utility class */
|
|
191
|
-
--color-action-secondary: var(--color-brand-
|
|
192
|
-
--color-action-secondary-hover: var(--color-brand-
|
|
191
|
+
--color-action-secondary: color-mix(in srgb, var(--color-brand-800) 10%, transparent);
|
|
192
|
+
--color-action-secondary-hover: color-mix(in srgb, var(--color-brand-800) 20%, transparent);
|
|
193
193
|
--color-action-secondary-active: var(--color-brand-400);
|
|
194
194
|
--color-action-secondary-disabled: var(--color-brand-500);
|
|
195
195
|
--color-action-secondary-subtle: var(--color-brand-600);
|