@llui/components 0.4.10 → 0.5.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/dist/components/accordion.d.ts +13 -13
- package/dist/components/accordion.d.ts.map +1 -1
- package/dist/components/accordion.js +9 -9
- package/dist/components/accordion.js.map +1 -1
- package/dist/components/alert-dialog.d.ts +8 -8
- package/dist/components/alert-dialog.d.ts.map +1 -1
- package/dist/components/alert-dialog.js +2 -2
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/angle-slider.d.ts +13 -13
- package/dist/components/angle-slider.d.ts.map +1 -1
- package/dist/components/angle-slider.js +11 -11
- package/dist/components/angle-slider.js.map +1 -1
- package/dist/components/async-list.d.ts +7 -7
- package/dist/components/async-list.d.ts.map +1 -1
- package/dist/components/async-list.js +5 -8
- package/dist/components/async-list.js.map +1 -1
- package/dist/components/avatar.d.ts +9 -9
- package/dist/components/avatar.d.ts.map +1 -1
- package/dist/components/avatar.js +7 -7
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/carousel.d.ts +18 -18
- package/dist/components/carousel.d.ts.map +1 -1
- package/dist/components/carousel.js +12 -12
- package/dist/components/carousel.js.map +1 -1
- package/dist/components/cascade-select.d.ts +12 -12
- package/dist/components/cascade-select.d.ts.map +1 -1
- package/dist/components/cascade-select.js +8 -8
- package/dist/components/cascade-select.js.map +1 -1
- package/dist/components/checkbox.d.ts +14 -14
- package/dist/components/checkbox.d.ts.map +1 -1
- package/dist/components/checkbox.js +12 -12
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/clipboard.d.ts +8 -8
- package/dist/components/clipboard.d.ts.map +1 -1
- package/dist/components/clipboard.js +6 -6
- package/dist/components/clipboard.js.map +1 -1
- package/dist/components/collapsible.d.ts +11 -11
- package/dist/components/collapsible.d.ts.map +1 -1
- package/dist/components/collapsible.js +9 -9
- package/dist/components/collapsible.js.map +1 -1
- package/dist/components/color-picker.d.ts +19 -19
- package/dist/components/color-picker.d.ts.map +1 -1
- package/dist/components/color-picker.js +21 -21
- package/dist/components/color-picker.js.map +1 -1
- package/dist/components/combobox.d.ts +25 -25
- package/dist/components/combobox.d.ts.map +1 -1
- package/dist/components/combobox.js +54 -59
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/context-menu.d.ts +14 -14
- package/dist/components/context-menu.d.ts.map +1 -1
- package/dist/components/context-menu.js +15 -19
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/date-input.d.ts +13 -13
- package/dist/components/date-input.d.ts.map +1 -1
- package/dist/components/date-input.js +11 -11
- package/dist/components/date-input.js.map +1 -1
- package/dist/components/date-picker.d.ts +11 -11
- package/dist/components/date-picker.d.ts.map +1 -1
- package/dist/components/date-picker.js +7 -7
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/dialog.d.ts +15 -15
- package/dist/components/dialog.d.ts.map +1 -1
- package/dist/components/dialog.js +45 -50
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/drawer.d.ts +13 -13
- package/dist/components/drawer.d.ts.map +1 -1
- package/dist/components/drawer.js +44 -49
- package/dist/components/drawer.js.map +1 -1
- package/dist/components/editable.d.ts +11 -11
- package/dist/components/editable.d.ts.map +1 -1
- package/dist/components/editable.js +9 -9
- package/dist/components/editable.js.map +1 -1
- package/dist/components/file-upload.d.ts +19 -19
- package/dist/components/file-upload.d.ts.map +1 -1
- package/dist/components/file-upload.js +14 -14
- package/dist/components/file-upload.js.map +1 -1
- package/dist/components/floating-panel.d.ts +14 -14
- package/dist/components/floating-panel.d.ts.map +1 -1
- package/dist/components/floating-panel.js +13 -14
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form.d.ts +9 -9
- package/dist/components/form.d.ts.map +1 -1
- package/dist/components/form.js +7 -7
- package/dist/components/form.js.map +1 -1
- package/dist/components/hover-card.d.ts +9 -9
- package/dist/components/hover-card.d.ts.map +1 -1
- package/dist/components/hover-card.js +12 -13
- package/dist/components/hover-card.js.map +1 -1
- package/dist/components/image-cropper.d.ts +8 -8
- package/dist/components/image-cropper.d.ts.map +1 -1
- package/dist/components/image-cropper.js +7 -8
- package/dist/components/image-cropper.js.map +1 -1
- package/dist/components/in-view.d.ts +6 -6
- package/dist/components/in-view.d.ts.map +1 -1
- package/dist/components/in-view.js +2 -2
- package/dist/components/in-view.js.map +1 -1
- package/dist/components/listbox.d.ts +16 -16
- package/dist/components/listbox.d.ts.map +1 -1
- package/dist/components/listbox.js +16 -16
- package/dist/components/listbox.js.map +1 -1
- package/dist/components/marquee.d.ts +8 -8
- package/dist/components/marquee.d.ts.map +1 -1
- package/dist/components/marquee.js +8 -11
- package/dist/components/marquee.js.map +1 -1
- package/dist/components/menu.d.ts +15 -15
- package/dist/components/menu.d.ts.map +1 -1
- package/dist/components/menu.js +16 -17
- package/dist/components/menu.js.map +1 -1
- package/dist/components/navigation-menu.d.ts +12 -12
- package/dist/components/navigation-menu.d.ts.map +1 -1
- package/dist/components/navigation-menu.js +8 -8
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/number-input.d.ts +18 -18
- package/dist/components/number-input.d.ts.map +1 -1
- package/dist/components/number-input.js +16 -20
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/pagination.d.ts +13 -13
- package/dist/components/pagination.d.ts.map +1 -1
- package/dist/components/pagination.js +11 -17
- package/dist/components/pagination.js.map +1 -1
- package/dist/components/password-input.d.ts +11 -11
- package/dist/components/password-input.d.ts.map +1 -1
- package/dist/components/password-input.js +11 -11
- package/dist/components/password-input.js.map +1 -1
- package/dist/components/pin-input.d.ts +9 -9
- package/dist/components/pin-input.d.ts.map +1 -1
- package/dist/components/pin-input.js +9 -9
- package/dist/components/pin-input.js.map +1 -1
- package/dist/components/popover.d.ts +11 -11
- package/dist/components/popover.d.ts.map +1 -1
- package/dist/components/popover.js +61 -60
- package/dist/components/popover.js.map +1 -1
- package/dist/components/presence.d.ts +7 -5
- package/dist/components/presence.d.ts.map +1 -1
- package/dist/components/presence.js +5 -3
- package/dist/components/presence.js.map +1 -1
- package/dist/components/progress.d.ts +14 -14
- package/dist/components/progress.d.ts.map +1 -1
- package/dist/components/progress.js +12 -12
- package/dist/components/progress.js.map +1 -1
- package/dist/components/qr-code.d.ts +7 -7
- package/dist/components/qr-code.d.ts.map +1 -1
- package/dist/components/qr-code.js +7 -7
- package/dist/components/qr-code.js.map +1 -1
- package/dist/components/radio-group.d.ts +15 -15
- package/dist/components/radio-group.d.ts.map +1 -1
- package/dist/components/radio-group.js +12 -13
- package/dist/components/radio-group.js.map +1 -1
- package/dist/components/rating-group.d.ts +13 -13
- package/dist/components/rating-group.d.ts.map +1 -1
- package/dist/components/rating-group.js +10 -11
- package/dist/components/rating-group.js.map +1 -1
- package/dist/components/scroll-area.d.ts +10 -10
- package/dist/components/scroll-area.d.ts.map +1 -1
- package/dist/components/scroll-area.js +10 -15
- package/dist/components/scroll-area.js.map +1 -1
- package/dist/components/select.d.ts +26 -26
- package/dist/components/select.d.ts.map +1 -1
- package/dist/components/select.js +29 -33
- package/dist/components/select.js.map +1 -1
- package/dist/components/signature-pad.d.ts +12 -12
- package/dist/components/signature-pad.d.ts.map +1 -1
- package/dist/components/signature-pad.js +10 -10
- package/dist/components/signature-pad.js.map +1 -1
- package/dist/components/slider.d.ts +22 -22
- package/dist/components/slider.d.ts.map +1 -1
- package/dist/components/slider.js +17 -17
- package/dist/components/slider.js.map +1 -1
- package/dist/components/sortable.d.ts +11 -11
- package/dist/components/sortable.d.ts.map +1 -1
- package/dist/components/sortable.js +20 -20
- package/dist/components/sortable.js.map +1 -1
- package/dist/components/splitter.d.ts +15 -15
- package/dist/components/splitter.d.ts.map +1 -1
- package/dist/components/splitter.js +15 -15
- package/dist/components/splitter.js.map +1 -1
- package/dist/components/steps.d.ts +14 -14
- package/dist/components/steps.d.ts.map +1 -1
- package/dist/components/steps.js +10 -16
- package/dist/components/steps.js.map +1 -1
- package/dist/components/switch.d.ts +12 -12
- package/dist/components/switch.d.ts.map +1 -1
- package/dist/components/switch.js +10 -10
- package/dist/components/switch.js.map +1 -1
- package/dist/components/tabs.d.ts +15 -15
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +11 -11
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/tags-input.d.ts +14 -14
- package/dist/components/tags-input.d.ts.map +1 -1
- package/dist/components/tags-input.js +10 -10
- package/dist/components/tags-input.js.map +1 -1
- package/dist/components/theme-switch.d.ts +5 -5
- package/dist/components/theme-switch.d.ts.map +1 -1
- package/dist/components/theme-switch.js +3 -3
- package/dist/components/theme-switch.js.map +1 -1
- package/dist/components/time-picker.d.ts +19 -19
- package/dist/components/time-picker.d.ts.map +1 -1
- package/dist/components/time-picker.js +17 -17
- package/dist/components/time-picker.js.map +1 -1
- package/dist/components/timer.d.ts +10 -10
- package/dist/components/timer.d.ts.map +1 -1
- package/dist/components/timer.js +8 -8
- package/dist/components/timer.js.map +1 -1
- package/dist/components/toast.d.ts +8 -8
- package/dist/components/toast.d.ts.map +1 -1
- package/dist/components/toast.js +4 -4
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toc.d.ts +12 -12
- package/dist/components/toc.d.ts.map +1 -1
- package/dist/components/toc.js +8 -8
- package/dist/components/toc.js.map +1 -1
- package/dist/components/toggle-group.d.ts +13 -13
- package/dist/components/toggle-group.d.ts.map +1 -1
- package/dist/components/toggle-group.js +9 -9
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle.d.ts +8 -8
- package/dist/components/toggle.d.ts.map +1 -1
- package/dist/components/toggle.js +6 -6
- package/dist/components/toggle.js.map +1 -1
- package/dist/components/tooltip.d.ts +10 -10
- package/dist/components/tooltip.d.ts.map +1 -1
- package/dist/components/tooltip.js +13 -14
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/tour.d.ts +7 -7
- package/dist/components/tour.d.ts.map +1 -1
- package/dist/components/tour.js +5 -5
- package/dist/components/tour.js.map +1 -1
- package/dist/components/tree-view.d.ts +19 -19
- package/dist/components/tree-view.d.ts.map +1 -1
- package/dist/components/tree-view.js +23 -23
- package/dist/components/tree-view.js.map +1 -1
- package/dist/patterns/confirm-dialog.d.ts +4 -4
- package/dist/patterns/confirm-dialog.d.ts.map +1 -1
- package/dist/patterns/confirm-dialog.js +7 -9
- package/dist/patterns/confirm-dialog.js.map +1 -1
- package/package.json +3 -3
- package/dist/components/enter-view.d.ts +0 -73
- package/dist/components/enter-view.d.ts.map +0 -1
- package/dist/components/enter-view.js +0 -51
- package/dist/utils/validators.d.ts +0 -34
- package/dist/utils/validators.d.ts.map +0 -1
- package/dist/utils/validators.js +0 -83
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pin-input.d.ts","sourceRoot":"","sources":["../../src/components/pin-input.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"pin-input.d.ts","sourceRoot":"","sources":["../../src/components/pin-input.ts"],"names":[],"mappings":"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"}
|
|
@@ -70,7 +70,7 @@ export function isComplete(state) {
|
|
|
70
70
|
export function getValue(state) {
|
|
71
71
|
return state.values.join('');
|
|
72
72
|
}
|
|
73
|
-
export function connect(
|
|
73
|
+
export function connect(state, send, opts) {
|
|
74
74
|
const labelId = `${opts.id}:label`;
|
|
75
75
|
const inputLabel = opts.inputLabel ?? en.pinInput.input;
|
|
76
76
|
const validate = opts.validate;
|
|
@@ -80,7 +80,7 @@ export function connect(get, send, opts) {
|
|
|
80
80
|
'aria-labelledby': labelId,
|
|
81
81
|
'data-scope': 'pin-input',
|
|
82
82
|
'data-part': 'root',
|
|
83
|
-
'data-disabled': (s) => (
|
|
83
|
+
'data-disabled': state.map((s) => (s.disabled ? '' : undefined)),
|
|
84
84
|
},
|
|
85
85
|
label: {
|
|
86
86
|
id: labelId,
|
|
@@ -88,10 +88,10 @@ export function connect(get, send, opts) {
|
|
|
88
88
|
'data-part': 'label',
|
|
89
89
|
},
|
|
90
90
|
input: (index) => ({
|
|
91
|
-
type: (s) => (
|
|
92
|
-
inputMode: (s) => (
|
|
93
|
-
pattern: (s) => {
|
|
94
|
-
switch (
|
|
91
|
+
type: state.map((s) => (s.mask ? 'password' : 'text')),
|
|
92
|
+
inputMode: state.map((s) => (s.type === 'numeric' ? 'numeric' : 'text')),
|
|
93
|
+
pattern: state.map((s) => {
|
|
94
|
+
switch (s.type) {
|
|
95
95
|
case 'numeric':
|
|
96
96
|
return '[0-9]*';
|
|
97
97
|
case 'alphabetic':
|
|
@@ -99,12 +99,12 @@ export function connect(get, send, opts) {
|
|
|
99
99
|
case 'alphanumeric':
|
|
100
100
|
return '[a-zA-Z0-9]*';
|
|
101
101
|
}
|
|
102
|
-
},
|
|
102
|
+
}),
|
|
103
103
|
maxLength: 1,
|
|
104
104
|
autoComplete: 'off',
|
|
105
105
|
'aria-label': inputLabel(index),
|
|
106
|
-
disabled: (s) =>
|
|
107
|
-
value: (s) =>
|
|
106
|
+
disabled: state.map((s) => s.disabled),
|
|
107
|
+
value: state.map((s) => s.values[index] ?? ''),
|
|
108
108
|
'data-scope': 'pin-input',
|
|
109
109
|
'data-part': 'input',
|
|
110
110
|
'data-index': String(index),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pin-input.js","sourceRoot":"","sources":["../../src/components/pin-input.ts"],"names":[],"mappings":"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","sourcesContent":["import { tagSend } from '@llui/dom'\nimport type { Send } from '@llui/dom'\nimport { flipArrow } from '../utils/direction.js'\nimport { en } from '../locale.js'\n\n/**\n * Pin input — a sequence of single-character fields for OTP codes, etc.\n * Auto-advances on input, handles backspace to previous field, supports\n * paste-to-fill across multiple fields.\n */\n\nexport type PinType = 'numeric' | 'alphanumeric' | 'alphabetic'\n\nexport interface PinInputState {\n values: string[]\n length: number\n type: PinType\n mask: boolean\n disabled: boolean\n focusedIndex: number\n}\n\nexport type PinInputMsg =\n /** @intent(\"Set the character at a given field index (auto-advances focus on accept)\") */\n | { type: 'setValue'; index: number; value: string }\n /** @intent(\"Replace every field at once (typically from paste)\") */\n | { type: 'setAll'; values: string[] }\n /** @humanOnly */\n | { type: 'focus'; index: number }\n /** @intent(\"Clear every field\") */\n | { type: 'clear' }\n /** @humanOnly */\n | { type: 'backspace'; index: number }\n\nexport interface PinInputInit {\n length?: number\n type?: PinType\n mask?: boolean\n disabled?: boolean\n values?: string[]\n}\n\nexport function init(opts: PinInputInit = {}): PinInputState {\n const length = opts.length ?? 4\n const values = opts.values ?? new Array<string>(length).fill('')\n return {\n values,\n length,\n type: opts.type ?? 'numeric',\n mask: opts.mask ?? false,\n disabled: opts.disabled ?? false,\n focusedIndex: 0,\n }\n}\n\nfunction sanitize(char: string, type: PinType): string {\n if (char.length !== 1) return ''\n if (type === 'numeric' && !/\\d/.test(char)) return ''\n if (type === 'alphabetic' && !/[a-zA-Z]/.test(char)) return ''\n if (type === 'alphanumeric' && !/[a-zA-Z0-9]/.test(char)) return ''\n return char\n}\n\nexport function update(state: PinInputState, msg: PinInputMsg): [PinInputState, never[]] {\n if (state.disabled) return [state, []]\n switch (msg.type) {\n case 'setValue': {\n const char = sanitize(msg.value.slice(-1), state.type)\n if (!char && msg.value !== '') return [state, []]\n const values = [...state.values]\n values[msg.index] = char\n // Auto-advance\n const nextIndex = char ? Math.min(msg.index + 1, state.length - 1) : msg.index\n return [{ ...state, values, focusedIndex: nextIndex }, []]\n }\n case 'setAll': {\n const values = new Array<string>(state.length).fill('')\n for (let i = 0; i < Math.min(msg.values.length, state.length); i++) {\n values[i] = sanitize(msg.values[i]!, state.type)\n }\n const lastFilled = values.findIndex((v) => v === '')\n const focusedIndex = lastFilled === -1 ? state.length - 1 : lastFilled\n return [{ ...state, values, focusedIndex }, []]\n }\n case 'focus':\n return [{ ...state, focusedIndex: Math.max(0, Math.min(msg.index, state.length - 1)) }, []]\n case 'clear':\n return [{ ...state, values: new Array<string>(state.length).fill(''), focusedIndex: 0 }, []]\n case 'backspace': {\n const values = [...state.values]\n if (values[msg.index]) {\n values[msg.index] = ''\n return [{ ...state, values }, []]\n }\n // Field is empty — move focus back and clear prior\n const prev = Math.max(0, msg.index - 1)\n values[prev] = ''\n return [{ ...state, values, focusedIndex: prev }, []]\n }\n }\n}\n\nexport function isComplete(state: PinInputState): boolean {\n return state.values.every((v) => v !== '')\n}\n\nexport function getValue(state: PinInputState): string {\n return state.values.join('')\n}\n\nexport interface PinInputParts<S> {\n root: {\n role: 'group'\n 'aria-labelledby': string\n 'data-scope': 'pin-input'\n 'data-part': 'root'\n 'data-disabled': (s: S) => '' | undefined\n }\n label: {\n id: string\n 'data-scope': 'pin-input'\n 'data-part': 'label'\n }\n /** Props for the input at a given index. */\n input: (index: number) => {\n type: (s: S) => 'text' | 'password'\n inputMode: (s: S) => 'numeric' | 'text'\n pattern: (s: S) => string\n maxLength: 1\n autoComplete: 'off'\n 'aria-label': string\n disabled: (s: S) => boolean\n value: (s: S) => string\n 'data-scope': 'pin-input'\n 'data-part': 'input'\n 'data-index': string\n onInput: (e: Event) => void\n onKeyDown: (e: KeyboardEvent) => void\n onFocus: (e: FocusEvent) => void\n onPaste: (e: ClipboardEvent) => void\n }\n}\n\nexport interface ConnectOptions {\n id: string\n inputLabel?: (index: number) => string\n /** Validate each character before setting. Non-empty array blocks setDigit. */\n validate?: (value: string) => string[] | null\n}\n\nexport function connect<S>(\n get: (s: S) => PinInputState,\n send: Send<PinInputMsg>,\n opts: ConnectOptions,\n): PinInputParts<S> {\n const labelId = `${opts.id}:label`\n const inputLabel = opts.inputLabel ?? en.pinInput.input\n const validate = opts.validate\n\n return {\n root: {\n role: 'group',\n 'aria-labelledby': labelId,\n 'data-scope': 'pin-input',\n 'data-part': 'root',\n 'data-disabled': (s) => (get(s).disabled ? '' : undefined),\n },\n label: {\n id: labelId,\n 'data-scope': 'pin-input',\n 'data-part': 'label',\n },\n input: (index: number) => ({\n type: (s) => (get(s).mask ? 'password' : 'text'),\n inputMode: (s) => (get(s).type === 'numeric' ? 'numeric' : 'text'),\n pattern: (s) => {\n switch (get(s).type) {\n case 'numeric':\n return '[0-9]*'\n case 'alphabetic':\n return '[a-zA-Z]*'\n case 'alphanumeric':\n return '[a-zA-Z0-9]*'\n }\n },\n maxLength: 1,\n autoComplete: 'off',\n 'aria-label': inputLabel(index),\n disabled: (s) => get(s).disabled,\n value: (s) => get(s).values[index] ?? '',\n 'data-scope': 'pin-input',\n 'data-part': 'input',\n 'data-index': String(index),\n onInput: tagSend(send, ['setValue'], (e) => {\n const value = (e.target as HTMLInputElement).value\n if (validate && value !== '') {\n const errors = validate(value.slice(-1))\n if (errors && errors.length > 0) return\n }\n send({ type: 'setValue', index, value })\n }),\n onKeyDown: tagSend(send, ['backspace', 'focus'], (e) => {\n const key = flipArrow(e.key, e.currentTarget as Element)\n if (key === 'Backspace') {\n send({ type: 'backspace', index })\n } else if (key === 'ArrowLeft') {\n e.preventDefault()\n send({ type: 'focus', index: index - 1 })\n } else if (key === 'ArrowRight') {\n e.preventDefault()\n send({ type: 'focus', index: index + 1 })\n }\n }),\n onFocus: tagSend(send, ['focus'], () => send({ type: 'focus', index })),\n onPaste: tagSend(send, ['setAll'], (e) => {\n e.preventDefault()\n const text = e.clipboardData?.getData('text') ?? ''\n send({ type: 'setAll', values: text.split('') })\n }),\n }),\n }\n}\n\nexport const pinInput = { init, update, connect, isComplete, getValue }\n"]}
|
|
1
|
+
{"version":3,"file":"pin-input.js","sourceRoot":"","sources":["../../src/components/pin-input.ts"],"names":[],"mappings":"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","sourcesContent":["import { tagSend } from '@llui/dom'\nimport type { Send, Signal } from '@llui/dom'\nimport { flipArrow } from '../utils/direction.js'\nimport { en } from '../locale.js'\n\n/**\n * Pin input — a sequence of single-character fields for OTP codes, etc.\n * Auto-advances on input, handles backspace to previous field, supports\n * paste-to-fill across multiple fields.\n */\n\nexport type PinType = 'numeric' | 'alphanumeric' | 'alphabetic'\n\nexport interface PinInputState {\n values: string[]\n length: number\n type: PinType\n mask: boolean\n disabled: boolean\n focusedIndex: number\n}\n\nexport type PinInputMsg =\n /** @intent(\"Set the character at a given field index (auto-advances focus on accept)\") */\n | { type: 'setValue'; index: number; value: string }\n /** @intent(\"Replace every field at once (typically from paste)\") */\n | { type: 'setAll'; values: string[] }\n /** @humanOnly */\n | { type: 'focus'; index: number }\n /** @intent(\"Clear every field\") */\n | { type: 'clear' }\n /** @humanOnly */\n | { type: 'backspace'; index: number }\n\nexport interface PinInputInit {\n length?: number\n type?: PinType\n mask?: boolean\n disabled?: boolean\n values?: string[]\n}\n\nexport function init(opts: PinInputInit = {}): PinInputState {\n const length = opts.length ?? 4\n const values = opts.values ?? new Array<string>(length).fill('')\n return {\n values,\n length,\n type: opts.type ?? 'numeric',\n mask: opts.mask ?? false,\n disabled: opts.disabled ?? false,\n focusedIndex: 0,\n }\n}\n\nfunction sanitize(char: string, type: PinType): string {\n if (char.length !== 1) return ''\n if (type === 'numeric' && !/\\d/.test(char)) return ''\n if (type === 'alphabetic' && !/[a-zA-Z]/.test(char)) return ''\n if (type === 'alphanumeric' && !/[a-zA-Z0-9]/.test(char)) return ''\n return char\n}\n\nexport function update(state: PinInputState, msg: PinInputMsg): [PinInputState, never[]] {\n if (state.disabled) return [state, []]\n switch (msg.type) {\n case 'setValue': {\n const char = sanitize(msg.value.slice(-1), state.type)\n if (!char && msg.value !== '') return [state, []]\n const values = [...state.values]\n values[msg.index] = char\n // Auto-advance\n const nextIndex = char ? Math.min(msg.index + 1, state.length - 1) : msg.index\n return [{ ...state, values, focusedIndex: nextIndex }, []]\n }\n case 'setAll': {\n const values = new Array<string>(state.length).fill('')\n for (let i = 0; i < Math.min(msg.values.length, state.length); i++) {\n values[i] = sanitize(msg.values[i]!, state.type)\n }\n const lastFilled = values.findIndex((v) => v === '')\n const focusedIndex = lastFilled === -1 ? state.length - 1 : lastFilled\n return [{ ...state, values, focusedIndex }, []]\n }\n case 'focus':\n return [{ ...state, focusedIndex: Math.max(0, Math.min(msg.index, state.length - 1)) }, []]\n case 'clear':\n return [{ ...state, values: new Array<string>(state.length).fill(''), focusedIndex: 0 }, []]\n case 'backspace': {\n const values = [...state.values]\n if (values[msg.index]) {\n values[msg.index] = ''\n return [{ ...state, values }, []]\n }\n // Field is empty — move focus back and clear prior\n const prev = Math.max(0, msg.index - 1)\n values[prev] = ''\n return [{ ...state, values, focusedIndex: prev }, []]\n }\n }\n}\n\nexport function isComplete(state: PinInputState): boolean {\n return state.values.every((v) => v !== '')\n}\n\nexport function getValue(state: PinInputState): string {\n return state.values.join('')\n}\n\nexport interface PinInputParts {\n root: {\n role: 'group'\n 'aria-labelledby': string\n 'data-scope': 'pin-input'\n 'data-part': 'root'\n 'data-disabled': Signal<'' | undefined>\n }\n label: {\n id: string\n 'data-scope': 'pin-input'\n 'data-part': 'label'\n }\n /** Props for the input at a given index. */\n input: (index: number) => {\n type: Signal<'text' | 'password'>\n inputMode: Signal<'numeric' | 'text'>\n pattern: Signal<string>\n maxLength: 1\n autoComplete: 'off'\n 'aria-label': string\n disabled: Signal<boolean>\n value: Signal<string>\n 'data-scope': 'pin-input'\n 'data-part': 'input'\n 'data-index': string\n onInput: (e: Event) => void\n onKeyDown: (e: KeyboardEvent) => void\n onFocus: (e: FocusEvent) => void\n onPaste: (e: ClipboardEvent) => void\n }\n}\n\nexport interface ConnectOptions {\n id: string\n inputLabel?: (index: number) => string\n /** Validate each character before setting. Non-empty array blocks setDigit. */\n validate?: (value: string) => string[] | null\n}\n\nexport function connect(\n state: Signal<PinInputState>,\n send: Send<PinInputMsg>,\n opts: ConnectOptions,\n): PinInputParts {\n const labelId = `${opts.id}:label`\n const inputLabel = opts.inputLabel ?? en.pinInput.input\n const validate = opts.validate\n\n return {\n root: {\n role: 'group',\n 'aria-labelledby': labelId,\n 'data-scope': 'pin-input',\n 'data-part': 'root',\n 'data-disabled': state.map((s) => (s.disabled ? '' : undefined)),\n },\n label: {\n id: labelId,\n 'data-scope': 'pin-input',\n 'data-part': 'label',\n },\n input: (index: number) => ({\n type: state.map((s) => (s.mask ? 'password' : 'text')),\n inputMode: state.map((s) => (s.type === 'numeric' ? 'numeric' : 'text')),\n pattern: state.map((s) => {\n switch (s.type) {\n case 'numeric':\n return '[0-9]*'\n case 'alphabetic':\n return '[a-zA-Z]*'\n case 'alphanumeric':\n return '[a-zA-Z0-9]*'\n }\n }),\n maxLength: 1,\n autoComplete: 'off',\n 'aria-label': inputLabel(index),\n disabled: state.map((s) => s.disabled),\n value: state.map((s) => s.values[index] ?? ''),\n 'data-scope': 'pin-input',\n 'data-part': 'input',\n 'data-index': String(index),\n onInput: tagSend(send, ['setValue'], (e) => {\n const value = (e.target as HTMLInputElement).value\n if (validate && value !== '') {\n const errors = validate(value.slice(-1))\n if (errors && errors.length > 0) return\n }\n send({ type: 'setValue', index, value })\n }),\n onKeyDown: tagSend(send, ['backspace', 'focus'], (e) => {\n const key = flipArrow(e.key, e.currentTarget as Element)\n if (key === 'Backspace') {\n send({ type: 'backspace', index })\n } else if (key === 'ArrowLeft') {\n e.preventDefault()\n send({ type: 'focus', index: index - 1 })\n } else if (key === 'ArrowRight') {\n e.preventDefault()\n send({ type: 'focus', index: index + 1 })\n }\n }),\n onFocus: tagSend(send, ['focus'], () => send({ type: 'focus', index })),\n onPaste: tagSend(send, ['setAll'], (e) => {\n e.preventDefault()\n const text = e.clipboardData?.getData('text') ?? ''\n send({ type: 'setAll', values: text.split('') })\n }),\n }),\n }\n}\n\nexport const pinInput = { init, update, connect, isComplete, getValue }\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Send, TransitionOptions } from '@llui/dom';
|
|
1
|
+
import type { Send, Signal, TransitionOptions } from '@llui/dom';
|
|
2
2
|
import { type Placement } from '../utils/floating.js';
|
|
3
3
|
/**
|
|
4
4
|
* Popover — click-triggered, non-modal floating overlay anchored to its
|
|
@@ -33,14 +33,14 @@ export interface PopoverInit {
|
|
|
33
33
|
}
|
|
34
34
|
export declare function init(opts?: PopoverInit): PopoverState;
|
|
35
35
|
export declare function update(state: PopoverState, msg: PopoverMsg): [PopoverState, never[]];
|
|
36
|
-
export interface PopoverParts
|
|
36
|
+
export interface PopoverParts {
|
|
37
37
|
trigger: {
|
|
38
38
|
type: 'button';
|
|
39
39
|
'aria-haspopup': 'dialog';
|
|
40
|
-
'aria-expanded':
|
|
40
|
+
'aria-expanded': Signal<boolean>;
|
|
41
41
|
'aria-controls': string;
|
|
42
42
|
id: string;
|
|
43
|
-
'data-state':
|
|
43
|
+
'data-state': Signal<'open' | 'closed'>;
|
|
44
44
|
'data-scope': 'popover';
|
|
45
45
|
'data-part': 'trigger';
|
|
46
46
|
onClick: (e: MouseEvent) => void;
|
|
@@ -55,7 +55,7 @@ export interface PopoverParts<S> {
|
|
|
55
55
|
id: string;
|
|
56
56
|
'aria-labelledby': string;
|
|
57
57
|
tabIndex: -1;
|
|
58
|
-
'data-state':
|
|
58
|
+
'data-state': Signal<'open' | 'closed'>;
|
|
59
59
|
'data-scope': 'popover';
|
|
60
60
|
'data-part': 'content';
|
|
61
61
|
};
|
|
@@ -75,7 +75,7 @@ export interface PopoverParts<S> {
|
|
|
75
75
|
};
|
|
76
76
|
closeTrigger: {
|
|
77
77
|
type: 'button';
|
|
78
|
-
'aria-label': string
|
|
78
|
+
'aria-label': string;
|
|
79
79
|
'data-scope': 'popover';
|
|
80
80
|
'data-part': 'close-trigger';
|
|
81
81
|
onClick: (e: MouseEvent) => void;
|
|
@@ -85,11 +85,11 @@ export interface ConnectOptions {
|
|
|
85
85
|
id: string;
|
|
86
86
|
closeLabel?: string;
|
|
87
87
|
}
|
|
88
|
-
export declare function connect
|
|
89
|
-
export interface OverlayOptions
|
|
90
|
-
|
|
88
|
+
export declare function connect(state: Signal<PopoverState>, send: Send<PopoverMsg>, opts: ConnectOptions): PopoverParts;
|
|
89
|
+
export interface OverlayOptions {
|
|
90
|
+
state: Signal<PopoverState>;
|
|
91
91
|
send: Send<PopoverMsg>;
|
|
92
|
-
parts: PopoverParts
|
|
92
|
+
parts: PopoverParts;
|
|
93
93
|
content: () => Node[];
|
|
94
94
|
/** Placement preference — bottom | top | right | left with -start/-end variants. */
|
|
95
95
|
placement?: Placement;
|
|
@@ -114,7 +114,7 @@ export interface OverlayOptions<S> {
|
|
|
114
114
|
/** Arrow element selector within content (optional). */
|
|
115
115
|
arrowSelector?: string;
|
|
116
116
|
}
|
|
117
|
-
export declare function overlay
|
|
117
|
+
export declare function overlay(opts: OverlayOptions): Node[];
|
|
118
118
|
export declare const popover: {
|
|
119
119
|
init: typeof init;
|
|
120
120
|
update: typeof update;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../../src/components/popover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../../src/components/popover.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,22 +18,22 @@ export function update(state, msg) {
|
|
|
18
18
|
return [{ ...state, open: msg.open }, []];
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
export function connect(
|
|
21
|
+
export function connect(state, send, opts) {
|
|
22
22
|
const locale = useContext(LocaleContext);
|
|
23
23
|
const base = opts.id;
|
|
24
24
|
const triggerId = `${base}:trigger`;
|
|
25
25
|
const contentId = `${base}:content`;
|
|
26
26
|
const titleId = `${base}:title`;
|
|
27
27
|
const descId = `${base}:description`;
|
|
28
|
-
const closeLabel = opts.closeLabel ??
|
|
28
|
+
const closeLabel = opts.closeLabel ?? locale.popover.close;
|
|
29
29
|
return {
|
|
30
30
|
trigger: {
|
|
31
31
|
type: 'button',
|
|
32
32
|
'aria-haspopup': 'dialog',
|
|
33
|
-
'aria-expanded': (
|
|
33
|
+
'aria-expanded': state.map((st) => st.open),
|
|
34
34
|
'aria-controls': contentId,
|
|
35
35
|
id: triggerId,
|
|
36
|
-
'data-state': (
|
|
36
|
+
'data-state': state.map((st) => (st.open ? 'open' : 'closed')),
|
|
37
37
|
'data-scope': 'popover',
|
|
38
38
|
'data-part': 'trigger',
|
|
39
39
|
onClick: tagSend(send, ['toggle'], () => send({ type: 'toggle' })),
|
|
@@ -48,7 +48,7 @@ export function connect(get, send, opts) {
|
|
|
48
48
|
id: contentId,
|
|
49
49
|
'aria-labelledby': titleId,
|
|
50
50
|
tabIndex: -1,
|
|
51
|
-
'data-state': (
|
|
51
|
+
'data-state': state.map((st) => (st.open ? 'open' : 'closed')),
|
|
52
52
|
'data-scope': 'popover',
|
|
53
53
|
'data-part': 'content',
|
|
54
54
|
},
|
|
@@ -76,7 +76,7 @@ export function connect(get, send, opts) {
|
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
export function overlay(opts) {
|
|
79
|
-
const
|
|
79
|
+
const rawTarget = opts.target ?? 'body';
|
|
80
80
|
const placement = opts.placement ?? 'bottom';
|
|
81
81
|
const offset = opts.offset ?? 8;
|
|
82
82
|
const flip = opts.flip !== false;
|
|
@@ -88,62 +88,63 @@ export function overlay(opts) {
|
|
|
88
88
|
const parts = opts.parts;
|
|
89
89
|
const contentId = parts.content.id;
|
|
90
90
|
const triggerId = parts.trigger.id;
|
|
91
|
-
return
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
cleanups.push(pushFocusTrap({
|
|
119
|
-
container: contentEl,
|
|
120
|
-
restoreFocus,
|
|
91
|
+
return [
|
|
92
|
+
show(opts.state.map((st) => st.open), () => {
|
|
93
|
+
const targetEl = typeof rawTarget === 'string'
|
|
94
|
+
? (document.querySelector(rawTarget) ?? document.body)
|
|
95
|
+
: rawTarget;
|
|
96
|
+
return [
|
|
97
|
+
portal(() => {
|
|
98
|
+
onMount(() => {
|
|
99
|
+
const contentEl = document.getElementById(contentId);
|
|
100
|
+
const triggerEl = document.getElementById(triggerId);
|
|
101
|
+
if (!contentEl || !triggerEl)
|
|
102
|
+
return;
|
|
103
|
+
const cleanups = [];
|
|
104
|
+
// Position content relative to trigger
|
|
105
|
+
const positioner = contentEl.closest('[data-part="positioner"]');
|
|
106
|
+
const floatingEl = positioner ?? contentEl;
|
|
107
|
+
const arrow = opts.arrowSelector
|
|
108
|
+
? contentEl.querySelector(opts.arrowSelector)
|
|
109
|
+
: null;
|
|
110
|
+
cleanups.push(attachFloating({
|
|
111
|
+
anchor: triggerEl,
|
|
112
|
+
floating: floatingEl,
|
|
113
|
+
placement,
|
|
114
|
+
offset,
|
|
115
|
+
flip,
|
|
116
|
+
shift,
|
|
117
|
+
arrow: arrow ?? undefined,
|
|
121
118
|
}));
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
119
|
+
if (trapFocus) {
|
|
120
|
+
cleanups.push(pushFocusTrap({
|
|
121
|
+
container: contentEl,
|
|
122
|
+
restoreFocus,
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
if (closeOnEscape || closeOnOutsideClick) {
|
|
126
|
+
cleanups.push(pushDismissable({
|
|
127
|
+
element: contentEl,
|
|
128
|
+
ignore: () => [triggerEl],
|
|
129
|
+
disableEscape: !closeOnEscape,
|
|
130
|
+
disableOutside: !closeOnOutsideClick,
|
|
131
|
+
onDismiss: () => {
|
|
132
|
+
opts.send({ type: 'close' });
|
|
133
|
+
if (restoreFocus)
|
|
134
|
+
triggerEl.focus();
|
|
135
|
+
},
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
return () => {
|
|
139
|
+
for (let i = cleanups.length - 1; i >= 0; i--)
|
|
140
|
+
cleanups[i]();
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
return [div(parts.positioner, opts.content())];
|
|
144
|
+
}, targetEl),
|
|
145
|
+
];
|
|
143
146
|
}),
|
|
144
|
-
|
|
145
|
-
leave: opts.transition?.leave,
|
|
146
|
-
});
|
|
147
|
+
];
|
|
147
148
|
}
|
|
148
149
|
export const popover = { init, update, connect, overlay };
|
|
149
150
|
//# sourceMappingURL=popover.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.js","sourceRoot":"","sources":["../../src/components/popover.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send, TransitionOptions } from '@llui/dom'\nimport { show, portal, onMount, div, useContext, tagSend } from '@llui/dom'\nimport { LocaleContext } from '../locale.js'\nimport type { Locale } from '../locale.js'\nimport { pushDismissable } from '../utils/dismissable.js'\nimport { pushFocusTrap } from '../utils/focus-trap.js'\nimport { attachFloating, type Placement } from '../utils/floating.js'\n\n/**\n * Popover — click-triggered, non-modal floating overlay anchored to its\n * trigger. Use for menus, date pickers, color pickers, filters, etc.\n *\n * Like dialog, has a pure state machine + a view helper (`overlay()`) that\n * wires floating-ui positioning, dismissable, and optional focus trapping.\n */\n\nexport interface PopoverState {\n open: boolean\n}\n\nexport type PopoverMsg =\n /** @intent(\"Open the popover\") */\n | { type: 'open' }\n /** @intent(\"Close the popover\") */\n | { type: 'close' }\n /** @intent(\"Toggle the popover open/closed\") */\n | { type: 'toggle' }\n /** @intent(\"Set the popover's open state to a specific value\") */\n | { type: 'setOpen'; open: boolean }\n\nexport interface PopoverInit {\n open?: boolean\n}\n\nexport function init(opts: PopoverInit = {}): PopoverState {\n return { open: opts.open ?? false }\n}\n\nexport function update(state: PopoverState, msg: PopoverMsg): [PopoverState, never[]] {\n switch (msg.type) {\n case 'open':\n return [{ ...state, open: true }, []]\n case 'close':\n return [{ ...state, open: false }, []]\n case 'toggle':\n return [{ ...state, open: !state.open }, []]\n case 'setOpen':\n return [{ ...state, open: msg.open }, []]\n }\n}\n\nexport interface PopoverParts<S> {\n trigger: {\n type: 'button'\n 'aria-haspopup': 'dialog'\n 'aria-expanded': (s: S) => boolean\n 'aria-controls': string\n id: string\n 'data-state': (s: S) => 'open' | 'closed'\n 'data-scope': 'popover'\n 'data-part': 'trigger'\n onClick: (e: MouseEvent) => void\n }\n positioner: {\n 'data-scope': 'popover'\n 'data-part': 'positioner'\n style: string\n }\n content: {\n role: 'dialog'\n id: string\n 'aria-labelledby': string\n tabIndex: -1\n 'data-state': (s: S) => 'open' | 'closed'\n 'data-scope': 'popover'\n 'data-part': 'content'\n }\n title: {\n id: string\n 'data-scope': 'popover'\n 'data-part': 'title'\n }\n description: {\n id: string\n 'data-scope': 'popover'\n 'data-part': 'description'\n }\n arrow: {\n 'data-scope': 'popover'\n 'data-part': 'arrow'\n }\n closeTrigger: {\n type: 'button'\n 'aria-label': string | ((s: S) => string)\n 'data-scope': 'popover'\n 'data-part': 'close-trigger'\n onClick: (e: MouseEvent) => void\n }\n}\n\nexport interface ConnectOptions {\n id: string\n closeLabel?: string\n}\n\nexport function connect<S>(\n get: (s: S) => PopoverState,\n send: Send<PopoverMsg>,\n opts: ConnectOptions,\n): PopoverParts<S> {\n const locale = useContext<S, Locale>(LocaleContext)\n const base = opts.id\n const triggerId = `${base}:trigger`\n const contentId = `${base}:content`\n const titleId = `${base}:title`\n const descId = `${base}:description`\n const closeLabel: string | ((s: S) => string) =\n opts.closeLabel ?? ((s: S) => locale(s).popover.close)\n\n return {\n trigger: {\n type: 'button',\n 'aria-haspopup': 'dialog',\n 'aria-expanded': (s) => get(s).open,\n 'aria-controls': contentId,\n id: triggerId,\n 'data-state': (s) => (get(s).open ? 'open' : 'closed'),\n 'data-scope': 'popover',\n 'data-part': 'trigger',\n onClick: tagSend(send, ['toggle'], () => send({ type: 'toggle' })),\n },\n positioner: {\n 'data-scope': 'popover',\n 'data-part': 'positioner',\n style: 'position:absolute;top:0;left:0;',\n },\n content: {\n role: 'dialog',\n id: contentId,\n 'aria-labelledby': titleId,\n tabIndex: -1,\n 'data-state': (s) => (get(s).open ? 'open' : 'closed'),\n 'data-scope': 'popover',\n 'data-part': 'content',\n },\n title: {\n id: titleId,\n 'data-scope': 'popover',\n 'data-part': 'title',\n },\n description: {\n id: descId,\n 'data-scope': 'popover',\n 'data-part': 'description',\n },\n arrow: {\n 'data-scope': 'popover',\n 'data-part': 'arrow',\n },\n closeTrigger: {\n type: 'button',\n 'aria-label': closeLabel,\n 'data-scope': 'popover',\n 'data-part': 'close-trigger',\n onClick: tagSend(send, ['close'], () => send({ type: 'close' })),\n },\n }\n}\n\nexport interface OverlayOptions<S> {\n get: (s: S) => PopoverState\n send: Send<PopoverMsg>\n parts: PopoverParts<S>\n content: () => Node[]\n /** Placement preference — bottom | top | right | left with -start/-end variants. */\n placement?: Placement\n /** Offset between trigger and content, px (default: 8). */\n offset?: number\n /** Auto-flip to opposite side (default: true). */\n flip?: boolean\n /** Shift to keep in viewport (default: true). */\n shift?: boolean\n /** Optional transition. */\n transition?: TransitionOptions\n /** Close on Escape (default: true). */\n closeOnEscape?: boolean\n /** Close on outside click (default: true). */\n closeOnOutsideClick?: boolean\n /** Trap focus inside popover while open (default: false — non-modal). */\n trapFocus?: boolean\n /** Restore focus to trigger on close (default: true). */\n restoreFocus?: boolean\n /** Portal target (default: 'body'). */\n target?: string | HTMLElement\n /** Arrow element selector within content (optional). */\n arrowSelector?: string\n}\n\nexport function overlay<S>(opts: OverlayOptions<S>): Node[] {\n const target = opts.target ?? 'body'\n const placement = opts.placement ?? 'bottom'\n const offset = opts.offset ?? 8\n const flip = opts.flip !== false\n const shift = opts.shift !== false\n const closeOnEscape = opts.closeOnEscape !== false\n const closeOnOutsideClick = opts.closeOnOutsideClick !== false\n const trapFocus = opts.trapFocus === true\n const restoreFocus = opts.restoreFocus !== false\n const parts = opts.parts\n const contentId = parts.content.id\n const triggerId = parts.trigger.id\n\n return show<S, PopoverMsg>({\n when: (s) => opts.get(s).open,\n render: () =>\n portal({\n target,\n render: () => {\n onMount(() => {\n const contentEl = document.getElementById(contentId)\n const triggerEl = document.getElementById(triggerId)\n if (!contentEl || !triggerEl) return\n\n const cleanups: Array<() => void> = []\n\n // Position content relative to trigger\n const positioner = contentEl.closest('[data-part=\"positioner\"]') as HTMLElement | null\n const floatingEl = positioner ?? contentEl\n const arrow = opts.arrowSelector\n ? (contentEl.querySelector(opts.arrowSelector) as HTMLElement | null)\n : null\n cleanups.push(\n attachFloating({\n anchor: triggerEl,\n floating: floatingEl,\n placement,\n offset,\n flip,\n shift,\n arrow: arrow ?? undefined,\n }),\n )\n\n if (trapFocus) {\n cleanups.push(\n pushFocusTrap({\n container: contentEl,\n restoreFocus,\n }),\n )\n }\n\n if (closeOnEscape || closeOnOutsideClick) {\n cleanups.push(\n pushDismissable({\n element: contentEl,\n ignore: () => [triggerEl],\n disableEscape: !closeOnEscape,\n disableOutside: !closeOnOutsideClick,\n onDismiss: () => {\n opts.send({ type: 'close' })\n if (restoreFocus) triggerEl.focus()\n },\n }),\n )\n }\n\n return () => {\n for (let i = cleanups.length - 1; i >= 0; i--) cleanups[i]!()\n }\n })\n return [div(parts.positioner, opts.content())]\n },\n }),\n enter: opts.transition?.enter,\n leave: opts.transition?.leave,\n })\n}\n\nexport const popover = { init, update, connect, overlay }\n"]}
|
|
1
|
+
{"version":3,"file":"popover.js","sourceRoot":"","sources":["../../src/components/popover.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send, Signal, TransitionOptions } from '@llui/dom'\nimport { show, portal, onMount, div, useContext, tagSend } from '@llui/dom'\nimport { LocaleContext } from '../locale.js'\nimport { pushDismissable } from '../utils/dismissable.js'\nimport { pushFocusTrap } from '../utils/focus-trap.js'\nimport { attachFloating, type Placement } from '../utils/floating.js'\n\n/**\n * Popover — click-triggered, non-modal floating overlay anchored to its\n * trigger. Use for menus, date pickers, color pickers, filters, etc.\n *\n * Like dialog, has a pure state machine + a view helper (`overlay()`) that\n * wires floating-ui positioning, dismissable, and optional focus trapping.\n */\n\nexport interface PopoverState {\n open: boolean\n}\n\nexport type PopoverMsg =\n /** @intent(\"Open the popover\") */\n | { type: 'open' }\n /** @intent(\"Close the popover\") */\n | { type: 'close' }\n /** @intent(\"Toggle the popover open/closed\") */\n | { type: 'toggle' }\n /** @intent(\"Set the popover's open state to a specific value\") */\n | { type: 'setOpen'; open: boolean }\n\nexport interface PopoverInit {\n open?: boolean\n}\n\nexport function init(opts: PopoverInit = {}): PopoverState {\n return { open: opts.open ?? false }\n}\n\nexport function update(state: PopoverState, msg: PopoverMsg): [PopoverState, never[]] {\n switch (msg.type) {\n case 'open':\n return [{ ...state, open: true }, []]\n case 'close':\n return [{ ...state, open: false }, []]\n case 'toggle':\n return [{ ...state, open: !state.open }, []]\n case 'setOpen':\n return [{ ...state, open: msg.open }, []]\n }\n}\n\nexport interface PopoverParts {\n trigger: {\n type: 'button'\n 'aria-haspopup': 'dialog'\n 'aria-expanded': Signal<boolean>\n 'aria-controls': string\n id: string\n 'data-state': Signal<'open' | 'closed'>\n 'data-scope': 'popover'\n 'data-part': 'trigger'\n onClick: (e: MouseEvent) => void\n }\n positioner: {\n 'data-scope': 'popover'\n 'data-part': 'positioner'\n style: string\n }\n content: {\n role: 'dialog'\n id: string\n 'aria-labelledby': string\n tabIndex: -1\n 'data-state': Signal<'open' | 'closed'>\n 'data-scope': 'popover'\n 'data-part': 'content'\n }\n title: {\n id: string\n 'data-scope': 'popover'\n 'data-part': 'title'\n }\n description: {\n id: string\n 'data-scope': 'popover'\n 'data-part': 'description'\n }\n arrow: {\n 'data-scope': 'popover'\n 'data-part': 'arrow'\n }\n closeTrigger: {\n type: 'button'\n 'aria-label': string\n 'data-scope': 'popover'\n 'data-part': 'close-trigger'\n onClick: (e: MouseEvent) => void\n }\n}\n\nexport interface ConnectOptions {\n id: string\n closeLabel?: string\n}\n\nexport function connect(\n state: Signal<PopoverState>,\n send: Send<PopoverMsg>,\n opts: ConnectOptions,\n): PopoverParts {\n const locale = useContext(LocaleContext)\n const base = opts.id\n const triggerId = `${base}:trigger`\n const contentId = `${base}:content`\n const titleId = `${base}:title`\n const descId = `${base}:description`\n const closeLabel = opts.closeLabel ?? locale.popover.close\n\n return {\n trigger: {\n type: 'button',\n 'aria-haspopup': 'dialog',\n 'aria-expanded': state.map((st) => st.open),\n 'aria-controls': contentId,\n id: triggerId,\n 'data-state': state.map((st) => (st.open ? 'open' : 'closed')),\n 'data-scope': 'popover',\n 'data-part': 'trigger',\n onClick: tagSend(send, ['toggle'], () => send({ type: 'toggle' })),\n },\n positioner: {\n 'data-scope': 'popover',\n 'data-part': 'positioner',\n style: 'position:absolute;top:0;left:0;',\n },\n content: {\n role: 'dialog',\n id: contentId,\n 'aria-labelledby': titleId,\n tabIndex: -1,\n 'data-state': state.map((st) => (st.open ? 'open' : 'closed')),\n 'data-scope': 'popover',\n 'data-part': 'content',\n },\n title: {\n id: titleId,\n 'data-scope': 'popover',\n 'data-part': 'title',\n },\n description: {\n id: descId,\n 'data-scope': 'popover',\n 'data-part': 'description',\n },\n arrow: {\n 'data-scope': 'popover',\n 'data-part': 'arrow',\n },\n closeTrigger: {\n type: 'button',\n 'aria-label': closeLabel,\n 'data-scope': 'popover',\n 'data-part': 'close-trigger',\n onClick: tagSend(send, ['close'], () => send({ type: 'close' })),\n },\n }\n}\n\nexport interface OverlayOptions {\n state: Signal<PopoverState>\n send: Send<PopoverMsg>\n parts: PopoverParts\n content: () => Node[]\n /** Placement preference — bottom | top | right | left with -start/-end variants. */\n placement?: Placement\n /** Offset between trigger and content, px (default: 8). */\n offset?: number\n /** Auto-flip to opposite side (default: true). */\n flip?: boolean\n /** Shift to keep in viewport (default: true). */\n shift?: boolean\n /** Optional transition. */\n transition?: TransitionOptions\n /** Close on Escape (default: true). */\n closeOnEscape?: boolean\n /** Close on outside click (default: true). */\n closeOnOutsideClick?: boolean\n /** Trap focus inside popover while open (default: false — non-modal). */\n trapFocus?: boolean\n /** Restore focus to trigger on close (default: true). */\n restoreFocus?: boolean\n /** Portal target (default: 'body'). */\n target?: string | HTMLElement\n /** Arrow element selector within content (optional). */\n arrowSelector?: string\n}\n\nexport function overlay(opts: OverlayOptions): Node[] {\n const rawTarget = opts.target ?? 'body'\n const placement = opts.placement ?? 'bottom'\n const offset = opts.offset ?? 8\n const flip = opts.flip !== false\n const shift = opts.shift !== false\n const closeOnEscape = opts.closeOnEscape !== false\n const closeOnOutsideClick = opts.closeOnOutsideClick !== false\n const trapFocus = opts.trapFocus === true\n const restoreFocus = opts.restoreFocus !== false\n const parts = opts.parts\n const contentId = parts.content.id\n const triggerId = parts.trigger.id\n\n return [\n show(\n opts.state.map((st) => st.open),\n () => {\n const targetEl =\n typeof rawTarget === 'string'\n ? (document.querySelector(rawTarget) ?? document.body)\n : rawTarget\n return [\n portal(() => {\n onMount(() => {\n const contentEl = document.getElementById(contentId)\n const triggerEl = document.getElementById(triggerId)\n if (!contentEl || !triggerEl) return\n\n const cleanups: Array<() => void> = []\n\n // Position content relative to trigger\n const positioner = contentEl.closest('[data-part=\"positioner\"]') as HTMLElement | null\n const floatingEl = positioner ?? contentEl\n const arrow = opts.arrowSelector\n ? (contentEl.querySelector(opts.arrowSelector) as HTMLElement | null)\n : null\n cleanups.push(\n attachFloating({\n anchor: triggerEl,\n floating: floatingEl,\n placement,\n offset,\n flip,\n shift,\n arrow: arrow ?? undefined,\n }),\n )\n\n if (trapFocus) {\n cleanups.push(\n pushFocusTrap({\n container: contentEl,\n restoreFocus,\n }),\n )\n }\n\n if (closeOnEscape || closeOnOutsideClick) {\n cleanups.push(\n pushDismissable({\n element: contentEl,\n ignore: () => [triggerEl],\n disableEscape: !closeOnEscape,\n disableOutside: !closeOnOutsideClick,\n onDismiss: () => {\n opts.send({ type: 'close' })\n if (restoreFocus) triggerEl.focus()\n },\n }),\n )\n }\n\n return () => {\n for (let i = cleanups.length - 1; i >= 0; i--) cleanups[i]!()\n }\n })\n return [div(parts.positioner, opts.content())]\n }, targetEl),\n ]\n },\n ),\n ]\n}\n\nexport const popover = { init, update, connect, overlay }\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Send } from '@llui/dom';
|
|
1
|
+
import type { Send, Signal } from '@llui/dom';
|
|
2
2
|
/**
|
|
3
3
|
* Presence — track mount/unmount lifecycle with exit-delay support.
|
|
4
4
|
*
|
|
@@ -59,17 +59,19 @@ export declare function isMounted(state: PresenceState): boolean;
|
|
|
59
59
|
/** Whether the element is visible (not running an exit animation). */
|
|
60
60
|
export declare function isVisible(state: PresenceState): boolean;
|
|
61
61
|
export declare function isAnimating(state: PresenceState): boolean;
|
|
62
|
-
export interface PresenceParts
|
|
62
|
+
export interface PresenceParts {
|
|
63
63
|
root: {
|
|
64
64
|
'data-scope': 'presence';
|
|
65
65
|
'data-part': 'root';
|
|
66
|
-
'data-state':
|
|
67
|
-
hidden:
|
|
66
|
+
'data-state': Signal<PresenceStatus>;
|
|
67
|
+
hidden: Signal<boolean>;
|
|
68
68
|
onAnimationEnd: (e: AnimationEvent) => void;
|
|
69
69
|
onTransitionEnd: (e: TransitionEvent) => void;
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
/** Signal-surface connect: takes the component's `presence` state slice as a
|
|
73
|
+
* Signal and returns reactive (handle-based) props for spreading into a view. */
|
|
74
|
+
export declare function connect(state: Signal<PresenceState>, send: Send<PresenceMsg>): PresenceParts;
|
|
73
75
|
export declare const presence: {
|
|
74
76
|
init: typeof init;
|
|
75
77
|
update: typeof update;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../src/components/presence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../src/components/presence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAE7C;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAA;AAEtE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,cAAc,CAAA;IACtB,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,MAAM,WAAW;AACrB,qFAAqF;AACnF;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;AAClB,kFAAkF;GAChF;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE;AACnB,uDAAuD;GACrD;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE;AACpB,iBAAiB;GACf;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE;AAC1B,iFAAiF;GAC/E;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAA;AAE5C,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,8FAA8F;IAC9F,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,wBAAgB,IAAI,CAAC,IAAI,GAAE,YAAiB,GAAG,aAAa,CAK3D;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,WAAW,GAAG,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC,CAmBvF;AAED,0DAA0D;AAC1D,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAGvD;AAED,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAEvD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAEzD;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE;QACJ,YAAY,EAAE,UAAU,CAAA;QACxB,WAAW,EAAE,MAAM,CAAA;QACnB,YAAY,EAAE,MAAM,CAAC,cAAc,CAAC,CAAA;QACpC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;QACvB,cAAc,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAA;QAC3C,eAAe,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,IAAI,CAAA;KAC9C,CAAA;CACF;AAED;iFACiF;AACjF,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,aAAa,CAY5F;AAED,eAAO,MAAM,QAAQ;;;;;;;CAA+D,CAAA"}
|
|
@@ -41,14 +41,16 @@ export function isVisible(state) {
|
|
|
41
41
|
export function isAnimating(state) {
|
|
42
42
|
return state.status === 'opening' || state.status === 'closing';
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
/** Signal-surface connect: takes the component's `presence` state slice as a
|
|
45
|
+
* Signal and returns reactive (handle-based) props for spreading into a view. */
|
|
46
|
+
export function connect(state, send) {
|
|
45
47
|
const onEnd = () => send({ type: 'animationEnd' });
|
|
46
48
|
return {
|
|
47
49
|
root: {
|
|
48
50
|
'data-scope': 'presence',
|
|
49
51
|
'data-part': 'root',
|
|
50
|
-
'data-state': (s) =>
|
|
51
|
-
hidden: (s) =>
|
|
52
|
+
'data-state': state.map((s) => s.status),
|
|
53
|
+
hidden: state.map((s) => s.status === 'closed' && !s.unmountOnExit),
|
|
52
54
|
onAnimationEnd: onEnd,
|
|
53
55
|
onTransitionEnd: onEnd,
|
|
54
56
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presence.js","sourceRoot":"","sources":["../../src/components/presence.ts"],"names":[],"mappings":"AAiDA,MAAM,UAAU,IAAI,CAAC,OAAqB,EAAE;IAC1C,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;QACxC,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI;KAC1C,CAAA;AACH,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,KAAoB,EAAE,GAAgB;IAC3D,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,MAAM;YACT,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;YAC7E,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAA;QAC9C,KAAK,OAAO;YACV,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;YAC/E,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAA;QAC9C,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,CAAA;YACrE,OAAO,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;QAC5D,CAAC;QACD,KAAK,cAAc;YACjB,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;YACzE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;YAC3E,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QACpB,KAAK,YAAY;YACf,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;IACtE,CAAC;AACH,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,SAAS,CAAC,KAAoB;IAC5C,IAAI,CAAC,KAAK,CAAC,aAAa;QAAE,OAAO,IAAI,CAAA;IACrC,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAA;AAClC,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,SAAS,CAAC,KAAoB;IAC5C,OAAO,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,CAAA;AAC9D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAoB;IAC9C,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,CAAA;AACjE,CAAC;AAaD,MAAM,UAAU,OAAO,
|
|
1
|
+
{"version":3,"file":"presence.js","sourceRoot":"","sources":["../../src/components/presence.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send, Signal } from '@llui/dom'\n\n/**\n * Presence — track mount/unmount lifecycle with exit-delay support.\n *\n * In many components (dialogs, tooltips, menus) the consumer wants to:\n * 1. close the overlay (fire exit animation)\n * 2. keep it mounted long enough for the animation to finish\n * 3. unmount it\n *\n * LLui already provides `@llui/transitions` for most of this, but a\n * presence machine is useful when you want to coordinate multiple\n * elements or expose state outside the transition primitive.\n *\n * State flow:\n * closed → (open) → opening → open\n * open → (close) → closing → closed\n *\n * The consumer fires `animationEnd` to advance past opening/closing.\n * If `unmountOnExit` is true, `closed` means \"safe to remove from DOM\";\n * otherwise the element stays mounted even when closed (display:none).\n */\n\nexport type PresenceStatus = 'closed' | 'opening' | 'open' | 'closing'\n\nexport interface PresenceState {\n status: PresenceStatus\n unmountOnExit: boolean\n}\n\nexport type PresenceMsg =\n /** @intent(\"Begin opening the element (closed → opening, plays enter animation)\") */\n | { type: 'open' }\n /** @intent(\"Begin closing the element (open → closing, plays exit animation)\") */\n | { type: 'close' }\n /** @intent(\"Toggle between open and closed states\") */\n | { type: 'toggle' }\n /** @humanOnly */\n | { type: 'animationEnd' }\n /** @intent(\"Set the desired presence directly (true = open, false = closed)\") */\n | { type: 'setPresent'; present: boolean }\n\nexport interface PresenceInit {\n /** Initial presence — true starts in 'open', false starts in 'closed'. */\n present?: boolean\n /** Whether 'closed' means \"unmount\" (true) or \"hidden but mounted\" (false). Default: true. */\n unmountOnExit?: boolean\n}\n\nexport function init(opts: PresenceInit = {}): PresenceState {\n return {\n status: opts.present ? 'open' : 'closed',\n unmountOnExit: opts.unmountOnExit ?? true,\n }\n}\n\nexport function update(state: PresenceState, msg: PresenceMsg): [PresenceState, never[]] {\n switch (msg.type) {\n case 'open':\n if (state.status === 'open' || state.status === 'opening') return [state, []]\n return [{ ...state, status: 'opening' }, []]\n case 'close':\n if (state.status === 'closed' || state.status === 'closing') return [state, []]\n return [{ ...state, status: 'closing' }, []]\n case 'toggle': {\n const present = state.status === 'open' || state.status === 'opening'\n return update(state, { type: present ? 'close' : 'open' })\n }\n case 'animationEnd':\n if (state.status === 'opening') return [{ ...state, status: 'open' }, []]\n if (state.status === 'closing') return [{ ...state, status: 'closed' }, []]\n return [state, []]\n case 'setPresent':\n return [{ ...state, status: msg.present ? 'open' : 'closed' }, []]\n }\n}\n\n/** Whether the element should be in the DOM (mounted). */\nexport function isMounted(state: PresenceState): boolean {\n if (!state.unmountOnExit) return true\n return state.status !== 'closed'\n}\n\n/** Whether the element is visible (not running an exit animation). */\nexport function isVisible(state: PresenceState): boolean {\n return state.status === 'open' || state.status === 'opening'\n}\n\nexport function isAnimating(state: PresenceState): boolean {\n return state.status === 'opening' || state.status === 'closing'\n}\n\nexport interface PresenceParts {\n root: {\n 'data-scope': 'presence'\n 'data-part': 'root'\n 'data-state': Signal<PresenceStatus>\n hidden: Signal<boolean>\n onAnimationEnd: (e: AnimationEvent) => void\n onTransitionEnd: (e: TransitionEvent) => void\n }\n}\n\n/** Signal-surface connect: takes the component's `presence` state slice as a\n * Signal and returns reactive (handle-based) props for spreading into a view. */\nexport function connect(state: Signal<PresenceState>, send: Send<PresenceMsg>): PresenceParts {\n const onEnd = (): void => send({ type: 'animationEnd' })\n return {\n root: {\n 'data-scope': 'presence',\n 'data-part': 'root',\n 'data-state': state.map((s) => s.status),\n hidden: state.map((s) => s.status === 'closed' && !s.unmountOnExit),\n onAnimationEnd: onEnd,\n onTransitionEnd: onEnd,\n },\n }\n}\n\nexport const presence = { init, update, connect, isMounted, isVisible, isAnimating }\n"]}
|