@radix-solid-js/menu 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1071 @@
1
+ 'use strict';
2
+
3
+ var solidJs = require('solid-js');
4
+ var primitive = require('@radix-solid-js/primitive');
5
+ var collection = require('@radix-solid-js/collection');
6
+ var composeRefs = require('@radix-solid-js/compose-refs');
7
+ var context = require('@radix-solid-js/context');
8
+ var direction = require('@radix-solid-js/direction');
9
+ var dismissableLayer = require('@radix-solid-js/dismissable-layer');
10
+ var focusGuards = require('@radix-solid-js/focus-guards');
11
+ var focusScope = require('@radix-solid-js/focus-scope');
12
+ var id = require('@radix-solid-js/id');
13
+ var popper = require('@radix-solid-js/popper');
14
+ var portal = require('@radix-solid-js/portal');
15
+ var presence = require('@radix-solid-js/presence');
16
+ var primitiveComponent = require('@radix-solid-js/primitive-component');
17
+ var rovingFocus = require('@radix-solid-js/roving-focus');
18
+ var ariaHidden = require('aria-hidden');
19
+
20
+ // src/menu.tsx
21
+ var SELECTION_KEYS = ["Enter", " "];
22
+ var FIRST_KEYS = ["ArrowDown", "PageUp", "Home"];
23
+ var LAST_KEYS = ["ArrowUp", "PageDown", "End"];
24
+ var FIRST_LAST_KEYS = [...FIRST_KEYS, ...LAST_KEYS];
25
+ var SUB_OPEN_KEYS = {
26
+ ltr: [...SELECTION_KEYS, "ArrowRight"],
27
+ rtl: [...SELECTION_KEYS, "ArrowLeft"]
28
+ };
29
+ var SUB_CLOSE_KEYS = {
30
+ ltr: ["ArrowLeft"],
31
+ rtl: ["ArrowRight"]
32
+ };
33
+ var MENU_NAME = "Menu";
34
+ var [Collection, useCollection, createCollectionScope] = collection.createCollection(MENU_NAME);
35
+ var [createMenuContext, createMenuScope] = context.createContextScope(MENU_NAME, [
36
+ createCollectionScope,
37
+ popper.createPopperScope,
38
+ rovingFocus.createRovingFocusGroupScope
39
+ ]);
40
+ var usePopperScope = popper.createPopperScope();
41
+ var useRovingFocusGroupScope = rovingFocus.createRovingFocusGroupScope();
42
+ var [MenuProvider, useMenuContext] = createMenuContext(MENU_NAME);
43
+ var [MenuRootProvider, useMenuRootContext] = createMenuContext(MENU_NAME);
44
+ function Menu(props) {
45
+ const [local] = solidJs.splitProps(props, [
46
+ "__scopeMenu",
47
+ "children",
48
+ "open",
49
+ "onOpenChange",
50
+ "dir",
51
+ "modal"
52
+ ]);
53
+ const popperScope = usePopperScope(local.__scopeMenu);
54
+ const [content, setContent] = solidJs.createSignal(null);
55
+ const [isUsingKeyboard, setIsUsingKeyboard] = solidJs.createSignal(false);
56
+ const direction$1 = direction.useDirection(local.dir);
57
+ const open = () => local.open ?? false;
58
+ const modal = () => local.modal ?? true;
59
+ const handleOpenChange = (value) => local.onOpenChange?.(value);
60
+ solidJs.createEffect(() => {
61
+ const handleKeyDown = () => {
62
+ setIsUsingKeyboard(true);
63
+ document.addEventListener("pointerdown", handlePointer, {
64
+ capture: true,
65
+ once: true
66
+ });
67
+ document.addEventListener("pointermove", handlePointer, {
68
+ capture: true,
69
+ once: true
70
+ });
71
+ };
72
+ const handlePointer = () => setIsUsingKeyboard(false);
73
+ document.addEventListener("keydown", handleKeyDown, { capture: true });
74
+ solidJs.onCleanup(() => {
75
+ document.removeEventListener("keydown", handleKeyDown, { capture: true });
76
+ document.removeEventListener("pointerdown", handlePointer, {
77
+ capture: true
78
+ });
79
+ document.removeEventListener("pointermove", handlePointer, {
80
+ capture: true
81
+ });
82
+ });
83
+ });
84
+ return /* @__PURE__ */ React.createElement(popper.Popper, { ...popperScope }, /* @__PURE__ */ React.createElement(
85
+ MenuProvider,
86
+ {
87
+ scope: local.__scopeMenu,
88
+ open: open(),
89
+ onOpenChange: handleOpenChange,
90
+ content: content(),
91
+ onContentChange: setContent
92
+ },
93
+ /* @__PURE__ */ React.createElement(
94
+ MenuRootProvider,
95
+ {
96
+ scope: local.__scopeMenu,
97
+ onClose: () => handleOpenChange(false),
98
+ isUsingKeyboard: isUsingKeyboard(),
99
+ onIsUsingKeyboardChange: setIsUsingKeyboard,
100
+ dir: direction$1,
101
+ modal: modal()
102
+ },
103
+ local.children
104
+ )
105
+ ));
106
+ }
107
+ Menu.displayName = MENU_NAME;
108
+ var ANCHOR_NAME = "MenuAnchor";
109
+ function MenuAnchor(inProps) {
110
+ const [local, rest] = solidJs.splitProps(inProps, ["__scopeMenu", "ref"]);
111
+ const popperScope = usePopperScope(local.__scopeMenu);
112
+ return /* @__PURE__ */ React.createElement(popper.PopperAnchor, { ...popperScope, ...rest, ref: local.ref });
113
+ }
114
+ MenuAnchor.displayName = ANCHOR_NAME;
115
+ var PORTAL_NAME = "MenuPortal";
116
+ var [PortalProvider, usePortalContext] = createMenuContext(PORTAL_NAME, {
117
+ forceMount: void 0
118
+ });
119
+ function MenuPortal(props) {
120
+ const [local] = solidJs.splitProps(props, [
121
+ "__scopeMenu",
122
+ "forceMount",
123
+ "children",
124
+ "container"
125
+ ]);
126
+ const context = useMenuContext(PORTAL_NAME, local.__scopeMenu);
127
+ return /* @__PURE__ */ React.createElement(PortalProvider, { scope: local.__scopeMenu, forceMount: local.forceMount }, /* @__PURE__ */ React.createElement(presence.Presence, { present: local.forceMount || context.open }, /* @__PURE__ */ React.createElement(portal.Portal, { container: local.container }, local.children)));
128
+ }
129
+ MenuPortal.displayName = PORTAL_NAME;
130
+ var CONTENT_NAME = "MenuContent";
131
+ var [MenuContentProvider, useMenuContentContext] = createMenuContext(CONTENT_NAME);
132
+ function MenuContent(inProps) {
133
+ const [local, rest] = solidJs.splitProps(inProps, ["__scopeMenu", "forceMount"]);
134
+ const portalContext = usePortalContext(CONTENT_NAME, local.__scopeMenu);
135
+ const context = useMenuContext(CONTENT_NAME, local.__scopeMenu);
136
+ const rootContext = useMenuRootContext(CONTENT_NAME, local.__scopeMenu);
137
+ const forceMount = () => local.forceMount ?? portalContext.forceMount;
138
+ return /* @__PURE__ */ React.createElement(Collection.Provider, { scope: local.__scopeMenu }, /* @__PURE__ */ React.createElement(presence.Presence, { present: forceMount() || context.open }, /* @__PURE__ */ React.createElement(Collection.Slot, { scope: local.__scopeMenu }, /* @__PURE__ */ React.createElement(
139
+ solidJs.Show,
140
+ {
141
+ when: rootContext.modal,
142
+ fallback: /* @__PURE__ */ React.createElement(
143
+ MenuRootContentNonModal,
144
+ {
145
+ ...rest,
146
+ __scopeMenu: local.__scopeMenu
147
+ }
148
+ )
149
+ },
150
+ /* @__PURE__ */ React.createElement(MenuRootContentModal, { ...rest, __scopeMenu: local.__scopeMenu })
151
+ ))));
152
+ }
153
+ MenuContent.displayName = CONTENT_NAME;
154
+ function MenuRootContentModal(inProps) {
155
+ const [local, rest] = solidJs.splitProps(inProps, [
156
+ "__scopeMenu",
157
+ "ref",
158
+ "onFocusOutside"
159
+ ]);
160
+ const context = useMenuContext(CONTENT_NAME, local.__scopeMenu);
161
+ let contentRef;
162
+ solidJs.createEffect(() => {
163
+ if (contentRef) {
164
+ const cleanup = ariaHidden.hideOthers(contentRef);
165
+ solidJs.onCleanup(() => cleanup?.());
166
+ }
167
+ });
168
+ return /* @__PURE__ */ React.createElement(
169
+ MenuContentImpl,
170
+ {
171
+ ...rest,
172
+ __scopeMenu: local.__scopeMenu,
173
+ ref: composeRefs.mergeRefs(local.ref, (el) => {
174
+ contentRef = el;
175
+ }),
176
+ trapFocus: context.open,
177
+ disableOutsidePointerEvents: context.open,
178
+ disableOutsideScroll: true,
179
+ onFocusOutside: primitive.composeEventHandlers(
180
+ local.onFocusOutside,
181
+ (event) => event.preventDefault()
182
+ ),
183
+ onDismiss: () => context.onOpenChange(false)
184
+ }
185
+ );
186
+ }
187
+ function MenuRootContentNonModal(inProps) {
188
+ const [local, rest] = solidJs.splitProps(inProps, ["__scopeMenu"]);
189
+ const context = useMenuContext(CONTENT_NAME, local.__scopeMenu);
190
+ return /* @__PURE__ */ React.createElement(
191
+ MenuContentImpl,
192
+ {
193
+ ...rest,
194
+ __scopeMenu: local.__scopeMenu,
195
+ trapFocus: false,
196
+ disableOutsidePointerEvents: false,
197
+ disableOutsideScroll: false,
198
+ onDismiss: () => context.onOpenChange(false)
199
+ }
200
+ );
201
+ }
202
+ function MenuContentImpl(inProps) {
203
+ const [local, rest] = solidJs.splitProps(inProps, [
204
+ "__scopeMenu",
205
+ "ref",
206
+ "loop",
207
+ "trapFocus",
208
+ "onOpenAutoFocus",
209
+ "onCloseAutoFocus",
210
+ "disableOutsidePointerEvents",
211
+ "disableOutsideScroll",
212
+ "onEntryFocus",
213
+ "onEscapeKeyDown",
214
+ "onPointerDownOutside",
215
+ "onFocusOutside",
216
+ "onInteractOutside",
217
+ "onDismiss",
218
+ "side",
219
+ "sideOffset",
220
+ "align",
221
+ "alignOffset",
222
+ "avoidCollisions",
223
+ "collisionBoundary",
224
+ "collisionPadding",
225
+ "arrowPadding",
226
+ "sticky",
227
+ "hideWhenDetached",
228
+ "style",
229
+ "onKeyDown",
230
+ "onBlur",
231
+ "onPointerMove"
232
+ ]);
233
+ const context = useMenuContext(CONTENT_NAME, local.__scopeMenu);
234
+ const rootContext = useMenuRootContext(CONTENT_NAME, local.__scopeMenu);
235
+ const popperScope = usePopperScope(local.__scopeMenu);
236
+ const rovingFocusGroupScope = useRovingFocusGroupScope(local.__scopeMenu);
237
+ const getItems = useCollection(local.__scopeMenu);
238
+ const [currentItemId, setCurrentItemId] = solidJs.createSignal(null);
239
+ let contentRef;
240
+ let timerRef = 0;
241
+ let searchRef = "";
242
+ let pointerGraceTimerRef = 0;
243
+ let pointerGraceIntentRef = null;
244
+ let pointerDirRef = "right";
245
+ let lastPointerXRef = 0;
246
+ solidJs.createEffect(() => {
247
+ if (local.disableOutsideScroll) {
248
+ const body = document.body;
249
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
250
+ const originalOverflow = body.style.overflow;
251
+ const originalPaddingRight = body.style.paddingRight;
252
+ body.style.overflow = "hidden";
253
+ if (scrollbarWidth > 0) {
254
+ body.style.paddingRight = `${scrollbarWidth}px`;
255
+ }
256
+ solidJs.onCleanup(() => {
257
+ body.style.overflow = originalOverflow;
258
+ body.style.paddingRight = originalPaddingRight;
259
+ });
260
+ }
261
+ });
262
+ const handleTypeaheadSearch = (key) => {
263
+ const search = searchRef + key;
264
+ const items = getItems().filter((item) => !item.disabled);
265
+ const currentItem = document.activeElement;
266
+ const currentMatch = items.find(
267
+ (item) => item.ref === currentItem
268
+ )?.textValue;
269
+ const values = items.map((item) => item.textValue);
270
+ const nextMatch = getNextMatch(values, search, currentMatch);
271
+ const newItem = items.find(
272
+ (item) => item.textValue === nextMatch
273
+ )?.ref;
274
+ (function updateSearch(value) {
275
+ searchRef = value;
276
+ window.clearTimeout(timerRef);
277
+ if (value !== "")
278
+ timerRef = window.setTimeout(() => updateSearch(""), 1e3);
279
+ })(search);
280
+ if (newItem) {
281
+ setTimeout(() => newItem.focus());
282
+ }
283
+ };
284
+ solidJs.onCleanup(() => window.clearTimeout(timerRef));
285
+ focusGuards.useFocusGuards();
286
+ const isPointerMovingToSubmenu = (event) => {
287
+ const isMovingTowards = pointerDirRef === pointerGraceIntentRef?.side;
288
+ return isMovingTowards && isPointerInGraceArea(event, pointerGraceIntentRef?.area);
289
+ };
290
+ return /* @__PURE__ */ React.createElement(
291
+ MenuContentProvider,
292
+ {
293
+ scope: local.__scopeMenu,
294
+ searchRef,
295
+ onSearchChange: (value) => {
296
+ searchRef = value;
297
+ },
298
+ onItemEnter: (event) => {
299
+ if (isPointerMovingToSubmenu(event)) event.preventDefault();
300
+ },
301
+ onItemLeave: (event) => {
302
+ if (isPointerMovingToSubmenu(event)) return;
303
+ contentRef?.focus();
304
+ setCurrentItemId(null);
305
+ },
306
+ onTriggerLeave: (event) => {
307
+ if (isPointerMovingToSubmenu(event)) event.preventDefault();
308
+ },
309
+ pointerGraceTimerRef,
310
+ onPointerGraceTimerChange: (value) => {
311
+ pointerGraceTimerRef = value;
312
+ },
313
+ onPointerGraceIntentChange: (intent) => {
314
+ pointerGraceIntentRef = intent;
315
+ }
316
+ },
317
+ /* @__PURE__ */ React.createElement(
318
+ focusScope.FocusScope,
319
+ {
320
+ trapped: local.trapFocus,
321
+ onMountAutoFocus: primitive.composeEventHandlers(
322
+ local.onOpenAutoFocus,
323
+ (event) => {
324
+ event.preventDefault();
325
+ contentRef?.focus({ preventScroll: true });
326
+ }
327
+ ),
328
+ onUnmountAutoFocus: local.onCloseAutoFocus
329
+ },
330
+ /* @__PURE__ */ React.createElement(
331
+ dismissableLayer.DismissableLayer,
332
+ {
333
+ asChild: true,
334
+ disableOutsidePointerEvents: local.disableOutsidePointerEvents,
335
+ onEscapeKeyDown: local.onEscapeKeyDown,
336
+ onPointerDownOutside: local.onPointerDownOutside,
337
+ onFocusOutside: local.onFocusOutside,
338
+ onInteractOutside: local.onInteractOutside,
339
+ onDismiss: local.onDismiss
340
+ },
341
+ /* @__PURE__ */ React.createElement(
342
+ rovingFocus.RovingFocusGroup,
343
+ {
344
+ asChild: true,
345
+ ...rovingFocusGroupScope,
346
+ dir: rootContext.dir,
347
+ orientation: "vertical",
348
+ loop: local.loop ?? false,
349
+ currentTabStopId: currentItemId(),
350
+ onCurrentTabStopIdChange: setCurrentItemId,
351
+ onEntryFocus: primitive.composeEventHandlers(
352
+ local.onEntryFocus,
353
+ (event) => {
354
+ if (!rootContext.isUsingKeyboard) event.preventDefault();
355
+ }
356
+ ),
357
+ preventScrollOnEntryFocus: true
358
+ },
359
+ /* @__PURE__ */ React.createElement(
360
+ popper.PopperContent,
361
+ {
362
+ role: "menu",
363
+ "aria-orientation": "vertical",
364
+ "data-state": getOpenState(context.open),
365
+ "data-radix-menu-content": "",
366
+ dir: rootContext.dir,
367
+ ...popperScope,
368
+ ...rest,
369
+ ref: composeRefs.mergeRefs(local.ref, (el) => {
370
+ contentRef = el;
371
+ context.onContentChange(el);
372
+ }),
373
+ side: local.side,
374
+ sideOffset: local.sideOffset,
375
+ align: local.align,
376
+ alignOffset: local.alignOffset,
377
+ avoidCollisions: local.avoidCollisions,
378
+ collisionBoundary: local.collisionBoundary,
379
+ collisionPadding: local.collisionPadding,
380
+ arrowPadding: local.arrowPadding,
381
+ sticky: local.sticky,
382
+ hideWhenDetached: local.hideWhenDetached,
383
+ style: {
384
+ outline: "none",
385
+ ...typeof local.style === "object" ? local.style : {},
386
+ "--radix-menu-content-transform-origin": "var(--radix-popper-transform-origin)",
387
+ "--radix-menu-content-available-width": "var(--radix-popper-available-width)",
388
+ "--radix-menu-content-available-height": "var(--radix-popper-available-height)",
389
+ "--radix-menu-trigger-width": "var(--radix-popper-anchor-width)",
390
+ "--radix-menu-trigger-height": "var(--radix-popper-anchor-height)"
391
+ },
392
+ onKeyDown: primitive.composeEventHandlers(
393
+ local.onKeyDown,
394
+ (event) => {
395
+ const target = event.target;
396
+ const isKeyDownInside = target.closest("[data-radix-menu-content]") === event.currentTarget;
397
+ const isModifierKey = event.ctrlKey || event.altKey || event.metaKey;
398
+ const isCharacterKey = event.key.length === 1;
399
+ if (isKeyDownInside) {
400
+ if (event.key === "Tab") event.preventDefault();
401
+ if (!isModifierKey && isCharacterKey)
402
+ handleTypeaheadSearch(event.key);
403
+ }
404
+ const content = contentRef;
405
+ if (event.target !== content) return;
406
+ if (!FIRST_LAST_KEYS.includes(event.key)) return;
407
+ event.preventDefault();
408
+ const items = getItems().filter(
409
+ (item) => !item.disabled
410
+ );
411
+ const candidateNodes = items.map((item) => item.ref);
412
+ if (LAST_KEYS.includes(event.key)) candidateNodes.reverse();
413
+ focusFirst(candidateNodes);
414
+ }
415
+ ),
416
+ onBlur: primitive.composeEventHandlers(
417
+ local.onBlur,
418
+ (event) => {
419
+ if (!event.currentTarget.contains(
420
+ event.target
421
+ )) {
422
+ window.clearTimeout(timerRef);
423
+ searchRef = "";
424
+ }
425
+ }
426
+ ),
427
+ onPointerMove: primitive.composeEventHandlers(
428
+ local.onPointerMove,
429
+ whenMouse((event) => {
430
+ const target = event.target;
431
+ const pointerXHasChanged = lastPointerXRef !== event.clientX;
432
+ if (event.currentTarget.contains(target) && pointerXHasChanged) {
433
+ const newDir = event.clientX > lastPointerXRef ? "right" : "left";
434
+ pointerDirRef = newDir;
435
+ lastPointerXRef = event.clientX;
436
+ }
437
+ })
438
+ )
439
+ }
440
+ )
441
+ )
442
+ )
443
+ )
444
+ );
445
+ }
446
+ var GROUP_NAME = "MenuGroup";
447
+ function MenuGroup(inProps) {
448
+ const [local, rest] = solidJs.splitProps(inProps, ["__scopeMenu", "ref"]);
449
+ return /* @__PURE__ */ React.createElement(primitiveComponent.Primitive.div, { role: "group", ...rest, ref: local.ref });
450
+ }
451
+ MenuGroup.displayName = GROUP_NAME;
452
+ var LABEL_NAME = "MenuLabel";
453
+ function MenuLabel(inProps) {
454
+ const [local, rest] = solidJs.splitProps(inProps, ["__scopeMenu", "ref"]);
455
+ return /* @__PURE__ */ React.createElement(primitiveComponent.Primitive.div, { ...rest, ref: local.ref });
456
+ }
457
+ MenuLabel.displayName = LABEL_NAME;
458
+ var ITEM_NAME = "MenuItem";
459
+ var ITEM_SELECT = "menu.itemSelect";
460
+ function MenuItem(inProps) {
461
+ const [local, rest] = solidJs.splitProps(inProps, [
462
+ "__scopeMenu",
463
+ "disabled",
464
+ "onSelect",
465
+ "ref",
466
+ "onClick",
467
+ "onPointerDown",
468
+ "onPointerUp",
469
+ "onKeyDown"
470
+ ]);
471
+ const rootContext = useMenuRootContext(ITEM_NAME, local.__scopeMenu);
472
+ const contentContext = useMenuContentContext(ITEM_NAME, local.__scopeMenu);
473
+ let itemRef;
474
+ let isPointerDownRef = false;
475
+ const disabled = () => local.disabled ?? false;
476
+ const handleSelect = () => {
477
+ const menuItem = itemRef;
478
+ if (!disabled() && menuItem) {
479
+ const itemSelectEvent = new CustomEvent(ITEM_SELECT, {
480
+ bubbles: true,
481
+ cancelable: true
482
+ });
483
+ menuItem.addEventListener(
484
+ ITEM_SELECT,
485
+ (event) => local.onSelect?.(event),
486
+ { once: true }
487
+ );
488
+ primitiveComponent.dispatchDiscreteCustomEvent(menuItem, itemSelectEvent);
489
+ if (itemSelectEvent.defaultPrevented) {
490
+ isPointerDownRef = false;
491
+ } else {
492
+ rootContext.onClose();
493
+ }
494
+ }
495
+ };
496
+ return /* @__PURE__ */ React.createElement(
497
+ MenuItemImpl,
498
+ {
499
+ ...rest,
500
+ __scopeMenu: local.__scopeMenu,
501
+ ref: composeRefs.mergeRefs(local.ref, (el) => {
502
+ itemRef = el;
503
+ }),
504
+ disabled: disabled(),
505
+ onClick: primitive.composeEventHandlers(local.onClick, handleSelect),
506
+ onPointerDown: primitive.composeEventHandlers(local.onPointerDown, () => {
507
+ isPointerDownRef = true;
508
+ }),
509
+ onPointerUp: primitive.composeEventHandlers(
510
+ local.onPointerUp,
511
+ (event) => {
512
+ if (!isPointerDownRef) event.currentTarget?.click();
513
+ }
514
+ ),
515
+ onKeyDown: primitive.composeEventHandlers(
516
+ local.onKeyDown,
517
+ (event) => {
518
+ const isTypingAhead = contentContext.searchRef !== "";
519
+ if (disabled() || isTypingAhead && event.key === " ") return;
520
+ if (SELECTION_KEYS.includes(event.key)) {
521
+ event.currentTarget.click();
522
+ event.preventDefault();
523
+ }
524
+ }
525
+ )
526
+ }
527
+ );
528
+ }
529
+ MenuItem.displayName = ITEM_NAME;
530
+ function MenuItemImpl(inProps) {
531
+ const [local, rest] = solidJs.splitProps(inProps, [
532
+ "__scopeMenu",
533
+ "disabled",
534
+ "textValue",
535
+ "ref",
536
+ "onPointerMove",
537
+ "onPointerLeave",
538
+ "onFocus",
539
+ "onBlur"
540
+ ]);
541
+ const contentContext = useMenuContentContext(ITEM_NAME, local.__scopeMenu);
542
+ const rovingFocusGroupScope = useRovingFocusGroupScope(local.__scopeMenu);
543
+ let itemRef;
544
+ const [isFocused, setIsFocused] = solidJs.createSignal(false);
545
+ const [textContent, setTextContent] = solidJs.createSignal("");
546
+ const disabled = () => local.disabled ?? false;
547
+ solidJs.onMount(() => {
548
+ if (itemRef) {
549
+ setTextContent((itemRef.textContent ?? "").trim());
550
+ }
551
+ });
552
+ return /* @__PURE__ */ React.createElement(
553
+ Collection.ItemSlot,
554
+ {
555
+ scope: local.__scopeMenu,
556
+ disabled: disabled(),
557
+ textValue: local.textValue ?? textContent()
558
+ },
559
+ /* @__PURE__ */ React.createElement(
560
+ rovingFocus.RovingFocusGroupItem,
561
+ {
562
+ asChild: true,
563
+ ...rovingFocusGroupScope,
564
+ focusable: !disabled()
565
+ },
566
+ /* @__PURE__ */ React.createElement(
567
+ primitiveComponent.Primitive.div,
568
+ {
569
+ role: "menuitem",
570
+ "data-highlighted": isFocused() ? "" : void 0,
571
+ "aria-disabled": disabled() || void 0,
572
+ "data-disabled": disabled() ? "" : void 0,
573
+ ...rest,
574
+ ref: composeRefs.mergeRefs(local.ref, (el) => {
575
+ itemRef = el;
576
+ }),
577
+ onPointerMove: primitive.composeEventHandlers(
578
+ local.onPointerMove,
579
+ whenMouse((event) => {
580
+ if (disabled()) {
581
+ contentContext.onItemLeave(event);
582
+ } else {
583
+ contentContext.onItemEnter(event);
584
+ if (!event.defaultPrevented) {
585
+ const item = event.currentTarget;
586
+ item.focus({ preventScroll: true });
587
+ }
588
+ }
589
+ })
590
+ ),
591
+ onPointerLeave: primitive.composeEventHandlers(
592
+ local.onPointerLeave,
593
+ whenMouse(
594
+ (event) => contentContext.onItemLeave(event)
595
+ )
596
+ ),
597
+ onFocus: primitive.composeEventHandlers(
598
+ local.onFocus,
599
+ () => setIsFocused(true)
600
+ ),
601
+ onBlur: primitive.composeEventHandlers(
602
+ local.onBlur,
603
+ () => setIsFocused(false)
604
+ )
605
+ }
606
+ )
607
+ )
608
+ );
609
+ }
610
+ var CHECKBOX_ITEM_NAME = "MenuCheckboxItem";
611
+ function MenuCheckboxItem(inProps) {
612
+ const [local, rest] = solidJs.splitProps(inProps, [
613
+ "__scopeMenu",
614
+ "checked",
615
+ "onCheckedChange",
616
+ "onSelect"
617
+ ]);
618
+ const checked = () => local.checked ?? false;
619
+ return /* @__PURE__ */ React.createElement(ItemIndicatorProvider, { scope: local.__scopeMenu, checked: checked() }, /* @__PURE__ */ React.createElement(
620
+ MenuItem,
621
+ {
622
+ role: "menuitemcheckbox",
623
+ "aria-checked": isIndeterminate(checked()) ? "mixed" : checked(),
624
+ ...rest,
625
+ __scopeMenu: local.__scopeMenu,
626
+ "data-state": getCheckedState(checked()),
627
+ onSelect: primitive.composeEventHandlers(
628
+ local.onSelect,
629
+ () => local.onCheckedChange?.(
630
+ isIndeterminate(checked()) ? true : !checked()
631
+ ),
632
+ { checkForDefaultPrevented: false }
633
+ )
634
+ }
635
+ ));
636
+ }
637
+ MenuCheckboxItem.displayName = CHECKBOX_ITEM_NAME;
638
+ var RADIO_GROUP_NAME = "MenuRadioGroup";
639
+ var [RadioGroupProvider, useRadioGroupContext] = createMenuContext(RADIO_GROUP_NAME, {
640
+ value: void 0,
641
+ onValueChange: () => {
642
+ }
643
+ });
644
+ function MenuRadioGroup(inProps) {
645
+ const [local, rest] = solidJs.splitProps(inProps, [
646
+ "__scopeMenu",
647
+ "value",
648
+ "onValueChange"
649
+ ]);
650
+ return /* @__PURE__ */ React.createElement(
651
+ RadioGroupProvider,
652
+ {
653
+ scope: local.__scopeMenu,
654
+ value: local.value,
655
+ onValueChange: local.onValueChange
656
+ },
657
+ /* @__PURE__ */ React.createElement(MenuGroup, { ...rest, __scopeMenu: local.__scopeMenu })
658
+ );
659
+ }
660
+ MenuRadioGroup.displayName = RADIO_GROUP_NAME;
661
+ var RADIO_ITEM_NAME = "MenuRadioItem";
662
+ function MenuRadioItem(inProps) {
663
+ const [local, rest] = solidJs.splitProps(inProps, [
664
+ "__scopeMenu",
665
+ "value",
666
+ "onSelect"
667
+ ]);
668
+ const context = useRadioGroupContext(RADIO_ITEM_NAME, local.__scopeMenu);
669
+ const checked = () => local.value === context.value;
670
+ return /* @__PURE__ */ React.createElement(ItemIndicatorProvider, { scope: local.__scopeMenu, checked: checked() }, /* @__PURE__ */ React.createElement(
671
+ MenuItem,
672
+ {
673
+ role: "menuitemradio",
674
+ "aria-checked": checked(),
675
+ ...rest,
676
+ __scopeMenu: local.__scopeMenu,
677
+ "data-state": getCheckedState(checked()),
678
+ onSelect: primitive.composeEventHandlers(
679
+ local.onSelect,
680
+ () => context.onValueChange?.(local.value),
681
+ { checkForDefaultPrevented: false }
682
+ )
683
+ }
684
+ ));
685
+ }
686
+ MenuRadioItem.displayName = RADIO_ITEM_NAME;
687
+ var ITEM_INDICATOR_NAME = "MenuItemIndicator";
688
+ var [ItemIndicatorProvider, useItemIndicatorContext] = createMenuContext(ITEM_INDICATOR_NAME, {
689
+ checked: false
690
+ });
691
+ function MenuItemIndicator(inProps) {
692
+ const [local, rest] = solidJs.splitProps(inProps, [
693
+ "__scopeMenu",
694
+ "forceMount",
695
+ "ref"
696
+ ]);
697
+ const indicatorContext = useItemIndicatorContext(
698
+ ITEM_INDICATOR_NAME,
699
+ local.__scopeMenu
700
+ );
701
+ return /* @__PURE__ */ React.createElement(
702
+ presence.Presence,
703
+ {
704
+ present: local.forceMount || isIndeterminate(indicatorContext.checked) || indicatorContext.checked === true
705
+ },
706
+ /* @__PURE__ */ React.createElement(
707
+ primitiveComponent.Primitive.span,
708
+ {
709
+ ...rest,
710
+ ref: local.ref,
711
+ "data-state": getCheckedState(indicatorContext.checked)
712
+ }
713
+ )
714
+ );
715
+ }
716
+ MenuItemIndicator.displayName = ITEM_INDICATOR_NAME;
717
+ var SEPARATOR_NAME = "MenuSeparator";
718
+ function MenuSeparator(inProps) {
719
+ const [local, rest] = solidJs.splitProps(inProps, ["__scopeMenu", "ref"]);
720
+ return /* @__PURE__ */ React.createElement(
721
+ primitiveComponent.Primitive.div,
722
+ {
723
+ role: "separator",
724
+ "aria-orientation": "horizontal",
725
+ ...rest,
726
+ ref: local.ref
727
+ }
728
+ );
729
+ }
730
+ MenuSeparator.displayName = SEPARATOR_NAME;
731
+ var ARROW_NAME = "MenuArrow";
732
+ function MenuArrow(inProps) {
733
+ const [local, rest] = solidJs.splitProps(inProps, ["__scopeMenu"]);
734
+ const popperScope = usePopperScope(local.__scopeMenu);
735
+ return /* @__PURE__ */ React.createElement(popper.PopperArrow, { ...popperScope, ...rest });
736
+ }
737
+ MenuArrow.displayName = ARROW_NAME;
738
+ var SUB_NAME = "MenuSub";
739
+ var [MenuSubProvider, useMenuSubContext] = createMenuContext(SUB_NAME);
740
+ function MenuSub(props) {
741
+ const [local] = solidJs.splitProps(props, [
742
+ "__scopeMenu",
743
+ "children",
744
+ "open",
745
+ "onOpenChange"
746
+ ]);
747
+ const parentMenuContext = useMenuContext(SUB_NAME, local.__scopeMenu);
748
+ const popperScope = usePopperScope(local.__scopeMenu);
749
+ const [trigger, setTrigger] = solidJs.createSignal(null);
750
+ const [content, setContent] = solidJs.createSignal(null);
751
+ const open = () => local.open ?? false;
752
+ const handleOpenChange = (value) => local.onOpenChange?.(value);
753
+ solidJs.createEffect(() => {
754
+ if (parentMenuContext.open === false) handleOpenChange(false);
755
+ solidJs.onCleanup(() => handleOpenChange(false));
756
+ });
757
+ return /* @__PURE__ */ React.createElement(popper.Popper, { ...popperScope }, /* @__PURE__ */ React.createElement(
758
+ MenuProvider,
759
+ {
760
+ scope: local.__scopeMenu,
761
+ open: open(),
762
+ onOpenChange: handleOpenChange,
763
+ content: content(),
764
+ onContentChange: setContent
765
+ },
766
+ /* @__PURE__ */ React.createElement(
767
+ MenuSubProvider,
768
+ {
769
+ scope: local.__scopeMenu,
770
+ contentId: id.createId(),
771
+ triggerId: id.createId(),
772
+ trigger: trigger(),
773
+ onTriggerChange: setTrigger
774
+ },
775
+ local.children
776
+ )
777
+ ));
778
+ }
779
+ MenuSub.displayName = SUB_NAME;
780
+ var SUB_TRIGGER_NAME = "MenuSubTrigger";
781
+ function MenuSubTrigger(inProps) {
782
+ const [local, rest] = solidJs.splitProps(inProps, [
783
+ "__scopeMenu",
784
+ "ref",
785
+ "onClick",
786
+ "onPointerMove",
787
+ "onPointerLeave",
788
+ "onKeyDown"
789
+ ]);
790
+ const context = useMenuContext(SUB_TRIGGER_NAME, local.__scopeMenu);
791
+ const rootContext = useMenuRootContext(SUB_TRIGGER_NAME, local.__scopeMenu);
792
+ const subContext = useMenuSubContext(SUB_TRIGGER_NAME, local.__scopeMenu);
793
+ const contentContext = useMenuContentContext(
794
+ SUB_TRIGGER_NAME,
795
+ local.__scopeMenu
796
+ );
797
+ let openTimerRef = null;
798
+ const clearOpenTimer = () => {
799
+ if (openTimerRef) window.clearTimeout(openTimerRef);
800
+ openTimerRef = null;
801
+ };
802
+ solidJs.onCleanup(() => {
803
+ clearOpenTimer();
804
+ window.clearTimeout(contentContext.pointerGraceTimerRef);
805
+ contentContext.onPointerGraceIntentChange(null);
806
+ });
807
+ return /* @__PURE__ */ React.createElement(MenuAnchor, { __scopeMenu: local.__scopeMenu }, /* @__PURE__ */ React.createElement(
808
+ MenuItemImpl,
809
+ {
810
+ id: subContext.triggerId,
811
+ "aria-haspopup": "menu",
812
+ "aria-expanded": context.open,
813
+ "aria-controls": subContext.contentId,
814
+ "data-state": getOpenState(context.open),
815
+ ...rest,
816
+ __scopeMenu: local.__scopeMenu,
817
+ ref: composeRefs.mergeRefs(
818
+ local.ref,
819
+ (el) => subContext.onTriggerChange(el)
820
+ ),
821
+ onClick: primitive.composeEventHandlers(
822
+ local.onClick,
823
+ (event) => {
824
+ if (rest.disabled || event.defaultPrevented) return;
825
+ event.currentTarget.focus();
826
+ if (!context.open) context.onOpenChange(true);
827
+ }
828
+ ),
829
+ onPointerMove: primitive.composeEventHandlers(
830
+ local.onPointerMove,
831
+ whenMouse((event) => {
832
+ contentContext.onItemEnter(event);
833
+ if (event.defaultPrevented) return;
834
+ if (!rest.disabled && !context.open && !openTimerRef) {
835
+ contentContext.onPointerGraceIntentChange(null);
836
+ openTimerRef = window.setTimeout(() => {
837
+ context.onOpenChange(true);
838
+ clearOpenTimer();
839
+ }, 100);
840
+ }
841
+ })
842
+ ),
843
+ onPointerLeave: primitive.composeEventHandlers(
844
+ local.onPointerLeave,
845
+ whenMouse((event) => {
846
+ clearOpenTimer();
847
+ const contentRect = context.content?.getBoundingClientRect();
848
+ if (contentRect) {
849
+ const side = context.content?.dataset.side;
850
+ const rightSide = side === "right";
851
+ const bleed = rightSide ? -5 : 5;
852
+ const contentNearEdge = contentRect[rightSide ? "left" : "right"];
853
+ const contentFarEdge = contentRect[rightSide ? "right" : "left"];
854
+ contentContext.onPointerGraceIntentChange({
855
+ area: [
856
+ { x: event.clientX + bleed, y: event.clientY },
857
+ { x: contentNearEdge, y: contentRect.top },
858
+ { x: contentFarEdge, y: contentRect.top },
859
+ { x: contentFarEdge, y: contentRect.bottom },
860
+ { x: contentNearEdge, y: contentRect.bottom }
861
+ ],
862
+ side
863
+ });
864
+ window.clearTimeout(contentContext.pointerGraceTimerRef);
865
+ contentContext.onPointerGraceTimerChange(
866
+ window.setTimeout(
867
+ () => contentContext.onPointerGraceIntentChange(null),
868
+ 300
869
+ )
870
+ );
871
+ } else {
872
+ contentContext.onTriggerLeave(event);
873
+ if (event.defaultPrevented) return;
874
+ contentContext.onPointerGraceIntentChange(null);
875
+ }
876
+ })
877
+ ),
878
+ onKeyDown: primitive.composeEventHandlers(
879
+ local.onKeyDown,
880
+ (event) => {
881
+ const isTypingAhead = contentContext.searchRef !== "";
882
+ if (rest.disabled || isTypingAhead && event.key === " ") return;
883
+ if (SUB_OPEN_KEYS[rootContext.dir].includes(event.key)) {
884
+ context.onOpenChange(true);
885
+ context.content?.focus();
886
+ event.preventDefault();
887
+ }
888
+ }
889
+ )
890
+ }
891
+ ));
892
+ }
893
+ MenuSubTrigger.displayName = SUB_TRIGGER_NAME;
894
+ var SUB_CONTENT_NAME = "MenuSubContent";
895
+ function MenuSubContent(inProps) {
896
+ const [local, rest] = solidJs.splitProps(inProps, [
897
+ "__scopeMenu",
898
+ "forceMount",
899
+ "ref",
900
+ "onFocusOutside",
901
+ "onEscapeKeyDown",
902
+ "onKeyDown"
903
+ ]);
904
+ const portalContext = usePortalContext(CONTENT_NAME, local.__scopeMenu);
905
+ const context = useMenuContext(CONTENT_NAME, local.__scopeMenu);
906
+ const rootContext = useMenuRootContext(CONTENT_NAME, local.__scopeMenu);
907
+ const subContext = useMenuSubContext(SUB_CONTENT_NAME, local.__scopeMenu);
908
+ const forceMount = () => local.forceMount ?? portalContext.forceMount;
909
+ let contentRef;
910
+ return /* @__PURE__ */ React.createElement(Collection.Provider, { scope: local.__scopeMenu }, /* @__PURE__ */ React.createElement(presence.Presence, { present: forceMount() || context.open }, /* @__PURE__ */ React.createElement(Collection.Slot, { scope: local.__scopeMenu }, /* @__PURE__ */ React.createElement(
911
+ MenuContentImpl,
912
+ {
913
+ id: subContext.contentId,
914
+ "aria-labelledby": subContext.triggerId,
915
+ ...rest,
916
+ __scopeMenu: local.__scopeMenu,
917
+ ref: composeRefs.mergeRefs(local.ref, (el) => {
918
+ contentRef = el;
919
+ }),
920
+ align: "start",
921
+ side: rootContext.dir === "rtl" ? "left" : "right",
922
+ disableOutsidePointerEvents: false,
923
+ disableOutsideScroll: false,
924
+ trapFocus: false,
925
+ onOpenAutoFocus: (event) => {
926
+ if (rootContext.isUsingKeyboard) contentRef?.focus();
927
+ event.preventDefault();
928
+ },
929
+ onCloseAutoFocus: (event) => event.preventDefault(),
930
+ onFocusOutside: primitive.composeEventHandlers(
931
+ local.onFocusOutside,
932
+ (event) => {
933
+ if (event.target !== subContext.trigger)
934
+ context.onOpenChange(false);
935
+ }
936
+ ),
937
+ onEscapeKeyDown: primitive.composeEventHandlers(
938
+ local.onEscapeKeyDown,
939
+ (event) => {
940
+ rootContext.onClose();
941
+ event.preventDefault();
942
+ }
943
+ ),
944
+ onKeyDown: primitive.composeEventHandlers(
945
+ local.onKeyDown,
946
+ (event) => {
947
+ const isKeyDownInside = event.currentTarget.contains(event.target);
948
+ const isCloseKey = SUB_CLOSE_KEYS[rootContext.dir].includes(
949
+ event.key
950
+ );
951
+ if (isKeyDownInside && isCloseKey) {
952
+ context.onOpenChange(false);
953
+ subContext.trigger?.focus();
954
+ event.preventDefault();
955
+ }
956
+ }
957
+ )
958
+ }
959
+ ))));
960
+ }
961
+ MenuSubContent.displayName = SUB_CONTENT_NAME;
962
+ function getOpenState(open) {
963
+ return open ? "open" : "closed";
964
+ }
965
+ function isIndeterminate(checked) {
966
+ return checked === "indeterminate";
967
+ }
968
+ function getCheckedState(checked) {
969
+ return isIndeterminate(checked) ? "indeterminate" : checked ? "checked" : "unchecked";
970
+ }
971
+ function focusFirst(candidates) {
972
+ const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
973
+ for (const candidate of candidates) {
974
+ if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
975
+ candidate.focus();
976
+ if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
977
+ }
978
+ }
979
+ function wrapArray(array, startIndex) {
980
+ return array.map(
981
+ (_, index) => array[(startIndex + index) % array.length]
982
+ );
983
+ }
984
+ function getNextMatch(values, search, currentMatch) {
985
+ const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]);
986
+ const normalizedSearch = isRepeated ? search[0] : search;
987
+ const currentMatchIndex = currentMatch ? values.indexOf(currentMatch) : -1;
988
+ let wrappedValues = wrapArray(values, Math.max(currentMatchIndex, 0));
989
+ const excludeCurrentMatch = normalizedSearch.length === 1;
990
+ if (excludeCurrentMatch)
991
+ wrappedValues = wrappedValues.filter((v) => v !== currentMatch);
992
+ const nextMatch = wrappedValues.find(
993
+ (value) => value.toLowerCase().startsWith(normalizedSearch.toLowerCase())
994
+ );
995
+ return nextMatch !== currentMatch ? nextMatch : void 0;
996
+ }
997
+ function isPointInPolygon(point, polygon) {
998
+ const { x, y } = point;
999
+ let inside = false;
1000
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
1001
+ const ii = polygon[i];
1002
+ const jj = polygon[j];
1003
+ const xi = ii.x;
1004
+ const yi = ii.y;
1005
+ const xj = jj.x;
1006
+ const yj = jj.y;
1007
+ const intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
1008
+ if (intersect) inside = !inside;
1009
+ }
1010
+ return inside;
1011
+ }
1012
+ function isPointerInGraceArea(event, area) {
1013
+ if (!area) return false;
1014
+ const cursorPos = { x: event.clientX, y: event.clientY };
1015
+ return isPointInPolygon(cursorPos, area);
1016
+ }
1017
+ function whenMouse(handler) {
1018
+ return (event) => event.pointerType === "mouse" ? handler(event) : void 0;
1019
+ }
1020
+ var Root = Menu;
1021
+ var Anchor = MenuAnchor;
1022
+ var PortalExport = MenuPortal;
1023
+ var Content = MenuContent;
1024
+ var Group = MenuGroup;
1025
+ var Label = MenuLabel;
1026
+ var Item = MenuItem;
1027
+ var CheckboxItem = MenuCheckboxItem;
1028
+ var RadioGroup = MenuRadioGroup;
1029
+ var RadioItem = MenuRadioItem;
1030
+ var ItemIndicator = MenuItemIndicator;
1031
+ var Separator = MenuSeparator;
1032
+ var Arrow = MenuArrow;
1033
+ var Sub = MenuSub;
1034
+ var SubTrigger = MenuSubTrigger;
1035
+ var SubContent = MenuSubContent;
1036
+
1037
+ exports.Anchor = Anchor;
1038
+ exports.Arrow = Arrow;
1039
+ exports.CheckboxItem = CheckboxItem;
1040
+ exports.Content = Content;
1041
+ exports.Group = Group;
1042
+ exports.Item = Item;
1043
+ exports.ItemIndicator = ItemIndicator;
1044
+ exports.Label = Label;
1045
+ exports.Menu = Menu;
1046
+ exports.MenuAnchor = MenuAnchor;
1047
+ exports.MenuArrow = MenuArrow;
1048
+ exports.MenuCheckboxItem = MenuCheckboxItem;
1049
+ exports.MenuContent = MenuContent;
1050
+ exports.MenuGroup = MenuGroup;
1051
+ exports.MenuItem = MenuItem;
1052
+ exports.MenuItemIndicator = MenuItemIndicator;
1053
+ exports.MenuLabel = MenuLabel;
1054
+ exports.MenuPortal = MenuPortal;
1055
+ exports.MenuRadioGroup = MenuRadioGroup;
1056
+ exports.MenuRadioItem = MenuRadioItem;
1057
+ exports.MenuSeparator = MenuSeparator;
1058
+ exports.MenuSub = MenuSub;
1059
+ exports.MenuSubContent = MenuSubContent;
1060
+ exports.MenuSubTrigger = MenuSubTrigger;
1061
+ exports.Portal = PortalExport;
1062
+ exports.RadioGroup = RadioGroup;
1063
+ exports.RadioItem = RadioItem;
1064
+ exports.Root = Root;
1065
+ exports.Separator = Separator;
1066
+ exports.Sub = Sub;
1067
+ exports.SubContent = SubContent;
1068
+ exports.SubTrigger = SubTrigger;
1069
+ exports.createMenuScope = createMenuScope;
1070
+ //# sourceMappingURL=index.cjs.map
1071
+ //# sourceMappingURL=index.cjs.map