@sepale/components 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +65 -3
- package/dist/components/Accordion.svelte +48 -0
- package/dist/components/Accordion.svelte.d.ts +24 -0
- package/dist/components/Alert.svelte +107 -0
- package/dist/components/Alert.svelte.d.ts +40 -0
- package/dist/components/Avatar.svelte +75 -0
- package/dist/components/Avatar.svelte.d.ts +37 -0
- package/dist/components/Badge.svelte +46 -0
- package/dist/components/Badge.svelte.d.ts +22 -0
- package/dist/components/Breadcrumbs.svelte +36 -0
- package/dist/components/Breadcrumbs.svelte.d.ts +18 -0
- package/dist/components/Bubble.svelte +43 -0
- package/dist/components/Bubble.svelte.d.ts +23 -0
- package/dist/components/Button.svelte +132 -0
- package/dist/components/Button.svelte.d.ts +40 -0
- package/dist/components/Calendar.svelte +193 -0
- package/dist/components/Calendar.svelte.d.ts +60 -0
- package/dist/components/Card.svelte +69 -0
- package/dist/components/Card.svelte.d.ts +37 -0
- package/dist/components/Carousel.svelte +550 -0
- package/dist/components/Carousel.svelte.d.ts +124 -0
- package/dist/components/Checkbox.svelte +48 -0
- package/dist/components/Checkbox.svelte.d.ts +26 -0
- package/dist/components/Chip.svelte +60 -0
- package/dist/components/Chip.svelte.d.ts +26 -0
- package/dist/components/ChipsInput.svelte +158 -0
- package/dist/components/ChipsInput.svelte.d.ts +64 -0
- package/dist/components/Collapse.svelte +101 -0
- package/dist/components/Collapse.svelte.d.ts +46 -0
- package/dist/components/ColorPicker.svelte +135 -0
- package/dist/components/ColorPicker.svelte.d.ts +44 -0
- package/dist/components/Combobox.svelte +496 -0
- package/dist/components/Combobox.svelte.d.ts +96 -0
- package/dist/components/DataControl.svelte +754 -0
- package/dist/components/DataControl.svelte.d.ts +143 -0
- package/dist/components/DataHandler.svelte +67 -0
- package/dist/components/DataHandler.svelte.d.ts +44 -0
- package/dist/components/DatePicker.svelte +185 -0
- package/dist/components/DatePicker.svelte.d.ts +56 -0
- package/dist/components/Dialog.svelte +194 -0
- package/dist/components/Dialog.svelte.d.ts +51 -0
- package/dist/components/Divider.svelte +54 -0
- package/dist/components/Divider.svelte.d.ts +27 -0
- package/dist/components/Dock.svelte +34 -0
- package/dist/components/Dock.svelte.d.ts +18 -0
- package/dist/components/Drawer.svelte +174 -0
- package/dist/components/Drawer.svelte.d.ts +51 -0
- package/dist/components/Dropdown.svelte +183 -0
- package/dist/components/Dropdown.svelte.d.ts +52 -0
- package/dist/components/Editable.svelte +292 -0
- package/dist/components/Editable.svelte.d.ts +113 -0
- package/dist/components/EmptyState.svelte +64 -0
- package/dist/components/EmptyState.svelte.d.ts +29 -0
- package/dist/components/Fieldset.svelte +42 -0
- package/dist/components/Fieldset.svelte.d.ts +20 -0
- package/dist/components/FileDropzone.svelte +154 -0
- package/dist/components/FileDropzone.svelte.d.ts +45 -0
- package/dist/components/FileExplorer.svelte +643 -0
- package/dist/components/FileExplorer.svelte.d.ts +114 -0
- package/dist/components/FileInput.svelte +125 -0
- package/dist/components/FileInput.svelte.d.ts +51 -0
- package/dist/components/FloatingLabel.svelte +38 -0
- package/dist/components/FloatingLabel.svelte.d.ts +24 -0
- package/dist/components/Footer.svelte +68 -0
- package/dist/components/Footer.svelte.d.ts +37 -0
- package/dist/components/Grid.svelte +122 -0
- package/dist/components/Grid.svelte.d.ts +72 -0
- package/dist/components/Hero.svelte +62 -0
- package/dist/components/Hero.svelte.d.ts +33 -0
- package/dist/components/Input.svelte +184 -0
- package/dist/components/Input.svelte.d.ts +65 -0
- package/dist/components/Join.svelte +38 -0
- package/dist/components/Join.svelte.d.ts +21 -0
- package/dist/components/Kanban.svelte +284 -0
- package/dist/components/Kanban.svelte.d.ts +93 -0
- package/dist/components/Label.svelte +32 -0
- package/dist/components/Label.svelte.d.ts +21 -0
- package/dist/components/LaptopFrame.svelte +44 -0
- package/dist/components/LaptopFrame.svelte.d.ts +21 -0
- package/dist/components/List.svelte +95 -0
- package/dist/components/List.svelte.d.ts +69 -0
- package/dist/components/Loading.svelte +37 -0
- package/dist/components/Loading.svelte.d.ts +17 -0
- package/dist/components/Menu.svelte +144 -0
- package/dist/components/Menu.svelte.d.ts +53 -0
- package/dist/components/MobileFrame.svelte +41 -0
- package/dist/components/MobileFrame.svelte.d.ts +21 -0
- package/dist/components/Navbar.svelte +49 -0
- package/dist/components/Navbar.svelte.d.ts +25 -0
- package/dist/components/NumberInput.svelte +255 -0
- package/dist/components/NumberInput.svelte.d.ts +68 -0
- package/dist/components/Pagination.svelte +169 -0
- package/dist/components/Pagination.svelte.d.ts +50 -0
- package/dist/components/Progress.svelte +65 -0
- package/dist/components/Progress.svelte.d.ts +28 -0
- package/dist/components/RadialProgress.svelte +48 -0
- package/dist/components/RadialProgress.svelte.d.ts +20 -0
- package/dist/components/Radio.svelte +50 -0
- package/dist/components/Radio.svelte.d.ts +28 -0
- package/dist/components/Range.svelte +53 -0
- package/dist/components/Range.svelte.d.ts +26 -0
- package/dist/components/Rating.svelte +90 -0
- package/dist/components/Rating.svelte.d.ts +41 -0
- package/dist/components/Select.svelte +338 -0
- package/dist/components/Select.svelte.d.ts +74 -0
- package/dist/components/Sidebar.svelte +100 -0
- package/dist/components/Sidebar.svelte.d.ts +54 -0
- package/dist/components/Skeleton.svelte +40 -0
- package/dist/components/Skeleton.svelte.d.ts +18 -0
- package/dist/components/Stat.svelte +71 -0
- package/dist/components/Stat.svelte.d.ts +35 -0
- package/dist/components/Stats.svelte +38 -0
- package/dist/components/Stats.svelte.d.ts +22 -0
- package/dist/components/Steps.svelte +204 -0
- package/dist/components/Steps.svelte.d.ts +66 -0
- package/dist/components/Subtitle.svelte +44 -0
- package/dist/components/Subtitle.svelte.d.ts +26 -0
- package/dist/components/Swatch.svelte +61 -0
- package/dist/components/Swatch.svelte.d.ts +27 -0
- package/dist/components/Switch.svelte +105 -0
- package/dist/components/Switch.svelte.d.ts +44 -0
- package/dist/components/Table.svelte +243 -0
- package/dist/components/Table.svelte.d.ts +121 -0
- package/dist/components/Tabs.svelte +142 -0
- package/dist/components/Tabs.svelte.d.ts +80 -0
- package/dist/components/Text.svelte +43 -0
- package/dist/components/Text.svelte.d.ts +25 -0
- package/dist/components/Textarea.svelte +76 -0
- package/dist/components/Textarea.svelte.d.ts +39 -0
- package/dist/components/Title.svelte +46 -0
- package/dist/components/Title.svelte.d.ts +29 -0
- package/dist/components/Toast.svelte +154 -0
- package/dist/components/Toast.svelte.d.ts +49 -0
- package/dist/components/Toggle.svelte +48 -0
- package/dist/components/Toggle.svelte.d.ts +25 -0
- package/dist/components/Tooltip.svelte +70 -0
- package/dist/components/Tooltip.svelte.d.ts +34 -0
- package/dist/components/WindowFrame.svelte +53 -0
- package/dist/components/WindowFrame.svelte.d.ts +24 -0
- package/dist/components/accordion-context.d.ts +8 -0
- package/dist/components/accordion-context.js +25 -0
- package/dist/components/color.d.ts +39 -0
- package/dist/components/color.js +92 -0
- package/dist/components/data-handler.svelte.d.ts +362 -0
- package/dist/components/data-handler.svelte.js +690 -0
- package/dist/components/file-icons.d.ts +25 -0
- package/dist/components/file-icons.js +46 -0
- package/dist/family-types.d.ts +45 -0
- package/dist/family-types.js +73 -0
- package/dist/generated/classes.d.ts +17 -0
- package/dist/generated/classes.js +2511 -0
- package/dist/generated/components.d.ts +39 -0
- package/dist/generated/components.js +28666 -0
- package/dist/generated/registry.d.ts +35 -0
- package/dist/generated/registry.js +2658 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +99 -0
- package/dist/sepale-types.d.ts +8 -0
- package/dist/sepale-types.js +200 -0
- package/dist/theme.d.ts +123 -0
- package/dist/theme.js +226 -0
- package/dist/variants.d.ts +124 -0
- package/dist/variants.js +70 -0
- package/dist/vendor/air-datepicker/LICENSE.md +21 -0
- package/dist/vendor/air-datepicker/README.md +19 -0
- package/dist/vendor/air-datepicker/buttonPresets.d.ts +13 -0
- package/dist/vendor/air-datepicker/buttonPresets.js +12 -0
- package/dist/vendor/air-datepicker/consts.d.ts +18 -0
- package/dist/vendor/air-datepicker/consts.js +35 -0
- package/dist/vendor/air-datepicker/datepicker.d.ts +289 -0
- package/dist/vendor/air-datepicker/datepicker.js +1435 -0
- package/dist/vendor/air-datepicker/datepickerBody.d.ts +46 -0
- package/dist/vendor/air-datepicker/datepickerBody.js +386 -0
- package/dist/vendor/air-datepicker/datepickerButtons.d.ts +25 -0
- package/dist/vendor/air-datepicker/datepickerButtons.js +85 -0
- package/dist/vendor/air-datepicker/datepickerCell.d.ts +35 -0
- package/dist/vendor/air-datepicker/datepickerCell.js +297 -0
- package/dist/vendor/air-datepicker/datepickerKeyboard.d.ts +26 -0
- package/dist/vendor/air-datepicker/datepickerKeyboard.js +207 -0
- package/dist/vendor/air-datepicker/datepickerNav.d.ts +31 -0
- package/dist/vendor/air-datepicker/datepickerNav.js +200 -0
- package/dist/vendor/air-datepicker/datepickerTime.d.ts +60 -0
- package/dist/vendor/air-datepicker/datepickerTime.js +276 -0
- package/dist/vendor/air-datepicker/defaults.d.ts +65 -0
- package/dist/vendor/air-datepicker/defaults.js +79 -0
- package/dist/vendor/air-datepicker/index.d.ts +168 -0
- package/dist/vendor/air-datepicker/index.js +3 -0
- package/dist/vendor/air-datepicker/locale/ar.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/ar.js +14 -0
- package/dist/vendor/air-datepicker/locale/bg.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/bg.js +14 -0
- package/dist/vendor/air-datepicker/locale/ca.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/ca.js +14 -0
- package/dist/vendor/air-datepicker/locale/cs.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/cs.js +14 -0
- package/dist/vendor/air-datepicker/locale/da.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/da.js +14 -0
- package/dist/vendor/air-datepicker/locale/de.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/de.js +14 -0
- package/dist/vendor/air-datepicker/locale/el.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/el.js +14 -0
- package/dist/vendor/air-datepicker/locale/en.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/en.js +14 -0
- package/dist/vendor/air-datepicker/locale/es.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/es.js +14 -0
- package/dist/vendor/air-datepicker/locale/eu.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/eu.js +14 -0
- package/dist/vendor/air-datepicker/locale/fi.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/fi.js +14 -0
- package/dist/vendor/air-datepicker/locale/fr.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/fr.js +14 -0
- package/dist/vendor/air-datepicker/locale/hr.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/hr.js +14 -0
- package/dist/vendor/air-datepicker/locale/hu.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/hu.js +14 -0
- package/dist/vendor/air-datepicker/locale/id.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/id.js +14 -0
- package/dist/vendor/air-datepicker/locale/it.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/it.js +14 -0
- package/dist/vendor/air-datepicker/locale/ja.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/ja.js +14 -0
- package/dist/vendor/air-datepicker/locale/ko.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/ko.js +14 -0
- package/dist/vendor/air-datepicker/locale/nb.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/nb.js +14 -0
- package/dist/vendor/air-datepicker/locale/nl.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/nl.js +14 -0
- package/dist/vendor/air-datepicker/locale/pl.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/pl.js +14 -0
- package/dist/vendor/air-datepicker/locale/pt-BR.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/pt-BR.js +14 -0
- package/dist/vendor/air-datepicker/locale/pt.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/pt.js +14 -0
- package/dist/vendor/air-datepicker/locale/ro.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/ro.js +14 -0
- package/dist/vendor/air-datepicker/locale/ru.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/ru.js +14 -0
- package/dist/vendor/air-datepicker/locale/si.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/si.js +14 -0
- package/dist/vendor/air-datepicker/locale/sk.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/sk.js +14 -0
- package/dist/vendor/air-datepicker/locale/sl.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/sl.js +14 -0
- package/dist/vendor/air-datepicker/locale/sv.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/sv.js +14 -0
- package/dist/vendor/air-datepicker/locale/th.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/th.js +14 -0
- package/dist/vendor/air-datepicker/locale/tr.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/tr.js +16 -0
- package/dist/vendor/air-datepicker/locale/uk.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/uk.js +14 -0
- package/dist/vendor/air-datepicker/locale/zh.d.ts +4 -0
- package/dist/vendor/air-datepicker/locale/zh.js +14 -0
- package/dist/vendor/air-datepicker/typedefs.d.ts +3 -0
- package/dist/vendor/air-datepicker/typedefs.js +15 -0
- package/dist/vendor/air-datepicker/utils.d.ts +155 -0
- package/dist/vendor/air-datepicker/utils.js +395 -0
- package/dist/vendor/air-datepicker/withEvents.d.ts +1 -0
- package/dist/vendor/air-datepicker/withEvents.js +39 -0
- package/package.json +44 -3
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** What .mobile-frame is made of, classified. The props follow the
|
|
2
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
3
|
+
export declare const PREFIX = "mobile-frame";
|
|
4
|
+
export declare const AXES: {};
|
|
5
|
+
export declare const FLAGS: {};
|
|
6
|
+
export declare const PARTS: readonly ["screen"];
|
|
7
|
+
/**
|
|
8
|
+
* A phone drawn around content — for showcases and screenshots, not an
|
|
9
|
+
* emulator. Its width is `--mobile-frame-width`; the height follows.
|
|
10
|
+
*/
|
|
11
|
+
import type { Snippet } from 'svelte';
|
|
12
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
13
|
+
import { type AxisProps } from '../variants.js';
|
|
14
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLDivElement> {
|
|
15
|
+
class?: string;
|
|
16
|
+
/** What the screen shows. It scrolls. */
|
|
17
|
+
children?: Snippet;
|
|
18
|
+
}
|
|
19
|
+
declare const MobileFrame: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type MobileFrame = ReturnType<typeof MobileFrame>;
|
|
21
|
+
export default MobileFrame;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/** What .navbar is made of, classified. The props follow the
|
|
3
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
4
|
+
export const PREFIX = 'navbar';
|
|
5
|
+
|
|
6
|
+
export const AXES = {
|
|
7
|
+
size: ['xs', 'sm', 'md', 'lg', 'xl']
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export const FLAGS = {
|
|
11
|
+
sticky: 'Let the bar follow the page instead of scrolling away.'
|
|
12
|
+
} as const;
|
|
13
|
+
export const PARTS = ['start', 'center', 'end'] as const;
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
import type { Snippet } from 'svelte';
|
|
18
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
19
|
+
import { applyAxes, cx, type AxisProps } from '../variants.js';
|
|
20
|
+
|
|
21
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLElement> {
|
|
22
|
+
/** The three zones. start and end absorb the slack, so whatever is in
|
|
23
|
+
center stays centred while either side grows. */
|
|
24
|
+
start?: Snippet;
|
|
25
|
+
center?: Snippet;
|
|
26
|
+
end?: Snippet;
|
|
27
|
+
class?: string;
|
|
28
|
+
children?: Snippet;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
start,
|
|
33
|
+
center,
|
|
34
|
+
end,
|
|
35
|
+
class: klass = '',
|
|
36
|
+
children,
|
|
37
|
+
...axes
|
|
38
|
+
}: Props = $props();
|
|
39
|
+
|
|
40
|
+
let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
|
|
41
|
+
let classes = $derived(cx(PREFIX, applied.classes, klass));
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<nav {...applied.rest} class={classes}>
|
|
45
|
+
{#if start}<div class="navbar-start">{@render start()}</div>{/if}
|
|
46
|
+
{#if center}<div class="navbar-center">{@render center()}</div>{/if}
|
|
47
|
+
{#if end}<div class="navbar-end">{@render end()}</div>{/if}
|
|
48
|
+
{@render children?.()}
|
|
49
|
+
</nav>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** What .navbar is made of, classified. The props follow the
|
|
2
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
3
|
+
export declare const PREFIX = "navbar";
|
|
4
|
+
export declare const AXES: {
|
|
5
|
+
readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
|
|
6
|
+
};
|
|
7
|
+
export declare const FLAGS: {
|
|
8
|
+
readonly sticky: "Let the bar follow the page instead of scrolling away.";
|
|
9
|
+
};
|
|
10
|
+
export declare const PARTS: readonly ["start", "center", "end"];
|
|
11
|
+
import type { Snippet } from 'svelte';
|
|
12
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
13
|
+
import { type AxisProps } from '../variants.js';
|
|
14
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLElement> {
|
|
15
|
+
/** The three zones. start and end absorb the slack, so whatever is in
|
|
16
|
+
center stays centred while either side grows. */
|
|
17
|
+
start?: Snippet;
|
|
18
|
+
center?: Snippet;
|
|
19
|
+
end?: Snippet;
|
|
20
|
+
class?: string;
|
|
21
|
+
children?: Snippet;
|
|
22
|
+
}
|
|
23
|
+
declare const Navbar: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type Navbar = ReturnType<typeof Navbar>;
|
|
25
|
+
export default Navbar;
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/** What .number-input is made of, classified. The props follow the
|
|
3
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
4
|
+
export const PREFIX = 'number-input';
|
|
5
|
+
|
|
6
|
+
export const AXES = {
|
|
7
|
+
size: ['xs', 'sm', 'md', 'lg', 'xl']
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export const FLAGS = {} as const;
|
|
11
|
+
export const PARTS = ['dec', 'field', 'inc'] as const;
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
/**
|
|
16
|
+
* A number field, aligned to the end like a figure in a column.
|
|
17
|
+
*
|
|
18
|
+
* NOT an `<input type="number">`: that one follows the browser's locale, and
|
|
19
|
+
* in French Firefox refuses `.` while Chrome takes it, so the same keystroke
|
|
20
|
+
* gives a number in one and an empty, invalid field in the other. This is a
|
|
21
|
+
* text field with a numeric keyboard (`inputmode`), and it reads `.` and `,`
|
|
22
|
+
* alike as the decimal separator. What the native field did, it does:
|
|
23
|
+
*
|
|
24
|
+
* - typing is filtered to what a number can be on its way (`-`, digits,
|
|
25
|
+
* one separator); a paste is cleaned of its spaces first;
|
|
26
|
+
* - ↑ / ↓ step by `step`, within `min` / `max`;
|
|
27
|
+
* - `min`, `max` and `step` set the field's validity, so `:invalid` and
|
|
28
|
+
* the validator behave as before; `required` is the native one;
|
|
29
|
+
* - with a `name`, a hidden input submits the value written with a `.`,
|
|
30
|
+
* whatever separator was typed.
|
|
31
|
+
*
|
|
32
|
+
* With `controls`, a − and a + frame it. The buttons are structure, not
|
|
33
|
+
* paint, so they are a prop and not a variant: a family draws them when
|
|
34
|
+
* they are there.
|
|
35
|
+
*
|
|
36
|
+
* `value` is bindable and always a number: what the field holds, parsed,
|
|
37
|
+
* or `null` when it is empty or holds no finite number. Never a string.
|
|
38
|
+
*/
|
|
39
|
+
import { untrack, type Snippet } from 'svelte';
|
|
40
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
41
|
+
import { applyAxes, cx, type AxisProps } from '../variants.js';
|
|
42
|
+
|
|
43
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLInputAttributes, 'size' | 'value' | 'type'> {
|
|
44
|
+
/** Colour the field by its own validity (`required`, `min`, `max`, `step`)
|
|
45
|
+
and reveal `hint` once the browser marks it invalid. */
|
|
46
|
+
validator?: boolean;
|
|
47
|
+
/** Shown under the field when it is invalid. */
|
|
48
|
+
hint?: Snippet;
|
|
49
|
+
/** Bindable. Always a number, or `null` when the field is empty. */
|
|
50
|
+
value?: number | null;
|
|
51
|
+
/** Draw a − and a + around the field. Off: the field alone. */
|
|
52
|
+
controls?: boolean;
|
|
53
|
+
min?: number;
|
|
54
|
+
max?: number;
|
|
55
|
+
/** `any`: every value is valid, and the buttons move by 1. */
|
|
56
|
+
step?: number | 'any';
|
|
57
|
+
disabled?: boolean;
|
|
58
|
+
/** After every change, typed or stepped. */
|
|
59
|
+
onValue?: (value: number | null) => void;
|
|
60
|
+
decreaseLabel?: string;
|
|
61
|
+
increaseLabel?: string;
|
|
62
|
+
/** Submitted with the form, as a number written with a `.`. */
|
|
63
|
+
name?: string;
|
|
64
|
+
/** What the browser says when the field is invalid. */
|
|
65
|
+
messages?: Partial<{ nan: string; min: string; max: string; step: string }>;
|
|
66
|
+
class?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The axes are deliberately NOT destructured: they travel in `axes` and are
|
|
70
|
+
// resolved by name, so an axis a family drops changes nothing down here.
|
|
71
|
+
let {
|
|
72
|
+
validator = false,
|
|
73
|
+
hint,
|
|
74
|
+
value = $bindable(null),
|
|
75
|
+
controls = false,
|
|
76
|
+
min,
|
|
77
|
+
max,
|
|
78
|
+
step = 1,
|
|
79
|
+
disabled = false,
|
|
80
|
+
onValue,
|
|
81
|
+
decreaseLabel = 'Decrease',
|
|
82
|
+
increaseLabel = 'Increase',
|
|
83
|
+
name,
|
|
84
|
+
messages = {},
|
|
85
|
+
/* The field needs these four itself: taken out of the rest, and the
|
|
86
|
+
caller's own called after the field's (see the <input>). */
|
|
87
|
+
onbeforeinput: theirBeforeInput,
|
|
88
|
+
oninput: theirInput,
|
|
89
|
+
onblur: theirBlur,
|
|
90
|
+
onkeydown: theirKeydown,
|
|
91
|
+
class: klass = '',
|
|
92
|
+
...axes
|
|
93
|
+
}: Props = $props();
|
|
94
|
+
|
|
95
|
+
let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
|
|
96
|
+
let classes = $derived(cx(PREFIX, applied.classes, validator && 'validator', klass));
|
|
97
|
+
|
|
98
|
+
let field: HTMLInputElement | undefined = $state();
|
|
99
|
+
|
|
100
|
+
/* What a number can be while it is being typed: `-`, `12,`, `.5`. */
|
|
101
|
+
const PARTIAL = /^-?\d*(?:[.,]\d*)?$/;
|
|
102
|
+
/* Spaces a paste may carry: thousands, French narrow no-break included. */
|
|
103
|
+
const SPACES = /[\s ]/g;
|
|
104
|
+
|
|
105
|
+
/** A number from text: `.` or `,` as the separator; null when there is none. */
|
|
106
|
+
function parse(text: string): number | null {
|
|
107
|
+
const s = text.trim().replace(',', '.');
|
|
108
|
+
if (!/\d/.test(s)) return null;
|
|
109
|
+
const n = Number(s);
|
|
110
|
+
return Number.isFinite(n) ? n : null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const format = (n: number | null | undefined) => (n == null || !Number.isFinite(n) ? '' : String(n));
|
|
114
|
+
|
|
115
|
+
/* The text in the field, kept as typed (`12,5` stays `12,5`). It follows
|
|
116
|
+
`value` only when `value` changes from outside. */
|
|
117
|
+
let text = $state(untrack(() => format(value)));
|
|
118
|
+
$effect(() => {
|
|
119
|
+
const outside = value ?? null;
|
|
120
|
+
if (untrack(() => parse(text)) !== outside) text = format(outside);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
function commit(next: string) {
|
|
124
|
+
text = next;
|
|
125
|
+
value = parse(next);
|
|
126
|
+
onValue?.(value);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Keep out what no number can become; clean a paste of its spaces. */
|
|
130
|
+
function guard(e: InputEvent) {
|
|
131
|
+
if (!field || e.data == null) return;
|
|
132
|
+
const from = field.selectionStart ?? field.value.length;
|
|
133
|
+
const to = field.selectionEnd ?? from;
|
|
134
|
+
const data = e.data.replace(SPACES, '');
|
|
135
|
+
const next = field.value.slice(0, from) + data + field.value.slice(to);
|
|
136
|
+
if (!PARTIAL.test(next)) return e.preventDefault();
|
|
137
|
+
if (data !== e.data) {
|
|
138
|
+
e.preventDefault();
|
|
139
|
+
field.setRangeText(data, from, to, 'end');
|
|
140
|
+
commit(field.value);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* The last word, for input that skipped beforeinput (some mobile IMEs). */
|
|
145
|
+
function sanitise() {
|
|
146
|
+
if (!field) return;
|
|
147
|
+
if (!PARTIAL.test(field.value)) field.value = text;
|
|
148
|
+
else commit(field.value);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* `12,` or `-` left behind: tidied once the field is left. */
|
|
152
|
+
function tidy() {
|
|
153
|
+
if (text === '-' || /^-?[.,]$/.test(text)) commit('');
|
|
154
|
+
else if (/[.,]$/.test(text)) commit(text.slice(0, -1));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function arrows(e: KeyboardEvent) {
|
|
158
|
+
if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
|
|
159
|
+
e.preventDefault();
|
|
160
|
+
stepBy(e.key === 'ArrowUp');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Digits after the point, to keep 0.1 + 0.2 from showing 0.30000000000000004. */
|
|
164
|
+
const decimals = (n: number | null | undefined) =>
|
|
165
|
+
n == null ? 0 : (String(n).split('.')[1]?.length ?? 0);
|
|
166
|
+
|
|
167
|
+
function stepBy(up: boolean) {
|
|
168
|
+
if (disabled) return;
|
|
169
|
+
const by = step === 'any' ? 1 : step;
|
|
170
|
+
const current = parse(text);
|
|
171
|
+
let next = current === null ? (min ?? 0) : current + (up ? by : -by);
|
|
172
|
+
/* On the grid `min + k × step`, as stepUp() / stepDown() do. */
|
|
173
|
+
if (current !== null && step !== 'any') {
|
|
174
|
+
const base = min ?? 0;
|
|
175
|
+
next = base + Math.round((next - base) / by) * by;
|
|
176
|
+
}
|
|
177
|
+
next = Math.min(max ?? Infinity, Math.max(min ?? -Infinity, next));
|
|
178
|
+
next = Number(next.toFixed(Math.max(decimals(by), decimals(current), decimals(min))));
|
|
179
|
+
commit(format(next));
|
|
180
|
+
field?.dispatchEvent(new Event('change', { bubbles: true }));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* The validity the native field had: min, max, step, and "not a number". */
|
|
184
|
+
let problem = $derived.by(() => {
|
|
185
|
+
const n = parse(text);
|
|
186
|
+
if (n === null) return text.trim() && !PARTIAL.test(text.trim()) ? (messages.nan ?? 'Enter a number.') : '';
|
|
187
|
+
if (min !== undefined && n < min) return messages.min ?? `Enter ${min} or more.`;
|
|
188
|
+
if (max !== undefined && n > max) return messages.max ?? `Enter ${max} or less.`;
|
|
189
|
+
if (step !== 'any' && step > 0) {
|
|
190
|
+
const k = (n - (min ?? 0)) / step;
|
|
191
|
+
if (Math.abs(k - Math.round(k)) > 1e-9) return messages.step ?? `Enter a multiple of ${step}.`;
|
|
192
|
+
}
|
|
193
|
+
return '';
|
|
194
|
+
});
|
|
195
|
+
$effect(() => field?.setCustomValidity(problem));
|
|
196
|
+
|
|
197
|
+
/* A minus on the phone's keyboard only where a negative is allowed: iOS's
|
|
198
|
+
decimal pad has none. */
|
|
199
|
+
let mode = $derived<'decimal' | 'text'>(min !== undefined && min >= 0 ? 'decimal' : 'text');
|
|
200
|
+
</script>
|
|
201
|
+
|
|
202
|
+
<div class={classes}>
|
|
203
|
+
{#if controls}
|
|
204
|
+
<button
|
|
205
|
+
type="button"
|
|
206
|
+
class="number-input-dec"
|
|
207
|
+
tabindex="-1"
|
|
208
|
+
aria-label={decreaseLabel}
|
|
209
|
+
disabled={disabled || (min !== undefined && value !== null && value <= min)}
|
|
210
|
+
onclick={() => stepBy(false)}>−</button
|
|
211
|
+
>
|
|
212
|
+
{/if}
|
|
213
|
+
<input
|
|
214
|
+
{...applied.rest}
|
|
215
|
+
bind:this={field}
|
|
216
|
+
type="text"
|
|
217
|
+
inputmode={mode}
|
|
218
|
+
autocomplete="off"
|
|
219
|
+
role="spinbutton"
|
|
220
|
+
aria-valuenow={value ?? undefined}
|
|
221
|
+
aria-valuemin={min}
|
|
222
|
+
aria-valuemax={max}
|
|
223
|
+
class="number-input-field"
|
|
224
|
+
value={text}
|
|
225
|
+
{disabled}
|
|
226
|
+
onbeforeinput={(e) => {
|
|
227
|
+
guard(e);
|
|
228
|
+
theirBeforeInput?.(e);
|
|
229
|
+
}}
|
|
230
|
+
oninput={(e) => {
|
|
231
|
+
sanitise();
|
|
232
|
+
theirInput?.(e);
|
|
233
|
+
}}
|
|
234
|
+
onblur={(e) => {
|
|
235
|
+
tidy();
|
|
236
|
+
theirBlur?.(e);
|
|
237
|
+
}}
|
|
238
|
+
onkeydown={(e) => {
|
|
239
|
+
arrows(e);
|
|
240
|
+
theirKeydown?.(e);
|
|
241
|
+
}}
|
|
242
|
+
/>
|
|
243
|
+
{#if name}<input type="hidden" {name} value={format(value)} {disabled} />{/if}
|
|
244
|
+
{#if controls}
|
|
245
|
+
<button
|
|
246
|
+
type="button"
|
|
247
|
+
class="number-input-inc"
|
|
248
|
+
tabindex="-1"
|
|
249
|
+
aria-label={increaseLabel}
|
|
250
|
+
disabled={disabled || (max !== undefined && value !== null && value >= max)}
|
|
251
|
+
onclick={() => stepBy(true)}>+</button
|
|
252
|
+
>
|
|
253
|
+
{/if}
|
|
254
|
+
</div>
|
|
255
|
+
{#if hint}<p class="validator-hint">{@render hint()}</p>{/if}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** What .number-input is made of, classified. The props follow the
|
|
2
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
3
|
+
export declare const PREFIX = "number-input";
|
|
4
|
+
export declare const AXES: {
|
|
5
|
+
readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
|
|
6
|
+
};
|
|
7
|
+
export declare const FLAGS: {};
|
|
8
|
+
export declare const PARTS: readonly ["dec", "field", "inc"];
|
|
9
|
+
/**
|
|
10
|
+
* A number field, aligned to the end like a figure in a column.
|
|
11
|
+
*
|
|
12
|
+
* NOT an `<input type="number">`: that one follows the browser's locale, and
|
|
13
|
+
* in French Firefox refuses `.` while Chrome takes it, so the same keystroke
|
|
14
|
+
* gives a number in one and an empty, invalid field in the other. This is a
|
|
15
|
+
* text field with a numeric keyboard (`inputmode`), and it reads `.` and `,`
|
|
16
|
+
* alike as the decimal separator. What the native field did, it does:
|
|
17
|
+
*
|
|
18
|
+
* - typing is filtered to what a number can be on its way (`-`, digits,
|
|
19
|
+
* one separator); a paste is cleaned of its spaces first;
|
|
20
|
+
* - ↑ / ↓ step by `step`, within `min` / `max`;
|
|
21
|
+
* - `min`, `max` and `step` set the field's validity, so `:invalid` and
|
|
22
|
+
* the validator behave as before; `required` is the native one;
|
|
23
|
+
* - with a `name`, a hidden input submits the value written with a `.`,
|
|
24
|
+
* whatever separator was typed.
|
|
25
|
+
*
|
|
26
|
+
* With `controls`, a − and a + frame it. The buttons are structure, not
|
|
27
|
+
* paint, so they are a prop and not a variant: a family draws them when
|
|
28
|
+
* they are there.
|
|
29
|
+
*
|
|
30
|
+
* `value` is bindable and always a number: what the field holds, parsed,
|
|
31
|
+
* or `null` when it is empty or holds no finite number. Never a string.
|
|
32
|
+
*/
|
|
33
|
+
import { type Snippet } from 'svelte';
|
|
34
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
35
|
+
import { type AxisProps } from '../variants.js';
|
|
36
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLInputAttributes, 'size' | 'value' | 'type'> {
|
|
37
|
+
/** Colour the field by its own validity (`required`, `min`, `max`, `step`)
|
|
38
|
+
and reveal `hint` once the browser marks it invalid. */
|
|
39
|
+
validator?: boolean;
|
|
40
|
+
/** Shown under the field when it is invalid. */
|
|
41
|
+
hint?: Snippet;
|
|
42
|
+
/** Bindable. Always a number, or `null` when the field is empty. */
|
|
43
|
+
value?: number | null;
|
|
44
|
+
/** Draw a − and a + around the field. Off: the field alone. */
|
|
45
|
+
controls?: boolean;
|
|
46
|
+
min?: number;
|
|
47
|
+
max?: number;
|
|
48
|
+
/** `any`: every value is valid, and the buttons move by 1. */
|
|
49
|
+
step?: number | 'any';
|
|
50
|
+
disabled?: boolean;
|
|
51
|
+
/** After every change, typed or stepped. */
|
|
52
|
+
onValue?: (value: number | null) => void;
|
|
53
|
+
decreaseLabel?: string;
|
|
54
|
+
increaseLabel?: string;
|
|
55
|
+
/** Submitted with the form, as a number written with a `.`. */
|
|
56
|
+
name?: string;
|
|
57
|
+
/** What the browser says when the field is invalid. */
|
|
58
|
+
messages?: Partial<{
|
|
59
|
+
nan: string;
|
|
60
|
+
min: string;
|
|
61
|
+
max: string;
|
|
62
|
+
step: string;
|
|
63
|
+
}>;
|
|
64
|
+
class?: string;
|
|
65
|
+
}
|
|
66
|
+
declare const NumberInput: import("svelte").Component<Props, {}, "value">;
|
|
67
|
+
type NumberInput = ReturnType<typeof NumberInput>;
|
|
68
|
+
export default NumberInput;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/** What .pagination is made of, classified. The props follow the
|
|
3
|
+
families the project registers (variants.ts, ADR 0015).
|
|
4
|
+
|
|
5
|
+
The pagination draws its own pages: `.pagination-item`,
|
|
6
|
+
`.pagination-gap` and `.pagination-text` are PARTS. The current page is marked by
|
|
7
|
+
`aria-current="page"`, not by a class. */
|
|
8
|
+
export const PREFIX = 'pagination';
|
|
9
|
+
|
|
10
|
+
export const AXES = {
|
|
11
|
+
/* The fill of the current page. */
|
|
12
|
+
color: ['primary', 'secondary', 'accent', 'neutral', 'info', 'success', 'warning', 'error'],
|
|
13
|
+
size: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
14
|
+
/* How the pages are drawn. All three keep the same parts and props
|
|
15
|
+
(a different structure would be another component):
|
|
16
|
+
dots — a dot per page instead of its number, as under a carousel;
|
|
17
|
+
bars — a segment per page, stretched across the row;
|
|
18
|
+
count — no pages at all, "3 / 8" between previous and next. */
|
|
19
|
+
variant: ['dots', 'bars', 'count']
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
export const FLAGS = {} as const;
|
|
23
|
+
export const PARTS = ['item', 'gap', 'text', 'number'] as const;
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
/**
|
|
28
|
+
* A row of pages to jump between. Pages are numbered from 1. Past a few
|
|
29
|
+
* pages, the ones far from the current page fold into a gap: the first
|
|
30
|
+
* and the last stay, and `siblings` pages on each side of the current one.
|
|
31
|
+
* The dots and bars variants always show every page, and no previous /
|
|
32
|
+
* next; the count variant shows no page, only "3 / 8" between them.
|
|
33
|
+
*/
|
|
34
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
35
|
+
import { applyAxes, cx, type AxisProps } from '../variants.js';
|
|
36
|
+
|
|
37
|
+
// `color` is ours — the HTML attribute of the same name is long obsolete.
|
|
38
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLElement>, 'color'> {
|
|
39
|
+
/** How many pages there are. */
|
|
40
|
+
count: number;
|
|
41
|
+
/** The current page, from 1. Bindable. */
|
|
42
|
+
page?: number;
|
|
43
|
+
/** Pages kept on each side of the current one before a gap. Default 1. */
|
|
44
|
+
siblings?: number;
|
|
45
|
+
/** Keep the first and last pages, with a gap, around the window. Default no.
|
|
46
|
+
Without: only the current page and its siblings — `< 6 >` with
|
|
47
|
+
siblings 0, `< 5 6 7 >` with 1. */
|
|
48
|
+
edges?: boolean;
|
|
49
|
+
/** Draw previous / next. Default: yes, except in the dots and bars variants. */
|
|
50
|
+
controls?: boolean;
|
|
51
|
+
/** Called with the new page after every change. */
|
|
52
|
+
onPage?: (page: number) => void;
|
|
53
|
+
/** The accessible name of a page. Default `Page N`. */
|
|
54
|
+
pageLabel?: (page: number) => string;
|
|
55
|
+
/** The text of the count variant. Default `3 / 8`. */
|
|
56
|
+
countLabel?: (page: number, count: number) => string;
|
|
57
|
+
/** The accessible names of the controls. */
|
|
58
|
+
previousLabel?: string;
|
|
59
|
+
nextLabel?: string;
|
|
60
|
+
class?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// The axes are deliberately NOT destructured — except `variant`, which
|
|
64
|
+
// also changes what is drawn. It is handed back to applyAxes below.
|
|
65
|
+
let {
|
|
66
|
+
count,
|
|
67
|
+
page = $bindable(1),
|
|
68
|
+
siblings = 1,
|
|
69
|
+
edges = false,
|
|
70
|
+
controls,
|
|
71
|
+
onPage,
|
|
72
|
+
pageLabel = (p: number) => `Page ${p}`,
|
|
73
|
+
countLabel = (p: number, n: number) => `${p} / ${n}`,
|
|
74
|
+
previousLabel = 'Previous page',
|
|
75
|
+
nextLabel = 'Next page',
|
|
76
|
+
variant,
|
|
77
|
+
class: klass = '',
|
|
78
|
+
...axes
|
|
79
|
+
}: Props = $props();
|
|
80
|
+
|
|
81
|
+
let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, { ...axes, variant }));
|
|
82
|
+
let classes = $derived(cx(PREFIX, applied.classes, klass));
|
|
83
|
+
|
|
84
|
+
/** Every page drawn, none folded, no number shown. */
|
|
85
|
+
let marks = $derived(variant === 'dots' || variant === 'bars');
|
|
86
|
+
let showControls = $derived(controls ?? !marks);
|
|
87
|
+
|
|
88
|
+
/** The pages to draw, with `null` for a gap. */
|
|
89
|
+
let pages = $derived.by((): (number | null)[] => {
|
|
90
|
+
const all = Array.from({ length: Math.max(0, count) }, (_, i) => i + 1);
|
|
91
|
+
if (variant === 'count') return [];
|
|
92
|
+
if (marks) return all;
|
|
93
|
+
// No edges: a window of 2 * siblings + 1 pages, slid back inside the row at its ends.
|
|
94
|
+
if (!edges) {
|
|
95
|
+
const w = Math.min(count, 2 * siblings + 1);
|
|
96
|
+
const start = Math.min(Math.max(1, page - siblings), count - w + 1);
|
|
97
|
+
return all.slice(start - 1, start - 1 + w);
|
|
98
|
+
}
|
|
99
|
+
// first + last + current + siblings + two gaps: below that, folding saves nothing
|
|
100
|
+
if (count <= 2 * siblings + 5) return all;
|
|
101
|
+
// Always 2 * siblings + 5 slots, so the row keeps its width: near an
|
|
102
|
+
// end, the pages the missing gap would have taken are shown instead.
|
|
103
|
+
const edge = 2 * siblings + 3;
|
|
104
|
+
if (page <= siblings + 3) return [...all.slice(0, edge), null, count];
|
|
105
|
+
if (page >= count - siblings - 2) return [1, null, ...all.slice(count - edge)];
|
|
106
|
+
return [1, null, ...all.slice(page - siblings - 1, page + siblings), null, count];
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/** Which way the last move went, for a family that animates the numbers.
|
|
110
|
+
Unset until the first move, so nothing plays on load. */
|
|
111
|
+
let direction = $state<'next' | 'previous'>();
|
|
112
|
+
|
|
113
|
+
function go(p: number) {
|
|
114
|
+
const next = Math.min(Math.max(1, p), count);
|
|
115
|
+
if (next === page) return;
|
|
116
|
+
direction = next > page ? 'next' : 'previous';
|
|
117
|
+
page = next;
|
|
118
|
+
onPage?.(next);
|
|
119
|
+
}
|
|
120
|
+
</script>
|
|
121
|
+
|
|
122
|
+
<nav {...applied.rest} class={classes} data-direction={direction}>
|
|
123
|
+
{#if showControls}
|
|
124
|
+
<button
|
|
125
|
+
type="button"
|
|
126
|
+
class={`${PREFIX}-item`}
|
|
127
|
+
aria-label={previousLabel}
|
|
128
|
+
disabled={page <= 1}
|
|
129
|
+
onclick={() => go(page - 1)}
|
|
130
|
+
>
|
|
131
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg>
|
|
132
|
+
</button>
|
|
133
|
+
{/if}
|
|
134
|
+
|
|
135
|
+
{#if variant === 'count'}
|
|
136
|
+
<span class={`${PREFIX}-text`} aria-live="polite">{countLabel(page, count)}</span>
|
|
137
|
+
{/if}
|
|
138
|
+
|
|
139
|
+
<!-- Unkeyed: the slots stay put and only their numbers change, so the
|
|
140
|
+
current page's fill moves between fixed buttons. -->
|
|
141
|
+
{#each pages as p}
|
|
142
|
+
{#if p === null}
|
|
143
|
+
<span class={`${PREFIX}-gap`} aria-hidden="true">…</span>
|
|
144
|
+
{:else}
|
|
145
|
+
<button
|
|
146
|
+
type="button"
|
|
147
|
+
class={`${PREFIX}-item`}
|
|
148
|
+
aria-label={pageLabel(p)}
|
|
149
|
+
aria-current={p === page ? 'page' : undefined}
|
|
150
|
+
onclick={() => go(p)}
|
|
151
|
+
>
|
|
152
|
+
<!-- Keyed: a new number is a new span, so its entrance can play. -->
|
|
153
|
+
{#key p}<span class={`${PREFIX}-number`}>{p}</span>{/key}
|
|
154
|
+
</button>
|
|
155
|
+
{/if}
|
|
156
|
+
{/each}
|
|
157
|
+
|
|
158
|
+
{#if showControls}
|
|
159
|
+
<button
|
|
160
|
+
type="button"
|
|
161
|
+
class={`${PREFIX}-item`}
|
|
162
|
+
aria-label={nextLabel}
|
|
163
|
+
disabled={page >= count}
|
|
164
|
+
onclick={() => go(page + 1)}
|
|
165
|
+
>
|
|
166
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg>
|
|
167
|
+
</button>
|
|
168
|
+
{/if}
|
|
169
|
+
</nav>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** What .pagination is made of, classified. The props follow the
|
|
2
|
+
families the project registers (variants.ts, ADR 0015).
|
|
3
|
+
|
|
4
|
+
The pagination draws its own pages: `.pagination-item`,
|
|
5
|
+
`.pagination-gap` and `.pagination-text` are PARTS. The current page is marked by
|
|
6
|
+
`aria-current="page"`, not by a class. */
|
|
7
|
+
export declare const PREFIX = "pagination";
|
|
8
|
+
export declare const AXES: {
|
|
9
|
+
readonly color: readonly ["primary", "secondary", "accent", "neutral", "info", "success", "warning", "error"];
|
|
10
|
+
readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
|
|
11
|
+
readonly variant: readonly ["dots", "bars", "count"];
|
|
12
|
+
};
|
|
13
|
+
export declare const FLAGS: {};
|
|
14
|
+
export declare const PARTS: readonly ["item", "gap", "text", "number"];
|
|
15
|
+
/**
|
|
16
|
+
* A row of pages to jump between. Pages are numbered from 1. Past a few
|
|
17
|
+
* pages, the ones far from the current page fold into a gap: the first
|
|
18
|
+
* and the last stay, and `siblings` pages on each side of the current one.
|
|
19
|
+
* The dots and bars variants always show every page, and no previous /
|
|
20
|
+
* next; the count variant shows no page, only "3 / 8" between them.
|
|
21
|
+
*/
|
|
22
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
23
|
+
import { type AxisProps } from '../variants.js';
|
|
24
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLElement>, 'color'> {
|
|
25
|
+
/** How many pages there are. */
|
|
26
|
+
count: number;
|
|
27
|
+
/** The current page, from 1. Bindable. */
|
|
28
|
+
page?: number;
|
|
29
|
+
/** Pages kept on each side of the current one before a gap. Default 1. */
|
|
30
|
+
siblings?: number;
|
|
31
|
+
/** Keep the first and last pages, with a gap, around the window. Default no.
|
|
32
|
+
Without: only the current page and its siblings — `< 6 >` with
|
|
33
|
+
siblings 0, `< 5 6 7 >` with 1. */
|
|
34
|
+
edges?: boolean;
|
|
35
|
+
/** Draw previous / next. Default: yes, except in the dots and bars variants. */
|
|
36
|
+
controls?: boolean;
|
|
37
|
+
/** Called with the new page after every change. */
|
|
38
|
+
onPage?: (page: number) => void;
|
|
39
|
+
/** The accessible name of a page. Default `Page N`. */
|
|
40
|
+
pageLabel?: (page: number) => string;
|
|
41
|
+
/** The text of the count variant. Default `3 / 8`. */
|
|
42
|
+
countLabel?: (page: number, count: number) => string;
|
|
43
|
+
/** The accessible names of the controls. */
|
|
44
|
+
previousLabel?: string;
|
|
45
|
+
nextLabel?: string;
|
|
46
|
+
class?: string;
|
|
47
|
+
}
|
|
48
|
+
declare const Pagination: import("svelte").Component<Props, {}, "page">;
|
|
49
|
+
type Pagination = ReturnType<typeof Pagination>;
|
|
50
|
+
export default Pagination;
|