@noahbaumgartner/svelte-ui 0.0.1

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.
Files changed (149) hide show
  1. package/README.md +65 -0
  2. package/dist/components/alert-dialog/AlertDialog.svelte +161 -0
  3. package/dist/components/alert-dialog/AlertDialog.svelte.d.ts +20 -0
  4. package/dist/components/avatar/Avatar.svelte +85 -0
  5. package/dist/components/avatar/Avatar.svelte.d.ts +12 -0
  6. package/dist/components/badge/Badge.svelte +49 -0
  7. package/dist/components/badge/Badge.svelte.d.ts +12 -0
  8. package/dist/components/breadcrumb/Breadcrumb.svelte +37 -0
  9. package/dist/components/breadcrumb/Breadcrumb.svelte.d.ts +9 -0
  10. package/dist/components/breadcrumb/BreadcrumbEllipsis.svelte +32 -0
  11. package/dist/components/breadcrumb/BreadcrumbEllipsis.svelte.d.ts +10 -0
  12. package/dist/components/breadcrumb/BreadcrumbItem.svelte +71 -0
  13. package/dist/components/breadcrumb/BreadcrumbItem.svelte.d.ts +12 -0
  14. package/dist/components/breadcrumb/BreadcrumbSeparator.svelte +18 -0
  15. package/dist/components/breadcrumb/BreadcrumbSeparator.svelte.d.ts +18 -0
  16. package/dist/components/button/Button.svelte +247 -0
  17. package/dist/components/button/Button.svelte.d.ts +24 -0
  18. package/dist/components/button-group/ButtonGroup.svelte +89 -0
  19. package/dist/components/button-group/ButtonGroup.svelte.d.ts +12 -0
  20. package/dist/components/calendar/Calendar.svelte +401 -0
  21. package/dist/components/calendar/Calendar.svelte.d.ts +17 -0
  22. package/dist/components/calendar/dates.d.ts +12 -0
  23. package/dist/components/calendar/dates.js +48 -0
  24. package/dist/components/card/Card.svelte +55 -0
  25. package/dist/components/card/Card.svelte.d.ts +10 -0
  26. package/dist/components/card/CardAction.svelte +24 -0
  27. package/dist/components/card/CardAction.svelte.d.ts +9 -0
  28. package/dist/components/card/CardContent.svelte +20 -0
  29. package/dist/components/card/CardContent.svelte.d.ts +8 -0
  30. package/dist/components/card/CardDescription.svelte +23 -0
  31. package/dist/components/card/CardDescription.svelte.d.ts +8 -0
  32. package/dist/components/card/CardFooter.svelte +28 -0
  33. package/dist/components/card/CardFooter.svelte.d.ts +9 -0
  34. package/dist/components/card/CardHeader.svelte +29 -0
  35. package/dist/components/card/CardHeader.svelte.d.ts +9 -0
  36. package/dist/components/card/CardTitle.svelte +23 -0
  37. package/dist/components/card/CardTitle.svelte.d.ts +8 -0
  38. package/dist/components/checkbox/Checkbox.svelte +93 -0
  39. package/dist/components/checkbox/Checkbox.svelte.d.ts +5 -0
  40. package/dist/components/color-picker/ColorPicker.svelte +233 -0
  41. package/dist/components/color-picker/ColorPicker.svelte.d.ts +10 -0
  42. package/dist/components/context-menu/ContextMenu.svelte +201 -0
  43. package/dist/components/context-menu/ContextMenu.svelte.d.ts +14 -0
  44. package/dist/components/context-menu/ContextMenuItem.svelte +105 -0
  45. package/dist/components/context-menu/ContextMenuItem.svelte.d.ts +14 -0
  46. package/dist/components/context-menu/context.d.ts +14 -0
  47. package/dist/components/context-menu/context.js +2 -0
  48. package/dist/components/dialog/Dialog.svelte +225 -0
  49. package/dist/components/dialog/Dialog.svelte.d.ts +22 -0
  50. package/dist/components/field/Field.svelte +78 -0
  51. package/dist/components/field/Field.svelte.d.ts +11 -0
  52. package/dist/components/field/FieldContent.svelte +25 -0
  53. package/dist/components/field/FieldContent.svelte.d.ts +9 -0
  54. package/dist/components/field/FieldDescription.svelte +34 -0
  55. package/dist/components/field/FieldDescription.svelte.d.ts +9 -0
  56. package/dist/components/field/FieldError.svelte +45 -0
  57. package/dist/components/field/FieldError.svelte.d.ts +11 -0
  58. package/dist/components/field/FieldGroup.svelte +30 -0
  59. package/dist/components/field/FieldGroup.svelte.d.ts +9 -0
  60. package/dist/components/field/FieldLegend.svelte +34 -0
  61. package/dist/components/field/FieldLegend.svelte.d.ts +10 -0
  62. package/dist/components/field/FieldSeparator.svelte +31 -0
  63. package/dist/components/field/FieldSeparator.svelte.d.ts +9 -0
  64. package/dist/components/field/FieldSet.svelte +33 -0
  65. package/dist/components/field/FieldSet.svelte.d.ts +9 -0
  66. package/dist/components/input/Input.svelte +459 -0
  67. package/dist/components/input/Input.svelte.d.ts +10 -0
  68. package/dist/components/input-group/InputGroup.svelte +88 -0
  69. package/dist/components/input-group/InputGroup.svelte.d.ts +9 -0
  70. package/dist/components/input-group/InputGroupAddon.svelte +88 -0
  71. package/dist/components/input-group/InputGroupAddon.svelte.d.ts +11 -0
  72. package/dist/components/kbd/Kbd.svelte +46 -0
  73. package/dist/components/kbd/Kbd.svelte.d.ts +8 -0
  74. package/dist/components/kbd/KbdGroup.svelte +27 -0
  75. package/dist/components/kbd/KbdGroup.svelte.d.ts +8 -0
  76. package/dist/components/label/Label.svelte +48 -0
  77. package/dist/components/label/Label.svelte.d.ts +9 -0
  78. package/dist/components/link/Link.svelte +40 -0
  79. package/dist/components/link/Link.svelte.d.ts +10 -0
  80. package/dist/components/navbar/Navbar.svelte +266 -0
  81. package/dist/components/navbar/Navbar.svelte.d.ts +26 -0
  82. package/dist/components/navigation-menu/NavigationMenu.svelte +95 -0
  83. package/dist/components/navigation-menu/NavigationMenu.svelte.d.ts +12 -0
  84. package/dist/components/navigation-menu/NavigationMenuItem.svelte +257 -0
  85. package/dist/components/navigation-menu/NavigationMenuItem.svelte.d.ts +15 -0
  86. package/dist/components/navigation-menu/NavigationMenuLink.svelte +91 -0
  87. package/dist/components/navigation-menu/NavigationMenuLink.svelte.d.ts +16 -0
  88. package/dist/components/navigation-menu/context.d.ts +15 -0
  89. package/dist/components/navigation-menu/context.js +2 -0
  90. package/dist/components/popover/Popover.svelte +295 -0
  91. package/dist/components/popover/Popover.svelte.d.ts +34 -0
  92. package/dist/components/radio-group/RadioGroup.svelte +86 -0
  93. package/dist/components/radio-group/RadioGroup.svelte.d.ts +16 -0
  94. package/dist/components/radio-group/RadioGroupItem.svelte +95 -0
  95. package/dist/components/radio-group/RadioGroupItem.svelte.d.ts +7 -0
  96. package/dist/components/radio-group/context.d.ts +9 -0
  97. package/dist/components/radio-group/context.js +2 -0
  98. package/dist/components/separator/Separator.svelte +41 -0
  99. package/dist/components/separator/Separator.svelte.d.ts +10 -0
  100. package/dist/components/sidebar/Sidebar.svelte +180 -0
  101. package/dist/components/sidebar/Sidebar.svelte.d.ts +14 -0
  102. package/dist/components/sidebar/SidebarContent.svelte +30 -0
  103. package/dist/components/sidebar/SidebarContent.svelte.d.ts +9 -0
  104. package/dist/components/sidebar/SidebarFooter.svelte +23 -0
  105. package/dist/components/sidebar/SidebarFooter.svelte.d.ts +8 -0
  106. package/dist/components/sidebar/SidebarGroup.svelte +55 -0
  107. package/dist/components/sidebar/SidebarGroup.svelte.d.ts +11 -0
  108. package/dist/components/sidebar/SidebarHeader.svelte +23 -0
  109. package/dist/components/sidebar/SidebarHeader.svelte.d.ts +8 -0
  110. package/dist/components/sidebar/SidebarInset.svelte +25 -0
  111. package/dist/components/sidebar/SidebarInset.svelte.d.ts +9 -0
  112. package/dist/components/sidebar/SidebarMenu.svelte +27 -0
  113. package/dist/components/sidebar/SidebarMenu.svelte.d.ts +9 -0
  114. package/dist/components/sidebar/SidebarMenuItem.svelte +221 -0
  115. package/dist/components/sidebar/SidebarMenuItem.svelte.d.ts +23 -0
  116. package/dist/components/sidebar/SidebarProvider.svelte +62 -0
  117. package/dist/components/sidebar/SidebarProvider.svelte.d.ts +11 -0
  118. package/dist/components/sidebar/SidebarSeparator.svelte +5 -0
  119. package/dist/components/sidebar/SidebarSeparator.svelte.d.ts +18 -0
  120. package/dist/components/sidebar/SidebarTrigger.svelte +24 -0
  121. package/dist/components/sidebar/SidebarTrigger.svelte.d.ts +7 -0
  122. package/dist/components/sidebar/context.d.ts +15 -0
  123. package/dist/components/sidebar/context.js +2 -0
  124. package/dist/components/skeleton/Skeleton.svelte +30 -0
  125. package/dist/components/skeleton/Skeleton.svelte.d.ts +5 -0
  126. package/dist/components/spinner/Spinner.svelte +72 -0
  127. package/dist/components/spinner/Spinner.svelte.d.ts +9 -0
  128. package/dist/components/switch/Switch.svelte +88 -0
  129. package/dist/components/switch/Switch.svelte.d.ts +5 -0
  130. package/dist/components/table/Table.svelte +276 -0
  131. package/dist/components/table/Table.svelte.d.ts +60 -0
  132. package/dist/components/textarea/Textarea.svelte +57 -0
  133. package/dist/components/textarea/Textarea.svelte.d.ts +4 -0
  134. package/dist/components/time-picker/TimePicker.svelte +162 -0
  135. package/dist/components/time-picker/TimePicker.svelte.d.ts +11 -0
  136. package/dist/components/toaster/Toaster.svelte +151 -0
  137. package/dist/components/toaster/Toaster.svelte.d.ts +7 -0
  138. package/dist/components/toaster/toast.svelte.d.ts +28 -0
  139. package/dist/components/toaster/toast.svelte.js +22 -0
  140. package/dist/components/toggle/Toggle.svelte +31 -0
  141. package/dist/components/toggle/Toggle.svelte.d.ts +8 -0
  142. package/dist/components/tooltip/Tooltip.svelte +224 -0
  143. package/dist/components/tooltip/Tooltip.svelte.d.ts +27 -0
  144. package/dist/icons.d.ts +2 -0
  145. package/dist/icons.js +4 -0
  146. package/dist/index.d.ts +73 -0
  147. package/dist/index.js +66 -0
  148. package/dist/styles/tokens.css +50 -0
  149. package/package.json +88 -0
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+
5
+ type Props = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
6
+ /** The page content next to the sidebar. */
7
+ children: Snippet;
8
+ };
9
+
10
+ let { class: className, children, ...rest }: Props = $props();
11
+ </script>
12
+
13
+ <main {...rest} class={['sidebar-inset', className]}>
14
+ {@render children()}
15
+ </main>
16
+
17
+ <style>
18
+ .sidebar-inset {
19
+ display: flex;
20
+ flex-direction: column;
21
+ flex: 1;
22
+ min-width: 0;
23
+ background-color: var(--color-bg);
24
+ }
25
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type Props = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
4
+ /** The page content next to the sidebar. */
5
+ children: Snippet;
6
+ };
7
+ declare const SidebarInset: import("svelte").Component<Props, {}, "">;
8
+ type SidebarInset = ReturnType<typeof SidebarInset>;
9
+ export default SidebarInset;
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+
5
+ type Props = Omit<HTMLAttributes<HTMLUListElement>, 'children'> & {
6
+ /** `SidebarMenuItem`s. */
7
+ children: Snippet;
8
+ };
9
+
10
+ let { class: className, children, ...rest }: Props = $props();
11
+ </script>
12
+
13
+ <ul {...rest} class={['sidebar-menu', className]}>
14
+ {@render children()}
15
+ </ul>
16
+
17
+ <style>
18
+ .sidebar-menu {
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: 2px;
22
+ min-width: 0;
23
+ margin: 0;
24
+ padding: 0;
25
+ list-style: none;
26
+ }
27
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type Props = Omit<HTMLAttributes<HTMLUListElement>, 'children'> & {
4
+ /** `SidebarMenuItem`s. */
5
+ children: Snippet;
6
+ };
7
+ declare const SidebarMenu: import("svelte").Component<Props, {}, "">;
8
+ type SidebarMenu = ReturnType<typeof SidebarMenu>;
9
+ export default SidebarMenu;
@@ -0,0 +1,221 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { slide } from 'svelte/transition';
4
+ import type { LucideIcon } from '@lucide/svelte';
5
+ import ChevronRight from '@lucide/svelte/icons/chevron-right';
6
+ import Ellipsis from '@lucide/svelte/icons/ellipsis';
7
+ import Button from '../button/Button.svelte';
8
+ import ContextMenu from '../context-menu/ContextMenu.svelte';
9
+
10
+ type Props = {
11
+ /** Links the item. Ignored when the item has `items`. */
12
+ href?: string;
13
+ /** Any Lucide icon (import from `svelte-ui/icons`). Stays visible when the sidebar is collapsed to icons. */
14
+ icon?: LucideIcon;
15
+ /** Marks the item as the current page. */
16
+ active?: boolean;
17
+ /** Short count or status at the end of the item, e.g. unread messages. */
18
+ badge?: string | number;
19
+ /** Nested `SidebarMenuItem`s, shown in a submenu the item toggles. */
20
+ items?: Snippet;
21
+ /** Whether the submenu is open. Bindable. */
22
+ expanded?: boolean;
23
+ /** `ContextMenuItem`s for a menu revealed on hover. */
24
+ menu?: Snippet;
25
+ onclick?: () => void;
26
+ children: Snippet;
27
+ };
28
+
29
+ let {
30
+ href,
31
+ icon: Icon,
32
+ active = false,
33
+ badge,
34
+ items,
35
+ expanded = $bindable(false),
36
+ menu,
37
+ onclick,
38
+ children
39
+ }: Props = $props();
40
+
41
+ const id = $props.id();
42
+ let classes = $derived(['sidebar-menu-button', { 'sidebar-menu-button--active': active }]);
43
+
44
+ function toggle() {
45
+ expanded = !expanded;
46
+ onclick?.();
47
+ }
48
+ </script>
49
+
50
+ {#snippet content()}
51
+ {#if Icon}<Icon class="sidebar-menu-item-icon" aria-hidden="true" />{/if}
52
+ <span class="sidebar-menu-item-label">{@render children()}</span>
53
+ {#if badge !== undefined}<span class="sidebar-menu-item-badge">{badge}</span>{/if}
54
+ {#if items}<ChevronRight class="sidebar-menu-item-chevron" aria-hidden="true" />{/if}
55
+ {/snippet}
56
+
57
+ <li class={['sidebar-menu-item', { 'sidebar-menu-item--has-menu': menu }]}>
58
+ {#if items}
59
+ <button
60
+ type="button"
61
+ aria-expanded={expanded}
62
+ aria-controls={expanded ? `${id}-items` : undefined}
63
+ class={classes}
64
+ onclick={toggle}
65
+ >
66
+ {@render content()}
67
+ </button>
68
+ {:else if href}
69
+ <!-- href comes from the consumer, who resolves it; this library has no routes -->
70
+ <!-- eslint-disable-next-line svelte/no-navigation-without-resolve -->
71
+ <a {href} aria-current={active ? 'page' : undefined} class={classes} {onclick}>
72
+ {@render content()}
73
+ </a>
74
+ {:else}
75
+ <button type="button" aria-current={active ? 'page' : undefined} class={classes} {onclick}>
76
+ {@render content()}
77
+ </button>
78
+ {/if}
79
+
80
+ {#if menu}
81
+ <div class="sidebar-menu-item-menu">
82
+ <ContextMenu align="end">
83
+ {#snippet trigger(props)}
84
+ <Button {...props} variant="ghost" size="sm" icon={Ellipsis} label="More" />
85
+ {/snippet}
86
+ {@render menu()}
87
+ </ContextMenu>
88
+ </div>
89
+ {/if}
90
+
91
+ {#if items && expanded}
92
+ <ul id="{id}-items" class="sidebar-menu-sub" transition:slide={{ duration: 200 }}>
93
+ {@render items()}
94
+ </ul>
95
+ {/if}
96
+ </li>
97
+
98
+ <style>
99
+ .sidebar-menu-item {
100
+ position: relative;
101
+ min-width: 0;
102
+ }
103
+
104
+ .sidebar-menu-button {
105
+ display: flex;
106
+ align-items: center;
107
+ gap: 8px;
108
+ width: 100%;
109
+ height: 32px;
110
+ padding: 0 8px;
111
+ box-sizing: border-box;
112
+ overflow: hidden;
113
+ background-color: transparent;
114
+ border: none;
115
+ border-radius: 8px;
116
+ font: inherit;
117
+ font-size: 13px;
118
+ color: var(--color-text-secondary);
119
+ text-align: left;
120
+ text-decoration: none;
121
+ outline: none;
122
+ cursor: pointer;
123
+ transition:
124
+ background-color 150ms ease,
125
+ color 150ms ease,
126
+ width 200ms ease;
127
+ }
128
+
129
+ .sidebar-menu-button:hover {
130
+ background-color: var(--color-surface);
131
+ color: var(--color-text);
132
+ }
133
+
134
+ .sidebar-menu-button:focus-visible {
135
+ outline: 2px solid var(--color-accent);
136
+ outline-offset: -2px;
137
+ }
138
+
139
+ .sidebar-menu-button--active {
140
+ background-color: var(--color-surface);
141
+ color: var(--color-text);
142
+ font-weight: 500;
143
+ }
144
+
145
+ .sidebar-menu-item--has-menu > .sidebar-menu-button {
146
+ padding-right: 36px;
147
+ }
148
+
149
+ .sidebar-menu-item :global(.sidebar-menu-item-icon) {
150
+ width: 16px;
151
+ height: 16px;
152
+ flex-shrink: 0;
153
+ }
154
+
155
+ .sidebar-menu-item-label {
156
+ flex: 1;
157
+ min-width: 0;
158
+ overflow: hidden;
159
+ white-space: nowrap;
160
+ text-overflow: ellipsis;
161
+ }
162
+
163
+ .sidebar-menu-item-badge {
164
+ font-size: 12px;
165
+ font-variant-numeric: tabular-nums;
166
+ color: var(--color-text-muted);
167
+ }
168
+
169
+ .sidebar-menu-item :global(.sidebar-menu-item-chevron) {
170
+ width: 14px;
171
+ height: 14px;
172
+ flex-shrink: 0;
173
+ color: var(--color-text-muted);
174
+ transition: rotate 200ms ease;
175
+ }
176
+
177
+ .sidebar-menu-button[aria-expanded='true'] :global(.sidebar-menu-item-chevron) {
178
+ rotate: 90deg;
179
+ }
180
+
181
+ /* Menu trigger, shown while the item is hovered or the menu has focus */
182
+ .sidebar-menu-item-menu {
183
+ position: absolute;
184
+ top: 2px;
185
+ right: 2px;
186
+ opacity: 0;
187
+ transition: opacity 150ms ease;
188
+ }
189
+
190
+ .sidebar-menu-item:hover > .sidebar-menu-item-menu,
191
+ .sidebar-menu-item-menu:focus-within {
192
+ opacity: 1;
193
+ }
194
+
195
+ @media (hover: none) {
196
+ .sidebar-menu-item-menu {
197
+ opacity: 1;
198
+ }
199
+ }
200
+
201
+ .sidebar-menu-sub {
202
+ display: flex;
203
+ flex-direction: column;
204
+ gap: 2px;
205
+ margin: 2px 0 2px 15px;
206
+ padding: 0 0 0 8px;
207
+ list-style: none;
208
+ border-left: 1px solid var(--color-border);
209
+ }
210
+
211
+ /* Collapsed to icons: a square button with the label clipped (it stays the accessible name) */
212
+ :global(.sidebar--collapsed-icon) .sidebar-menu-item .sidebar-menu-button {
213
+ width: 32px;
214
+ padding: 0 8px;
215
+ }
216
+
217
+ :global(.sidebar--collapsed-icon) .sidebar-menu-item-menu,
218
+ :global(.sidebar--collapsed-icon) .sidebar-menu-sub {
219
+ display: none;
220
+ }
221
+ </style>
@@ -0,0 +1,23 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { LucideIcon } from '@lucide/svelte';
3
+ type Props = {
4
+ /** Links the item. Ignored when the item has `items`. */
5
+ href?: string;
6
+ /** Any Lucide icon (import from `svelte-ui/icons`). Stays visible when the sidebar is collapsed to icons. */
7
+ icon?: LucideIcon;
8
+ /** Marks the item as the current page. */
9
+ active?: boolean;
10
+ /** Short count or status at the end of the item, e.g. unread messages. */
11
+ badge?: string | number;
12
+ /** Nested `SidebarMenuItem`s, shown in a submenu the item toggles. */
13
+ items?: Snippet;
14
+ /** Whether the submenu is open. Bindable. */
15
+ expanded?: boolean;
16
+ /** `ContextMenuItem`s for a menu revealed on hover. */
17
+ menu?: Snippet;
18
+ onclick?: () => void;
19
+ children: Snippet;
20
+ };
21
+ declare const SidebarMenuItem: import("svelte").Component<Props, {}, "expanded">;
22
+ type SidebarMenuItem = ReturnType<typeof SidebarMenuItem>;
23
+ export default SidebarMenuItem;
@@ -0,0 +1,62 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import { setSidebarContext } from './context.js';
5
+
6
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
7
+ /** Whether the sidebar is expanded on desktop. Bindable. */
8
+ open?: boolean;
9
+ /** A `Sidebar` and a `SidebarInset`. Size them with the `--sidebar-width`, `--sidebar-width-icon` and `--sidebar-height` custom properties. */
10
+ children: Snippet;
11
+ };
12
+
13
+ let { open = $bindable(true), class: className, children, ...rest }: Props = $props();
14
+
15
+ const id = $props.id();
16
+ let mobile = $state(false);
17
+ let openMobile = $state(false);
18
+
19
+ setSidebarContext({
20
+ id: `${id}-sidebar`,
21
+ get open() {
22
+ return open;
23
+ },
24
+ get mobile() {
25
+ return mobile;
26
+ },
27
+ get openMobile() {
28
+ return openMobile;
29
+ },
30
+ toggle: () => {
31
+ if (mobile) openMobile = !openMobile;
32
+ else open = !open;
33
+ },
34
+ closeMobile: () => (openMobile = false)
35
+ });
36
+
37
+ $effect(() => {
38
+ const query = window.matchMedia('(max-width: 767px)');
39
+ const update = () => {
40
+ mobile = query.matches;
41
+ openMobile = false;
42
+ };
43
+ update();
44
+ query.addEventListener('change', update);
45
+ return () => query.removeEventListener('change', update);
46
+ });
47
+ </script>
48
+
49
+ <div {...rest} class={['sidebar-provider', className]}>
50
+ {@render children()}
51
+ </div>
52
+
53
+ <style>
54
+ .sidebar-provider {
55
+ --sidebar-width: 256px;
56
+ --sidebar-width-icon: 48px;
57
+ --sidebar-height: 100svh;
58
+ display: flex;
59
+ width: 100%;
60
+ min-height: var(--sidebar-height);
61
+ }
62
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
4
+ /** Whether the sidebar is expanded on desktop. Bindable. */
5
+ open?: boolean;
6
+ /** A `Sidebar` and a `SidebarInset`. Size them with the `--sidebar-width`, `--sidebar-width-icon` and `--sidebar-height` custom properties. */
7
+ children: Snippet;
8
+ };
9
+ declare const SidebarProvider: import("svelte").Component<Props, {}, "open">;
10
+ type SidebarProvider = ReturnType<typeof SidebarProvider>;
11
+ export default SidebarProvider;
@@ -0,0 +1,5 @@
1
+ <script lang="ts">
2
+ import Separator from '../separator/Separator.svelte';
3
+ </script>
4
+
5
+ <Separator decorative={false} style="margin: 0 8px;" />
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const SidebarSeparator: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type SidebarSeparator = InstanceType<typeof SidebarSeparator>;
18
+ export default SidebarSeparator;
@@ -0,0 +1,24 @@
1
+ <script lang="ts">
2
+ import PanelLeft from '@lucide/svelte/icons/panel-left';
3
+ import Button from '../button/Button.svelte';
4
+ import { getSidebarContext } from './context.js';
5
+
6
+ type Props = {
7
+ /** Accessible name of the button. */
8
+ label?: string;
9
+ };
10
+
11
+ let { label = 'Toggle sidebar' }: Props = $props();
12
+
13
+ const sidebar = getSidebarContext();
14
+ </script>
15
+
16
+ <Button
17
+ variant="ghost"
18
+ size="sm"
19
+ icon={PanelLeft}
20
+ {label}
21
+ aria-controls={sidebar.id}
22
+ aria-expanded={sidebar.mobile ? sidebar.openMobile : sidebar.open}
23
+ onclick={sidebar.toggle}
24
+ />
@@ -0,0 +1,7 @@
1
+ type Props = {
2
+ /** Accessible name of the button. */
3
+ label?: string;
4
+ };
5
+ declare const SidebarTrigger: import("svelte").Component<Props, {}, "">;
6
+ type SidebarTrigger = ReturnType<typeof SidebarTrigger>;
7
+ export default SidebarTrigger;
@@ -0,0 +1,15 @@
1
+ export type SidebarContext = {
2
+ /** Id of the sidebar element. */
3
+ readonly id: string;
4
+ /** Whether the sidebar is expanded on desktop. */
5
+ readonly open: boolean;
6
+ /** Below 768px the sidebar turns into a drawer. */
7
+ readonly mobile: boolean;
8
+ /** Whether the drawer is shown on mobile. */
9
+ readonly openMobile: boolean;
10
+ /** Collapses or expands the sidebar, or opens or closes the drawer on mobile. */
11
+ toggle: () => void;
12
+ /** Closes the drawer on mobile, e.g. after navigating. */
13
+ closeMobile: () => void;
14
+ };
15
+ export declare const getSidebarContext: () => SidebarContext, setSidebarContext: (context: SidebarContext) => SidebarContext;
@@ -0,0 +1,2 @@
1
+ import { createContext } from 'svelte';
2
+ export const [getSidebarContext, setSidebarContext] = createContext();
@@ -0,0 +1,30 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+
4
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'>;
5
+
6
+ let { class: className, ...rest }: Props = $props();
7
+ </script>
8
+
9
+ <div {...rest} aria-hidden="true" class={['skeleton', className]}></div>
10
+
11
+ <style>
12
+ .skeleton {
13
+ flex-shrink: 0;
14
+ border-radius: 6px;
15
+ background-color: var(--color-surface);
16
+ animation: skeleton-pulse 2s ease-in-out infinite;
17
+ }
18
+
19
+ @keyframes skeleton-pulse {
20
+ 50% {
21
+ opacity: 0.5;
22
+ }
23
+ }
24
+
25
+ @media (prefers-reduced-motion: reduce) {
26
+ .skeleton {
27
+ animation: none;
28
+ }
29
+ }
30
+ </style>
@@ -0,0 +1,5 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'>;
3
+ declare const Skeleton: import("svelte").Component<Props, {}, "">;
4
+ type Skeleton = ReturnType<typeof Skeleton>;
5
+ export default Skeleton;
@@ -0,0 +1,72 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+
4
+ type Props = Omit<HTMLAttributes<HTMLSpanElement>, 'children'> & {
5
+ size?: 'sm' | 'md' | 'lg';
6
+ /** Accessible name. Ignored when the spinner is `aria-hidden`, e.g. inside a loading Button. */
7
+ label?: string;
8
+ };
9
+
10
+ let { size = 'md', label = 'Loading', class: className, ...rest }: Props = $props();
11
+ </script>
12
+
13
+ <span {...rest} role="status" aria-label={label} class={['spinner', `spinner--${size}`, className]}
14
+ ></span>
15
+
16
+ <style>
17
+ @property --spinner-angle {
18
+ syntax: '<angle>';
19
+ initial-value: 0deg;
20
+ inherits: false;
21
+ }
22
+
23
+ .spinner {
24
+ --spinner-size: 16px;
25
+ position: relative;
26
+ display: inline-block;
27
+ flex-shrink: 0;
28
+ width: var(--spinner-size);
29
+ height: var(--spinner-size);
30
+ }
31
+
32
+ .spinner--sm {
33
+ --spinner-size: 14px;
34
+ }
35
+
36
+ .spinner--lg {
37
+ --spinner-size: 20px;
38
+ }
39
+
40
+ .spinner::before,
41
+ .spinner::after {
42
+ content: '';
43
+ position: absolute;
44
+ inset: 0;
45
+ box-sizing: border-box;
46
+ border: calc(var(--spinner-size) / 8) solid currentColor;
47
+ border-radius: 30%;
48
+ }
49
+
50
+ /* Track */
51
+ .spinner::before {
52
+ opacity: 0.2;
53
+ }
54
+
55
+ /* Handle: a conic mask whose head is opaque and whose tail fades out */
56
+ .spinner::after {
57
+ mask: conic-gradient(from var(--spinner-angle), transparent 0% 60%, #000 100%);
58
+ animation: spinner-rotate 1s linear infinite;
59
+ }
60
+
61
+ @keyframes spinner-rotate {
62
+ to {
63
+ --spinner-angle: 360deg;
64
+ }
65
+ }
66
+
67
+ @media (prefers-reduced-motion: reduce) {
68
+ .spinner::after {
69
+ animation-duration: 3s;
70
+ }
71
+ }
72
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Props = Omit<HTMLAttributes<HTMLSpanElement>, 'children'> & {
3
+ size?: 'sm' | 'md' | 'lg';
4
+ /** Accessible name. Ignored when the spinner is `aria-hidden`, e.g. inside a loading Button. */
5
+ label?: string;
6
+ };
7
+ declare const Spinner: import("svelte").Component<Props, {}, "">;
8
+ type Spinner = ReturnType<typeof Spinner>;
9
+ export default Spinner;
@@ -0,0 +1,88 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+
4
+ type Props = Omit<HTMLInputAttributes, 'type' | 'role'>;
5
+
6
+ let { checked = $bindable(false), class: className, ...rest }: Props = $props();
7
+ </script>
8
+
9
+ <span class={['switch', className]}>
10
+ <input {...rest} type="checkbox" role="switch" bind:checked />
11
+ <span class="switch-thumb" aria-hidden="true"></span>
12
+ </span>
13
+
14
+ <style>
15
+ .switch {
16
+ position: relative;
17
+ display: inline-flex;
18
+ flex-shrink: 0;
19
+ width: 36px;
20
+ height: 22px;
21
+ }
22
+
23
+ input {
24
+ appearance: none;
25
+ box-sizing: border-box;
26
+ width: 100%;
27
+ height: 100%;
28
+ margin: 0;
29
+ background-color: var(--color-surface);
30
+ border: 1px solid var(--color-border);
31
+ border-radius: 9px;
32
+ outline: none;
33
+ cursor: pointer;
34
+ transition:
35
+ background-color 200ms ease,
36
+ border-color 200ms ease;
37
+ }
38
+
39
+ input:checked {
40
+ background-color: var(--color-accent);
41
+ border-color: var(--color-accent);
42
+ }
43
+
44
+ input:focus-visible {
45
+ outline: 2px solid var(--color-accent);
46
+ outline-offset: 2px;
47
+ }
48
+
49
+ input[aria-invalid='true'] {
50
+ border-color: var(--color-destructive);
51
+ }
52
+
53
+ input:disabled {
54
+ cursor: not-allowed;
55
+ opacity: 0.5;
56
+ }
57
+
58
+ /* Radius is the track's 9px minus the 4px inset, so the corners stay concentric */
59
+ .switch-thumb {
60
+ position: absolute;
61
+ top: 4px;
62
+ left: 4px;
63
+ width: 14px;
64
+ height: 14px;
65
+ background-color: var(--color-base);
66
+ border-radius: 5px;
67
+ pointer-events: none;
68
+ transition:
69
+ transform 200ms ease,
70
+ background-color 200ms ease;
71
+ }
72
+
73
+ input:checked + .switch-thumb {
74
+ background-color: var(--color-accent-foreground);
75
+ transform: translateX(14px);
76
+ }
77
+
78
+ input:disabled + .switch-thumb {
79
+ opacity: 0.5;
80
+ }
81
+
82
+ @media (prefers-reduced-motion: reduce) {
83
+ input,
84
+ .switch-thumb {
85
+ transition: none;
86
+ }
87
+ }
88
+ </style>
@@ -0,0 +1,5 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ type Props = Omit<HTMLInputAttributes, 'type' | 'role'>;
3
+ declare const Switch: import("svelte").Component<Props, {}, "checked">;
4
+ type Switch = ReturnType<typeof Switch>;
5
+ export default Switch;