@rcarls/rc-menu-button 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -3
- package/dist/custom-elements.json +98 -44
- package/dist/{rc-menu-button-CmzW7P4h.js → rc-menu-button-BEFLL7vi.js} +109 -51
- package/dist/rc-menu-button-BEFLL7vi.js.map +1 -0
- package/dist/rc-menu-button-define.js +1 -1
- package/dist/rc-menu-button.js +1 -1
- package/dist/types/packages/rc-menu-button/src/rc-menu-button.d.ts +101 -39
- package/package.json +8 -11
- package/dist/demo.css +0 -85
- package/dist/demo.js +0 -40
- package/dist/rc-menu-button-CmzW7P4h.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# `@rcarls/rc-menu-button`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Trigger button that opens an `rc-menu` popup, following the [WAI-ARIA Menu Button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/).
|
|
4
|
+
|
|
5
|
+
Docs: [https://richardcarls.github.io/rc-webcomponents/components/rc-menu-button](https://richardcarls.github.io/rc-webcomponents/components/rc-menu-button).
|
|
6
|
+
|
|
7
|
+
Wraps a trigger element and an `rc-menu` popup, wiring ARIA attributes, light-dismiss, and keyboard open/close automatically.
|
|
4
8
|
|
|
5
9
|
---
|
|
6
10
|
|
|
@@ -13,7 +17,7 @@ npm install @rcarls/rc-menu-button
|
|
|
13
17
|
## Import
|
|
14
18
|
|
|
15
19
|
```js
|
|
16
|
-
import '@rcarls/rc-menu-button';
|
|
20
|
+
import '@rcarls/rc-menu-button/define'; // side-effect: registers <rc-menu-button>
|
|
17
21
|
import { RCMenuButton } from '@rcarls/rc-menu-button'; // named class export
|
|
18
22
|
```
|
|
19
23
|
|
|
@@ -54,6 +58,21 @@ The component automatically sets `aria-haspopup="menu"` and `aria-expanded` on t
|
|
|
54
58
|
|
|
55
59
|
| Property | Default | Description |
|
|
56
60
|
|---|---|---|
|
|
61
|
+
| `--rc-menu-button-trigger-block-size` | `var(--rc-control-block-size)` | Minimum block size of the trigger |
|
|
62
|
+
| `--rc-menu-button-trigger-padding-block` | `var(--rc-control-padding-block)` | Trigger block-axis padding |
|
|
63
|
+
| `--rc-menu-button-trigger-padding-inline` | `var(--rc-control-padding-inline)` | Trigger inline-axis padding |
|
|
64
|
+
| `--rc-menu-button-trigger-gap` | `var(--rc-item-gap)` | Gap between flex children inside the trigger |
|
|
65
|
+
| `--rc-menu-button-trigger-border` | `var(--rc-border)` | Trigger border |
|
|
66
|
+
| `--rc-menu-button-trigger-radius` | `var(--rc-control-radius)` | Trigger border radius |
|
|
67
|
+
| `--rc-menu-button-trigger-background` | `var(--rc-button-bg)` | Trigger background |
|
|
68
|
+
| `--rc-menu-button-trigger-color` | `var(--rc-button-text)` | Trigger text color |
|
|
69
|
+
| `--rc-menu-button-trigger-transition` | — | Trigger state transition |
|
|
70
|
+
| `--rc-menu-button-trigger-hover-background` | `color-mix(in srgb, Highlight 8%, transparent)` | Trigger hover background |
|
|
71
|
+
| `--rc-menu-button-trigger-hover-color` | `inherit` | Trigger hover text color |
|
|
72
|
+
| `--rc-menu-button-trigger-hover-border-color` | `currentColor` | Trigger hover border color |
|
|
73
|
+
| `--rc-menu-button-trigger-open-background` | `color-mix(in srgb, Highlight 12%, transparent)` | Trigger background while the menu is open |
|
|
74
|
+
| `--rc-menu-button-trigger-open-color` | `inherit` | Trigger text color while the menu is open |
|
|
75
|
+
| `--rc-menu-button-trigger-open-border-color` | `currentColor` | Trigger border color while the menu is open |
|
|
57
76
|
| `--rc-menu-button-popup-z-index` | `1000` | `z-index` of the popup container |
|
|
58
77
|
|
|
59
78
|
### CSS parts
|
|
@@ -119,7 +138,9 @@ Keyboard handling depends on the resolved `orientation` (horizontal by default,
|
|
|
119
138
|
|
|
120
139
|
## Notes
|
|
121
140
|
|
|
122
|
-
**Popup positioning**
|
|
141
|
+
**Popup positioning** uses `AnchorController`, with native CSS Anchor
|
|
142
|
+
Positioning when available and a viewport-aware fallback otherwise. Set the
|
|
143
|
+
`placement` property or attribute to choose the preferred direction.
|
|
123
144
|
|
|
124
145
|
**Light dismiss** is active at all times. A capture-phase `click` listener on `document` closes the menu whenever the click falls outside the component boundary (checked via `composedPath()`).
|
|
125
146
|
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"declarations": [
|
|
47
47
|
{
|
|
48
48
|
"kind": "class",
|
|
49
|
-
"description": "
|
|
49
|
+
"description": "Trigger button that opens an rc-menu popup, following the WAI-ARIA Menu Button pattern.",
|
|
50
50
|
"name": "RCMenuButton",
|
|
51
51
|
"cssProperties": [
|
|
52
52
|
{
|
|
@@ -64,6 +64,11 @@
|
|
|
64
64
|
"name": "--rc-menu-button-trigger-padding-inline",
|
|
65
65
|
"default": "var(--rc-control-padding-inline)"
|
|
66
66
|
},
|
|
67
|
+
{
|
|
68
|
+
"description": "Gap between flex children in the trigger",
|
|
69
|
+
"name": "--rc-menu-button-trigger-gap",
|
|
70
|
+
"default": "var(--rc-item-gap)"
|
|
71
|
+
},
|
|
67
72
|
{
|
|
68
73
|
"description": "Trigger border",
|
|
69
74
|
"name": "--rc-menu-button-trigger-border",
|
|
@@ -84,6 +89,40 @@
|
|
|
84
89
|
"name": "--rc-menu-button-trigger-color",
|
|
85
90
|
"default": "var(--rc-button-text)"
|
|
86
91
|
},
|
|
92
|
+
{
|
|
93
|
+
"description": "CSS transition applied to the trigger",
|
|
94
|
+
"name": "--rc-menu-button-trigger-transition"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"description": "Trigger hover background",
|
|
98
|
+
"name": "--rc-menu-button-trigger-hover-background",
|
|
99
|
+
"default": "color-mix(in srgb, Highlight 8%, transparent)"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"description": "Trigger hover text color",
|
|
103
|
+
"name": "--rc-menu-button-trigger-hover-color",
|
|
104
|
+
"default": "inherit"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"description": "Trigger hover border color",
|
|
108
|
+
"name": "--rc-menu-button-trigger-hover-border-color",
|
|
109
|
+
"default": "currentColor"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"description": "Trigger background when the menu is open",
|
|
113
|
+
"name": "--rc-menu-button-trigger-open-background",
|
|
114
|
+
"default": "color-mix(in srgb, Highlight 12%, transparent)"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"description": "Trigger text color when the menu is open",
|
|
118
|
+
"name": "--rc-menu-button-trigger-open-color",
|
|
119
|
+
"default": "inherit"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"description": "Trigger border color when the menu is open",
|
|
123
|
+
"name": "--rc-menu-button-trigger-open-border-color",
|
|
124
|
+
"default": "currentColor"
|
|
125
|
+
},
|
|
87
126
|
{
|
|
88
127
|
"description": "Z-index of the popup overlay",
|
|
89
128
|
"name": "--rc-menu-button-popup-z-index",
|
|
@@ -119,7 +158,7 @@
|
|
|
119
158
|
},
|
|
120
159
|
"static": true,
|
|
121
160
|
"default": "{ ...LitElement.shadowRootOptions, delegatesFocus: true, }",
|
|
122
|
-
"description": "Enables delegatesFocus so the browser routes focus() on the host element\nto the slotted trigger button.
|
|
161
|
+
"description": "Enables delegatesFocus so the browser routes focus() on the host element\nto the slotted trigger button."
|
|
123
162
|
},
|
|
124
163
|
{
|
|
125
164
|
"kind": "field",
|
|
@@ -145,7 +184,7 @@
|
|
|
145
184
|
"type": {
|
|
146
185
|
"text": "boolean"
|
|
147
186
|
},
|
|
148
|
-
"description": "
|
|
187
|
+
"description": "Applies open state in controlled mode without dispatching an event.",
|
|
149
188
|
"attribute": "open",
|
|
150
189
|
"reflects": true
|
|
151
190
|
},
|
|
@@ -155,7 +194,7 @@
|
|
|
155
194
|
"type": {
|
|
156
195
|
"text": "boolean"
|
|
157
196
|
},
|
|
158
|
-
"description": "
|
|
197
|
+
"description": "Applies the uncontrolled default before any controlled write occurs.",
|
|
159
198
|
"attribute": "default-open"
|
|
160
199
|
},
|
|
161
200
|
{
|
|
@@ -164,7 +203,7 @@
|
|
|
164
203
|
"type": {
|
|
165
204
|
"text": "'horizontal' | 'vertical' | undefined"
|
|
166
205
|
},
|
|
167
|
-
"description": "Orientation of this menu button, affects which arrow keys open/close the menu.\nIf not set, inherits from a parent rc-menubar or element with role=\"menubar\".",
|
|
206
|
+
"description": "Orientation of this menu button, affects which arrow keys open/close the menu.\n\nIf not set, inherits from a parent rc-menubar or element with role=\"menubar\".",
|
|
168
207
|
"attribute": "orientation",
|
|
169
208
|
"reflects": true
|
|
170
209
|
},
|
|
@@ -185,7 +224,8 @@
|
|
|
185
224
|
"type": {
|
|
186
225
|
"text": "AnchorPlacement"
|
|
187
226
|
},
|
|
188
|
-
"privacy": "
|
|
227
|
+
"privacy": "protected",
|
|
228
|
+
"description": "Popup placement adjusted for orientation.\n\nSwitches from `bottom-start` to `right-start` when the resolved orientation\nis `vertical`, so vertical menubars open submenus to the side.",
|
|
189
229
|
"readonly": true
|
|
190
230
|
},
|
|
191
231
|
{
|
|
@@ -194,7 +234,7 @@
|
|
|
194
234
|
"type": {
|
|
195
235
|
"text": "'horizontal' | 'vertical'"
|
|
196
236
|
},
|
|
197
|
-
"privacy": "
|
|
237
|
+
"privacy": "protected",
|
|
198
238
|
"description": "Resolved orientation, considering inheritance from parent menubar.\nFalls back to 'horizontal' if no orientation is set or inherited.",
|
|
199
239
|
"readonly": true
|
|
200
240
|
},
|
|
@@ -204,7 +244,8 @@
|
|
|
204
244
|
"type": {
|
|
205
245
|
"text": "HTMLElement"
|
|
206
246
|
},
|
|
207
|
-
"privacy": "
|
|
247
|
+
"privacy": "protected",
|
|
248
|
+
"description": "Shadow DOM root container. Serves as the anchor element for `_anchorCtrl`."
|
|
208
249
|
},
|
|
209
250
|
{
|
|
210
251
|
"kind": "field",
|
|
@@ -212,37 +253,39 @@
|
|
|
212
253
|
"type": {
|
|
213
254
|
"text": "HTMLElement"
|
|
214
255
|
},
|
|
215
|
-
"privacy": "
|
|
256
|
+
"privacy": "protected",
|
|
257
|
+
"description": "Shadow DOM popup container. Hidden when `open` is `false`."
|
|
216
258
|
},
|
|
217
259
|
{
|
|
218
260
|
"kind": "field",
|
|
219
|
-
"name": "
|
|
261
|
+
"name": "_$trigger",
|
|
220
262
|
"type": {
|
|
221
263
|
"text": "WeakRef<HTMLElement> | undefined"
|
|
222
264
|
},
|
|
223
|
-
"privacy": "
|
|
224
|
-
"description": "
|
|
265
|
+
"privacy": "protected",
|
|
266
|
+
"description": "Weak reference to the slotted trigger button."
|
|
225
267
|
},
|
|
226
268
|
{
|
|
227
269
|
"kind": "field",
|
|
228
|
-
"name": "
|
|
270
|
+
"name": "_$menu",
|
|
229
271
|
"type": {
|
|
230
272
|
"text": "WeakRef<RCMenu> | undefined"
|
|
231
273
|
},
|
|
232
|
-
"privacy": "
|
|
233
|
-
"description": "
|
|
274
|
+
"privacy": "protected",
|
|
275
|
+
"description": "Weak reference to the slotted `rc-menu` element."
|
|
234
276
|
},
|
|
235
277
|
{
|
|
236
278
|
"kind": "field",
|
|
237
279
|
"name": "_anchorCtrl",
|
|
238
|
-
"privacy": "
|
|
239
|
-
"default": "new AnchorController(this, { anchor: () => this._$root ?? null, floating: () => this._$popup ?? null, shadowHost: () => this, placement: 'bottom-start', offset: 2, })"
|
|
280
|
+
"privacy": "protected",
|
|
281
|
+
"default": "new AnchorController(this, { anchor: () => this._$root ?? null, floating: () => this._$popup ?? null, shadowHost: () => this, placement: 'bottom-start', offset: 2, })",
|
|
282
|
+
"description": "Positions the popup relative to the trigger using CSS anchor positioning."
|
|
240
283
|
},
|
|
241
284
|
{
|
|
242
285
|
"kind": "field",
|
|
243
286
|
"name": "_boundHandleDocumentClick",
|
|
244
|
-
"privacy": "
|
|
245
|
-
"description": "Bound handler for document click (light dismiss)"
|
|
287
|
+
"privacy": "protected",
|
|
288
|
+
"description": "Bound handler for document click (light dismiss). Stored for `removeEventListener` pairing."
|
|
246
289
|
},
|
|
247
290
|
{
|
|
248
291
|
"kind": "field",
|
|
@@ -250,8 +293,8 @@
|
|
|
250
293
|
"type": {
|
|
251
294
|
"text": "MutationObserver | undefined"
|
|
252
295
|
},
|
|
253
|
-
"privacy": "
|
|
254
|
-
"description": "Watches the host's own `tabindex` attribute so the trigger button's\ntabindex can be kept in sync
|
|
296
|
+
"privacy": "protected",
|
|
297
|
+
"description": "Watches the host's own `tabindex` attribute so the trigger button's\ntabindex can be kept in sync.\n\nWhen a parent (e.g. rc-toolbar) places the\nhost under roving-tabindex management it sets/removes tabindex on the\nhost; we mirror that to the trigger so it doesn't become an independent\ntab stop. When the host has no tabindex the trigger keeps its natural\ntabindex=0 for standalone usage."
|
|
255
298
|
},
|
|
256
299
|
{
|
|
257
300
|
"kind": "method",
|
|
@@ -262,10 +305,11 @@
|
|
|
262
305
|
"default": "'first'",
|
|
263
306
|
"type": {
|
|
264
307
|
"text": "'first' | 'last'"
|
|
265
|
-
}
|
|
308
|
+
},
|
|
309
|
+
"description": "which item receives initial focus when the menu opens"
|
|
266
310
|
}
|
|
267
311
|
],
|
|
268
|
-
"description": "Opens the menu and
|
|
312
|
+
"description": "Opens the menu and moves focus into it."
|
|
269
313
|
},
|
|
270
314
|
{
|
|
271
315
|
"kind": "method",
|
|
@@ -273,25 +317,27 @@
|
|
|
273
317
|
"parameters": [
|
|
274
318
|
{
|
|
275
319
|
"name": "returnFocus",
|
|
276
|
-
"default": "true"
|
|
320
|
+
"default": "true",
|
|
321
|
+
"description": "when `true`, returns focus to the trigger button after closing"
|
|
277
322
|
}
|
|
278
323
|
],
|
|
279
|
-
"description": "Closes the menu
|
|
324
|
+
"description": "Closes the menu."
|
|
280
325
|
},
|
|
281
326
|
{
|
|
282
327
|
"kind": "method",
|
|
283
328
|
"name": "toggleMenu",
|
|
284
|
-
"description": "Toggles the menu open
|
|
329
|
+
"description": "Toggles the menu between open and closed."
|
|
285
330
|
},
|
|
286
331
|
{
|
|
287
332
|
"kind": "method",
|
|
288
333
|
"name": "_dispatchToggle",
|
|
289
|
-
"privacy": "
|
|
334
|
+
"privacy": "protected",
|
|
335
|
+
"description": "Dispatches the `rc-menu-button-toggle` bubbling composed event with the current open state."
|
|
290
336
|
},
|
|
291
337
|
{
|
|
292
338
|
"kind": "method",
|
|
293
339
|
"name": "_handleTriggerSlotChange",
|
|
294
|
-
"privacy": "
|
|
340
|
+
"privacy": "protected",
|
|
295
341
|
"parameters": [
|
|
296
342
|
{
|
|
297
343
|
"name": "e",
|
|
@@ -299,12 +345,13 @@
|
|
|
299
345
|
"text": "Event"
|
|
300
346
|
}
|
|
301
347
|
}
|
|
302
|
-
]
|
|
348
|
+
],
|
|
349
|
+
"description": "Caches a weak reference to the first assigned trigger element and syncs its ARIA and tabindex state."
|
|
303
350
|
},
|
|
304
351
|
{
|
|
305
352
|
"kind": "method",
|
|
306
353
|
"name": "_syncTriggerTabindex",
|
|
307
|
-
"privacy": "
|
|
354
|
+
"privacy": "protected",
|
|
308
355
|
"description": "Mirrors the host's `tabindex` attribute to the trigger button.\n\nWhen a roving-tabindex parent (e.g. rc-toolbar) marks this host inactive\nwith `tabindex=\"-1\"`, the trigger must also be suppressed so it does not\nbecome an independent tab stop. When the host is the active item\n(`tabindex=\"0\"`) or standalone (no tabindex), the trigger retains its\nnatural focusability so that Chrome's delegatesFocus can route sequential\nTab navigation through the host to the trigger."
|
|
309
356
|
},
|
|
310
357
|
{
|
|
@@ -324,7 +371,7 @@
|
|
|
324
371
|
{
|
|
325
372
|
"kind": "method",
|
|
326
373
|
"name": "_handleMenuSlotChange",
|
|
327
|
-
"privacy": "
|
|
374
|
+
"privacy": "protected",
|
|
328
375
|
"parameters": [
|
|
329
376
|
{
|
|
330
377
|
"name": "e",
|
|
@@ -332,17 +379,19 @@
|
|
|
332
379
|
"text": "Event"
|
|
333
380
|
}
|
|
334
381
|
}
|
|
335
|
-
]
|
|
382
|
+
],
|
|
383
|
+
"description": "Caches a weak reference to the first assigned `rc-menu` element."
|
|
336
384
|
},
|
|
337
385
|
{
|
|
338
386
|
"kind": "field",
|
|
339
387
|
"name": "_onNavigate",
|
|
340
|
-
"privacy": "
|
|
388
|
+
"privacy": "protected",
|
|
389
|
+
"description": "Keyboard navigation handler.\n\nOpens or closes the menu in response to arrow/Enter/Escape keys."
|
|
341
390
|
},
|
|
342
391
|
{
|
|
343
392
|
"kind": "method",
|
|
344
393
|
"name": "_handleTriggerClick",
|
|
345
|
-
"privacy": "
|
|
394
|
+
"privacy": "protected",
|
|
346
395
|
"parameters": [
|
|
347
396
|
{
|
|
348
397
|
"name": "e",
|
|
@@ -350,22 +399,25 @@
|
|
|
350
399
|
"text": "MouseEvent"
|
|
351
400
|
}
|
|
352
401
|
}
|
|
353
|
-
]
|
|
402
|
+
],
|
|
403
|
+
"description": "Prevents the default button action and delegates to `toggleMenu`."
|
|
354
404
|
},
|
|
355
405
|
{
|
|
356
406
|
"kind": "method",
|
|
357
407
|
"name": "_handleMenuClose",
|
|
358
|
-
"privacy": "
|
|
408
|
+
"privacy": "protected",
|
|
409
|
+
"description": "Closes the menu when an `rc-menu-close` event bubbles up from the popup."
|
|
359
410
|
},
|
|
360
411
|
{
|
|
361
412
|
"kind": "method",
|
|
362
413
|
"name": "_handleMenuActivate",
|
|
363
|
-
"privacy": "
|
|
414
|
+
"privacy": "protected",
|
|
415
|
+
"description": "Closes the menu when an `rc-menu-activate` event bubbles up from the popup."
|
|
364
416
|
},
|
|
365
417
|
{
|
|
366
418
|
"kind": "method",
|
|
367
419
|
"name": "_handleDocumentClick",
|
|
368
|
-
"privacy": "
|
|
420
|
+
"privacy": "protected",
|
|
369
421
|
"parameters": [
|
|
370
422
|
{
|
|
371
423
|
"name": "e",
|
|
@@ -373,7 +425,8 @@
|
|
|
373
425
|
"text": "MouseEvent"
|
|
374
426
|
}
|
|
375
427
|
}
|
|
376
|
-
]
|
|
428
|
+
],
|
|
429
|
+
"description": "Closes the menu on clicks outside this component (light dismiss)."
|
|
377
430
|
},
|
|
378
431
|
{
|
|
379
432
|
"kind": "method",
|
|
@@ -402,12 +455,13 @@
|
|
|
402
455
|
{
|
|
403
456
|
"kind": "method",
|
|
404
457
|
"name": "_syncTriggerAria",
|
|
405
|
-
"privacy": "
|
|
458
|
+
"privacy": "protected",
|
|
406
459
|
"return": {
|
|
407
460
|
"type": {
|
|
408
461
|
"text": "void"
|
|
409
462
|
}
|
|
410
|
-
}
|
|
463
|
+
},
|
|
464
|
+
"description": "Writes `aria-haspopup` and `aria-expanded` to the trigger button to match the current open state."
|
|
411
465
|
}
|
|
412
466
|
],
|
|
413
467
|
"events": [
|
|
@@ -434,7 +488,7 @@
|
|
|
434
488
|
"type": {
|
|
435
489
|
"text": "boolean"
|
|
436
490
|
},
|
|
437
|
-
"description": "
|
|
491
|
+
"description": "Applies open state in controlled mode without dispatching an event.",
|
|
438
492
|
"fieldName": "open"
|
|
439
493
|
},
|
|
440
494
|
{
|
|
@@ -442,7 +496,7 @@
|
|
|
442
496
|
"type": {
|
|
443
497
|
"text": "boolean"
|
|
444
498
|
},
|
|
445
|
-
"description": "
|
|
499
|
+
"description": "Applies the uncontrolled default before any controlled write occurs.",
|
|
446
500
|
"fieldName": "defaultOpen"
|
|
447
501
|
},
|
|
448
502
|
{
|
|
@@ -450,7 +504,7 @@
|
|
|
450
504
|
"type": {
|
|
451
505
|
"text": "'horizontal' | 'vertical' | undefined"
|
|
452
506
|
},
|
|
453
|
-
"description": "Orientation of this menu button, affects which arrow keys open/close the menu.\nIf not set, inherits from a parent rc-menubar or element with role=\"menubar\".",
|
|
507
|
+
"description": "Orientation of this menu button, affects which arrow keys open/close the menu.\n\nIf not set, inherits from a parent rc-menubar or element with role=\"menubar\".",
|
|
454
508
|
"fieldName": "orientation"
|
|
455
509
|
}
|
|
456
510
|
],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { css as
|
|
2
|
-
import { property as l, query as
|
|
3
|
-
import { AnchorController as
|
|
4
|
-
const _ =
|
|
1
|
+
import { css as d, LitElement as p, html as b } from "lit";
|
|
2
|
+
import { property as l, query as g, state as h } from "lit/decorators.js";
|
|
3
|
+
import { AnchorController as m, keyNavigation as f, keyInteraction as v } from "@rcarls/rc-common";
|
|
4
|
+
const _ = d`
|
|
5
5
|
:host {
|
|
6
6
|
display: inline-block;
|
|
7
7
|
}
|
|
@@ -24,14 +24,44 @@ const _ = g`
|
|
|
24
24
|
|
|
25
25
|
slot[name='trigger']::slotted(button),
|
|
26
26
|
slot[name='trigger']::slotted([role='button']) {
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
gap: var(--rc-menu-button-trigger-gap, var(--rc-item-gap, 0.5em));
|
|
27
31
|
min-block-size: var(--rc-menu-button-trigger-block-size, var(--rc-control-block-size, 2.25em));
|
|
28
32
|
padding: var(--rc-menu-button-trigger-padding-block, var(--rc-control-padding-block, 0.25em))
|
|
29
33
|
var(--rc-menu-button-trigger-padding-inline, var(--rc-control-padding-inline, 0.5em));
|
|
34
|
+
appearance: none;
|
|
35
|
+
box-sizing: border-box;
|
|
30
36
|
border: var(--rc-menu-button-trigger-border, var(--rc-border, 1px solid ButtonBorder));
|
|
31
37
|
border-radius: var(--rc-menu-button-trigger-radius, var(--rc-control-radius, 0.125em));
|
|
32
38
|
background: var(--rc-menu-button-trigger-background, var(--rc-button-bg, ButtonFace));
|
|
33
39
|
color: var(--rc-menu-button-trigger-color, var(--rc-button-text, ButtonText));
|
|
34
40
|
font: inherit;
|
|
41
|
+
text-decoration: none;
|
|
42
|
+
cursor: default;
|
|
43
|
+
user-select: none;
|
|
44
|
+
transition: var(--rc-menu-button-trigger-transition);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
slot[name='trigger']::slotted(button:hover),
|
|
48
|
+
slot[name='trigger']::slotted([role='button']:hover) {
|
|
49
|
+
border-color: var(--rc-menu-button-trigger-hover-border-color, currentColor);
|
|
50
|
+
background: var(
|
|
51
|
+
--rc-menu-button-trigger-hover-background,
|
|
52
|
+
color-mix(in srgb, Highlight 8%, transparent)
|
|
53
|
+
);
|
|
54
|
+
color: var(--rc-menu-button-trigger-hover-color, inherit);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
slot[name='trigger']::slotted(button[aria-expanded='true']),
|
|
58
|
+
slot[name='trigger']::slotted([role='button'][aria-expanded='true']) {
|
|
59
|
+
border-color: var(--rc-menu-button-trigger-open-border-color, currentColor);
|
|
60
|
+
background: var(
|
|
61
|
+
--rc-menu-button-trigger-open-background,
|
|
62
|
+
color-mix(in srgb, Highlight 12%, transparent)
|
|
63
|
+
);
|
|
64
|
+
color: var(--rc-menu-button-trigger-open-color, inherit);
|
|
35
65
|
}
|
|
36
66
|
|
|
37
67
|
:host([orientation='vertical']) slot[name='trigger']::slotted(button),
|
|
@@ -48,14 +78,14 @@ const _ = g`
|
|
|
48
78
|
display: none;
|
|
49
79
|
}
|
|
50
80
|
`;
|
|
51
|
-
var
|
|
81
|
+
var y = Object.defineProperty, O = Object.getOwnPropertyDescriptor, i = (u, t, e, r) => {
|
|
52
82
|
for (var n = r > 1 ? void 0 : r ? O(t, e) : t, s = u.length - 1, c; s >= 0; s--)
|
|
53
83
|
(c = u[s]) && (n = (r ? c(t, e, n) : c(n)) || n);
|
|
54
|
-
return r && n &&
|
|
84
|
+
return r && n && y(t, e, n), n;
|
|
55
85
|
};
|
|
56
86
|
const a = class a extends p {
|
|
57
87
|
constructor() {
|
|
58
|
-
super(...arguments), this._defaultOpen = !1, this._open = !1, this.placement = "bottom-start", this._anchorCtrl = new
|
|
88
|
+
super(...arguments), this._defaultOpen = !1, this._open = !1, this.placement = "bottom-start", this._anchorCtrl = new m(this, {
|
|
59
89
|
anchor: () => this._$root ?? null,
|
|
60
90
|
floating: () => this._$popup ?? null,
|
|
61
91
|
shadowHost: () => this,
|
|
@@ -79,7 +109,7 @@ const a = class a extends p {
|
|
|
79
109
|
get open() {
|
|
80
110
|
return this._open;
|
|
81
111
|
}
|
|
82
|
-
/**
|
|
112
|
+
/** Applies open state in controlled mode without dispatching an event. */
|
|
83
113
|
set open(t) {
|
|
84
114
|
const e = this._open;
|
|
85
115
|
t !== void 0 && (this._setOpen(t, !1), this.requestUpdate("open", e));
|
|
@@ -87,11 +117,17 @@ const a = class a extends p {
|
|
|
87
117
|
get defaultOpen() {
|
|
88
118
|
return this._defaultOpen;
|
|
89
119
|
}
|
|
90
|
-
/**
|
|
120
|
+
/** Applies the uncontrolled default before any controlled write occurs. */
|
|
91
121
|
set defaultOpen(t) {
|
|
92
122
|
const e = this._defaultOpen;
|
|
93
123
|
this._defaultOpen = t, t && !this._open && this._setOpen(!0, !1), this.requestUpdate("defaultOpen", e);
|
|
94
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Popup placement adjusted for orientation.
|
|
127
|
+
*
|
|
128
|
+
* Switches from `bottom-start` to `right-start` when the resolved orientation
|
|
129
|
+
* is `vertical`, so vertical menubars open submenus to the side.
|
|
130
|
+
*/
|
|
95
131
|
get _effectivePlacement() {
|
|
96
132
|
return this.placement !== "bottom-start" ? this.placement : this._resolvedOrientation === "vertical" ? "right-start" : this.placement;
|
|
97
133
|
}
|
|
@@ -100,36 +136,50 @@ const a = class a extends p {
|
|
|
100
136
|
* Falls back to 'horizontal' if no orientation is set or inherited.
|
|
101
137
|
*/
|
|
102
138
|
get _resolvedOrientation() {
|
|
103
|
-
if (this.orientation)
|
|
139
|
+
if (this.orientation)
|
|
140
|
+
return this.orientation;
|
|
104
141
|
const t = this.closest('rc-menubar, [role="menubar"]');
|
|
105
142
|
if (t) {
|
|
106
143
|
const e = t.getAttribute("orientation") ?? t.getAttribute("aria-orientation");
|
|
107
|
-
if (e === "vertical")
|
|
108
|
-
|
|
144
|
+
if (e === "vertical")
|
|
145
|
+
return "vertical";
|
|
146
|
+
if (e === "horizontal")
|
|
147
|
+
return "horizontal";
|
|
109
148
|
}
|
|
110
149
|
return "horizontal";
|
|
111
150
|
}
|
|
151
|
+
/** Installs the tabindex mutation observer and the document-level click listener. */
|
|
112
152
|
connectedCallback() {
|
|
113
153
|
super.connectedCallback(), this._tabObserver = new MutationObserver(() => this._syncTriggerTabindex()), this._tabObserver.observe(this, { attributes: !0, attributeFilter: ["tabindex"] }), document.addEventListener("click", this._boundHandleDocumentClick, !0), this.defaultOpen && this._setOpen(!0, !1);
|
|
114
154
|
}
|
|
155
|
+
/** Disconnects the tabindex observer and removes the document-level click listener. */
|
|
115
156
|
disconnectedCallback() {
|
|
116
157
|
this._tabObserver?.disconnect(), this._tabObserver = void 0, super.disconnectedCallback(), document.removeEventListener("click", this._boundHandleDocumentClick, !0);
|
|
117
158
|
}
|
|
118
|
-
/**
|
|
159
|
+
/**
|
|
160
|
+
* Opens the menu and moves focus into it.
|
|
161
|
+
*
|
|
162
|
+
* @param focusTarget - which item receives initial focus when the menu opens
|
|
163
|
+
*/
|
|
119
164
|
openMenu(t = "first") {
|
|
120
165
|
this.open || (this._anchorCtrl.setOptions({ placement: this._effectivePlacement }), this._setOpen(!0, !0), this._anchorCtrl.update(), this.updateComplete.then(() => {
|
|
121
|
-
const e = this.
|
|
166
|
+
const e = this._$menu?.deref();
|
|
122
167
|
t === "last" ? e?.focusLast() : e?.focusFirst();
|
|
123
168
|
}));
|
|
124
169
|
}
|
|
125
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* Closes the menu.
|
|
172
|
+
*
|
|
173
|
+
* @param returnFocus - when `true`, returns focus to the trigger button after closing
|
|
174
|
+
*/
|
|
126
175
|
closeMenu(t = !0) {
|
|
127
|
-
this.open && (this._setOpen(!1, !0), t && this.
|
|
176
|
+
this.open && (this._setOpen(!1, !0), t && this._$trigger?.deref()?.focus());
|
|
128
177
|
}
|
|
129
|
-
/** Toggles the menu open
|
|
178
|
+
/** Toggles the menu between open and closed. */
|
|
130
179
|
toggleMenu() {
|
|
131
180
|
this.open ? this.closeMenu() : this.openMenu();
|
|
132
181
|
}
|
|
182
|
+
/** Dispatches the `rc-menu-button-toggle` bubbling composed event with the current open state. */
|
|
133
183
|
_dispatchToggle() {
|
|
134
184
|
this.dispatchEvent(
|
|
135
185
|
new CustomEvent("rc-menu-button-toggle", {
|
|
@@ -139,9 +189,10 @@ const a = class a extends p {
|
|
|
139
189
|
})
|
|
140
190
|
);
|
|
141
191
|
}
|
|
192
|
+
/** Caches a weak reference to the first assigned trigger element and syncs its ARIA and tabindex state. */
|
|
142
193
|
_handleTriggerSlotChange(t) {
|
|
143
194
|
const r = t.currentTarget.assignedElements()[0];
|
|
144
|
-
r && (this.
|
|
195
|
+
r && (this._$trigger = new WeakRef(r), this._syncTriggerAria(), this._syncTriggerTabindex());
|
|
145
196
|
}
|
|
146
197
|
/**
|
|
147
198
|
* Mirrors the host's `tabindex` attribute to the trigger button.
|
|
@@ -154,7 +205,7 @@ const a = class a extends p {
|
|
|
154
205
|
* Tab navigation through the host to the trigger.
|
|
155
206
|
*/
|
|
156
207
|
_syncTriggerTabindex() {
|
|
157
|
-
const t = this.
|
|
208
|
+
const t = this._$trigger?.deref();
|
|
158
209
|
t && (this.getAttribute("tabindex") === "-1" ? t.setAttribute("tabindex", "-1") : t.removeAttribute("tabindex"));
|
|
159
210
|
}
|
|
160
211
|
/**
|
|
@@ -168,39 +219,47 @@ const a = class a extends p {
|
|
|
168
219
|
* directly calling trigger.focus() bypasses that restriction.
|
|
169
220
|
*/
|
|
170
221
|
focus(t) {
|
|
171
|
-
const e = this.
|
|
222
|
+
const e = this._$trigger?.deref();
|
|
172
223
|
if (!e) {
|
|
173
224
|
super.focus(t);
|
|
174
225
|
return;
|
|
175
226
|
}
|
|
176
227
|
e.removeAttribute("tabindex"), e.focus(t);
|
|
177
228
|
}
|
|
229
|
+
/** Caches a weak reference to the first assigned `rc-menu` element. */
|
|
178
230
|
_handleMenuSlotChange(t) {
|
|
179
231
|
const n = t.currentTarget.assignedElements().find((s) => s.tagName === "RC-MENU");
|
|
180
|
-
n && (this.
|
|
232
|
+
n && (this._$menu = new WeakRef(n));
|
|
181
233
|
}
|
|
234
|
+
/** Prevents the default button action and delegates to `toggleMenu`. */
|
|
182
235
|
_handleTriggerClick(t) {
|
|
183
236
|
t.preventDefault(), this.toggleMenu();
|
|
184
237
|
}
|
|
238
|
+
/** Closes the menu when an `rc-menu-close` event bubbles up from the popup. */
|
|
185
239
|
_handleMenuClose() {
|
|
186
240
|
this.closeMenu();
|
|
187
241
|
}
|
|
242
|
+
/** Closes the menu when an `rc-menu-activate` event bubbles up from the popup. */
|
|
188
243
|
_handleMenuActivate() {
|
|
189
244
|
this.closeMenu();
|
|
190
245
|
}
|
|
246
|
+
/** Closes the menu on clicks outside this component (light dismiss). */
|
|
191
247
|
_handleDocumentClick(t) {
|
|
192
248
|
!this.open || t.composedPath().includes(this) || this.closeMenu(!1);
|
|
193
249
|
}
|
|
250
|
+
/** Syncs trigger ARIA when `open` changes; updates anchor placement when `placement` or `orientation` change. */
|
|
194
251
|
updated(t) {
|
|
195
252
|
super.updated(t), t.has("open") && this._syncTriggerAria(), (t.has("placement") || t.has("orientation")) && this._anchorCtrl.setOptions({ placement: this._effectivePlacement });
|
|
196
253
|
}
|
|
197
254
|
_setOpen(t, e) {
|
|
198
|
-
if (this._open === t)
|
|
255
|
+
if (this._open === t)
|
|
256
|
+
return;
|
|
199
257
|
const r = this._open;
|
|
200
258
|
this._open = t, this.requestUpdate("open", r), this._syncTriggerAria(), e && this._dispatchToggle();
|
|
201
259
|
}
|
|
260
|
+
/** Writes `aria-haspopup` and `aria-expanded` to the trigger button to match the current open state. */
|
|
202
261
|
_syncTriggerAria() {
|
|
203
|
-
const t = this.
|
|
262
|
+
const t = this._$trigger?.deref();
|
|
204
263
|
t?.isConnected && (t.setAttribute("aria-haspopup", "menu"), t.setAttribute("aria-expanded", String(this.open)));
|
|
205
264
|
}
|
|
206
265
|
render() {
|
|
@@ -208,20 +267,19 @@ const a = class a extends p {
|
|
|
208
267
|
<div id="root" part="root">
|
|
209
268
|
<div
|
|
210
269
|
id="trigger-wrap"
|
|
211
|
-
${
|
|
270
|
+
${f(this._onNavigate, {
|
|
212
271
|
navigationAxis: this._resolvedOrientation,
|
|
213
272
|
handleNavAxis: !1,
|
|
214
273
|
handleOpenAxis: !0,
|
|
215
274
|
handleActivate: !0,
|
|
216
275
|
handleEscape: this.open
|
|
217
276
|
})}
|
|
277
|
+
${v()}
|
|
218
278
|
@click=${this._handleTriggerClick}
|
|
219
279
|
>
|
|
220
|
-
<slot
|
|
221
|
-
name="trigger"
|
|
222
|
-
@slotchange=${this._handleTriggerSlotChange}
|
|
223
|
-
></slot>
|
|
280
|
+
<slot name="trigger" @slotchange=${this._handleTriggerSlotChange}></slot>
|
|
224
281
|
</div>
|
|
282
|
+
|
|
225
283
|
<div
|
|
226
284
|
id="popup"
|
|
227
285
|
part="popup"
|
|
@@ -239,32 +297,32 @@ a.styles = [_], a.shadowRootOptions = {
|
|
|
239
297
|
...p.shadowRootOptions,
|
|
240
298
|
delegatesFocus: !0
|
|
241
299
|
};
|
|
242
|
-
let
|
|
243
|
-
|
|
300
|
+
let o = a;
|
|
301
|
+
i([
|
|
244
302
|
l({ type: Boolean, reflect: !0 })
|
|
245
|
-
],
|
|
246
|
-
|
|
303
|
+
], o.prototype, "open", 1);
|
|
304
|
+
i([
|
|
247
305
|
l({ type: Boolean, attribute: "default-open" })
|
|
248
|
-
],
|
|
249
|
-
|
|
306
|
+
], o.prototype, "defaultOpen", 1);
|
|
307
|
+
i([
|
|
250
308
|
l({ type: String, reflect: !0 })
|
|
251
|
-
],
|
|
252
|
-
|
|
309
|
+
], o.prototype, "orientation", 2);
|
|
310
|
+
i([
|
|
253
311
|
l({ reflect: !0 })
|
|
254
|
-
],
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
],
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
],
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
],
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
],
|
|
312
|
+
], o.prototype, "placement", 2);
|
|
313
|
+
i([
|
|
314
|
+
g("#root")
|
|
315
|
+
], o.prototype, "_$root", 2);
|
|
316
|
+
i([
|
|
317
|
+
g("#popup")
|
|
318
|
+
], o.prototype, "_$popup", 2);
|
|
319
|
+
i([
|
|
320
|
+
h()
|
|
321
|
+
], o.prototype, "_$trigger", 2);
|
|
322
|
+
i([
|
|
323
|
+
h()
|
|
324
|
+
], o.prototype, "_$menu", 2);
|
|
267
325
|
export {
|
|
268
|
-
|
|
326
|
+
o as R
|
|
269
327
|
};
|
|
270
|
-
//# sourceMappingURL=rc-menu-button-
|
|
328
|
+
//# sourceMappingURL=rc-menu-button-BEFLL7vi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc-menu-button-BEFLL7vi.js","sources":["../src/rc-menu-button.styles.ts","../src/rc-menu-button.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const menuButtonStyles = css`\n :host {\n display: inline-block;\n }\n\n :host([orientation='vertical']) {\n display: block;\n }\n\n #root {\n display: inline-block;\n }\n\n :host([orientation='vertical']) #root {\n display: block;\n }\n\n #trigger-wrap {\n display: contents;\n }\n\n slot[name='trigger']::slotted(button),\n slot[name='trigger']::slotted([role='button']) {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--rc-menu-button-trigger-gap, var(--rc-item-gap, 0.5em));\n min-block-size: var(--rc-menu-button-trigger-block-size, var(--rc-control-block-size, 2.25em));\n padding: var(--rc-menu-button-trigger-padding-block, var(--rc-control-padding-block, 0.25em))\n var(--rc-menu-button-trigger-padding-inline, var(--rc-control-padding-inline, 0.5em));\n appearance: none;\n box-sizing: border-box;\n border: var(--rc-menu-button-trigger-border, var(--rc-border, 1px solid ButtonBorder));\n border-radius: var(--rc-menu-button-trigger-radius, var(--rc-control-radius, 0.125em));\n background: var(--rc-menu-button-trigger-background, var(--rc-button-bg, ButtonFace));\n color: var(--rc-menu-button-trigger-color, var(--rc-button-text, ButtonText));\n font: inherit;\n text-decoration: none;\n cursor: default;\n user-select: none;\n transition: var(--rc-menu-button-trigger-transition);\n }\n\n slot[name='trigger']::slotted(button:hover),\n slot[name='trigger']::slotted([role='button']:hover) {\n border-color: var(--rc-menu-button-trigger-hover-border-color, currentColor);\n background: var(\n --rc-menu-button-trigger-hover-background,\n color-mix(in srgb, Highlight 8%, transparent)\n );\n color: var(--rc-menu-button-trigger-hover-color, inherit);\n }\n\n slot[name='trigger']::slotted(button[aria-expanded='true']),\n slot[name='trigger']::slotted([role='button'][aria-expanded='true']) {\n border-color: var(--rc-menu-button-trigger-open-border-color, currentColor);\n background: var(\n --rc-menu-button-trigger-open-background,\n color-mix(in srgb, Highlight 12%, transparent)\n );\n color: var(--rc-menu-button-trigger-open-color, inherit);\n }\n\n :host([orientation='vertical']) slot[name='trigger']::slotted(button),\n :host([orientation='vertical']) slot[name='trigger']::slotted([role='button']) {\n inline-size: 100%;\n text-align: start;\n }\n\n #popup {\n z-index: var(--rc-menu-button-popup-z-index, 1000);\n }\n\n #popup[hidden] {\n display: none;\n }\n`;\n\nexport default menuButtonStyles;\n","import { LitElement, html } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\n\nimport {\n AnchorController,\n keyInteraction,\n keyNavigation,\n type AnchorPlacement,\n type KeyboardNavigationAction,\n} from '@rcarls/rc-common';\nimport type { RCMenu } from '@rcarls/rc-menu';\n\nimport menuButtonStyles from './rc-menu-button.styles';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-menu-button': RCMenuButton;\n }\n}\n\n/** Detail payload for the `rc-menu-button-toggle` event. */\nexport interface RCMenuButtonToggleEvent {\n /** Whether the menu transitioned to open (`true`) or closed (`false`). */\n open: boolean;\n}\n\n/**\n * Trigger button that opens an rc-menu popup, following the WAI-ARIA Menu Button pattern.\n *\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-menu-button rc-menu-button docs}\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/ WAI-ARIA Menu Button pattern}\n *\n * @slot trigger - The button element that triggers the menu\n * @slot default - The rc-menu element to display as popup\n *\n * @fires rc-menu-button-toggle - Fired when the menu opens or closes\n *\n * @cssprop [--rc-menu-button-trigger-block-size=var(--rc-control-block-size)] - Minimum block size of the trigger\n * @cssprop [--rc-menu-button-trigger-padding-block=var(--rc-control-padding-block)] - Trigger block-axis padding\n * @cssprop [--rc-menu-button-trigger-padding-inline=var(--rc-control-padding-inline)] - Trigger inline-axis padding\n * @cssprop [--rc-menu-button-trigger-gap=var(--rc-item-gap)] - Gap between flex children in the trigger\n * @cssprop [--rc-menu-button-trigger-border=var(--rc-border)] - Trigger border\n * @cssprop [--rc-menu-button-trigger-radius=var(--rc-control-radius)] - Trigger border radius\n * @cssprop [--rc-menu-button-trigger-background=var(--rc-button-bg)] - Trigger background\n * @cssprop [--rc-menu-button-trigger-color=var(--rc-button-text)] - Trigger text color\n * @cssprop [--rc-menu-button-trigger-transition] - CSS transition applied to the trigger\n * @cssprop [--rc-menu-button-trigger-hover-background=color-mix(in srgb, Highlight 8%, transparent)] - Trigger hover background\n * @cssprop [--rc-menu-button-trigger-hover-color=inherit] - Trigger hover text color\n * @cssprop [--rc-menu-button-trigger-hover-border-color=currentColor] - Trigger hover border color\n * @cssprop [--rc-menu-button-trigger-open-background=color-mix(in srgb, Highlight 12%, transparent)] - Trigger background when the menu is open\n * @cssprop [--rc-menu-button-trigger-open-color=inherit] - Trigger text color when the menu is open\n * @cssprop [--rc-menu-button-trigger-open-border-color=currentColor] - Trigger border color when the menu is open\n * @cssprop [--rc-menu-button-popup-z-index=1000] - Z-index of the popup overlay\n *\n * @csspart root - The root container element\n * @csspart popup - The popup container element\n *\n * @attr placement - Preferred placement of the popup relative to the trigger.\n */\nexport class RCMenuButton extends LitElement {\n static styles = [menuButtonStyles];\n\n /**\n * Enables delegatesFocus so the browser routes focus() on the host element\n * to the slotted trigger button.\n */\n static override shadowRootOptions: ShadowRootInit = {\n ...LitElement.shadowRootOptions,\n\n delegatesFocus: true,\n };\n\n private _defaultOpen = false;\n private _open = false;\n\n /**\n * Whether the menu is currently open.\n *\n * In controlled mode the host owns this value; writing the property applies\n * the state silently without dispatching `rc-menu-button-toggle`.\n */\n @property({ type: Boolean, reflect: true })\n get open(): boolean {\n return this._open;\n }\n\n /** Applies open state in controlled mode without dispatching an event. */\n set open(value: boolean | undefined) {\n const oldValue = this._open;\n\n if (value === undefined) return;\n\n this._setOpen(value, false);\n this.requestUpdate('open', oldValue);\n }\n\n /**\n * Initial open state for uncontrolled mode.\n *\n * Ignored after the first controlled write; use `open` to update state\n * programmatically after mount.\n */\n @property({ type: Boolean, attribute: 'default-open' })\n get defaultOpen(): boolean {\n return this._defaultOpen;\n }\n\n /** Applies the uncontrolled default before any controlled write occurs. */\n set defaultOpen(value: boolean) {\n const oldValue = this._defaultOpen;\n\n this._defaultOpen = value;\n\n if (value && !this._open) {\n this._setOpen(true, false);\n }\n\n this.requestUpdate('defaultOpen', oldValue);\n }\n\n /**\n * Orientation of this menu button, affects which arrow keys open/close the menu.\n *\n * If not set, inherits from a parent rc-menubar or element with role=\"menubar\".\n */\n @property({ type: String, reflect: true })\n orientation: 'horizontal' | 'vertical' | undefined;\n\n /** Preferred placement of the popup relative to the trigger button. */\n @property({ reflect: true })\n placement: AnchorPlacement = 'bottom-start';\n\n /**\n * Popup placement adjusted for orientation.\n *\n * Switches from `bottom-start` to `right-start` when the resolved orientation\n * is `vertical`, so vertical menubars open submenus to the side.\n */\n protected get _effectivePlacement(): AnchorPlacement {\n if (this.placement !== 'bottom-start') {\n return this.placement;\n }\n\n return this._resolvedOrientation === 'vertical' ? 'right-start' : this.placement;\n }\n\n /**\n * Resolved orientation, considering inheritance from parent menubar.\n * Falls back to 'horizontal' if no orientation is set or inherited.\n */\n protected get _resolvedOrientation(): 'horizontal' | 'vertical' {\n if (this.orientation) {\n return this.orientation;\n }\n\n const $menubar = this.closest('rc-menubar, [role=\"menubar\"]');\n\n if ($menubar) {\n const orientation =\n $menubar.getAttribute('orientation') ?? $menubar.getAttribute('aria-orientation');\n\n if (orientation === 'vertical') {\n return 'vertical';\n }\n\n if (orientation === 'horizontal') {\n return 'horizontal';\n }\n }\n\n return 'horizontal';\n }\n\n /** Shadow DOM root container. Serves as the anchor element for `_anchorCtrl`. */\n @query('#root') protected _$root!: HTMLElement;\n\n /** Shadow DOM popup container. Hidden when `open` is `false`. */\n @query('#popup') protected _$popup!: HTMLElement;\n\n /** Weak reference to the slotted trigger button. */\n @state()\n protected _$trigger: WeakRef<HTMLElement> | undefined;\n\n /** Weak reference to the slotted `rc-menu` element. */\n @state()\n protected _$menu: WeakRef<RCMenu> | undefined;\n\n /** Positions the popup relative to the trigger using CSS anchor positioning. */\n protected _anchorCtrl = new AnchorController(this, {\n anchor: () => this._$root ?? null,\n floating: () => this._$popup ?? null,\n shadowHost: () => this,\n placement: 'bottom-start',\n offset: 2,\n });\n\n /** Bound handler for document click (light dismiss). Stored for `removeEventListener` pairing. */\n protected _boundHandleDocumentClick = this._handleDocumentClick.bind(this);\n\n /**\n * Watches the host's own `tabindex` attribute so the trigger button's\n * tabindex can be kept in sync.\n *\n * When a parent (e.g. rc-toolbar) places the\n * host under roving-tabindex management it sets/removes tabindex on the\n * host; we mirror that to the trigger so it doesn't become an independent\n * tab stop. When the host has no tabindex the trigger keeps its natural\n * tabindex=0 for standalone usage.\n */\n protected _tabObserver?: MutationObserver;\n\n /** Installs the tabindex mutation observer and the document-level click listener. */\n override connectedCallback() {\n super.connectedCallback();\n\n this._tabObserver = new MutationObserver(() => this._syncTriggerTabindex());\n this._tabObserver.observe(this, { attributes: true, attributeFilter: ['tabindex'] });\n\n document.addEventListener('click', this._boundHandleDocumentClick, true);\n\n if (this.defaultOpen) {\n this._setOpen(true, false);\n }\n }\n\n /** Disconnects the tabindex observer and removes the document-level click listener. */\n override disconnectedCallback() {\n this._tabObserver?.disconnect();\n this._tabObserver = undefined;\n\n super.disconnectedCallback();\n document.removeEventListener('click', this._boundHandleDocumentClick, true);\n }\n\n /**\n * Opens the menu and moves focus into it.\n *\n * @param focusTarget - which item receives initial focus when the menu opens\n */\n openMenu(focusTarget: 'first' | 'last' = 'first') {\n if (this.open) return;\n\n this._anchorCtrl.setOptions({ placement: this._effectivePlacement });\n this._setOpen(true, true);\n this._anchorCtrl.update();\n\n this.updateComplete.then(() => {\n const $menu = this._$menu?.deref();\n\n focusTarget === 'last' ? $menu?.focusLast() : $menu?.focusFirst();\n });\n }\n\n /**\n * Closes the menu.\n *\n * @param returnFocus - when `true`, returns focus to the trigger button after closing\n */\n closeMenu(returnFocus = true) {\n if (!this.open) return;\n\n this._setOpen(false, true);\n\n if (returnFocus) {\n const $trigger = this._$trigger?.deref();\n\n $trigger?.focus();\n }\n }\n\n /** Toggles the menu between open and closed. */\n toggleMenu() {\n if (this.open) {\n this.closeMenu();\n } else {\n this.openMenu();\n }\n }\n\n /** Dispatches the `rc-menu-button-toggle` bubbling composed event with the current open state. */\n protected _dispatchToggle() {\n this.dispatchEvent(\n new CustomEvent<RCMenuButtonToggleEvent>('rc-menu-button-toggle', {\n bubbles: true,\n composed: true,\n detail: { open: this.open },\n }),\n );\n }\n\n /** Caches a weak reference to the first assigned trigger element and syncs its ARIA and tabindex state. */\n protected _handleTriggerSlotChange(e: Event) {\n const slot = e.currentTarget as HTMLSlotElement;\n const $trigger = slot.assignedElements()[0] as HTMLElement | undefined;\n\n if ($trigger) {\n this._$trigger = new WeakRef($trigger);\n this._syncTriggerAria();\n this._syncTriggerTabindex();\n }\n }\n\n /**\n * Mirrors the host's `tabindex` attribute to the trigger button.\n *\n * When a roving-tabindex parent (e.g. rc-toolbar) marks this host inactive\n * with `tabindex=\"-1\"`, the trigger must also be suppressed so it does not\n * become an independent tab stop. When the host is the active item\n * (`tabindex=\"0\"`) or standalone (no tabindex), the trigger retains its\n * natural focusability so that Chrome's delegatesFocus can route sequential\n * Tab navigation through the host to the trigger.\n */\n protected _syncTriggerTabindex() {\n const $trigger = this._$trigger?.deref();\n\n if (!$trigger) return;\n\n if (this.getAttribute('tabindex') === '-1') {\n $trigger.setAttribute('tabindex', '-1');\n } else {\n $trigger.removeAttribute('tabindex');\n }\n }\n\n /**\n * Overrides the default focus() so that programmatic focus calls (e.g. from\n * a roving-tabindex parent navigating back to this element via arrow keys)\n * always reach the trigger.\n *\n * Chrome's delegatesFocus will not delegate to a tabindex=\"-1\" element, so\n * when the toolbar marks this host inactive it suppresses the trigger — and\n * the next focusItem() call silently fails. Lifting the suppression here and\n * directly calling trigger.focus() bypasses that restriction.\n */\n override focus(options?: FocusOptions) {\n const $trigger = this._$trigger?.deref();\n\n if (!$trigger) {\n super.focus(options);\n return;\n }\n\n $trigger.removeAttribute('tabindex');\n $trigger.focus(options);\n }\n\n /** Caches a weak reference to the first assigned `rc-menu` element. */\n protected _handleMenuSlotChange(e: Event) {\n const slot = e.currentTarget as HTMLSlotElement;\n const elements = slot.assignedElements();\n const $menu = elements.find((el) => el.tagName === 'RC-MENU') as RCMenu | undefined;\n\n if ($menu) {\n this._$menu = new WeakRef($menu);\n }\n }\n\n /**\n * Keyboard navigation handler.\n *\n * Opens or closes the menu in response to arrow/Enter/Escape keys.\n **/\n protected _onNavigate = (action: KeyboardNavigationAction) => {\n switch (action) {\n case 'open-to-first':\n case 'activate':\n this.openMenu();\n break;\n\n case 'open-to-last':\n this.openMenu('last');\n break;\n\n case 'escape':\n this.closeMenu();\n break;\n }\n };\n\n /** Prevents the default button action and delegates to `toggleMenu`. */\n protected _handleTriggerClick(e: MouseEvent) {\n e.preventDefault();\n\n this.toggleMenu();\n }\n\n /** Closes the menu when an `rc-menu-close` event bubbles up from the popup. */\n protected _handleMenuClose() {\n this.closeMenu();\n }\n\n /** Closes the menu when an `rc-menu-activate` event bubbles up from the popup. */\n protected _handleMenuActivate() {\n this.closeMenu();\n }\n\n /** Closes the menu on clicks outside this component (light dismiss). */\n protected _handleDocumentClick(e: MouseEvent) {\n if (!this.open) {\n return;\n }\n\n const path = e.composedPath();\n\n if (path.includes(this)) {\n return;\n }\n\n this.closeMenu(false);\n }\n\n /** Syncs trigger ARIA when `open` changes; updates anchor placement when `placement` or `orientation` change. */\n protected override updated(changedProperties: Map<PropertyKey, unknown>) {\n super.updated(changedProperties);\n\n if (changedProperties.has('open')) {\n this._syncTriggerAria();\n }\n\n if (changedProperties.has('placement') || changedProperties.has('orientation')) {\n this._anchorCtrl.setOptions({ placement: this._effectivePlacement });\n }\n }\n\n private _setOpen(open: boolean, dispatch: boolean): void {\n if (this._open === open) {\n return;\n }\n\n const oldValue = this._open;\n\n this._open = open;\n\n this.requestUpdate('open', oldValue);\n this._syncTriggerAria();\n\n if (dispatch) {\n this._dispatchToggle();\n }\n }\n\n /** Writes `aria-haspopup` and `aria-expanded` to the trigger button to match the current open state. */\n protected _syncTriggerAria(): void {\n const $trigger = this._$trigger?.deref();\n\n if (!$trigger?.isConnected) {\n return;\n }\n\n $trigger.setAttribute('aria-haspopup', 'menu');\n $trigger.setAttribute('aria-expanded', String(this.open));\n }\n\n protected override render() {\n return html`\n <div id=\"root\" part=\"root\">\n <div\n id=\"trigger-wrap\"\n ${keyNavigation(this._onNavigate, {\n navigationAxis: this._resolvedOrientation,\n handleNavAxis: false,\n handleOpenAxis: true,\n handleActivate: true,\n handleEscape: this.open,\n })}\n ${keyInteraction()}\n @click=${this._handleTriggerClick}\n >\n <slot name=\"trigger\" @slotchange=${this._handleTriggerSlotChange}></slot>\n </div>\n\n <div\n id=\"popup\"\n part=\"popup\"\n ?hidden=${!this.open}\n @rc-menu-close=${this._handleMenuClose}\n @rc-menu-activate=${this._handleMenuActivate}\n >\n <slot @slotchange=${this._handleMenuSlotChange}></slot>\n </div>\n </div>\n `;\n }\n}\n\nexport default RCMenuButton;\n"],"names":["menuButtonStyles","css","_RCMenuButton","LitElement","AnchorController","action","value","oldValue","$menubar","orientation","focusTarget","$menu","returnFocus","e","$trigger","options","el","changedProperties","open","dispatch","html","keyNavigation","keyInteraction","RCMenuButton","__decorateClass","property","query","state"],"mappings":"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"}
|
package/dist/rc-menu-button.js
CHANGED
|
@@ -1,92 +1,142 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
|
-
import { AnchorPlacement } from '@rcarls/rc-common';
|
|
3
|
-
|
|
4
|
-
open: boolean;
|
|
5
|
-
}
|
|
2
|
+
import { AnchorController, AnchorPlacement, KeyboardNavigationAction } from '@rcarls/rc-common';
|
|
3
|
+
import { RCMenu } from '@rcarls/rc-menu';
|
|
6
4
|
declare global {
|
|
7
5
|
interface HTMLElementTagNameMap {
|
|
8
6
|
'rc-menu-button': RCMenuButton;
|
|
9
7
|
}
|
|
10
8
|
}
|
|
9
|
+
/** Detail payload for the `rc-menu-button-toggle` event. */
|
|
10
|
+
export interface RCMenuButtonToggleEvent {
|
|
11
|
+
/** Whether the menu transitioned to open (`true`) or closed (`false`). */
|
|
12
|
+
open: boolean;
|
|
13
|
+
}
|
|
11
14
|
/**
|
|
12
|
-
*
|
|
15
|
+
* Trigger button that opens an rc-menu popup, following the WAI-ARIA Menu Button pattern.
|
|
16
|
+
*
|
|
17
|
+
*
|
|
18
|
+
* @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-menu-button rc-menu-button docs}
|
|
19
|
+
* @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/ WAI-ARIA Menu Button pattern}
|
|
13
20
|
*
|
|
14
|
-
* @see https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/
|
|
15
21
|
* @slot trigger - The button element that triggers the menu
|
|
16
22
|
* @slot default - The rc-menu element to display as popup
|
|
23
|
+
*
|
|
17
24
|
* @fires rc-menu-button-toggle - Fired when the menu opens or closes
|
|
25
|
+
*
|
|
18
26
|
* @cssprop [--rc-menu-button-trigger-block-size=var(--rc-control-block-size)] - Minimum block size of the trigger
|
|
19
27
|
* @cssprop [--rc-menu-button-trigger-padding-block=var(--rc-control-padding-block)] - Trigger block-axis padding
|
|
20
28
|
* @cssprop [--rc-menu-button-trigger-padding-inline=var(--rc-control-padding-inline)] - Trigger inline-axis padding
|
|
29
|
+
* @cssprop [--rc-menu-button-trigger-gap=var(--rc-item-gap)] - Gap between flex children in the trigger
|
|
21
30
|
* @cssprop [--rc-menu-button-trigger-border=var(--rc-border)] - Trigger border
|
|
22
31
|
* @cssprop [--rc-menu-button-trigger-radius=var(--rc-control-radius)] - Trigger border radius
|
|
23
32
|
* @cssprop [--rc-menu-button-trigger-background=var(--rc-button-bg)] - Trigger background
|
|
24
33
|
* @cssprop [--rc-menu-button-trigger-color=var(--rc-button-text)] - Trigger text color
|
|
34
|
+
* @cssprop [--rc-menu-button-trigger-transition] - CSS transition applied to the trigger
|
|
35
|
+
* @cssprop [--rc-menu-button-trigger-hover-background=color-mix(in srgb, Highlight 8%, transparent)] - Trigger hover background
|
|
36
|
+
* @cssprop [--rc-menu-button-trigger-hover-color=inherit] - Trigger hover text color
|
|
37
|
+
* @cssprop [--rc-menu-button-trigger-hover-border-color=currentColor] - Trigger hover border color
|
|
38
|
+
* @cssprop [--rc-menu-button-trigger-open-background=color-mix(in srgb, Highlight 12%, transparent)] - Trigger background when the menu is open
|
|
39
|
+
* @cssprop [--rc-menu-button-trigger-open-color=inherit] - Trigger text color when the menu is open
|
|
40
|
+
* @cssprop [--rc-menu-button-trigger-open-border-color=currentColor] - Trigger border color when the menu is open
|
|
25
41
|
* @cssprop [--rc-menu-button-popup-z-index=1000] - Z-index of the popup overlay
|
|
42
|
+
*
|
|
26
43
|
* @csspart root - The root container element
|
|
27
44
|
* @csspart popup - The popup container element
|
|
45
|
+
*
|
|
28
46
|
* @attr placement - Preferred placement of the popup relative to the trigger.
|
|
29
47
|
*/
|
|
30
48
|
export declare class RCMenuButton extends LitElement {
|
|
31
49
|
static styles: import('lit').CSSResult[];
|
|
32
50
|
/**
|
|
33
51
|
* Enables delegatesFocus so the browser routes focus() on the host element
|
|
34
|
-
* to the slotted trigger button.
|
|
35
|
-
* roving-tabindex group (e.g. rc-toolbar) as a single tab stop while the
|
|
36
|
-
* trigger remains the visible focus target.
|
|
52
|
+
* to the slotted trigger button.
|
|
37
53
|
*/
|
|
38
54
|
static shadowRootOptions: ShadowRootInit;
|
|
39
55
|
private _defaultOpen;
|
|
40
56
|
private _open;
|
|
41
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Whether the menu is currently open.
|
|
59
|
+
*
|
|
60
|
+
* In controlled mode the host owns this value; writing the property applies
|
|
61
|
+
* the state silently without dispatching `rc-menu-button-toggle`.
|
|
62
|
+
*/
|
|
42
63
|
get open(): boolean;
|
|
43
|
-
/**
|
|
64
|
+
/** Applies open state in controlled mode without dispatching an event. */
|
|
44
65
|
set open(value: boolean | undefined);
|
|
45
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Initial open state for uncontrolled mode.
|
|
68
|
+
*
|
|
69
|
+
* Ignored after the first controlled write; use `open` to update state
|
|
70
|
+
* programmatically after mount.
|
|
71
|
+
*/
|
|
46
72
|
get defaultOpen(): boolean;
|
|
47
|
-
/**
|
|
73
|
+
/** Applies the uncontrolled default before any controlled write occurs. */
|
|
48
74
|
set defaultOpen(value: boolean);
|
|
49
75
|
/**
|
|
50
76
|
* Orientation of this menu button, affects which arrow keys open/close the menu.
|
|
77
|
+
*
|
|
51
78
|
* If not set, inherits from a parent rc-menubar or element with role="menubar".
|
|
52
79
|
*/
|
|
53
80
|
orientation: 'horizontal' | 'vertical' | undefined;
|
|
54
81
|
/** Preferred placement of the popup relative to the trigger button. */
|
|
55
82
|
placement: AnchorPlacement;
|
|
56
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Popup placement adjusted for orientation.
|
|
85
|
+
*
|
|
86
|
+
* Switches from `bottom-start` to `right-start` when the resolved orientation
|
|
87
|
+
* is `vertical`, so vertical menubars open submenus to the side.
|
|
88
|
+
*/
|
|
89
|
+
protected get _effectivePlacement(): AnchorPlacement;
|
|
57
90
|
/**
|
|
58
91
|
* Resolved orientation, considering inheritance from parent menubar.
|
|
59
92
|
* Falls back to 'horizontal' if no orientation is set or inherited.
|
|
60
93
|
*/
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
94
|
+
protected get _resolvedOrientation(): 'horizontal' | 'vertical';
|
|
95
|
+
/** Shadow DOM root container. Serves as the anchor element for `_anchorCtrl`. */
|
|
96
|
+
protected _$root: HTMLElement;
|
|
97
|
+
/** Shadow DOM popup container. Hidden when `open` is `false`. */
|
|
98
|
+
protected _$popup: HTMLElement;
|
|
99
|
+
/** Weak reference to the slotted trigger button. */
|
|
100
|
+
protected _$trigger: WeakRef<HTMLElement> | undefined;
|
|
101
|
+
/** Weak reference to the slotted `rc-menu` element. */
|
|
102
|
+
protected _$menu: WeakRef<RCMenu> | undefined;
|
|
103
|
+
/** Positions the popup relative to the trigger using CSS anchor positioning. */
|
|
104
|
+
protected _anchorCtrl: AnchorController;
|
|
105
|
+
/** Bound handler for document click (light dismiss). Stored for `removeEventListener` pairing. */
|
|
106
|
+
protected _boundHandleDocumentClick: (e: MouseEvent) => void;
|
|
71
107
|
/**
|
|
72
108
|
* Watches the host's own `tabindex` attribute so the trigger button's
|
|
73
|
-
* tabindex can be kept in sync.
|
|
109
|
+
* tabindex can be kept in sync.
|
|
110
|
+
*
|
|
111
|
+
* When a parent (e.g. rc-toolbar) places the
|
|
74
112
|
* host under roving-tabindex management it sets/removes tabindex on the
|
|
75
113
|
* host; we mirror that to the trigger so it doesn't become an independent
|
|
76
114
|
* tab stop. When the host has no tabindex the trigger keeps its natural
|
|
77
115
|
* tabindex=0 for standalone usage.
|
|
78
116
|
*/
|
|
79
|
-
|
|
117
|
+
protected _tabObserver?: MutationObserver;
|
|
118
|
+
/** Installs the tabindex mutation observer and the document-level click listener. */
|
|
80
119
|
connectedCallback(): void;
|
|
120
|
+
/** Disconnects the tabindex observer and removes the document-level click listener. */
|
|
81
121
|
disconnectedCallback(): void;
|
|
82
|
-
/**
|
|
122
|
+
/**
|
|
123
|
+
* Opens the menu and moves focus into it.
|
|
124
|
+
*
|
|
125
|
+
* @param focusTarget - which item receives initial focus when the menu opens
|
|
126
|
+
*/
|
|
83
127
|
openMenu(focusTarget?: 'first' | 'last'): void;
|
|
84
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* Closes the menu.
|
|
130
|
+
*
|
|
131
|
+
* @param returnFocus - when `true`, returns focus to the trigger button after closing
|
|
132
|
+
*/
|
|
85
133
|
closeMenu(returnFocus?: boolean): void;
|
|
86
|
-
/** Toggles the menu open
|
|
134
|
+
/** Toggles the menu between open and closed. */
|
|
87
135
|
toggleMenu(): void;
|
|
88
|
-
|
|
89
|
-
|
|
136
|
+
/** Dispatches the `rc-menu-button-toggle` bubbling composed event with the current open state. */
|
|
137
|
+
protected _dispatchToggle(): void;
|
|
138
|
+
/** Caches a weak reference to the first assigned trigger element and syncs its ARIA and tabindex state. */
|
|
139
|
+
protected _handleTriggerSlotChange(e: Event): void;
|
|
90
140
|
/**
|
|
91
141
|
* Mirrors the host's `tabindex` attribute to the trigger button.
|
|
92
142
|
*
|
|
@@ -97,7 +147,7 @@ export declare class RCMenuButton extends LitElement {
|
|
|
97
147
|
* natural focusability so that Chrome's delegatesFocus can route sequential
|
|
98
148
|
* Tab navigation through the host to the trigger.
|
|
99
149
|
*/
|
|
100
|
-
|
|
150
|
+
protected _syncTriggerTabindex(): void;
|
|
101
151
|
/**
|
|
102
152
|
* Overrides the default focus() so that programmatic focus calls (e.g. from
|
|
103
153
|
* a roving-tabindex parent navigating back to this element via arrow keys)
|
|
@@ -109,15 +159,27 @@ export declare class RCMenuButton extends LitElement {
|
|
|
109
159
|
* directly calling trigger.focus() bypasses that restriction.
|
|
110
160
|
*/
|
|
111
161
|
focus(options?: FocusOptions): void;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
162
|
+
/** Caches a weak reference to the first assigned `rc-menu` element. */
|
|
163
|
+
protected _handleMenuSlotChange(e: Event): void;
|
|
164
|
+
/**
|
|
165
|
+
* Keyboard navigation handler.
|
|
166
|
+
*
|
|
167
|
+
* Opens or closes the menu in response to arrow/Enter/Escape keys.
|
|
168
|
+
**/
|
|
169
|
+
protected _onNavigate: (action: KeyboardNavigationAction) => void;
|
|
170
|
+
/** Prevents the default button action and delegates to `toggleMenu`. */
|
|
171
|
+
protected _handleTriggerClick(e: MouseEvent): void;
|
|
172
|
+
/** Closes the menu when an `rc-menu-close` event bubbles up from the popup. */
|
|
173
|
+
protected _handleMenuClose(): void;
|
|
174
|
+
/** Closes the menu when an `rc-menu-activate` event bubbles up from the popup. */
|
|
175
|
+
protected _handleMenuActivate(): void;
|
|
176
|
+
/** Closes the menu on clicks outside this component (light dismiss). */
|
|
177
|
+
protected _handleDocumentClick(e: MouseEvent): void;
|
|
178
|
+
/** Syncs trigger ARIA when `open` changes; updates anchor placement when `placement` or `orientation` change. */
|
|
118
179
|
protected updated(changedProperties: Map<PropertyKey, unknown>): void;
|
|
119
180
|
private _setOpen;
|
|
120
|
-
|
|
181
|
+
/** Writes `aria-haspopup` and `aria-expanded` to the trigger button to match the current open state. */
|
|
182
|
+
protected _syncTriggerAria(): void;
|
|
121
183
|
protected render(): import('lit').TemplateResult<1>;
|
|
122
184
|
}
|
|
123
185
|
export default RCMenuButton;
|
package/package.json
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
7
|
-
"description": "
|
|
6
|
+
"version": "0.3.0",
|
|
7
|
+
"description": "Trigger button that opens an rc-menu popup.",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/richardcarls/rc-webcomponents.git",
|
|
11
11
|
"directory": "packages/rc-menu-button"
|
|
12
12
|
},
|
|
13
|
-
"homepage": "https://github.
|
|
13
|
+
"homepage": "https://richardcarls.github.io/rc-webcomponents/components/rc-menu-button",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"files": [
|
|
@@ -37,23 +37,20 @@
|
|
|
37
37
|
],
|
|
38
38
|
"customElements": "dist/custom-elements.json",
|
|
39
39
|
"scripts": {
|
|
40
|
-
"dev": "vite",
|
|
41
40
|
"build": "tsc && vite build && cem analyze",
|
|
42
41
|
"cem:analyze": "cem analyze",
|
|
43
42
|
"preview": "vite preview",
|
|
44
|
-
"test:browser": "vitest",
|
|
45
|
-
"test:browser:chrome": "vitest --project=chromium",
|
|
46
|
-
"test:browser:firefox": "vitest --project=firefox"
|
|
47
|
-
"yalc:publish": "yalc publish --push"
|
|
43
|
+
"test:browser": "vitest --run",
|
|
44
|
+
"test:browser:chrome": "vitest --run --project=chromium",
|
|
45
|
+
"test:browser:firefox": "vitest --run --project=firefox"
|
|
48
46
|
},
|
|
49
47
|
"dependencies": {
|
|
50
|
-
"@rcarls/rc-common": "workspace
|
|
51
|
-
"@rcarls/rc-menu": "workspace
|
|
48
|
+
"@rcarls/rc-common": "workspace:*",
|
|
49
|
+
"@rcarls/rc-menu": "workspace:*",
|
|
52
50
|
"@types/node": "^25.6.0"
|
|
53
51
|
},
|
|
54
52
|
"devDependencies": {
|
|
55
53
|
"@custom-elements-manifest/analyzer": "0.11.0",
|
|
56
|
-
"@guanghechen/rollup-plugin-copy": "^6.0.9",
|
|
57
54
|
"@vitest/browser-playwright": "4.1.5",
|
|
58
55
|
"lit": "^3.0.0",
|
|
59
56
|
"playwright": "^1.56.0",
|
package/dist/demo.css
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
*, *::before, *::after {
|
|
2
|
-
box-sizing: border-box;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
:root {
|
|
6
|
-
color-scheme: light dark;
|
|
7
|
-
--rc-accent: Highlight; /* Chrome doesn't support AccentColor on the open web */
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
@supports (color: AccentColor) {
|
|
11
|
-
:root {
|
|
12
|
-
--rc-accent: AccentColor;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
[data-theme="light"] { color-scheme: light; }
|
|
17
|
-
[data-theme="dark"] { color-scheme: dark; }
|
|
18
|
-
|
|
19
|
-
[data-theme="solarized-light"] {
|
|
20
|
-
color-scheme: light;
|
|
21
|
-
--rc-surface: #fdf6e3;
|
|
22
|
-
--rc-text: #657b83;
|
|
23
|
-
--rc-border: 1px solid #93a1a1;
|
|
24
|
-
--rc-shadow: 0 2px 8px rgba(0, 0, 0, .12);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
[data-theme="solarized-dark"] {
|
|
28
|
-
color-scheme: dark;
|
|
29
|
-
--rc-surface: #002b36;
|
|
30
|
-
--rc-text: #839496;
|
|
31
|
-
--rc-border: 1px solid #586e75;
|
|
32
|
-
--rc-shadow: 0 2px 8px rgba(0, 0, 0, .3);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
body {
|
|
36
|
-
font-family: system-ui, sans-serif;
|
|
37
|
-
margin: 0;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.demo-page {
|
|
41
|
-
max-width: 60rem;
|
|
42
|
-
margin: 0 auto;
|
|
43
|
-
padding: 2rem 1.5rem;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.demo-controls {
|
|
47
|
-
display: flex;
|
|
48
|
-
align-items: center;
|
|
49
|
-
gap: 1rem;
|
|
50
|
-
margin-bottom: 2rem;
|
|
51
|
-
padding-bottom: 1rem;
|
|
52
|
-
border-bottom: 1px solid ButtonBorder;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.demo-controls .demo-title {
|
|
56
|
-
flex: 1;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.demo-controls h1 {
|
|
60
|
-
margin: 0 0 0.15rem;
|
|
61
|
-
font-size: 1.5rem;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.demo-controls a {
|
|
65
|
-
font-size: 0.8rem;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.demo-section {
|
|
69
|
-
margin-bottom: 2rem;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.demo-section h2 {
|
|
73
|
-
margin: 0 0 0.5rem;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.theme-picker {
|
|
77
|
-
padding: 0.35em 0.75em;
|
|
78
|
-
font-family: inherit;
|
|
79
|
-
font-size: 0.8rem;
|
|
80
|
-
cursor: pointer;
|
|
81
|
-
border: 1px solid ButtonBorder;
|
|
82
|
-
border-radius: 4px;
|
|
83
|
-
background: Canvas;
|
|
84
|
-
color: CanvasText;
|
|
85
|
-
}
|
package/dist/demo.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const THEMES = ['', 'light', 'dark', 'solarized-light', 'solarized-dark'];
|
|
2
|
-
const LABELS = ['Auto', 'Light', 'Dark', 'Solarized ☀', 'Solarized ☾'];
|
|
3
|
-
|
|
4
|
-
const stored = localStorage.getItem('rc-demo-theme') ?? '';
|
|
5
|
-
applyTheme(stored);
|
|
6
|
-
|
|
7
|
-
function applyTheme(theme) {
|
|
8
|
-
if (theme) {
|
|
9
|
-
document.documentElement.dataset.theme = theme;
|
|
10
|
-
} else {
|
|
11
|
-
delete document.documentElement.dataset.theme;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function currentIndex() {
|
|
16
|
-
const current = document.documentElement.dataset.theme ?? '';
|
|
17
|
-
const idx = THEMES.indexOf(current);
|
|
18
|
-
return idx === -1 ? 0 : idx;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function updateButtons() {
|
|
22
|
-
const label = LABELS[currentIndex()];
|
|
23
|
-
document.querySelectorAll('.theme-picker').forEach((btn) => {
|
|
24
|
-
btn.textContent = label;
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
window.cycleTheme = function () {
|
|
29
|
-
const next = THEMES[(currentIndex() + 1) % THEMES.length];
|
|
30
|
-
applyTheme(next);
|
|
31
|
-
localStorage.setItem('rc-demo-theme', next);
|
|
32
|
-
updateButtons();
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
36
|
-
updateButtons();
|
|
37
|
-
document.querySelectorAll('.theme-picker').forEach((btn) => {
|
|
38
|
-
btn.addEventListener('click', window.cycleTheme);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rc-menu-button-CmzW7P4h.js","sources":["../src/rc-menu-button.styles.ts","../src/rc-menu-button.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const menuButtonStyles = css`\n :host {\n display: inline-block;\n }\n\n :host([orientation='vertical']) {\n display: block;\n }\n\n #root {\n display: inline-block;\n }\n\n :host([orientation='vertical']) #root {\n display: block;\n }\n\n #trigger-wrap {\n display: contents;\n }\n\n slot[name='trigger']::slotted(button),\n slot[name='trigger']::slotted([role='button']) {\n min-block-size: var(--rc-menu-button-trigger-block-size, var(--rc-control-block-size, 2.25em));\n padding: var(--rc-menu-button-trigger-padding-block, var(--rc-control-padding-block, 0.25em))\n var(--rc-menu-button-trigger-padding-inline, var(--rc-control-padding-inline, 0.5em));\n border: var(--rc-menu-button-trigger-border, var(--rc-border, 1px solid ButtonBorder));\n border-radius: var(--rc-menu-button-trigger-radius, var(--rc-control-radius, 0.125em));\n background: var(--rc-menu-button-trigger-background, var(--rc-button-bg, ButtonFace));\n color: var(--rc-menu-button-trigger-color, var(--rc-button-text, ButtonText));\n font: inherit;\n }\n\n :host([orientation='vertical']) slot[name='trigger']::slotted(button),\n :host([orientation='vertical']) slot[name='trigger']::slotted([role='button']) {\n inline-size: 100%;\n text-align: start;\n }\n\n #popup {\n z-index: var(--rc-menu-button-popup-z-index, 1000);\n }\n\n #popup[hidden] {\n display: none;\n }\n`;\n\nexport default menuButtonStyles;\n","import { LitElement, html } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\n\nimport {\n AnchorController,\n keyNavigation,\n type AnchorPlacement,\n type KeyboardNavigationAction,\n} from '@rcarls/rc-common';\nimport type { RCMenu } from '@rcarls/rc-menu';\n\nimport menuButtonStyles from './rc-menu-button.styles';\n\nexport interface RCMenuButtonToggleEvent {\n open: boolean;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-menu-button': RCMenuButton;\n }\n}\n\n/**\n * A menu button component that opens/closes a menu popup\n *\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/\n * @slot trigger - The button element that triggers the menu\n * @slot default - The rc-menu element to display as popup\n * @fires rc-menu-button-toggle - Fired when the menu opens or closes\n * @cssprop [--rc-menu-button-trigger-block-size=var(--rc-control-block-size)] - Minimum block size of the trigger\n * @cssprop [--rc-menu-button-trigger-padding-block=var(--rc-control-padding-block)] - Trigger block-axis padding\n * @cssprop [--rc-menu-button-trigger-padding-inline=var(--rc-control-padding-inline)] - Trigger inline-axis padding\n * @cssprop [--rc-menu-button-trigger-border=var(--rc-border)] - Trigger border\n * @cssprop [--rc-menu-button-trigger-radius=var(--rc-control-radius)] - Trigger border radius\n * @cssprop [--rc-menu-button-trigger-background=var(--rc-button-bg)] - Trigger background\n * @cssprop [--rc-menu-button-trigger-color=var(--rc-button-text)] - Trigger text color\n * @cssprop [--rc-menu-button-popup-z-index=1000] - Z-index of the popup overlay\n * @csspart root - The root container element\n * @csspart popup - The popup container element\n * @attr placement - Preferred placement of the popup relative to the trigger.\n */\nexport class RCMenuButton extends LitElement {\n static styles = [menuButtonStyles];\n\n /**\n * Enables delegatesFocus so the browser routes focus() on the host element\n * to the slotted trigger button. This lets the host participate in a\n * roving-tabindex group (e.g. rc-toolbar) as a single tab stop while the\n * trigger remains the visible focus target.\n */\n static override shadowRootOptions: ShadowRootInit = {\n ...LitElement.shadowRootOptions,\n delegatesFocus: true,\n };\n\n private _defaultOpen = false;\n private _open = false;\n\n /** Whether the menu is currently open. Host writes update silently. */\n @property({ type: Boolean, reflect: true })\n get open(): boolean {\n return this._open;\n }\n\n /** Whether the menu is currently open. Host writes update silently. */\n set open(value: boolean | undefined) {\n const oldValue = this._open;\n\n if (value === undefined) return;\n\n this._setOpen(value, false);\n this.requestUpdate('open', oldValue);\n }\n\n /** Initial uncontrolled open state. */\n @property({ type: Boolean, attribute: 'default-open' })\n get defaultOpen(): boolean {\n return this._defaultOpen;\n }\n\n /** Initial uncontrolled open state. */\n set defaultOpen(value: boolean) {\n const oldValue = this._defaultOpen;\n\n this._defaultOpen = value;\n\n if (value && !this._open) {\n this._setOpen(true, false);\n }\n\n this.requestUpdate('defaultOpen', oldValue);\n }\n\n /**\n * Orientation of this menu button, affects which arrow keys open/close the menu.\n * If not set, inherits from a parent rc-menubar or element with role=\"menubar\".\n */\n @property({ type: String, reflect: true })\n orientation: 'horizontal' | 'vertical' | undefined;\n\n /** Preferred placement of the popup relative to the trigger button. */\n @property({ reflect: true })\n placement: AnchorPlacement = 'bottom-start';\n\n private get _effectivePlacement(): AnchorPlacement {\n if (this.placement !== 'bottom-start') return this.placement;\n return this._resolvedOrientation === 'vertical' ? 'right-start' : this.placement;\n }\n\n /**\n * Resolved orientation, considering inheritance from parent menubar.\n * Falls back to 'horizontal' if no orientation is set or inherited.\n */\n private get _resolvedOrientation(): 'horizontal' | 'vertical' {\n if (this.orientation) return this.orientation;\n\n // Inherit from parent rc-menubar or element with role=\"menubar\"\n const menubar = this.closest('rc-menubar, [role=\"menubar\"]');\n if (menubar) {\n const orientation =\n menubar.getAttribute('orientation') ??\n menubar.getAttribute('aria-orientation');\n if (orientation === 'vertical') return 'vertical';\n if (orientation === 'horizontal') return 'horizontal';\n }\n\n return 'horizontal';\n }\n\n @query('#root') private _$root!: HTMLElement;\n @query('#popup') private _$popup!: HTMLElement;\n\n /** Reference to the trigger button element */\n @state()\n private _trigger: WeakRef<HTMLElement> | undefined;\n\n /** Reference to the menu element */\n @state()\n private _menu: WeakRef<RCMenu> | undefined;\n\n private _anchorCtrl = new AnchorController(this, {\n anchor: () => this._$root ?? null,\n floating: () => this._$popup ?? null,\n shadowHost: () => this,\n placement: 'bottom-start',\n offset: 2,\n });\n\n /** Bound handler for document click (light dismiss) */\n private _boundHandleDocumentClick = this._handleDocumentClick.bind(this);\n\n /**\n * Watches the host's own `tabindex` attribute so the trigger button's\n * tabindex can be kept in sync. When a parent (e.g. rc-toolbar) places the\n * host under roving-tabindex management it sets/removes tabindex on the\n * host; we mirror that to the trigger so it doesn't become an independent\n * tab stop. When the host has no tabindex the trigger keeps its natural\n * tabindex=0 for standalone usage.\n */\n private _tabObserver?: MutationObserver;\n\n connectedCallback() {\n super.connectedCallback();\n\n this._tabObserver = new MutationObserver(() => this._syncTriggerTabindex());\n this._tabObserver.observe(this, { attributes: true, attributeFilter: ['tabindex'] });\n\n document.addEventListener('click', this._boundHandleDocumentClick, true);\n\n if (this.defaultOpen) {\n this._setOpen(true, false);\n }\n }\n\n disconnectedCallback() {\n this._tabObserver?.disconnect();\n this._tabObserver = undefined;\n\n super.disconnectedCallback();\n document.removeEventListener('click', this._boundHandleDocumentClick, true);\n }\n\n /** Opens the menu and focuses the first or last item */\n openMenu(focusTarget: 'first' | 'last' = 'first') {\n if (this.open) return;\n\n this._anchorCtrl.setOptions({ placement: this._effectivePlacement });\n this._setOpen(true, true);\n this._anchorCtrl.update();\n\n this.updateComplete.then(() => {\n const menu = this._menu?.deref();\n focusTarget === 'last' ? menu?.focusLast() : menu?.focusFirst();\n });\n }\n\n /** Closes the menu and optionally returns focus to trigger */\n closeMenu(returnFocus = true) {\n if (!this.open) return;\n\n this._setOpen(false, true);\n\n if (returnFocus) {\n const trigger = this._trigger?.deref();\n trigger?.focus();\n }\n }\n\n /** Toggles the menu open/closed */\n toggleMenu() {\n if (this.open) {\n this.closeMenu();\n } else {\n this.openMenu();\n }\n }\n\n private _dispatchToggle() {\n this.dispatchEvent(\n new CustomEvent<RCMenuButtonToggleEvent>('rc-menu-button-toggle', {\n bubbles: true,\n composed: true,\n detail: { open: this.open },\n }),\n );\n }\n\n private _handleTriggerSlotChange(e: Event) {\n const slot = e.currentTarget as HTMLSlotElement;\n const trigger = slot.assignedElements()[0] as HTMLElement | undefined;\n\n if (trigger) {\n this._trigger = new WeakRef(trigger);\n this._syncTriggerAria();\n this._syncTriggerTabindex();\n }\n }\n\n /**\n * Mirrors the host's `tabindex` attribute to the trigger button.\n *\n * When a roving-tabindex parent (e.g. rc-toolbar) marks this host inactive\n * with `tabindex=\"-1\"`, the trigger must also be suppressed so it does not\n * become an independent tab stop. When the host is the active item\n * (`tabindex=\"0\"`) or standalone (no tabindex), the trigger retains its\n * natural focusability so that Chrome's delegatesFocus can route sequential\n * Tab navigation through the host to the trigger.\n */\n private _syncTriggerTabindex() {\n const trigger = this._trigger?.deref();\n\n if (!trigger) return;\n\n if (this.getAttribute('tabindex') === '-1') {\n trigger.setAttribute('tabindex', '-1');\n } else {\n trigger.removeAttribute('tabindex');\n }\n }\n\n /**\n * Overrides the default focus() so that programmatic focus calls (e.g. from\n * a roving-tabindex parent navigating back to this element via arrow keys)\n * always reach the trigger.\n *\n * Chrome's delegatesFocus will not delegate to a tabindex=\"-1\" element, so\n * when the toolbar marks this host inactive it suppresses the trigger — and\n * the next focusItem() call silently fails. Lifting the suppression here and\n * directly calling trigger.focus() bypasses that restriction.\n */\n override focus(options?: FocusOptions) {\n const trigger = this._trigger?.deref();\n\n if (!trigger) {\n super.focus(options);\n return;\n }\n\n trigger.removeAttribute('tabindex');\n trigger.focus(options);\n }\n\n\n private _handleMenuSlotChange(e: Event) {\n const slot = e.currentTarget as HTMLSlotElement;\n const elements = slot.assignedElements();\n const menu = elements.find((el) => el.tagName === 'RC-MENU') as\n | RCMenu\n | undefined;\n\n if (menu) {\n this._menu = new WeakRef(menu);\n }\n }\n\n private _onNavigate = (action: KeyboardNavigationAction) => {\n switch (action) {\n case 'open-to-first':\n case 'activate':\n this.openMenu();\n break;\n case 'open-to-last':\n this.openMenu('last');\n break;\n case 'escape':\n this.closeMenu();\n break;\n }\n };\n\n private _handleTriggerClick(e: MouseEvent) {\n e.preventDefault();\n this.toggleMenu();\n }\n\n private _handleMenuClose() {\n this.closeMenu();\n }\n\n private _handleMenuActivate() {\n // Close menu after item activation\n this.closeMenu();\n }\n\n private _handleDocumentClick(e: MouseEvent) {\n if (!this.open) return;\n\n const path = e.composedPath();\n\n // Check if click was inside this component\n if (path.includes(this)) return;\n\n // Close menu on outside click\n this.closeMenu(false);\n }\n\n protected updated(changedProperties: Map<PropertyKey, unknown>) {\n super.updated(changedProperties);\n\n // Keep aria-expanded in sync\n if (changedProperties.has('open')) {\n this._syncTriggerAria();\n }\n\n if (\n changedProperties.has('placement') ||\n changedProperties.has('orientation')\n ) {\n this._anchorCtrl.setOptions({ placement: this._effectivePlacement });\n }\n }\n\n private _setOpen(open: boolean, dispatch: boolean): void {\n if (this._open === open) return;\n\n const oldValue = this._open;\n\n this._open = open;\n this.requestUpdate('open', oldValue);\n this._syncTriggerAria();\n\n if (dispatch) this._dispatchToggle();\n }\n\n private _syncTriggerAria(): void {\n const trigger = this._trigger?.deref();\n if (!trigger?.isConnected) return;\n\n trigger.setAttribute('aria-haspopup', 'menu');\n trigger.setAttribute('aria-expanded', String(this.open));\n }\n\n protected render() {\n return html`\n <div id=\"root\" part=\"root\">\n <div\n id=\"trigger-wrap\"\n ${keyNavigation(this._onNavigate, {\n navigationAxis: this._resolvedOrientation,\n handleNavAxis: false,\n handleOpenAxis: true,\n handleActivate: true,\n handleEscape: this.open,\n })}\n @click=${this._handleTriggerClick}\n >\n <slot\n name=\"trigger\"\n @slotchange=${this._handleTriggerSlotChange}\n ></slot>\n </div>\n <div\n id=\"popup\"\n part=\"popup\"\n ?hidden=${!this.open}\n @rc-menu-close=${this._handleMenuClose}\n @rc-menu-activate=${this._handleMenuActivate}\n >\n <slot @slotchange=${this._handleMenuSlotChange}></slot>\n </div>\n </div>\n `;\n }\n}\n\nexport default RCMenuButton;\n"],"names":["menuButtonStyles","css","_RCMenuButton","LitElement","AnchorController","action","value","oldValue","menubar","orientation","focusTarget","menu","returnFocus","e","trigger","options","el","changedProperties","open","dispatch","html","keyNavigation","RCMenuButton","__decorateClass","property","query","state"],"mappings":"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"}
|