@loidolt/theme-svelte 0.2.0 → 0.3.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/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 +109 -0
- package/dist/components/AlertDialog.svelte.d.ts +32 -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/Drawer.svelte +90 -0
- package/dist/components/Drawer.svelte.d.ts +28 -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/IconButton.svelte +1 -1
- package/dist/components/Input.svelte.d.ts +1 -1
- 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/RadioGroup.svelte.d.ts +2 -2
- package/dist/components/Select.svelte.d.ts +2 -2
- 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 +15 -7
- package/dist/components/Tabs.svelte.d.ts +4 -3
- package/dist/components/Textarea.svelte.d.ts +1 -1
- package/dist/components/ThemeToggle.svelte +62 -0
- package/dist/components/ThemeToggle.svelte.d.ts +25 -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 +18 -5
- package/dist/components/Workspace.svelte.d.ts +8 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.js +13 -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 +156 -0
- package/dist/toaster.svelte.js +22 -8
- package/dist/types.d.ts +21 -0
- package/package.json +5 -4
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Dialog as DialogPrimitive } from 'bits-ui';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import type { DialogContentProps } from 'bits-ui';
|
|
5
|
+
import type { HeadingLevel, TriggerChildProps } from '../types.js';
|
|
6
|
+
import { cx } from '../utils.js';
|
|
7
|
+
|
|
8
|
+
type Props = Omit<DialogContentProps, 'children' | 'class' | 'child'> & {
|
|
9
|
+
open?: boolean;
|
|
10
|
+
title: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
/** Edge the panel is anchored to. `left`/`right` size by width, `top`/`bottom` by height. */
|
|
13
|
+
side?: 'left' | 'right' | 'top' | 'bottom';
|
|
14
|
+
/** Trigger contents, rendered inside a button styled with `triggerClass`. */
|
|
15
|
+
trigger?: Snippet;
|
|
16
|
+
/** Full control of the trigger element — receives the props Bits needs on it. */
|
|
17
|
+
triggerChild?: Snippet<[TriggerChildProps]>;
|
|
18
|
+
triggerClass?: string;
|
|
19
|
+
children: Snippet;
|
|
20
|
+
footer?: Snippet;
|
|
21
|
+
headingLevel?: HeadingLevel;
|
|
22
|
+
showClose?: boolean;
|
|
23
|
+
closeLabel?: string;
|
|
24
|
+
/** `class` lands on the portaled Content element. */
|
|
25
|
+
class?: string;
|
|
26
|
+
overlayClass?: string;
|
|
27
|
+
onOpenChange?: (open: boolean) => void;
|
|
28
|
+
ref?: HTMLElement | null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
open = $bindable(false),
|
|
33
|
+
title,
|
|
34
|
+
description,
|
|
35
|
+
side = 'left',
|
|
36
|
+
trigger,
|
|
37
|
+
triggerChild,
|
|
38
|
+
triggerClass = 'ldt-button',
|
|
39
|
+
children,
|
|
40
|
+
footer,
|
|
41
|
+
headingLevel = 2,
|
|
42
|
+
showClose = true,
|
|
43
|
+
closeLabel = 'Close',
|
|
44
|
+
class: className,
|
|
45
|
+
overlayClass,
|
|
46
|
+
onOpenChange,
|
|
47
|
+
ref = $bindable(null),
|
|
48
|
+
...rest
|
|
49
|
+
}: Props = $props();
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<!--
|
|
53
|
+
A modal panel anchored to one edge — the narrow-viewport form of a sidebar, and the usual home
|
|
54
|
+
for navigation that has no room to sit in the topbar. Same primitive as `Dialog`, so focus is
|
|
55
|
+
trapped, Escape closes, and the page behind is inert; set `--ldt-drawer-size` for the measure
|
|
56
|
+
across the anchored edge.
|
|
57
|
+
-->
|
|
58
|
+
<DialogPrimitive.Root bind:open {onOpenChange}>
|
|
59
|
+
{#if triggerChild}<DialogPrimitive.Trigger
|
|
60
|
+
>{#snippet child(childProps)}{@render triggerChild(
|
|
61
|
+
childProps
|
|
62
|
+
)}{/snippet}</DialogPrimitive.Trigger
|
|
63
|
+
>{:else if trigger}<DialogPrimitive.Trigger class={triggerClass}
|
|
64
|
+
>{@render trigger()}</DialogPrimitive.Trigger
|
|
65
|
+
>{/if}
|
|
66
|
+
<DialogPrimitive.Portal>
|
|
67
|
+
<DialogPrimitive.Overlay class={cx('ldt-overlay', overlayClass)} />
|
|
68
|
+
<DialogPrimitive.Content
|
|
69
|
+
bind:ref
|
|
70
|
+
class={cx('ldt-drawer', `ldt-drawer--${side}`, className)}
|
|
71
|
+
{...rest}
|
|
72
|
+
>
|
|
73
|
+
<header class="ldt-drawer__header">
|
|
74
|
+
<div>
|
|
75
|
+
<DialogPrimitive.Title level={headingLevel} class="ldt-drawer__title"
|
|
76
|
+
>{title}</DialogPrimitive.Title
|
|
77
|
+
>{#if description}<DialogPrimitive.Description class="ldt-drawer__description"
|
|
78
|
+
>{description}</DialogPrimitive.Description
|
|
79
|
+
>{/if}
|
|
80
|
+
</div>
|
|
81
|
+
{#if showClose}<DialogPrimitive.Close
|
|
82
|
+
class="ldt-button ldt-icon-button ldt-button--ghost"
|
|
83
|
+
aria-label={closeLabel}>×</DialogPrimitive.Close
|
|
84
|
+
>{/if}
|
|
85
|
+
</header>
|
|
86
|
+
<div class="ldt-drawer__body ldt-scrollbar">{@render children()}</div>
|
|
87
|
+
{#if footer}<footer class="ldt-drawer__footer">{@render footer()}</footer>{/if}
|
|
88
|
+
</DialogPrimitive.Content>
|
|
89
|
+
</DialogPrimitive.Portal>
|
|
90
|
+
</DialogPrimitive.Root>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { DialogContentProps } from 'bits-ui';
|
|
3
|
+
import type { HeadingLevel, TriggerChildProps } from '../types.js';
|
|
4
|
+
type Props = Omit<DialogContentProps, 'children' | 'class' | 'child'> & {
|
|
5
|
+
open?: boolean;
|
|
6
|
+
title: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
/** Edge the panel is anchored to. `left`/`right` size by width, `top`/`bottom` by height. */
|
|
9
|
+
side?: 'left' | 'right' | 'top' | 'bottom';
|
|
10
|
+
/** Trigger contents, rendered inside a button styled with `triggerClass`. */
|
|
11
|
+
trigger?: Snippet;
|
|
12
|
+
/** Full control of the trigger element — receives the props Bits needs on it. */
|
|
13
|
+
triggerChild?: Snippet<[TriggerChildProps]>;
|
|
14
|
+
triggerClass?: string;
|
|
15
|
+
children: Snippet;
|
|
16
|
+
footer?: Snippet;
|
|
17
|
+
headingLevel?: HeadingLevel;
|
|
18
|
+
showClose?: boolean;
|
|
19
|
+
closeLabel?: string;
|
|
20
|
+
/** `class` lands on the portaled Content element. */
|
|
21
|
+
class?: string;
|
|
22
|
+
overlayClass?: string;
|
|
23
|
+
onOpenChange?: (open: boolean) => void;
|
|
24
|
+
ref?: HTMLElement | null;
|
|
25
|
+
};
|
|
26
|
+
declare const Drawer: import("svelte").Component<Props, {}, "ref" | "open">;
|
|
27
|
+
type Drawer = ReturnType<typeof Drawer>;
|
|
28
|
+
export default Drawer;
|
|
@@ -58,13 +58,13 @@
|
|
|
58
58
|
{#if item.href}
|
|
59
59
|
<a {...props} href={item.href}
|
|
60
60
|
>{#if item.icon}{@render item.icon()}{/if}<span>{item.label}</span>{#if item.hint}<span
|
|
61
|
-
|
|
61
|
+
class="ldt-menu__hint">{item.hint}</span
|
|
62
62
|
>{/if}</a
|
|
63
63
|
>
|
|
64
64
|
{:else}
|
|
65
65
|
<button {...props} type="button"
|
|
66
66
|
>{#if item.icon}{@render item.icon()}{/if}<span>{item.label}</span>{#if item.hint}<span
|
|
67
|
-
|
|
67
|
+
class="ldt-menu__hint">{item.hint}</span
|
|
68
68
|
>{/if}</button
|
|
69
69
|
>
|
|
70
70
|
{/if}
|
|
@@ -2,7 +2,7 @@ import { DropdownMenu as MenuPrimitive } from 'bits-ui';
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { Alignment, MenuItem, Placement, TriggerChildProps } from '../types.js';
|
|
4
4
|
declare function $$render<T extends string = string>(): {
|
|
5
|
-
props: Omit<MenuPrimitive.ContentProps, "
|
|
5
|
+
props: Omit<MenuPrimitive.ContentProps, "children" | "class" | "child"> & {
|
|
6
6
|
open?: boolean;
|
|
7
7
|
/** Heading rendered above `items`, associated with them via a menu group. */
|
|
8
8
|
groupLabel?: string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import type { HeadingLevel } from '../types.js';
|
|
5
|
+
import { cx } from '../utils.js';
|
|
6
|
+
|
|
7
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
8
|
+
title: string;
|
|
9
|
+
/** What to do about it. An empty state without a next step is just a blank screen. */
|
|
10
|
+
description?: string;
|
|
11
|
+
/** Leading visual, sized by the consumer. */
|
|
12
|
+
icon?: Snippet;
|
|
13
|
+
/** Buttons or links offering the next step. */
|
|
14
|
+
actions?: Snippet;
|
|
15
|
+
headingLevel?: HeadingLevel;
|
|
16
|
+
class?: string;
|
|
17
|
+
children?: Snippet;
|
|
18
|
+
ref?: HTMLDivElement | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let {
|
|
22
|
+
title,
|
|
23
|
+
description,
|
|
24
|
+
icon,
|
|
25
|
+
actions,
|
|
26
|
+
headingLevel = 3,
|
|
27
|
+
class: className,
|
|
28
|
+
children,
|
|
29
|
+
ref = $bindable(null),
|
|
30
|
+
...rest
|
|
31
|
+
}: Props = $props();
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<div bind:this={ref} class={cx('ldt-empty-state', className)} {...rest}>
|
|
35
|
+
{#if icon}<div class="ldt-empty-state__icon" aria-hidden="true">{@render icon()}</div>{/if}
|
|
36
|
+
<svelte:element this={`h${headingLevel}`} class="ldt-empty-state__title">{title}</svelte:element>
|
|
37
|
+
{#if description}<p class="ldt-empty-state__description">{description}</p>{/if}
|
|
38
|
+
{#if children}{@render children()}{/if}
|
|
39
|
+
{#if actions}<div class="ldt-empty-state__actions">{@render actions()}</div>{/if}
|
|
40
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { HeadingLevel } from '../types.js';
|
|
4
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
title: string;
|
|
6
|
+
/** What to do about it. An empty state without a next step is just a blank screen. */
|
|
7
|
+
description?: string;
|
|
8
|
+
/** Leading visual, sized by the consumer. */
|
|
9
|
+
icon?: Snippet;
|
|
10
|
+
/** Buttons or links offering the next step. */
|
|
11
|
+
actions?: Snippet;
|
|
12
|
+
headingLevel?: HeadingLevel;
|
|
13
|
+
class?: string;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
ref?: HTMLDivElement | null;
|
|
16
|
+
}
|
|
17
|
+
declare const EmptyState: import("svelte").Component<Props, {}, "ref">;
|
|
18
|
+
type EmptyState = ReturnType<typeof EmptyState>;
|
|
19
|
+
export default EmptyState;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLFieldsetAttributes } from 'svelte/elements';
|
|
4
|
+
import { cx } from '../utils.js';
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLFieldsetAttributes {
|
|
7
|
+
/** Defaults to an SSR-stable generated id, used to wire the description and error. */
|
|
8
|
+
id?: string;
|
|
9
|
+
/** Visible group label, rendered as a `<legend>`. */
|
|
10
|
+
legend: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
optional?: boolean;
|
|
14
|
+
/** Text of the "optional" marker, for localisation. */
|
|
15
|
+
optionalText?: string;
|
|
16
|
+
class?: string;
|
|
17
|
+
children: Snippet;
|
|
18
|
+
ref?: HTMLFieldSetElement | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const generatedId = $props.id();
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
id = generatedId,
|
|
25
|
+
legend,
|
|
26
|
+
description,
|
|
27
|
+
error,
|
|
28
|
+
optional = false,
|
|
29
|
+
optionalText = 'Optional',
|
|
30
|
+
class: className,
|
|
31
|
+
children,
|
|
32
|
+
ref = $bindable(null),
|
|
33
|
+
...rest
|
|
34
|
+
}: Props = $props();
|
|
35
|
+
|
|
36
|
+
const describedBy = $derived(
|
|
37
|
+
[description && `${id}-description`, error && `${id}-error`].filter(Boolean).join(' ') ||
|
|
38
|
+
undefined
|
|
39
|
+
);
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<!--
|
|
43
|
+
`Field` wires one control; `Fieldset` wires a group of them. The description and error hang off
|
|
44
|
+
the `<fieldset>` rather than off each control: grouping context is announced once on entry, so
|
|
45
|
+
a shared message stays shared instead of repeating on every checkbox in the set.
|
|
46
|
+
-->
|
|
47
|
+
<fieldset
|
|
48
|
+
bind:this={ref}
|
|
49
|
+
{id}
|
|
50
|
+
class={cx('ldt-fieldset', className)}
|
|
51
|
+
aria-describedby={describedBy}
|
|
52
|
+
{...rest}
|
|
53
|
+
>
|
|
54
|
+
<legend class="ldt-legend"
|
|
55
|
+
><span>{legend}</span>{#if optional}<small>{optionalText}</small>{/if}</legend
|
|
56
|
+
>
|
|
57
|
+
<div class="ldt-fieldset__body">{@render children()}</div>
|
|
58
|
+
{#if description}<p class="ldt-field__description" id={`${id}-description`}>{description}</p>{/if}
|
|
59
|
+
{#if error}<p class="ldt-field__error" id={`${id}-error`} role="alert">{error}</p>{/if}
|
|
60
|
+
</fieldset>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLFieldsetAttributes } from 'svelte/elements';
|
|
3
|
+
interface Props extends HTMLFieldsetAttributes {
|
|
4
|
+
/** Defaults to an SSR-stable generated id, used to wire the description and error. */
|
|
5
|
+
id?: string;
|
|
6
|
+
/** Visible group label, rendered as a `<legend>`. */
|
|
7
|
+
legend: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
optional?: boolean;
|
|
11
|
+
/** Text of the "optional" marker, for localisation. */
|
|
12
|
+
optionalText?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
children: Snippet;
|
|
15
|
+
ref?: HTMLFieldSetElement | null;
|
|
16
|
+
}
|
|
17
|
+
declare const Fieldset: import("svelte").Component<Props, {}, "ref">;
|
|
18
|
+
type Fieldset = ReturnType<typeof Fieldset>;
|
|
19
|
+
export default Fieldset;
|
|
@@ -6,6 +6,6 @@ interface Props extends Omit<HTMLInputAttributes, 'value'> {
|
|
|
6
6
|
class?: string;
|
|
7
7
|
ref?: HTMLInputElement | null;
|
|
8
8
|
}
|
|
9
|
-
declare const Input: import("svelte").Component<Props, {}, "
|
|
9
|
+
declare const Input: import("svelte").Component<Props, {}, "value" | "ref">;
|
|
10
10
|
type Input = ReturnType<typeof Input>;
|
|
11
11
|
export default Input;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script lang="ts" generics="T extends string = string">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
3
4
|
import DropdownMenu from './DropdownMenu.svelte';
|
|
4
5
|
import type { Alignment, MenuItem, Placement } from '../types.js';
|
|
5
6
|
import { cx } from '../utils.js';
|
|
6
7
|
|
|
7
|
-
interface Props {
|
|
8
|
+
interface Props extends Omit<HTMLButtonAttributes, 'children' | 'class' | 'type'> {
|
|
8
9
|
open?: boolean;
|
|
9
10
|
/** Trigger text. */
|
|
10
11
|
label: string;
|
|
@@ -36,11 +37,15 @@
|
|
|
36
37
|
menuClass,
|
|
37
38
|
onSelect,
|
|
38
39
|
onOpenChange,
|
|
40
|
+
...rest
|
|
39
41
|
}: Props = $props();
|
|
40
42
|
</script>
|
|
41
43
|
|
|
42
44
|
{#snippet navTrigger({ props }: { props: Record<string, unknown> })}
|
|
45
|
+
<!-- `rest` first: the menu's trigger wiring (from `props`) and this component's own
|
|
46
|
+
attributes are policy and must win over forwarded attributes. -->
|
|
43
47
|
<button
|
|
48
|
+
{...rest}
|
|
44
49
|
{...props}
|
|
45
50
|
type="button"
|
|
46
51
|
class={cx('ldt-nav-menu', active && 'ldt-nav-menu--active', className)}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
2
3
|
import type { Alignment, MenuItem, Placement } from '../types.js';
|
|
3
4
|
declare function $$render<T extends string = string>(): {
|
|
4
|
-
props: {
|
|
5
|
+
props: Omit<HTMLButtonAttributes, "children" | "class" | "type"> & {
|
|
5
6
|
open?: boolean;
|
|
6
7
|
/** Trigger text. */
|
|
7
8
|
label: string;
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
decrementLabel = (name) => `Decrease ${name}`,
|
|
30
30
|
incrementLabel = (name) => `Increase ${name}`,
|
|
31
31
|
boxed = false,
|
|
32
|
+
disabled = false,
|
|
32
33
|
onValueChange,
|
|
34
|
+
onchange,
|
|
33
35
|
class: className,
|
|
34
36
|
inputClass,
|
|
35
37
|
ref = $bindable(null),
|
|
@@ -52,6 +54,7 @@
|
|
|
52
54
|
<button
|
|
53
55
|
class="ldt-number-field__button"
|
|
54
56
|
type="button"
|
|
57
|
+
{disabled}
|
|
55
58
|
aria-label={decrementLabel(label)}
|
|
56
59
|
aria-disabled={atMin || undefined}
|
|
57
60
|
onclick={() => !atMin && set(value - step)}>−</button
|
|
@@ -64,13 +67,21 @@
|
|
|
64
67
|
min={Number.isFinite(min) ? min : undefined}
|
|
65
68
|
max={Number.isFinite(max) ? max : undefined}
|
|
66
69
|
{step}
|
|
70
|
+
{disabled}
|
|
67
71
|
aria-label={label}
|
|
68
72
|
{...rest}
|
|
69
|
-
onchange={(event) =>
|
|
73
|
+
onchange={(event) => {
|
|
74
|
+
// `Number('')` is 0, not NaN — a cleared field must hit the documented fallback,
|
|
75
|
+
// not silently clamp to zero.
|
|
76
|
+
const raw = event.currentTarget.value;
|
|
77
|
+
set(raw === '' ? NaN : Number(raw));
|
|
78
|
+
onchange?.(event);
|
|
79
|
+
}}
|
|
70
80
|
/>
|
|
71
81
|
<button
|
|
72
82
|
class="ldt-number-field__button"
|
|
73
83
|
type="button"
|
|
84
|
+
{disabled}
|
|
74
85
|
aria-label={incrementLabel(label)}
|
|
75
86
|
aria-disabled={atMax || undefined}
|
|
76
87
|
onclick={() => !atMax && set(value + step)}>+</button
|
|
@@ -16,6 +16,6 @@ interface Props extends Omit<HTMLInputAttributes, 'type' | 'value' | 'min' | 'ma
|
|
|
16
16
|
inputClass?: string;
|
|
17
17
|
ref?: HTMLInputElement | null;
|
|
18
18
|
}
|
|
19
|
-
declare const NumberField: import("svelte").Component<Props, {}, "
|
|
19
|
+
declare const NumberField: import("svelte").Component<Props, {}, "value" | "ref">;
|
|
20
20
|
type NumberField = ReturnType<typeof NumberField>;
|
|
21
21
|
export default NumberField;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Pagination as PaginationPrimitive } from 'bits-ui';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { cx } from '../utils.js';
|
|
5
|
+
|
|
6
|
+
interface Props extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
|
|
7
|
+
/** Total number of items being paged through — not the number of pages. */
|
|
8
|
+
count: number;
|
|
9
|
+
perPage?: number;
|
|
10
|
+
/** 1-based current page. */
|
|
11
|
+
page?: number;
|
|
12
|
+
/** Pages kept either side of the current one before the trail turns into an ellipsis. */
|
|
13
|
+
siblingCount?: number;
|
|
14
|
+
/** Accessible name of the landmark. */
|
|
15
|
+
label?: string;
|
|
16
|
+
previousLabel?: string;
|
|
17
|
+
nextLabel?: string;
|
|
18
|
+
/** Accessible name for a page button, e.g. `(page) => `Page ${page}``. */
|
|
19
|
+
pageLabel?: (page: number) => string;
|
|
20
|
+
onPageChange?: (page: number) => void;
|
|
21
|
+
class?: string;
|
|
22
|
+
ref?: HTMLElement | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
count,
|
|
27
|
+
perPage = 10,
|
|
28
|
+
page = $bindable(1),
|
|
29
|
+
siblingCount = 1,
|
|
30
|
+
label = 'Pagination',
|
|
31
|
+
previousLabel = 'Previous',
|
|
32
|
+
nextLabel = 'Next',
|
|
33
|
+
pageLabel = (value: number) => `Page ${value}`,
|
|
34
|
+
onPageChange,
|
|
35
|
+
class: className,
|
|
36
|
+
ref = $bindable(null),
|
|
37
|
+
...rest
|
|
38
|
+
}: Props = $props();
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<!--
|
|
42
|
+
Bits' Root is a `<div>`; the landmark is ours, so the control set is reachable as "Pagination"
|
|
43
|
+
from a rotor without the page having to add one.
|
|
44
|
+
-->
|
|
45
|
+
<nav bind:this={ref} class={cx('ldt-pagination', className)} aria-label={label} {...rest}>
|
|
46
|
+
<PaginationPrimitive.Root
|
|
47
|
+
{count}
|
|
48
|
+
{perPage}
|
|
49
|
+
{siblingCount}
|
|
50
|
+
bind:page
|
|
51
|
+
onPageChange={(next) => onPageChange?.(next)}
|
|
52
|
+
class="ldt-pagination__list"
|
|
53
|
+
>
|
|
54
|
+
{#snippet children({ pages, currentPage })}
|
|
55
|
+
<PaginationPrimitive.PrevButton class="ldt-button ldt-button--quiet ldt-button--sm"
|
|
56
|
+
>{previousLabel}</PaginationPrimitive.PrevButton
|
|
57
|
+
>
|
|
58
|
+
{#each pages as item (item.key)}
|
|
59
|
+
{#if item.type === 'ellipsis'}
|
|
60
|
+
<!-- Presentational: the gap is a rendering device, and "…" read aloud between page
|
|
61
|
+
numbers is noise. The page buttons either side already say where the trail jumps. -->
|
|
62
|
+
<span class="ldt-pagination__ellipsis" aria-hidden="true">…</span>
|
|
63
|
+
{:else}
|
|
64
|
+
<!--
|
|
65
|
+
Rendered through Bits' `child` snippet rather than as a styled primitive: the
|
|
66
|
+
primitive merges its own `aria-label` last and would overwrite `pageLabel`. Spreading
|
|
67
|
+
`props` first leaves its behaviour intact while the naming stays ours.
|
|
68
|
+
-->
|
|
69
|
+
<PaginationPrimitive.Page page={item}>
|
|
70
|
+
{#snippet child({ props })}
|
|
71
|
+
<button
|
|
72
|
+
{...props}
|
|
73
|
+
class="ldt-button ldt-button--quiet ldt-button--sm ldt-pagination__page"
|
|
74
|
+
aria-label={pageLabel(item.value)}
|
|
75
|
+
aria-current={item.value === currentPage ? 'page' : undefined}>{item.value}</button
|
|
76
|
+
>
|
|
77
|
+
{/snippet}
|
|
78
|
+
</PaginationPrimitive.Page>
|
|
79
|
+
{/if}
|
|
80
|
+
{/each}
|
|
81
|
+
<PaginationPrimitive.NextButton class="ldt-button ldt-button--quiet ldt-button--sm"
|
|
82
|
+
>{nextLabel}</PaginationPrimitive.NextButton
|
|
83
|
+
>
|
|
84
|
+
{/snippet}
|
|
85
|
+
</PaginationPrimitive.Root>
|
|
86
|
+
</nav>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
interface Props extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
|
|
3
|
+
/** Total number of items being paged through — not the number of pages. */
|
|
4
|
+
count: number;
|
|
5
|
+
perPage?: number;
|
|
6
|
+
/** 1-based current page. */
|
|
7
|
+
page?: number;
|
|
8
|
+
/** Pages kept either side of the current one before the trail turns into an ellipsis. */
|
|
9
|
+
siblingCount?: number;
|
|
10
|
+
/** Accessible name of the landmark. */
|
|
11
|
+
label?: string;
|
|
12
|
+
previousLabel?: string;
|
|
13
|
+
nextLabel?: string;
|
|
14
|
+
/** Accessible name for a page button, e.g. `(page) => `Page ${page}``. */
|
|
15
|
+
pageLabel?: (page: number) => string;
|
|
16
|
+
onPageChange?: (page: number) => void;
|
|
17
|
+
class?: string;
|
|
18
|
+
ref?: HTMLElement | null;
|
|
19
|
+
}
|
|
20
|
+
declare const Pagination: import("svelte").Component<Props, {}, "ref" | "page">;
|
|
21
|
+
type Pagination = ReturnType<typeof Pagination>;
|
|
22
|
+
export default Pagination;
|
|
@@ -14,7 +14,7 @@ declare function $$render<T extends string = string>(): {
|
|
|
14
14
|
ref?: HTMLFieldSetElement | null;
|
|
15
15
|
};
|
|
16
16
|
exports: {};
|
|
17
|
-
bindings: "
|
|
17
|
+
bindings: "value" | "ref";
|
|
18
18
|
slots: {};
|
|
19
19
|
events: {};
|
|
20
20
|
};
|
|
@@ -22,7 +22,7 @@ declare class __sveltets_Render<T extends string = string> {
|
|
|
22
22
|
props(): ReturnType<typeof $$render<T>>['props'];
|
|
23
23
|
events(): ReturnType<typeof $$render<T>>['events'];
|
|
24
24
|
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
25
|
-
bindings(): "
|
|
25
|
+
bindings(): "value" | "ref";
|
|
26
26
|
exports(): {};
|
|
27
27
|
}
|
|
28
28
|
interface $$IsomorphicComponent {
|
|
@@ -18,7 +18,7 @@ declare function $$render<T extends string = string>(): {
|
|
|
18
18
|
ref?: HTMLSelectElement | null;
|
|
19
19
|
};
|
|
20
20
|
exports: {};
|
|
21
|
-
bindings: "
|
|
21
|
+
bindings: "value" | "ref";
|
|
22
22
|
slots: {};
|
|
23
23
|
events: {};
|
|
24
24
|
};
|
|
@@ -26,7 +26,7 @@ declare class __sveltets_Render<T extends string = string> {
|
|
|
26
26
|
props(): ReturnType<typeof $$render<T>>['props'];
|
|
27
27
|
events(): ReturnType<typeof $$render<T>>['events'];
|
|
28
28
|
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
29
|
-
bindings(): "
|
|
29
|
+
bindings(): "value" | "ref";
|
|
30
30
|
exports(): {};
|
|
31
31
|
}
|
|
32
32
|
interface $$IsomorphicComponent {
|
|
@@ -8,6 +8,19 @@
|
|
|
8
8
|
caption?: string;
|
|
9
9
|
/** Accessible name for the scroll region when there is no `caption`. */
|
|
10
10
|
regionLabel?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Pins the header row while the body scrolls. Needs a bounded scroll container — set
|
|
13
|
+
* `--ldt-table-height` on the wrapper, or the page scrolls instead and nothing sticks.
|
|
14
|
+
*/
|
|
15
|
+
sticky?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Rendered in place of the rows, as a single full-width cell. Pass it only when there is
|
|
18
|
+
* nothing to show — `empty={rows.length === 0 ? nothingYet : undefined}` — since the
|
|
19
|
+
* component cannot see inside `children` to count rows.
|
|
20
|
+
*/
|
|
21
|
+
empty?: Snippet;
|
|
22
|
+
/** Columns the empty cell spans. Defaults to the whole row. */
|
|
23
|
+
columns?: number;
|
|
11
24
|
/** `class` lands on the `<table>`; use `wrapperClass` for the scroll container. */
|
|
12
25
|
class?: string;
|
|
13
26
|
wrapperClass?: string;
|
|
@@ -19,6 +32,9 @@
|
|
|
19
32
|
let {
|
|
20
33
|
caption,
|
|
21
34
|
regionLabel = 'Data table',
|
|
35
|
+
sticky = false,
|
|
36
|
+
empty,
|
|
37
|
+
columns,
|
|
22
38
|
class: className,
|
|
23
39
|
wrapperClass,
|
|
24
40
|
children,
|
|
@@ -32,12 +48,23 @@
|
|
|
32
48
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
33
49
|
<div
|
|
34
50
|
bind:this={wrapperRef}
|
|
35
|
-
class={cx('ldt-table-wrap', wrapperClass)}
|
|
51
|
+
class={cx('ldt-table-wrap', sticky && 'ldt-table-wrap--sticky', wrapperClass)}
|
|
36
52
|
role="region"
|
|
37
53
|
tabindex="0"
|
|
38
54
|
aria-label={caption ?? regionLabel}
|
|
39
55
|
>
|
|
40
|
-
<table
|
|
41
|
-
|
|
56
|
+
<table
|
|
57
|
+
bind:this={ref}
|
|
58
|
+
class={cx('ldt-table', sticky && 'ldt-table--sticky', className)}
|
|
59
|
+
{...rest}
|
|
60
|
+
>
|
|
61
|
+
{#if caption}<caption class="ldt-sr-only">{caption}</caption
|
|
62
|
+
>{/if}{@render children()}{#if empty}<tbody class="ldt-table__empty">
|
|
63
|
+
<tr>
|
|
64
|
+
<!-- `colspan` defaults to the row-spanning maximum so the cell never leaves a gap
|
|
65
|
+
beside it, whatever the column count turns out to be. -->
|
|
66
|
+
<td colspan={columns ?? 1000}>{@render empty()}</td>
|
|
67
|
+
</tr>
|
|
68
|
+
</tbody>{/if}
|
|
42
69
|
</table>
|
|
43
70
|
</div>
|
|
@@ -5,6 +5,19 @@ interface Props extends HTMLTableAttributes {
|
|
|
5
5
|
caption?: string;
|
|
6
6
|
/** Accessible name for the scroll region when there is no `caption`. */
|
|
7
7
|
regionLabel?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Pins the header row while the body scrolls. Needs a bounded scroll container — set
|
|
10
|
+
* `--ldt-table-height` on the wrapper, or the page scrolls instead and nothing sticks.
|
|
11
|
+
*/
|
|
12
|
+
sticky?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Rendered in place of the rows, as a single full-width cell. Pass it only when there is
|
|
15
|
+
* nothing to show — `empty={rows.length === 0 ? nothingYet : undefined}` — since the
|
|
16
|
+
* component cannot see inside `children` to count rows.
|
|
17
|
+
*/
|
|
18
|
+
empty?: Snippet;
|
|
19
|
+
/** Columns the empty cell spans. Defaults to the whole row. */
|
|
20
|
+
columns?: number;
|
|
8
21
|
/** `class` lands on the `<table>`; use `wrapperClass` for the scroll container. */
|
|
9
22
|
class?: string;
|
|
10
23
|
wrapperClass?: string;
|