@loidolt/theme-svelte 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/components/Accordion.svelte +100 -0
- package/dist/components/Accordion.svelte.d.ts +50 -0
- package/dist/components/Alert.svelte +6 -3
- package/dist/components/Alert.svelte.d.ts +3 -2
- package/dist/components/AlertDialog.svelte +130 -0
- package/dist/components/AlertDialog.svelte.d.ts +35 -0
- package/dist/components/AppShell.svelte +2 -1
- package/dist/components/AppShell.svelte.d.ts +1 -0
- package/dist/components/Badge.svelte +9 -2
- package/dist/components/Badge.svelte.d.ts +2 -1
- package/dist/components/Brand.svelte +29 -15
- package/dist/components/Brand.svelte.d.ts +8 -4
- package/dist/components/Breadcrumbs.svelte +53 -0
- package/dist/components/Breadcrumbs.svelte.d.ts +18 -0
- package/dist/components/Button.svelte +43 -21
- package/dist/components/Button.svelte.d.ts +9 -4
- package/dist/components/Card.svelte +23 -5
- package/dist/components/Card.svelte.d.ts +11 -4
- package/dist/components/CodeBlock.svelte +55 -0
- package/dist/components/CodeBlock.svelte.d.ts +14 -0
- package/dist/components/CommentList.svelte +67 -0
- package/dist/components/CommentList.svelte.d.ts +53 -0
- package/dist/components/Dialog.svelte +25 -3
- package/dist/components/Dialog.svelte.d.ts +4 -1
- package/dist/components/Drawer.svelte +105 -0
- package/dist/components/Drawer.svelte.d.ts +31 -0
- package/dist/components/DropdownMenu.svelte +2 -2
- package/dist/components/DropdownMenu.svelte.d.ts +1 -1
- package/dist/components/EmptyState.svelte +40 -0
- package/dist/components/EmptyState.svelte.d.ts +19 -0
- package/dist/components/Fieldset.svelte +60 -0
- package/dist/components/Fieldset.svelte.d.ts +19 -0
- package/dist/components/FileInput.svelte +47 -0
- package/dist/components/FileInput.svelte.d.ts +15 -0
- package/dist/components/Filmstrip.svelte +72 -0
- package/dist/components/Filmstrip.svelte.d.ts +50 -0
- package/dist/components/FloatingBar.svelte +43 -0
- package/dist/components/FloatingBar.svelte.d.ts +16 -0
- package/dist/components/IconButton.svelte +1 -1
- package/dist/components/Input.svelte.d.ts +1 -1
- package/dist/components/ListRow.svelte +50 -0
- package/dist/components/ListRow.svelte.d.ts +18 -0
- package/dist/components/Marker.svelte +38 -0
- package/dist/components/Marker.svelte.d.ts +14 -0
- package/dist/components/NavMenu.svelte +6 -1
- package/dist/components/NavMenu.svelte.d.ts +2 -1
- package/dist/components/NumberField.svelte +12 -1
- package/dist/components/NumberField.svelte.d.ts +1 -1
- package/dist/components/Pagination.svelte +86 -0
- package/dist/components/Pagination.svelte.d.ts +22 -0
- package/dist/components/Popover.svelte +24 -3
- package/dist/components/Popover.svelte.d.ts +4 -1
- package/dist/components/RadioGroup.svelte.d.ts +2 -2
- package/dist/components/RecordStepper.svelte +58 -0
- package/dist/components/RecordStepper.svelte.d.ts +18 -0
- package/dist/components/SegmentedNav.svelte +53 -0
- package/dist/components/SegmentedNav.svelte.d.ts +22 -0
- package/dist/components/Select.svelte.d.ts +2 -2
- package/dist/components/Stat.svelte +41 -0
- package/dist/components/Stat.svelte.d.ts +17 -0
- package/dist/components/StatusDot.svelte +27 -0
- package/dist/components/StatusDot.svelte.d.ts +12 -0
- package/dist/components/SwatchGroup.svelte +69 -0
- package/dist/components/SwatchGroup.svelte.d.ts +44 -0
- package/dist/components/Switch.svelte +19 -0
- package/dist/components/Table.svelte +30 -3
- package/dist/components/Table.svelte.d.ts +13 -0
- package/dist/components/TableHeader.svelte +72 -0
- package/dist/components/TableHeader.svelte.d.ts +24 -0
- package/dist/components/Tabs.svelte +23 -5
- package/dist/components/Tabs.svelte.d.ts +4 -3
- package/dist/components/Textarea.svelte.d.ts +1 -1
- package/dist/components/ThemeToggle.svelte +133 -0
- package/dist/components/ThemeToggle.svelte.d.ts +20 -0
- package/dist/components/Thumbnail.svelte +51 -0
- package/dist/components/Thumbnail.svelte.d.ts +19 -0
- package/dist/components/ToggleGroup.svelte +107 -0
- package/dist/components/ToggleGroup.svelte.d.ts +46 -0
- package/dist/components/TooltipProvider.svelte +1 -0
- package/dist/components/Workspace.svelte +27 -6
- package/dist/components/Workspace.svelte.d.ts +12 -0
- package/dist/index.d.ts +29 -2
- package/dist/index.js +26 -1
- package/dist/media.svelte.d.ts +48 -0
- package/dist/media.svelte.js +69 -0
- package/dist/theme.svelte.d.ts +86 -0
- package/dist/theme.svelte.js +166 -0
- package/dist/toaster.svelte.d.ts +2 -0
- package/dist/toaster.svelte.js +51 -9
- package/dist/types.d.ts +21 -0
- package/package.json +10 -8
|
@@ -4,35 +4,56 @@
|
|
|
4
4
|
import { cx } from '../utils.js';
|
|
5
5
|
|
|
6
6
|
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
/** Full-width row above the panes — a ContextBar or toolbar. */
|
|
8
|
+
header?: Snippet;
|
|
7
9
|
sidebar?: Snippet;
|
|
8
10
|
children: Snippet;
|
|
9
11
|
inspector?: Snippet;
|
|
12
|
+
/**
|
|
13
|
+
* Collapses the sidebar column when `false` — the toggle behind a "hide navigation" control.
|
|
14
|
+
* The snippet is left unrendered rather than hidden, so nothing in it holds focus or is
|
|
15
|
+
* reachable by the tab order while collapsed.
|
|
16
|
+
*/
|
|
17
|
+
sidebarOpen?: boolean;
|
|
18
|
+
/** The same for the inspector column. */
|
|
19
|
+
inspectorOpen?: boolean;
|
|
10
20
|
class?: string;
|
|
21
|
+
/** Extra classes for the main pane, replacing consumer reach-ins on `__main`. */
|
|
22
|
+
mainClass?: string;
|
|
11
23
|
ref?: HTMLDivElement | null;
|
|
12
24
|
}
|
|
13
25
|
|
|
14
26
|
let {
|
|
27
|
+
header,
|
|
15
28
|
sidebar,
|
|
16
29
|
children,
|
|
17
30
|
inspector,
|
|
31
|
+
sidebarOpen = true,
|
|
32
|
+
inspectorOpen = true,
|
|
18
33
|
class: className,
|
|
34
|
+
mainClass,
|
|
19
35
|
ref = $bindable(null),
|
|
20
36
|
...rest
|
|
21
37
|
}: Props = $props();
|
|
38
|
+
|
|
39
|
+
const showSidebar = $derived(Boolean(sidebar) && sidebarOpen);
|
|
40
|
+
const showInspector = $derived(Boolean(inspector) && inspectorOpen);
|
|
22
41
|
</script>
|
|
23
42
|
|
|
24
|
-
<!-- The grid columns follow the
|
|
43
|
+
<!-- The grid columns follow the panes that are actually rendered. -->
|
|
25
44
|
<div
|
|
26
45
|
bind:this={ref}
|
|
27
46
|
class={cx(
|
|
28
47
|
'ldt-workspace',
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
header && 'ldt-workspace--header',
|
|
49
|
+
showSidebar && 'ldt-workspace--sidebar',
|
|
50
|
+
showInspector && 'ldt-workspace--inspector',
|
|
31
51
|
className
|
|
32
52
|
)}
|
|
33
53
|
{...rest}
|
|
34
54
|
>
|
|
35
|
-
{#if
|
|
36
|
-
|
|
37
|
-
{
|
|
55
|
+
{#if header}<div class="ldt-workspace__header">{@render header()}</div>{/if}
|
|
56
|
+
{#if showSidebar}{@render sidebar!()}{/if}
|
|
57
|
+
<div class={cx('ldt-workspace__main', mainClass)}>{@render children()}</div>
|
|
58
|
+
{#if showInspector}{@render inspector!()}{/if}
|
|
38
59
|
</div>
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
3
|
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
/** Full-width row above the panes — a ContextBar or toolbar. */
|
|
5
|
+
header?: Snippet;
|
|
4
6
|
sidebar?: Snippet;
|
|
5
7
|
children: Snippet;
|
|
6
8
|
inspector?: Snippet;
|
|
9
|
+
/**
|
|
10
|
+
* Collapses the sidebar column when `false` — the toggle behind a "hide navigation" control.
|
|
11
|
+
* The snippet is left unrendered rather than hidden, so nothing in it holds focus or is
|
|
12
|
+
* reachable by the tab order while collapsed.
|
|
13
|
+
*/
|
|
14
|
+
sidebarOpen?: boolean;
|
|
15
|
+
/** The same for the inspector column. */
|
|
16
|
+
inspectorOpen?: boolean;
|
|
7
17
|
class?: string;
|
|
18
|
+
/** Extra classes for the main pane, replacing consumer reach-ins on `__main`. */
|
|
19
|
+
mainClass?: string;
|
|
8
20
|
ref?: HTMLDivElement | null;
|
|
9
21
|
}
|
|
10
22
|
declare const Workspace: import("svelte").Component<Props, {}, "ref">;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,36 +1,59 @@
|
|
|
1
|
+
export { default as Accordion } from './components/Accordion.svelte';
|
|
1
2
|
export { default as Alert } from './components/Alert.svelte';
|
|
3
|
+
export { default as AlertDialog } from './components/AlertDialog.svelte';
|
|
2
4
|
export { default as AppShell } from './components/AppShell.svelte';
|
|
3
5
|
export { default as Badge } from './components/Badge.svelte';
|
|
4
6
|
export { default as Brand } from './components/Brand.svelte';
|
|
7
|
+
export { default as Breadcrumbs } from './components/Breadcrumbs.svelte';
|
|
5
8
|
export { default as Button } from './components/Button.svelte';
|
|
6
9
|
export { default as Card } from './components/Card.svelte';
|
|
7
10
|
export { default as Checkbox } from './components/Checkbox.svelte';
|
|
11
|
+
export { default as CodeBlock } from './components/CodeBlock.svelte';
|
|
12
|
+
export { default as CommentList } from './components/CommentList.svelte';
|
|
8
13
|
export { default as ContextBar } from './components/ContextBar.svelte';
|
|
9
14
|
export { default as Dialog } from './components/Dialog.svelte';
|
|
15
|
+
export { default as Drawer } from './components/Drawer.svelte';
|
|
10
16
|
export { default as DropdownMenu } from './components/DropdownMenu.svelte';
|
|
17
|
+
export { default as EmptyState } from './components/EmptyState.svelte';
|
|
11
18
|
export { default as Field } from './components/Field.svelte';
|
|
19
|
+
export { default as Fieldset } from './components/Fieldset.svelte';
|
|
20
|
+
export { default as FileInput } from './components/FileInput.svelte';
|
|
21
|
+
export { default as Filmstrip } from './components/Filmstrip.svelte';
|
|
22
|
+
export { default as FloatingBar } from './components/FloatingBar.svelte';
|
|
12
23
|
export { default as IconButton } from './components/IconButton.svelte';
|
|
13
24
|
export { default as Input } from './components/Input.svelte';
|
|
14
25
|
export { default as Label } from './components/Label.svelte';
|
|
26
|
+
export { default as ListRow } from './components/ListRow.svelte';
|
|
27
|
+
export { default as Marker } from './components/Marker.svelte';
|
|
15
28
|
export { default as NavMenu } from './components/NavMenu.svelte';
|
|
16
29
|
export { default as NumberField } from './components/NumberField.svelte';
|
|
17
30
|
export { default as PageHeader } from './components/PageHeader.svelte';
|
|
31
|
+
export { default as Pagination } from './components/Pagination.svelte';
|
|
18
32
|
export { default as Panel } from './components/Panel.svelte';
|
|
19
33
|
export { default as Popover } from './components/Popover.svelte';
|
|
20
34
|
export { default as Progress } from './components/Progress.svelte';
|
|
21
35
|
export { default as RadioGroup } from './components/RadioGroup.svelte';
|
|
36
|
+
export { default as RecordStepper } from './components/RecordStepper.svelte';
|
|
22
37
|
export { default as Section } from './components/Section.svelte';
|
|
38
|
+
export { default as SegmentedNav } from './components/SegmentedNav.svelte';
|
|
23
39
|
export { default as Select } from './components/Select.svelte';
|
|
24
40
|
export { default as Separator } from './components/Separator.svelte';
|
|
25
41
|
export { default as Sidebar } from './components/Sidebar.svelte';
|
|
26
42
|
export { default as Skeleton } from './components/Skeleton.svelte';
|
|
27
43
|
export { default as Spinner } from './components/Spinner.svelte';
|
|
44
|
+
export { default as Stat } from './components/Stat.svelte';
|
|
45
|
+
export { default as StatusDot } from './components/StatusDot.svelte';
|
|
46
|
+
export { default as SwatchGroup } from './components/SwatchGroup.svelte';
|
|
28
47
|
export { default as Switch } from './components/Switch.svelte';
|
|
29
48
|
export { default as Table } from './components/Table.svelte';
|
|
49
|
+
export { default as TableHeader } from './components/TableHeader.svelte';
|
|
30
50
|
export { default as Tabs } from './components/Tabs.svelte';
|
|
31
51
|
export { default as Textarea } from './components/Textarea.svelte';
|
|
52
|
+
export { default as ThemeToggle } from './components/ThemeToggle.svelte';
|
|
53
|
+
export { default as Thumbnail } from './components/Thumbnail.svelte';
|
|
32
54
|
export { default as Toast } from './components/Toast.svelte';
|
|
33
55
|
export { default as ToastViewport } from './components/ToastViewport.svelte';
|
|
56
|
+
export { default as ToggleGroup } from './components/ToggleGroup.svelte';
|
|
34
57
|
export { default as Tooltip } from './components/Tooltip.svelte';
|
|
35
58
|
export { default as TooltipProvider } from './components/TooltipProvider.svelte';
|
|
36
59
|
export { default as Topbar } from './components/Topbar.svelte';
|
|
@@ -39,8 +62,12 @@ export { default as Workspace } from './components/Workspace.svelte';
|
|
|
39
62
|
* Bits UI primitives behind the wrappers, for menus and overlays this package does not
|
|
40
63
|
* model declaratively (checkbox/radio items, submenus, custom overlay composition).
|
|
41
64
|
*/
|
|
42
|
-
export { Dialog as DialogPrimitive, DropdownMenu as DropdownMenuPrimitive, Popover as PopoverPrimitive, Tabs as TabsPrimitive, Tooltip as TooltipPrimitive, } from 'bits-ui';
|
|
65
|
+
export { Accordion as AccordionPrimitive, AlertDialog as AlertDialogPrimitive, Dialog as DialogPrimitive, DropdownMenu as DropdownMenuPrimitive, Pagination as PaginationPrimitive, Popover as PopoverPrimitive, Tabs as TabsPrimitive, ToggleGroup as ToggleGroupPrimitive, Tooltip as TooltipPrimitive, } from 'bits-ui';
|
|
43
66
|
export { cx } from './utils.js';
|
|
44
67
|
export { createToaster } from './toaster.svelte.js';
|
|
45
68
|
export type { Toaster, ToasterOptions, ToastOptions, ToastRecord } from './toaster.svelte.js';
|
|
46
|
-
export
|
|
69
|
+
export { createTheme, themeScript } from './theme.svelte.js';
|
|
70
|
+
export type { ColorScheme, Theme, ThemeOptions, ThemePreference, ThemeScriptOptions, } from './theme.svelte.js';
|
|
71
|
+
export { breakpointQuery, createMediaQuery } from './media.svelte.js';
|
|
72
|
+
export type { BreakpointName, MediaQuery, MediaQueryOptions } from './media.svelte.js';
|
|
73
|
+
export type { ActionVariant, Alignment, BadgeSize, BadgeVariant, ColumnAlign, Crumb, Disclosure, ControlSize, DialogSize, HeadingLevel, MenuItem, NavItem, Option, Orientation, Placement, SortDirection, StatusVariant, TriggerChildProps, } from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -1,36 +1,59 @@
|
|
|
1
|
+
export { default as Accordion } from './components/Accordion.svelte';
|
|
1
2
|
export { default as Alert } from './components/Alert.svelte';
|
|
3
|
+
export { default as AlertDialog } from './components/AlertDialog.svelte';
|
|
2
4
|
export { default as AppShell } from './components/AppShell.svelte';
|
|
3
5
|
export { default as Badge } from './components/Badge.svelte';
|
|
4
6
|
export { default as Brand } from './components/Brand.svelte';
|
|
7
|
+
export { default as Breadcrumbs } from './components/Breadcrumbs.svelte';
|
|
5
8
|
export { default as Button } from './components/Button.svelte';
|
|
6
9
|
export { default as Card } from './components/Card.svelte';
|
|
7
10
|
export { default as Checkbox } from './components/Checkbox.svelte';
|
|
11
|
+
export { default as CodeBlock } from './components/CodeBlock.svelte';
|
|
12
|
+
export { default as CommentList } from './components/CommentList.svelte';
|
|
8
13
|
export { default as ContextBar } from './components/ContextBar.svelte';
|
|
9
14
|
export { default as Dialog } from './components/Dialog.svelte';
|
|
15
|
+
export { default as Drawer } from './components/Drawer.svelte';
|
|
10
16
|
export { default as DropdownMenu } from './components/DropdownMenu.svelte';
|
|
17
|
+
export { default as EmptyState } from './components/EmptyState.svelte';
|
|
11
18
|
export { default as Field } from './components/Field.svelte';
|
|
19
|
+
export { default as Fieldset } from './components/Fieldset.svelte';
|
|
20
|
+
export { default as FileInput } from './components/FileInput.svelte';
|
|
21
|
+
export { default as Filmstrip } from './components/Filmstrip.svelte';
|
|
22
|
+
export { default as FloatingBar } from './components/FloatingBar.svelte';
|
|
12
23
|
export { default as IconButton } from './components/IconButton.svelte';
|
|
13
24
|
export { default as Input } from './components/Input.svelte';
|
|
14
25
|
export { default as Label } from './components/Label.svelte';
|
|
26
|
+
export { default as ListRow } from './components/ListRow.svelte';
|
|
27
|
+
export { default as Marker } from './components/Marker.svelte';
|
|
15
28
|
export { default as NavMenu } from './components/NavMenu.svelte';
|
|
16
29
|
export { default as NumberField } from './components/NumberField.svelte';
|
|
17
30
|
export { default as PageHeader } from './components/PageHeader.svelte';
|
|
31
|
+
export { default as Pagination } from './components/Pagination.svelte';
|
|
18
32
|
export { default as Panel } from './components/Panel.svelte';
|
|
19
33
|
export { default as Popover } from './components/Popover.svelte';
|
|
20
34
|
export { default as Progress } from './components/Progress.svelte';
|
|
21
35
|
export { default as RadioGroup } from './components/RadioGroup.svelte';
|
|
36
|
+
export { default as RecordStepper } from './components/RecordStepper.svelte';
|
|
22
37
|
export { default as Section } from './components/Section.svelte';
|
|
38
|
+
export { default as SegmentedNav } from './components/SegmentedNav.svelte';
|
|
23
39
|
export { default as Select } from './components/Select.svelte';
|
|
24
40
|
export { default as Separator } from './components/Separator.svelte';
|
|
25
41
|
export { default as Sidebar } from './components/Sidebar.svelte';
|
|
26
42
|
export { default as Skeleton } from './components/Skeleton.svelte';
|
|
27
43
|
export { default as Spinner } from './components/Spinner.svelte';
|
|
44
|
+
export { default as Stat } from './components/Stat.svelte';
|
|
45
|
+
export { default as StatusDot } from './components/StatusDot.svelte';
|
|
46
|
+
export { default as SwatchGroup } from './components/SwatchGroup.svelte';
|
|
28
47
|
export { default as Switch } from './components/Switch.svelte';
|
|
29
48
|
export { default as Table } from './components/Table.svelte';
|
|
49
|
+
export { default as TableHeader } from './components/TableHeader.svelte';
|
|
30
50
|
export { default as Tabs } from './components/Tabs.svelte';
|
|
31
51
|
export { default as Textarea } from './components/Textarea.svelte';
|
|
52
|
+
export { default as ThemeToggle } from './components/ThemeToggle.svelte';
|
|
53
|
+
export { default as Thumbnail } from './components/Thumbnail.svelte';
|
|
32
54
|
export { default as Toast } from './components/Toast.svelte';
|
|
33
55
|
export { default as ToastViewport } from './components/ToastViewport.svelte';
|
|
56
|
+
export { default as ToggleGroup } from './components/ToggleGroup.svelte';
|
|
34
57
|
export { default as Tooltip } from './components/Tooltip.svelte';
|
|
35
58
|
export { default as TooltipProvider } from './components/TooltipProvider.svelte';
|
|
36
59
|
export { default as Topbar } from './components/Topbar.svelte';
|
|
@@ -39,6 +62,8 @@ export { default as Workspace } from './components/Workspace.svelte';
|
|
|
39
62
|
* Bits UI primitives behind the wrappers, for menus and overlays this package does not
|
|
40
63
|
* model declaratively (checkbox/radio items, submenus, custom overlay composition).
|
|
41
64
|
*/
|
|
42
|
-
export { Dialog as DialogPrimitive, DropdownMenu as DropdownMenuPrimitive, Popover as PopoverPrimitive, Tabs as TabsPrimitive, Tooltip as TooltipPrimitive, } from 'bits-ui';
|
|
65
|
+
export { Accordion as AccordionPrimitive, AlertDialog as AlertDialogPrimitive, Dialog as DialogPrimitive, DropdownMenu as DropdownMenuPrimitive, Pagination as PaginationPrimitive, Popover as PopoverPrimitive, Tabs as TabsPrimitive, ToggleGroup as ToggleGroupPrimitive, Tooltip as TooltipPrimitive, } from 'bits-ui';
|
|
43
66
|
export { cx } from './utils.js';
|
|
44
67
|
export { createToaster } from './toaster.svelte.js';
|
|
68
|
+
export { createTheme, themeScript } from './theme.svelte.js';
|
|
69
|
+
export { breakpointQuery, createMediaQuery } from './media.svelte.js';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { breakpoints } from '@loidolt/theme-tokens';
|
|
2
|
+
export interface MediaQueryOptions {
|
|
3
|
+
/**
|
|
4
|
+
* What `matches` reports before a `matchMedia` result exists — during SSR, and on the very
|
|
5
|
+
* first client frame if the environment has no `matchMedia`. Pick the value that renders the
|
|
6
|
+
* layout you would rather ship to a bot or an old browser; it defaults to `false`.
|
|
7
|
+
*/
|
|
8
|
+
fallback?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface MediaQuery {
|
|
11
|
+
/** Live match state. Reactive — read it in markup or a `$derived`. */
|
|
12
|
+
readonly matches: boolean;
|
|
13
|
+
readonly query: string;
|
|
14
|
+
/**
|
|
15
|
+
* Removes the change listener. Called automatically when the query is created during
|
|
16
|
+
* component initialisation; call it yourself for a module-scope instance you want to retire.
|
|
17
|
+
*/
|
|
18
|
+
destroy(): void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reactive `matchMedia`.
|
|
22
|
+
*
|
|
23
|
+
* ```svelte
|
|
24
|
+
* <script lang="ts">
|
|
25
|
+
* import { createMediaQuery, breakpointQuery } from '@loidolt/theme-svelte';
|
|
26
|
+
* const compact = createMediaQuery(breakpointQuery('compact'));
|
|
27
|
+
* </script>
|
|
28
|
+
*
|
|
29
|
+
* {#if compact.matches}<Drawer …/>{:else}<Sidebar …/>{/if}
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* SSR-safe: on the server `matches` is the `fallback` and nothing subscribes. Because the
|
|
33
|
+
* server cannot know the viewport, markup that switches on a media query hydrates with the
|
|
34
|
+
* fallback branch and swaps on the first client frame — render both branches, or gate the
|
|
35
|
+
* swap on a mount flag, if that flash matters.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createMediaQuery(query: string, options?: MediaQueryOptions): MediaQuery;
|
|
38
|
+
export type BreakpointName = keyof typeof breakpoints;
|
|
39
|
+
/**
|
|
40
|
+
* The media query for a token breakpoint. `below` (the default) matches the narrow side, which
|
|
41
|
+
* is the direction the stylesheet's own `@media (max-width: …)` blocks use.
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* breakpointQuery('compact'); // '(max-width: 760px)'
|
|
45
|
+
* breakpointQuery('compact', 'above'); // '(min-width: 760.02px)'
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function breakpointQuery(name: BreakpointName, direction?: 'below' | 'above'): string;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { onDestroy } from 'svelte';
|
|
2
|
+
import { breakpoints } from '@loidolt/theme-tokens';
|
|
3
|
+
/**
|
|
4
|
+
* Reactive `matchMedia`.
|
|
5
|
+
*
|
|
6
|
+
* ```svelte
|
|
7
|
+
* <script lang="ts">
|
|
8
|
+
* import { createMediaQuery, breakpointQuery } from '@loidolt/theme-svelte';
|
|
9
|
+
* const compact = createMediaQuery(breakpointQuery('compact'));
|
|
10
|
+
* </script>
|
|
11
|
+
*
|
|
12
|
+
* {#if compact.matches}<Drawer …/>{:else}<Sidebar …/>{/if}
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* SSR-safe: on the server `matches` is the `fallback` and nothing subscribes. Because the
|
|
16
|
+
* server cannot know the viewport, markup that switches on a media query hydrates with the
|
|
17
|
+
* fallback branch and swaps on the first client frame — render both branches, or gate the
|
|
18
|
+
* swap on a mount flag, if that flash matters.
|
|
19
|
+
*/
|
|
20
|
+
export function createMediaQuery(query, options = {}) {
|
|
21
|
+
const { fallback = false } = options;
|
|
22
|
+
let matches = $state(fallback);
|
|
23
|
+
let list;
|
|
24
|
+
const update = (event) => {
|
|
25
|
+
matches = event.matches;
|
|
26
|
+
};
|
|
27
|
+
if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {
|
|
28
|
+
list = window.matchMedia(query);
|
|
29
|
+
matches = list.matches;
|
|
30
|
+
list.addEventListener('change', update);
|
|
31
|
+
}
|
|
32
|
+
const destroy = () => {
|
|
33
|
+
list?.removeEventListener('change', update);
|
|
34
|
+
list = undefined;
|
|
35
|
+
};
|
|
36
|
+
// A query created inside a component should not outlive it. Created at module scope — the
|
|
37
|
+
// usual place for an app-wide singleton — there is no component to hook into, and the caller
|
|
38
|
+
// owns `destroy()` instead.
|
|
39
|
+
try {
|
|
40
|
+
onDestroy(destroy);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
/* module scope: no lifecycle to attach to */
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
get matches() {
|
|
47
|
+
return matches;
|
|
48
|
+
},
|
|
49
|
+
query,
|
|
50
|
+
destroy,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The media query for a token breakpoint. `below` (the default) matches the narrow side, which
|
|
55
|
+
* is the direction the stylesheet's own `@media (max-width: …)` blocks use.
|
|
56
|
+
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* breakpointQuery('compact'); // '(max-width: 760px)'
|
|
59
|
+
* breakpointQuery('compact', 'above'); // '(min-width: 760.02px)'
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function breakpointQuery(name, direction = 'below') {
|
|
63
|
+
const value = breakpoints[name];
|
|
64
|
+
if (direction === 'below')
|
|
65
|
+
return `(max-width: ${value})`;
|
|
66
|
+
// 0.02px, not 1px: the CSS Media Queries spec resolves widths at fractional precision, so a
|
|
67
|
+
// whole-pixel step leaves a dead zone on fractional-DPI viewports where neither side matches.
|
|
68
|
+
return `(min-width: ${Number.parseFloat(value) + 0.02}px)`;
|
|
69
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** What the user chose. `system` defers to `prefers-color-scheme`. */
|
|
2
|
+
export type ThemePreference = 'light' | 'dark' | 'system';
|
|
3
|
+
/** What that choice resolves to right now — the value written to the DOM. */
|
|
4
|
+
export type ColorScheme = 'light' | 'dark';
|
|
5
|
+
export interface ThemeOptions {
|
|
6
|
+
/** `localStorage` key holding the preference. Set to `null` to keep the choice in memory. */
|
|
7
|
+
storageKey?: string | null;
|
|
8
|
+
/** Attribute the resolved scheme is written to. Must match the stylesheet's selector. */
|
|
9
|
+
attribute?: string;
|
|
10
|
+
/** Element the attribute lands on. Defaults to `<html>`. */
|
|
11
|
+
element?: HTMLElement | null;
|
|
12
|
+
/** Preference before anything is stored. */
|
|
13
|
+
defaultPreference?: ThemePreference;
|
|
14
|
+
/** What `system` resolves to where `prefers-color-scheme` cannot be read (SSR, old engines). */
|
|
15
|
+
defaultScheme?: ColorScheme;
|
|
16
|
+
/**
|
|
17
|
+
* Mirror the choice across tabs by listening for `storage` events. On by default; turn it off
|
|
18
|
+
* if separate windows should be able to hold different themes.
|
|
19
|
+
*/
|
|
20
|
+
sync?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface Theme {
|
|
23
|
+
/** The stored choice. Assignable: `theme.preference = 'dark'`. */
|
|
24
|
+
preference: ThemePreference;
|
|
25
|
+
/** The scheme in force — `preference`, or the system scheme when that is `system`. */
|
|
26
|
+
readonly resolved: ColorScheme;
|
|
27
|
+
/** What the system currently prefers, regardless of the choice. */
|
|
28
|
+
readonly system: ColorScheme;
|
|
29
|
+
/** Flips between explicit light and dark, starting from whatever is showing. */
|
|
30
|
+
toggle(): void;
|
|
31
|
+
/** Removes the media and storage listeners. Automatic for a component-scoped instance. */
|
|
32
|
+
destroy(): void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Theme state: the stored preference, the system preference, and the resolved scheme written to
|
|
36
|
+
* the DOM.
|
|
37
|
+
*
|
|
38
|
+
* ```ts
|
|
39
|
+
* // src/lib/theme.ts — one instance for the app
|
|
40
|
+
* import { createTheme } from '@loidolt/theme-svelte';
|
|
41
|
+
* export const theme = createTheme();
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* ```svelte
|
|
45
|
+
* <button onclick={theme.toggle}>{theme.resolved === 'dark' ? 'Light' : 'Dark'} mode</button>
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* The **resolved** scheme is always written out, even for `system`, so the same store drives
|
|
49
|
+
* both `@loidolt/theme-styles/dark` (attribute-only) and `/dark-auto` (which also follows
|
|
50
|
+
* `prefers-color-scheme`): an explicit `data-theme="light"` is exactly what `dark-auto` expects
|
|
51
|
+
* to see when a user overrides a dark system.
|
|
52
|
+
*
|
|
53
|
+
* Pair it with {@link themeScript} in `app.html`, or the first paint of a reload is light.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createTheme(options?: ThemeOptions): Theme;
|
|
56
|
+
export interface ThemeScriptOptions extends Pick<ThemeOptions, 'storageKey' | 'attribute' | 'defaultPreference' | 'defaultScheme'> {
|
|
57
|
+
/** Wrap the source in a `<script>` tag. Off by default, so it can go inside your own tag. */
|
|
58
|
+
tag?: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The blocking snippet that stops the light-mode flash on reload. It performs the same resolve
|
|
62
|
+
* as {@link createTheme} — read the stored preference, fall back to `prefers-color-scheme`,
|
|
63
|
+
* write the attribute — before the browser paints.
|
|
64
|
+
*
|
|
65
|
+
* It must run **synchronously in `<head>`, before the stylesheet's first paint**. A module
|
|
66
|
+
* script, a `defer`ed script, or anything in `<body>` is too late by definition.
|
|
67
|
+
*
|
|
68
|
+
* ```html
|
|
69
|
+
* <!-- src/app.html -->
|
|
70
|
+
* <head>
|
|
71
|
+
* %sveltekit.head%
|
|
72
|
+
* <script>__LOIDOLT_THEME__</script>
|
|
73
|
+
* </head>
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* ```ts
|
|
77
|
+
* // src/hooks.server.ts
|
|
78
|
+
* import { themeScript } from '@loidolt/theme-svelte';
|
|
79
|
+
* export const handle = ({ event, resolve }) =>
|
|
80
|
+
* resolve(event, { transformPageChunk: ({ html }) => html.replace('__LOIDOLT_THEME__', themeScript()) });
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* Returns source only unless `tag` is set. The output contains no `<` at all, so it is safe to
|
|
84
|
+
* inline in an HTML `<script>` element without further escaping.
|
|
85
|
+
*/
|
|
86
|
+
export declare function themeScript(options?: ThemeScriptOptions): string;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { onDestroy } from 'svelte';
|
|
2
|
+
const SCHEMES = ['light', 'dark', 'system'];
|
|
3
|
+
const isPreference = (value) => typeof value === 'string' && SCHEMES.includes(value);
|
|
4
|
+
/**
|
|
5
|
+
* Theme state: the stored preference, the system preference, and the resolved scheme written to
|
|
6
|
+
* the DOM.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // src/lib/theme.ts — one instance for the app
|
|
10
|
+
* import { createTheme } from '@loidolt/theme-svelte';
|
|
11
|
+
* export const theme = createTheme();
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* ```svelte
|
|
15
|
+
* <button onclick={theme.toggle}>{theme.resolved === 'dark' ? 'Light' : 'Dark'} mode</button>
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* The **resolved** scheme is always written out, even for `system`, so the same store drives
|
|
19
|
+
* both `@loidolt/theme-styles/dark` (attribute-only) and `/dark-auto` (which also follows
|
|
20
|
+
* `prefers-color-scheme`): an explicit `data-theme="light"` is exactly what `dark-auto` expects
|
|
21
|
+
* to see when a user overrides a dark system.
|
|
22
|
+
*
|
|
23
|
+
* Pair it with {@link themeScript} in `app.html`, or the first paint of a reload is light.
|
|
24
|
+
*/
|
|
25
|
+
export function createTheme(options = {}) {
|
|
26
|
+
const { storageKey = 'loidolt-theme', attribute = 'data-theme', element, defaultPreference = 'system', defaultScheme = 'light', sync = true, } = options;
|
|
27
|
+
const browser = typeof document !== 'undefined';
|
|
28
|
+
const target = element ?? (browser ? document.documentElement : null);
|
|
29
|
+
const read = () => {
|
|
30
|
+
if (!browser || storageKey === null)
|
|
31
|
+
return defaultPreference;
|
|
32
|
+
try {
|
|
33
|
+
const stored = localStorage.getItem(storageKey);
|
|
34
|
+
return isPreference(stored) ? stored : defaultPreference;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Storage can throw outright in a partitioned iframe or with cookies blocked. A theme is
|
|
38
|
+
// not worth breaking the page over — fall back to the default and keep the choice in memory.
|
|
39
|
+
return defaultPreference;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
let preference = $state(read());
|
|
43
|
+
let system = $state(defaultScheme);
|
|
44
|
+
let media;
|
|
45
|
+
const onSystemChange = (event) => {
|
|
46
|
+
system = event.matches ? 'dark' : 'light';
|
|
47
|
+
apply();
|
|
48
|
+
};
|
|
49
|
+
if (browser && typeof window.matchMedia === 'function') {
|
|
50
|
+
media = window.matchMedia('(prefers-color-scheme: dark)');
|
|
51
|
+
system = media.matches ? 'dark' : 'light';
|
|
52
|
+
media.addEventListener('change', onSystemChange);
|
|
53
|
+
}
|
|
54
|
+
const resolve = () => (preference === 'system' ? system : preference);
|
|
55
|
+
function apply() {
|
|
56
|
+
target?.setAttribute(attribute, resolve());
|
|
57
|
+
}
|
|
58
|
+
const onStorage = (event) => {
|
|
59
|
+
if (event.key !== storageKey)
|
|
60
|
+
return;
|
|
61
|
+
// A cleared key means another tab reset to the default rather than picking a scheme.
|
|
62
|
+
preference = isPreference(event.newValue) ? event.newValue : defaultPreference;
|
|
63
|
+
apply();
|
|
64
|
+
};
|
|
65
|
+
if (browser && sync && storageKey !== null)
|
|
66
|
+
window.addEventListener('storage', onStorage);
|
|
67
|
+
// Written on creation, not in an effect: `createTheme()` is meant to be callable at module
|
|
68
|
+
// scope, where there is no reactive context to own an effect.
|
|
69
|
+
apply();
|
|
70
|
+
function set(next) {
|
|
71
|
+
preference = next;
|
|
72
|
+
if (browser && storageKey !== null) {
|
|
73
|
+
try {
|
|
74
|
+
localStorage.setItem(storageKey, next);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
/* see `read`: unavailable storage degrades to a memory-only choice */
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
apply();
|
|
81
|
+
}
|
|
82
|
+
const destroy = () => {
|
|
83
|
+
media?.removeEventListener('change', onSystemChange);
|
|
84
|
+
media = undefined;
|
|
85
|
+
if (browser && sync && storageKey !== null)
|
|
86
|
+
window.removeEventListener('storage', onStorage);
|
|
87
|
+
};
|
|
88
|
+
try {
|
|
89
|
+
onDestroy(destroy);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
/* module scope: the caller owns `destroy()` */
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
get preference() {
|
|
96
|
+
return preference;
|
|
97
|
+
},
|
|
98
|
+
set preference(next) {
|
|
99
|
+
set(next);
|
|
100
|
+
},
|
|
101
|
+
get resolved() {
|
|
102
|
+
return resolve();
|
|
103
|
+
},
|
|
104
|
+
get system() {
|
|
105
|
+
return system;
|
|
106
|
+
},
|
|
107
|
+
toggle() {
|
|
108
|
+
set(resolve() === 'dark' ? 'light' : 'dark');
|
|
109
|
+
},
|
|
110
|
+
destroy,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The blocking snippet that stops the light-mode flash on reload. It performs the same resolve
|
|
115
|
+
* as {@link createTheme} — read the stored preference, fall back to `prefers-color-scheme`,
|
|
116
|
+
* write the attribute — before the browser paints.
|
|
117
|
+
*
|
|
118
|
+
* It must run **synchronously in `<head>`, before the stylesheet's first paint**. A module
|
|
119
|
+
* script, a `defer`ed script, or anything in `<body>` is too late by definition.
|
|
120
|
+
*
|
|
121
|
+
* ```html
|
|
122
|
+
* <!-- src/app.html -->
|
|
123
|
+
* <head>
|
|
124
|
+
* %sveltekit.head%
|
|
125
|
+
* <script>__LOIDOLT_THEME__</script>
|
|
126
|
+
* </head>
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* ```ts
|
|
130
|
+
* // src/hooks.server.ts
|
|
131
|
+
* import { themeScript } from '@loidolt/theme-svelte';
|
|
132
|
+
* export const handle = ({ event, resolve }) =>
|
|
133
|
+
* resolve(event, { transformPageChunk: ({ html }) => html.replace('__LOIDOLT_THEME__', themeScript()) });
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* Returns source only unless `tag` is set. The output contains no `<` at all, so it is safe to
|
|
137
|
+
* inline in an HTML `<script>` element without further escaping.
|
|
138
|
+
*/
|
|
139
|
+
export function themeScript(options = {}) {
|
|
140
|
+
const { storageKey = 'loidolt-theme', attribute = 'data-theme', defaultPreference = 'system', defaultScheme = 'light', tag = false, } = options;
|
|
141
|
+
// JSON string escaping alone leaves `<` intact, including a literal `</script>` that would end
|
|
142
|
+
// an inline script element. Escaping HTML-significant characters and the two JavaScript line
|
|
143
|
+
// separators keeps every option inside its string literal in both source-only and tagged forms.
|
|
144
|
+
const serialize = (value) => JSON.stringify(value)
|
|
145
|
+
.replaceAll('<', '\\u003c')
|
|
146
|
+
.replaceAll('>', '\\u003e')
|
|
147
|
+
.replaceAll('&', '\\u0026')
|
|
148
|
+
.replaceAll('\u2028', '\\u2028')
|
|
149
|
+
.replaceAll('\u2029', '\\u2029');
|
|
150
|
+
const key = serialize(storageKey);
|
|
151
|
+
const attr = serialize(attribute);
|
|
152
|
+
const fallback = serialize(defaultPreference);
|
|
153
|
+
const scheme = serialize(defaultScheme);
|
|
154
|
+
const source = `(function(){try{` +
|
|
155
|
+
`var p=${fallback};` +
|
|
156
|
+
// Storage failure must not skip system resolution and attribute application.
|
|
157
|
+
`try{var v=${key}===null?null:localStorage.getItem(${key});` +
|
|
158
|
+
`if(v==="light"||v==="dark"||v==="system")p=v;}catch(e){}` +
|
|
159
|
+
`var s=${scheme};` +
|
|
160
|
+
`if(typeof matchMedia==="function")s=matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";` +
|
|
161
|
+
`document.documentElement.setAttribute(${attr},p==="system"?s:p);` +
|
|
162
|
+
// A theme is cosmetic; a throw here would abort a blocking head script and take the page
|
|
163
|
+
// down with it. Swallow and let the stylesheet's own default stand.
|
|
164
|
+
`}catch(e){}})()`;
|
|
165
|
+
return tag ? `<script>${source}</script>` : source;
|
|
166
|
+
}
|
package/dist/toaster.svelte.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface Toaster {
|
|
|
26
26
|
pause(): void;
|
|
27
27
|
/** Resumes with the time each toast had left — call on pointer leave / focus out. */
|
|
28
28
|
resume(): void;
|
|
29
|
+
/** Clears the queue and every timer. Automatic for a component-scoped instance. */
|
|
30
|
+
destroy(): void;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* Queue behind `Toast` / `ToastViewport`: auto-dismiss with the remaining time preserved
|