@sveltier/ui 0.0.0-stage → 0.1.0-alpha.1
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/Accordion/Accordion.svelte +74 -0
- package/dist/components/Accordion/Accordion.svelte.d.ts +25 -0
- package/dist/components/Accordion/accordion.css +88 -0
- package/dist/components/Accordion/index.d.ts +2 -0
- package/dist/components/Accordion/index.js +1 -0
- package/dist/components/Accordion/types.d.ts +64 -0
- package/dist/components/Accordion/types.js +1 -0
- 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/Dialog/Dialog.svelte +36 -0
- package/dist/components/Dialog/Dialog.svelte.d.ts +4 -0
- package/dist/components/Dialog/index.d.ts +2 -0
- package/dist/components/Dialog/index.js +1 -0
- package/dist/components/Dialog/types.d.ts +50 -0
- package/dist/components/Dialog/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/Menu/Menu.svelte +195 -0
- package/dist/components/Menu/Menu.svelte.d.ts +4 -0
- package/dist/components/Menu/index.d.ts +2 -0
- package/dist/components/Menu/index.js +1 -0
- package/dist/components/Menu/menu.css +103 -0
- package/dist/components/Menu/types.d.ts +90 -0
- package/dist/components/Menu/types.js +1 -0
- package/dist/components/Modal/Modal.svelte +146 -0
- package/dist/components/Modal/Modal.svelte.d.ts +4 -0
- package/dist/components/Modal/modal.css +150 -0
- package/dist/components/Modal/types.d.ts +54 -0
- package/dist/components/Modal/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/Popover/Popover.svelte +115 -0
- package/dist/components/Popover/Popover.svelte.d.ts +4 -0
- package/dist/components/Popover/index.d.ts +2 -0
- package/dist/components/Popover/index.js +1 -0
- package/dist/components/Popover/popover.css +67 -0
- package/dist/components/Popover/types.d.ts +83 -0
- package/dist/components/Popover/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/Sheet/Sheet.svelte +56 -0
- package/dist/components/Sheet/Sheet.svelte.d.ts +4 -0
- package/dist/components/Sheet/index.d.ts +2 -0
- package/dist/components/Sheet/index.js +2 -0
- package/dist/components/Sheet/sheet.css +61 -0
- package/dist/components/Sheet/types.d.ts +51 -0
- package/dist/components/Sheet/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/components/Tooltip/Tooltip.svelte +161 -0
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +4 -0
- package/dist/components/Tooltip/index.d.ts +2 -0
- package/dist/components/Tooltip/index.js +1 -0
- package/dist/components/Tooltip/tooltip.css +44 -0
- package/dist/components/Tooltip/types.d.ts +55 -0
- package/dist/components/Tooltip/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 +124 -0
- package/dist/core/errors/codes.js +108 -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 +56 -0
- package/dist/core/locale/locale.js +82 -0
- package/dist/core/overlay/types.d.ts +33 -0
- package/dist/core/overlay/types.js +1 -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 +63 -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 +73 -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/core/toast/ToastOutlet.svelte +192 -0
- package/dist/core/toast/ToastOutlet.svelte.d.ts +11 -0
- package/dist/core/toast/ToastProvider.svelte +39 -0
- package/dist/core/toast/ToastProvider.svelte.d.ts +19 -0
- package/dist/core/toast/toast.css +134 -0
- package/dist/core/toast/toast.svelte.d.ts +117 -0
- package/dist/core/toast/toast.svelte.js +133 -0
- package/dist/dev/off.d.ts +5 -0
- package/dist/dev/off.js +4 -0
- package/dist/dev/on.d.ts +20 -0
- package/dist/dev/on.js +49 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +83 -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 +32 -0
- package/dist/primitives/layer/dismissable.js +91 -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 +140 -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,146 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// A modal <dialog> in the top layer with a panel: the shell of Dialog and Sheet. See
|
|
3
|
+
// ModalProps.
|
|
4
|
+
import { createAttachmentKey, type Attachment } from 'svelte/attachments';
|
|
5
|
+
import { DEV, warnUnnamed } from '#dev';
|
|
6
|
+
import { useLocale } from '../../core/locale/locale.js';
|
|
7
|
+
import type { CloseReason, TriggerAttributes } from '../../core/overlay/types.js';
|
|
8
|
+
import { getToasts } from '../../core/toast/toast.svelte.js';
|
|
9
|
+
import ToastOutlet from '../../core/toast/ToastOutlet.svelte';
|
|
10
|
+
import { focusTrap } from '../../primitives/focus/focus-trap.js';
|
|
11
|
+
import { dismissable } from '../../primitives/layer/dismissable.js';
|
|
12
|
+
import { fade } from '../../primitives/motion/motion.js';
|
|
13
|
+
import { scrollLock } from '../../primitives/scroll-lock/scroll-lock.js';
|
|
14
|
+
import './modal.css';
|
|
15
|
+
import type { ModalProps } from './types.js';
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
component,
|
|
19
|
+
placement,
|
|
20
|
+
panelClass,
|
|
21
|
+
enter,
|
|
22
|
+
open = $bindable(false),
|
|
23
|
+
title,
|
|
24
|
+
children,
|
|
25
|
+
actions,
|
|
26
|
+
trigger,
|
|
27
|
+
persistent = false,
|
|
28
|
+
closable = false,
|
|
29
|
+
closeLabel,
|
|
30
|
+
fullscreen = false,
|
|
31
|
+
onclose,
|
|
32
|
+
id: idProp,
|
|
33
|
+
class: className,
|
|
34
|
+
ref = $bindable(null),
|
|
35
|
+
...rest
|
|
36
|
+
}: ModalProps = $props();
|
|
37
|
+
|
|
38
|
+
const uid = $props.id();
|
|
39
|
+
const id = $derived(idProp ?? uid);
|
|
40
|
+
const locale = useLocale();
|
|
41
|
+
|
|
42
|
+
const CLOSE =
|
|
43
|
+
'M6.4 5 12 10.6 17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6L6.4 19 5 17.6l5.6-5.6L5 6.4z';
|
|
44
|
+
|
|
45
|
+
// The control that opened it: focus goes back to it, as Safari does not focus a clicked
|
|
46
|
+
// button by itself.
|
|
47
|
+
let opener = $state<HTMLElement | null>(null);
|
|
48
|
+
const capture: Attachment<HTMLElement> = (node) => {
|
|
49
|
+
opener = node;
|
|
50
|
+
return () => {
|
|
51
|
+
if (opener === node) opener = null;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
const OPENER = createAttachmentKey();
|
|
55
|
+
const triggerAttributes: TriggerAttributes = $derived({
|
|
56
|
+
'aria-haspopup': 'dialog',
|
|
57
|
+
'aria-controls': open ? id : undefined,
|
|
58
|
+
onclick: () => (open = true),
|
|
59
|
+
[OPENER]: capture
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
function close(reason: CloseReason) {
|
|
63
|
+
if (!open) return;
|
|
64
|
+
open = false;
|
|
65
|
+
onclose?.(reason);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// The top layer, with the rest of the page inert.
|
|
69
|
+
const modal: Attachment<HTMLDialogElement> = (dialog) => {
|
|
70
|
+
dialog.showModal();
|
|
71
|
+
if (DEV) warnUnnamed(component, dialog, 'a title');
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// The rest of the page is inert under a modal dialog, toasts included, so they show in the
|
|
75
|
+
// dialog while it is open, and go back to the page as soon as it starts to close.
|
|
76
|
+
const toasts = getToasts();
|
|
77
|
+
const outlet = {};
|
|
78
|
+
let toastRegion = $state<HTMLElement | null>(null);
|
|
79
|
+
$effect(() => {
|
|
80
|
+
if (open && toasts) return toasts.register(outlet);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Escape is left to the layer stack, so nested layers close one at a time. The browser may
|
|
84
|
+
// still close the dialog itself: Chrome does after repeated presses, and a form with
|
|
85
|
+
// method="dialog" does on submit. Either way the state follows, with the likely reason.
|
|
86
|
+
let escaped = false;
|
|
87
|
+
function cancel(event: Event) {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
escaped = true;
|
|
90
|
+
}
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
{#if trigger}{@render trigger(triggerAttributes)}{/if}
|
|
94
|
+
|
|
95
|
+
{#if open}
|
|
96
|
+
<dialog
|
|
97
|
+
bind:this={ref}
|
|
98
|
+
aria-labelledby={title ? `${id}-title` : undefined}
|
|
99
|
+
{...rest}
|
|
100
|
+
{id}
|
|
101
|
+
class={['sv-modal', `sv-modal--${placement}`, fullscreen && 'sv-modal--fullscreen', className]}
|
|
102
|
+
{@attach modal}
|
|
103
|
+
{@attach scrollLock()}
|
|
104
|
+
{@attach focusTrap({ returnFocus: opener ?? true })}
|
|
105
|
+
oncancel={cancel}
|
|
106
|
+
onclose={() => close(escaped ? 'escape' : 'action')}
|
|
107
|
+
transition:fade
|
|
108
|
+
>
|
|
109
|
+
<!-- The scrim is the dialog itself, so a press beside the panel is a press outside it. -->
|
|
110
|
+
<div
|
|
111
|
+
class={['sv-modal__panel', panelClass]}
|
|
112
|
+
{@attach dismissable({
|
|
113
|
+
onDismiss: close,
|
|
114
|
+
escape: !persistent,
|
|
115
|
+
outside: !persistent,
|
|
116
|
+
inside: () => [toastRegion]
|
|
117
|
+
})}
|
|
118
|
+
transition:enter
|
|
119
|
+
>
|
|
120
|
+
{#if title || closable}
|
|
121
|
+
<header class="sv-modal__header">
|
|
122
|
+
{#if title}<h2 class="sv-modal__title" id="{id}-title">{title}</h2>{/if}
|
|
123
|
+
{#if closable}
|
|
124
|
+
<button
|
|
125
|
+
type="button"
|
|
126
|
+
class="sv-modal__close"
|
|
127
|
+
aria-label={closeLabel ?? locale.t('close')}
|
|
128
|
+
onclick={() => close('action')}
|
|
129
|
+
>
|
|
130
|
+
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"
|
|
131
|
+
><path d={CLOSE} /></svg
|
|
132
|
+
>
|
|
133
|
+
</button>
|
|
134
|
+
{/if}
|
|
135
|
+
</header>
|
|
136
|
+
{/if}
|
|
137
|
+
{#if children}<div class="sv-modal__body">{@render children()}</div>{/if}
|
|
138
|
+
{#if actions}
|
|
139
|
+
<footer class="sv-modal__actions">
|
|
140
|
+
{@render actions({ close: () => close('action') })}
|
|
141
|
+
</footer>
|
|
142
|
+
{/if}
|
|
143
|
+
</div>
|
|
144
|
+
{#if toasts}<ToastOutlet {toasts} {outlet} bind:ref={toastRegion} />{/if}
|
|
145
|
+
</dialog>
|
|
146
|
+
{/if}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
@layer sv.tokens, sv.base, sv.components, sv.utilities;
|
|
2
|
+
|
|
3
|
+
@layer sv.components {
|
|
4
|
+
/*
|
|
5
|
+
* The <dialog> is the whole viewport: a scrim with the panel on it, so a press beside the panel
|
|
6
|
+
* is a press outside it. The scrim comes with the theme: its ink over a light theme,
|
|
7
|
+
* its background over a dark one, so custom themes get one in their own tone too.
|
|
8
|
+
*/
|
|
9
|
+
.sv-modal {
|
|
10
|
+
position: fixed;
|
|
11
|
+
inset: 0;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
display: grid;
|
|
14
|
+
place-items: center;
|
|
15
|
+
inline-size: 100%;
|
|
16
|
+
max-inline-size: none;
|
|
17
|
+
block-size: 100%;
|
|
18
|
+
max-block-size: none;
|
|
19
|
+
margin: 0;
|
|
20
|
+
padding: var(--sv-space-21);
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
border: 0;
|
|
23
|
+
background: var(--sv-color-scrim);
|
|
24
|
+
color: var(--sv-color-text);
|
|
25
|
+
overscroll-behavior: contain;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.sv-modal::backdrop {
|
|
29
|
+
background: transparent;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* The panel: a large surface, as wide as its size allows, as tall as the screen at most. */
|
|
33
|
+
.sv-modal__panel {
|
|
34
|
+
--sv-modal-pad: calc(var(--sv-space-21) * var(--sv-density));
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
inline-size: 100%;
|
|
39
|
+
max-inline-size: var(--sv-modal-width);
|
|
40
|
+
max-block-size: 100%;
|
|
41
|
+
border-radius: var(--sv-rounded, var(--sv-radius-xl));
|
|
42
|
+
background-color: var(--sv-color-surface);
|
|
43
|
+
box-shadow: var(--sv-shadow);
|
|
44
|
+
font-family: var(--sv-font-ui);
|
|
45
|
+
font-size: var(--sv-text-md);
|
|
46
|
+
line-height: var(--sv-leading-normal);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Widths on a half step of φ: 21, 27, 34, 44 and 55rem. */
|
|
50
|
+
.sv-modal__panel.sv-size-xs {
|
|
51
|
+
--sv-modal-width: 21rem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.sv-modal__panel.sv-size-sm {
|
|
55
|
+
--sv-modal-width: 27rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.sv-modal__panel.sv-size-md {
|
|
59
|
+
--sv-modal-width: 34rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.sv-modal__panel.sv-size-lg {
|
|
63
|
+
--sv-modal-width: 44rem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.sv-modal__panel.sv-size-xl {
|
|
67
|
+
--sv-modal-width: 55rem;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.sv-modal__header {
|
|
71
|
+
display: flex;
|
|
72
|
+
flex: none;
|
|
73
|
+
gap: var(--sv-space-13);
|
|
74
|
+
align-items: flex-start;
|
|
75
|
+
padding: var(--sv-modal-pad) var(--sv-modal-pad) 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* The heading in the theme's display voice, as a Card title. */
|
|
79
|
+
.sv-modal__title {
|
|
80
|
+
flex: 1;
|
|
81
|
+
min-inline-size: 0;
|
|
82
|
+
margin: 0;
|
|
83
|
+
font: var(--sv-weight-display) var(--sv-text-xl) / var(--sv-leading-snug) var(--sv-font-display);
|
|
84
|
+
letter-spacing: var(--sv-tracking-display);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.sv-modal__close {
|
|
88
|
+
display: inline-grid;
|
|
89
|
+
flex: none;
|
|
90
|
+
place-items: center;
|
|
91
|
+
inline-size: 2.058rem;
|
|
92
|
+
block-size: 2.058rem;
|
|
93
|
+
margin-block: -0.236rem;
|
|
94
|
+
margin-inline-end: -0.382rem;
|
|
95
|
+
padding: 0;
|
|
96
|
+
border: 0;
|
|
97
|
+
border-radius: var(--sv-radius-full);
|
|
98
|
+
background: transparent;
|
|
99
|
+
color: var(--sv-color-text-muted);
|
|
100
|
+
cursor: pointer;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@media (hover: hover) {
|
|
104
|
+
.sv-modal__close:hover {
|
|
105
|
+
background-color: color-mix(in oklch, currentColor var(--sv-state-hover), transparent);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.sv-modal__close svg {
|
|
110
|
+
inline-size: 1.272rem;
|
|
111
|
+
block-size: 1.272rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* The body scrolls; the heading and the actions stay. */
|
|
115
|
+
.sv-modal__body {
|
|
116
|
+
flex: 1;
|
|
117
|
+
min-block-size: 0;
|
|
118
|
+
padding: var(--sv-modal-pad);
|
|
119
|
+
overflow-y: auto;
|
|
120
|
+
overscroll-behavior: contain;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.sv-modal__header + .sv-modal__body {
|
|
124
|
+
padding-block-start: var(--sv-space-13);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.sv-modal__actions {
|
|
128
|
+
display: flex;
|
|
129
|
+
flex: none;
|
|
130
|
+
flex-wrap: wrap;
|
|
131
|
+
gap: var(--sv-space-8);
|
|
132
|
+
justify-content: flex-end;
|
|
133
|
+
padding: 0 var(--sv-modal-pad) var(--sv-modal-pad);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.sv-modal__header:last-child,
|
|
137
|
+
.sv-modal__body:last-child {
|
|
138
|
+
padding-block-end: var(--sv-modal-pad);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.sv-modal--fullscreen {
|
|
142
|
+
padding: 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.sv-modal--fullscreen > .sv-modal__panel {
|
|
146
|
+
max-inline-size: none;
|
|
147
|
+
block-size: 100%;
|
|
148
|
+
border-radius: 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { ClassValue, HTMLDialogAttributes } from 'svelte/elements';
|
|
3
|
+
import type { TransitionConfig } from 'svelte/transition';
|
|
4
|
+
import type { CloseReason, TriggerAttributes } from '../../core/overlay/types.js';
|
|
5
|
+
import type { ElementProps } from '../../core/props/element.js';
|
|
6
|
+
import type { SheetSide } from '../Sheet/types.js';
|
|
7
|
+
/** The props Dialog and Sheet share. */
|
|
8
|
+
export interface ModalContentProps {
|
|
9
|
+
/**
|
|
10
|
+
* Whether it is open. Bindable; it sets this to `false` when it closes itself.
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
open?: boolean;
|
|
14
|
+
/** The heading, which also names it. Without it, name it with `aria-label`. */
|
|
15
|
+
title?: string;
|
|
16
|
+
/** The body. */
|
|
17
|
+
children?: Snippet;
|
|
18
|
+
/** Buttons at the end. `close()` closes it with the reason `'action'`. */
|
|
19
|
+
actions?: Snippet<[{
|
|
20
|
+
close: () => void;
|
|
21
|
+
}]>;
|
|
22
|
+
/** The control that opens it. Spread the attributes it receives onto it. */
|
|
23
|
+
trigger?: Snippet<[TriggerAttributes]>;
|
|
24
|
+
/**
|
|
25
|
+
* Escape and a press on the backdrop do not close it: an answer is needed.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
persistent?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Adds a close button to the heading.
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
closable?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The accessible name of the close button.
|
|
36
|
+
* @default 'Close' (from the locale)
|
|
37
|
+
*/
|
|
38
|
+
closeLabel?: string;
|
|
39
|
+
/** Called when it closes itself, with the reason. */
|
|
40
|
+
onclose?: (reason: CloseReason) => void;
|
|
41
|
+
}
|
|
42
|
+
/** A modal `<dialog>` with a panel, the shell of Dialog and Sheet. Internal. */
|
|
43
|
+
export type ModalProps = ElementProps<HTMLDialogElement, Omit<HTMLDialogAttributes, 'open' | 'title' | 'children'>, ModalContentProps & {
|
|
44
|
+
/** The component's name, for development warnings. */
|
|
45
|
+
component: string;
|
|
46
|
+
/** Where the panel sits: in the centre (a dialog) or against an edge (a sheet). */
|
|
47
|
+
placement: 'center' | SheetSide;
|
|
48
|
+
/** The shared-prop classes of the panel. */
|
|
49
|
+
panelClass: ClassValue;
|
|
50
|
+
/** How the panel comes in and goes: a dialog pops, a sheet slides. */
|
|
51
|
+
enter: (node: Element) => TransitionConfig;
|
|
52
|
+
/** The panel fills the screen. */
|
|
53
|
+
fullscreen?: boolean;
|
|
54
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<script lang="ts" generics="T extends string | number">
|
|
2
|
+
// One choice from a list, as the platform's own select in a Field. See NativeSelectProps.
|
|
3
|
+
import { untrack } from 'svelte';
|
|
4
|
+
import { DEV, warnUnnamed } from '#dev';
|
|
5
|
+
import { useDefaults } from '../../core/defaults/context.js';
|
|
6
|
+
import { createField } from '../../core/form/field.svelte.js';
|
|
7
|
+
import type { Rule } from '../../core/form/types.js';
|
|
8
|
+
import { useLocale } from '../../core/locale/locale.js';
|
|
9
|
+
import Field from '../Field/Field.svelte';
|
|
10
|
+
import './native-select.css';
|
|
11
|
+
import type { NativeSelectDefaults, NativeSelectProps } from './types.js';
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
variant,
|
|
15
|
+
color,
|
|
16
|
+
size,
|
|
17
|
+
density,
|
|
18
|
+
rounded,
|
|
19
|
+
disabled,
|
|
20
|
+
options,
|
|
21
|
+
value = $bindable(null),
|
|
22
|
+
placeholder,
|
|
23
|
+
label,
|
|
24
|
+
hint,
|
|
25
|
+
error,
|
|
26
|
+
rules,
|
|
27
|
+
validateOn,
|
|
28
|
+
required = false,
|
|
29
|
+
prepend,
|
|
30
|
+
id,
|
|
31
|
+
'aria-describedby': describedBy,
|
|
32
|
+
class: className,
|
|
33
|
+
style,
|
|
34
|
+
ref = $bindable(null),
|
|
35
|
+
...rest
|
|
36
|
+
}: NativeSelectProps<T> = $props();
|
|
37
|
+
|
|
38
|
+
const resolve = useDefaults<NativeSelectDefaults>('NativeSelect', {
|
|
39
|
+
variant: 'outlined',
|
|
40
|
+
color: 'primary',
|
|
41
|
+
size: 'md',
|
|
42
|
+
density: 'default',
|
|
43
|
+
rounded: true,
|
|
44
|
+
disabled: false
|
|
45
|
+
});
|
|
46
|
+
const p = $derived(resolve({ variant, color, size, density, rounded, disabled }));
|
|
47
|
+
const locale = useLocale();
|
|
48
|
+
|
|
49
|
+
// The choice a form reset goes back to: the one the select started with.
|
|
50
|
+
const initial = untrack(() => value ?? null);
|
|
51
|
+
|
|
52
|
+
const field = createField<T | null>({
|
|
53
|
+
value: () => value ?? null,
|
|
54
|
+
empty: (choice) => choice === null,
|
|
55
|
+
check: (choice) => (choice === null && required ? locale.t('required') : undefined),
|
|
56
|
+
// Rules never see null: an empty choice skips them.
|
|
57
|
+
rules: () => rules as readonly Rule<T | null>[] | undefined,
|
|
58
|
+
error: () => error,
|
|
59
|
+
validateOn: () => validateOn,
|
|
60
|
+
control: () => ref
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (DEV) $effect(() => warnUnnamed('NativeSelect', ref));
|
|
64
|
+
|
|
65
|
+
// Leaving means focus went outside the field.
|
|
66
|
+
function leave(event: FocusEvent) {
|
|
67
|
+
const root = event.currentTarget as HTMLElement;
|
|
68
|
+
if (!root.contains(event.relatedTarget as Node | null)) field.left();
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<Field
|
|
73
|
+
{...p}
|
|
74
|
+
{id}
|
|
75
|
+
{label}
|
|
76
|
+
{hint}
|
|
77
|
+
error={field.error}
|
|
78
|
+
{required}
|
|
79
|
+
{prepend}
|
|
80
|
+
class={[
|
|
81
|
+
'sv-native-select',
|
|
82
|
+
placeholder !== undefined && value == null && 'sv-native-select--empty',
|
|
83
|
+
className
|
|
84
|
+
]}
|
|
85
|
+
{style}
|
|
86
|
+
oninput={field.changed}
|
|
87
|
+
onfocusout={leave}
|
|
88
|
+
>
|
|
89
|
+
{#snippet children(attributes)}
|
|
90
|
+
<select
|
|
91
|
+
bind:this={ref}
|
|
92
|
+
{...rest}
|
|
93
|
+
{...attributes}
|
|
94
|
+
aria-describedby={[describedBy, attributes['aria-describedby']].filter(Boolean).join(' ') ||
|
|
95
|
+
undefined}
|
|
96
|
+
bind:value={() => value ?? '', (next: T | '') => (value = next === '' ? null : next)}
|
|
97
|
+
>
|
|
98
|
+
{#if placeholder !== undefined}
|
|
99
|
+
<option value="" disabled={required} selected={initial === null}>{placeholder}</option>
|
|
100
|
+
{/if}
|
|
101
|
+
{#each options as option (option.value)}
|
|
102
|
+
<option
|
|
103
|
+
value={option.value}
|
|
104
|
+
disabled={option.disabled}
|
|
105
|
+
selected={Object.is(option.value, initial)}>{option.label}</option
|
|
106
|
+
>
|
|
107
|
+
{/each}
|
|
108
|
+
</select>
|
|
109
|
+
{/snippet}
|
|
110
|
+
</Field>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { NativeSelectProps } from './types.js';
|
|
2
|
+
declare function $$render<T extends string | number>(): {
|
|
3
|
+
props: NativeSelectProps<T>;
|
|
4
|
+
exports: {};
|
|
5
|
+
bindings: "ref" | "value";
|
|
6
|
+
slots: {};
|
|
7
|
+
events: {};
|
|
8
|
+
};
|
|
9
|
+
declare class __sveltets_Render<T extends string | number> {
|
|
10
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
11
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
12
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
13
|
+
bindings(): "ref" | "value";
|
|
14
|
+
exports(): {};
|
|
15
|
+
}
|
|
16
|
+
interface $$IsomorphicComponent {
|
|
17
|
+
new <T extends string | number>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
18
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
19
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
20
|
+
<T extends string | number>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
21
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
22
|
+
}
|
|
23
|
+
declare const NativeSelect: $$IsomorphicComponent;
|
|
24
|
+
type NativeSelect<T extends string | number> = InstanceType<typeof NativeSelect<T>>;
|
|
25
|
+
export default NativeSelect;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as NativeSelect } from './NativeSelect.svelte';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
@layer sv.tokens, sv.base, sv.components, sv.utilities;
|
|
2
|
+
|
|
3
|
+
@layer sv.components {
|
|
4
|
+
.sv-native-select > .sv-field__box {
|
|
5
|
+
cursor: pointer;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Room at the end for the arrow, which the text never runs under. */
|
|
9
|
+
.sv-native-select .sv-field__control {
|
|
10
|
+
padding-inline-end: calc(1.272em + var(--sv-space-8));
|
|
11
|
+
text-overflow: ellipsis;
|
|
12
|
+
cursor: inherit;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* The placeholder reads as one, as in a text field; the list keeps the text colour. */
|
|
16
|
+
.sv-native-select--empty .sv-field__control {
|
|
17
|
+
color: var(--sv-color-text-subtle);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.sv-native-select .sv-field__control option {
|
|
21
|
+
color: var(--sv-color-text);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* The arrow: a mask filled with the muted text colour. It lets presses through to the select,
|
|
26
|
+
* so the whole box opens the list.
|
|
27
|
+
*/
|
|
28
|
+
.sv-native-select > .sv-field__box::before {
|
|
29
|
+
content: '';
|
|
30
|
+
position: absolute;
|
|
31
|
+
inset-block: 0;
|
|
32
|
+
inset-inline-end: calc(var(--sv-size-height) * var(--sv-density) * 0.382);
|
|
33
|
+
inline-size: 1.272em;
|
|
34
|
+
block-size: 1.272em;
|
|
35
|
+
margin-block: auto;
|
|
36
|
+
background-color: var(--sv-color-text-muted);
|
|
37
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E")
|
|
38
|
+
center / contain no-repeat;
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.sv-native-select:where(.sv-variant-text, .sv-variant-plain) > .sv-field__box::before {
|
|
43
|
+
inset-inline-end: 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { HTMLSelectAttributes } from 'svelte/elements';
|
|
2
|
+
import type { Rule, ValidateOn } from '../../core/form/types.js';
|
|
3
|
+
import type { ElementProps } from '../../core/props/element.js';
|
|
4
|
+
import type { FieldContentProps, FieldDefaults } from '../Field/types.js';
|
|
5
|
+
/** The NativeSelect props that `DefaultsProvider` and `UIProvider` can set for a subtree. */
|
|
6
|
+
export type NativeSelectDefaults = FieldDefaults;
|
|
7
|
+
declare module '../../core/defaults/context.js' {
|
|
8
|
+
interface ComponentDefaults {
|
|
9
|
+
NativeSelect: NativeSelectDefaults;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** One choice of a NativeSelect. */
|
|
13
|
+
export interface NativeSelectOption<T extends string | number = string> {
|
|
14
|
+
/** The value, never the empty string, which stands for no choice. */
|
|
15
|
+
value: T;
|
|
16
|
+
/** What the option says. */
|
|
17
|
+
label: string;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* One choice from a list, as the platform's own `<select>` in a Field: the phone's picker and the
|
|
22
|
+
* browser's keyboard behaviour come for free. `class` and `style` apply to the field; `id`, `ref`,
|
|
23
|
+
* `name` and other attributes go to the `<select>`. The end of the box holds its arrow, so there
|
|
24
|
+
* is no `append`.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* <NativeSelect
|
|
28
|
+
* label="Country"
|
|
29
|
+
* name="country"
|
|
30
|
+
* placeholder="Choose a country"
|
|
31
|
+
* required
|
|
32
|
+
* options={[
|
|
33
|
+
* { value: 'gb', label: 'United Kingdom' },
|
|
34
|
+
* { value: 'vn', label: 'Vietnam' }
|
|
35
|
+
* ]}
|
|
36
|
+
* bind:value={country}
|
|
37
|
+
* />
|
|
38
|
+
*/
|
|
39
|
+
export type NativeSelectProps<T extends string | number = string> = ElementProps<HTMLSelectElement, HTMLSelectAttributes, Partial<NativeSelectDefaults> & Omit<FieldContentProps, 'append'> & {
|
|
40
|
+
/** The choices, in order. */
|
|
41
|
+
options: readonly NativeSelectOption<T>[];
|
|
42
|
+
/** The chosen value, or `null` for none. Bindable. */
|
|
43
|
+
value?: T | null;
|
|
44
|
+
/** A first option that stands for no choice, such as "Choose a country". */
|
|
45
|
+
placeholder?: string;
|
|
46
|
+
/** Checks run in order after `required`, with the chosen value. The first message is shown. */
|
|
47
|
+
rules?: readonly Rule<T>[];
|
|
48
|
+
/** When the message shows. Defaults to the form's setting, else `'blur'`. */
|
|
49
|
+
validateOn?: ValidateOn;
|
|
50
|
+
/**
|
|
51
|
+
* A choice must be made. Adds an asterisk to the label, and the placeholder cannot be chosen
|
|
52
|
+
* again once the user has picked something.
|
|
53
|
+
* @default false
|
|
54
|
+
*/
|
|
55
|
+
required?: boolean;
|
|
56
|
+
/** Inline style of the field's root element. */
|
|
57
|
+
style?: string | null;
|
|
58
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|