@data-slot/dropdown-menu 0.2.163 → 0.2.165
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/README.md +231 -122
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +81 -8
- package/dist/index.d.ts +81 -8
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @data-slot/dropdown-menu
|
|
2
2
|
|
|
3
|
-
Headless dropdown menu
|
|
3
|
+
Headless dropdown menu for vanilla JavaScript. Supports action items, single-select radio items, and multi-select checkbox items with full keyboard navigation and ARIA support.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,22 +10,47 @@ npm install @data-slot/dropdown-menu
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
###
|
|
13
|
+
### Action Menu
|
|
14
14
|
|
|
15
15
|
```html
|
|
16
16
|
<div data-slot="dropdown-menu">
|
|
17
|
-
<button data-slot="dropdown-menu-trigger">
|
|
17
|
+
<button data-slot="dropdown-menu-trigger">Actions</button>
|
|
18
18
|
<div data-slot="dropdown-menu-content">
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
<button data-slot="dropdown-menu-item" data-value="edit">Edit</button>
|
|
20
|
+
<button data-slot="dropdown-menu-item" data-value="copy">Copy</button>
|
|
21
|
+
<button data-slot="dropdown-menu-item" data-variant="destructive" data-value="delete">
|
|
22
|
+
Delete
|
|
23
|
+
</button>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Single-Select Menu
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<div data-slot="dropdown-menu" data-default-value="pro">
|
|
32
|
+
<button data-slot="dropdown-menu-trigger">Plan</button>
|
|
33
|
+
<div data-slot="dropdown-menu-content">
|
|
34
|
+
<button data-slot="dropdown-menu-radio-item" data-value="starter">Starter</button>
|
|
35
|
+
<button data-slot="dropdown-menu-radio-item" data-value="pro">Pro</button>
|
|
36
|
+
<button data-slot="dropdown-menu-radio-item" data-value="team">Team</button>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Multi-Select Menu
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<div
|
|
45
|
+
data-slot="dropdown-menu"
|
|
46
|
+
data-close-on-select="false"
|
|
47
|
+
data-default-values='["email","push"]'
|
|
48
|
+
>
|
|
49
|
+
<button data-slot="dropdown-menu-trigger">Channels</button>
|
|
50
|
+
<div data-slot="dropdown-menu-content">
|
|
51
|
+
<button data-slot="dropdown-menu-checkbox-item" data-value="email">Email</button>
|
|
52
|
+
<button data-slot="dropdown-menu-checkbox-item" data-value="sms">SMS</button>
|
|
53
|
+
<button data-slot="dropdown-menu-checkbox-item" data-value="push">Push</button>
|
|
29
54
|
</div>
|
|
30
55
|
</div>
|
|
31
56
|
```
|
|
@@ -35,24 +60,49 @@ npm install @data-slot/dropdown-menu
|
|
|
35
60
|
```js
|
|
36
61
|
import { create, createDropdownMenu } from "@data-slot/dropdown-menu";
|
|
37
62
|
|
|
38
|
-
// Auto-bind all dropdown menus in the document
|
|
39
63
|
const controllers = create();
|
|
40
64
|
|
|
41
|
-
// Or bind a specific element
|
|
42
65
|
const root = document.querySelector('[data-slot="dropdown-menu"]');
|
|
43
66
|
const controller = createDropdownMenu(root, {
|
|
44
|
-
onOpenChange: (open) => console.log("
|
|
45
|
-
onSelect: (value) => console.log("
|
|
67
|
+
onOpenChange: (open) => console.log("open:", open),
|
|
68
|
+
onSelect: (value) => console.log("activation:", value),
|
|
69
|
+
onValueChange: (value) => console.log("radio value:", value),
|
|
70
|
+
onValuesChange: (values) => console.log("checkbox values:", values),
|
|
46
71
|
});
|
|
47
72
|
|
|
48
|
-
|
|
49
|
-
controller.
|
|
50
|
-
controller.
|
|
51
|
-
controller.toggle();
|
|
73
|
+
controller.set({ value: "pro" });
|
|
74
|
+
controller.set({ values: ["email", "push"] });
|
|
75
|
+
controller.set({ open: true, highlightedValue: "push" });
|
|
52
76
|
controller.destroy();
|
|
53
77
|
```
|
|
54
78
|
|
|
55
|
-
`createDropdownMenu(root)` is idempotent per root. Calling it again for the same element returns the existing controller
|
|
79
|
+
`createDropdownMenu(root)` is idempotent per root. Calling it again for the same element returns the existing controller.
|
|
80
|
+
|
|
81
|
+
## Controller
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
interface DropdownMenuController {
|
|
85
|
+
open(): void;
|
|
86
|
+
close(): void;
|
|
87
|
+
toggle(): void;
|
|
88
|
+
set(detail: DropdownMenuSetDetail): void;
|
|
89
|
+
readonly isOpen: boolean;
|
|
90
|
+
readonly value: string | null;
|
|
91
|
+
readonly values: string[];
|
|
92
|
+
readonly highlightedValue: string | null;
|
|
93
|
+
destroy(): void;
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`set()` applies fields in this order: `value`, `values`, `open`, `highlightedValue`.
|
|
98
|
+
|
|
99
|
+
- `set({ value })` commits radio selection.
|
|
100
|
+
- `set({ values })` commits checkbox selection.
|
|
101
|
+
- `set({ open })` opens or closes the menu.
|
|
102
|
+
- `set({ highlightedValue })` updates highlight only while the menu is open.
|
|
103
|
+
- Programmatic `set()` never emits `dropdown-menu:select`.
|
|
104
|
+
- No-op updates are silent.
|
|
105
|
+
- Unknown `value` / `values` targets are ignored, not thrown.
|
|
56
106
|
|
|
57
107
|
## Slots
|
|
58
108
|
|
|
@@ -60,16 +110,18 @@ controller.destroy();
|
|
|
60
110
|
|------|-------------|
|
|
61
111
|
| `dropdown-menu` | Root container |
|
|
62
112
|
| `dropdown-menu-trigger` | Button that opens the menu |
|
|
63
|
-
| `dropdown-menu-content` |
|
|
113
|
+
| `dropdown-menu-content` | Menu panel |
|
|
114
|
+
| `dropdown-menu-item` | Action item with no owned selection state |
|
|
115
|
+
| `dropdown-menu-radio-item` | Single-select menu item |
|
|
116
|
+
| `dropdown-menu-checkbox-item` | Multi-select menu item |
|
|
64
117
|
| `dropdown-menu-group` | Groups related items |
|
|
65
|
-
| `dropdown-menu-label` | Non-interactive label
|
|
66
|
-
| `dropdown-menu-item` | Clickable menu item |
|
|
118
|
+
| `dropdown-menu-label` | Non-interactive label |
|
|
67
119
|
| `dropdown-menu-separator` | Visual divider |
|
|
68
120
|
| `dropdown-menu-shortcut` | Keyboard shortcut hint |
|
|
69
|
-
| `dropdown-menu-positioner` | Optional authored positioning wrapper
|
|
70
|
-
| `dropdown-menu-portal` | Optional authored portal wrapper that
|
|
121
|
+
| `dropdown-menu-positioner` | Optional authored positioning wrapper |
|
|
122
|
+
| `dropdown-menu-portal` | Optional authored portal wrapper that contains `dropdown-menu-positioner` |
|
|
71
123
|
|
|
72
|
-
### Composed Portal Markup
|
|
124
|
+
### Composed Portal Markup
|
|
73
125
|
|
|
74
126
|
```html
|
|
75
127
|
<div data-slot="dropdown-menu">
|
|
@@ -82,166 +134,223 @@ controller.destroy();
|
|
|
82
134
|
</div>
|
|
83
135
|
```
|
|
84
136
|
|
|
85
|
-
## Data Attributes
|
|
137
|
+
## State and Data Attributes
|
|
138
|
+
|
|
139
|
+
### Root and Content
|
|
86
140
|
|
|
87
|
-
| Attribute |
|
|
141
|
+
| Attribute | Target | Description |
|
|
88
142
|
|-----------|--------|-------------|
|
|
89
|
-
| `data-state` |
|
|
90
|
-
| `data-
|
|
91
|
-
| `data-
|
|
92
|
-
| `data-
|
|
93
|
-
| `data-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
143
|
+
| `data-state="open|closed"` | root, content | Current open state |
|
|
144
|
+
| `data-open` / `data-closed` | root, content | Presence aliases for state styling |
|
|
145
|
+
| `data-value="..."` | root | Current committed radio value only |
|
|
146
|
+
| `data-side` | content, positioner | Computed side after collision handling |
|
|
147
|
+
| `data-align` | content, positioner | Computed alignment after collision handling |
|
|
148
|
+
|
|
149
|
+
### Items
|
|
150
|
+
|
|
151
|
+
| Attribute | Target | Description |
|
|
152
|
+
|-----------|--------|-------------|
|
|
153
|
+
| `data-highlighted` | item | Current highlighted item |
|
|
154
|
+
| `data-checked` | radio, checkbox items | Current committed checked state |
|
|
155
|
+
| `data-disabled` | item | Disabled item |
|
|
156
|
+
| `data-variant` | item | Styling hook such as `destructive` |
|
|
157
|
+
| `data-inset` | item | Styling hook for left padding |
|
|
158
|
+
|
|
159
|
+
### Defaults and Options
|
|
160
|
+
|
|
161
|
+
| Attribute | Target | Description |
|
|
162
|
+
|-----------|--------|-------------|
|
|
163
|
+
| `data-default-open` | root | Initial open state |
|
|
164
|
+
| `data-default-value` | root | Initial radio value |
|
|
165
|
+
| `data-default-values='["a","b"]'` | root | Initial checkbox values as a JSON array string |
|
|
166
|
+
| `data-default-checked` | radio, checkbox item | Item-level default checked state |
|
|
167
|
+
| `data-close-on-click-outside` | root | Close on outside interaction |
|
|
168
|
+
| `data-close-on-escape` | root | Close on Escape |
|
|
169
|
+
| `data-close-on-select` | root | Close after accepted activation |
|
|
170
|
+
| `data-highlight-item-on-hover` | root | Highlight and focus items on hover |
|
|
171
|
+
|
|
172
|
+
Default precedence is:
|
|
173
|
+
|
|
174
|
+
1. JavaScript options
|
|
175
|
+
2. Root data attributes
|
|
176
|
+
3. Item `data-default-checked`
|
|
177
|
+
4. Empty state
|
|
178
|
+
|
|
179
|
+
For radio items, root defaults win over item defaults. For checkbox items, root `data-default-values` wins over item `data-default-checked`.
|
|
97
180
|
|
|
98
181
|
## Keyboard Navigation
|
|
99
182
|
|
|
100
183
|
| Key | Action |
|
|
101
184
|
|-----|--------|
|
|
102
|
-
| `Enter` / `Space` | Open menu
|
|
103
|
-
| `ArrowDown` | Open menu
|
|
104
|
-
| `ArrowUp` | Move to previous item |
|
|
105
|
-
| `Home` | Move to first item |
|
|
106
|
-
| `End` | Move to last item |
|
|
185
|
+
| `Enter` / `Space` | Open menu from trigger, or activate highlighted item |
|
|
186
|
+
| `ArrowDown` | Open menu from trigger, or move to next enabled item |
|
|
187
|
+
| `ArrowUp` | Move to previous enabled item |
|
|
188
|
+
| `Home` | Move to first enabled item |
|
|
189
|
+
| `End` | Move to last enabled item |
|
|
107
190
|
| `Escape` | Close menu |
|
|
108
|
-
| `
|
|
191
|
+
| `Tab` | Close menu and continue tab order |
|
|
192
|
+
| `A-Z` | Typeahead by item text |
|
|
109
193
|
|
|
110
194
|
## Events
|
|
111
195
|
|
|
112
196
|
### Outbound Events
|
|
113
197
|
|
|
114
|
-
| Event | Detail |
|
|
115
|
-
|
|
116
|
-
| `dropdown-menu:change` | `{ open
|
|
117
|
-
| `dropdown-menu:
|
|
198
|
+
| Event | Detail | Notes |
|
|
199
|
+
|-------|--------|-------|
|
|
200
|
+
| `dropdown-menu:open-change` | `{ open, previousOpen, source, reason }` | Fires on real open-state changes |
|
|
201
|
+
| `dropdown-menu:change` | same detail | Deprecated alias for `open-change`; only `detail.open` is compatibility-guaranteed |
|
|
202
|
+
| `dropdown-menu:highlight-change` | `{ value, previousValue, item, previousItem, source }` | `value` and `item` become `null` when highlight clears |
|
|
203
|
+
| `dropdown-menu:select` | `{ value, item, itemType, source, checked? }` | Cancelable, user-only, fires before commit |
|
|
204
|
+
| `dropdown-menu:value-change` | `{ value, previousValue, item, previousItem, source }` | Radio commits only |
|
|
205
|
+
| `dropdown-menu:values-change` | `{ values, previousValues, changedValue, checked, item, source }` | Checkbox commits only |
|
|
206
|
+
|
|
207
|
+
`dropdown-menu:select` behavior:
|
|
208
|
+
|
|
209
|
+
- Fires only for user activation attempts. Disabled items and programmatic updates do not emit it.
|
|
210
|
+
- If `event.preventDefault()` is called, no selection state changes and no auto-close from selection occur.
|
|
211
|
+
- Programmatic updates never emit it.
|
|
212
|
+
|
|
213
|
+
`dropdown-menu:value-change`, `dropdown-menu:values-change`, `onValueChange`, and `onValuesChange` are silent on:
|
|
118
214
|
|
|
119
|
-
|
|
215
|
+
- initialization
|
|
216
|
+
- no-op commits
|
|
217
|
+
- ignored unknown targets
|
|
218
|
+
|
|
219
|
+
For `dropdown-menu:values-change`, `changedValue`, `checked`, and `item` are `null` when one programmatic update changes more than one checkbox at once.
|
|
220
|
+
|
|
221
|
+
### Inbound Event
|
|
120
222
|
|
|
121
223
|
| Event | Detail | Description |
|
|
122
224
|
|-------|--------|-------------|
|
|
123
|
-
| `dropdown-menu:set` | `
|
|
225
|
+
| `dropdown-menu:set` | `DropdownMenuSetDetail` | Partial programmatic state update |
|
|
124
226
|
|
|
125
|
-
```
|
|
126
|
-
// Open the menu
|
|
227
|
+
```js
|
|
127
228
|
root.dispatchEvent(
|
|
128
|
-
new CustomEvent("dropdown-menu:set", {
|
|
229
|
+
new CustomEvent("dropdown-menu:set", {
|
|
230
|
+
detail: { value: "pro", source: "restore" },
|
|
231
|
+
})
|
|
129
232
|
);
|
|
130
233
|
|
|
131
|
-
// Close the menu
|
|
132
234
|
root.dispatchEvent(
|
|
133
|
-
new CustomEvent("dropdown-menu:set", {
|
|
235
|
+
new CustomEvent("dropdown-menu:set", {
|
|
236
|
+
detail: { values: ["email", "push"], open: true },
|
|
237
|
+
})
|
|
134
238
|
);
|
|
135
239
|
```
|
|
136
240
|
|
|
137
|
-
|
|
241
|
+
### Event Order
|
|
138
242
|
|
|
139
|
-
|
|
243
|
+
User radio selection:
|
|
140
244
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
245
|
+
1. `dropdown-menu:select`
|
|
246
|
+
2. `dropdown-menu:value-change`
|
|
247
|
+
3. `dropdown-menu:open-change` if the menu closes
|
|
248
|
+
|
|
249
|
+
User checkbox selection:
|
|
250
|
+
|
|
251
|
+
1. `dropdown-menu:select`
|
|
252
|
+
2. `dropdown-menu:values-change`
|
|
253
|
+
3. `dropdown-menu:open-change` if the menu closes
|
|
254
|
+
|
|
255
|
+
Programmatic selection:
|
|
147
256
|
|
|
148
|
-
|
|
257
|
+
1. `dropdown-menu:set` or `controller.set(...)`
|
|
258
|
+
2. `dropdown-menu:value-change` or `dropdown-menu:values-change`
|
|
259
|
+
3. No `dropdown-menu:select`
|
|
260
|
+
|
|
261
|
+
If closing the menu also clears an existing highlight, `dropdown-menu:highlight-change` is emitted before the close-side `dropdown-menu:open-change`.
|
|
149
262
|
|
|
150
263
|
## Options
|
|
151
264
|
|
|
152
265
|
```ts
|
|
153
266
|
interface DropdownMenuOptions {
|
|
154
|
-
/** Initial open state */
|
|
155
267
|
defaultOpen?: boolean;
|
|
156
|
-
|
|
268
|
+
defaultValue?: string | null;
|
|
269
|
+
defaultValues?: string[];
|
|
157
270
|
onOpenChange?: (open: boolean) => void;
|
|
158
|
-
/** Callback when an item is selected */
|
|
159
271
|
onSelect?: (value: string) => void;
|
|
160
|
-
|
|
272
|
+
onValueChange?: (value: string | null) => void;
|
|
273
|
+
onValuesChange?: (values: string[]) => void;
|
|
161
274
|
closeOnClickOutside?: boolean;
|
|
162
|
-
/** Close when pressing Escape (default: true) */
|
|
163
275
|
closeOnEscape?: boolean;
|
|
164
|
-
/** Close when an item is selected (default: true) */
|
|
165
276
|
closeOnSelect?: boolean;
|
|
166
|
-
/** Highlight and focus items on pointer hover (default: true) */
|
|
167
277
|
highlightItemOnHover?: boolean;
|
|
168
|
-
|
|
169
|
-
// Positioning options (Radix-compatible)
|
|
170
|
-
/** Preferred side of trigger: "top" | "right" | "bottom" | "left" (default: "bottom") */
|
|
171
278
|
side?: "top" | "right" | "bottom" | "left";
|
|
172
|
-
/** Alignment against trigger: "start" | "center" | "end" (default: "start") */
|
|
173
279
|
align?: "start" | "center" | "end";
|
|
174
|
-
/** Distance from trigger in px (default: 4) */
|
|
175
280
|
sideOffset?: number;
|
|
176
|
-
/** Offset from alignment edge in px (default: 0) */
|
|
177
281
|
alignOffset?: number;
|
|
178
|
-
/** Flip/shift to stay in viewport (default: true) */
|
|
179
282
|
avoidCollisions?: boolean;
|
|
180
|
-
/** Viewport edge padding in px (default: 8) */
|
|
181
283
|
collisionPadding?: number;
|
|
284
|
+
lockScroll?: boolean;
|
|
182
285
|
}
|
|
183
286
|
```
|
|
184
287
|
|
|
185
|
-
|
|
288
|
+
Notes:
|
|
186
289
|
|
|
187
|
-
|
|
290
|
+
- `closeOnSelect` defaults to `true`. Multi-select menus usually want `false`.
|
|
291
|
+
- `onSelect` tracks accepted user activation. It does not fire for programmatic state changes.
|
|
292
|
+
- `onValueChange` and `onValuesChange` follow the same silence rules as their DOM events.
|
|
188
293
|
|
|
189
|
-
|
|
294
|
+
## Downstream Wrapper Contract
|
|
190
295
|
|
|
191
|
-
|
|
192
|
-
2. `dropdown-menu-content`
|
|
193
|
-
3. `dropdown-menu-positioner`
|
|
194
|
-
4. `dropdown-menu` root (fallback)
|
|
195
|
-
|
|
196
|
-
| Attribute | Type | Default | Description |
|
|
197
|
-
|-----------|------|---------|-------------|
|
|
198
|
-
| `data-default-open` | boolean | `false` | Initial open state |
|
|
199
|
-
| `data-close-on-click-outside` | boolean | `true` | Close when clicking outside |
|
|
200
|
-
| `data-close-on-escape` | boolean | `true` | Close when pressing Escape |
|
|
201
|
-
| `data-close-on-select` | boolean | `true` | Close when an item is selected |
|
|
202
|
-
| `data-highlight-item-on-hover` | boolean | `true` | Highlight and focus items on pointer hover |
|
|
203
|
-
| `data-side` | string | `"bottom"` | Preferred side: top, right, bottom, left |
|
|
204
|
-
| `data-align` | string | `"start"` | Alignment: start, center, end |
|
|
205
|
-
| `data-side-offset` | number | `4` | Distance from trigger in px |
|
|
206
|
-
| `data-align-offset` | number | `0` | Offset from alignment edge in px |
|
|
207
|
-
| `data-avoid-collisions` | boolean | `true` | Flip/shift to stay in viewport |
|
|
208
|
-
| `data-collision-padding` | number | `8` | Viewport edge padding in px |
|
|
209
|
-
|
|
210
|
-
Boolean attributes: present or `"true"` = true, `"false"` = false, absent = default.
|
|
296
|
+
This package does not ship Astro components, but downstream wrappers should mirror this authoring model:
|
|
211
297
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
</div>
|
|
298
|
+
- `DropdownMenuItem` renders `data-slot="dropdown-menu-item"`.
|
|
299
|
+
- `DropdownMenuRadioItem` renders `data-slot="dropdown-menu-radio-item"` and requires `value`.
|
|
300
|
+
- `DropdownMenuCheckboxItem` renders `data-slot="dropdown-menu-checkbox-item"` and requires `value`.
|
|
301
|
+
- Radio and checkbox wrappers may expose `defaultChecked`, but root defaults still take precedence over item defaults.
|
|
217
302
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
303
|
+
## Deprecated APIs
|
|
304
|
+
|
|
305
|
+
The following compatibility APIs are deprecated and will be removed in the next major release:
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
// Deprecated open-state alias
|
|
309
|
+
root.addEventListener("dropdown-menu:change", (event) => {
|
|
310
|
+
console.log(event.detail.open);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Deprecated programmatic shape
|
|
314
|
+
root.dispatchEvent(
|
|
315
|
+
new CustomEvent("dropdown-menu:set", { detail: { value: true } })
|
|
316
|
+
);
|
|
222
317
|
```
|
|
223
318
|
|
|
319
|
+
Use `dropdown-menu:open-change` and `dropdown-menu:set { open: boolean }` instead.
|
|
320
|
+
|
|
321
|
+
## Migration Notes
|
|
322
|
+
|
|
323
|
+
If you currently use dropdown-menu as a picker:
|
|
324
|
+
|
|
325
|
+
- Replace picker-style `dropdown-menu-item` usage with `dropdown-menu-radio-item` or `dropdown-menu-checkbox-item`.
|
|
326
|
+
- Stop manually writing `data-selected` or `data-checked`.
|
|
327
|
+
- Listen to `dropdown-menu:value-change` or `dropdown-menu:values-change` for committed state.
|
|
328
|
+
- Push external restore/randomize/popstate/storage changes back in through `controller.set(...)` or `dropdown-menu:set`.
|
|
329
|
+
- Switch open-state listeners from `dropdown-menu:change` to `dropdown-menu:open-change`.
|
|
330
|
+
|
|
224
331
|
## Positioning
|
|
225
332
|
|
|
226
|
-
|
|
333
|
+
Placement attributes (`data-side`, `data-align`, `data-side-offset`, `data-align-offset`, `data-avoid-collisions`, `data-collision-padding`) resolve in this order:
|
|
334
|
+
|
|
335
|
+
1. JavaScript option
|
|
336
|
+
2. `dropdown-menu-content`
|
|
337
|
+
3. `dropdown-menu-positioner`
|
|
338
|
+
4. `dropdown-menu` root
|
|
339
|
+
|
|
340
|
+
The dropdown menu uses `position: fixed` by default and automatically positions itself relative to the trigger:
|
|
227
341
|
|
|
228
342
|
```js
|
|
229
343
|
createDropdownMenu(root, {
|
|
230
|
-
side: "bottom",
|
|
231
|
-
align: "start",
|
|
232
|
-
sideOffset: 4,
|
|
233
|
-
alignOffset: 0,
|
|
344
|
+
side: "bottom",
|
|
345
|
+
align: "start",
|
|
346
|
+
sideOffset: 4,
|
|
347
|
+
alignOffset: 0,
|
|
234
348
|
avoidCollisions: true,
|
|
235
349
|
collisionPadding: 8,
|
|
236
350
|
});
|
|
237
351
|
```
|
|
238
352
|
|
|
239
|
-
When `avoidCollisions` is enabled
|
|
240
|
-
- Flip to the opposite side if it would overflow the viewport
|
|
241
|
-
- Shift/clamp to stay within the viewport with the specified padding
|
|
242
|
-
|
|
243
|
-
The content element receives `data-side` (computed, may flip) and `data-align` (requested, position may shift) attributes, useful for animations.
|
|
244
|
-
The positioned element (`dropdown-menu-positioner`, or `dropdown-menu-content` when no positioner is used) also receives `--transform-origin` for scale/zoom animation origins.
|
|
353
|
+
When `avoidCollisions` is enabled, the menu may flip sides or shift within the viewport. The positioned element also receives `--transform-origin` for animation origins.
|
|
245
354
|
|
|
246
355
|
## License
|
|
247
356
|
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@data-slot/core`);const t=[`top`,`right`,`bottom`,`left`],n=[`start`,`center`,`end`],r=`@data-slot/dropdown-menu`;function i(i,a={}){let o=(0,e.reuseRootBinding)(i,r,`[@data-slot/dropdown-menu] createDropdownMenu() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(o)return o;let s=(0,e.getPart)(i,`dropdown-menu-trigger`),c=(0,e.getPart)(i,`dropdown-menu-content`),l=(0,e.getPart)(i,`dropdown-menu-positioner`),u=l&&c&&l.contains(c)?l:null,d=(0,e.getPart)(i,`dropdown-menu-portal`),f=d&&u&&d.contains(u)?d:null;if(!s||!c)throw Error(`DropdownMenu requires trigger and content slots`);let p=a.defaultOpen??(0,e.getDataBool)(i,`defaultOpen`)??!1,m=a.onOpenChange,h=a.onSelect,ee=a.closeOnClickOutside??(0,e.getDataBool)(i,`closeOnClickOutside`)??!0,g=a.closeOnEscape??(0,e.getDataBool)(i,`closeOnEscape`)??!0,_=a.closeOnSelect??(0,e.getDataBool)(i,`closeOnSelect`)??!0,v=(t,n)=>(0,e.getDataEnum)(c,t,n)??(u?(0,e.getDataEnum)(u,t,n):void 0)??(0,e.getDataEnum)(i,t,n),y=t=>(0,e.getDataNumber)(c,t)??(u?(0,e.getDataNumber)(u,t):void 0)??(0,e.getDataNumber)(i,t),te=t=>(0,e.getDataBool)(c,t)??(u?(0,e.getDataBool)(u,t):void 0)??(0,e.getDataBool)(i,t),b=a.side??v(`side`,t)??`bottom`,x=a.align??v(`align`,n)??`start`,S=a.sideOffset??y(`sideOffset`)??4,C=a.alignOffset??y(`alignOffset`)??0,ne=a.avoidCollisions??te(`avoidCollisions`)??!0,re=a.collisionPadding??y(`collisionPadding`)??8,w=a.lockScroll??(0,e.getDataBool)(i,`lockScroll`)??!0,T=a.highlightItemOnHover??(0,e.getDataBool)(i,`highlightItemOnHover`)??!0,E=!1,D=null,O=-1,k=``,A=null,j=!1,M=[],N=!1,P=(0,e.createPortalLifecycle)({content:c,root:i,wrapperSlot:u?void 0:`dropdown-menu-positioner`,container:u??void 0,mountTarget:u?f??u:void 0}),F=!1,I=[],L=[],R=new Map,z=e=>e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,B=e=>e.pointerType!==`touch`,V=(0,e.ensureId)(s,`dropdown-menu-trigger`),H=(0,e.ensureId)(c,`dropdown-menu-content`);s.setAttribute(`aria-haspopup`,`menu`),s.setAttribute(`aria-controls`,H),c.setAttribute(`role`,`menu`),c.setAttribute(`aria-labelledby`,V),c.tabIndex=-1;let U=()=>{I=(0,e.getParts)(c,`dropdown-menu-item`);for(let e of I)e.setAttribute(`role`,`menuitem`),e.hasAttribute(`data-disabled`)||e.hasAttribute(`disabled`)?e.setAttribute(`aria-disabled`,`true`):e.removeAttribute(`aria-disabled`),e.tabIndex=-1;L=I.filter(e=>!z(e)),R=new Map(L.map((e,t)=>[e,t]))},W=()=>{let t=P.container,n=i.ownerDocument.defaultView??window,r=s.getBoundingClientRect(),a=(0,e.computeFloatingPosition)({anchorRect:r,contentRect:(0,e.measurePopupContentRect)(c),side:b,align:x,sideOffset:S,alignOffset:C,avoidCollisions:ne,collisionPadding:re}),o=(0,e.computeFloatingTransformOrigin)({side:a.side,align:a.align,anchorRect:r,popupX:a.x,popupY:a.y});w?(t.style.position=`fixed`,t.style.top=`0px`,t.style.left=`0px`,t.style.transform=`translate3d(${a.x}px, ${a.y}px, 0)`):(t.style.position=`absolute`,t.style.top=`0px`,t.style.left=`0px`,t.style.transform=`translate3d(${a.x+n.scrollX}px, ${a.y+n.scrollY}px, 0)`),t.style.setProperty(`--transform-origin`,o),t.style.willChange=`transform`,t.style.margin=`0`,c.setAttribute(`data-side`,a.side),c.setAttribute(`data-align`,a.align),t!==c&&(t.setAttribute(`data-side`,a.side),t.setAttribute(`data-align`,a.align))},G=(0,e.createPositionSync)({observedElements:[s,c],isActive:()=>E,ancestorScroll:w,onUpdate:W}),K=(t,n=!0)=>{for(let r=0;r<L.length;r++){let i=L[r];r===t?(i.setAttribute(`data-highlighted`,``),(0,e.ensureItemVisibleInContainer)(i,c),n&&i.focus()):i.removeAttribute(`data-highlighted`)}O=t},q=()=>{for(let e of I)e.removeAttribute(`data-highlighted`);O=-1},J=()=>{q(),(0,e.focusElement)(c)},Y=e=>{i.setAttribute(`data-state`,e),c.setAttribute(`data-state`,e),e===`open`?(i.setAttribute(`data-open`,``),c.setAttribute(`data-open`,``),i.removeAttribute(`data-closed`),c.removeAttribute(`data-closed`)):(i.setAttribute(`data-closed`,``),c.setAttribute(`data-closed`,``),i.removeAttribute(`data-open`),c.removeAttribute(`data-open`))},ie=()=>{requestAnimationFrame(()=>{D&&document.contains(D)?(0,e.focusElement)(D):s&&document.contains(s)&&(0,e.focusElement)(s),D=null})},X=(0,e.createPresenceLifecycle)({element:c,onExitComplete:()=>{F||(P.restore(),c.hidden=!0,ie())}}),Z=t=>{E!==t&&(t?(D=document.activeElement,E=!0,(0,e.setAria)(s,`expanded`,!0),P.mount(),c.hidden=!1,Y(`open`),X.enter(),w&&!N&&((0,e.lockScroll)(),N=!0),U(),j=!1,q(),G.start(),W(),G.update(),c.focus()):(E=!1,(0,e.setAria)(s,`expanded`,!1),Y(`closed`),q(),k=``,j=!1,N&&=((0,e.unlockScroll)(),!1),G.stop(),X.exit()),(0,e.emit)(i,`dropdown-menu:change`,{open:E}),m?.(E))},Q=t=>{if(z(t))return;let n=t.dataset.value||t.textContent?.trim()||``;(0,e.emit)(i,`dropdown-menu:select`,{value:n}),h?.(n),_&&Z(!1)},ae=e=>{let t=L.length;if(t!==0)switch(e.key){case`ArrowDown`:e.preventDefault(),j=!0,K(O===-1?0:(O+1)%t);break;case`ArrowUp`:e.preventDefault(),j=!0,K(O===-1?t-1:(O-1+t)%t);break;case`Home`:e.preventDefault(),j=!0,K(0);break;case`End`:e.preventDefault(),j=!0,K(t-1);break;case`Enter`:case` `:e.preventDefault(),O>=0&&Q(L[O]);break;case`Tab`:Z(!1);break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.preventDefault(),oe(e.key.toLowerCase()))}},oe=e=>{A&&clearTimeout(A),A=setTimeout(()=>{k=``},500),k+=e;let t=L.findIndex(e=>(e.textContent?.trim().toLowerCase()||``).startsWith(k));if(t===-1&&k.length===1){let n=O+1;for(let r=0;r<L.length;r++){let i=(n+r)%L.length;if((L[i].textContent?.trim().toLowerCase()||``).startsWith(e)){t=i;break}}}t!==-1&&(j=!0,K(t))};(0,e.setAria)(s,`expanded`,!1),c.hidden=!0,Y(`closed`),M.push((0,e.on)(s,`click`,()=>Z(!E)),(0,e.on)(s,`keydown`,e=>{(e.key===`Enter`||e.key===` `||e.key===`ArrowDown`)&&!E&&(e.preventDefault(),Z(!0))})),M.push((0,e.on)(c,`keydown`,ae),(0,e.on)(c,`click`,e=>{let t=e.target.closest?.(`[data-slot="dropdown-menu-item"]`);t&&Q(t)}),(0,e.on)(c,`pointermove`,e=>{if(!T||!B(e))return;let t=e.target.closest?.(`[data-slot="dropdown-menu-item"]`);if(!(j&&(j=!1,t&&R.get(t)===O)))if(t&&!z(t)){let e=R.get(t);e!==void 0&&e!==O&&K(e,!0)}else J()}),(0,e.on)(c,`pointerleave`,e=>{!T||!B(e)||j||J()})),M.push((0,e.createDismissLayer)({root:i,isOpen:()=>E,onDismiss:()=>Z(!1),closeOnClickOutside:ee,closeOnEscape:g})),M.push((0,e.on)(i,`dropdown-menu:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&Z(n)}));let $={open:()=>Z(!0),close:()=>Z(!1),toggle:()=>Z(!E),get isOpen(){return E},destroy:()=>{F=!0,A&&clearTimeout(A),G.stop(),X.cleanup(),P.cleanup(),N&&=((0,e.unlockScroll)(),!1),M.forEach(e=>e()),M.length=0,(0,e.clearRootBinding)(i,r,$)}};return(0,e.setRootBinding)(i,r,$),p&&Z(!0),$}function a(t=document){let n=[];for(let a of(0,e.getRoots)(t,`dropdown-menu`))(0,e.hasRootBinding)(a,r)||n.push(i(a));return n}exports.create=a,exports.createDropdownMenu=i;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@data-slot/core`);const t=[`top`,`right`,`bottom`,`left`],n=[`start`,`center`,`end`],r=`@data-slot/dropdown-menu`,i=`[data-slot="dropdown-menu-item"], [data-slot="dropdown-menu-radio-item"], [data-slot="dropdown-menu-checkbox-item"]`,a=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o=(e,t,n)=>{n?e.setAttribute(t,``):e.removeAttribute(t)},s=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0},c=(e,t,n,r=!1)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:r,detail:n})),ee=e=>{switch(e){case`radio`:return`menuitemradio`;case`checkbox`:return`menuitemcheckbox`;default:return`menuitem`}},te=e=>{let t=e.getAttribute(`data-slot`);return t===`dropdown-menu-radio-item`?`radio`:t===`dropdown-menu-checkbox-item`?`checkbox`:`item`},ne=e=>{let t=e.dataset.value;if(t===void 0)return null;let n=t.trim();return n.length>0?n:null},re=e=>e.type===`item`?e.value?e.value:e.el.textContent?.trim()??``:e.value??``,ie=e=>{if(e===void 0)return[];let t=e.trim();if(t.length===0)return[];try{let e=JSON.parse(t);return Array.isArray(e)?e.filter(e=>typeof e==`string`).map(e=>e.trim()).filter(e=>e.length>0):[]}catch{return[]}};function l(l,u={}){let ae=(0,e.reuseRootBinding)(l,r,`[@data-slot/dropdown-menu] createDropdownMenu() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(ae)return ae;let d=(0,e.getPart)(l,`dropdown-menu-trigger`),f=(0,e.getPart)(l,`dropdown-menu-content`),p=(0,e.getPart)(l,`dropdown-menu-positioner`),m=p&&f&&p.contains(f)?p:null,h=(0,e.getPart)(l,`dropdown-menu-portal`),oe=h&&m&&h.contains(m)?h:null;if(!d||!f)throw Error(`DropdownMenu requires trigger and content slots`);let se=u.defaultOpen??(0,e.getDataBool)(l,`defaultOpen`)??!1,ce=u.onOpenChange,le=u.onSelect,ue=u.onValueChange,de=u.onValuesChange,g=u.closeOnClickOutside??(0,e.getDataBool)(l,`closeOnClickOutside`)??!0,fe=u.closeOnEscape??(0,e.getDataBool)(l,`closeOnEscape`)??!0,pe=u.closeOnSelect??(0,e.getDataBool)(l,`closeOnSelect`)??!0,me=(t,n)=>(0,e.getDataEnum)(f,t,n)??(m?(0,e.getDataEnum)(m,t,n):void 0)??(0,e.getDataEnum)(l,t,n),_=t=>(0,e.getDataNumber)(f,t)??(m?(0,e.getDataNumber)(m,t):void 0)??(0,e.getDataNumber)(l,t),he=t=>(0,e.getDataBool)(f,t)??(m?(0,e.getDataBool)(m,t):void 0)??(0,e.getDataBool)(l,t),ge=u.side??me(`side`,t)??`bottom`,_e=u.align??me(`align`,n)??`start`,ve=u.sideOffset??_(`sideOffset`)??4,ye=u.alignOffset??_(`alignOffset`)??0,be=u.avoidCollisions??he(`avoidCollisions`)??!0,xe=u.collisionPadding??_(`collisionPadding`)??8,v=u.lockScroll??(0,e.getDataBool)(l,`lockScroll`)??!0,Se=u.highlightItemOnHover??(0,e.getDataBool)(l,`highlightItemOnHover`)??!0,Ce=a(u,`defaultValue`),we=a(u,`defaultValues`),Te=l.hasAttribute(`data-default-value`),Ee=l.hasAttribute(`data-default-values`),De=Ce?u.defaultValue??null:Te?(0,e.getDataString)(l,`defaultValue`)??null:null,Oe=we?u.defaultValues??[]:Ee?ie((0,e.getDataString)(l,`defaultValues`)):[],y=!1,b=null,x=[],S=null,C=null,w=``,T=null,E=!1,D=!1,ke=!1,O=null,k=[],A=(0,e.createPortalLifecycle)({content:f,root:l,wrapperSlot:m?void 0:`dropdown-menu-positioner`,container:m??void 0,mountTarget:m?oe??m:void 0}),j=[],M=[],N=new Map,Ae=e=>e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,je=e=>e.pointerType!==`touch`,Me=e=>e instanceof Element?e.closest(i):null,P=e=>e?j.find(t=>t.el===e)??null:null,F=()=>j.filter(e=>e.type===`radio`),I=()=>j.filter(e=>e.type===`checkbox`),Ne=(e,t)=>e.find(e=>e.type===`radio`&&e.value===t)??null,L=e=>e?e.type===`item`?re(e):e.value:null,Pe=e=>(e.type===`radio`||e.type===`checkbox`)&&e.value===null,R=e=>Ae(e.el)||Pe(e),z=e=>F().find(t=>t.value===e)??null,Fe=e=>M.find(t=>L(t)===e)??null,Ie=(e,t,n=I())=>{let r=new Set(e),i=new Set(t),a=null,o=null,s=null;for(let e of n){if(e.type!==`checkbox`)continue;let t=e.value;if(!t)continue;let n=r.has(t),c=i.has(t);if(n!==c){if(a!==null)return{changedValue:null,checked:null,item:null};a=t,o=c,s=e.el}}return{changedValue:a,checked:o,item:s}},B=(e,t)=>{let n=I().filter(e=>e.value!==null);if(n.length===0)return null;if(e.length===0)return[];let r=new Set(e.filter(e=>typeof e==`string`).map(e=>e.trim()).filter(e=>e.length>0));if(r.size===0)return t===`init`?[]:null;let i=[];for(let e of n)e.value&&r.has(e.value)&&i.push(e.value);return i.length===0?t===`init`?[]:null:i},V=()=>{for(let t of j){let n=R(t);if(t.el.setAttribute(`role`,ee(t.type)),t.el.tabIndex=-1,n?t.el.setAttribute(`aria-disabled`,`true`):t.el.removeAttribute(`aria-disabled`),t.type===`radio`){let n=t.value!==null&&b===t.value;o(t.el,`data-checked`,n),(0,e.setAria)(t.el,`checked`,t.value===null?null:n)}else if(t.type===`checkbox`){let n=t.value!==null&&x.includes(t.value);o(t.el,`data-checked`,n),(0,e.setAria)(t.el,`checked`,t.value===null?null:n)}else t.el.removeAttribute(`data-checked`),t.el.removeAttribute(`aria-checked`)}F().length>0&&b!==null?l.setAttribute(`data-value`,b):l.removeAttribute(`data-value`)},Le=()=>{for(let e of j)o(e.el,`data-highlighted`,e.el===S)},H=({source:e=`programmatic`,emitSelectionInvalidation:t=!1}={})=>{let n=j,r=b,a=[...x];j=Array.from(f.querySelectorAll(i)).map(e=>({el:e,type:te(e),value:ne(e)}));let o=r!==null&&z(r)?r:null,c=a.length>0?B(a,`init`)??[]:[];if(b=o,x=c,M=j.filter(e=>!R(e)),N=new Map(M.map((e,t)=>[e.el,t])),S&&!N.has(S)&&(S=null),V(),Le(),t&&(r!==b&&Be({value:b,previousValue:r,item:b===null?null:z(b)?.el??null,previousItem:r===null?null:Ne(n,r)?.el??null,source:e}),!s(a,x))){let t=Ie(a,x,n.filter(e=>e.type===`checkbox`));Ve({values:[...x],previousValues:a,changedValue:t.changedValue,checked:t.checked,item:t.item,source:e})}},Re=t=>{(0,e.emit)(l,`dropdown-menu:open-change`,t),(0,e.emit)(l,`dropdown-menu:change`,t),ce?.(t.open)},ze=t=>{(0,e.emit)(l,`dropdown-menu:highlight-change`,t)},Be=t=>{(0,e.emit)(l,`dropdown-menu:value-change`,t),ue?.(t.value)},Ve=t=>{(0,e.emit)(l,`dropdown-menu:values-change`,t),de?.([...t.values])},He=()=>{let t=A.container,n=l.ownerDocument.defaultView??window,r=d.getBoundingClientRect(),i=(0,e.computeFloatingPosition)({anchorRect:r,contentRect:(0,e.measurePopupContentRect)(f),side:ge,align:_e,sideOffset:ve,alignOffset:ye,avoidCollisions:be,collisionPadding:xe}),a=(0,e.computeFloatingTransformOrigin)({side:i.side,align:i.align,anchorRect:r,popupX:i.x,popupY:i.y});v?(t.style.position=`fixed`,t.style.top=`0px`,t.style.left=`0px`,t.style.transform=`translate3d(${i.x}px, ${i.y}px, 0)`):(t.style.position=`absolute`,t.style.top=`0px`,t.style.left=`0px`,t.style.transform=`translate3d(${i.x+n.scrollX}px, ${i.y+n.scrollY}px, 0)`),t.style.setProperty(`--transform-origin`,a),t.style.willChange=`transform`,t.style.margin=`0`,f.setAttribute(`data-side`,i.side),f.setAttribute(`data-align`,i.align),t!==f&&(t.setAttribute(`data-side`,i.side),t.setAttribute(`data-align`,i.align))},U=(0,e.createPositionSync)({observedElements:[d,f],isActive:()=>y,ancestorScroll:v,onUpdate:He}),Ue=()=>{requestAnimationFrame(()=>{C&&document.contains(C)?(0,e.focusElement)(C):document.contains(d)&&(0,e.focusElement)(d),C=null})},W=(0,e.createPresenceLifecycle)({element:f,onExitComplete:()=>{ke||(A.restore(),f.hidden=!0,Ue())}}),G=e=>{l.setAttribute(`data-state`,e),f.setAttribute(`data-state`,e),e===`open`?(l.setAttribute(`data-open`,``),f.setAttribute(`data-open`,``),l.removeAttribute(`data-closed`),f.removeAttribute(`data-closed`)):(l.setAttribute(`data-closed`,``),f.setAttribute(`data-closed`,``),l.removeAttribute(`data-open`),f.removeAttribute(`data-open`))},K=(t,{source:n,focus:r=!0,focusContentOnClear:i=!1})=>{if(t&&!N.has(t))return!1;let a=S;return a===t?(t&&r?((0,e.ensureItemVisibleInContainer)(t,f),(0,e.focusElement)(t)):!t&&i&&(0,e.focusElement)(f),!1):(S=t,Le(),t?((0,e.ensureItemVisibleInContainer)(t,f),r&&(0,e.focusElement)(t)):i&&(0,e.focusElement)(f),ze({value:L(P(t)),previousValue:L(P(a)),item:t,previousItem:a,source:n}),!0)},q=(e,t,n=!0)=>{if(H({source:t,emitSelectionInvalidation:n}),F().length===0)return!1;let r=e===null?null:z(e);if(e!==null&&!r||b===e)return!1;let i=b,a=i===null?null:z(i);return b=e,V(),n&&Be({value:b,previousValue:i,item:r?.el??null,previousItem:a?.el??null,source:t}),!0},We=(e,t,n=!0)=>{H({source:t,emitSelectionInvalidation:n});let r=B(e,n?`set`:`init`);if(r===null||s(x,r))return!1;let i=[...x],a=Ie(i,r);return x=r,V(),n&&Ve({values:[...x],previousValues:i,changedValue:a.changedValue,checked:a.checked,item:a.item,source:t}),!0},Ge=()=>{if(H(),Ce||Te)De===null?b=null:q(De,`programmatic`,!1);else for(let t of F())if(t.value!==null&&(0,e.getDataBool)(t.el,`defaultChecked`)){b=t.value;break}x=we||Ee?B(Oe,`init`)??[]:B(I().filter(t=>t.value!==null&&(0,e.getDataBool)(t.el,`defaultChecked`)).map(e=>e.value),`init`)??[],V()},J=(t,{source:n,reason:r})=>{if(y===t)return;O=null;let i=y;t?(C=document.activeElement,y=!0,(0,e.setAria)(d,`expanded`,!0),A.mount(),f.hidden=!1,G(`open`),W.enter(),v&&!D&&((0,e.lockScroll)(),D=!0),H({source:n===`restore`?`restore`:`programmatic`,emitSelectionInvalidation:n!==`init`}),E=!1,w=``,U.start(),He(),U.update(),(0,e.focusElement)(f)):(y=!1,(0,e.setAria)(d,`expanded`,!1),G(`closed`),S&&K(null,{source:n===`init`?`programmatic`:n,focus:!1,focusContentOnClear:!1}),w=``,E=!1,D&&=((0,e.unlockScroll)(),!1),U.stop(),W.exit()),Re({open:y,previousOpen:i,source:n,reason:r})},Y=(e,t)=>{let n={source:e,reason:t};O=n,queueMicrotask(()=>{O===n&&(O=null)})},X=(e,t)=>{if(R(e))return;let n=L(e);if(n===null)return;let r;if(e.type===`radio`?r=!0:e.type===`checkbox`&&e.value!==null&&(r=!x.includes(e.value)),c(l,`dropdown-menu:select`,{value:n,item:e.el,itemType:e.type,source:t,checked:r},!0)){if(le?.(n),e.type===`radio`)q(e.value,t,!0);else if(e.type===`checkbox`&&e.value!==null){let n=new Set(x);n.has(e.value)?n.delete(e.value):n.add(e.value),We([...n],t,!0)}pe&&J(!1,{source:t,reason:`select`})}},Ke=e=>{T&&clearTimeout(T),T=setTimeout(()=>{w=``},500),w+=e;let t=M.findIndex(e=>(e.el.textContent?.trim().toLowerCase()??``).startsWith(w));if(t===-1&&w.length===1){let n=S?(N.get(S)??-1)+1:0;for(let r=0;r<M.length;r++){let i=(n+r)%M.length;if((M[i]?.el.textContent?.trim().toLowerCase()??``).startsWith(e)){t=i;break}}}t!==-1&&(E=!0,K(M[t]?.el??null,{source:`keyboard`,focus:!0}))},Z=e=>{let t=e.source??`programmatic`;if(e.value!==void 0&&q(e.value,t,!0),e.values!==void 0&&We(e.values,t,!0),e.open!==void 0&&J(e.open,{source:t,reason:`programmatic`}),e.highlightedValue!==void 0){if(!y)return;if(e.highlightedValue===null)K(null,{source:t,focus:!1,focusContentOnClear:!0});else{let n=Fe(e.highlightedValue);n&&K(n.el,{source:t,focus:!0})}}},qe=(0,e.ensureId)(d,`dropdown-menu-trigger`),Je=(0,e.ensureId)(f,`dropdown-menu-content`);d.setAttribute(`aria-haspopup`,`menu`),d.setAttribute(`aria-controls`,Je),f.setAttribute(`role`,`menu`),f.setAttribute(`aria-labelledby`,qe),f.tabIndex=-1,(0,e.setAria)(d,`expanded`,!1),f.hidden=!0,G(`closed`),Ge(),k.push((0,e.on)(d,`click`,()=>{J(!y,{source:`pointer`,reason:`trigger`})}),(0,e.on)(d,`keydown`,e=>{(e.key===`Enter`||e.key===` `||e.key===`ArrowDown`)&&!y&&(e.preventDefault(),J(!0,{source:`keyboard`,reason:`trigger`}))})),k.push((0,e.on)(f,`keydown`,e=>{if(e.key===`Tab`){J(!1,{source:`keyboard`,reason:`tab`});return}let t=M.length;if(t!==0)switch(e.key){case`ArrowDown`:e.preventDefault(),E=!0,K(M[S?((N.get(S)??-1)+1)%t:0]?.el??null,{source:`keyboard`,focus:!0});break;case`ArrowUp`:e.preventDefault(),E=!0,K(M[S?(N.get(S)-1+t)%t:t-1]?.el??null,{source:`keyboard`,focus:!0});break;case`Home`:e.preventDefault(),E=!0,K(M[0]?.el??null,{source:`keyboard`,focus:!0});break;case`End`:e.preventDefault(),E=!0,K(M[t-1]?.el??null,{source:`keyboard`,focus:!0});break;case`Enter`:case` `:if(e.preventDefault(),S){let e=P(S);e&&X(e,`keyboard`)}break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.preventDefault(),Ke(e.key.toLowerCase()))}}),(0,e.on)(f,`click`,e=>{let t=P(Me(e.target));t&&X(t,`pointer`)}),(0,e.on)(f,`pointermove`,e=>{if(!Se||!je(e))return;let t=Me(e.target);E&&(E=!1,t&&t===S)||(t&&N.has(t)?K(t,{source:`pointer`,focus:!0}):S&&K(null,{source:`pointer`,focus:!1,focusContentOnClear:!0}))}),(0,e.on)(f,`pointerleave`,e=>{!Se||!je(e)||E||!S||K(null,{source:`pointer`,focus:!1,focusContentOnClear:!0})}));let Q=l.ownerDocument??document;k.push((0,e.on)(Q,`pointerdown`,t=>{if(!y||!g||t.pointerType===`touch`)return;let n=t.target;(0,e.containsWithPortals)(l,n)||Y(`pointer`,`outside`)},{capture:!0}),(0,e.on)(Q,`click`,t=>{if(!y||!g)return;let n=t.target;(0,e.containsWithPortals)(l,n)||Y(`pointer`,`outside`)},{capture:!0}),(0,e.on)(Q,`keydown`,e=>{!y||!fe||e.key!==`Escape`||e.defaultPrevented||Y(`keyboard`,`escape`)},{capture:!0})),k.push((0,e.createDismissLayer)({root:l,isOpen:()=>y,onDismiss:()=>{let e=O;if(O=null,e?.reason===`escape`){J(!1,{source:e.source,reason:`escape`});return}J(!1,{source:e?.source??`pointer`,reason:e?.reason??`outside`})},closeOnClickOutside:g,closeOnEscape:fe})),k.push((0,e.on)(l,`dropdown-menu:set`,e=>{let t=e.detail;if(!t||typeof t!=`object`)return;let n={source:t.source===`restore`||t.source===`programmatic`?t.source:void 0};t.open!==void 0&&(n.open=t.open),t.values!==void 0&&(n.values=Array.isArray(t.values)?t.values.filter(e=>typeof e==`string`):void 0),t.highlightedValue!==void 0&&(n.highlightedValue=t.highlightedValue===null||typeof t.highlightedValue==`string`?t.highlightedValue:void 0),t.value!==void 0&&(typeof t.value==`boolean`&&t.open===void 0?n.open=t.value:(t.value===null||typeof t.value==`string`)&&(n.value=t.value)),Z(n)}));let $={open:()=>J(!0,{source:`programmatic`,reason:`programmatic`}),close:()=>J(!1,{source:`programmatic`,reason:`programmatic`}),toggle:()=>J(!y,{source:`programmatic`,reason:`programmatic`}),set:e=>{Z(e)},get isOpen(){return y},get value(){return b},get values(){return[...x]},get highlightedValue(){return L(P(S))},destroy:()=>{ke=!0,T&&clearTimeout(T),U.stop(),W.cleanup(),A.cleanup(),D&&=((0,e.unlockScroll)(),!1),k.forEach(e=>e()),k.length=0,(0,e.clearRootBinding)(l,r,$)}};return(0,e.setRootBinding)(l,r,$),se&&J(!0,{source:`init`,reason:`init`}),$}function u(t=document){let n=[];for(let i of(0,e.getRoots)(t,`dropdown-menu`))(0,e.hasRootBinding)(i,r)||n.push(l(i));return n}exports.create=u,exports.createDropdownMenu=l;
|
package/dist/index.d.cts
CHANGED
|
@@ -3,13 +3,69 @@
|
|
|
3
3
|
type Side = "top" | "right" | "bottom" | "left";
|
|
4
4
|
/** Alignment of the content relative to the trigger */
|
|
5
5
|
type Align = "start" | "center" | "end";
|
|
6
|
+
type DropdownMenuItemType = "item" | "radio" | "checkbox";
|
|
7
|
+
type DropdownMenuUserSource = "pointer" | "keyboard";
|
|
8
|
+
type DropdownMenuSetSource = "programmatic" | "restore";
|
|
9
|
+
type DropdownMenuSelectionSource = DropdownMenuUserSource | DropdownMenuSetSource;
|
|
10
|
+
type DropdownMenuOpenChangeSource = DropdownMenuSelectionSource | "init";
|
|
11
|
+
type DropdownMenuOpenChangeReason = "trigger" | "select" | "outside" | "escape" | "tab" | "programmatic" | "init";
|
|
12
|
+
interface DropdownMenuOpenChangeDetail {
|
|
13
|
+
open: boolean;
|
|
14
|
+
previousOpen: boolean;
|
|
15
|
+
source: DropdownMenuOpenChangeSource;
|
|
16
|
+
reason: DropdownMenuOpenChangeReason;
|
|
17
|
+
}
|
|
18
|
+
interface DropdownMenuHighlightChangeDetail {
|
|
19
|
+
value: string | null;
|
|
20
|
+
previousValue: string | null;
|
|
21
|
+
item: HTMLElement | null;
|
|
22
|
+
previousItem: HTMLElement | null;
|
|
23
|
+
source: DropdownMenuSelectionSource;
|
|
24
|
+
}
|
|
25
|
+
interface DropdownMenuSelectDetail {
|
|
26
|
+
value: string;
|
|
27
|
+
item: HTMLElement;
|
|
28
|
+
itemType: DropdownMenuItemType;
|
|
29
|
+
source: DropdownMenuUserSource;
|
|
30
|
+
checked?: boolean;
|
|
31
|
+
}
|
|
32
|
+
interface DropdownMenuValueChangeDetail {
|
|
33
|
+
value: string | null;
|
|
34
|
+
previousValue: string | null;
|
|
35
|
+
item: HTMLElement | null;
|
|
36
|
+
previousItem: HTMLElement | null;
|
|
37
|
+
source: DropdownMenuSelectionSource;
|
|
38
|
+
}
|
|
39
|
+
interface DropdownMenuValuesChangeDetail {
|
|
40
|
+
values: string[];
|
|
41
|
+
previousValues: string[];
|
|
42
|
+
changedValue: string | null;
|
|
43
|
+
checked: boolean | null;
|
|
44
|
+
item: HTMLElement | null;
|
|
45
|
+
source: DropdownMenuSelectionSource;
|
|
46
|
+
}
|
|
47
|
+
interface DropdownMenuSetDetail {
|
|
48
|
+
open?: boolean;
|
|
49
|
+
value?: string | null;
|
|
50
|
+
values?: string[];
|
|
51
|
+
highlightedValue?: string | null;
|
|
52
|
+
source?: DropdownMenuSetSource;
|
|
53
|
+
}
|
|
6
54
|
interface DropdownMenuOptions {
|
|
7
55
|
/** Initial open state */
|
|
8
56
|
defaultOpen?: boolean;
|
|
57
|
+
/** Initial radio selection state */
|
|
58
|
+
defaultValue?: string | null;
|
|
59
|
+
/** Initial checkbox selection state */
|
|
60
|
+
defaultValues?: string[];
|
|
9
61
|
/** Callback when open state changes */
|
|
10
62
|
onOpenChange?: (open: boolean) => void;
|
|
11
|
-
/** Callback when
|
|
63
|
+
/** Callback when a user activation is accepted */
|
|
12
64
|
onSelect?: (value: string) => void;
|
|
65
|
+
/** Callback when the committed radio value changes */
|
|
66
|
+
onValueChange?: (value: string | null) => void;
|
|
67
|
+
/** Callback when the committed checkbox values change */
|
|
68
|
+
onValuesChange?: (values: string[]) => void;
|
|
13
69
|
/** Close when clicking outside */
|
|
14
70
|
closeOnClickOutside?: boolean;
|
|
15
71
|
/** Close when pressing Escape */
|
|
@@ -66,8 +122,16 @@ interface DropdownMenuController {
|
|
|
66
122
|
close(): void;
|
|
67
123
|
/** Toggle the dropdown menu */
|
|
68
124
|
toggle(): void;
|
|
125
|
+
/** Set one or more dropdown menu state fields programmatically */
|
|
126
|
+
set(detail: DropdownMenuSetDetail): void;
|
|
69
127
|
/** Current open state */
|
|
70
128
|
readonly isOpen: boolean;
|
|
129
|
+
/** Current committed radio value */
|
|
130
|
+
readonly value: string | null;
|
|
131
|
+
/** Current committed checkbox values */
|
|
132
|
+
readonly values: string[];
|
|
133
|
+
/** Current highlighted value */
|
|
134
|
+
readonly highlightedValue: string | null;
|
|
71
135
|
/** Cleanup all event listeners */
|
|
72
136
|
destroy(): void;
|
|
73
137
|
}
|
|
@@ -83,16 +147,25 @@ interface DropdownMenuController {
|
|
|
83
147
|
* - `collisionPadding`: viewport edge padding in px (default: 8)
|
|
84
148
|
*
|
|
85
149
|
* ## Events
|
|
86
|
-
* - **Outbound** `dropdown-menu:change` (on root): Fires when menu opens/closes.
|
|
87
|
-
* `event.detail:
|
|
88
|
-
* - **Outbound** `dropdown-menu:
|
|
89
|
-
*
|
|
150
|
+
* - **Outbound** `dropdown-menu:open-change` (on root): Fires when menu opens/closes.
|
|
151
|
+
* `event.detail: DropdownMenuOpenChangeDetail`
|
|
152
|
+
* - **Outbound** `dropdown-menu:change` (on root): Deprecated alias for `dropdown-menu:open-change`.
|
|
153
|
+
* - **Outbound** `dropdown-menu:highlight-change` (on root): Fires when highlight changes.
|
|
154
|
+
* `event.detail: DropdownMenuHighlightChangeDetail`
|
|
155
|
+
* - **Outbound** `dropdown-menu:select` (on root): Cancelable user activation event fired before commit.
|
|
156
|
+
* `event.detail: DropdownMenuSelectDetail`
|
|
157
|
+
* - **Outbound** `dropdown-menu:value-change` (on root): Fires when radio selection changes.
|
|
158
|
+
* `event.detail: DropdownMenuValueChangeDetail`
|
|
159
|
+
* - **Outbound** `dropdown-menu:values-change` (on root): Fires when checkbox selection changes.
|
|
160
|
+
* `event.detail: DropdownMenuValuesChangeDetail`
|
|
161
|
+
* - **Inbound** `dropdown-menu:set` (on root): Set open/highlight/selection state programmatically.
|
|
162
|
+
* `event.detail: DropdownMenuSetDetail`
|
|
90
163
|
*/
|
|
91
164
|
declare function createDropdownMenu(root: Element, options?: DropdownMenuOptions): DropdownMenuController;
|
|
92
165
|
/**
|
|
93
|
-
* Find and bind all dropdown menu components in a scope
|
|
94
|
-
* Returns array of controllers for programmatic access
|
|
166
|
+
* Find and bind all dropdown menu components in a scope.
|
|
167
|
+
* Returns array of controllers for programmatic access.
|
|
95
168
|
*/
|
|
96
169
|
declare function create(scope?: ParentNode): DropdownMenuController[];
|
|
97
170
|
//#endregion
|
|
98
|
-
export { Align, DropdownMenuController, DropdownMenuOptions, Side, create, createDropdownMenu };
|
|
171
|
+
export { Align, DropdownMenuController, DropdownMenuHighlightChangeDetail, DropdownMenuItemType, DropdownMenuOpenChangeDetail, DropdownMenuOpenChangeReason, DropdownMenuOpenChangeSource, DropdownMenuOptions, DropdownMenuSelectDetail, DropdownMenuSelectionSource, DropdownMenuSetDetail, DropdownMenuSetSource, DropdownMenuUserSource, DropdownMenuValueChangeDetail, DropdownMenuValuesChangeDetail, Side, create, createDropdownMenu };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,13 +3,69 @@
|
|
|
3
3
|
type Side = "top" | "right" | "bottom" | "left";
|
|
4
4
|
/** Alignment of the content relative to the trigger */
|
|
5
5
|
type Align = "start" | "center" | "end";
|
|
6
|
+
type DropdownMenuItemType = "item" | "radio" | "checkbox";
|
|
7
|
+
type DropdownMenuUserSource = "pointer" | "keyboard";
|
|
8
|
+
type DropdownMenuSetSource = "programmatic" | "restore";
|
|
9
|
+
type DropdownMenuSelectionSource = DropdownMenuUserSource | DropdownMenuSetSource;
|
|
10
|
+
type DropdownMenuOpenChangeSource = DropdownMenuSelectionSource | "init";
|
|
11
|
+
type DropdownMenuOpenChangeReason = "trigger" | "select" | "outside" | "escape" | "tab" | "programmatic" | "init";
|
|
12
|
+
interface DropdownMenuOpenChangeDetail {
|
|
13
|
+
open: boolean;
|
|
14
|
+
previousOpen: boolean;
|
|
15
|
+
source: DropdownMenuOpenChangeSource;
|
|
16
|
+
reason: DropdownMenuOpenChangeReason;
|
|
17
|
+
}
|
|
18
|
+
interface DropdownMenuHighlightChangeDetail {
|
|
19
|
+
value: string | null;
|
|
20
|
+
previousValue: string | null;
|
|
21
|
+
item: HTMLElement | null;
|
|
22
|
+
previousItem: HTMLElement | null;
|
|
23
|
+
source: DropdownMenuSelectionSource;
|
|
24
|
+
}
|
|
25
|
+
interface DropdownMenuSelectDetail {
|
|
26
|
+
value: string;
|
|
27
|
+
item: HTMLElement;
|
|
28
|
+
itemType: DropdownMenuItemType;
|
|
29
|
+
source: DropdownMenuUserSource;
|
|
30
|
+
checked?: boolean;
|
|
31
|
+
}
|
|
32
|
+
interface DropdownMenuValueChangeDetail {
|
|
33
|
+
value: string | null;
|
|
34
|
+
previousValue: string | null;
|
|
35
|
+
item: HTMLElement | null;
|
|
36
|
+
previousItem: HTMLElement | null;
|
|
37
|
+
source: DropdownMenuSelectionSource;
|
|
38
|
+
}
|
|
39
|
+
interface DropdownMenuValuesChangeDetail {
|
|
40
|
+
values: string[];
|
|
41
|
+
previousValues: string[];
|
|
42
|
+
changedValue: string | null;
|
|
43
|
+
checked: boolean | null;
|
|
44
|
+
item: HTMLElement | null;
|
|
45
|
+
source: DropdownMenuSelectionSource;
|
|
46
|
+
}
|
|
47
|
+
interface DropdownMenuSetDetail {
|
|
48
|
+
open?: boolean;
|
|
49
|
+
value?: string | null;
|
|
50
|
+
values?: string[];
|
|
51
|
+
highlightedValue?: string | null;
|
|
52
|
+
source?: DropdownMenuSetSource;
|
|
53
|
+
}
|
|
6
54
|
interface DropdownMenuOptions {
|
|
7
55
|
/** Initial open state */
|
|
8
56
|
defaultOpen?: boolean;
|
|
57
|
+
/** Initial radio selection state */
|
|
58
|
+
defaultValue?: string | null;
|
|
59
|
+
/** Initial checkbox selection state */
|
|
60
|
+
defaultValues?: string[];
|
|
9
61
|
/** Callback when open state changes */
|
|
10
62
|
onOpenChange?: (open: boolean) => void;
|
|
11
|
-
/** Callback when
|
|
63
|
+
/** Callback when a user activation is accepted */
|
|
12
64
|
onSelect?: (value: string) => void;
|
|
65
|
+
/** Callback when the committed radio value changes */
|
|
66
|
+
onValueChange?: (value: string | null) => void;
|
|
67
|
+
/** Callback when the committed checkbox values change */
|
|
68
|
+
onValuesChange?: (values: string[]) => void;
|
|
13
69
|
/** Close when clicking outside */
|
|
14
70
|
closeOnClickOutside?: boolean;
|
|
15
71
|
/** Close when pressing Escape */
|
|
@@ -66,8 +122,16 @@ interface DropdownMenuController {
|
|
|
66
122
|
close(): void;
|
|
67
123
|
/** Toggle the dropdown menu */
|
|
68
124
|
toggle(): void;
|
|
125
|
+
/** Set one or more dropdown menu state fields programmatically */
|
|
126
|
+
set(detail: DropdownMenuSetDetail): void;
|
|
69
127
|
/** Current open state */
|
|
70
128
|
readonly isOpen: boolean;
|
|
129
|
+
/** Current committed radio value */
|
|
130
|
+
readonly value: string | null;
|
|
131
|
+
/** Current committed checkbox values */
|
|
132
|
+
readonly values: string[];
|
|
133
|
+
/** Current highlighted value */
|
|
134
|
+
readonly highlightedValue: string | null;
|
|
71
135
|
/** Cleanup all event listeners */
|
|
72
136
|
destroy(): void;
|
|
73
137
|
}
|
|
@@ -83,16 +147,25 @@ interface DropdownMenuController {
|
|
|
83
147
|
* - `collisionPadding`: viewport edge padding in px (default: 8)
|
|
84
148
|
*
|
|
85
149
|
* ## Events
|
|
86
|
-
* - **Outbound** `dropdown-menu:change` (on root): Fires when menu opens/closes.
|
|
87
|
-
* `event.detail:
|
|
88
|
-
* - **Outbound** `dropdown-menu:
|
|
89
|
-
*
|
|
150
|
+
* - **Outbound** `dropdown-menu:open-change` (on root): Fires when menu opens/closes.
|
|
151
|
+
* `event.detail: DropdownMenuOpenChangeDetail`
|
|
152
|
+
* - **Outbound** `dropdown-menu:change` (on root): Deprecated alias for `dropdown-menu:open-change`.
|
|
153
|
+
* - **Outbound** `dropdown-menu:highlight-change` (on root): Fires when highlight changes.
|
|
154
|
+
* `event.detail: DropdownMenuHighlightChangeDetail`
|
|
155
|
+
* - **Outbound** `dropdown-menu:select` (on root): Cancelable user activation event fired before commit.
|
|
156
|
+
* `event.detail: DropdownMenuSelectDetail`
|
|
157
|
+
* - **Outbound** `dropdown-menu:value-change` (on root): Fires when radio selection changes.
|
|
158
|
+
* `event.detail: DropdownMenuValueChangeDetail`
|
|
159
|
+
* - **Outbound** `dropdown-menu:values-change` (on root): Fires when checkbox selection changes.
|
|
160
|
+
* `event.detail: DropdownMenuValuesChangeDetail`
|
|
161
|
+
* - **Inbound** `dropdown-menu:set` (on root): Set open/highlight/selection state programmatically.
|
|
162
|
+
* `event.detail: DropdownMenuSetDetail`
|
|
90
163
|
*/
|
|
91
164
|
declare function createDropdownMenu(root: Element, options?: DropdownMenuOptions): DropdownMenuController;
|
|
92
165
|
/**
|
|
93
|
-
* Find and bind all dropdown menu components in a scope
|
|
94
|
-
* Returns array of controllers for programmatic access
|
|
166
|
+
* Find and bind all dropdown menu components in a scope.
|
|
167
|
+
* Returns array of controllers for programmatic access.
|
|
95
168
|
*/
|
|
96
169
|
declare function create(scope?: ParentNode): DropdownMenuController[];
|
|
97
170
|
//#endregion
|
|
98
|
-
export { Align, DropdownMenuController, DropdownMenuOptions, Side, create, createDropdownMenu };
|
|
171
|
+
export { Align, DropdownMenuController, DropdownMenuHighlightChangeDetail, DropdownMenuItemType, DropdownMenuOpenChangeDetail, DropdownMenuOpenChangeReason, DropdownMenuOpenChangeSource, DropdownMenuOptions, DropdownMenuSelectDetail, DropdownMenuSelectionSource, DropdownMenuSetDetail, DropdownMenuSetSource, DropdownMenuUserSource, DropdownMenuValueChangeDetail, DropdownMenuValuesChangeDetail, Side, create, createDropdownMenu };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createDismissLayer as r,createPortalLifecycle as i,createPositionSync as a,createPresenceLifecycle as ee,emit as o,ensureId as s,ensureItemVisibleInContainer as te,focusElement as c,getDataBool as l,getDataEnum as u,getDataNumber as d,getPart as f,getParts as ne,getRoots as p,hasRootBinding as m,lockScroll as re,measurePopupContentRect as ie,on as h,reuseRootBinding as ae,setAria as g,setRootBinding as oe,unlockScroll as _}from"@data-slot/core";const se=[`top`,`right`,`bottom`,`left`],ce=[`start`,`center`,`end`],v=`@data-slot/dropdown-menu`;function y(p,m={}){let y=ae(p,v,`[@data-slot/dropdown-menu] createDropdownMenu() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(y)return y;let b=f(p,`dropdown-menu-trigger`),x=f(p,`dropdown-menu-content`),S=f(p,`dropdown-menu-positioner`),C=S&&x&&S.contains(x)?S:null,w=f(p,`dropdown-menu-portal`),le=w&&C&&w.contains(C)?w:null;if(!b||!x)throw Error(`DropdownMenu requires trigger and content slots`);let ue=m.defaultOpen??l(p,`defaultOpen`)??!1,de=m.onOpenChange,fe=m.onSelect,pe=m.closeOnClickOutside??l(p,`closeOnClickOutside`)??!0,me=m.closeOnEscape??l(p,`closeOnEscape`)??!0,he=m.closeOnSelect??l(p,`closeOnSelect`)??!0,T=(e,t)=>u(x,e,t)??(C?u(C,e,t):void 0)??u(p,e,t),E=e=>d(x,e)??(C?d(C,e):void 0)??d(p,e),ge=e=>l(x,e)??(C?l(C,e):void 0)??l(p,e),_e=m.side??T(`side`,se)??`bottom`,ve=m.align??T(`align`,ce)??`start`,ye=m.sideOffset??E(`sideOffset`)??4,be=m.alignOffset??E(`alignOffset`)??0,xe=m.avoidCollisions??ge(`avoidCollisions`)??!0,Se=m.collisionPadding??E(`collisionPadding`)??8,D=m.lockScroll??l(p,`lockScroll`)??!0,O=m.highlightItemOnHover??l(p,`highlightItemOnHover`)??!0,k=!1,A=null,j=-1,M=``,N=null,P=!1,F=[],I=!1,L=i({content:x,root:p,wrapperSlot:C?void 0:`dropdown-menu-positioner`,container:C??void 0,mountTarget:C?le??C:void 0}),R=!1,z=[],B=[],V=new Map,H=e=>e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,U=e=>e.pointerType!==`touch`,Ce=s(b,`dropdown-menu-trigger`),we=s(x,`dropdown-menu-content`);b.setAttribute(`aria-haspopup`,`menu`),b.setAttribute(`aria-controls`,we),x.setAttribute(`role`,`menu`),x.setAttribute(`aria-labelledby`,Ce),x.tabIndex=-1;let Te=()=>{z=ne(x,`dropdown-menu-item`);for(let e of z)e.setAttribute(`role`,`menuitem`),e.hasAttribute(`data-disabled`)||e.hasAttribute(`disabled`)?e.setAttribute(`aria-disabled`,`true`):e.removeAttribute(`aria-disabled`),e.tabIndex=-1;B=z.filter(e=>!H(e)),V=new Map(B.map((e,t)=>[e,t]))},W=()=>{let e=L.container,r=p.ownerDocument.defaultView??window,i=b.getBoundingClientRect(),a=t({anchorRect:i,contentRect:ie(x),side:_e,align:ve,sideOffset:ye,alignOffset:be,avoidCollisions:xe,collisionPadding:Se}),ee=n({side:a.side,align:a.align,anchorRect:i,popupX:a.x,popupY:a.y});D?(e.style.position=`fixed`,e.style.top=`0px`,e.style.left=`0px`,e.style.transform=`translate3d(${a.x}px, ${a.y}px, 0)`):(e.style.position=`absolute`,e.style.top=`0px`,e.style.left=`0px`,e.style.transform=`translate3d(${a.x+r.scrollX}px, ${a.y+r.scrollY}px, 0)`),e.style.setProperty(`--transform-origin`,ee),e.style.willChange=`transform`,e.style.margin=`0`,x.setAttribute(`data-side`,a.side),x.setAttribute(`data-align`,a.align),e!==x&&(e.setAttribute(`data-side`,a.side),e.setAttribute(`data-align`,a.align))},G=a({observedElements:[b,x],isActive:()=>k,ancestorScroll:D,onUpdate:W}),K=(e,t=!0)=>{for(let n=0;n<B.length;n++){let r=B[n];n===e?(r.setAttribute(`data-highlighted`,``),te(r,x),t&&r.focus()):r.removeAttribute(`data-highlighted`)}j=e},q=()=>{for(let e of z)e.removeAttribute(`data-highlighted`);j=-1},J=()=>{q(),c(x)},Y=e=>{p.setAttribute(`data-state`,e),x.setAttribute(`data-state`,e),e===`open`?(p.setAttribute(`data-open`,``),x.setAttribute(`data-open`,``),p.removeAttribute(`data-closed`),x.removeAttribute(`data-closed`)):(p.setAttribute(`data-closed`,``),x.setAttribute(`data-closed`,``),p.removeAttribute(`data-open`),x.removeAttribute(`data-open`))},Ee=()=>{requestAnimationFrame(()=>{A&&document.contains(A)?c(A):b&&document.contains(b)&&c(b),A=null})},X=ee({element:x,onExitComplete:()=>{R||(L.restore(),x.hidden=!0,Ee())}}),Z=e=>{k!==e&&(e?(A=document.activeElement,k=!0,g(b,`expanded`,!0),L.mount(),x.hidden=!1,Y(`open`),X.enter(),D&&!I&&(re(),I=!0),Te(),P=!1,q(),G.start(),W(),G.update(),x.focus()):(k=!1,g(b,`expanded`,!1),Y(`closed`),q(),M=``,P=!1,I&&=(_(),!1),G.stop(),X.exit()),o(p,`dropdown-menu:change`,{open:k}),de?.(k))},Q=e=>{if(H(e))return;let t=e.dataset.value||e.textContent?.trim()||``;o(p,`dropdown-menu:select`,{value:t}),fe?.(t),he&&Z(!1)},De=e=>{let t=B.length;if(t!==0)switch(e.key){case`ArrowDown`:e.preventDefault(),P=!0,K(j===-1?0:(j+1)%t);break;case`ArrowUp`:e.preventDefault(),P=!0,K(j===-1?t-1:(j-1+t)%t);break;case`Home`:e.preventDefault(),P=!0,K(0);break;case`End`:e.preventDefault(),P=!0,K(t-1);break;case`Enter`:case` `:e.preventDefault(),j>=0&&Q(B[j]);break;case`Tab`:Z(!1);break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.preventDefault(),Oe(e.key.toLowerCase()))}},Oe=e=>{N&&clearTimeout(N),N=setTimeout(()=>{M=``},500),M+=e;let t=B.findIndex(e=>(e.textContent?.trim().toLowerCase()||``).startsWith(M));if(t===-1&&M.length===1){let n=j+1;for(let r=0;r<B.length;r++){let i=(n+r)%B.length;if((B[i].textContent?.trim().toLowerCase()||``).startsWith(e)){t=i;break}}}t!==-1&&(P=!0,K(t))};g(b,`expanded`,!1),x.hidden=!0,Y(`closed`),F.push(h(b,`click`,()=>Z(!k)),h(b,`keydown`,e=>{(e.key===`Enter`||e.key===` `||e.key===`ArrowDown`)&&!k&&(e.preventDefault(),Z(!0))})),F.push(h(x,`keydown`,De),h(x,`click`,e=>{let t=e.target.closest?.(`[data-slot="dropdown-menu-item"]`);t&&Q(t)}),h(x,`pointermove`,e=>{if(!O||!U(e))return;let t=e.target.closest?.(`[data-slot="dropdown-menu-item"]`);if(!(P&&(P=!1,t&&V.get(t)===j)))if(t&&!H(t)){let e=V.get(t);e!==void 0&&e!==j&&K(e,!0)}else J()}),h(x,`pointerleave`,e=>{!O||!U(e)||P||J()})),F.push(r({root:p,isOpen:()=>k,onDismiss:()=>Z(!1),closeOnClickOutside:pe,closeOnEscape:me})),F.push(h(p,`dropdown-menu:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&Z(n)}));let $={open:()=>Z(!0),close:()=>Z(!1),toggle:()=>Z(!k),get isOpen(){return k},destroy:()=>{R=!0,N&&clearTimeout(N),G.stop(),X.cleanup(),L.cleanup(),I&&=(_(),!1),F.forEach(e=>e()),F.length=0,e(p,v,$)}};return oe(p,v,$),ue&&Z(!0),$}function b(e=document){let t=[];for(let n of p(e,`dropdown-menu`))m(n,v)||t.push(y(n));return t}export{b as create,y as createDropdownMenu};
|
|
1
|
+
import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,containsWithPortals as r,createDismissLayer as i,createPortalLifecycle as a,createPositionSync as o,createPresenceLifecycle as ee,emit as s,ensureId as te,ensureItemVisibleInContainer as ne,focusElement as c,getDataBool as l,getDataEnum as u,getDataNumber as d,getDataString as re,getPart as f,getRoots as p,hasRootBinding as m,lockScroll as ie,measurePopupContentRect as ae,on as h,reuseRootBinding as oe,setAria as g,setRootBinding as se,unlockScroll as ce}from"@data-slot/core";const le=[`top`,`right`,`bottom`,`left`],ue=[`start`,`center`,`end`],_=`@data-slot/dropdown-menu`,de=`[data-slot="dropdown-menu-item"], [data-slot="dropdown-menu-radio-item"], [data-slot="dropdown-menu-checkbox-item"]`,fe=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),v=(e,t,n)=>{n?e.setAttribute(t,``):e.removeAttribute(t)},pe=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0},me=(e,t,n,r=!1)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:r,detail:n})),he=e=>{switch(e){case`radio`:return`menuitemradio`;case`checkbox`:return`menuitemcheckbox`;default:return`menuitem`}},ge=e=>{let t=e.getAttribute(`data-slot`);return t===`dropdown-menu-radio-item`?`radio`:t===`dropdown-menu-checkbox-item`?`checkbox`:`item`},_e=e=>{let t=e.dataset.value;if(t===void 0)return null;let n=t.trim();return n.length>0?n:null},ve=e=>e.type===`item`?e.value?e.value:e.el.textContent?.trim()??``:e.value??``,ye=e=>{if(e===void 0)return[];let t=e.trim();if(t.length===0)return[];try{let e=JSON.parse(t);return Array.isArray(e)?e.filter(e=>typeof e==`string`).map(e=>e.trim()).filter(e=>e.length>0):[]}catch{return[]}};function y(p,m={}){let y=oe(p,_,`[@data-slot/dropdown-menu] createDropdownMenu() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(y)return y;let b=f(p,`dropdown-menu-trigger`),x=f(p,`dropdown-menu-content`),be=f(p,`dropdown-menu-positioner`),S=be&&x&&be.contains(x)?be:null,xe=f(p,`dropdown-menu-portal`),Se=xe&&S&&xe.contains(S)?xe:null;if(!b||!x)throw Error(`DropdownMenu requires trigger and content slots`);let Ce=m.defaultOpen??l(p,`defaultOpen`)??!1,we=m.onOpenChange,Te=m.onSelect,Ee=m.onValueChange,De=m.onValuesChange,C=m.closeOnClickOutside??l(p,`closeOnClickOutside`)??!0,Oe=m.closeOnEscape??l(p,`closeOnEscape`)??!0,ke=m.closeOnSelect??l(p,`closeOnSelect`)??!0,Ae=(e,t)=>u(x,e,t)??(S?u(S,e,t):void 0)??u(p,e,t),w=e=>d(x,e)??(S?d(S,e):void 0)??d(p,e),je=e=>l(x,e)??(S?l(S,e):void 0)??l(p,e),Me=m.side??Ae(`side`,le)??`bottom`,Ne=m.align??Ae(`align`,ue)??`start`,Pe=m.sideOffset??w(`sideOffset`)??4,Fe=m.alignOffset??w(`alignOffset`)??0,Ie=m.avoidCollisions??je(`avoidCollisions`)??!0,Le=m.collisionPadding??w(`collisionPadding`)??8,T=m.lockScroll??l(p,`lockScroll`)??!0,Re=m.highlightItemOnHover??l(p,`highlightItemOnHover`)??!0,ze=fe(m,`defaultValue`),Be=fe(m,`defaultValues`),Ve=p.hasAttribute(`data-default-value`),He=p.hasAttribute(`data-default-values`),Ue=ze?m.defaultValue??null:Ve?re(p,`defaultValue`)??null:null,We=Be?m.defaultValues??[]:He?ye(re(p,`defaultValues`)):[],E=!1,D=null,O=[],k=null,A=null,j=``,M=null,N=!1,P=!1,Ge=!1,F=null,I=[],L=a({content:x,root:p,wrapperSlot:S?void 0:`dropdown-menu-positioner`,container:S??void 0,mountTarget:S?Se??S:void 0}),R=[],z=[],B=new Map,Ke=e=>e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,qe=e=>e.pointerType!==`touch`,Je=e=>e instanceof Element?e.closest(de):null,V=e=>e?R.find(t=>t.el===e)??null:null,H=()=>R.filter(e=>e.type===`radio`),U=()=>R.filter(e=>e.type===`checkbox`),Ye=(e,t)=>e.find(e=>e.type===`radio`&&e.value===t)??null,W=e=>e?e.type===`item`?ve(e):e.value:null,Xe=e=>(e.type===`radio`||e.type===`checkbox`)&&e.value===null,G=e=>Ke(e.el)||Xe(e),K=e=>H().find(t=>t.value===e)??null,Ze=e=>z.find(t=>W(t)===e)??null,Qe=(e,t,n=U())=>{let r=new Set(e),i=new Set(t),a=null,o=null,ee=null;for(let e of n){if(e.type!==`checkbox`)continue;let t=e.value;if(!t)continue;let n=r.has(t),s=i.has(t);if(n!==s){if(a!==null)return{changedValue:null,checked:null,item:null};a=t,o=s,ee=e.el}}return{changedValue:a,checked:o,item:ee}},q=(e,t)=>{let n=U().filter(e=>e.value!==null);if(n.length===0)return null;if(e.length===0)return[];let r=new Set(e.filter(e=>typeof e==`string`).map(e=>e.trim()).filter(e=>e.length>0));if(r.size===0)return t===`init`?[]:null;let i=[];for(let e of n)e.value&&r.has(e.value)&&i.push(e.value);return i.length===0?t===`init`?[]:null:i},J=()=>{for(let e of R){let t=G(e);if(e.el.setAttribute(`role`,he(e.type)),e.el.tabIndex=-1,t?e.el.setAttribute(`aria-disabled`,`true`):e.el.removeAttribute(`aria-disabled`),e.type===`radio`){let t=e.value!==null&&D===e.value;v(e.el,`data-checked`,t),g(e.el,`checked`,e.value===null?null:t)}else if(e.type===`checkbox`){let t=e.value!==null&&O.includes(e.value);v(e.el,`data-checked`,t),g(e.el,`checked`,e.value===null?null:t)}else e.el.removeAttribute(`data-checked`),e.el.removeAttribute(`aria-checked`)}H().length>0&&D!==null?p.setAttribute(`data-value`,D):p.removeAttribute(`data-value`)},$e=()=>{for(let e of R)v(e.el,`data-highlighted`,e.el===k)},Y=({source:e=`programmatic`,emitSelectionInvalidation:t=!1}={})=>{let n=R,r=D,i=[...O];R=Array.from(x.querySelectorAll(de)).map(e=>({el:e,type:ge(e),value:_e(e)}));let a=r!==null&&K(r)?r:null,o=i.length>0?q(i,`init`)??[]:[];if(D=a,O=o,z=R.filter(e=>!G(e)),B=new Map(z.map((e,t)=>[e.el,t])),k&&!B.has(k)&&(k=null),J(),$e(),t&&(r!==D&&nt({value:D,previousValue:r,item:D===null?null:K(D)?.el??null,previousItem:r===null?null:Ye(n,r)?.el??null,source:e}),!pe(i,O))){let t=Qe(i,O,n.filter(e=>e.type===`checkbox`));rt({values:[...O],previousValues:i,changedValue:t.changedValue,checked:t.checked,item:t.item,source:e})}},et=e=>{s(p,`dropdown-menu:open-change`,e),s(p,`dropdown-menu:change`,e),we?.(e.open)},tt=e=>{s(p,`dropdown-menu:highlight-change`,e)},nt=e=>{s(p,`dropdown-menu:value-change`,e),Ee?.(e.value)},rt=e=>{s(p,`dropdown-menu:values-change`,e),De?.([...e.values])},it=()=>{let e=L.container,r=p.ownerDocument.defaultView??window,i=b.getBoundingClientRect(),a=t({anchorRect:i,contentRect:ae(x),side:Me,align:Ne,sideOffset:Pe,alignOffset:Fe,avoidCollisions:Ie,collisionPadding:Le}),o=n({side:a.side,align:a.align,anchorRect:i,popupX:a.x,popupY:a.y});T?(e.style.position=`fixed`,e.style.top=`0px`,e.style.left=`0px`,e.style.transform=`translate3d(${a.x}px, ${a.y}px, 0)`):(e.style.position=`absolute`,e.style.top=`0px`,e.style.left=`0px`,e.style.transform=`translate3d(${a.x+r.scrollX}px, ${a.y+r.scrollY}px, 0)`),e.style.setProperty(`--transform-origin`,o),e.style.willChange=`transform`,e.style.margin=`0`,x.setAttribute(`data-side`,a.side),x.setAttribute(`data-align`,a.align),e!==x&&(e.setAttribute(`data-side`,a.side),e.setAttribute(`data-align`,a.align))},X=o({observedElements:[b,x],isActive:()=>E,ancestorScroll:T,onUpdate:it}),at=()=>{requestAnimationFrame(()=>{A&&document.contains(A)?c(A):document.contains(b)&&c(b),A=null})},ot=ee({element:x,onExitComplete:()=>{Ge||(L.restore(),x.hidden=!0,at())}}),Z=e=>{p.setAttribute(`data-state`,e),x.setAttribute(`data-state`,e),e===`open`?(p.setAttribute(`data-open`,``),x.setAttribute(`data-open`,``),p.removeAttribute(`data-closed`),x.removeAttribute(`data-closed`)):(p.setAttribute(`data-closed`,``),x.setAttribute(`data-closed`,``),p.removeAttribute(`data-open`),x.removeAttribute(`data-open`))},Q=(e,{source:t,focus:n=!0,focusContentOnClear:r=!1})=>{if(e&&!B.has(e))return!1;let i=k;return i===e?(e&&n?(ne(e,x),c(e)):!e&&r&&c(x),!1):(k=e,$e(),e?(ne(e,x),n&&c(e)):r&&c(x),tt({value:W(V(e)),previousValue:W(V(i)),item:e,previousItem:i,source:t}),!0)},st=(e,t,n=!0)=>{if(Y({source:t,emitSelectionInvalidation:n}),H().length===0)return!1;let r=e===null?null:K(e);if(e!==null&&!r||D===e)return!1;let i=D,a=i===null?null:K(i);return D=e,J(),n&&nt({value:D,previousValue:i,item:r?.el??null,previousItem:a?.el??null,source:t}),!0},ct=(e,t,n=!0)=>{Y({source:t,emitSelectionInvalidation:n});let r=q(e,n?`set`:`init`);if(r===null||pe(O,r))return!1;let i=[...O],a=Qe(i,r);return O=r,J(),n&&rt({values:[...O],previousValues:i,changedValue:a.changedValue,checked:a.checked,item:a.item,source:t}),!0},lt=()=>{if(Y(),ze||Ve)Ue===null?D=null:st(Ue,`programmatic`,!1);else for(let e of H())if(e.value!==null&&l(e.el,`defaultChecked`)){D=e.value;break}O=Be||He?q(We,`init`)??[]:q(U().filter(e=>e.value!==null&&l(e.el,`defaultChecked`)).map(e=>e.value),`init`)??[],J()},$=(e,{source:t,reason:n})=>{if(E===e)return;F=null;let r=E;e?(A=document.activeElement,E=!0,g(b,`expanded`,!0),L.mount(),x.hidden=!1,Z(`open`),ot.enter(),T&&!P&&(ie(),P=!0),Y({source:t===`restore`?`restore`:`programmatic`,emitSelectionInvalidation:t!==`init`}),N=!1,j=``,X.start(),it(),X.update(),c(x)):(E=!1,g(b,`expanded`,!1),Z(`closed`),k&&Q(null,{source:t===`init`?`programmatic`:t,focus:!1,focusContentOnClear:!1}),j=``,N=!1,P&&=(ce(),!1),X.stop(),ot.exit()),et({open:E,previousOpen:r,source:t,reason:n})},ut=(e,t)=>{let n={source:e,reason:t};F=n,queueMicrotask(()=>{F===n&&(F=null)})},dt=(e,t)=>{if(G(e))return;let n=W(e);if(n===null)return;let r;if(e.type===`radio`?r=!0:e.type===`checkbox`&&e.value!==null&&(r=!O.includes(e.value)),me(p,`dropdown-menu:select`,{value:n,item:e.el,itemType:e.type,source:t,checked:r},!0)){if(Te?.(n),e.type===`radio`)st(e.value,t,!0);else if(e.type===`checkbox`&&e.value!==null){let n=new Set(O);n.has(e.value)?n.delete(e.value):n.add(e.value),ct([...n],t,!0)}ke&&$(!1,{source:t,reason:`select`})}},ft=e=>{M&&clearTimeout(M),M=setTimeout(()=>{j=``},500),j+=e;let t=z.findIndex(e=>(e.el.textContent?.trim().toLowerCase()??``).startsWith(j));if(t===-1&&j.length===1){let n=k?(B.get(k)??-1)+1:0;for(let r=0;r<z.length;r++){let i=(n+r)%z.length;if((z[i]?.el.textContent?.trim().toLowerCase()??``).startsWith(e)){t=i;break}}}t!==-1&&(N=!0,Q(z[t]?.el??null,{source:`keyboard`,focus:!0}))},pt=e=>{let t=e.source??`programmatic`;if(e.value!==void 0&&st(e.value,t,!0),e.values!==void 0&&ct(e.values,t,!0),e.open!==void 0&&$(e.open,{source:t,reason:`programmatic`}),e.highlightedValue!==void 0){if(!E)return;if(e.highlightedValue===null)Q(null,{source:t,focus:!1,focusContentOnClear:!0});else{let n=Ze(e.highlightedValue);n&&Q(n.el,{source:t,focus:!0})}}},mt=te(b,`dropdown-menu-trigger`),ht=te(x,`dropdown-menu-content`);b.setAttribute(`aria-haspopup`,`menu`),b.setAttribute(`aria-controls`,ht),x.setAttribute(`role`,`menu`),x.setAttribute(`aria-labelledby`,mt),x.tabIndex=-1,g(b,`expanded`,!1),x.hidden=!0,Z(`closed`),lt(),I.push(h(b,`click`,()=>{$(!E,{source:`pointer`,reason:`trigger`})}),h(b,`keydown`,e=>{(e.key===`Enter`||e.key===` `||e.key===`ArrowDown`)&&!E&&(e.preventDefault(),$(!0,{source:`keyboard`,reason:`trigger`}))})),I.push(h(x,`keydown`,e=>{if(e.key===`Tab`){$(!1,{source:`keyboard`,reason:`tab`});return}let t=z.length;if(t!==0)switch(e.key){case`ArrowDown`:e.preventDefault(),N=!0,Q(z[k?((B.get(k)??-1)+1)%t:0]?.el??null,{source:`keyboard`,focus:!0});break;case`ArrowUp`:e.preventDefault(),N=!0,Q(z[k?(B.get(k)-1+t)%t:t-1]?.el??null,{source:`keyboard`,focus:!0});break;case`Home`:e.preventDefault(),N=!0,Q(z[0]?.el??null,{source:`keyboard`,focus:!0});break;case`End`:e.preventDefault(),N=!0,Q(z[t-1]?.el??null,{source:`keyboard`,focus:!0});break;case`Enter`:case` `:if(e.preventDefault(),k){let e=V(k);e&&dt(e,`keyboard`)}break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.preventDefault(),ft(e.key.toLowerCase()))}}),h(x,`click`,e=>{let t=V(Je(e.target));t&&dt(t,`pointer`)}),h(x,`pointermove`,e=>{if(!Re||!qe(e))return;let t=Je(e.target);N&&(N=!1,t&&t===k)||(t&&B.has(t)?Q(t,{source:`pointer`,focus:!0}):k&&Q(null,{source:`pointer`,focus:!1,focusContentOnClear:!0}))}),h(x,`pointerleave`,e=>{!Re||!qe(e)||N||!k||Q(null,{source:`pointer`,focus:!1,focusContentOnClear:!0})}));let gt=p.ownerDocument??document;I.push(h(gt,`pointerdown`,e=>{if(!E||!C||e.pointerType===`touch`)return;let t=e.target;r(p,t)||ut(`pointer`,`outside`)},{capture:!0}),h(gt,`click`,e=>{if(!E||!C)return;let t=e.target;r(p,t)||ut(`pointer`,`outside`)},{capture:!0}),h(gt,`keydown`,e=>{!E||!Oe||e.key!==`Escape`||e.defaultPrevented||ut(`keyboard`,`escape`)},{capture:!0})),I.push(i({root:p,isOpen:()=>E,onDismiss:()=>{let e=F;if(F=null,e?.reason===`escape`){$(!1,{source:e.source,reason:`escape`});return}$(!1,{source:e?.source??`pointer`,reason:e?.reason??`outside`})},closeOnClickOutside:C,closeOnEscape:Oe})),I.push(h(p,`dropdown-menu:set`,e=>{let t=e.detail;if(!t||typeof t!=`object`)return;let n={source:t.source===`restore`||t.source===`programmatic`?t.source:void 0};t.open!==void 0&&(n.open=t.open),t.values!==void 0&&(n.values=Array.isArray(t.values)?t.values.filter(e=>typeof e==`string`):void 0),t.highlightedValue!==void 0&&(n.highlightedValue=t.highlightedValue===null||typeof t.highlightedValue==`string`?t.highlightedValue:void 0),t.value!==void 0&&(typeof t.value==`boolean`&&t.open===void 0?n.open=t.value:(t.value===null||typeof t.value==`string`)&&(n.value=t.value)),pt(n)}));let _t={open:()=>$(!0,{source:`programmatic`,reason:`programmatic`}),close:()=>$(!1,{source:`programmatic`,reason:`programmatic`}),toggle:()=>$(!E,{source:`programmatic`,reason:`programmatic`}),set:e=>{pt(e)},get isOpen(){return E},get value(){return D},get values(){return[...O]},get highlightedValue(){return W(V(k))},destroy:()=>{Ge=!0,M&&clearTimeout(M),X.stop(),ot.cleanup(),L.cleanup(),P&&=(ce(),!1),I.forEach(e=>e()),I.length=0,e(p,_,_t)}};return se(p,_,_t),Ce&&$(!0,{source:`init`,reason:`init`}),_t}function b(e=document){let t=[];for(let n of p(e,`dropdown-menu`))m(n,_)||t.push(y(n));return t}export{b as create,y as createDropdownMenu};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@data-slot/dropdown-menu",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.165",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
],
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@data-slot/core": "0.2.
|
|
38
|
+
"@data-slot/core": "0.2.165"
|
|
39
39
|
}
|
|
40
40
|
}
|