@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,183 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/** What .dropdown is made of, classified. The props follow the
|
|
3
|
+
families the project registers (variants.ts, ADR 0015).
|
|
4
|
+
|
|
5
|
+
`.dropdown-open` and `.dropdown-fixed` are PARTS: the wrapper writes
|
|
6
|
+
them from its own state, nobody opts into them. */
|
|
7
|
+
export const PREFIX = 'dropdown';
|
|
8
|
+
|
|
9
|
+
export const AXES = {
|
|
10
|
+
size: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
11
|
+
/* Which side of the trigger the box opens on. Below by default. */
|
|
12
|
+
side: ['top', 'bottom', 'left', 'right'],
|
|
13
|
+
/* Where the box lines up along that side. The start by default. */
|
|
14
|
+
align: ['center', 'end']
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
export const FLAGS = {} as const;
|
|
18
|
+
export const PARTS = ['content', 'open', 'fixed'] as const;
|
|
19
|
+
|
|
20
|
+
/** What the trigger snippet receives: spread `props` on your button. */
|
|
21
|
+
export interface DropdownTrigger {
|
|
22
|
+
open: boolean;
|
|
23
|
+
toggle: () => void;
|
|
24
|
+
props: {
|
|
25
|
+
'aria-expanded': boolean;
|
|
26
|
+
'aria-controls': string;
|
|
27
|
+
'aria-haspopup': 'true';
|
|
28
|
+
onclick: () => void;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<script lang="ts">
|
|
34
|
+
/**
|
|
35
|
+
* A trigger and a box that floats next to it. The box is placed in fixed
|
|
36
|
+
* coordinates, so no ancestor's `overflow` crops it, and it flips to the
|
|
37
|
+
* other side when there is no room. It closes on Escape and on a press
|
|
38
|
+
* outside — not on a click inside, which is the content's to decide
|
|
39
|
+
* (`close` is handed to it).
|
|
40
|
+
*/
|
|
41
|
+
import type { Snippet } from 'svelte';
|
|
42
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
43
|
+
import { fly } from 'svelte/transition';
|
|
44
|
+
import { applyAxes, cx, type AxisProps } from '../variants.js';
|
|
45
|
+
|
|
46
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
47
|
+
/** Whether the box is shown. Bindable. */
|
|
48
|
+
open?: boolean;
|
|
49
|
+
/** The element that opens it. Spread `props` on it. */
|
|
50
|
+
trigger: Snippet<[DropdownTrigger]>;
|
|
51
|
+
/** The box's content. `close` closes it. */
|
|
52
|
+
children?: Snippet<[{ close: () => void }]>;
|
|
53
|
+
/** Animate the box. Ignored when the visitor asks for reduced motion. */
|
|
54
|
+
animated?: boolean;
|
|
55
|
+
/** Called after every change of `open`. */
|
|
56
|
+
onToggle?: (open: boolean) => void;
|
|
57
|
+
class?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let {
|
|
61
|
+
open = $bindable(false),
|
|
62
|
+
trigger,
|
|
63
|
+
children,
|
|
64
|
+
animated = true,
|
|
65
|
+
onToggle,
|
|
66
|
+
side,
|
|
67
|
+
align,
|
|
68
|
+
class: klass = '',
|
|
69
|
+
...axes
|
|
70
|
+
}: Props = $props();
|
|
71
|
+
|
|
72
|
+
// `side` and `align` are destructured because the placement reads them;
|
|
73
|
+
// they are handed back to applyAxes so they still write their classes.
|
|
74
|
+
let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, { ...axes, side, align }));
|
|
75
|
+
let classes = $derived(cx(PREFIX, applied.classes, `${PREFIX}-fixed`, open && `${PREFIX}-open`, klass));
|
|
76
|
+
|
|
77
|
+
const uid = $props.id();
|
|
78
|
+
|
|
79
|
+
let root: HTMLDivElement | undefined = $state();
|
|
80
|
+
let content: HTMLDivElement | undefined = $state();
|
|
81
|
+
let coords = $state('');
|
|
82
|
+
|
|
83
|
+
function set(value: boolean) {
|
|
84
|
+
if (value === open) return;
|
|
85
|
+
open = value;
|
|
86
|
+
onToggle?.(value);
|
|
87
|
+
}
|
|
88
|
+
const toggle = () => set(!open);
|
|
89
|
+
const close = () => set(false);
|
|
90
|
+
|
|
91
|
+
/** Fixed coordinates for the box, flipped when the asked side has no room. */
|
|
92
|
+
function place() {
|
|
93
|
+
if (!root || !content) return;
|
|
94
|
+
const t = (root.firstElementChild ?? root).getBoundingClientRect();
|
|
95
|
+
const b = content.getBoundingClientRect();
|
|
96
|
+
const gap = parseFloat(getComputedStyle(root).getPropertyValue('--dropdown-gap')) || 4;
|
|
97
|
+
const vw = document.documentElement.clientWidth;
|
|
98
|
+
const vh = document.documentElement.clientHeight;
|
|
99
|
+
const rtl = getComputedStyle(root).direction === 'rtl';
|
|
100
|
+
|
|
101
|
+
let top: number;
|
|
102
|
+
let left: number;
|
|
103
|
+
if (side === 'left' || side === 'right') {
|
|
104
|
+
const fitsRight = vw - t.right >= b.width + gap;
|
|
105
|
+
const fitsLeft = t.left >= b.width + gap;
|
|
106
|
+
const toRight = side === 'right' ? fitsRight || !fitsLeft : !fitsLeft && fitsRight;
|
|
107
|
+
left = toRight ? t.right + gap : t.left - gap - b.width;
|
|
108
|
+
top = align === 'end' ? t.bottom - b.height : align === 'center' ? t.top + (t.height - b.height) / 2 : t.top;
|
|
109
|
+
} else {
|
|
110
|
+
const below = vh - t.bottom;
|
|
111
|
+
const above = t.top;
|
|
112
|
+
const wantUp = side === 'top';
|
|
113
|
+
const up = wantUp ? above >= b.height + gap || above > below : below < b.height + gap && above > below;
|
|
114
|
+
top = up ? t.top - gap - b.height : t.bottom + gap;
|
|
115
|
+
const start = rtl ? t.right - b.width : t.left;
|
|
116
|
+
const end = rtl ? t.left : t.right - b.width;
|
|
117
|
+
left = align === 'end' ? end : align === 'center' ? t.left + (t.width - b.width) / 2 : start;
|
|
118
|
+
}
|
|
119
|
+
/* Kept inside the viewport, a little off its edges. */
|
|
120
|
+
left = Math.min(Math.max(4, left), Math.max(4, vw - b.width - 4));
|
|
121
|
+
top = Math.min(Math.max(4, top), Math.max(4, vh - b.height - 4));
|
|
122
|
+
coords = `top:${Math.round(top)}px;left:${Math.round(left)}px;`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* While open: placed now, and again on every scroll or resize. Closed by
|
|
126
|
+
Escape and by a press outside the dropdown. */
|
|
127
|
+
$effect(() => {
|
|
128
|
+
if (!open || !content) return;
|
|
129
|
+
place();
|
|
130
|
+
const reposition = () => place();
|
|
131
|
+
const outside = (e: PointerEvent) => {
|
|
132
|
+
if (root && !root.contains(e.target as Node)) close();
|
|
133
|
+
};
|
|
134
|
+
const escape = (e: KeyboardEvent) => {
|
|
135
|
+
if (e.key !== 'Escape') return;
|
|
136
|
+
/* Focus inside: back to the trigger, which the box belonged to. */
|
|
137
|
+
const inside = root?.contains(document.activeElement);
|
|
138
|
+
close();
|
|
139
|
+
if (inside) root?.querySelector<HTMLElement>('[aria-controls]')?.focus();
|
|
140
|
+
};
|
|
141
|
+
const observer = new ResizeObserver(reposition);
|
|
142
|
+
observer.observe(content);
|
|
143
|
+
window.addEventListener('scroll', reposition, true);
|
|
144
|
+
window.addEventListener('resize', reposition);
|
|
145
|
+
document.addEventListener('pointerdown', outside, true);
|
|
146
|
+
document.addEventListener('keydown', escape);
|
|
147
|
+
return () => {
|
|
148
|
+
observer.disconnect();
|
|
149
|
+
window.removeEventListener('scroll', reposition, true);
|
|
150
|
+
window.removeEventListener('resize', reposition);
|
|
151
|
+
document.removeEventListener('pointerdown', outside, true);
|
|
152
|
+
document.removeEventListener('keydown', escape);
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const reduced = () =>
|
|
157
|
+
typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
158
|
+
let duration = $derived(animated && !reduced() ? 120 : 0);
|
|
159
|
+
</script>
|
|
160
|
+
|
|
161
|
+
<div {...applied.rest} bind:this={root} class={classes}>
|
|
162
|
+
{@render trigger({
|
|
163
|
+
open,
|
|
164
|
+
toggle,
|
|
165
|
+
props: {
|
|
166
|
+
'aria-expanded': open,
|
|
167
|
+
'aria-controls': `${uid}-content`,
|
|
168
|
+
'aria-haspopup': 'true',
|
|
169
|
+
onclick: toggle
|
|
170
|
+
}
|
|
171
|
+
})}
|
|
172
|
+
{#if open}
|
|
173
|
+
<div
|
|
174
|
+
bind:this={content}
|
|
175
|
+
id={`${uid}-content`}
|
|
176
|
+
class={`${PREFIX}-content`}
|
|
177
|
+
style={coords}
|
|
178
|
+
transition:fly={{ y: side === 'top' ? 4 : -4, duration }}
|
|
179
|
+
>
|
|
180
|
+
{@render children?.({ close })}
|
|
181
|
+
</div>
|
|
182
|
+
{/if}
|
|
183
|
+
</div>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** What .dropdown is made of, classified. The props follow the
|
|
2
|
+
families the project registers (variants.ts, ADR 0015).
|
|
3
|
+
|
|
4
|
+
`.dropdown-open` and `.dropdown-fixed` are PARTS: the wrapper writes
|
|
5
|
+
them from its own state, nobody opts into them. */
|
|
6
|
+
export declare const PREFIX = "dropdown";
|
|
7
|
+
export declare const AXES: {
|
|
8
|
+
readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
|
|
9
|
+
readonly side: readonly ["top", "bottom", "left", "right"];
|
|
10
|
+
readonly align: readonly ["center", "end"];
|
|
11
|
+
};
|
|
12
|
+
export declare const FLAGS: {};
|
|
13
|
+
export declare const PARTS: readonly ["content", "open", "fixed"];
|
|
14
|
+
/** What the trigger snippet receives: spread `props` on your button. */
|
|
15
|
+
export interface DropdownTrigger {
|
|
16
|
+
open: boolean;
|
|
17
|
+
toggle: () => void;
|
|
18
|
+
props: {
|
|
19
|
+
'aria-expanded': boolean;
|
|
20
|
+
'aria-controls': string;
|
|
21
|
+
'aria-haspopup': 'true';
|
|
22
|
+
onclick: () => void;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A trigger and a box that floats next to it. The box is placed in fixed
|
|
27
|
+
* coordinates, so no ancestor's `overflow` crops it, and it flips to the
|
|
28
|
+
* other side when there is no room. It closes on Escape and on a press
|
|
29
|
+
* outside — not on a click inside, which is the content's to decide
|
|
30
|
+
* (`close` is handed to it).
|
|
31
|
+
*/
|
|
32
|
+
import type { Snippet } from 'svelte';
|
|
33
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
34
|
+
import { type AxisProps } from '../variants.js';
|
|
35
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
36
|
+
/** Whether the box is shown. Bindable. */
|
|
37
|
+
open?: boolean;
|
|
38
|
+
/** The element that opens it. Spread `props` on it. */
|
|
39
|
+
trigger: Snippet<[DropdownTrigger]>;
|
|
40
|
+
/** The box's content. `close` closes it. */
|
|
41
|
+
children?: Snippet<[{
|
|
42
|
+
close: () => void;
|
|
43
|
+
}]>;
|
|
44
|
+
/** Animate the box. Ignored when the visitor asks for reduced motion. */
|
|
45
|
+
animated?: boolean;
|
|
46
|
+
/** Called after every change of `open`. */
|
|
47
|
+
onToggle?: (open: boolean) => void;
|
|
48
|
+
class?: string;
|
|
49
|
+
}
|
|
50
|
+
declare const Dropdown: import("svelte").Component<Props, {}, "open">;
|
|
51
|
+
type Dropdown = ReturnType<typeof Dropdown>;
|
|
52
|
+
export default Dropdown;
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/** What .editable is made of, classified. The props follow the
|
|
3
|
+
families the project registers (variants.ts, ADR 0015).
|
|
4
|
+
|
|
5
|
+
The editable draws the value at rest and the frame around its editor:
|
|
6
|
+
`.editable-display`, `.editable-empty` and `.editable-form` are PARTS.
|
|
7
|
+
The editor itself is another component (Input, NumberInput…). */
|
|
8
|
+
export const PREFIX = 'editable';
|
|
9
|
+
|
|
10
|
+
export const AXES = {
|
|
11
|
+
size: ['xs', 'sm', 'md', 'lg', 'xl']
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
export const FLAGS = {} as const;
|
|
15
|
+
export const PARTS = ['display', 'empty', 'form', 'list'] as const;
|
|
16
|
+
|
|
17
|
+
/** The editors the component draws by itself, by the shape of the value. */
|
|
18
|
+
/** What opens the editor at rest, by itself. Enter covers Space too. */
|
|
19
|
+
export type EditableTrigger = 'click' | 'enter' | 'f2';
|
|
20
|
+
|
|
21
|
+
export type EditableKind = 'text' | 'number' | 'boolean' | 'text[]' | 'number[]' | 'boolean[]';
|
|
22
|
+
|
|
23
|
+
/** What the `edit` snippet is handed. */
|
|
24
|
+
export interface EditableControl<V> {
|
|
25
|
+
/** The value being edited — not yet the component's. */
|
|
26
|
+
readonly value: V;
|
|
27
|
+
/** Change the value being edited. */
|
|
28
|
+
set: (value: V) => void;
|
|
29
|
+
/** Keep the edit: `onChange` is called if the value changed. */
|
|
30
|
+
commit: () => void;
|
|
31
|
+
/** Drop the edit. */
|
|
32
|
+
cancel: () => void;
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<script lang="ts" generics="V">
|
|
37
|
+
/**
|
|
38
|
+
* A value shown as it is, that turns into its editor when asked.
|
|
39
|
+
*
|
|
40
|
+
* At rest the value is drawn by `display` — or as text — inside a button:
|
|
41
|
+
* a click, Enter or F2 opens the editor. The editor is the `edit` snippet,
|
|
42
|
+
* handed an `EditableControl`; or, for a plain value, the one `kind` names
|
|
43
|
+
* (`text`, `number`, `boolean` and their lists). Enter keeps the edit
|
|
44
|
+
* (Ctrl + Enter in a multi-line field), Escape drops it, and leaving the
|
|
45
|
+
* editor keeps it. `onChange` is only called when the value changed.
|
|
46
|
+
*
|
|
47
|
+
* Each of those is a default, not a rule. `editing` is bindable: read it,
|
|
48
|
+
* or set it to open and close the editor from anywhere — a pencil button,
|
|
49
|
+
* a row's "Edit", a whole form switching mode. `openOn` picks which of
|
|
50
|
+
* click, Enter and F2 still open it by themselves (none: the value at rest
|
|
51
|
+
* is plain text, not a button), and `commitOnBlur` whether leaving the
|
|
52
|
+
* editor keeps the edit. Closing — by `editing = false` or `commit()` —
|
|
53
|
+
* keeps the edit; only Escape and `cancel()` drop it.
|
|
54
|
+
*
|
|
55
|
+
* The component keeps no value of its own: `value` is what is shown, and
|
|
56
|
+
* it is the owner's to update in `onChange` — a DataHandler row, a form.
|
|
57
|
+
*/
|
|
58
|
+
import { tick, untrack, type Snippet } from 'svelte';
|
|
59
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
60
|
+
import { applyAxes, cx, type AxisProps } from '../variants.js';
|
|
61
|
+
import Input from './Input.svelte';
|
|
62
|
+
import NumberInput from './NumberInput.svelte';
|
|
63
|
+
import Toggle from './Toggle.svelte';
|
|
64
|
+
import Checkbox from './Checkbox.svelte';
|
|
65
|
+
import ChipsInput from './ChipsInput.svelte';
|
|
66
|
+
|
|
67
|
+
interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
|
|
68
|
+
/** What is shown, and what an edit starts from. */
|
|
69
|
+
value: V;
|
|
70
|
+
/** After an edit that changed the value. */
|
|
71
|
+
onChange?: (value: V) => void;
|
|
72
|
+
/** The editor drawn when there is no `edit` snippet. Default `text`. */
|
|
73
|
+
kind?: EditableKind;
|
|
74
|
+
/** How the value is drawn at rest. Default: as text. */
|
|
75
|
+
display?: Snippet<[V]>;
|
|
76
|
+
/** The editor. Default: the one `kind` names. */
|
|
77
|
+
edit?: Snippet<[EditableControl<V>]>;
|
|
78
|
+
/** Shown at rest when the value is empty. */
|
|
79
|
+
placeholder?: string;
|
|
80
|
+
/** Never opens the editor. */
|
|
81
|
+
disabled?: boolean;
|
|
82
|
+
/** Whether the editor is open. Bindable: set it to open or close it from outside. */
|
|
83
|
+
editing?: boolean;
|
|
84
|
+
/** What opens the editor by itself. Default all three; `[]` leaves it to `editing` alone. */
|
|
85
|
+
openOn?: EditableTrigger[];
|
|
86
|
+
/** Leaving the editor keeps the edit. Default yes; without, the editor stays open until closed. */
|
|
87
|
+
commitOnBlur?: boolean;
|
|
88
|
+
/** Said after the value by a screen reader, on the button that opens the editor. Default `Edit`. */
|
|
89
|
+
editLabel?: string;
|
|
90
|
+
/** The text of a boolean at rest. */
|
|
91
|
+
trueLabel?: string;
|
|
92
|
+
falseLabel?: string;
|
|
93
|
+
class?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let {
|
|
97
|
+
value,
|
|
98
|
+
onChange,
|
|
99
|
+
kind = 'text',
|
|
100
|
+
display,
|
|
101
|
+
edit,
|
|
102
|
+
placeholder = '—',
|
|
103
|
+
disabled = false,
|
|
104
|
+
editing = $bindable(false),
|
|
105
|
+
openOn = ['click', 'enter', 'f2'],
|
|
106
|
+
commitOnBlur = true,
|
|
107
|
+
editLabel = 'Edit',
|
|
108
|
+
trueLabel = 'Yes',
|
|
109
|
+
falseLabel = 'No',
|
|
110
|
+
class: klass = '',
|
|
111
|
+
...axes
|
|
112
|
+
}: Props = $props();
|
|
113
|
+
|
|
114
|
+
let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
|
|
115
|
+
let classes = $derived(cx(PREFIX, applied.classes, klass));
|
|
116
|
+
|
|
117
|
+
let draft = $state<V>() as V;
|
|
118
|
+
let form = $state<HTMLElement>();
|
|
119
|
+
let button = $state<HTMLButtonElement>();
|
|
120
|
+
|
|
121
|
+
const same = (a: unknown, b: unknown) => a === b || JSON.stringify(a) === JSON.stringify(b);
|
|
122
|
+
const copy = (v: V): V => (Array.isArray(v) ? ([...v] as V) : v);
|
|
123
|
+
|
|
124
|
+
/* Every way in or out — a trigger, a method, `editing` set from outside —
|
|
125
|
+
goes through `editing`; what opening and closing do is here, once.
|
|
126
|
+
Opening starts a draft from the value; closing hands the draft on if it
|
|
127
|
+
changed (cancel has put the value back in it first). The focus goes
|
|
128
|
+
back to the value only if it was lost with the editor: not when it
|
|
129
|
+
left for something else. */
|
|
130
|
+
let was = false;
|
|
131
|
+
$effect(() => {
|
|
132
|
+
const now = editing;
|
|
133
|
+
untrack(() => {
|
|
134
|
+
if (now === was) return;
|
|
135
|
+
was = now;
|
|
136
|
+
if (now) {
|
|
137
|
+
draft = copy(value);
|
|
138
|
+
tick().then(focusEditor);
|
|
139
|
+
} else {
|
|
140
|
+
if (!same(draft, value)) onChange?.(draft);
|
|
141
|
+
tick().then(() => {
|
|
142
|
+
if (document.activeElement === document.body) button?.focus();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
function focusEditor() {
|
|
149
|
+
/* A field before anything else: a NumberInput with controls starts with a button. */
|
|
150
|
+
const first =
|
|
151
|
+
form?.querySelector<HTMLElement>('input:not([type=hidden], [type=checkbox], [type=radio]), textarea, select') ??
|
|
152
|
+
form?.querySelector<HTMLElement>('input, button, [tabindex]');
|
|
153
|
+
first?.focus();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Open the editor on the current value. */
|
|
157
|
+
export function open() {
|
|
158
|
+
if (!disabled) editing = true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Keep the edit. */
|
|
162
|
+
export function commit() {
|
|
163
|
+
/* Already closed: the editor leaving the page fires a last focusout. */
|
|
164
|
+
if (!editing) return;
|
|
165
|
+
editing = false;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Drop the edit. */
|
|
169
|
+
export function cancel() {
|
|
170
|
+
if (!editing) return;
|
|
171
|
+
draft = copy(value);
|
|
172
|
+
editing = false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* At rest. Enter and Space would click a button by themselves: they are
|
|
176
|
+
taken here, so that `openOn` can tell a key from a click. */
|
|
177
|
+
let opens = $derived(new Set(openOn));
|
|
178
|
+
function onDisplayKey(e: KeyboardEvent) {
|
|
179
|
+
const key = e.key === 'F2' ? 'f2' : e.key === 'Enter' || e.key === ' ' ? 'enter' : null;
|
|
180
|
+
if (!key) return;
|
|
181
|
+
e.preventDefault();
|
|
182
|
+
if (opens.has(key)) open();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const control: EditableControl<V> = {
|
|
186
|
+
get value() {
|
|
187
|
+
return draft;
|
|
188
|
+
},
|
|
189
|
+
set: (v) => (draft = v),
|
|
190
|
+
commit,
|
|
191
|
+
cancel
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
function onkeydown(e: KeyboardEvent) {
|
|
195
|
+
if (e.key === 'Escape') {
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
cancel();
|
|
198
|
+
} else if (e.key === 'Enter') {
|
|
199
|
+
const multiline = (e.target as HTMLElement).tagName === 'TEXTAREA';
|
|
200
|
+
if (multiline && !(e.ctrlKey || e.metaKey)) return;
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
commit();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* Leaving the editor keeps the edit — unless the focus only moved inside
|
|
207
|
+
it (a chip's remove button, a stepper). Escape and Enter have already
|
|
208
|
+
closed it by then. */
|
|
209
|
+
function onfocusout(e: FocusEvent) {
|
|
210
|
+
const to = e.relatedTarget as Node | null;
|
|
211
|
+
if (!commitOnBlur || (to && form?.contains(to))) return;
|
|
212
|
+
commit();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* The keys and the focus come up from the editor inside: the group only
|
|
216
|
+
listens, it is not a control — so the listeners are attached, not
|
|
217
|
+
written as handlers on a non-interactive element. */
|
|
218
|
+
function listen(node: HTMLElement) {
|
|
219
|
+
node.addEventListener('keydown', onkeydown);
|
|
220
|
+
node.addEventListener('focusout', onfocusout);
|
|
221
|
+
return () => {
|
|
222
|
+
node.removeEventListener('keydown', onkeydown);
|
|
223
|
+
node.removeEventListener('focusout', onfocusout);
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const isEmpty = (v: unknown) => v == null || v === '' || (Array.isArray(v) && v.length === 0);
|
|
228
|
+
const asText = (v: unknown): string =>
|
|
229
|
+
Array.isArray(v) ? v.map(asText).join(', ') : typeof v === 'boolean' ? (v ? trueLabel : falseLabel) : String(v);
|
|
230
|
+
|
|
231
|
+
/* The built-in editors work on the draft through these. */
|
|
232
|
+
const list = () => (Array.isArray(draft) ? (draft as unknown[]) : []);
|
|
233
|
+
function setAt(i: number, v: unknown) {
|
|
234
|
+
const next = [...list()];
|
|
235
|
+
next[i] = v;
|
|
236
|
+
draft = next as V;
|
|
237
|
+
}
|
|
238
|
+
</script>
|
|
239
|
+
|
|
240
|
+
<div {...applied.rest} class={classes}>
|
|
241
|
+
{#if editing}
|
|
242
|
+
<div class={`${PREFIX}-form`} bind:this={form} role="group" {@attach listen}>
|
|
243
|
+
{#if edit}
|
|
244
|
+
{@render edit(control)}
|
|
245
|
+
{:else if kind === 'number'}
|
|
246
|
+
<NumberInput value={draft as number | null} onValue={(v) => (draft = v as V)} />
|
|
247
|
+
{:else if kind === 'boolean'}
|
|
248
|
+
<Toggle checked={!!draft} onchange={(e) => (draft = e.currentTarget.checked as V)} />
|
|
249
|
+
{:else if kind === 'text[]' || kind === 'number[]'}
|
|
250
|
+
<ChipsInput
|
|
251
|
+
values={list().map(String)}
|
|
252
|
+
unique={false}
|
|
253
|
+
onChange={(vs) => (draft = (kind === 'number[]' ? vs.map(Number).filter((n) => !Number.isNaN(n)) : vs) as V)}
|
|
254
|
+
/>
|
|
255
|
+
{:else if kind === 'boolean[]'}
|
|
256
|
+
<span class={`${PREFIX}-list`}>
|
|
257
|
+
{#each list() as b, i (i)}
|
|
258
|
+
<Checkbox checked={!!b} onchange={(e) => setAt(i, e.currentTarget.checked)} />
|
|
259
|
+
{/each}
|
|
260
|
+
</span>
|
|
261
|
+
{:else}
|
|
262
|
+
<Input value={draft == null ? '' : String(draft)} oninput={(e) => (draft = e.currentTarget.value as V)} />
|
|
263
|
+
{/if}
|
|
264
|
+
</div>
|
|
265
|
+
{:else}
|
|
266
|
+
{#snippet shown()}
|
|
267
|
+
{#if isEmpty(value)}
|
|
268
|
+
<span class={`${PREFIX}-empty`}>{placeholder}</span>
|
|
269
|
+
{:else if display}
|
|
270
|
+
{@render display(value)}
|
|
271
|
+
{:else}
|
|
272
|
+
{asText(value)}
|
|
273
|
+
{/if}
|
|
274
|
+
{/snippet}
|
|
275
|
+
{#if opens.size}
|
|
276
|
+
<button
|
|
277
|
+
type="button"
|
|
278
|
+
class={`${PREFIX}-display`}
|
|
279
|
+
bind:this={button}
|
|
280
|
+
{disabled}
|
|
281
|
+
aria-label={`${isEmpty(value) ? placeholder : asText(value)}, ${editLabel}`}
|
|
282
|
+
onclick={() => opens.has('click') && open()}
|
|
283
|
+
onkeydown={onDisplayKey}
|
|
284
|
+
>
|
|
285
|
+
{@render shown()}
|
|
286
|
+
</button>
|
|
287
|
+
{:else}
|
|
288
|
+
<!-- Nothing here opens it: the value is text, not a control. -->
|
|
289
|
+
<span class={`${PREFIX}-display`}>{@render shown()}</span>
|
|
290
|
+
{/if}
|
|
291
|
+
{/if}
|
|
292
|
+
</div>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/** What .editable is made of, classified. The props follow the
|
|
2
|
+
families the project registers (variants.ts, ADR 0015).
|
|
3
|
+
|
|
4
|
+
The editable draws the value at rest and the frame around its editor:
|
|
5
|
+
`.editable-display`, `.editable-empty` and `.editable-form` are PARTS.
|
|
6
|
+
The editor itself is another component (Input, NumberInput…). */
|
|
7
|
+
export declare const PREFIX = "editable";
|
|
8
|
+
export declare const AXES: {
|
|
9
|
+
readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
|
|
10
|
+
};
|
|
11
|
+
export declare const FLAGS: {};
|
|
12
|
+
export declare const PARTS: readonly ["display", "empty", "form", "list"];
|
|
13
|
+
/** The editors the component draws by itself, by the shape of the value. */
|
|
14
|
+
/** What opens the editor at rest, by itself. Enter covers Space too. */
|
|
15
|
+
export type EditableTrigger = 'click' | 'enter' | 'f2';
|
|
16
|
+
export type EditableKind = 'text' | 'number' | 'boolean' | 'text[]' | 'number[]' | 'boolean[]';
|
|
17
|
+
/** What the `edit` snippet is handed. */
|
|
18
|
+
export interface EditableControl<V> {
|
|
19
|
+
/** The value being edited — not yet the component's. */
|
|
20
|
+
readonly value: V;
|
|
21
|
+
/** Change the value being edited. */
|
|
22
|
+
set: (value: V) => void;
|
|
23
|
+
/** Keep the edit: `onChange` is called if the value changed. */
|
|
24
|
+
commit: () => void;
|
|
25
|
+
/** Drop the edit. */
|
|
26
|
+
cancel: () => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A value shown as it is, that turns into its editor when asked.
|
|
30
|
+
*
|
|
31
|
+
* At rest the value is drawn by `display` — or as text — inside a button:
|
|
32
|
+
* a click, Enter or F2 opens the editor. The editor is the `edit` snippet,
|
|
33
|
+
* handed an `EditableControl`; or, for a plain value, the one `kind` names
|
|
34
|
+
* (`text`, `number`, `boolean` and their lists). Enter keeps the edit
|
|
35
|
+
* (Ctrl + Enter in a multi-line field), Escape drops it, and leaving the
|
|
36
|
+
* editor keeps it. `onChange` is only called when the value changed.
|
|
37
|
+
*
|
|
38
|
+
* Each of those is a default, not a rule. `editing` is bindable: read it,
|
|
39
|
+
* or set it to open and close the editor from anywhere — a pencil button,
|
|
40
|
+
* a row's "Edit", a whole form switching mode. `openOn` picks which of
|
|
41
|
+
* click, Enter and F2 still open it by themselves (none: the value at rest
|
|
42
|
+
* is plain text, not a button), and `commitOnBlur` whether leaving the
|
|
43
|
+
* editor keeps the edit. Closing — by `editing = false` or `commit()` —
|
|
44
|
+
* keeps the edit; only Escape and `cancel()` drop it.
|
|
45
|
+
*
|
|
46
|
+
* The component keeps no value of its own: `value` is what is shown, and
|
|
47
|
+
* it is the owner's to update in `onChange` — a DataHandler row, a form.
|
|
48
|
+
*/
|
|
49
|
+
import { type Snippet } from 'svelte';
|
|
50
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
51
|
+
declare function $$render<V>(): {
|
|
52
|
+
props: {
|
|
53
|
+
readonly size?: "xs" | "sm" | "md" | "lg" | "xl" | undefined;
|
|
54
|
+
} & {} & {
|
|
55
|
+
background?: string;
|
|
56
|
+
} & Omit<HTMLAttributes<HTMLDivElement>, "onchange"> & {
|
|
57
|
+
/** What is shown, and what an edit starts from. */
|
|
58
|
+
value: V;
|
|
59
|
+
/** After an edit that changed the value. */
|
|
60
|
+
onChange?: (value: V) => void;
|
|
61
|
+
/** The editor drawn when there is no `edit` snippet. Default `text`. */
|
|
62
|
+
kind?: EditableKind;
|
|
63
|
+
/** How the value is drawn at rest. Default: as text. */
|
|
64
|
+
display?: Snippet<[V]>;
|
|
65
|
+
/** The editor. Default: the one `kind` names. */
|
|
66
|
+
edit?: Snippet<[EditableControl<V>]>;
|
|
67
|
+
/** Shown at rest when the value is empty. */
|
|
68
|
+
placeholder?: string;
|
|
69
|
+
/** Never opens the editor. */
|
|
70
|
+
disabled?: boolean;
|
|
71
|
+
/** Whether the editor is open. Bindable: set it to open or close it from outside. */
|
|
72
|
+
editing?: boolean;
|
|
73
|
+
/** What opens the editor by itself. Default all three; `[]` leaves it to `editing` alone. */
|
|
74
|
+
openOn?: EditableTrigger[];
|
|
75
|
+
/** Leaving the editor keeps the edit. Default yes; without, the editor stays open until closed. */
|
|
76
|
+
commitOnBlur?: boolean;
|
|
77
|
+
/** Said after the value by a screen reader, on the button that opens the editor. Default `Edit`. */
|
|
78
|
+
editLabel?: string;
|
|
79
|
+
/** The text of a boolean at rest. */
|
|
80
|
+
trueLabel?: string;
|
|
81
|
+
falseLabel?: string;
|
|
82
|
+
class?: string;
|
|
83
|
+
};
|
|
84
|
+
exports: {
|
|
85
|
+
open: () => void;
|
|
86
|
+
commit: () => void;
|
|
87
|
+
cancel: () => void;
|
|
88
|
+
};
|
|
89
|
+
bindings: "editing";
|
|
90
|
+
slots: {};
|
|
91
|
+
events: {};
|
|
92
|
+
};
|
|
93
|
+
declare class __sveltets_Render<V> {
|
|
94
|
+
props(): ReturnType<typeof $$render<V>>['props'];
|
|
95
|
+
events(): ReturnType<typeof $$render<V>>['events'];
|
|
96
|
+
slots(): ReturnType<typeof $$render<V>>['slots'];
|
|
97
|
+
bindings(): "editing";
|
|
98
|
+
exports(): {
|
|
99
|
+
open: () => void;
|
|
100
|
+
commit: () => void;
|
|
101
|
+
cancel: () => void;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
interface $$IsomorphicComponent {
|
|
105
|
+
new <V>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<V>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<V>['props']>, ReturnType<__sveltets_Render<V>['events']>, ReturnType<__sveltets_Render<V>['slots']>> & {
|
|
106
|
+
$$bindings?: ReturnType<__sveltets_Render<V>['bindings']>;
|
|
107
|
+
} & ReturnType<__sveltets_Render<V>['exports']>;
|
|
108
|
+
<V>(internal: unknown, props: ReturnType<__sveltets_Render<V>['props']> & {}): ReturnType<__sveltets_Render<V>['exports']>;
|
|
109
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
110
|
+
}
|
|
111
|
+
declare const Editable: $$IsomorphicComponent;
|
|
112
|
+
type Editable<V> = InstanceType<typeof Editable<V>>;
|
|
113
|
+
export default Editable;
|