@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.
- package/README.md +65 -0
- package/dist/components/alert-dialog/AlertDialog.svelte +161 -0
- package/dist/components/alert-dialog/AlertDialog.svelte.d.ts +20 -0
- package/dist/components/avatar/Avatar.svelte +85 -0
- package/dist/components/avatar/Avatar.svelte.d.ts +12 -0
- package/dist/components/badge/Badge.svelte +49 -0
- package/dist/components/badge/Badge.svelte.d.ts +12 -0
- package/dist/components/breadcrumb/Breadcrumb.svelte +37 -0
- package/dist/components/breadcrumb/Breadcrumb.svelte.d.ts +9 -0
- package/dist/components/breadcrumb/BreadcrumbEllipsis.svelte +32 -0
- package/dist/components/breadcrumb/BreadcrumbEllipsis.svelte.d.ts +10 -0
- package/dist/components/breadcrumb/BreadcrumbItem.svelte +71 -0
- package/dist/components/breadcrumb/BreadcrumbItem.svelte.d.ts +12 -0
- package/dist/components/breadcrumb/BreadcrumbSeparator.svelte +18 -0
- package/dist/components/breadcrumb/BreadcrumbSeparator.svelte.d.ts +18 -0
- package/dist/components/button/Button.svelte +247 -0
- package/dist/components/button/Button.svelte.d.ts +24 -0
- package/dist/components/button-group/ButtonGroup.svelte +89 -0
- package/dist/components/button-group/ButtonGroup.svelte.d.ts +12 -0
- package/dist/components/calendar/Calendar.svelte +401 -0
- package/dist/components/calendar/Calendar.svelte.d.ts +17 -0
- package/dist/components/calendar/dates.d.ts +12 -0
- package/dist/components/calendar/dates.js +48 -0
- package/dist/components/card/Card.svelte +55 -0
- package/dist/components/card/Card.svelte.d.ts +10 -0
- package/dist/components/card/CardAction.svelte +24 -0
- package/dist/components/card/CardAction.svelte.d.ts +9 -0
- package/dist/components/card/CardContent.svelte +20 -0
- package/dist/components/card/CardContent.svelte.d.ts +8 -0
- package/dist/components/card/CardDescription.svelte +23 -0
- package/dist/components/card/CardDescription.svelte.d.ts +8 -0
- package/dist/components/card/CardFooter.svelte +28 -0
- package/dist/components/card/CardFooter.svelte.d.ts +9 -0
- package/dist/components/card/CardHeader.svelte +29 -0
- package/dist/components/card/CardHeader.svelte.d.ts +9 -0
- package/dist/components/card/CardTitle.svelte +23 -0
- package/dist/components/card/CardTitle.svelte.d.ts +8 -0
- package/dist/components/checkbox/Checkbox.svelte +93 -0
- package/dist/components/checkbox/Checkbox.svelte.d.ts +5 -0
- package/dist/components/color-picker/ColorPicker.svelte +233 -0
- package/dist/components/color-picker/ColorPicker.svelte.d.ts +10 -0
- package/dist/components/context-menu/ContextMenu.svelte +201 -0
- package/dist/components/context-menu/ContextMenu.svelte.d.ts +14 -0
- package/dist/components/context-menu/ContextMenuItem.svelte +105 -0
- package/dist/components/context-menu/ContextMenuItem.svelte.d.ts +14 -0
- package/dist/components/context-menu/context.d.ts +14 -0
- package/dist/components/context-menu/context.js +2 -0
- package/dist/components/dialog/Dialog.svelte +225 -0
- package/dist/components/dialog/Dialog.svelte.d.ts +22 -0
- package/dist/components/field/Field.svelte +78 -0
- package/dist/components/field/Field.svelte.d.ts +11 -0
- package/dist/components/field/FieldContent.svelte +25 -0
- package/dist/components/field/FieldContent.svelte.d.ts +9 -0
- package/dist/components/field/FieldDescription.svelte +34 -0
- package/dist/components/field/FieldDescription.svelte.d.ts +9 -0
- package/dist/components/field/FieldError.svelte +45 -0
- package/dist/components/field/FieldError.svelte.d.ts +11 -0
- package/dist/components/field/FieldGroup.svelte +30 -0
- package/dist/components/field/FieldGroup.svelte.d.ts +9 -0
- package/dist/components/field/FieldLegend.svelte +34 -0
- package/dist/components/field/FieldLegend.svelte.d.ts +10 -0
- package/dist/components/field/FieldSeparator.svelte +31 -0
- package/dist/components/field/FieldSeparator.svelte.d.ts +9 -0
- package/dist/components/field/FieldSet.svelte +33 -0
- package/dist/components/field/FieldSet.svelte.d.ts +9 -0
- package/dist/components/input/Input.svelte +459 -0
- package/dist/components/input/Input.svelte.d.ts +10 -0
- package/dist/components/input-group/InputGroup.svelte +88 -0
- package/dist/components/input-group/InputGroup.svelte.d.ts +9 -0
- package/dist/components/input-group/InputGroupAddon.svelte +88 -0
- package/dist/components/input-group/InputGroupAddon.svelte.d.ts +11 -0
- package/dist/components/kbd/Kbd.svelte +46 -0
- package/dist/components/kbd/Kbd.svelte.d.ts +8 -0
- package/dist/components/kbd/KbdGroup.svelte +27 -0
- package/dist/components/kbd/KbdGroup.svelte.d.ts +8 -0
- package/dist/components/label/Label.svelte +48 -0
- package/dist/components/label/Label.svelte.d.ts +9 -0
- package/dist/components/link/Link.svelte +40 -0
- package/dist/components/link/Link.svelte.d.ts +10 -0
- package/dist/components/navbar/Navbar.svelte +266 -0
- package/dist/components/navbar/Navbar.svelte.d.ts +26 -0
- package/dist/components/navigation-menu/NavigationMenu.svelte +95 -0
- package/dist/components/navigation-menu/NavigationMenu.svelte.d.ts +12 -0
- package/dist/components/navigation-menu/NavigationMenuItem.svelte +257 -0
- package/dist/components/navigation-menu/NavigationMenuItem.svelte.d.ts +15 -0
- package/dist/components/navigation-menu/NavigationMenuLink.svelte +91 -0
- package/dist/components/navigation-menu/NavigationMenuLink.svelte.d.ts +16 -0
- package/dist/components/navigation-menu/context.d.ts +15 -0
- package/dist/components/navigation-menu/context.js +2 -0
- package/dist/components/popover/Popover.svelte +295 -0
- package/dist/components/popover/Popover.svelte.d.ts +34 -0
- package/dist/components/radio-group/RadioGroup.svelte +86 -0
- package/dist/components/radio-group/RadioGroup.svelte.d.ts +16 -0
- package/dist/components/radio-group/RadioGroupItem.svelte +95 -0
- package/dist/components/radio-group/RadioGroupItem.svelte.d.ts +7 -0
- package/dist/components/radio-group/context.d.ts +9 -0
- package/dist/components/radio-group/context.js +2 -0
- package/dist/components/separator/Separator.svelte +41 -0
- package/dist/components/separator/Separator.svelte.d.ts +10 -0
- package/dist/components/sidebar/Sidebar.svelte +180 -0
- package/dist/components/sidebar/Sidebar.svelte.d.ts +14 -0
- package/dist/components/sidebar/SidebarContent.svelte +30 -0
- package/dist/components/sidebar/SidebarContent.svelte.d.ts +9 -0
- package/dist/components/sidebar/SidebarFooter.svelte +23 -0
- package/dist/components/sidebar/SidebarFooter.svelte.d.ts +8 -0
- package/dist/components/sidebar/SidebarGroup.svelte +55 -0
- package/dist/components/sidebar/SidebarGroup.svelte.d.ts +11 -0
- package/dist/components/sidebar/SidebarHeader.svelte +23 -0
- package/dist/components/sidebar/SidebarHeader.svelte.d.ts +8 -0
- package/dist/components/sidebar/SidebarInset.svelte +25 -0
- package/dist/components/sidebar/SidebarInset.svelte.d.ts +9 -0
- package/dist/components/sidebar/SidebarMenu.svelte +27 -0
- package/dist/components/sidebar/SidebarMenu.svelte.d.ts +9 -0
- package/dist/components/sidebar/SidebarMenuItem.svelte +221 -0
- package/dist/components/sidebar/SidebarMenuItem.svelte.d.ts +23 -0
- package/dist/components/sidebar/SidebarProvider.svelte +62 -0
- package/dist/components/sidebar/SidebarProvider.svelte.d.ts +11 -0
- package/dist/components/sidebar/SidebarSeparator.svelte +5 -0
- package/dist/components/sidebar/SidebarSeparator.svelte.d.ts +18 -0
- package/dist/components/sidebar/SidebarTrigger.svelte +24 -0
- package/dist/components/sidebar/SidebarTrigger.svelte.d.ts +7 -0
- package/dist/components/sidebar/context.d.ts +15 -0
- package/dist/components/sidebar/context.js +2 -0
- package/dist/components/skeleton/Skeleton.svelte +30 -0
- package/dist/components/skeleton/Skeleton.svelte.d.ts +5 -0
- package/dist/components/spinner/Spinner.svelte +72 -0
- package/dist/components/spinner/Spinner.svelte.d.ts +9 -0
- package/dist/components/switch/Switch.svelte +88 -0
- package/dist/components/switch/Switch.svelte.d.ts +5 -0
- package/dist/components/table/Table.svelte +276 -0
- package/dist/components/table/Table.svelte.d.ts +60 -0
- package/dist/components/textarea/Textarea.svelte +57 -0
- package/dist/components/textarea/Textarea.svelte.d.ts +4 -0
- package/dist/components/time-picker/TimePicker.svelte +162 -0
- package/dist/components/time-picker/TimePicker.svelte.d.ts +11 -0
- package/dist/components/toaster/Toaster.svelte +151 -0
- package/dist/components/toaster/Toaster.svelte.d.ts +7 -0
- package/dist/components/toaster/toast.svelte.d.ts +28 -0
- package/dist/components/toaster/toast.svelte.js +22 -0
- package/dist/components/toggle/Toggle.svelte +31 -0
- package/dist/components/toggle/Toggle.svelte.d.ts +8 -0
- package/dist/components/tooltip/Tooltip.svelte +224 -0
- package/dist/components/tooltip/Tooltip.svelte.d.ts +27 -0
- package/dist/icons.d.ts +2 -0
- package/dist/icons.js +4 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +66 -0
- package/dist/styles/tokens.css +50 -0
- package/package.json +88 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Attachment } from 'svelte/attachments';
|
|
2
|
+
export type PopoverTriggerProps = {
|
|
3
|
+
onclick?: () => void;
|
|
4
|
+
onpointerenter?: (event: PointerEvent) => void;
|
|
5
|
+
onpointerleave?: () => void;
|
|
6
|
+
onfocus?: () => void;
|
|
7
|
+
onblur?: (event: FocusEvent) => void;
|
|
8
|
+
'aria-haspopup'?: 'dialog';
|
|
9
|
+
'aria-expanded'?: boolean;
|
|
10
|
+
'aria-controls'?: string;
|
|
11
|
+
[key: symbol]: Attachment<HTMLElement>;
|
|
12
|
+
};
|
|
13
|
+
import type { Snippet } from 'svelte';
|
|
14
|
+
type Side = 'top' | 'right' | 'bottom' | 'left';
|
|
15
|
+
type Props = {
|
|
16
|
+
/** Whether the popover is open. Bindable. */
|
|
17
|
+
open?: boolean;
|
|
18
|
+
/** Opens on hover and keyboard focus instead of on click, like a hover card. */
|
|
19
|
+
hover?: boolean;
|
|
20
|
+
/** Milliseconds to wait on hover before opening. Only used with `hover`. */
|
|
21
|
+
delay?: number;
|
|
22
|
+
/** Preferred side; flips to the opposite one when there is not enough room. */
|
|
23
|
+
side?: Side;
|
|
24
|
+
/** Alignment along the trigger's edge. */
|
|
25
|
+
align?: 'start' | 'center' | 'end';
|
|
26
|
+
title?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
/** Renders the trigger. Spread the given props onto it, e.g. `<Button {...props} />`. */
|
|
29
|
+
trigger: Snippet<[PopoverTriggerProps]>;
|
|
30
|
+
children?: Snippet;
|
|
31
|
+
};
|
|
32
|
+
declare const Popover: import("svelte").Component<Props, {}, "open">;
|
|
33
|
+
type Popover = ReturnType<typeof Popover>;
|
|
34
|
+
export default Popover;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { setRadioGroupContext } from './context.js';
|
|
5
|
+
|
|
6
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
7
|
+
/** Value of the selected RadioGroupItem. */
|
|
8
|
+
value?: string;
|
|
9
|
+
/** Form field name. Generated when omitted. */
|
|
10
|
+
name?: string;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
required?: boolean;
|
|
13
|
+
orientation?: 'vertical' | 'horizontal';
|
|
14
|
+
/** Horizontal Fields, each with a RadioGroupItem and a Label. */
|
|
15
|
+
children: Snippet;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
value = $bindable(),
|
|
20
|
+
name,
|
|
21
|
+
disabled = false,
|
|
22
|
+
required = false,
|
|
23
|
+
orientation = 'vertical',
|
|
24
|
+
class: className,
|
|
25
|
+
children,
|
|
26
|
+
...rest
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
const fallbackName = $props.id();
|
|
30
|
+
|
|
31
|
+
setRadioGroupContext({
|
|
32
|
+
get name() {
|
|
33
|
+
return name ?? fallbackName;
|
|
34
|
+
},
|
|
35
|
+
get value() {
|
|
36
|
+
return value;
|
|
37
|
+
},
|
|
38
|
+
get disabled() {
|
|
39
|
+
return disabled;
|
|
40
|
+
},
|
|
41
|
+
get required() {
|
|
42
|
+
return required;
|
|
43
|
+
},
|
|
44
|
+
select: (next) => {
|
|
45
|
+
value = next;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<div
|
|
51
|
+
{...rest}
|
|
52
|
+
role="radiogroup"
|
|
53
|
+
aria-disabled={disabled || undefined}
|
|
54
|
+
aria-required={required || undefined}
|
|
55
|
+
aria-orientation={orientation}
|
|
56
|
+
class={['radio-group', `radio-group--${orientation}`, className]}
|
|
57
|
+
>
|
|
58
|
+
{@render children()}
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<style>
|
|
62
|
+
.radio-group {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
gap: 12px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.radio-group--horizontal {
|
|
69
|
+
flex-direction: row;
|
|
70
|
+
flex-wrap: wrap;
|
|
71
|
+
gap: 12px 24px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.radio-group--horizontal > :global(.field) {
|
|
75
|
+
width: auto;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* aria-invalid is not allowed on a radio, so the group carries it */
|
|
79
|
+
.radio-group[aria-invalid='true'] :global(.radio input) {
|
|
80
|
+
border-color: var(--color-destructive);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.radio-group[aria-invalid='true'] :global(.label) {
|
|
84
|
+
color: var(--color-destructive);
|
|
85
|
+
}
|
|
86
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
4
|
+
/** Value of the selected RadioGroupItem. */
|
|
5
|
+
value?: string;
|
|
6
|
+
/** Form field name. Generated when omitted. */
|
|
7
|
+
name?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
orientation?: 'vertical' | 'horizontal';
|
|
11
|
+
/** Horizontal Fields, each with a RadioGroupItem and a Label. */
|
|
12
|
+
children: Snippet;
|
|
13
|
+
};
|
|
14
|
+
declare const RadioGroup: import("svelte").Component<Props, {}, "value">;
|
|
15
|
+
type RadioGroup = ReturnType<typeof RadioGroup>;
|
|
16
|
+
export default RadioGroup;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
|
+
import { getRadioGroupContext } from './context.js';
|
|
4
|
+
|
|
5
|
+
type Props = Omit<HTMLInputAttributes, 'type' | 'name' | 'value' | 'checked'> & {
|
|
6
|
+
value: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
let { value, disabled, class: className, ...rest }: Props = $props();
|
|
10
|
+
|
|
11
|
+
const group = getRadioGroupContext();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<span class={['radio', className]}>
|
|
15
|
+
<input
|
|
16
|
+
{...rest}
|
|
17
|
+
type="radio"
|
|
18
|
+
name={group.name}
|
|
19
|
+
{value}
|
|
20
|
+
checked={group.value === value}
|
|
21
|
+
disabled={group.disabled || disabled}
|
|
22
|
+
required={group.required}
|
|
23
|
+
onchange={(event) => {
|
|
24
|
+
if (event.currentTarget.checked) group.select(value);
|
|
25
|
+
rest.onchange?.(event);
|
|
26
|
+
}}
|
|
27
|
+
/>
|
|
28
|
+
<span class="radio-indicator" aria-hidden="true"></span>
|
|
29
|
+
</span>
|
|
30
|
+
|
|
31
|
+
<style>
|
|
32
|
+
.radio {
|
|
33
|
+
position: relative;
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
flex-shrink: 0;
|
|
36
|
+
width: 16px;
|
|
37
|
+
height: 16px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
input {
|
|
41
|
+
appearance: none;
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
width: 100%;
|
|
44
|
+
height: 100%;
|
|
45
|
+
margin: 0;
|
|
46
|
+
background-color: transparent;
|
|
47
|
+
border: 1px solid var(--color-border-strong);
|
|
48
|
+
border-radius: 50%;
|
|
49
|
+
outline: none;
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
transition:
|
|
52
|
+
background-color 200ms ease,
|
|
53
|
+
border-color 200ms ease;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
input:checked {
|
|
57
|
+
background-color: var(--color-accent);
|
|
58
|
+
border-color: var(--color-accent);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
input:focus-visible {
|
|
62
|
+
outline: 2px solid var(--color-accent);
|
|
63
|
+
outline-offset: 2px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
input:disabled {
|
|
67
|
+
cursor: not-allowed;
|
|
68
|
+
opacity: 0.5;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.radio-indicator {
|
|
72
|
+
position: absolute;
|
|
73
|
+
inset: 4.5px;
|
|
74
|
+
background-color: var(--color-accent-foreground);
|
|
75
|
+
border-radius: 50%;
|
|
76
|
+
pointer-events: none;
|
|
77
|
+
transform: scale(0);
|
|
78
|
+
transition: transform 200ms ease;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
input:checked + .radio-indicator {
|
|
82
|
+
transform: scale(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
input:checked:disabled + .radio-indicator {
|
|
86
|
+
opacity: 0.5;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@media (prefers-reduced-motion: reduce) {
|
|
90
|
+
input,
|
|
91
|
+
.radio-indicator {
|
|
92
|
+
transition: none;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
2
|
+
type Props = Omit<HTMLInputAttributes, 'type' | 'name' | 'value' | 'checked'> & {
|
|
3
|
+
value: string;
|
|
4
|
+
};
|
|
5
|
+
declare const RadioGroupItem: import("svelte").Component<Props, {}, "">;
|
|
6
|
+
type RadioGroupItem = ReturnType<typeof RadioGroupItem>;
|
|
7
|
+
export default RadioGroupItem;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type RadioGroupContext = {
|
|
2
|
+
/** Shared `name` of the radios, so the browser handles arrow keys and single selection. */
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly value: string | undefined;
|
|
5
|
+
readonly disabled: boolean;
|
|
6
|
+
readonly required: boolean;
|
|
7
|
+
select: (value: string) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare const getRadioGroupContext: () => RadioGroupContext, setRadioGroupContext: (context: RadioGroupContext) => RadioGroupContext;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
5
|
+
/** Direction of the line. */
|
|
6
|
+
orientation?: 'horizontal' | 'vertical';
|
|
7
|
+
/** Purely visual line, hidden from assistive technology. Set to false when it separates content semantically. */
|
|
8
|
+
decorative?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
orientation = 'horizontal',
|
|
13
|
+
decorative = true,
|
|
14
|
+
class: className,
|
|
15
|
+
...rest
|
|
16
|
+
}: Props = $props();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<div
|
|
20
|
+
{...rest}
|
|
21
|
+
role={decorative ? 'none' : 'separator'}
|
|
22
|
+
aria-orientation={decorative || orientation === 'horizontal' ? undefined : orientation}
|
|
23
|
+
data-orientation={orientation}
|
|
24
|
+
class={['separator', `separator--${orientation}`, className]}
|
|
25
|
+
></div>
|
|
26
|
+
|
|
27
|
+
<style>
|
|
28
|
+
.separator {
|
|
29
|
+
flex-shrink: 0;
|
|
30
|
+
background-color: var(--color-border);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.separator--horizontal {
|
|
34
|
+
height: 1px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.separator--vertical {
|
|
38
|
+
width: 1px;
|
|
39
|
+
align-self: stretch;
|
|
40
|
+
}
|
|
41
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
3
|
+
/** Direction of the line. */
|
|
4
|
+
orientation?: 'horizontal' | 'vertical';
|
|
5
|
+
/** Purely visual line, hidden from assistive technology. Set to false when it separates content semantically. */
|
|
6
|
+
decorative?: boolean;
|
|
7
|
+
};
|
|
8
|
+
declare const Separator: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Separator = ReturnType<typeof Separator>;
|
|
10
|
+
export default Separator;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { fade } from 'svelte/transition';
|
|
5
|
+
import { getSidebarContext } from './context.js';
|
|
6
|
+
|
|
7
|
+
type Props = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
|
|
8
|
+
side?: 'left' | 'right';
|
|
9
|
+
/** `floating` detaches the sidebar from the edge as a rounded panel. */
|
|
10
|
+
variant?: 'sidebar' | 'floating';
|
|
11
|
+
/** What collapsing does: slide out (`offcanvas`), shrink to icons (`icon`) or nothing (`none`). */
|
|
12
|
+
collapsible?: 'offcanvas' | 'icon' | 'none';
|
|
13
|
+
/** `SidebarHeader`, `SidebarContent` and `SidebarFooter`. */
|
|
14
|
+
children: Snippet;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
side = 'left',
|
|
19
|
+
variant = 'sidebar',
|
|
20
|
+
collapsible = 'offcanvas',
|
|
21
|
+
class: className,
|
|
22
|
+
children,
|
|
23
|
+
...rest
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
|
|
26
|
+
const sidebar = getSidebarContext();
|
|
27
|
+
|
|
28
|
+
let collapsed = $derived(!sidebar.mobile && !sidebar.open && collapsible !== 'none');
|
|
29
|
+
let hidden = $derived(
|
|
30
|
+
sidebar.mobile ? !sidebar.openMobile : collapsed && collapsible === 'offcanvas'
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
$effect(() => {
|
|
34
|
+
if (!sidebar.openMobile) return;
|
|
35
|
+
|
|
36
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
37
|
+
if (event.key === 'Escape') sidebar.closeMobile();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
window.addEventListener('keydown', handleKeydown);
|
|
41
|
+
return () => window.removeEventListener('keydown', handleKeydown);
|
|
42
|
+
});
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
{#if sidebar.mobile && sidebar.openMobile}
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
tabindex="-1"
|
|
49
|
+
aria-label="Close sidebar"
|
|
50
|
+
class="sidebar-backdrop"
|
|
51
|
+
onclick={sidebar.closeMobile}
|
|
52
|
+
transition:fade={{ duration: 200 }}
|
|
53
|
+
></button>
|
|
54
|
+
{/if}
|
|
55
|
+
|
|
56
|
+
<aside
|
|
57
|
+
{...rest}
|
|
58
|
+
id={sidebar.id}
|
|
59
|
+
inert={hidden}
|
|
60
|
+
data-state={collapsed ? 'collapsed' : 'expanded'}
|
|
61
|
+
class={[
|
|
62
|
+
'sidebar',
|
|
63
|
+
`sidebar--${side}`,
|
|
64
|
+
`sidebar--${variant}`,
|
|
65
|
+
`sidebar--collapsible-${collapsible}`,
|
|
66
|
+
collapsed && `sidebar--collapsed-${collapsible}`,
|
|
67
|
+
sidebar.mobile && 'sidebar--mobile',
|
|
68
|
+
sidebar.openMobile && 'sidebar--open-mobile',
|
|
69
|
+
className
|
|
70
|
+
]}
|
|
71
|
+
>
|
|
72
|
+
<div class="sidebar-inner">
|
|
73
|
+
{@render children()}
|
|
74
|
+
</div>
|
|
75
|
+
</aside>
|
|
76
|
+
|
|
77
|
+
<style>
|
|
78
|
+
.sidebar {
|
|
79
|
+
position: sticky;
|
|
80
|
+
top: 0;
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-shrink: 0;
|
|
83
|
+
width: var(--sidebar-width);
|
|
84
|
+
height: var(--sidebar-height);
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
transition: width 200ms ease;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* The inner panel keeps its width while the outer one shrinks, so it slides out instead of squashing */
|
|
90
|
+
.sidebar--left {
|
|
91
|
+
justify-content: flex-end;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.sidebar--right {
|
|
95
|
+
order: 1;
|
|
96
|
+
justify-content: flex-start;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.sidebar--collapsed-offcanvas {
|
|
100
|
+
width: 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.sidebar--collapsed-icon {
|
|
104
|
+
width: var(--sidebar-width-icon);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.sidebar-inner {
|
|
108
|
+
display: flex;
|
|
109
|
+
flex-direction: column;
|
|
110
|
+
flex-shrink: 0;
|
|
111
|
+
box-sizing: border-box;
|
|
112
|
+
width: var(--sidebar-width);
|
|
113
|
+
height: 100%;
|
|
114
|
+
background-color: var(--color-bg);
|
|
115
|
+
color: var(--color-text);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.sidebar--collapsible-icon .sidebar-inner {
|
|
119
|
+
width: 100%;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Variants */
|
|
123
|
+
|
|
124
|
+
.sidebar--sidebar.sidebar--left .sidebar-inner {
|
|
125
|
+
border-right: 1px solid var(--color-border);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.sidebar--sidebar.sidebar--right .sidebar-inner {
|
|
129
|
+
border-left: 1px solid var(--color-border);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.sidebar--floating .sidebar-inner {
|
|
133
|
+
width: calc(var(--sidebar-width) - 16px);
|
|
134
|
+
height: calc(100% - 16px);
|
|
135
|
+
margin: 8px;
|
|
136
|
+
border: 1px solid var(--color-border);
|
|
137
|
+
border-radius: 12px;
|
|
138
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.sidebar--floating.sidebar--collapsible-icon .sidebar-inner {
|
|
142
|
+
width: calc(100% - 16px);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.sidebar--floating.sidebar--collapsed-icon {
|
|
146
|
+
width: calc(var(--sidebar-width-icon) + 16px);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Mobile drawer */
|
|
150
|
+
|
|
151
|
+
.sidebar--mobile {
|
|
152
|
+
position: fixed;
|
|
153
|
+
top: 0;
|
|
154
|
+
bottom: 0;
|
|
155
|
+
left: 0;
|
|
156
|
+
z-index: 50;
|
|
157
|
+
height: auto;
|
|
158
|
+
transform: translateX(-100%);
|
|
159
|
+
transition: transform 200ms ease;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.sidebar--mobile.sidebar--right {
|
|
163
|
+
left: auto;
|
|
164
|
+
right: 0;
|
|
165
|
+
transform: translateX(100%);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.sidebar--mobile.sidebar--open-mobile {
|
|
169
|
+
transform: none;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.sidebar-backdrop {
|
|
173
|
+
position: fixed;
|
|
174
|
+
inset: 0;
|
|
175
|
+
z-index: 49;
|
|
176
|
+
padding: 0;
|
|
177
|
+
background-color: rgba(0, 0, 0, 0.4);
|
|
178
|
+
border: none;
|
|
179
|
+
}
|
|
180
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type Props = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
|
|
4
|
+
side?: 'left' | 'right';
|
|
5
|
+
/** `floating` detaches the sidebar from the edge as a rounded panel. */
|
|
6
|
+
variant?: 'sidebar' | 'floating';
|
|
7
|
+
/** What collapsing does: slide out (`offcanvas`), shrink to icons (`icon`) or nothing (`none`). */
|
|
8
|
+
collapsible?: 'offcanvas' | 'icon' | 'none';
|
|
9
|
+
/** `SidebarHeader`, `SidebarContent` and `SidebarFooter`. */
|
|
10
|
+
children: Snippet;
|
|
11
|
+
};
|
|
12
|
+
declare const Sidebar: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type Sidebar = ReturnType<typeof Sidebar>;
|
|
14
|
+
export default Sidebar;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
6
|
+
/** `SidebarGroup`s. Scrolls when it overflows. */
|
|
7
|
+
children: Snippet;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
let { class: className, children, ...rest }: Props = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div {...rest} class={['sidebar-content', className]}>
|
|
14
|
+
{@render children()}
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
.sidebar-content {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
flex: 1;
|
|
22
|
+
min-height: 0;
|
|
23
|
+
overflow-x: hidden;
|
|
24
|
+
overflow-y: auto;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:global(.sidebar--collapsed-icon) .sidebar-content {
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
}
|
|
30
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
4
|
+
/** `SidebarGroup`s. Scrolls when it overflows. */
|
|
5
|
+
children: Snippet;
|
|
6
|
+
};
|
|
7
|
+
declare const SidebarContent: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type SidebarContent = ReturnType<typeof SidebarContent>;
|
|
9
|
+
export default SidebarContent;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
6
|
+
children: Snippet;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
let { class: className, children, ...rest }: Props = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div {...rest} class={['sidebar-footer', className]}>
|
|
13
|
+
{@render children()}
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<style>
|
|
17
|
+
.sidebar-footer {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: 8px;
|
|
21
|
+
padding: 8px;
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
4
|
+
children: Snippet;
|
|
5
|
+
};
|
|
6
|
+
declare const SidebarFooter: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type SidebarFooter = ReturnType<typeof SidebarFooter>;
|
|
8
|
+
export default SidebarFooter;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
6
|
+
/** Heading of the group. Hidden while the sidebar is collapsed to icons. */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** Usually a `SidebarMenu`. */
|
|
9
|
+
children: Snippet;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let { label, class: className, children, ...rest }: Props = $props();
|
|
13
|
+
|
|
14
|
+
const id = $props.id();
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<div
|
|
18
|
+
{...rest}
|
|
19
|
+
role="group"
|
|
20
|
+
aria-labelledby={label ? `${id}-label` : undefined}
|
|
21
|
+
class={['sidebar-group', className]}
|
|
22
|
+
>
|
|
23
|
+
{#if label}<div id="{id}-label" class="sidebar-group-label">{label}</div>{/if}
|
|
24
|
+
{@render children()}
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<style>
|
|
28
|
+
.sidebar-group {
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
min-width: 0;
|
|
32
|
+
padding: 8px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.sidebar-group-label {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
flex-shrink: 0;
|
|
39
|
+
height: 32px;
|
|
40
|
+
padding: 0 8px;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
font-size: 12px;
|
|
43
|
+
font-weight: 500;
|
|
44
|
+
color: var(--color-text-muted);
|
|
45
|
+
white-space: nowrap;
|
|
46
|
+
transition:
|
|
47
|
+
margin-top 200ms ease,
|
|
48
|
+
opacity 200ms ease;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
:global(.sidebar--collapsed-icon) .sidebar-group-label {
|
|
52
|
+
margin-top: -32px;
|
|
53
|
+
opacity: 0;
|
|
54
|
+
}
|
|
55
|
+
</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
|
+
/** Heading of the group. Hidden while the sidebar is collapsed to icons. */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** Usually a `SidebarMenu`. */
|
|
7
|
+
children: Snippet;
|
|
8
|
+
};
|
|
9
|
+
declare const SidebarGroup: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type SidebarGroup = ReturnType<typeof SidebarGroup>;
|
|
11
|
+
export default SidebarGroup;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
6
|
+
children: Snippet;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
let { class: className, children, ...rest }: Props = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div {...rest} class={['sidebar-header', className]}>
|
|
13
|
+
{@render children()}
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<style>
|
|
17
|
+
.sidebar-header {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: 8px;
|
|
21
|
+
padding: 8px;
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
4
|
+
children: Snippet;
|
|
5
|
+
};
|
|
6
|
+
declare const SidebarHeader: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type SidebarHeader = ReturnType<typeof SidebarHeader>;
|
|
8
|
+
export default SidebarHeader;
|