@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,48 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/** What .checkbox is made of, classified. The props follow the
|
|
3
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
4
|
+
export const PREFIX = 'checkbox';
|
|
5
|
+
|
|
6
|
+
export const AXES = {
|
|
7
|
+
color: ['primary', 'secondary', 'accent', 'neutral', 'info', 'success', 'warning', 'error'],
|
|
8
|
+
size: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
9
|
+
/* A square checkbox where the family rounds them, and the other way round. */
|
|
10
|
+
shape: ['square'],
|
|
11
|
+
variant: ['outline']
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
export const FLAGS = {} as const;
|
|
15
|
+
export const PARTS = [] as const;
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<script lang="ts">
|
|
19
|
+
import type { Snippet } from 'svelte';
|
|
20
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
21
|
+
import { applyAxes, cx, type AxisProps } from '../variants.js';
|
|
22
|
+
|
|
23
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLInputAttributes, 'size' | 'color' | 'type'> {
|
|
24
|
+
/** Colour the control by its own validity and reveal `hint` when invalid. */
|
|
25
|
+
validator?: boolean;
|
|
26
|
+
/** Shown below once invalid. It must be the control's next sibling for the
|
|
27
|
+
CSS to reach it, which is why it is a prop rather than your markup. */
|
|
28
|
+
hint?: Snippet;
|
|
29
|
+
class?: string;
|
|
30
|
+
checked?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// The axes are deliberately NOT destructured: they travel in `axes` and are
|
|
34
|
+
// resolved by name, so an axis a family drops changes nothing down here.
|
|
35
|
+
let {
|
|
36
|
+
validator = false,
|
|
37
|
+
hint,
|
|
38
|
+
checked = $bindable(false),
|
|
39
|
+
class: klass = '',
|
|
40
|
+
...axes
|
|
41
|
+
}: Props = $props();
|
|
42
|
+
|
|
43
|
+
let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
|
|
44
|
+
let classes = $derived(cx(PREFIX, applied.classes, validator && 'validator', klass));
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<input type="checkbox" {...applied.rest} class={classes} bind:checked />
|
|
48
|
+
{#if hint}<p class="validator-hint">{@render hint()}</p>{/if}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** What .checkbox is made of, classified. The props follow the
|
|
2
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
3
|
+
export declare const PREFIX = "checkbox";
|
|
4
|
+
export declare const AXES: {
|
|
5
|
+
readonly color: readonly ["primary", "secondary", "accent", "neutral", "info", "success", "warning", "error"];
|
|
6
|
+
readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
|
|
7
|
+
readonly shape: readonly ["square"];
|
|
8
|
+
readonly variant: readonly ["outline"];
|
|
9
|
+
};
|
|
10
|
+
export declare const FLAGS: {};
|
|
11
|
+
export declare const PARTS: readonly [];
|
|
12
|
+
import type { Snippet } from 'svelte';
|
|
13
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
14
|
+
import { type AxisProps } from '../variants.js';
|
|
15
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLInputAttributes, 'size' | 'color' | 'type'> {
|
|
16
|
+
/** Colour the control by its own validity and reveal `hint` when invalid. */
|
|
17
|
+
validator?: boolean;
|
|
18
|
+
/** Shown below once invalid. It must be the control's next sibling for the
|
|
19
|
+
CSS to reach it, which is why it is a prop rather than your markup. */
|
|
20
|
+
hint?: Snippet;
|
|
21
|
+
class?: string;
|
|
22
|
+
checked?: boolean;
|
|
23
|
+
}
|
|
24
|
+
declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
|
|
25
|
+
type Checkbox = ReturnType<typeof Checkbox>;
|
|
26
|
+
export default Checkbox;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/** What .chip is made of, classified. The props follow the
|
|
3
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
4
|
+
export const PREFIX = 'chip';
|
|
5
|
+
|
|
6
|
+
export const AXES = {
|
|
7
|
+
color: ['primary', 'secondary', 'accent', 'neutral', 'info', 'success', 'warning', 'error'],
|
|
8
|
+
size: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
9
|
+
/* Fill and border treatments. The family ships them as flat combinations
|
|
10
|
+
rather than orthogonal axes, so they are one axis — and `variant` is the
|
|
11
|
+
name kept for an exclusive group with no better one. */
|
|
12
|
+
variant: ['outline', 'dash', 'soft', 'ghost']
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
export const FLAGS = {} as const;
|
|
16
|
+
export const PARTS = ['remove'] as const;
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
import type { Snippet } from 'svelte';
|
|
21
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
22
|
+
import { applyAxes, cx, type AxisProps } from '../variants.js';
|
|
23
|
+
|
|
24
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {
|
|
25
|
+
/** Draw a remove button after the content, calling this. */
|
|
26
|
+
onRemove?: () => void;
|
|
27
|
+
/** The remove button's accessible name. */
|
|
28
|
+
removeLabel?: string;
|
|
29
|
+
/** Keep the remove button out of reach (it is hidden). */
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
class?: string;
|
|
32
|
+
children?: Snippet;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// The axes are deliberately NOT destructured: they travel in `axes` and are
|
|
36
|
+
// resolved by name, so an axis a family drops changes nothing down here.
|
|
37
|
+
let {
|
|
38
|
+
onRemove,
|
|
39
|
+
removeLabel = 'Remove',
|
|
40
|
+
disabled = false,
|
|
41
|
+
class: klass = '',
|
|
42
|
+
children,
|
|
43
|
+
...axes
|
|
44
|
+
}: Props = $props();
|
|
45
|
+
|
|
46
|
+
let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
|
|
47
|
+
let classes = $derived(cx(PREFIX, applied.classes, klass));
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<span {...applied.rest} class={classes}
|
|
51
|
+
>{@render children?.()}{#if onRemove}<button
|
|
52
|
+
type="button"
|
|
53
|
+
class="chip-remove"
|
|
54
|
+
aria-label={removeLabel}
|
|
55
|
+
{disabled}
|
|
56
|
+
onclick={onRemove}
|
|
57
|
+
>
|
|
58
|
+
<svg viewBox="0 0 24 24" width="0.9em" height="0.9em" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"><path d="m6 6 12 12M18 6 6 18" /></svg>
|
|
59
|
+
</button>{/if}</span
|
|
60
|
+
>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** What .chip is made of, classified. The props follow the
|
|
2
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
3
|
+
export declare const PREFIX = "chip";
|
|
4
|
+
export declare const AXES: {
|
|
5
|
+
readonly color: readonly ["primary", "secondary", "accent", "neutral", "info", "success", "warning", "error"];
|
|
6
|
+
readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
|
|
7
|
+
readonly variant: readonly ["outline", "dash", "soft", "ghost"];
|
|
8
|
+
};
|
|
9
|
+
export declare const FLAGS: {};
|
|
10
|
+
export declare const PARTS: readonly ["remove"];
|
|
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>, Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {
|
|
15
|
+
/** Draw a remove button after the content, calling this. */
|
|
16
|
+
onRemove?: () => void;
|
|
17
|
+
/** The remove button's accessible name. */
|
|
18
|
+
removeLabel?: string;
|
|
19
|
+
/** Keep the remove button out of reach (it is hidden). */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
class?: string;
|
|
22
|
+
children?: Snippet;
|
|
23
|
+
}
|
|
24
|
+
declare const Chip: import("svelte").Component<Props, {}, "">;
|
|
25
|
+
type Chip = ReturnType<typeof Chip>;
|
|
26
|
+
export default Chip;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/** What .chips-input is made of, classified. The props follow the
|
|
3
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
4
|
+
export const PREFIX = 'chips-input';
|
|
5
|
+
|
|
6
|
+
export const AXES = {
|
|
7
|
+
size: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
8
|
+
/* Where the list sits next to the field. Below by default. */
|
|
9
|
+
side: ['top', 'bottom', 'start', 'end']
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export const FLAGS = {} as const;
|
|
13
|
+
export const PARTS = ['list', 'remove'] as const;
|
|
14
|
+
|
|
15
|
+
/** What the `field` snippet receives: call `add` when the field commits a value. */
|
|
16
|
+
export interface ChipsInputField {
|
|
17
|
+
add: (value: string) => void;
|
|
18
|
+
/** Whether a value is already in the list. */
|
|
19
|
+
has: (value: string) => boolean;
|
|
20
|
+
/** `required` and no value yet: the field should say so to its form. */
|
|
21
|
+
missing: boolean;
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<script lang="ts">
|
|
26
|
+
/**
|
|
27
|
+
* A field that feeds a list of chips. A utility: the field is any
|
|
28
|
+
* component — an Input, a Select, a Combobox — given `add` and calling it
|
|
29
|
+
* when it commits a value; the values pile up as chips in a list next to
|
|
30
|
+
* it (`side`), never inside it.
|
|
31
|
+
*
|
|
32
|
+
* Without `field`, a plain `.input` adds on Enter or a comma.
|
|
33
|
+
*
|
|
34
|
+
* `values` is bindable. Each one is also a hidden `name[]` input, so a
|
|
35
|
+
* form submits them.
|
|
36
|
+
*/
|
|
37
|
+
import type { Snippet } from 'svelte';
|
|
38
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
39
|
+
import { applyAxes, cx, type AxisProps } from '../variants.js';
|
|
40
|
+
|
|
41
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
|
|
42
|
+
/** The values. Bindable. */
|
|
43
|
+
values?: string[];
|
|
44
|
+
/** For a form: one hidden `name[]` input per value. */
|
|
45
|
+
name?: string;
|
|
46
|
+
/** Refuse a value already there. */
|
|
47
|
+
unique?: boolean;
|
|
48
|
+
/** The default field's placeholder. */
|
|
49
|
+
placeholder?: string;
|
|
50
|
+
disabled?: boolean;
|
|
51
|
+
/** At least one value. The default field is then `required` while the list is empty. */
|
|
52
|
+
required?: boolean;
|
|
53
|
+
/** The default field's longest value. */
|
|
54
|
+
maxlength?: number;
|
|
55
|
+
/** Colour the default field by its validity, as Input's `validator`. */
|
|
56
|
+
validator?: boolean;
|
|
57
|
+
/** The field, given `add`. Defaults to a plain input (Enter or comma adds). */
|
|
58
|
+
field?: Snippet<[ChipsInputField]>;
|
|
59
|
+
/** How one chip reads. Defaults to the value. */
|
|
60
|
+
chip?: Snippet<[string]>;
|
|
61
|
+
/** After every addition or removal. */
|
|
62
|
+
onChange?: (values: string[]) => void;
|
|
63
|
+
/** A remove button's accessible name. */
|
|
64
|
+
removeLabel?: (value: string) => string;
|
|
65
|
+
/** The list's accessible name. */
|
|
66
|
+
listLabel?: string;
|
|
67
|
+
/** Classes on each chip: `chip-primary`, `chip-soft`… */
|
|
68
|
+
chipClass?: string;
|
|
69
|
+
class?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// The axes are deliberately NOT destructured: they travel in `axes` and are
|
|
73
|
+
// resolved by name, so an axis a family drops changes nothing down here.
|
|
74
|
+
let {
|
|
75
|
+
values = $bindable([]),
|
|
76
|
+
name,
|
|
77
|
+
unique = true,
|
|
78
|
+
placeholder,
|
|
79
|
+
disabled = false,
|
|
80
|
+
required = false,
|
|
81
|
+
maxlength,
|
|
82
|
+
validator = false,
|
|
83
|
+
field,
|
|
84
|
+
chip,
|
|
85
|
+
onChange,
|
|
86
|
+
removeLabel = (v: string) => `Remove ${v}`,
|
|
87
|
+
listLabel,
|
|
88
|
+
chipClass = '',
|
|
89
|
+
class: klass = '',
|
|
90
|
+
...axes
|
|
91
|
+
}: Props = $props();
|
|
92
|
+
|
|
93
|
+
let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
|
|
94
|
+
let classes = $derived(cx(PREFIX, applied.classes, klass));
|
|
95
|
+
|
|
96
|
+
let draft = $state('');
|
|
97
|
+
|
|
98
|
+
function add(value: string) {
|
|
99
|
+
const v = `${value ?? ''}`.trim();
|
|
100
|
+
if (!v || disabled || (unique && values.includes(v))) return;
|
|
101
|
+
values = [...values, v];
|
|
102
|
+
onChange?.(values);
|
|
103
|
+
}
|
|
104
|
+
const has = (value: string) => values.includes(`${value ?? ''}`.trim());
|
|
105
|
+
let missing = $derived(required && values.length === 0);
|
|
106
|
+
|
|
107
|
+
function remove(i: number) {
|
|
108
|
+
values = values.filter((_, n) => n !== i);
|
|
109
|
+
onChange?.(values);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function draftKeydown(e: KeyboardEvent) {
|
|
113
|
+
if (e.key === 'Enter' || e.key === ',') {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
add(draft);
|
|
116
|
+
draft = '';
|
|
117
|
+
} else if (e.key === 'Backspace' && draft === '' && values.length && !disabled) {
|
|
118
|
+
/* Backspace in the empty default field: the last chip goes. */
|
|
119
|
+
remove(values.length - 1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<div {...applied.rest} class={classes}>
|
|
125
|
+
{#if field}
|
|
126
|
+
{@render field({ add, has, missing })}
|
|
127
|
+
{:else}
|
|
128
|
+
<input
|
|
129
|
+
class={cx('input', validator && 'validator')}
|
|
130
|
+
bind:value={draft}
|
|
131
|
+
{placeholder}
|
|
132
|
+
{disabled}
|
|
133
|
+
{maxlength}
|
|
134
|
+
required={missing}
|
|
135
|
+
onkeydown={draftKeydown}
|
|
136
|
+
onblur={() => (add(draft), (draft = ''))}
|
|
137
|
+
/>
|
|
138
|
+
{/if}
|
|
139
|
+
<ul class="chips-input-list" aria-label={listLabel}>
|
|
140
|
+
{#each values as v, i (v + i)}
|
|
141
|
+
<li class={cx('chip', chipClass)}>
|
|
142
|
+
{#if chip}{@render chip(v)}{:else}{v}{/if}
|
|
143
|
+
<button
|
|
144
|
+
type="button"
|
|
145
|
+
class="chips-input-remove"
|
|
146
|
+
aria-label={removeLabel(v)}
|
|
147
|
+
{disabled}
|
|
148
|
+
onclick={() => remove(i)}
|
|
149
|
+
>
|
|
150
|
+
<svg viewBox="0 0 24 24" width="0.9em" height="0.9em" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"><path d="m6 6 12 12M18 6 6 18" /></svg>
|
|
151
|
+
</button>
|
|
152
|
+
</li>
|
|
153
|
+
{/each}
|
|
154
|
+
</ul>
|
|
155
|
+
{#if name}
|
|
156
|
+
{#each values as v, i (v + i)}<input type="hidden" name="{name}[]" value={v} />{/each}
|
|
157
|
+
{/if}
|
|
158
|
+
</div>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** What .chips-input is made of, classified. The props follow the
|
|
2
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
3
|
+
export declare const PREFIX = "chips-input";
|
|
4
|
+
export declare const AXES: {
|
|
5
|
+
readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
|
|
6
|
+
readonly side: readonly ["top", "bottom", "start", "end"];
|
|
7
|
+
};
|
|
8
|
+
export declare const FLAGS: {};
|
|
9
|
+
export declare const PARTS: readonly ["list", "remove"];
|
|
10
|
+
/** What the `field` snippet receives: call `add` when the field commits a value. */
|
|
11
|
+
export interface ChipsInputField {
|
|
12
|
+
add: (value: string) => void;
|
|
13
|
+
/** Whether a value is already in the list. */
|
|
14
|
+
has: (value: string) => boolean;
|
|
15
|
+
/** `required` and no value yet: the field should say so to its form. */
|
|
16
|
+
missing: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A field that feeds a list of chips. A utility: the field is any
|
|
20
|
+
* component — an Input, a Select, a Combobox — given `add` and calling it
|
|
21
|
+
* when it commits a value; the values pile up as chips in a list next to
|
|
22
|
+
* it (`side`), never inside it.
|
|
23
|
+
*
|
|
24
|
+
* Without `field`, a plain `.input` adds on Enter or a comma.
|
|
25
|
+
*
|
|
26
|
+
* `values` is bindable. Each one is also a hidden `name[]` input, so a
|
|
27
|
+
* form submits them.
|
|
28
|
+
*/
|
|
29
|
+
import type { Snippet } from 'svelte';
|
|
30
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
31
|
+
import { type AxisProps } from '../variants.js';
|
|
32
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
|
|
33
|
+
/** The values. Bindable. */
|
|
34
|
+
values?: string[];
|
|
35
|
+
/** For a form: one hidden `name[]` input per value. */
|
|
36
|
+
name?: string;
|
|
37
|
+
/** Refuse a value already there. */
|
|
38
|
+
unique?: boolean;
|
|
39
|
+
/** The default field's placeholder. */
|
|
40
|
+
placeholder?: string;
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
/** At least one value. The default field is then `required` while the list is empty. */
|
|
43
|
+
required?: boolean;
|
|
44
|
+
/** The default field's longest value. */
|
|
45
|
+
maxlength?: number;
|
|
46
|
+
/** Colour the default field by its validity, as Input's `validator`. */
|
|
47
|
+
validator?: boolean;
|
|
48
|
+
/** The field, given `add`. Defaults to a plain input (Enter or comma adds). */
|
|
49
|
+
field?: Snippet<[ChipsInputField]>;
|
|
50
|
+
/** How one chip reads. Defaults to the value. */
|
|
51
|
+
chip?: Snippet<[string]>;
|
|
52
|
+
/** After every addition or removal. */
|
|
53
|
+
onChange?: (values: string[]) => void;
|
|
54
|
+
/** A remove button's accessible name. */
|
|
55
|
+
removeLabel?: (value: string) => string;
|
|
56
|
+
/** The list's accessible name. */
|
|
57
|
+
listLabel?: string;
|
|
58
|
+
/** Classes on each chip: `chip-primary`, `chip-soft`… */
|
|
59
|
+
chipClass?: string;
|
|
60
|
+
class?: string;
|
|
61
|
+
}
|
|
62
|
+
declare const ChipsInput: import("svelte").Component<Props, {}, "values">;
|
|
63
|
+
type ChipsInput = ReturnType<typeof ChipsInput>;
|
|
64
|
+
export default ChipsInput;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/** What .collapse is made of, classified. The props follow the
|
|
3
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
4
|
+
export const PREFIX = 'collapse';
|
|
5
|
+
|
|
6
|
+
export const AXES = {
|
|
7
|
+
size: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
8
|
+
/* Draw no frame: the trigger and the content sit on the page. */
|
|
9
|
+
variant: ['ghost']
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export const FLAGS = {
|
|
13
|
+
arrow: 'Draw a chevron that turns when open.',
|
|
14
|
+
plus: 'Draw a plus that becomes a minus when open.'
|
|
15
|
+
} as const;
|
|
16
|
+
export const PARTS = ['trigger', 'panel', 'inner'] as const;
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
/**
|
|
21
|
+
* A panel that folds under its trigger.
|
|
22
|
+
*
|
|
23
|
+
* A button and a region rather than <details>: the height animates, and the
|
|
24
|
+
* state is the button's `aria-expanded`, which the stylesheet reads. The
|
|
25
|
+
* closed panel is `inert`, so nothing inside it can be focused.
|
|
26
|
+
*
|
|
27
|
+
* `open` is where it starts. To drive it from outside, use the methods —
|
|
28
|
+
* `bind:this={c}` then `c.show()`, `c.hide()`, `c.toggle()` — and listen
|
|
29
|
+
* with `onToggle`. Inside an exclusive Accordion, opening one closes the
|
|
30
|
+
* others.
|
|
31
|
+
*/
|
|
32
|
+
import { untrack, type Snippet } from 'svelte';
|
|
33
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
34
|
+
import { getAccordion } from './accordion-context.js';
|
|
35
|
+
import { applyAxes, cx, type AxisProps } from '../variants.js';
|
|
36
|
+
|
|
37
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
38
|
+
/** The trigger's text. */
|
|
39
|
+
title?: string;
|
|
40
|
+
/** The trigger's content, as markup. Wins over `title`. */
|
|
41
|
+
trigger?: Snippet;
|
|
42
|
+
/** Whether it starts open. Not bound: use the methods to drive it. */
|
|
43
|
+
open?: boolean;
|
|
44
|
+
/** After every change, with the new state. */
|
|
45
|
+
onToggle?: (open: boolean) => void;
|
|
46
|
+
class?: string;
|
|
47
|
+
children?: Snippet;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The axes are deliberately NOT destructured: they travel in `axes` and are
|
|
51
|
+
// resolved by name, so an axis a family drops changes nothing down here.
|
|
52
|
+
let {
|
|
53
|
+
title,
|
|
54
|
+
trigger,
|
|
55
|
+
open: initial = false,
|
|
56
|
+
onToggle,
|
|
57
|
+
class: klass = '',
|
|
58
|
+
children,
|
|
59
|
+
...axes
|
|
60
|
+
}: Props = $props();
|
|
61
|
+
|
|
62
|
+
let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
|
|
63
|
+
let classes = $derived(cx(PREFIX, applied.classes, klass));
|
|
64
|
+
|
|
65
|
+
/* Where it starts, read once: afterwards the methods own it. */
|
|
66
|
+
let isOpen = $state(untrack(() => initial));
|
|
67
|
+
const id = `collapse-${Math.random().toString(36).slice(2, 9)}`;
|
|
68
|
+
|
|
69
|
+
const group = getAccordion();
|
|
70
|
+
const closeFromGroup = () => set(false);
|
|
71
|
+
if (group) {
|
|
72
|
+
const leave = group.join(closeFromGroup);
|
|
73
|
+
$effect(() => leave);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function set(next: boolean) {
|
|
77
|
+
if (next === isOpen) return;
|
|
78
|
+
isOpen = next;
|
|
79
|
+
if (next) group?.opened(closeFromGroup);
|
|
80
|
+
onToggle?.(next);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function show() {
|
|
84
|
+
set(true);
|
|
85
|
+
}
|
|
86
|
+
export function hide() {
|
|
87
|
+
set(false);
|
|
88
|
+
}
|
|
89
|
+
export function toggle() {
|
|
90
|
+
set(!isOpen);
|
|
91
|
+
}
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<div {...applied.rest} class={classes}>
|
|
95
|
+
<button type="button" class="collapse-trigger" aria-expanded={isOpen} aria-controls={id} onclick={toggle}>
|
|
96
|
+
{#if trigger}{@render trigger()}{:else}{title}{/if}
|
|
97
|
+
</button>
|
|
98
|
+
<div class="collapse-panel" {id} role="region" inert={!isOpen}>
|
|
99
|
+
<div class="collapse-inner">{@render children?.()}</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** What .collapse is made of, classified. The props follow the
|
|
2
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
3
|
+
export declare const PREFIX = "collapse";
|
|
4
|
+
export declare const AXES: {
|
|
5
|
+
readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
|
|
6
|
+
readonly variant: readonly ["ghost"];
|
|
7
|
+
};
|
|
8
|
+
export declare const FLAGS: {
|
|
9
|
+
readonly arrow: "Draw a chevron that turns when open.";
|
|
10
|
+
readonly plus: "Draw a plus that becomes a minus when open.";
|
|
11
|
+
};
|
|
12
|
+
export declare const PARTS: readonly ["trigger", "panel", "inner"];
|
|
13
|
+
/**
|
|
14
|
+
* A panel that folds under its trigger.
|
|
15
|
+
*
|
|
16
|
+
* A button and a region rather than <details>: the height animates, and the
|
|
17
|
+
* state is the button's `aria-expanded`, which the stylesheet reads. The
|
|
18
|
+
* closed panel is `inert`, so nothing inside it can be focused.
|
|
19
|
+
*
|
|
20
|
+
* `open` is where it starts. To drive it from outside, use the methods —
|
|
21
|
+
* `bind:this={c}` then `c.show()`, `c.hide()`, `c.toggle()` — and listen
|
|
22
|
+
* with `onToggle`. Inside an exclusive Accordion, opening one closes the
|
|
23
|
+
* others.
|
|
24
|
+
*/
|
|
25
|
+
import { type Snippet } from 'svelte';
|
|
26
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
27
|
+
import { type AxisProps } from '../variants.js';
|
|
28
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
29
|
+
/** The trigger's text. */
|
|
30
|
+
title?: string;
|
|
31
|
+
/** The trigger's content, as markup. Wins over `title`. */
|
|
32
|
+
trigger?: Snippet;
|
|
33
|
+
/** Whether it starts open. Not bound: use the methods to drive it. */
|
|
34
|
+
open?: boolean;
|
|
35
|
+
/** After every change, with the new state. */
|
|
36
|
+
onToggle?: (open: boolean) => void;
|
|
37
|
+
class?: string;
|
|
38
|
+
children?: Snippet;
|
|
39
|
+
}
|
|
40
|
+
declare const Collapse: import("svelte").Component<Props, {
|
|
41
|
+
show: () => void;
|
|
42
|
+
hide: () => void;
|
|
43
|
+
toggle: () => void;
|
|
44
|
+
}, "">;
|
|
45
|
+
type Collapse = ReturnType<typeof Collapse>;
|
|
46
|
+
export default Collapse;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/** What .color-picker is made of, classified. The props follow the
|
|
3
|
+
families the project registers (variants.ts, ADR 0015). */
|
|
4
|
+
export const PREFIX = 'color-picker';
|
|
5
|
+
|
|
6
|
+
export const AXES = {
|
|
7
|
+
size: ['xs', 'sm', 'md', 'lg', 'xl']
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export const FLAGS = {
|
|
11
|
+
flat: 'No box of its own: the host — a dropdown, a card — draws it.'
|
|
12
|
+
} as const;
|
|
13
|
+
export const PARTS = ['head', 'preview', 'value', 'channel', 'track', 'presets'] as const;
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
/**
|
|
18
|
+
* One colour, edited in OKLCH: lightness, chroma, hue, each a slider
|
|
19
|
+
* whose track is the gradient it moves along, plus the raw value for
|
|
20
|
+
* anything the sliders cannot say (`color-mix(…)`, a `var()`).
|
|
21
|
+
*
|
|
22
|
+
* A panel, not a popover: put it in a Dropdown to float it.
|
|
23
|
+
*
|
|
24
|
+
* The three numbers are state, seeded from `value` and then only written:
|
|
25
|
+
* reading them back from the colour on every drag goes through a lossy
|
|
26
|
+
* conversion, and moving the hue would nudge the lightness.
|
|
27
|
+
*/
|
|
28
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
29
|
+
import { oklchToCss, toOklch, type Oklch } from './color.js';
|
|
30
|
+
import { applyAxes, cx, type AxisProps } from '../variants.js';
|
|
31
|
+
|
|
32
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
|
|
33
|
+
/** Any CSS colour. Bindable; written back as `oklch(…)`. */
|
|
34
|
+
value?: string;
|
|
35
|
+
/** Colours one click away, drawn as swatches. */
|
|
36
|
+
presets?: string[];
|
|
37
|
+
/** Hide the raw value field. */
|
|
38
|
+
noInput?: boolean;
|
|
39
|
+
/** The channels' accessible names. */
|
|
40
|
+
labels?: { l?: string; c?: string; h?: string; value?: string };
|
|
41
|
+
/** After every change. */
|
|
42
|
+
onChange?: (value: string) => void;
|
|
43
|
+
class?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The axes are deliberately NOT destructured: they travel in `axes` and are
|
|
47
|
+
// resolved by name, so an axis a family drops changes nothing down here.
|
|
48
|
+
let {
|
|
49
|
+
value = $bindable('oklch(62% 0.19 260)'),
|
|
50
|
+
presets = [],
|
|
51
|
+
noInput = false,
|
|
52
|
+
labels = {},
|
|
53
|
+
onChange,
|
|
54
|
+
class: klass = '',
|
|
55
|
+
...axes
|
|
56
|
+
}: Props = $props();
|
|
57
|
+
|
|
58
|
+
let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
|
|
59
|
+
let classes = $derived(cx(PREFIX, applied.classes, klass));
|
|
60
|
+
|
|
61
|
+
let lch = $state<Oklch>({ l: 0.62, c: 0.19, h: 260 });
|
|
62
|
+
/* The value this component last wrote: a different one came from outside. */
|
|
63
|
+
let written = '';
|
|
64
|
+
$effect.pre(() => {
|
|
65
|
+
if (value !== written && typeof document !== 'undefined') lch = toOklch(value);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function emit(v: string) {
|
|
69
|
+
written = v;
|
|
70
|
+
value = v;
|
|
71
|
+
onChange?.(v);
|
|
72
|
+
}
|
|
73
|
+
function set(part: Partial<Oklch>) {
|
|
74
|
+
lch = { ...lch, ...part };
|
|
75
|
+
emit(oklchToCss(lch));
|
|
76
|
+
}
|
|
77
|
+
function pick(v: string) {
|
|
78
|
+
lch = toOklch(v);
|
|
79
|
+
emit(v);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const ramp = (vals: number[], key: 'l' | 'c' | 'h') =>
|
|
83
|
+
`linear-gradient(to right, ${vals.map((v) => oklchToCss({ ...lch, [key]: v })).join(', ')})`;
|
|
84
|
+
let channels = $derived([
|
|
85
|
+
{ key: 'l' as const, short: 'L', name: labels.l ?? 'Lightness', min: 0, max: 1, step: 0.005, track: ramp([0, 0.25, 0.5, 0.75, 1], 'l'), shown: Math.round(lch.l * 100) },
|
|
86
|
+
{ key: 'c' as const, short: 'C', name: labels.c ?? 'Chroma', min: 0, max: 0.37, step: 0.005, track: ramp([0, 0.1, 0.2, 0.3, 0.37], 'c'), shown: Math.round(lch.c * 100) / 100 },
|
|
87
|
+
{ key: 'h' as const, short: 'H', name: labels.h ?? 'Hue', min: 0, max: 360, step: 1, track: ramp(Array.from({ length: 13 }, (_, i) => i * 30), 'h'), shown: Math.round(lch.h) }
|
|
88
|
+
]);
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<div {...applied.rest} class={classes} style:--color-picker-value={value}>
|
|
92
|
+
<div class="{PREFIX}-head">
|
|
93
|
+
<span class="{PREFIX}-preview"></span>
|
|
94
|
+
{#if !noInput}
|
|
95
|
+
<input
|
|
96
|
+
class="input input-sm {PREFIX}-value"
|
|
97
|
+
aria-label={labels.value ?? 'Value'}
|
|
98
|
+
spellcheck="false"
|
|
99
|
+
{value}
|
|
100
|
+
onchange={(e) => pick(e.currentTarget.value)}
|
|
101
|
+
/>
|
|
102
|
+
{/if}
|
|
103
|
+
</div>
|
|
104
|
+
{#each channels as ch (ch.key)}
|
|
105
|
+
<label class="{PREFIX}-channel">
|
|
106
|
+
<span title={ch.name}>{ch.short}</span>
|
|
107
|
+
<input
|
|
108
|
+
type="range"
|
|
109
|
+
class="{PREFIX}-track"
|
|
110
|
+
aria-label={ch.name}
|
|
111
|
+
min={ch.min}
|
|
112
|
+
max={ch.max}
|
|
113
|
+
step={ch.step}
|
|
114
|
+
value={lch[ch.key]}
|
|
115
|
+
style:--color-picker-track={ch.track}
|
|
116
|
+
oninput={(e) => set({ [ch.key]: Number(e.currentTarget.value) })}
|
|
117
|
+
/>
|
|
118
|
+
<output>{ch.shown}</output>
|
|
119
|
+
</label>
|
|
120
|
+
{/each}
|
|
121
|
+
{#if presets.length}
|
|
122
|
+
<div class="{PREFIX}-presets">
|
|
123
|
+
{#each presets as p (p)}
|
|
124
|
+
<button
|
|
125
|
+
type="button"
|
|
126
|
+
class={cx('swatch', 'swatch-sm', p === value && 'swatch-active')}
|
|
127
|
+
style:--swatch-color={p}
|
|
128
|
+
aria-label={p}
|
|
129
|
+
title={p}
|
|
130
|
+
onclick={() => pick(p)}
|
|
131
|
+
></button>
|
|
132
|
+
{/each}
|
|
133
|
+
</div>
|
|
134
|
+
{/if}
|
|
135
|
+
</div>
|