@makolabs/ripple 0.0.1-dev.3 → 0.0.1-dev.5
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 +229 -96
- package/dist/button/Button.svelte +46 -0
- package/dist/button/Button.svelte.d.ts +4 -0
- package/dist/button/button.d.ts +136 -0
- package/dist/button/button.js +167 -0
- package/dist/button/index.d.ts +1 -0
- package/dist/button/index.js +1 -0
- package/dist/drawer/Drawer.svelte +213 -0
- package/dist/drawer/Drawer.svelte.d.ts +4 -0
- package/dist/drawer/drawer.d.ts +177 -0
- package/dist/drawer/drawer.js +80 -0
- package/dist/drawer/index.d.ts +2 -0
- package/dist/drawer/index.js +1 -0
- package/dist/elements/badge/Badge.svelte +35 -0
- package/dist/elements/badge/Badge.svelte.d.ts +4 -0
- package/dist/elements/badge/badge.d.ts +193 -0
- package/dist/elements/badge/badge.js +65 -0
- package/dist/elements/badge/index.d.ts +2 -0
- package/dist/elements/badge/index.js +2 -0
- package/dist/elements/dropdown/Dropdown.svelte +272 -0
- package/dist/elements/dropdown/Dropdown.svelte.d.ts +4 -0
- package/dist/elements/dropdown/Select.svelte +230 -0
- package/dist/elements/dropdown/Select.svelte.d.ts +4 -0
- package/dist/elements/dropdown/dropdown.d.ts +274 -0
- package/dist/elements/dropdown/dropdown.js +89 -0
- package/dist/elements/dropdown/index.d.ts +3 -0
- package/dist/elements/dropdown/index.js +2 -0
- package/dist/elements/dropdown/select.d.ts +220 -0
- package/dist/elements/dropdown/select.js +74 -0
- package/dist/header/Breadcrumbs.svelte +72 -0
- package/dist/header/Breadcrumbs.svelte.d.ts +4 -0
- package/dist/header/PageHeader.svelte +30 -0
- package/dist/header/PageHeader.svelte.d.ts +4 -0
- package/dist/header/breadcrumbs.d.ts +220 -0
- package/dist/header/breadcrumbs.js +81 -0
- package/dist/header/index.d.ts +4 -0
- package/dist/header/index.js +2 -0
- package/dist/header/pageheaders.d.ts +10 -0
- package/dist/header/pageheaders.js +1 -0
- package/dist/helper/cls.d.ts +1 -0
- package/dist/helper/cls.js +4 -0
- package/dist/helper/nav.svelte.d.ts +6 -0
- package/dist/helper/nav.svelte.js +23 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +18 -1
- package/dist/layout/card/Card.svelte +44 -0
- package/dist/layout/card/Card.svelte.d.ts +4 -0
- package/dist/layout/card/StatsCard.svelte +236 -0
- package/dist/layout/card/StatsCard.svelte.d.ts +4 -0
- package/dist/layout/card/card.d.ts +139 -0
- package/dist/layout/card/card.js +50 -0
- package/dist/layout/card/index.d.ts +4 -0
- package/dist/layout/card/index.js +2 -0
- package/dist/layout/card/stats-card.d.ts +208 -0
- package/dist/layout/card/stats-card.js +73 -0
- package/dist/layout/index.d.ts +5 -1
- package/dist/layout/index.js +5 -1
- package/dist/layout/navbar/Navbar.svelte +206 -0
- package/dist/layout/navbar/Navbar.svelte.d.ts +4 -0
- package/dist/layout/navbar/index.d.ts +2 -0
- package/dist/layout/navbar/index.js +2 -0
- package/dist/layout/navbar/navbar.d.ts +228 -0
- package/dist/layout/navbar/navbar.js +98 -0
- package/dist/layout/sidebar/NavGroup.svelte +101 -0
- package/dist/layout/sidebar/NavGroup.svelte.d.ts +4 -0
- package/dist/layout/sidebar/NavItem.svelte +29 -0
- package/dist/layout/sidebar/NavItem.svelte.d.ts +4 -0
- package/dist/layout/sidebar/Sidebar.svelte +145 -0
- package/dist/layout/sidebar/Sidebar.svelte.d.ts +4 -0
- package/dist/layout/sidebar/index.d.ts +2 -0
- package/dist/layout/sidebar/index.js +1 -0
- package/dist/layout/sidebar/sidebar.d.ts +46 -0
- package/dist/layout/sidebar/sidebar.js +1 -0
- package/dist/layout/table/Cells.svelte +111 -0
- package/dist/layout/table/Cells.svelte.d.ts +27 -0
- package/dist/layout/table/Table.svelte +413 -0
- package/dist/layout/table/Table.svelte.d.ts +4 -0
- package/dist/layout/table/index.d.ts +3 -0
- package/dist/layout/table/index.js +2 -0
- package/dist/layout/table/table.d.ts +303 -0
- package/dist/layout/table/table.js +149 -0
- package/dist/layout/tabs/Tab.svelte +57 -0
- package/dist/layout/tabs/Tab.svelte.d.ts +4 -0
- package/dist/layout/tabs/TabContent.svelte +31 -0
- package/dist/layout/tabs/TabContent.svelte.d.ts +4 -0
- package/dist/layout/tabs/TabGroup.svelte +57 -0
- package/dist/layout/tabs/TabGroup.svelte.d.ts +4 -0
- package/dist/layout/tabs/index.d.ts +3 -0
- package/dist/layout/tabs/index.js +3 -0
- package/dist/layout/tabs/tabs.d.ts +155 -0
- package/dist/layout/tabs/tabs.js +156 -0
- package/dist/modal/Modal.svelte +206 -0
- package/dist/modal/Modal.svelte.d.ts +4 -0
- package/dist/modal/index.d.ts +1 -0
- package/dist/modal/index.js +1 -0
- package/dist/modal/modal.d.ts +234 -0
- package/dist/modal/modal.js +81 -0
- package/dist/types/variants.d.ts +21 -0
- package/dist/types/variants.js +19 -0
- package/package.json +100 -102
- package/dist/layout/Card.svelte +0 -179
- package/dist/layout/Card.svelte.d.ts +0 -208
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import type { ClassValue } from 'tailwind-variants';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { Component } from 'svelte';
|
|
4
|
+
export type NavbarLinkItem = {
|
|
5
|
+
label: string;
|
|
6
|
+
href: string;
|
|
7
|
+
icon?: Component;
|
|
8
|
+
current?: boolean;
|
|
9
|
+
children?: NavbarLinkItem[];
|
|
10
|
+
};
|
|
11
|
+
export declare const navbar: import("tailwind-variants").TVReturnType<{
|
|
12
|
+
color: {
|
|
13
|
+
default: {
|
|
14
|
+
base: string;
|
|
15
|
+
mobileMenuButton: string;
|
|
16
|
+
link: string;
|
|
17
|
+
mobileLink: string;
|
|
18
|
+
selectItem: string;
|
|
19
|
+
};
|
|
20
|
+
primary: {
|
|
21
|
+
base: string;
|
|
22
|
+
mobileMenuButton: string;
|
|
23
|
+
link: string;
|
|
24
|
+
mobileLink: string;
|
|
25
|
+
selectItem: string;
|
|
26
|
+
};
|
|
27
|
+
secondary: {
|
|
28
|
+
base: string;
|
|
29
|
+
mobileMenuButton: string;
|
|
30
|
+
link: string;
|
|
31
|
+
mobileLink: string;
|
|
32
|
+
selectItem: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
size: {
|
|
36
|
+
sm: {
|
|
37
|
+
container: string;
|
|
38
|
+
link: string;
|
|
39
|
+
mobileLink: string;
|
|
40
|
+
};
|
|
41
|
+
base: {
|
|
42
|
+
container: string;
|
|
43
|
+
link: string;
|
|
44
|
+
mobileLink: string;
|
|
45
|
+
};
|
|
46
|
+
lg: {
|
|
47
|
+
container: string;
|
|
48
|
+
link: string;
|
|
49
|
+
mobileLink: string;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
active: {
|
|
53
|
+
true: {};
|
|
54
|
+
};
|
|
55
|
+
}, {
|
|
56
|
+
base: string;
|
|
57
|
+
wrapper: string;
|
|
58
|
+
container: string;
|
|
59
|
+
brand: string;
|
|
60
|
+
menu: string;
|
|
61
|
+
mobileMenu: string;
|
|
62
|
+
mobileMenuButton: string;
|
|
63
|
+
mobileMenuContainer: string;
|
|
64
|
+
links: string;
|
|
65
|
+
link: string;
|
|
66
|
+
mobileLink: string;
|
|
67
|
+
actions: string;
|
|
68
|
+
avatar: string;
|
|
69
|
+
selectButton: string;
|
|
70
|
+
selectMenu: string;
|
|
71
|
+
selectItem: string;
|
|
72
|
+
divider: string;
|
|
73
|
+
}, undefined, {
|
|
74
|
+
color: {
|
|
75
|
+
default: {
|
|
76
|
+
base: string;
|
|
77
|
+
mobileMenuButton: string;
|
|
78
|
+
link: string;
|
|
79
|
+
mobileLink: string;
|
|
80
|
+
selectItem: string;
|
|
81
|
+
};
|
|
82
|
+
primary: {
|
|
83
|
+
base: string;
|
|
84
|
+
mobileMenuButton: string;
|
|
85
|
+
link: string;
|
|
86
|
+
mobileLink: string;
|
|
87
|
+
selectItem: string;
|
|
88
|
+
};
|
|
89
|
+
secondary: {
|
|
90
|
+
base: string;
|
|
91
|
+
mobileMenuButton: string;
|
|
92
|
+
link: string;
|
|
93
|
+
mobileLink: string;
|
|
94
|
+
selectItem: string;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
size: {
|
|
98
|
+
sm: {
|
|
99
|
+
container: string;
|
|
100
|
+
link: string;
|
|
101
|
+
mobileLink: string;
|
|
102
|
+
};
|
|
103
|
+
base: {
|
|
104
|
+
container: string;
|
|
105
|
+
link: string;
|
|
106
|
+
mobileLink: string;
|
|
107
|
+
};
|
|
108
|
+
lg: {
|
|
109
|
+
container: string;
|
|
110
|
+
link: string;
|
|
111
|
+
mobileLink: string;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
active: {
|
|
115
|
+
true: {};
|
|
116
|
+
};
|
|
117
|
+
}, {
|
|
118
|
+
base: string;
|
|
119
|
+
wrapper: string;
|
|
120
|
+
container: string;
|
|
121
|
+
brand: string;
|
|
122
|
+
menu: string;
|
|
123
|
+
mobileMenu: string;
|
|
124
|
+
mobileMenuButton: string;
|
|
125
|
+
mobileMenuContainer: string;
|
|
126
|
+
links: string;
|
|
127
|
+
link: string;
|
|
128
|
+
mobileLink: string;
|
|
129
|
+
actions: string;
|
|
130
|
+
avatar: string;
|
|
131
|
+
selectButton: string;
|
|
132
|
+
selectMenu: string;
|
|
133
|
+
selectItem: string;
|
|
134
|
+
divider: string;
|
|
135
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
136
|
+
color: {
|
|
137
|
+
default: {
|
|
138
|
+
base: string;
|
|
139
|
+
mobileMenuButton: string;
|
|
140
|
+
link: string;
|
|
141
|
+
mobileLink: string;
|
|
142
|
+
selectItem: string;
|
|
143
|
+
};
|
|
144
|
+
primary: {
|
|
145
|
+
base: string;
|
|
146
|
+
mobileMenuButton: string;
|
|
147
|
+
link: string;
|
|
148
|
+
mobileLink: string;
|
|
149
|
+
selectItem: string;
|
|
150
|
+
};
|
|
151
|
+
secondary: {
|
|
152
|
+
base: string;
|
|
153
|
+
mobileMenuButton: string;
|
|
154
|
+
link: string;
|
|
155
|
+
mobileLink: string;
|
|
156
|
+
selectItem: string;
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
size: {
|
|
160
|
+
sm: {
|
|
161
|
+
container: string;
|
|
162
|
+
link: string;
|
|
163
|
+
mobileLink: string;
|
|
164
|
+
};
|
|
165
|
+
base: {
|
|
166
|
+
container: string;
|
|
167
|
+
link: string;
|
|
168
|
+
mobileLink: string;
|
|
169
|
+
};
|
|
170
|
+
lg: {
|
|
171
|
+
container: string;
|
|
172
|
+
link: string;
|
|
173
|
+
mobileLink: string;
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
active: {
|
|
177
|
+
true: {};
|
|
178
|
+
};
|
|
179
|
+
}, {
|
|
180
|
+
base: string;
|
|
181
|
+
wrapper: string;
|
|
182
|
+
container: string;
|
|
183
|
+
brand: string;
|
|
184
|
+
menu: string;
|
|
185
|
+
mobileMenu: string;
|
|
186
|
+
mobileMenuButton: string;
|
|
187
|
+
mobileMenuContainer: string;
|
|
188
|
+
links: string;
|
|
189
|
+
link: string;
|
|
190
|
+
mobileLink: string;
|
|
191
|
+
actions: string;
|
|
192
|
+
avatar: string;
|
|
193
|
+
selectButton: string;
|
|
194
|
+
selectMenu: string;
|
|
195
|
+
selectItem: string;
|
|
196
|
+
divider: string;
|
|
197
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
198
|
+
export type NavbarProps = {
|
|
199
|
+
logo?: string | Component;
|
|
200
|
+
links?: NavbarLinkItem[];
|
|
201
|
+
color?: keyof typeof navbar.variants.color;
|
|
202
|
+
size?: keyof typeof navbar.variants.size;
|
|
203
|
+
class?: ClassValue;
|
|
204
|
+
wrapperClass?: ClassValue;
|
|
205
|
+
brandClass?: ClassValue;
|
|
206
|
+
menuClass?: ClassValue;
|
|
207
|
+
mobileMenuClass?: ClassValue;
|
|
208
|
+
linksClass?: ClassValue;
|
|
209
|
+
linkClass?: ClassValue;
|
|
210
|
+
mobileLinkClass?: ClassValue;
|
|
211
|
+
actionsClass?: ClassValue;
|
|
212
|
+
sticky?: boolean;
|
|
213
|
+
brand?: Snippet;
|
|
214
|
+
children?: Snippet;
|
|
215
|
+
actions?: Snippet;
|
|
216
|
+
userMenu?: boolean;
|
|
217
|
+
username?: string;
|
|
218
|
+
userAvatar?: string;
|
|
219
|
+
userMenuItems?: {
|
|
220
|
+
label: string;
|
|
221
|
+
href: string;
|
|
222
|
+
onClick?: () => void;
|
|
223
|
+
}[];
|
|
224
|
+
onmenuitemclick?: (item: {
|
|
225
|
+
label: string;
|
|
226
|
+
href: string;
|
|
227
|
+
}) => void;
|
|
228
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { tv } from 'tailwind-variants';
|
|
2
|
+
export const navbar = tv({
|
|
3
|
+
slots: {
|
|
4
|
+
base: 'sticky top-0 z-40 w-full backdrop-blur',
|
|
5
|
+
wrapper: 'mx-auto px-4 sm:px-6 lg:px-8',
|
|
6
|
+
container: 'flex h-16 justify-between',
|
|
7
|
+
brand: 'flex flex-shrink-0 items-center',
|
|
8
|
+
menu: 'hidden sm:ml-6 sm:flex sm:space-x-8',
|
|
9
|
+
mobileMenu: 'sm:hidden',
|
|
10
|
+
mobileMenuButton: 'inline-flex items-center justify-center rounded-md p-2',
|
|
11
|
+
mobileMenuContainer: 'space-y-1 pb-3 pt-2',
|
|
12
|
+
links: 'flex items-center space-x-4',
|
|
13
|
+
link: 'inline-flex h-full items-center gap-2 border-b-2 px-1 pt-1 text-sm font-medium',
|
|
14
|
+
mobileLink: 'block border-l-4 py-2 pl-3 pr-4 text-base font-medium',
|
|
15
|
+
actions: 'hidden sm:ml-6 sm:flex sm:items-center',
|
|
16
|
+
avatar: 'h-8 w-8 rounded-full',
|
|
17
|
+
selectButton: 'flex rounded-full focus:outline-none',
|
|
18
|
+
selectMenu: 'absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5',
|
|
19
|
+
selectItem: 'block px-4 py-2 text-sm',
|
|
20
|
+
divider: 'border-t border-default-200 my-2'
|
|
21
|
+
},
|
|
22
|
+
variants: {
|
|
23
|
+
color: {
|
|
24
|
+
default: {
|
|
25
|
+
base: 'bg-white/80 border-b border-default-200',
|
|
26
|
+
mobileMenuButton: 'text-default-500 hover:bg-default-100 focus:outline-none',
|
|
27
|
+
link: 'text-default-500 hover:text-default-900 border-transparent hover:border-default-300',
|
|
28
|
+
mobileLink: 'text-default-600 hover:bg-default-50 border-transparent',
|
|
29
|
+
selectItem: 'text-default-700 hover:bg-default-100'
|
|
30
|
+
},
|
|
31
|
+
primary: {
|
|
32
|
+
base: 'bg-primary-50/80 border-b border-primary-200',
|
|
33
|
+
mobileMenuButton: 'text-primary-500 hover:bg-primary-100 focus:outline-none',
|
|
34
|
+
link: 'text-primary-500 hover:text-primary-900 border-transparent hover:border-primary-300',
|
|
35
|
+
mobileLink: 'text-primary-600 hover:bg-primary-50 border-transparent',
|
|
36
|
+
selectItem: 'text-primary-700 hover:bg-primary-100'
|
|
37
|
+
},
|
|
38
|
+
secondary: {
|
|
39
|
+
base: 'bg-secondary-50/80 border-b border-secondary-200',
|
|
40
|
+
mobileMenuButton: 'text-secondary-500 hover:bg-secondary-100 focus:outline-none',
|
|
41
|
+
link: 'text-secondary-500 hover:text-secondary-900 border-transparent hover:border-secondary-300',
|
|
42
|
+
mobileLink: 'text-secondary-600 hover:bg-secondary-50 border-transparent',
|
|
43
|
+
selectItem: 'text-secondary-700 hover:bg-secondary-100'
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
size: {
|
|
47
|
+
sm: {
|
|
48
|
+
container: 'h-12',
|
|
49
|
+
link: 'text-xs',
|
|
50
|
+
mobileLink: 'text-xs'
|
|
51
|
+
},
|
|
52
|
+
base: {
|
|
53
|
+
container: 'h-16',
|
|
54
|
+
link: 'text-sm',
|
|
55
|
+
mobileLink: 'text-sm'
|
|
56
|
+
},
|
|
57
|
+
lg: {
|
|
58
|
+
container: 'h-20',
|
|
59
|
+
link: 'text-base',
|
|
60
|
+
mobileLink: 'text-base'
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
active: {
|
|
64
|
+
true: {}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
compoundVariants: [
|
|
68
|
+
{
|
|
69
|
+
active: true,
|
|
70
|
+
color: 'default',
|
|
71
|
+
class: {
|
|
72
|
+
link: 'border-default-500 text-default-900',
|
|
73
|
+
mobileLink: 'border-default-500 bg-default-50 text-default-700'
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
active: true,
|
|
78
|
+
color: 'primary',
|
|
79
|
+
class: {
|
|
80
|
+
link: 'border-primary-500 text-primary-900',
|
|
81
|
+
mobileLink: 'border-primary-500 bg-primary-50 text-primary-700'
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
active: true,
|
|
86
|
+
color: 'secondary',
|
|
87
|
+
class: {
|
|
88
|
+
link: 'border-secondary-500 text-secondary-900',
|
|
89
|
+
mobileLink: 'border-secondary-500 bg-secondary-50 text-secondary-700'
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
defaultVariants: {
|
|
94
|
+
color: 'default',
|
|
95
|
+
size: 'base',
|
|
96
|
+
active: false
|
|
97
|
+
}
|
|
98
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import FluentChevronRight16Filled from '../../../icons/FluentChevronRight16Filled.svelte';
|
|
3
|
+
import { getContext } from 'svelte';
|
|
4
|
+
import type { MenuBar, NavGroupProps } from './sidebar.js';
|
|
5
|
+
import { cn } from '../../helper/cls.js';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
labelArea,
|
|
9
|
+
children,
|
|
10
|
+
class: className = '',
|
|
11
|
+
active: isActive = false
|
|
12
|
+
}: NavGroupProps = $props();
|
|
13
|
+
|
|
14
|
+
function toggle() {
|
|
15
|
+
isActive = !isActive;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function resetIsActive() {
|
|
19
|
+
isActive = false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const menubar: MenuBar = getContext('menubar');
|
|
23
|
+
|
|
24
|
+
$effect(() => {
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
26
|
+
menubar.collapsed;
|
|
27
|
+
resetIsActive();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const navGroupClasses = $derived(
|
|
31
|
+
cn(
|
|
32
|
+
`flex items-center gap-x-3 hover:bg-gray-950 p-1.5 rounded-md w-full cursor-pointer
|
|
33
|
+
text-gray-400 text-sm/6 text-left`,
|
|
34
|
+
{ 'font-semibold bg-gray-950': isActive, 'hover:bg-gray-950': !isActive },
|
|
35
|
+
className
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const chevronIconClasses = $derived(
|
|
40
|
+
cn('size-4 text-gray-600 shrink-0', {
|
|
41
|
+
'rotate-90': isActive,
|
|
42
|
+
'-ml-2 xl:ml-auto': !menubar.collapsed,
|
|
43
|
+
'-ml-2': menubar.collapsed
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const labelClasses = $derived(
|
|
48
|
+
cn('items-center gap-x-3', {
|
|
49
|
+
hidden: menubar.collapsed,
|
|
50
|
+
'hidden xl:flex': !menubar.collapsed
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const topDivClasses = $derived(
|
|
55
|
+
cn('overflow-hidden transition-all duration-200', {
|
|
56
|
+
'fixed left-0 top-0 shadow-lg w-screen h-screen z-50 bg-gray-700/30 backdrop-blur': isActive,
|
|
57
|
+
'xl:static xl:shadow-none xl:bg-transparent mt-0 xl:mt-1 xl:w-auto xl:h-auto':
|
|
58
|
+
!menubar.collapsed
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const toggleButtonClasses = $derived(
|
|
63
|
+
cn('bg-slate-900 w-72 pl-3 h-8 flex p-2', {
|
|
64
|
+
'xl:hidden': isActive && !menubar.collapsed
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const navUlClasses = $derived(
|
|
69
|
+
cn('w-52', {
|
|
70
|
+
'bg-slate-900 w-72 h-[calc(100vh-32px)] p-2': isActive,
|
|
71
|
+
'xl:h-auto xl:bg-transparent xl:p-0 xl:w-52 xl:ml-2': !menubar.collapsed
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<svelte:window onresize={resetIsActive} />
|
|
77
|
+
|
|
78
|
+
<div>
|
|
79
|
+
<button type="button" class={navGroupClasses} onclick={toggle}>
|
|
80
|
+
{@render labelArea(labelClasses)}
|
|
81
|
+
<FluentChevronRight16Filled class={chevronIconClasses} />
|
|
82
|
+
</button>
|
|
83
|
+
|
|
84
|
+
{#if isActive}
|
|
85
|
+
<div class={topDivClasses}>
|
|
86
|
+
<div class={toggleButtonClasses}>
|
|
87
|
+
<button
|
|
88
|
+
type="button"
|
|
89
|
+
class="flex h-7 w-7 cursor-pointer items-center justify-center gap-x-3
|
|
90
|
+
rounded-md bg-slate-800 text-left text-sm/6 text-gray-700"
|
|
91
|
+
onclick={toggle}
|
|
92
|
+
>
|
|
93
|
+
<FluentChevronRight16Filled class="size-4 shrink-0 -rotate-180 text-gray-600" />
|
|
94
|
+
</button>
|
|
95
|
+
</div>
|
|
96
|
+
<ul class={navUlClasses}>
|
|
97
|
+
{@render children?.()}
|
|
98
|
+
</ul>
|
|
99
|
+
</div>
|
|
100
|
+
{/if}
|
|
101
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { getContext } from 'svelte';
|
|
4
|
+
import type { MenuBar, NavItemProps } from './sidebar.js';
|
|
5
|
+
import { cn } from '../../helper/cls.js';
|
|
6
|
+
|
|
7
|
+
let { href, children, active, class: className = '' }: NavItemProps = $props();
|
|
8
|
+
|
|
9
|
+
const menubar: MenuBar = getContext('menubar');
|
|
10
|
+
|
|
11
|
+
const navItemClasses = $derived(
|
|
12
|
+
cn([
|
|
13
|
+
'group flex gap-x-3 p-2 rounded-md text-gray-400 text-sm/6',
|
|
14
|
+
{ 'bg-gray-950 font-semibold': active, 'hover:bg-gray-950': !active },
|
|
15
|
+
className
|
|
16
|
+
])
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const navChildrenClasses = $derived(
|
|
20
|
+
clsx('flex-1 truncate', {
|
|
21
|
+
hidden: menubar.collapsed,
|
|
22
|
+
'hidden xl:flex': !menubar.collapsed
|
|
23
|
+
})
|
|
24
|
+
);
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<a {href} class={navItemClasses}>
|
|
28
|
+
{@render children?.(navChildrenClasses)}
|
|
29
|
+
</a>
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import NavGroup from './NavGroup.svelte';
|
|
3
|
+
import NavItem from './NavItem.svelte';
|
|
4
|
+
import { setContext } from 'svelte';
|
|
5
|
+
import type {
|
|
6
|
+
MenuBar,
|
|
7
|
+
NavigationItem,
|
|
8
|
+
SidebarProps,
|
|
9
|
+
ParentItem,
|
|
10
|
+
LinkItem
|
|
11
|
+
} from './sidebar.js';
|
|
12
|
+
import clsx from 'clsx';
|
|
13
|
+
import { cn } from '../../helper/cls.js';
|
|
14
|
+
import { isRouteActive } from '../../helper/nav.svelte.js';
|
|
15
|
+
|
|
16
|
+
let { items = [], logo }: SidebarProps = $props();
|
|
17
|
+
let menubar: MenuBar = $state({
|
|
18
|
+
collapsed: false
|
|
19
|
+
});
|
|
20
|
+
setContext('menubar', menubar);
|
|
21
|
+
|
|
22
|
+
function toggle() {
|
|
23
|
+
menubar.collapsed = !menubar.collapsed;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Process a navigation item to determine if it should be active
|
|
28
|
+
*/
|
|
29
|
+
function processNavigationItem(item: NavigationItem): NavigationItem {
|
|
30
|
+
if ('type' in item && item.type === 'horizontal-divider') {
|
|
31
|
+
return item;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if ('children' in item) {
|
|
35
|
+
const parentItem = item as ParentItem;
|
|
36
|
+
let anyChildActive = false;
|
|
37
|
+
|
|
38
|
+
const updatedChildren = parentItem.children.map((child) => {
|
|
39
|
+
const childActive = isRouteActive(child.href);
|
|
40
|
+
if (childActive) {
|
|
41
|
+
anyChildActive = true;
|
|
42
|
+
}
|
|
43
|
+
return { ...child, active: childActive };
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return { ...parentItem, active: anyChildActive, children: updatedChildren };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if ('href' in item) {
|
|
50
|
+
const linkItem = item as LinkItem;
|
|
51
|
+
return { ...linkItem, active: isRouteActive(linkItem.href) };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return item;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Reactively compute the active states based on the current route
|
|
58
|
+
const navigationItems = $derived(items.map((item) => processNavigationItem(item)));
|
|
59
|
+
|
|
60
|
+
const sidebarClasses = $derived(
|
|
61
|
+
clsx(
|
|
62
|
+
`flex min-h-screen max-h-screen overflow-y-auto flex-col gap-y-1 overflow-y-auto
|
|
63
|
+
border-r border-gray-800 bg-gray-900 px-3 duration-300 sticky top-0`,
|
|
64
|
+
{
|
|
65
|
+
'w-16': menubar.collapsed,
|
|
66
|
+
'w-16 xl:w-72 shrink-0': !menubar.collapsed
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const logoTextClasses = $derived(
|
|
72
|
+
clsx('text-xl font-semibold hidden text-white', { 'xl:block': !menubar.collapsed })
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const logoWrapperClasses = $derived(
|
|
76
|
+
clsx('flex shrink-0 items-center justify-between mt-3 flex-col', {
|
|
77
|
+
'xl:mt-0 xl:h-16 flex-row': !menubar.collapsed
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<div class={sidebarClasses}>
|
|
83
|
+
<div class={logoWrapperClasses}>
|
|
84
|
+
<div class="flex items-center gap-x-1">
|
|
85
|
+
{#if logo.src}
|
|
86
|
+
<img src={logo.src} alt={logo.title} class="size-8 shrink-0" />
|
|
87
|
+
{/if}
|
|
88
|
+
{#if logo.title}
|
|
89
|
+
<h1 class={logoTextClasses}>{logo.title}</h1>
|
|
90
|
+
{/if}
|
|
91
|
+
</div>
|
|
92
|
+
<button onclick={toggle} class="hidden cursor-pointer xl:block" aria-label="Toggle Sidebar">
|
|
93
|
+
<svg
|
|
94
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
95
|
+
width="0.8em"
|
|
96
|
+
height="0.8em"
|
|
97
|
+
viewBox="0 0 24 24"
|
|
98
|
+
class="size-6 shrink-0 text-gray-600"
|
|
99
|
+
>
|
|
100
|
+
<path
|
|
101
|
+
fill="currentColor"
|
|
102
|
+
d="M3 17h18a1 1 0 0 1 .117 1.993L21 19H3a1 1 0 0 1-.117-1.993zh18zm0-6l18-.002a1 1 0 0 1 .117 1.993l-.117.007L3 13a1 1 0 0 1-.117-1.993zl18-.002zm0-6h18a1 1 0 0 1 .117 1.993L21 7H3a1 1 0 0 1-.117-1.993zh18z"
|
|
103
|
+
/>
|
|
104
|
+
</svg>
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
<nav class="flex flex-1 flex-col">
|
|
108
|
+
<ul role="list" class="flex flex-1 flex-col gap-y-1">
|
|
109
|
+
{#each navigationItems as item, index (index)}
|
|
110
|
+
{#if 'type' in item && item.type === 'horizontal-divider'}
|
|
111
|
+
<li class="my-3 border-t border-gray-700"></li>
|
|
112
|
+
{:else if 'children' in item}
|
|
113
|
+
<NavGroup active={item.active}>
|
|
114
|
+
{#snippet labelArea(classes)}
|
|
115
|
+
{#if item.Icon}
|
|
116
|
+
{@const Icon = item.Icon}
|
|
117
|
+
<Icon class="size-6 shrink-0 text-gray-600" />
|
|
118
|
+
{/if}
|
|
119
|
+
<span class={cn(classes)}>{item.label}</span>
|
|
120
|
+
{/snippet}
|
|
121
|
+
{#each item.children as child (child.label)}
|
|
122
|
+
<NavItem href={child.href} active={child.active}>
|
|
123
|
+
{#if child.Icon}
|
|
124
|
+
{@const Icon = child.Icon}
|
|
125
|
+
<Icon class="size-6 shrink-0 text-gray-600" />
|
|
126
|
+
{/if}
|
|
127
|
+
<span class="truncate">{child.label}</span>
|
|
128
|
+
</NavItem>
|
|
129
|
+
{/each}
|
|
130
|
+
</NavGroup>
|
|
131
|
+
{:else if 'href' in item}
|
|
132
|
+
<NavItem href={item.href} active={item.active}>
|
|
133
|
+
{#snippet children(classes)}
|
|
134
|
+
{#if item.Icon}
|
|
135
|
+
{@const Icon = item.Icon}
|
|
136
|
+
<Icon class="size-6 shrink-0 text-gray-600" />
|
|
137
|
+
{/if}
|
|
138
|
+
<span class={cn(classes)}>{item.label}</span>
|
|
139
|
+
{/snippet}
|
|
140
|
+
</NavItem>
|
|
141
|
+
{/if}
|
|
142
|
+
{/each}
|
|
143
|
+
</ul>
|
|
144
|
+
</nav>
|
|
145
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Sidebar } from './Sidebar.svelte';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
import type { ClassValue } from 'tailwind-variants';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
export type MenuBar = {
|
|
5
|
+
collapsed: boolean;
|
|
6
|
+
};
|
|
7
|
+
export interface BaseNavigationItem {
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
10
|
+
export interface WithIcon {
|
|
11
|
+
Icon?: Component;
|
|
12
|
+
}
|
|
13
|
+
export interface Activatable {
|
|
14
|
+
active?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface LinkItem extends BaseNavigationItem, WithIcon, Activatable {
|
|
17
|
+
href: string;
|
|
18
|
+
meta?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ParentItem extends BaseNavigationItem, WithIcon, Activatable {
|
|
21
|
+
children: LinkItem[];
|
|
22
|
+
}
|
|
23
|
+
export interface DividerItem {
|
|
24
|
+
type: 'horizontal-divider';
|
|
25
|
+
}
|
|
26
|
+
export type NavigationItem = LinkItem | ParentItem | DividerItem;
|
|
27
|
+
export type LogoType = {
|
|
28
|
+
src?: string;
|
|
29
|
+
title: string;
|
|
30
|
+
};
|
|
31
|
+
export interface NavGroupProps {
|
|
32
|
+
labelArea: Snippet<[ClassValue]>;
|
|
33
|
+
active?: boolean;
|
|
34
|
+
children?: Snippet;
|
|
35
|
+
class?: ClassValue;
|
|
36
|
+
}
|
|
37
|
+
export interface NavItemProps {
|
|
38
|
+
href: string;
|
|
39
|
+
active?: boolean;
|
|
40
|
+
children: Snippet<[ClassValue]>;
|
|
41
|
+
class?: ClassValue;
|
|
42
|
+
}
|
|
43
|
+
export interface SidebarProps {
|
|
44
|
+
items?: NavigationItem[];
|
|
45
|
+
logo: LogoType;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|