@sveltier/ui 0.0.0-stage → 0.1.0-alpha.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 +82 -2
- package/dist/components/Alert/Alert.svelte +88 -0
- package/dist/components/Alert/Alert.svelte.d.ts +4 -0
- package/dist/components/Alert/alert.css +78 -0
- package/dist/components/Alert/index.d.ts +2 -0
- package/dist/components/Alert/index.js +1 -0
- package/dist/components/Alert/types.d.ts +67 -0
- package/dist/components/Alert/types.js +1 -0
- package/dist/components/Avatar/Avatar.svelte +47 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +4 -0
- package/dist/components/Avatar/avatar.css +34 -0
- package/dist/components/Avatar/index.d.ts +2 -0
- package/dist/components/Avatar/index.js +1 -0
- package/dist/components/Avatar/types.d.ts +47 -0
- package/dist/components/Avatar/types.js +1 -0
- package/dist/components/Badge/Badge.svelte +53 -0
- package/dist/components/Badge/Badge.svelte.d.ts +4 -0
- package/dist/components/Badge/badge.css +57 -0
- package/dist/components/Badge/index.d.ts +2 -0
- package/dist/components/Badge/index.js +1 -0
- package/dist/components/Badge/types.d.ts +43 -0
- package/dist/components/Badge/types.js +1 -0
- package/dist/components/Button/Button.svelte +85 -0
- package/dist/components/Button/Button.svelte.d.ts +4 -0
- package/dist/components/Button/button.css +132 -0
- package/dist/components/Button/index.d.ts +2 -0
- package/dist/components/Button/index.js +1 -0
- package/dist/components/Button/types.d.ts +76 -0
- package/dist/components/Button/types.js +1 -0
- package/dist/components/ButtonGroup/ButtonGroup.svelte +61 -0
- package/dist/components/ButtonGroup/ButtonGroup.svelte.d.ts +4 -0
- package/dist/components/ButtonGroup/button-group.css +39 -0
- package/dist/components/ButtonGroup/index.d.ts +2 -0
- package/dist/components/ButtonGroup/index.js +1 -0
- package/dist/components/ButtonGroup/types.d.ts +48 -0
- package/dist/components/ButtonGroup/types.js +1 -0
- package/dist/components/Card/Card.svelte +53 -0
- package/dist/components/Card/Card.svelte.d.ts +4 -0
- package/dist/components/Card/card.css +62 -0
- package/dist/components/Card/index.d.ts +2 -0
- package/dist/components/Card/index.js +1 -0
- package/dist/components/Card/types.d.ts +56 -0
- package/dist/components/Card/types.js +1 -0
- package/dist/components/Checkbox/Checkbox.svelte +105 -0
- package/dist/components/Checkbox/Checkbox.svelte.d.ts +4 -0
- package/dist/components/Checkbox/checkbox.css +138 -0
- package/dist/components/Checkbox/index.d.ts +2 -0
- package/dist/components/Checkbox/index.js +1 -0
- package/dist/components/Checkbox/types.d.ts +76 -0
- package/dist/components/Checkbox/types.js +1 -0
- package/dist/components/Chip/Chip.svelte +109 -0
- package/dist/components/Chip/Chip.svelte.d.ts +4 -0
- package/dist/components/Chip/chip.css +101 -0
- package/dist/components/Chip/index.d.ts +2 -0
- package/dist/components/Chip/index.js +1 -0
- package/dist/components/Chip/types.d.ts +69 -0
- package/dist/components/Chip/types.js +1 -0
- package/dist/components/Choice/Choice.svelte +60 -0
- package/dist/components/Choice/Choice.svelte.d.ts +4 -0
- package/dist/components/Choice/choice.css +75 -0
- package/dist/components/Choice/types.d.ts +43 -0
- package/dist/components/Choice/types.js +1 -0
- package/dist/components/Divider/Divider.svelte +19 -0
- package/dist/components/Divider/Divider.svelte.d.ts +4 -0
- package/dist/components/Divider/divider.css +23 -0
- package/dist/components/Divider/index.d.ts +2 -0
- package/dist/components/Divider/index.js +1 -0
- package/dist/components/Divider/types.d.ts +10 -0
- package/dist/components/Divider/types.js +1 -0
- package/dist/components/Field/Field.svelte +95 -0
- package/dist/components/Field/Field.svelte.d.ts +4 -0
- package/dist/components/Field/field.css +210 -0
- package/dist/components/Field/index.d.ts +3 -0
- package/dist/components/Field/index.js +2 -0
- package/dist/components/Field/types.d.ts +109 -0
- package/dist/components/Field/types.js +2 -0
- package/dist/components/Form/Form.svelte +93 -0
- package/dist/components/Form/Form.svelte.d.ts +8 -0
- package/dist/components/Form/form.css +16 -0
- package/dist/components/Form/index.d.ts +2 -0
- package/dist/components/Form/index.js +1 -0
- package/dist/components/Form/types.d.ts +38 -0
- package/dist/components/Form/types.js +1 -0
- package/dist/components/Icon/Icon.svelte +38 -0
- package/dist/components/Icon/Icon.svelte.d.ts +4 -0
- package/dist/components/Icon/icon.css +33 -0
- package/dist/components/Icon/index.d.ts +2 -0
- package/dist/components/Icon/index.js +1 -0
- package/dist/components/Icon/types.d.ts +34 -0
- package/dist/components/Icon/types.js +1 -0
- package/dist/components/List/List.svelte +39 -0
- package/dist/components/List/List.svelte.d.ts +4 -0
- package/dist/components/List/ListItem.svelte +57 -0
- package/dist/components/List/ListItem.svelte.d.ts +4 -0
- package/dist/components/List/index.d.ts +3 -0
- package/dist/components/List/index.js +2 -0
- package/dist/components/List/list.css +127 -0
- package/dist/components/List/types.d.ts +67 -0
- package/dist/components/List/types.js +1 -0
- package/dist/components/NativeSelect/NativeSelect.svelte +110 -0
- package/dist/components/NativeSelect/NativeSelect.svelte.d.ts +25 -0
- package/dist/components/NativeSelect/index.d.ts +2 -0
- package/dist/components/NativeSelect/index.js +1 -0
- package/dist/components/NativeSelect/native-select.css +45 -0
- package/dist/components/NativeSelect/types.d.ts +58 -0
- package/dist/components/NativeSelect/types.js +1 -0
- package/dist/components/NumberField/NumberField.svelte +246 -0
- package/dist/components/NumberField/NumberField.svelte.d.ts +4 -0
- package/dist/components/NumberField/index.d.ts +2 -0
- package/dist/components/NumberField/index.js +1 -0
- package/dist/components/NumberField/number-field.css +74 -0
- package/dist/components/NumberField/types.d.ts +73 -0
- package/dist/components/NumberField/types.js +1 -0
- package/dist/components/Progress/ProgressCircular.svelte +61 -0
- package/dist/components/Progress/ProgressCircular.svelte.d.ts +4 -0
- package/dist/components/Progress/ProgressLinear.svelte +44 -0
- package/dist/components/Progress/ProgressLinear.svelte.d.ts +4 -0
- package/dist/components/Progress/index.d.ts +3 -0
- package/dist/components/Progress/index.js +2 -0
- package/dist/components/Progress/progress-circular.css +61 -0
- package/dist/components/Progress/progress-linear.css +67 -0
- package/dist/components/Progress/types.d.ts +53 -0
- package/dist/components/Progress/types.js +1 -0
- package/dist/components/RadioGroup/RadioGroup.svelte +149 -0
- package/dist/components/RadioGroup/RadioGroup.svelte.d.ts +25 -0
- package/dist/components/RadioGroup/index.d.ts +2 -0
- package/dist/components/RadioGroup/index.js +1 -0
- package/dist/components/RadioGroup/radio-group.css +139 -0
- package/dist/components/RadioGroup/types.d.ts +93 -0
- package/dist/components/RadioGroup/types.js +1 -0
- package/dist/components/SegmentedControl/SegmentedControl.svelte +70 -0
- package/dist/components/SegmentedControl/SegmentedControl.svelte.d.ts +25 -0
- package/dist/components/SegmentedControl/index.d.ts +2 -0
- package/dist/components/SegmentedControl/index.js +1 -0
- package/dist/components/SegmentedControl/segmented-control.css +68 -0
- package/dist/components/SegmentedControl/types.d.ts +60 -0
- package/dist/components/SegmentedControl/types.js +1 -0
- package/dist/components/Skeleton/Skeleton.svelte +33 -0
- package/dist/components/Skeleton/Skeleton.svelte.d.ts +4 -0
- package/dist/components/Skeleton/index.d.ts +2 -0
- package/dist/components/Skeleton/index.js +1 -0
- package/dist/components/Skeleton/skeleton.css +70 -0
- package/dist/components/Skeleton/types.d.ts +25 -0
- package/dist/components/Skeleton/types.js +1 -0
- package/dist/components/Slider/Slider.svelte +153 -0
- package/dist/components/Slider/Slider.svelte.d.ts +4 -0
- package/dist/components/Slider/index.d.ts +2 -0
- package/dist/components/Slider/index.js +1 -0
- package/dist/components/Slider/slider.css +206 -0
- package/dist/components/Slider/types.d.ts +102 -0
- package/dist/components/Slider/types.js +1 -0
- package/dist/components/Switch/Switch.svelte +94 -0
- package/dist/components/Switch/Switch.svelte.d.ts +4 -0
- package/dist/components/Switch/index.d.ts +2 -0
- package/dist/components/Switch/index.js +1 -0
- package/dist/components/Switch/switch.css +118 -0
- package/dist/components/Switch/types.d.ts +67 -0
- package/dist/components/Switch/types.js +1 -0
- package/dist/components/Tabs/Tabs.svelte +93 -0
- package/dist/components/Tabs/Tabs.svelte.d.ts +25 -0
- package/dist/components/Tabs/index.d.ts +2 -0
- package/dist/components/Tabs/index.js +1 -0
- package/dist/components/Tabs/tabs.css +73 -0
- package/dist/components/Tabs/types.d.ts +59 -0
- package/dist/components/Tabs/types.js +1 -0
- package/dist/components/TextField/TextField.svelte +115 -0
- package/dist/components/TextField/TextField.svelte.d.ts +4 -0
- package/dist/components/TextField/index.d.ts +2 -0
- package/dist/components/TextField/index.js +1 -0
- package/dist/components/TextField/types.d.ts +57 -0
- package/dist/components/TextField/types.js +1 -0
- package/dist/components/Textarea/Textarea.svelte +116 -0
- package/dist/components/Textarea/Textarea.svelte.d.ts +4 -0
- package/dist/components/Textarea/index.d.ts +2 -0
- package/dist/components/Textarea/index.js +1 -0
- package/dist/components/Textarea/textarea.css +35 -0
- package/dist/components/Textarea/types.d.ts +60 -0
- package/dist/components/Textarea/types.js +1 -0
- package/dist/core/defaults/DefaultsProvider.svelte +30 -0
- package/dist/core/defaults/DefaultsProvider.svelte.d.ts +13 -0
- package/dist/core/defaults/context.d.ts +45 -0
- package/dist/core/defaults/context.js +38 -0
- package/dist/core/display/display.d.ts +39 -0
- package/dist/core/display/display.js +53 -0
- package/dist/core/errors/codes.d.ts +112 -0
- package/dist/core/errors/codes.js +96 -0
- package/dist/core/errors/errors.d.ts +8 -0
- package/dist/core/errors/errors.js +7 -0
- package/dist/core/form/constraints.d.ts +15 -0
- package/dist/core/form/constraints.js +42 -0
- package/dist/core/form/context.d.ts +19 -0
- package/dist/core/form/context.js +4 -0
- package/dist/core/form/field.svelte.d.ts +34 -0
- package/dist/core/form/field.svelte.js +66 -0
- package/dist/core/form/number.d.ts +24 -0
- package/dist/core/form/number.js +71 -0
- package/dist/core/form/types.d.ts +22 -0
- package/dist/core/form/types.js +1 -0
- package/dist/core/locale/locale.d.ts +55 -0
- package/dist/core/locale/locale.js +80 -0
- package/dist/core/props/classes.d.ts +21 -0
- package/dist/core/props/classes.js +44 -0
- package/dist/core/props/element.d.ts +18 -0
- package/dist/core/props/element.js +1 -0
- package/dist/core/props/types.d.ts +47 -0
- package/dist/core/props/types.js +16 -0
- package/dist/core/provider/UIProvider.svelte +60 -0
- package/dist/core/provider/UIProvider.svelte.d.ts +29 -0
- package/dist/core/theme/ThemeProvider.svelte +178 -0
- package/dist/core/theme/ThemeProvider.svelte.d.ts +25 -0
- package/dist/core/theme/color.d.ts +23 -0
- package/dist/core/theme/color.js +54 -0
- package/dist/core/theme/context.d.ts +25 -0
- package/dist/core/theme/context.js +14 -0
- package/dist/core/theme/css.d.ts +25 -0
- package/dist/core/theme/css.js +69 -0
- package/dist/core/theme/define.d.ts +11 -0
- package/dist/core/theme/define.js +21 -0
- package/dist/core/theme/resolve.d.ts +6 -0
- package/dist/core/theme/resolve.js +18 -0
- package/dist/core/theme/theme-provider.css +13 -0
- package/dist/core/theme/types.d.ts +35 -0
- package/dist/core/theme/types.js +61 -0
- package/dist/dev/off.d.ts +4 -0
- package/dist/dev/off.js +3 -0
- package/dist/dev/on.d.ts +8 -0
- package/dist/dev/on.js +33 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +72 -0
- package/dist/primitives/anchored/anchored.d.ts +39 -0
- package/dist/primitives/anchored/anchored.js +104 -0
- package/dist/primitives/anchored/fallback.d.ts +31 -0
- package/dist/primitives/anchored/fallback.js +73 -0
- package/dist/primitives/focus/focus-trap.d.ts +25 -0
- package/dist/primitives/focus/focus-trap.js +86 -0
- package/dist/primitives/focus/roving-focus.d.ts +22 -0
- package/dist/primitives/focus/roving-focus.js +86 -0
- package/dist/primitives/focus/tabbable.d.ts +9 -0
- package/dist/primitives/focus/tabbable.js +75 -0
- package/dist/primitives/inert/inert-others.d.ts +12 -0
- package/dist/primitives/inert/inert-others.js +44 -0
- package/dist/primitives/keyboard/keymap.d.ts +17 -0
- package/dist/primitives/keyboard/keymap.js +39 -0
- package/dist/primitives/keyboard/typeahead.d.ts +18 -0
- package/dist/primitives/keyboard/typeahead.js +50 -0
- package/dist/primitives/layer/dismissable.d.ts +26 -0
- package/dist/primitives/layer/dismissable.js +84 -0
- package/dist/primitives/motion/easing.d.ts +9 -0
- package/dist/primitives/motion/easing.js +46 -0
- package/dist/primitives/motion/motion.d.ts +59 -0
- package/dist/primitives/motion/motion.js +80 -0
- package/dist/primitives/portal/Portal.svelte +37 -0
- package/dist/primitives/portal/Portal.svelte.d.ts +9 -0
- package/dist/primitives/portal/PortalContent.svelte +15 -0
- package/dist/primitives/portal/PortalContent.svelte.d.ts +9 -0
- package/dist/primitives/portal/portal.css +8 -0
- package/dist/primitives/scroll-lock/scroll-lock.d.ts +10 -0
- package/dist/primitives/scroll-lock/scroll-lock.js +33 -0
- package/dist/primitives/virtual/virtual-list.svelte.d.ts +65 -0
- package/dist/primitives/virtual/virtual-list.svelte.js +291 -0
- package/dist/styles/base.css +44 -0
- package/dist/styles/props.css +137 -0
- package/dist/styles/tokens.css +84 -0
- package/dist/styles/visually-hidden.css +16 -0
- package/dist/themes/aurora.d.ts +13 -0
- package/dist/themes/aurora.js +103 -0
- package/dist/themes/heritage.d.ts +13 -0
- package/dist/themes/heritage.js +102 -0
- package/dist/themes/maison.d.ts +13 -0
- package/dist/themes/maison.js +99 -0
- package/dist/themes/milano.d.ts +13 -0
- package/dist/themes/milano.js +101 -0
- package/dist/themes/monolith.d.ts +13 -0
- package/dist/themes/monolith.js +101 -0
- package/package.json +87 -5
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// A form that checks its fields on submit. See FormProps for the props.
|
|
3
|
+
import { flushSync } from 'svelte';
|
|
4
|
+
import DefaultsProvider from '../../core/defaults/DefaultsProvider.svelte';
|
|
5
|
+
import type { DefaultsConfig } from '../../core/defaults/context.js';
|
|
6
|
+
import { setFormContext, type FormField } from '../../core/form/context.js';
|
|
7
|
+
import './form.css';
|
|
8
|
+
import type { FormProps } from './types.js';
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
validateOn,
|
|
12
|
+
disabled = false,
|
|
13
|
+
onsubmit,
|
|
14
|
+
onreset,
|
|
15
|
+
children,
|
|
16
|
+
class: className,
|
|
17
|
+
ref = $bindable(null),
|
|
18
|
+
...rest
|
|
19
|
+
}: FormProps = $props();
|
|
20
|
+
|
|
21
|
+
// A registry, not state: nothing renders from it, so a plain Set rather than a SvelteSet.
|
|
22
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- never read reactively
|
|
23
|
+
const fields = new Set<FormField>();
|
|
24
|
+
setFormContext({
|
|
25
|
+
get validateOn() {
|
|
26
|
+
return validateOn;
|
|
27
|
+
},
|
|
28
|
+
register(field) {
|
|
29
|
+
fields.add(field);
|
|
30
|
+
return () => fields.delete(field);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Always rendered, so turning `disabled` on and off never remounts the fields.
|
|
35
|
+
const defaults: DefaultsConfig = $derived(disabled ? { global: { disabled: true } } : {});
|
|
36
|
+
|
|
37
|
+
/** Shows the message of every field. Returns `true` when every field is valid. */
|
|
38
|
+
export function validate(): boolean {
|
|
39
|
+
let valid = true;
|
|
40
|
+
for (const field of fields) if (!field.validate()) valid = false;
|
|
41
|
+
return valid;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Puts every field back to the value it started with, and hides every message. */
|
|
45
|
+
export function reset(): void {
|
|
46
|
+
ref?.reset();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Hides every message and keeps the values. */
|
|
50
|
+
export function resetValidation(): void {
|
|
51
|
+
for (const field of fields) field.resetValidation();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function submit(event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }) {
|
|
55
|
+
const invalid = [...fields].filter((field) => !field.validate());
|
|
56
|
+
if (invalid.length === 0) {
|
|
57
|
+
onsubmit?.(event);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Stop here, including listeners added later such as SvelteKit's enhance, which would
|
|
61
|
+
// otherwise send the form anyway.
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
event.stopImmediatePropagation();
|
|
64
|
+
// Focus the first invalid control in document order, once its message is in the DOM, so
|
|
65
|
+
// its label and error are read out together.
|
|
66
|
+
flushSync();
|
|
67
|
+
const controls = invalid
|
|
68
|
+
.map((field) => field.control())
|
|
69
|
+
.filter((control): control is HTMLElement => control != null);
|
|
70
|
+
controls.sort((a, b) =>
|
|
71
|
+
a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
|
|
72
|
+
);
|
|
73
|
+
controls[0]?.focus();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function afterReset(event: Event & { currentTarget: EventTarget & HTMLFormElement }) {
|
|
77
|
+
resetValidation();
|
|
78
|
+
(onreset as ((event: Event) => void) | null | undefined)?.(event);
|
|
79
|
+
}
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<form
|
|
83
|
+
bind:this={ref}
|
|
84
|
+
{...rest}
|
|
85
|
+
novalidate
|
|
86
|
+
class={['sv-form', className]}
|
|
87
|
+
onsubmit={submit}
|
|
88
|
+
onreset={afterReset}
|
|
89
|
+
>
|
|
90
|
+
<DefaultsProvider {defaults}>
|
|
91
|
+
{@render children?.()}
|
|
92
|
+
</DefaultsProvider>
|
|
93
|
+
</form>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
@layer sv.tokens, sv.base, sv.components, sv.utilities;
|
|
2
|
+
|
|
3
|
+
@layer sv.components {
|
|
4
|
+
/*
|
|
5
|
+
* Fields stack at one rhythm and take the full width; buttons keep their own width. A class
|
|
6
|
+
* on the form changes the layout, for example to a row for a search bar.
|
|
7
|
+
*/
|
|
8
|
+
.sv-form {
|
|
9
|
+
display: grid;
|
|
10
|
+
gap: var(--sv-space-21);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.sv-form > :where(.sv-button) {
|
|
14
|
+
justify-self: start;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Form } from './Form.svelte';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLFormAttributes } from 'svelte/elements';
|
|
3
|
+
import type { ValidateOn } from '../../core/form/types.js';
|
|
4
|
+
import type { ElementProps } from '../../core/props/element.js';
|
|
5
|
+
/**
|
|
6
|
+
* A `<form>` that checks its fields when it is submitted. An invalid form does not submit: every
|
|
7
|
+
* field shows its message and the first invalid one takes focus. A valid form calls `onsubmit`,
|
|
8
|
+
* or submits as HTML does when there is no `onsubmit`, so SvelteKit form actions work as they
|
|
9
|
+
* are. Other attributes go to the `<form>`. Through `bind:this` it offers `validate()`,
|
|
10
|
+
* `reset()` and `resetValidation()`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <Form method="POST" onsubmit={(event) => { event.preventDefault(); save(); }}>
|
|
14
|
+
* <TextField label="Email" name="email" type="email" required />
|
|
15
|
+
* <Button type="submit">Sign up</Button>
|
|
16
|
+
* </Form>
|
|
17
|
+
*/
|
|
18
|
+
export type FormProps = ElementProps<HTMLFormElement, HTMLFormAttributes, {
|
|
19
|
+
/**
|
|
20
|
+
* When the fields show their messages, unless a field sets its own.
|
|
21
|
+
* @default 'blur'
|
|
22
|
+
*/
|
|
23
|
+
validateOn?: ValidateOn;
|
|
24
|
+
/**
|
|
25
|
+
* Disables every control inside, Buttons included, for example while a submission runs.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Called when the form is submitted and every field is valid. Without it, the form submits
|
|
31
|
+
* as HTML does; call `event.preventDefault()` to handle the submission yourself.
|
|
32
|
+
*/
|
|
33
|
+
onsubmit?: (event: SubmitEvent & {
|
|
34
|
+
currentTarget: EventTarget & HTMLFormElement;
|
|
35
|
+
}) => void;
|
|
36
|
+
/** The fields and buttons. */
|
|
37
|
+
children?: Snippet;
|
|
38
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// An icon from path data or any SVG of your own. See IconProps for the props.
|
|
3
|
+
import { sharedClasses } from '../../core/props/classes.js';
|
|
4
|
+
import './icon.css';
|
|
5
|
+
import type { IconProps } from './types.js';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
path,
|
|
9
|
+
viewBox = '0 0 24 24',
|
|
10
|
+
size,
|
|
11
|
+
color,
|
|
12
|
+
label,
|
|
13
|
+
children,
|
|
14
|
+
class: className,
|
|
15
|
+
ref = $bindable(null),
|
|
16
|
+
...rest
|
|
17
|
+
}: IconProps = $props();
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<span
|
|
21
|
+
bind:this={ref}
|
|
22
|
+
{...rest}
|
|
23
|
+
role={label ? 'img' : undefined}
|
|
24
|
+
aria-label={label}
|
|
25
|
+
aria-hidden={label ? undefined : 'true'}
|
|
26
|
+
class={[
|
|
27
|
+
'sv-icon',
|
|
28
|
+
sharedClasses({ size, color }, 'Icon'),
|
|
29
|
+
color && 'sv-icon--colored',
|
|
30
|
+
className
|
|
31
|
+
]}
|
|
32
|
+
>
|
|
33
|
+
{#if path}
|
|
34
|
+
<svg class="sv-icon__paths" {viewBox} aria-hidden="true"><path d={path} /></svg>
|
|
35
|
+
{:else}
|
|
36
|
+
{@render children?.()}
|
|
37
|
+
{/if}
|
|
38
|
+
</span>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@layer sv.tokens, sv.base, sv.components, sv.utilities;
|
|
2
|
+
|
|
3
|
+
@layer sv.components {
|
|
4
|
+
/* φ^½ of the text: the same proportion as icons in buttons. */
|
|
5
|
+
.sv-icon {
|
|
6
|
+
display: inline-flex;
|
|
7
|
+
flex: none;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
inline-size: 1.272em;
|
|
11
|
+
block-size: 1.272em;
|
|
12
|
+
line-height: 1;
|
|
13
|
+
vertical-align: -0.236em;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Only an icon with its own size reads the shared size; otherwise it follows its text. */
|
|
17
|
+
.sv-icon:is(.sv-size-xs, .sv-size-sm, .sv-size-md, .sv-size-lg, .sv-size-xl) {
|
|
18
|
+
font-size: var(--sv-size-text);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.sv-icon--colored {
|
|
22
|
+
color: var(--sv-color);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.sv-icon > svg {
|
|
26
|
+
inline-size: 100%;
|
|
27
|
+
block-size: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.sv-icon__paths {
|
|
31
|
+
fill: currentColor;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Icon } from './Icon.svelte';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { ElementProps } from '../../core/props/element.js';
|
|
4
|
+
import type { Color, Size } from '../../core/props/types.js';
|
|
5
|
+
/**
|
|
6
|
+
* An icon: SVG path data, or any icon of your own as `children`. Decorative unless it has a
|
|
7
|
+
* `label`.
|
|
8
|
+
*/
|
|
9
|
+
export type IconProps = ElementProps<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>, {
|
|
10
|
+
/**
|
|
11
|
+
* SVG path data, filled with the current colour: the format of path-based icon sets such
|
|
12
|
+
* as Material Design Icons. Several shapes go in one string (`M… Z M… Z`).
|
|
13
|
+
*/
|
|
14
|
+
path?: string;
|
|
15
|
+
/**
|
|
16
|
+
* The coordinate box of `path`.
|
|
17
|
+
* @default '0 0 24 24'
|
|
18
|
+
*/
|
|
19
|
+
viewBox?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The text size the icon is drawn at, φ^½ larger. Without it, the icon follows the text
|
|
22
|
+
* around it.
|
|
23
|
+
*/
|
|
24
|
+
size?: Size;
|
|
25
|
+
/** The icon's colour. Without it, the icon takes the colour of the text around it. */
|
|
26
|
+
color?: Color;
|
|
27
|
+
/**
|
|
28
|
+
* A name for an icon that means something on its own, for assistive technology. Without
|
|
29
|
+
* it, the icon is decorative and hidden from it.
|
|
30
|
+
*/
|
|
31
|
+
label?: string;
|
|
32
|
+
/** Any other icon (an `<svg>`, an icon component), sized and coloured like a path. */
|
|
33
|
+
children?: Snippet;
|
|
34
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// A list of ListItems. See ListProps for the props.
|
|
3
|
+
import { useDefaults } from '../../core/defaults/context.js';
|
|
4
|
+
import { sharedClasses } from '../../core/props/classes.js';
|
|
5
|
+
import './list.css';
|
|
6
|
+
import type { ListDefaults, ListProps } from './types.js';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
density,
|
|
10
|
+
color,
|
|
11
|
+
nav,
|
|
12
|
+
children,
|
|
13
|
+
class: className,
|
|
14
|
+
ref = $bindable(null),
|
|
15
|
+
...rest
|
|
16
|
+
}: ListProps = $props();
|
|
17
|
+
|
|
18
|
+
const resolve = useDefaults<ListDefaults>('List', {
|
|
19
|
+
density: 'default',
|
|
20
|
+
color: 'primary',
|
|
21
|
+
nav: false
|
|
22
|
+
});
|
|
23
|
+
const p = $derived(resolve({ density, color, nav }));
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<!-- role="list": Safari drops the list semantics of a list without bullets. -->
|
|
27
|
+
<ul
|
|
28
|
+
bind:this={ref}
|
|
29
|
+
{...rest}
|
|
30
|
+
role="list"
|
|
31
|
+
class={[
|
|
32
|
+
'sv-list',
|
|
33
|
+
sharedClasses({ density: p.density, color: p.color }, 'List'),
|
|
34
|
+
p.nav && 'sv-list--nav',
|
|
35
|
+
className
|
|
36
|
+
]}
|
|
37
|
+
>
|
|
38
|
+
{@render children?.()}
|
|
39
|
+
</ul>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// A row of a List: text, a link or a button. See ListItemProps for the props.
|
|
3
|
+
import './list.css';
|
|
4
|
+
import type { ListItemProps } from './types.js';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
title,
|
|
8
|
+
subtitle,
|
|
9
|
+
href,
|
|
10
|
+
target,
|
|
11
|
+
rel,
|
|
12
|
+
onclick,
|
|
13
|
+
active = false,
|
|
14
|
+
disabled = false,
|
|
15
|
+
prepend,
|
|
16
|
+
append,
|
|
17
|
+
children,
|
|
18
|
+
class: className,
|
|
19
|
+
ref = $bindable(null),
|
|
20
|
+
...rest
|
|
21
|
+
}: ListItemProps = $props();
|
|
22
|
+
|
|
23
|
+
const tag = $derived(href != null ? 'a' : onclick ? 'button' : 'div');
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<li
|
|
27
|
+
bind:this={ref}
|
|
28
|
+
{...rest}
|
|
29
|
+
class={[
|
|
30
|
+
'sv-list-item',
|
|
31
|
+
active && 'sv-list-item--active',
|
|
32
|
+
disabled && 'sv-list-item--disabled',
|
|
33
|
+
className
|
|
34
|
+
]}
|
|
35
|
+
>
|
|
36
|
+
<svelte:element
|
|
37
|
+
this={tag}
|
|
38
|
+
class="sv-list-item__main"
|
|
39
|
+
href={tag === 'a' && !disabled ? href : undefined}
|
|
40
|
+
target={tag === 'a' ? target : undefined}
|
|
41
|
+
rel={tag === 'a' ? rel : undefined}
|
|
42
|
+
type={tag === 'button' ? 'button' : undefined}
|
|
43
|
+
role={tag === 'a' && disabled ? 'link' : undefined}
|
|
44
|
+
disabled={(tag === 'button' && disabled) || undefined}
|
|
45
|
+
aria-disabled={(tag === 'a' && disabled) || undefined}
|
|
46
|
+
aria-current={tag === 'a' && active ? 'page' : undefined}
|
|
47
|
+
onclick={tag === 'button' ? onclick : undefined}
|
|
48
|
+
>
|
|
49
|
+
{#if prepend}<span class="sv-list-item__prepend">{@render prepend()}</span>{/if}
|
|
50
|
+
<span class="sv-list-item__text">
|
|
51
|
+
{#if title}<span class="sv-list-item__title">{title}</span>{/if}
|
|
52
|
+
{#if subtitle}<span class="sv-list-item__subtitle">{subtitle}</span>{/if}
|
|
53
|
+
{@render children?.()}
|
|
54
|
+
</span>
|
|
55
|
+
</svelte:element>
|
|
56
|
+
{#if append}<span class="sv-list-item__append">{@render append()}</span>{/if}
|
|
57
|
+
</li>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
@layer sv.tokens, sv.base, sv.components, sv.utilities;
|
|
2
|
+
|
|
3
|
+
@layer sv.components {
|
|
4
|
+
.sv-list {
|
|
5
|
+
display: grid;
|
|
6
|
+
margin: 0;
|
|
7
|
+
padding: calc(var(--sv-space-5) * var(--sv-density)) 0;
|
|
8
|
+
list-style: none;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.sv-list-item {
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
min-inline-size: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* The row: a control tall, its text a φ step from what is before it. */
|
|
18
|
+
.sv-list-item__main {
|
|
19
|
+
position: relative;
|
|
20
|
+
isolation: isolate;
|
|
21
|
+
display: flex;
|
|
22
|
+
flex: 1;
|
|
23
|
+
gap: var(--sv-space-13);
|
|
24
|
+
align-items: center;
|
|
25
|
+
box-sizing: border-box;
|
|
26
|
+
min-inline-size: 0;
|
|
27
|
+
min-block-size: calc(var(--sv-control-md) * var(--sv-density));
|
|
28
|
+
margin: 0;
|
|
29
|
+
padding: calc(var(--sv-space-5) * var(--sv-density))
|
|
30
|
+
calc(var(--sv-space-13) * var(--sv-density));
|
|
31
|
+
border: 0;
|
|
32
|
+
background: transparent;
|
|
33
|
+
color: inherit;
|
|
34
|
+
font: inherit;
|
|
35
|
+
font-size: var(--sv-text-sm);
|
|
36
|
+
line-height: var(--sv-leading-snug);
|
|
37
|
+
text-align: start;
|
|
38
|
+
text-decoration: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Rows that act share Button's state layer. */
|
|
42
|
+
:is(a, button).sv-list-item__main {
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
-webkit-tap-highlight-color: transparent;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
:is(a, button).sv-list-item__main::before {
|
|
48
|
+
content: '';
|
|
49
|
+
position: absolute;
|
|
50
|
+
inset: 0;
|
|
51
|
+
z-index: -1;
|
|
52
|
+
border-radius: inherit;
|
|
53
|
+
background-color: currentColor;
|
|
54
|
+
opacity: 0;
|
|
55
|
+
pointer-events: none;
|
|
56
|
+
transition: opacity var(--sv-duration-fast) var(--sv-ease);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@media (hover: hover) {
|
|
60
|
+
.sv-list-item:not(.sv-list-item--disabled) :is(a, button).sv-list-item__main:hover::before {
|
|
61
|
+
opacity: var(--sv-state-hover);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.sv-list-item:not(.sv-list-item--disabled) :is(a, button).sv-list-item__main:active::before {
|
|
66
|
+
opacity: var(--sv-state-press);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Navigation: rounded rows, set in from the edges. */
|
|
70
|
+
.sv-list--nav {
|
|
71
|
+
padding-inline: var(--sv-space-5);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.sv-list--nav .sv-list-item__main {
|
|
75
|
+
border-radius: var(--sv-radius-md);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.sv-list-item--active .sv-list-item__main {
|
|
79
|
+
background-color: color-mix(in oklch, var(--sv-color) var(--sv-tonal-wash), transparent);
|
|
80
|
+
color: color-mix(in oklch, var(--sv-color) var(--sv-tonal-text-mix), var(--sv-color-text));
|
|
81
|
+
font-weight: var(--sv-weight-medium);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.sv-list-item--disabled {
|
|
85
|
+
opacity: 0.382;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.sv-list-item--disabled .sv-list-item__main {
|
|
89
|
+
cursor: not-allowed;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.sv-list-item__prepend,
|
|
93
|
+
.sv-list-item__append {
|
|
94
|
+
display: flex;
|
|
95
|
+
flex: none;
|
|
96
|
+
align-items: center;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.sv-list-item__append {
|
|
100
|
+
padding-inline-end: calc(var(--sv-space-13) * var(--sv-density));
|
|
101
|
+
font-size: var(--sv-text-sm);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
:where(.sv-list-item__prepend, .sv-list-item__append) svg {
|
|
105
|
+
inline-size: 1.272em;
|
|
106
|
+
block-size: 1.272em;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.sv-list-item__text {
|
|
110
|
+
display: grid;
|
|
111
|
+
flex: 1;
|
|
112
|
+
gap: var(--sv-space-2);
|
|
113
|
+
min-inline-size: 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.sv-list-item__title {
|
|
117
|
+
overflow: hidden;
|
|
118
|
+
text-overflow: ellipsis;
|
|
119
|
+
white-space: nowrap;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.sv-list-item__subtitle {
|
|
123
|
+
color: var(--sv-color-text-subtle);
|
|
124
|
+
font-size: var(--sv-text-xs);
|
|
125
|
+
font-weight: var(--sv-weight-regular);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAnchorAttributes, HTMLAttributes, HTMLLiAttributes } from 'svelte/elements';
|
|
3
|
+
import type { ElementProps } from '../../core/props/element.js';
|
|
4
|
+
import type { Color, Density } from '../../core/props/types.js';
|
|
5
|
+
/** The List props that `DefaultsProvider` and `UIProvider` can set for a subtree. */
|
|
6
|
+
export interface ListDefaults {
|
|
7
|
+
/**
|
|
8
|
+
* Multiplies the height and padding of its items.
|
|
9
|
+
* @default 'default'
|
|
10
|
+
*/
|
|
11
|
+
density: Density;
|
|
12
|
+
/**
|
|
13
|
+
* The colour of the active item.
|
|
14
|
+
* @default 'primary'
|
|
15
|
+
*/
|
|
16
|
+
color: Color;
|
|
17
|
+
/**
|
|
18
|
+
* Rounded rows set in from the edges, for navigation.
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
nav: boolean;
|
|
22
|
+
}
|
|
23
|
+
declare module '../../core/defaults/context.js' {
|
|
24
|
+
interface ComponentDefaults {
|
|
25
|
+
List: ListDefaults;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** A list of rows: `ListItem`s. Density and colour are set here, for every item. */
|
|
29
|
+
export type ListProps = ElementProps<HTMLUListElement, HTMLAttributes<HTMLUListElement>, Partial<ListDefaults> & {
|
|
30
|
+
/** The `ListItem`s. */
|
|
31
|
+
children?: Snippet;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* A row of a `List`. With `href` it is a link and with `onclick` a button, filling the row;
|
|
35
|
+
* `append` stays outside it, so it can hold controls of its own.
|
|
36
|
+
*/
|
|
37
|
+
export type ListItemProps = ElementProps<HTMLLIElement, HTMLLiAttributes, {
|
|
38
|
+
/** The main line. */
|
|
39
|
+
title?: string;
|
|
40
|
+
/** A second line, under the title. */
|
|
41
|
+
subtitle?: string;
|
|
42
|
+
/** Makes the row a link. */
|
|
43
|
+
href?: string;
|
|
44
|
+
/** Where a link opens, as on `<a>`. */
|
|
45
|
+
target?: HTMLAnchorAttributes['target'];
|
|
46
|
+
/** The relationship of a link, as on `<a>`. */
|
|
47
|
+
rel?: string;
|
|
48
|
+
/** Makes the row a button that calls this. */
|
|
49
|
+
onclick?: (event: MouseEvent) => void;
|
|
50
|
+
/**
|
|
51
|
+
* The current page or the selected row: tinted in the list's colour, and
|
|
52
|
+
* `aria-current="page"` on a link.
|
|
53
|
+
* @default false
|
|
54
|
+
*/
|
|
55
|
+
active?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* A disabled row cannot be pressed; a disabled link loses its `href`.
|
|
58
|
+
* @default false
|
|
59
|
+
*/
|
|
60
|
+
disabled?: boolean;
|
|
61
|
+
/** Content before the text, such as an avatar or an icon. */
|
|
62
|
+
prepend?: Snippet;
|
|
63
|
+
/** Content after the row's link or button, such as a value or a menu button. */
|
|
64
|
+
append?: Snippet;
|
|
65
|
+
/** More content under the title and subtitle. */
|
|
66
|
+
children?: Snippet;
|
|
67
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|