@llui/components 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +143 -0
- package/dist/components/accordion.d.ts +115 -0
- package/dist/components/accordion.d.ts.map +1 -0
- package/dist/components/accordion.js +138 -0
- package/dist/components/alert-dialog.d.ts +45 -0
- package/dist/components/alert-dialog.d.ts.map +1 -0
- package/dist/components/alert-dialog.js +12 -0
- package/dist/components/angle-slider.d.ts +121 -0
- package/dist/components/angle-slider.d.ts.map +1 -0
- package/dist/components/angle-slider.js +145 -0
- package/dist/components/async-list.d.ts +104 -0
- package/dist/components/async-list.d.ts.map +1 -0
- package/dist/components/async-list.js +117 -0
- package/dist/components/avatar.d.ts +58 -0
- package/dist/components/avatar.d.ts.map +1 -0
- package/dist/components/avatar.js +43 -0
- package/dist/components/carousel.d.ts +128 -0
- package/dist/components/carousel.d.ts.map +1 -0
- package/dist/components/carousel.js +131 -0
- package/dist/components/cascade-select.d.ts +95 -0
- package/dist/components/cascade-select.d.ts.map +1 -0
- package/dist/components/cascade-select.js +100 -0
- package/dist/components/checkbox.d.ts +74 -0
- package/dist/components/checkbox.d.ts.map +1 -0
- package/dist/components/checkbox.js +73 -0
- package/dist/components/clipboard.d.ts +72 -0
- package/dist/components/clipboard.d.ts.map +1 -0
- package/dist/components/clipboard.js +73 -0
- package/dist/components/collapsible.d.ts +64 -0
- package/dist/components/collapsible.d.ts.map +1 -0
- package/dist/components/collapsible.js +51 -0
- package/dist/components/color-picker.d.ts +125 -0
- package/dist/components/color-picker.d.ts.map +1 -0
- package/dist/components/color-picker.js +169 -0
- package/dist/components/combobox.d.ts +163 -0
- package/dist/components/combobox.d.ts.map +1 -0
- package/dist/components/combobox.js +345 -0
- package/dist/components/context-menu.d.ts +105 -0
- package/dist/components/context-menu.d.ts.map +1 -0
- package/dist/components/context-menu.js +177 -0
- package/dist/components/date-input.d.ts +117 -0
- package/dist/components/date-input.d.ts.map +1 -0
- package/dist/components/date-input.js +149 -0
- package/dist/components/date-picker.d.ts +142 -0
- package/dist/components/date-picker.d.ts.map +1 -0
- package/dist/components/date-picker.js +294 -0
- package/dist/components/dialog.d.ts +152 -0
- package/dist/components/dialog.d.ts.map +1 -0
- package/dist/components/dialog.js +140 -0
- package/dist/components/drawer.d.ts +106 -0
- package/dist/components/drawer.d.ts.map +1 -0
- package/dist/components/drawer.js +136 -0
- package/dist/components/editable.d.ts +92 -0
- package/dist/components/editable.d.ts.map +1 -0
- package/dist/components/editable.js +112 -0
- package/dist/components/file-upload.d.ts +251 -0
- package/dist/components/file-upload.d.ts.map +1 -0
- package/dist/components/file-upload.js +324 -0
- package/dist/components/floating-panel.d.ts +171 -0
- package/dist/components/floating-panel.d.ts.map +1 -0
- package/dist/components/floating-panel.js +198 -0
- package/dist/components/hover-card.d.ts +85 -0
- package/dist/components/hover-card.d.ts.map +1 -0
- package/dist/components/hover-card.js +128 -0
- package/dist/components/image-cropper.d.ts +129 -0
- package/dist/components/image-cropper.d.ts.map +1 -0
- package/dist/components/image-cropper.js +208 -0
- package/dist/components/index.d.ts +109 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +54 -0
- package/dist/components/listbox.d.ts +98 -0
- package/dist/components/listbox.d.ts.map +1 -0
- package/dist/components/listbox.js +174 -0
- package/dist/components/marquee.d.ts +84 -0
- package/dist/components/marquee.d.ts.map +1 -0
- package/dist/components/marquee.js +73 -0
- package/dist/components/menu.d.ts +131 -0
- package/dist/components/menu.d.ts.map +1 -0
- package/dist/components/menu.js +262 -0
- package/dist/components/navigation-menu.d.ts +111 -0
- package/dist/components/navigation-menu.d.ts.map +1 -0
- package/dist/components/navigation-menu.js +102 -0
- package/dist/components/number-input.d.ts +106 -0
- package/dist/components/number-input.d.ts.map +1 -0
- package/dist/components/number-input.js +178 -0
- package/dist/components/pagination.d.ts +113 -0
- package/dist/components/pagination.d.ts.map +1 -0
- package/dist/components/pagination.js +135 -0
- package/dist/components/password-input.d.ts +64 -0
- package/dist/components/password-input.d.ts.map +1 -0
- package/dist/components/password-input.js +52 -0
- package/dist/components/pin-input.d.ts +89 -0
- package/dist/components/pin-input.d.ts.map +1 -0
- package/dist/components/pin-input.js +139 -0
- package/dist/components/popover.d.ts +116 -0
- package/dist/components/popover.d.ts.map +1 -0
- package/dist/components/popover.js +146 -0
- package/dist/components/presence.d.ts +71 -0
- package/dist/components/presence.d.ts.map +1 -0
- package/dist/components/presence.js +57 -0
- package/dist/components/progress.d.ts +74 -0
- package/dist/components/progress.d.ts.map +1 -0
- package/dist/components/progress.js +80 -0
- package/dist/components/qr-code.d.ts +114 -0
- package/dist/components/qr-code.d.ts.map +1 -0
- package/dist/components/qr-code.js +108 -0
- package/dist/components/radio-group.d.ts +89 -0
- package/dist/components/radio-group.d.ts.map +1 -0
- package/dist/components/radio-group.js +161 -0
- package/dist/components/rating-group.d.ts +88 -0
- package/dist/components/rating-group.d.ts.map +1 -0
- package/dist/components/rating-group.js +122 -0
- package/dist/components/scroll-area.d.ts +124 -0
- package/dist/components/scroll-area.d.ts.map +1 -0
- package/dist/components/scroll-area.js +152 -0
- package/dist/components/select.d.ts +161 -0
- package/dist/components/select.d.ts.map +1 -0
- package/dist/components/select.js +333 -0
- package/dist/components/signature-pad.d.ts +138 -0
- package/dist/components/signature-pad.d.ts.map +1 -0
- package/dist/components/signature-pad.js +142 -0
- package/dist/components/slider.d.ts +117 -0
- package/dist/components/slider.d.ts.map +1 -0
- package/dist/components/slider.js +210 -0
- package/dist/components/splitter.d.ts +87 -0
- package/dist/components/splitter.d.ts.map +1 -0
- package/dist/components/splitter.js +119 -0
- package/dist/components/steps.d.ts +104 -0
- package/dist/components/steps.d.ts.map +1 -0
- package/dist/components/steps.js +133 -0
- package/dist/components/switch.d.ts +66 -0
- package/dist/components/switch.d.ts.map +1 -0
- package/dist/components/switch.js +59 -0
- package/dist/components/tabs.d.ts +146 -0
- package/dist/components/tabs.d.ts.map +1 -0
- package/dist/components/tabs.js +244 -0
- package/dist/components/tags-input.d.ts +118 -0
- package/dist/components/tags-input.d.ts.map +1 -0
- package/dist/components/tags-input.js +168 -0
- package/dist/components/time-picker.d.ts +121 -0
- package/dist/components/time-picker.d.ts.map +1 -0
- package/dist/components/time-picker.js +147 -0
- package/dist/components/timer.d.ts +131 -0
- package/dist/components/timer.d.ts.map +1 -0
- package/dist/components/timer.js +117 -0
- package/dist/components/toast.d.ts +119 -0
- package/dist/components/toast.d.ts.map +1 -0
- package/dist/components/toast.js +102 -0
- package/dist/components/toc.d.ts +119 -0
- package/dist/components/toc.d.ts.map +1 -0
- package/dist/components/toc.js +107 -0
- package/dist/components/toggle-group.d.ts +80 -0
- package/dist/components/toggle-group.d.ts.map +1 -0
- package/dist/components/toggle-group.js +93 -0
- package/dist/components/toggle.d.ts +47 -0
- package/dist/components/toggle.d.ts.map +1 -0
- package/dist/components/toggle.js +41 -0
- package/dist/components/tooltip.d.ts +92 -0
- package/dist/components/tooltip.d.ts.map +1 -0
- package/dist/components/tooltip.js +147 -0
- package/dist/components/tour.d.ts +145 -0
- package/dist/components/tour.d.ts.map +1 -0
- package/dist/components/tour.js +133 -0
- package/dist/components/tree-view.d.ts +216 -0
- package/dist/components/tree-view.d.ts.map +1 -0
- package/dist/components/tree-view.js +293 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/patterns/confirm-dialog.d.ts +92 -0
- package/dist/patterns/confirm-dialog.d.ts.map +1 -0
- package/dist/patterns/confirm-dialog.js +92 -0
- package/dist/patterns/index.d.ts +3 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +1 -0
- package/dist/utils/anatomy.d.ts +40 -0
- package/dist/utils/anatomy.d.ts.map +1 -0
- package/dist/utils/anatomy.js +41 -0
- package/dist/utils/aria-hidden.d.ts +12 -0
- package/dist/utils/aria-hidden.d.ts.map +1 -0
- package/dist/utils/aria-hidden.js +72 -0
- package/dist/utils/dismissable.d.ts +25 -0
- package/dist/utils/dismissable.d.ts.map +1 -0
- package/dist/utils/dismissable.js +65 -0
- package/dist/utils/dom.d.ts +8 -0
- package/dist/utils/dom.d.ts.map +1 -0
- package/dist/utils/dom.js +21 -0
- package/dist/utils/floating.d.ts +44 -0
- package/dist/utils/floating.d.ts.map +1 -0
- package/dist/utils/floating.js +44 -0
- package/dist/utils/focus-trap.d.ts +18 -0
- package/dist/utils/focus-trap.d.ts.map +1 -0
- package/dist/utils/focus-trap.js +85 -0
- package/dist/utils/focusables.d.ts +6 -0
- package/dist/utils/focusables.d.ts.map +1 -0
- package/dist/utils/focusables.js +65 -0
- package/dist/utils/index.d.ts +18 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +10 -0
- package/dist/utils/interact-outside.d.ts +26 -0
- package/dist/utils/interact-outside.d.ts.map +1 -0
- package/dist/utils/interact-outside.js +46 -0
- package/dist/utils/remove-scroll.d.ts +8 -0
- package/dist/utils/remove-scroll.d.ts.map +1 -0
- package/dist/utils/remove-scroll.js +37 -0
- package/dist/utils/tree-collection.d.ts +61 -0
- package/dist/utils/tree-collection.d.ts.map +1 -0
- package/dist/utils/tree-collection.js +137 -0
- package/dist/utils/typeahead.d.ts +49 -0
- package/dist/utils/typeahead.d.ts.map +1 -0
- package/dist/utils/typeahead.js +81 -0
- package/package.json +282 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Franco Ponticelli
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# @llui/components
|
|
2
|
+
|
|
3
|
+
54 headless UI components for [LLui](../../README.md). Pure state machines with no DOM opinions — you own the markup and styling via `data-scope` / `data-part` attributes.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @llui/components
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependency: `@llui/dom`.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Each component exports `init`, `update`, `connect`, and a barrel object:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { component, div, button } from '@llui/dom'
|
|
19
|
+
import { tabs } from '@llui/components/tabs'
|
|
20
|
+
|
|
21
|
+
type State = { tabs: tabs.TabsState }
|
|
22
|
+
type Msg = { type: 'tabs'; msg: tabs.TabsMsg }
|
|
23
|
+
|
|
24
|
+
const App = component<State, Msg, never>({
|
|
25
|
+
name: 'App',
|
|
26
|
+
init: () => [{ tabs: tabs.init({ items: ['a', 'b', 'c'], value: 'a' }) }, []],
|
|
27
|
+
update: (s, m) => {
|
|
28
|
+
const [t] = tabs.update(s.tabs, m.msg)
|
|
29
|
+
return [{ tabs: t }, []]
|
|
30
|
+
},
|
|
31
|
+
view: ({ send, text }) => {
|
|
32
|
+
const t = tabs.connect<State>(
|
|
33
|
+
(s) => s.tabs,
|
|
34
|
+
(m) => send({ type: 'tabs', msg: m }),
|
|
35
|
+
{ id: 'demo' },
|
|
36
|
+
)
|
|
37
|
+
return [
|
|
38
|
+
div({ ...t.root }, [
|
|
39
|
+
div({ ...t.list }, [
|
|
40
|
+
button({ ...t.item('a').trigger }, [text('Tab A')]),
|
|
41
|
+
button({ ...t.item('b').trigger }, [text('Tab B')]),
|
|
42
|
+
button({ ...t.item('c').trigger }, [text('Tab C')]),
|
|
43
|
+
]),
|
|
44
|
+
div({ ...t.item('a').panel }, [text('Content A')]),
|
|
45
|
+
div({ ...t.item('b').panel }, [text('Content B')]),
|
|
46
|
+
div({ ...t.item('c').panel }, [text('Content C')]),
|
|
47
|
+
]),
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Pattern
|
|
54
|
+
|
|
55
|
+
1. **`init(opts?)`** — creates the initial state
|
|
56
|
+
2. **`update(state, msg)`** — pure reducer, returns `[newState, effects[]]`
|
|
57
|
+
3. **`connect(get, send, opts?)`** — returns parts objects with reactive props, ARIA attributes, and event handlers. Spread parts onto your elements: `div({ ...parts.root }, [...])`
|
|
58
|
+
4. **Overlay helpers** (dialog, popover, menu, etc.) — `overlay()` wires up portals, focus traps, dismiss layers, and positioning
|
|
59
|
+
|
|
60
|
+
### Composition with `sliceHandler`
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { mergeHandlers, sliceHandler } from '@llui/dom'
|
|
64
|
+
|
|
65
|
+
const update = mergeHandlers<State, Msg, never>(
|
|
66
|
+
sliceHandler({
|
|
67
|
+
get: (s) => s.tabs,
|
|
68
|
+
set: (s, v) => ({ ...s, tabs: v }),
|
|
69
|
+
narrow: (m) => (m.type === 'tabs' ? m.msg : null),
|
|
70
|
+
sub: tabs.update,
|
|
71
|
+
}),
|
|
72
|
+
// ... more slices
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Components (54)
|
|
77
|
+
|
|
78
|
+
### Form controls
|
|
79
|
+
|
|
80
|
+
accordion, checkbox, collapsible, editable, number-input, password-input, pin-input, radio-group, rating-group, slider, switch, tabs, tags-input, toggle, toggle-group
|
|
81
|
+
|
|
82
|
+
### Overlays
|
|
83
|
+
|
|
84
|
+
alert-dialog, combobox, context-menu, dialog, drawer, hover-card, menu, navigation-menu, popover, select, toast, tooltip, tour
|
|
85
|
+
|
|
86
|
+
### Data display
|
|
87
|
+
|
|
88
|
+
async-list, avatar, carousel, cascade-select, listbox, pagination, progress, qr-code, scroll-area, steps, toc, tree-view
|
|
89
|
+
|
|
90
|
+
### Pickers
|
|
91
|
+
|
|
92
|
+
color-picker, date-input, date-picker, time-picker, angle-slider
|
|
93
|
+
|
|
94
|
+
### Media / canvas
|
|
95
|
+
|
|
96
|
+
file-upload, floating-panel, image-cropper, marquee, presence, signature-pad, timer
|
|
97
|
+
|
|
98
|
+
### Patterns
|
|
99
|
+
|
|
100
|
+
`@llui/components/patterns/confirm-dialog` — pre-wired alert-dialog for destructive confirmations.
|
|
101
|
+
|
|
102
|
+
## Utilities
|
|
103
|
+
|
|
104
|
+
Shared helpers used internally and exported for advanced use:
|
|
105
|
+
|
|
106
|
+
| Utility | Purpose |
|
|
107
|
+
| ---------------- | ------------------------------------------------------------------------ |
|
|
108
|
+
| `typeahead` | First-letter search across menu, select, listbox, tree-view |
|
|
109
|
+
| `TreeCollection` | Indexed tree traversal — visibleItems, labels, indeterminate computation |
|
|
110
|
+
| `floating` | `@floating-ui/dom` wrapper for popover/menu positioning |
|
|
111
|
+
| `focus-trap` | Stack-based focus containment for modals |
|
|
112
|
+
| `dismissable` | Esc / outside-click dismiss layer stack |
|
|
113
|
+
| `aria-hidden` | `aria-hidden` on siblings of a modal for screen readers |
|
|
114
|
+
| `remove-scroll` | Body scroll lock for modals/drawers |
|
|
115
|
+
|
|
116
|
+
## Sub-path imports
|
|
117
|
+
|
|
118
|
+
Every component has its own entry point for tree-shaking:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { tabs } from '@llui/components/tabs'
|
|
122
|
+
import { dialog } from '@llui/components/dialog'
|
|
123
|
+
import { timer } from '@llui/components/timer'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Validation
|
|
127
|
+
|
|
128
|
+
Input components accept an optional `validate` callback on `ConnectOptions` that gates state changes:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const parts = editable.connect<S>(get, send, {
|
|
132
|
+
validate: (value) => {
|
|
133
|
+
if (value.length < 3) return ['Too short']
|
|
134
|
+
return null // valid
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Supported on: editable, number-input, tags-input, pin-input, file-upload.
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { Send } from '@llui/dom';
|
|
2
|
+
/**
|
|
3
|
+
* Accordion — a stack of expandable panels. Items are identified by a string
|
|
4
|
+
* value. Either a single item is expandable at a time (default) or many
|
|
5
|
+
* (`multiple: true`). `collapsible: false` prevents closing the only open
|
|
6
|
+
* item in single mode.
|
|
7
|
+
*
|
|
8
|
+
* Items themselves are provided by the user's view (accordion is agnostic to
|
|
9
|
+
* item data). The `connect()` API returns a `root` prop set and an `item(value)`
|
|
10
|
+
* factory that produces `trigger` and `content` prop sets scoped to that item.
|
|
11
|
+
*/
|
|
12
|
+
export interface AccordionState {
|
|
13
|
+
/** Values of currently-expanded items. */
|
|
14
|
+
value: string[];
|
|
15
|
+
multiple: boolean;
|
|
16
|
+
collapsible: boolean;
|
|
17
|
+
disabled: boolean;
|
|
18
|
+
/** Ordered list of item values (for keyboard navigation). */
|
|
19
|
+
items: string[];
|
|
20
|
+
}
|
|
21
|
+
export type AccordionMsg = {
|
|
22
|
+
type: 'toggle';
|
|
23
|
+
value: string;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'open';
|
|
26
|
+
value: string;
|
|
27
|
+
} | {
|
|
28
|
+
type: 'close';
|
|
29
|
+
value: string;
|
|
30
|
+
} | {
|
|
31
|
+
type: 'setValue';
|
|
32
|
+
value: string[];
|
|
33
|
+
} | {
|
|
34
|
+
type: 'setItems';
|
|
35
|
+
items: string[];
|
|
36
|
+
} | {
|
|
37
|
+
type: 'focusNext';
|
|
38
|
+
value: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: 'focusPrev';
|
|
41
|
+
value: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: 'focusFirst';
|
|
44
|
+
} | {
|
|
45
|
+
type: 'focusLast';
|
|
46
|
+
};
|
|
47
|
+
export interface AccordionInit {
|
|
48
|
+
value?: string[];
|
|
49
|
+
multiple?: boolean;
|
|
50
|
+
collapsible?: boolean;
|
|
51
|
+
disabled?: boolean;
|
|
52
|
+
items?: string[];
|
|
53
|
+
}
|
|
54
|
+
export declare function init(opts?: AccordionInit): AccordionState;
|
|
55
|
+
export declare function update(state: AccordionState, msg: AccordionMsg): [AccordionState, never[]];
|
|
56
|
+
export interface AccordionItemParts<S> {
|
|
57
|
+
trigger: {
|
|
58
|
+
type: 'button';
|
|
59
|
+
'aria-expanded': (s: S) => boolean;
|
|
60
|
+
'aria-controls': string;
|
|
61
|
+
id: string;
|
|
62
|
+
'data-state': (s: S) => 'open' | 'closed';
|
|
63
|
+
'data-disabled': (s: S) => '' | undefined;
|
|
64
|
+
disabled: (s: S) => boolean;
|
|
65
|
+
'data-scope': 'accordion';
|
|
66
|
+
'data-part': 'trigger';
|
|
67
|
+
'data-value': string;
|
|
68
|
+
onClick: (e: MouseEvent) => void;
|
|
69
|
+
onKeyDown: (e: KeyboardEvent) => void;
|
|
70
|
+
};
|
|
71
|
+
content: {
|
|
72
|
+
role: 'region';
|
|
73
|
+
id: string;
|
|
74
|
+
'aria-labelledby': string;
|
|
75
|
+
'data-state': (s: S) => 'open' | 'closed';
|
|
76
|
+
'data-scope': 'accordion';
|
|
77
|
+
'data-part': 'content';
|
|
78
|
+
hidden: (s: S) => boolean;
|
|
79
|
+
};
|
|
80
|
+
item: {
|
|
81
|
+
'data-state': (s: S) => 'open' | 'closed';
|
|
82
|
+
'data-disabled': (s: S) => '' | undefined;
|
|
83
|
+
'data-scope': 'accordion';
|
|
84
|
+
'data-part': 'item';
|
|
85
|
+
'data-value': string;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export interface AccordionParts<S> {
|
|
89
|
+
root: {
|
|
90
|
+
role: 'region';
|
|
91
|
+
'data-scope': 'accordion';
|
|
92
|
+
'data-part': 'root';
|
|
93
|
+
'data-orientation': 'vertical';
|
|
94
|
+
};
|
|
95
|
+
item: (value: string) => AccordionItemParts<S>;
|
|
96
|
+
}
|
|
97
|
+
export interface ConnectOptions {
|
|
98
|
+
/** Namespace prefix for part ids (for ARIA wiring). Should be unique per instance. */
|
|
99
|
+
id: string;
|
|
100
|
+
}
|
|
101
|
+
export declare function connect<S>(get: (s: S) => AccordionState, send: Send<AccordionMsg>, opts: ConnectOptions): AccordionParts<S>;
|
|
102
|
+
/**
|
|
103
|
+
* Helper: compute the next/prev item value given a focus message + current state.
|
|
104
|
+
* Users' view/onMount can use this to move DOM focus to the correct trigger.
|
|
105
|
+
*/
|
|
106
|
+
export declare function focusTarget(state: AccordionState, msg: Extract<AccordionMsg, {
|
|
107
|
+
type: `focus${string}`;
|
|
108
|
+
}>): string | null;
|
|
109
|
+
export declare const accordion: {
|
|
110
|
+
init: typeof init;
|
|
111
|
+
update: typeof update;
|
|
112
|
+
connect: typeof connect;
|
|
113
|
+
focusTarget: typeof focusTarget;
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=accordion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accordion.d.ts","sourceRoot":"","sources":["../../src/components/accordion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export function init(opts = {}) {
|
|
2
|
+
return {
|
|
3
|
+
value: opts.value ?? [],
|
|
4
|
+
multiple: opts.multiple ?? false,
|
|
5
|
+
collapsible: opts.collapsible ?? true,
|
|
6
|
+
disabled: opts.disabled ?? false,
|
|
7
|
+
items: opts.items ?? [],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function toggleValue(state, value) {
|
|
11
|
+
const isOpen = state.value.includes(value);
|
|
12
|
+
if (state.multiple) {
|
|
13
|
+
return isOpen ? state.value.filter((v) => v !== value) : [...state.value, value];
|
|
14
|
+
}
|
|
15
|
+
// single mode
|
|
16
|
+
if (isOpen) {
|
|
17
|
+
return state.collapsible ? [] : state.value;
|
|
18
|
+
}
|
|
19
|
+
return [value];
|
|
20
|
+
}
|
|
21
|
+
export function update(state, msg) {
|
|
22
|
+
if (state.disabled)
|
|
23
|
+
return [state, []];
|
|
24
|
+
switch (msg.type) {
|
|
25
|
+
case 'toggle':
|
|
26
|
+
return [{ ...state, value: toggleValue(state, msg.value) }, []];
|
|
27
|
+
case 'open':
|
|
28
|
+
if (state.value.includes(msg.value))
|
|
29
|
+
return [state, []];
|
|
30
|
+
return [{ ...state, value: state.multiple ? [...state.value, msg.value] : [msg.value] }, []];
|
|
31
|
+
case 'close':
|
|
32
|
+
if (!state.value.includes(msg.value))
|
|
33
|
+
return [state, []];
|
|
34
|
+
if (!state.multiple && !state.collapsible)
|
|
35
|
+
return [state, []];
|
|
36
|
+
return [{ ...state, value: state.value.filter((v) => v !== msg.value) }, []];
|
|
37
|
+
case 'setValue':
|
|
38
|
+
return [{ ...state, value: msg.value }, []];
|
|
39
|
+
case 'setItems':
|
|
40
|
+
return [{ ...state, items: msg.items }, []];
|
|
41
|
+
// Focus messages don't mutate state but are emitted so user handlers can respond.
|
|
42
|
+
case 'focusNext':
|
|
43
|
+
case 'focusPrev':
|
|
44
|
+
case 'focusFirst':
|
|
45
|
+
case 'focusLast':
|
|
46
|
+
return [state, []];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function connect(get, send, opts) {
|
|
50
|
+
const base = opts.id;
|
|
51
|
+
const triggerId = (v) => `${base}:trigger:${v}`;
|
|
52
|
+
const contentId = (v) => `${base}:content:${v}`;
|
|
53
|
+
return {
|
|
54
|
+
root: {
|
|
55
|
+
role: 'region',
|
|
56
|
+
'data-scope': 'accordion',
|
|
57
|
+
'data-part': 'root',
|
|
58
|
+
'data-orientation': 'vertical',
|
|
59
|
+
},
|
|
60
|
+
item: (value) => ({
|
|
61
|
+
trigger: {
|
|
62
|
+
type: 'button',
|
|
63
|
+
'aria-expanded': (s) => get(s).value.includes(value),
|
|
64
|
+
'aria-controls': contentId(value),
|
|
65
|
+
id: triggerId(value),
|
|
66
|
+
'data-state': (s) => (get(s).value.includes(value) ? 'open' : 'closed'),
|
|
67
|
+
'data-disabled': (s) => (get(s).disabled ? '' : undefined),
|
|
68
|
+
disabled: (s) => get(s).disabled,
|
|
69
|
+
'data-scope': 'accordion',
|
|
70
|
+
'data-part': 'trigger',
|
|
71
|
+
'data-value': value,
|
|
72
|
+
onClick: () => send({ type: 'toggle', value }),
|
|
73
|
+
onKeyDown: (e) => {
|
|
74
|
+
switch (e.key) {
|
|
75
|
+
case 'ArrowDown':
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
send({ type: 'focusNext', value });
|
|
78
|
+
return;
|
|
79
|
+
case 'ArrowUp':
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
send({ type: 'focusPrev', value });
|
|
82
|
+
return;
|
|
83
|
+
case 'Home':
|
|
84
|
+
e.preventDefault();
|
|
85
|
+
send({ type: 'focusFirst' });
|
|
86
|
+
return;
|
|
87
|
+
case 'End':
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
send({ type: 'focusLast' });
|
|
90
|
+
return;
|
|
91
|
+
case ' ':
|
|
92
|
+
case 'Enter':
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
send({ type: 'toggle', value });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
content: {
|
|
100
|
+
role: 'region',
|
|
101
|
+
id: contentId(value),
|
|
102
|
+
'aria-labelledby': triggerId(value),
|
|
103
|
+
'data-state': (s) => (get(s).value.includes(value) ? 'open' : 'closed'),
|
|
104
|
+
'data-scope': 'accordion',
|
|
105
|
+
'data-part': 'content',
|
|
106
|
+
hidden: (s) => !get(s).value.includes(value),
|
|
107
|
+
},
|
|
108
|
+
item: {
|
|
109
|
+
'data-state': (s) => (get(s).value.includes(value) ? 'open' : 'closed'),
|
|
110
|
+
'data-disabled': (s) => (get(s).disabled ? '' : undefined),
|
|
111
|
+
'data-scope': 'accordion',
|
|
112
|
+
'data-part': 'item',
|
|
113
|
+
'data-value': value,
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Helper: compute the next/prev item value given a focus message + current state.
|
|
120
|
+
* Users' view/onMount can use this to move DOM focus to the correct trigger.
|
|
121
|
+
*/
|
|
122
|
+
export function focusTarget(state, msg) {
|
|
123
|
+
const items = state.items;
|
|
124
|
+
if (items.length === 0)
|
|
125
|
+
return null;
|
|
126
|
+
if (msg.type === 'focusFirst')
|
|
127
|
+
return items[0];
|
|
128
|
+
if (msg.type === 'focusLast')
|
|
129
|
+
return items[items.length - 1];
|
|
130
|
+
const idx = items.indexOf(msg.value);
|
|
131
|
+
if (idx === -1)
|
|
132
|
+
return null;
|
|
133
|
+
if (msg.type === 'focusNext')
|
|
134
|
+
return items[(idx + 1) % items.length];
|
|
135
|
+
// focusPrev
|
|
136
|
+
return items[(idx - 1 + items.length) % items.length];
|
|
137
|
+
}
|
|
138
|
+
export const accordion = { init, update, connect, focusTarget };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Send, TransitionOptions } from '@llui/dom';
|
|
2
|
+
import { init, update, type DialogState, type DialogMsg, type DialogParts, type ConnectOptions as DialogConnectOptions } from './dialog';
|
|
3
|
+
/**
|
|
4
|
+
* Alert dialog — a variant of dialog for destructive confirmations or
|
|
5
|
+
* blocking messages. Uses `role="alertdialog"` and defaults to:
|
|
6
|
+
* - `closeOnOutsideClick: false` (user must choose an action explicitly)
|
|
7
|
+
*
|
|
8
|
+
* Shares state, messages, and part structure with `dialog`. Render a
|
|
9
|
+
* `cancelTrigger` alongside the `closeTrigger` and let the application
|
|
10
|
+
* dispatch a follow-up action after the dialog closes.
|
|
11
|
+
*/
|
|
12
|
+
export type { DialogState as AlertDialogState, DialogMsg as AlertDialogMsg };
|
|
13
|
+
export { init, update };
|
|
14
|
+
export interface AlertDialogConnectOptions extends Omit<DialogConnectOptions, 'role'> {
|
|
15
|
+
/** Accessible label for the cancel button (default: 'Cancel'). */
|
|
16
|
+
cancelLabel?: string;
|
|
17
|
+
/** Accessible label for the confirm button (default: 'Confirm'). */
|
|
18
|
+
confirmLabel?: string;
|
|
19
|
+
}
|
|
20
|
+
export type AlertDialogParts<S> = DialogParts<S>;
|
|
21
|
+
export declare function connect<S>(get: (s: S) => DialogState, send: Send<DialogMsg>, opts: AlertDialogConnectOptions): AlertDialogParts<S>;
|
|
22
|
+
export interface AlertDialogOverlayOptions<S> {
|
|
23
|
+
get: (s: S) => DialogState;
|
|
24
|
+
send: Send<DialogMsg>;
|
|
25
|
+
parts: AlertDialogParts<S>;
|
|
26
|
+
content: () => Node[];
|
|
27
|
+
transition?: TransitionOptions;
|
|
28
|
+
closeOnEscape?: boolean;
|
|
29
|
+
/** Whether outside-click should dismiss (default: false for alert dialogs). */
|
|
30
|
+
closeOnOutsideClick?: boolean;
|
|
31
|
+
trapFocus?: boolean;
|
|
32
|
+
lockScroll?: boolean;
|
|
33
|
+
hideSiblings?: boolean;
|
|
34
|
+
target?: string | HTMLElement;
|
|
35
|
+
initialFocus?: Element | (() => Element | null);
|
|
36
|
+
restoreFocus?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function overlay<S>(opts: AlertDialogOverlayOptions<S>): Node[];
|
|
39
|
+
export declare const alertDialog: {
|
|
40
|
+
init: typeof init;
|
|
41
|
+
update: typeof update;
|
|
42
|
+
connect: typeof connect;
|
|
43
|
+
overlay: typeof overlay;
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=alert-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alert-dialog.d.ts","sourceRoot":"","sources":["../../src/components/alert-dialog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,EACL,IAAI,EACJ,MAAM,EAGN,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,cAAc,IAAI,oBAAoB,EAC5C,MAAM,UAAU,CAAA;AAEjB;;;;;;;;GAQG;AAEH,YAAY,EAAE,WAAW,IAAI,gBAAgB,EAAE,SAAS,IAAI,cAAc,EAAE,CAAA;AAE5E,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;AAEvB,MAAM,WAAW,yBAA0B,SAAQ,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACnF,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAA;AAEhD,wBAAgB,OAAO,CAAC,CAAC,EACvB,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,WAAW,EAC1B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EACrB,IAAI,EAAE,yBAAyB,GAC9B,gBAAgB,CAAC,CAAC,CAAC,CAErB;AAED,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,WAAW,CAAA;IAC1B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IACrB,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAC1B,OAAO,EAAE,MAAM,IAAI,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;IAC7B,YAAY,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAA;IAC/C,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,yBAAyB,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAKrE;AAED,eAAO,MAAM,WAAW;;;;;CAAqC,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { init, update, connect as dialogConnect, overlay as dialogOverlay, } from './dialog';
|
|
2
|
+
export { init, update };
|
|
3
|
+
export function connect(get, send, opts) {
|
|
4
|
+
return dialogConnect(get, send, { ...opts, role: 'alertdialog' });
|
|
5
|
+
}
|
|
6
|
+
export function overlay(opts) {
|
|
7
|
+
return dialogOverlay({
|
|
8
|
+
...opts,
|
|
9
|
+
closeOnOutsideClick: opts.closeOnOutsideClick ?? false,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export const alertDialog = { init, update, connect, overlay };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Send } from '@llui/dom';
|
|
2
|
+
/**
|
|
3
|
+
* Angle slider — a circular input that selects a value in 0..360 degrees
|
|
4
|
+
* by dragging a thumb around a control. The state machine tracks the
|
|
5
|
+
* current angle; the view layer computes angles from pointer positions
|
|
6
|
+
* (helpers exported for that purpose).
|
|
7
|
+
*
|
|
8
|
+
* Typical view wiring: on pointerdown/pointermove, read the control
|
|
9
|
+
* element's bounding rect, compute the angle from `(pointerX, pointerY)`
|
|
10
|
+
* to the rect center via `angleFromPoint()`, and dispatch `setValue`.
|
|
11
|
+
*
|
|
12
|
+
* Keyboard: Arrow keys adjust by `step`; Home/End jump to min/max;
|
|
13
|
+
* PageUp/PageDown adjust by `step * 10`.
|
|
14
|
+
*/
|
|
15
|
+
export interface AngleSliderState {
|
|
16
|
+
value: number;
|
|
17
|
+
min: number;
|
|
18
|
+
max: number;
|
|
19
|
+
step: number;
|
|
20
|
+
disabled: boolean;
|
|
21
|
+
readOnly: boolean;
|
|
22
|
+
}
|
|
23
|
+
export type AngleSliderMsg = {
|
|
24
|
+
type: 'setValue';
|
|
25
|
+
value: number;
|
|
26
|
+
} | {
|
|
27
|
+
type: 'increment';
|
|
28
|
+
steps?: number;
|
|
29
|
+
} | {
|
|
30
|
+
type: 'decrement';
|
|
31
|
+
steps?: number;
|
|
32
|
+
} | {
|
|
33
|
+
type: 'setMin';
|
|
34
|
+
min: number;
|
|
35
|
+
} | {
|
|
36
|
+
type: 'setMax';
|
|
37
|
+
max: number;
|
|
38
|
+
};
|
|
39
|
+
export interface AngleSliderInit {
|
|
40
|
+
value?: number;
|
|
41
|
+
min?: number;
|
|
42
|
+
max?: number;
|
|
43
|
+
step?: number;
|
|
44
|
+
disabled?: boolean;
|
|
45
|
+
readOnly?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export declare function init(opts?: AngleSliderInit): AngleSliderState;
|
|
48
|
+
export declare function update(state: AngleSliderState, msg: AngleSliderMsg): [AngleSliderState, never[]];
|
|
49
|
+
/**
|
|
50
|
+
* Compute the angle in degrees from the center of a rect to a point.
|
|
51
|
+
* 0° = up (12 o'clock), increases clockwise. Result is in 0..360.
|
|
52
|
+
*
|
|
53
|
+
* Useful inside a pointermove handler:
|
|
54
|
+
* const rect = control.getBoundingClientRect()
|
|
55
|
+
* const angle = angleFromPoint(rect, e.clientX, e.clientY)
|
|
56
|
+
* send({ type: 'setValue', value: angle })
|
|
57
|
+
*/
|
|
58
|
+
export declare function angleFromPoint(rect: DOMRect, x: number, y: number): number;
|
|
59
|
+
/** Convert an angle to (x, y) on a unit circle (radius 1 at origin). */
|
|
60
|
+
export declare function pointFromAngle(angleDeg: number): {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
};
|
|
64
|
+
export interface AngleSliderParts<S> {
|
|
65
|
+
root: {
|
|
66
|
+
role: 'slider';
|
|
67
|
+
'aria-valuemin': (s: S) => number;
|
|
68
|
+
'aria-valuemax': (s: S) => number;
|
|
69
|
+
'aria-valuenow': (s: S) => number;
|
|
70
|
+
'aria-valuetext': (s: S) => string;
|
|
71
|
+
'aria-orientation': 'horizontal';
|
|
72
|
+
'aria-disabled': (s: S) => 'true' | undefined;
|
|
73
|
+
'aria-readonly': (s: S) => 'true' | undefined;
|
|
74
|
+
tabIndex: (s: S) => number;
|
|
75
|
+
'data-scope': 'angle-slider';
|
|
76
|
+
'data-part': 'root';
|
|
77
|
+
'data-disabled': (s: S) => '' | undefined;
|
|
78
|
+
onKeyDown: (e: KeyboardEvent) => void;
|
|
79
|
+
};
|
|
80
|
+
control: {
|
|
81
|
+
'data-scope': 'angle-slider';
|
|
82
|
+
'data-part': 'control';
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* The draggable thumb element. Its position is typically computed via
|
|
86
|
+
* CSS custom properties `--angle` (0..360) that the consumer sets from
|
|
87
|
+
* `state.value` using pointFromAngle() or a CSS `transform: rotate()`.
|
|
88
|
+
*/
|
|
89
|
+
thumb: {
|
|
90
|
+
'data-scope': 'angle-slider';
|
|
91
|
+
'data-part': 'thumb';
|
|
92
|
+
'data-value': (s: S) => string;
|
|
93
|
+
};
|
|
94
|
+
valueText: {
|
|
95
|
+
'data-scope': 'angle-slider';
|
|
96
|
+
'data-part': 'value-text';
|
|
97
|
+
};
|
|
98
|
+
/** A hidden input for form participation. */
|
|
99
|
+
hiddenInput: {
|
|
100
|
+
type: 'hidden';
|
|
101
|
+
value: (s: S) => string;
|
|
102
|
+
name?: string;
|
|
103
|
+
'data-scope': 'angle-slider';
|
|
104
|
+
'data-part': 'hidden-input';
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export interface ConnectOptions {
|
|
108
|
+
/** Name for the hidden input (form integration). */
|
|
109
|
+
name?: string;
|
|
110
|
+
/** Formatter for aria-valuetext (default: "{value}°"). */
|
|
111
|
+
format?: (value: number) => string;
|
|
112
|
+
}
|
|
113
|
+
export declare function connect<S>(get: (s: S) => AngleSliderState, send: Send<AngleSliderMsg>, opts?: ConnectOptions): AngleSliderParts<S>;
|
|
114
|
+
export declare const angleSlider: {
|
|
115
|
+
init: typeof init;
|
|
116
|
+
update: typeof update;
|
|
117
|
+
connect: typeof connect;
|
|
118
|
+
angleFromPoint: typeof angleFromPoint;
|
|
119
|
+
pointFromAngle: typeof pointFromAngle;
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=angle-slider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"angle-slider.d.ts","sourceRoot":"","sources":["../../src/components/angle-slider.ts"],"names":[],"mappings":"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"}
|