@astryxdesign/core 0.4.1-canary.2abfff4 → 0.4.1-canary.41950da

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.
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenuSubMenu.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuSubMenu.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAc,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAuB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;AAoB5D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAqF5C,UAAU,4BAA6B,SAAQ,IAAI,CACjD,SAAS,EACT,QAAQ,GAAG,WAAW,GAAG,OAAO,CACjC;IACC,2DAA2D;IAC3D,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B,8CAA8C;IAC9C,KAAK,EAAE,SAAS,CAAC;IACjB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,8CAA8C;IAC9C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mCAAmC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,wBAAyB,SAAQ,4BAA4B;IAC5E;;;;;;;;OAQG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,wBAAwB,GAC9B,SAAS,CAwVX;yBA1Ve,mBAAmB"}
1
+ {"version":3,"file":"DropdownMenuSubMenu.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuSubMenu.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAc,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAuB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;AAoB5D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAqF5C,UAAU,4BAA6B,SAAQ,IAAI,CACjD,SAAS,EACT,QAAQ,GAAG,WAAW,GAAG,OAAO,CACjC;IACC,2DAA2D;IAC3D,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B,8CAA8C;IAC9C,KAAK,EAAE,SAAS,CAAC;IACjB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,8CAA8C;IAC9C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mCAAmC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,wBAAyB,SAAQ,4BAA4B;IAC5E;;;;;;;;OAQG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,wBAAwB,GAC9B,SAAS,CAoVX;yBAtVe,mBAAmB"}
@@ -198,13 +198,9 @@ export function DropdownMenuSubMenu(props) {
198
198
 
199
199
  // Hover-intent: entering the trigger opens after a short delay; leaving
200
200
  // either surface closes after a delay. Hover-open does not steal focus.
201
- // Hover intent and the shared hover→click guard only: this level owns its own
202
- // click handling, roving focus and typeahead. popover="manual", so the
203
- // invoker wiring other consumers need does not apply.
204
201
  const {
205
202
  triggerProps,
206
- contentProps,
207
- confirmHoverOpen
203
+ contentProps
208
204
  } = useMenuHover({
209
205
  show: showLayer,
210
206
  hide: hideLayer,
@@ -217,12 +213,18 @@ export function DropdownMenuSubMenu(props) {
217
213
  }
218
214
  layer.show();
219
215
  if (options?.focusFirst) {
220
- // Synchronous by design — see the focus note in useMenuHover. A
221
- // still-loading flyout has no focusable item, so fall back to the
222
- // container: keyboard ownership must leave the parent list either way.
223
- if (!focusFirst()) {
224
- menuRef.current?.focus();
225
- }
216
+ requestAnimationFrame(() => {
217
+ // Move focus into the flyout. When it has no focusable items yet
218
+ // (e.g. an async submenu showing only a disabled "Loading…" row via
219
+ // hasSpinner), focusFirst() finds nothing — fall back to focusing the
220
+ // flyout container itself so keyboard ownership still transfers off
221
+ // the parent list. Otherwise the parent would keep focus, letting
222
+ // arrow keys rove the parent while the empty flyout stays open.
223
+ const focusedItem = focusFirst();
224
+ if (!focusedItem) {
225
+ menuRef.current?.focus();
226
+ }
227
+ });
226
228
  }
227
229
  }, [canOpen, layer, focusFirst, menuRef]);
228
230
  const close = useCallback(options => {
@@ -242,14 +244,8 @@ export function DropdownMenuSubMenu(props) {
242
244
  if (isDisabled) {
243
245
  return;
244
246
  }
245
- // Toggles, except for the click that follows a hover-open (#3121).
247
+ // Click toggles the flyout, moving focus into it on open.
246
248
  if (isOpen) {
247
- if (confirmHoverOpen()) {
248
- if (!focusFirst()) {
249
- menuRef.current?.focus();
250
- }
251
- return;
252
- }
253
249
  close({
254
250
  focusTrigger: true
255
251
  });
@@ -258,7 +254,7 @@ export function DropdownMenuSubMenu(props) {
258
254
  focusFirst: true
259
255
  });
260
256
  }
261
- }, [isDisabled, isOpen, open, close, confirmHoverOpen, focusFirst, menuRef]);
257
+ }, [isDisabled, isOpen, open, close]);
262
258
  const handleTriggerKeyDown = useCallback(e => {
263
259
  if (isDisabled) {
264
260
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"SideNavHeading.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavHeading.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAoBtD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAErD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAiN5C,MAAM,WAAW,mBAAoB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACpE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,EAC7B,EAAE,EACF,IAAI,EACJ,OAAO,EACP,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,aAAa,EAAE,MAAM,EACrB,GAAG,EACH,GAAG,KAAK,EACT,EAAE,mBAAmB,sCAwfrB;yBAzgBe,cAAc"}
1
+ {"version":3,"file":"SideNavHeading.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavHeading.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAoBtD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAErD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAiN5C,MAAM,WAAW,mBAAoB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACpE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,EAC7B,EAAE,EACF,IAAI,EACJ,OAAO,EACP,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,aAAa,EAAE,MAAM,EACrB,GAAG,EACH,GAAG,KAAK,EACT,EAAE,mBAAmB,sCA8erB;yBA/fe,cAAc"}
@@ -247,12 +247,14 @@ export function SideNavHeading({
247
247
  role: 'none',
248
248
  hasCloseButton: false
249
249
  });
250
+ const closeMenuCtx = useMemo(() => ({
251
+ closeMenu: popover.hide
252
+ }), [popover.hide]);
250
253
  const {
251
254
  triggerProps,
252
255
  contentProps,
253
256
  menuRef,
254
- setTriggerEl,
255
- close: closeMenu
257
+ setTriggerEl
256
258
  } = useMenuHover({
257
259
  show: popover.show,
258
260
  hide: popover.hide,
@@ -260,14 +262,7 @@ export function SideNavHeading({
260
262
  isEnabled: !!menu,
261
263
  showDelay: 0
262
264
  });
263
- const closeMenuCtx = useMemo(() => ({
264
- closeMenu
265
- }), [closeMenu]);
266
-
267
- // setTriggerEl belongs on the chevron button, not this root: it is the
268
- // focus-restore target and a <div> cannot take focus. triggerRef stays here
269
- // because the panel anchors to the whole heading.
270
- const setRef = mergeRefs(rootRef, ref, menu ? popover.triggerRef : undefined);
265
+ const setRef = mergeRefs(rootRef, setTriggerEl, ref, menu ? popover.triggerRef : undefined);
271
266
 
272
267
  // In collapsed mode: hide if no icon, show icon-only if has icon
273
268
  if (isCollapsed && !icon) {
@@ -280,9 +275,7 @@ export function SideNavHeading({
280
275
  },
281
276
  children: icon
282
277
  });
283
- const collapsedSetRef = mergeRefs(collapsedItemRef, ref,
284
- // Collapsed, this button is the trigger, so it takes both roles.
285
- menu ? popover.triggerRef : undefined, menu ? setTriggerEl : undefined);
278
+ const collapsedSetRef = mergeRefs(collapsedItemRef, ref, menu ? popover.triggerRef : undefined);
286
279
  let collapsedElement;
287
280
  if (headingHref) {
288
281
  collapsedElement = /*#__PURE__*/_jsx(LinkComponent, {
@@ -313,8 +306,7 @@ export function SideNavHeading({
313
306
  children: [/*#__PURE__*/_jsxs("button", {
314
307
  type: "button",
315
308
  ...focusOutlineProps.focusVisible(styles.popoverHeading),
316
- // A close affordance, not the trigger: dismiss only.
317
- onClick: closeMenu,
309
+ onClick: triggerProps.onClick,
318
310
  children: [icon && /*#__PURE__*/_jsx("span", {
319
311
  ...{
320
312
  className: "x2lah0s x78zum5 x6s0dn4 xl56j7k"
@@ -454,8 +446,7 @@ export function SideNavHeading({
454
446
  const popoverHeadingContent = /*#__PURE__*/_jsxs("button", {
455
447
  type: "button",
456
448
  ...focusOutlineProps.focusVisible(styles.popoverHeading),
457
- // A close affordance, not the trigger: dismiss only.
458
- onClick: closeMenu,
449
+ onClick: triggerProps.onClick,
459
450
  children: [icon && /*#__PURE__*/_jsx("span", {
460
451
  ...{
461
452
  className: "x2lah0s x78zum5 x6s0dn4 xl56j7k"
@@ -500,7 +491,6 @@ export function SideNavHeading({
500
491
  },
501
492
  children: icon
502
493
  }), renderTextContent(/*#__PURE__*/_jsx("button", {
503
- ref: setTriggerEl,
504
494
  type: "button",
505
495
  "aria-label": t('@astryx.sideNav.heading.openMenu'),
506
496
  onClick: e => {
@@ -556,7 +546,6 @@ export function SideNavHeading({
556
546
  },
557
547
  children: icon
558
548
  })), renderTextContent(showChevron ? /*#__PURE__*/_jsx("button", {
559
- ref: setTriggerEl,
560
549
  type: "button",
561
550
  "aria-label": t('@astryx.sideNav.heading.openMenu'),
562
551
  onClick: e => {
@@ -1 +1 @@
1
- {"version":3,"file":"SideNavItem.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAYf,OAAO,EAAuB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;AAE5D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAIrD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAgB,KAAK,WAAW,EAAC,MAAM,iCAAiC,CAAC;AA8NhF,MAAM,WAAW,gBAAiB,SAAQ,SAAS,CAAC,WAAW,CAAC;IAC9D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC7B;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACpC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACxC;;OAEG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EACR,OAAO,GACP;QACE,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;KACpD,CAAC;IACN;;;OAGG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,EAAE,EACF,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,UAAkB,EAClB,UAAkB,EAClB,IAAI,EACJ,OAAO,EACP,UAAU,EACV,QAAQ,EACR,WAAW,EAAE,eAAe,EAC5B,IAAW,EACX,aAAa,EAAE,MAAM,EACrB,GAAG,EACH,MAAM,EACN,GAAG,IAAI,EACR,EAAE,gBAAgB,sCA4UlB;yBA7Ve,WAAW"}
1
+ {"version":3,"file":"SideNavItem.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAYf,OAAO,EAAuB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;AAE5D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAIrD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAgB,KAAK,WAAW,EAAC,MAAM,iCAAiC,CAAC;AA8NhF,MAAM,WAAW,gBAAiB,SAAQ,SAAS,CAAC,WAAW,CAAC;IAC9D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC7B;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACpC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACxC;;OAEG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EACR,OAAO,GACP;QACE,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;KACpD,CAAC;IACN;;;OAGG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,EAAE,EACF,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,UAAkB,EAClB,UAAkB,EAClB,IAAI,EACJ,OAAO,EACP,UAAU,EACV,QAAQ,EACR,WAAW,EAAE,eAAe,EAC5B,IAAW,EACX,aAAa,EAAE,MAAM,EACrB,GAAG,EACH,MAAM,EACN,GAAG,IAAI,EACR,EAAE,gBAAgB,sCAuUlB;yBAxVe,WAAW"}
@@ -295,12 +295,7 @@ export function SideNavItem({
295
295
  show: popover.show,
296
296
  hide: popover.hide,
297
297
  isOpen: popover.isOpen,
298
- isEnabled: isCollapsed && hasChildren,
299
- // Standard popover toggling: the flyout opens beside the rail, not over
300
- // the icon, so the click after a hover-open is a deliberate dismissal
301
- // rather than the #3121 confirmation the nav menus need.
302
- clickGuardMs: 0,
303
- ownsFocus: false
298
+ isEnabled: isCollapsed && hasChildren
304
299
  });
305
300
 
306
301
  // In collapsed mode: hide items without icons
@@ -1 +1 @@
1
- {"version":3,"file":"TopNavHeading.d.ts","sourceRoot":"","sources":["../../src/TopNav/TopNavHeading.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAatD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAErD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAiL5C,MAAM,WAAW,kBAAmB,SAAQ,SAAS,CAAC,WAAW,CAAC;IAChE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC7B;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;CACxB;AAMD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,EAC5B,EAAE,EACF,IAAI,EACJ,SAAS,EACT,OAAO,EACP,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,aAAa,EAAE,MAAM,EACrB,GAAG,EACH,GAAG,KAAK,EACT,EAAE,kBAAkB,+BAwYpB;yBA1Ze,aAAa"}
1
+ {"version":3,"file":"TopNavHeading.d.ts","sourceRoot":"","sources":["../../src/TopNav/TopNavHeading.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAatD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAErD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAiL5C,MAAM,WAAW,kBAAmB,SAAQ,SAAS,CAAC,WAAW,CAAC;IAChE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC7B;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;CACxB;AAMD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,EAC5B,EAAE,EACF,IAAI,EACJ,SAAS,EACT,OAAO,EACP,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,aAAa,EAAE,MAAM,EACrB,GAAG,EACH,GAAG,KAAK,EACT,EAAE,kBAAkB,+BAiYpB;yBAnZe,aAAa"}
@@ -158,12 +158,14 @@ export function TopNavHeading({
158
158
  role: 'none',
159
159
  hasCloseButton: false
160
160
  });
161
+ const closeMenuCtx = useMemo(() => ({
162
+ closeMenu: popover.hide
163
+ }), [popover.hide]);
161
164
  const {
162
165
  triggerProps,
163
166
  contentProps,
164
167
  menuRef,
165
- setTriggerEl,
166
- close: closeMenu
168
+ setTriggerEl
167
169
  } = useMenuHover({
168
170
  show: popover.show,
169
171
  hide: popover.hide,
@@ -171,14 +173,7 @@ export function TopNavHeading({
171
173
  isEnabled: !!menu,
172
174
  showDelay: 0
173
175
  });
174
- const closeMenuCtx = useMemo(() => ({
175
- closeMenu
176
- }), [closeMenu]);
177
-
178
- // setTriggerEl belongs on the chevron button, not this root: it is the
179
- // focus-restore target and a <div> cannot take focus. triggerRef stays here
180
- // because the panel anchors to the whole heading.
181
- const setRef = mergeRefs(rootRef, ref, menu ? popover.triggerRef : undefined);
176
+ const setRef = mergeRefs(rootRef, setTriggerEl, ref, menu ? popover.triggerRef : undefined);
182
177
  const showChevron = !!menu;
183
178
  const hasAnyHref = !!(headingHref || superheadingHref || subheadingHref);
184
179
 
@@ -252,8 +247,7 @@ export function TopNavHeading({
252
247
  ...{
253
248
  className: "x78zum5 x6s0dn4 x1txdalj xh8yej3 xjbqb8w xjb2p0i x1qlqyl8 x1heor9g x1yc453h xkoleio x12gdq22 x1betex2 x1djylfy xt970qd xcsaf9d x1p37lm5 xhrcg97 x1ypdohk"
254
249
  },
255
- // A close affordance, not the trigger: dismiss only.
256
- onClick: closeMenu,
250
+ onClick: triggerProps.onClick,
257
251
  children: [logo && /*#__PURE__*/_jsx("span", {
258
252
  ...{
259
253
  className: "x2lah0s x78zum5 x6s0dn4 xl56j7k"
@@ -321,7 +315,6 @@ export function TopNavHeading({
321
315
  // Stays a <button>: this box is the popover trigger, so it carries
322
316
  // the accessible name and handlers — only the glyph moves to Icon.
323
317
  _jsx("button", {
324
- ref: setTriggerEl,
325
318
  type: "button",
326
319
  "aria-label": t('@astryx.topNav.heading.openMenu'),
327
320
  onClick: e => {
@@ -388,7 +381,6 @@ export function TopNavHeading({
388
381
  // carries the accessible name and handlers — only the glyph
389
382
  // moves to Icon.
390
383
  _jsx("button", {
391
- ref: setTriggerEl,
392
384
  type: "button",
393
385
  "aria-label": t('@astryx.topNav.heading.openMenu'),
394
386
  onClick: e => {
@@ -1 +1 @@
1
- {"version":3,"file":"TopNavMegaMenu.d.ts","sourceRoot":"","sources":["../../src/TopNav/TopNavMegaMenu.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,EAKZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAkBf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAoM5C,MAAM,WAAW,mBAAoB,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IACvE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,KAAK,EACL,KAAK,EACL,QAAQ,EACR,KAAW,EACX,SAAe,EACf,YAAY,GACb,EAAE,mBAAmB,4BA+BrB;yBAvCe,cAAc"}
1
+ {"version":3,"file":"TopNavMegaMenu.d.ts","sourceRoot":"","sources":["../../src/TopNav/TopNavMegaMenu.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,EAKZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAiBf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAoM5C,MAAM,WAAW,mBAAoB,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IACvE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,KAAK,EACL,KAAK,EACL,QAAQ,EACR,KAAW,EACX,SAAe,EACf,YAAY,GACb,EAAE,mBAAmB,4BA+BrB;yBAvCe,cAAc"}
@@ -34,7 +34,6 @@ import * as stylex from '@stylexjs/stylex';
34
34
  import "../theme/tokens.stylex.js";
35
35
  import { colorVars, spacingVars, radiusVars, durationVars, easeVars, fontWeightVars, shadowVars, typeScaleVars, borderVars } from "../theme/tokens.stylex.js";
36
36
  import { usePopover } from "../Popover/usePopover.js";
37
- import { useMenuHover } from "../hooks/useMenuHover.js";
38
37
  import { Grid } from "../Grid/Grid.js";
39
38
  import { Icon } from "../Icon/index.js";
40
39
  import { mergeProps, mergeRefs } from "../utils/index.js";
@@ -234,8 +233,7 @@ TopNavMegaMenu.displayName = 'TopNavMegaMenu';
234
233
  // DefaultMegaMenu — desktop popover mode
235
234
  // =============================================================================
236
235
 
237
- /** The panel is a grid of links, not `role="menuitem"` rows. */
238
- const PANEL_ITEM_SELECTOR = 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])';
236
+ const CLICK_GUARD_MS = 500;
239
237
  function DefaultMegaMenu({
240
238
  ref,
241
239
  label,
@@ -246,11 +244,17 @@ function DefaultMegaMenu({
246
244
  onOpenChange
247
245
  }) {
248
246
  const slot = useTopNavSlot();
247
+ const showTimeoutRef = useRef(null);
248
+ const hideTimeoutRef = useRef(null);
249
249
  const triggerButtonRef = useRef(null);
250
+ const hoverOpenedAtRef = useRef(0);
251
+ const stickyRef = useRef(false);
250
252
  const handlePopoverShow = useCallback(() => {
251
253
  onOpenChange?.(true);
252
254
  }, [onOpenChange]);
253
255
  const handlePopoverHide = useCallback(() => {
256
+ hoverOpenedAtRef.current = 0;
257
+ stickyRef.current = false;
254
258
  onOpenChange?.(false);
255
259
  }, [onOpenChange]);
256
260
  const popover = usePopover({
@@ -284,29 +288,93 @@ function DefaultMegaMenu({
284
288
  popover.triggerRef(null);
285
289
  };
286
290
  }, [popover]);
287
- const {
288
- triggerProps: hoverTriggerProps,
289
- contentProps,
290
- menuRef,
291
- setTriggerEl
292
- } = useMenuHover({
293
- show: popover.show,
294
- hide: popover.hide,
295
- isOpen: popover.isOpen,
296
- isEnabled: true,
297
- showDelay: delay,
298
- hideDelay,
299
- itemSelector: PANEL_ITEM_SELECTOR,
300
- // Trigger sits outside an auto popover; the invoker relationship exempts it
301
- // from light dismiss.
302
- popoverId: popover.id
303
- });
291
+ const clearTimeouts = useCallback(() => {
292
+ if (showTimeoutRef.current) {
293
+ clearTimeout(showTimeoutRef.current);
294
+ showTimeoutRef.current = null;
295
+ }
296
+ if (hideTimeoutRef.current) {
297
+ clearTimeout(hideTimeoutRef.current);
298
+ hideTimeoutRef.current = null;
299
+ }
300
+ }, []);
301
+ const scheduleShow = useCallback(() => {
302
+ clearTimeouts();
303
+ showTimeoutRef.current = setTimeout(() => {
304
+ hoverOpenedAtRef.current = Date.now();
305
+ popover.show({
306
+ skipAutoFocus: true
307
+ });
308
+ }, delay);
309
+ }, [clearTimeouts, delay, popover]);
310
+ const scheduleHide = useCallback(() => {
311
+ clearTimeouts();
312
+ hideTimeoutRef.current = setTimeout(() => {
313
+ popover.hide();
314
+ }, hideDelay);
315
+ }, [clearTimeouts, hideDelay, popover]);
316
+ const focusFirstPanelItem = useCallback(() => {
317
+ popover.contentRef.current?.querySelector('a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])')?.focus();
318
+ }, [popover.contentRef]);
319
+ const handleTriggerMouseEnter = useCallback(() => {
320
+ clearTimeouts();
321
+ if (!popover.isOpen) {
322
+ scheduleShow();
323
+ }
324
+ }, [clearTimeouts, popover.isOpen, scheduleShow]);
325
+ const handlePanelMouseEnter = useCallback(() => {
326
+ clearTimeouts();
327
+ }, [clearTimeouts]);
328
+ const handleMouseLeave = useCallback(() => {
329
+ if (!stickyRef.current) {
330
+ scheduleHide();
331
+ }
332
+ }, [scheduleHide]);
333
+ const handleClick = useCallback(event => {
334
+ // Cancel the native invoker toggle so this guard is the single source of
335
+ // truth for trigger activation. popoverTarget still establishes the
336
+ // invoker relationship used by native light-dismiss and stacking.
337
+ event.preventDefault();
338
+ clearTimeouts();
339
+ if (event.detail === 0) {
340
+ stickyRef.current = true;
341
+ hoverOpenedAtRef.current = 0;
342
+ if (popover.isOpen) {
343
+ focusFirstPanelItem();
344
+ } else {
345
+ popover.show();
346
+ }
347
+ } else if (!popover.isOpen) {
348
+ stickyRef.current = true;
349
+ popover.show({
350
+ skipAutoFocus: true
351
+ });
352
+ } else if (Date.now() - hoverOpenedAtRef.current < CLICK_GUARD_MS) {
353
+ // A click that naturally follows a hover-open confirms the open state
354
+ // instead of toggling the panel shut. From here it behaves like any
355
+ // other click-open and stays pinned until explicit dismissal.
356
+ stickyRef.current = true;
357
+ hoverOpenedAtRef.current = 0;
358
+ } else {
359
+ popover.hide();
360
+ triggerButtonRef.current?.focus();
361
+ }
362
+ }, [clearTimeouts, focusFirstPanelItem, popover]);
363
+ useEffect(() => {
364
+ return () => {
365
+ clearTimeouts();
366
+ };
367
+ }, [clearTimeouts]);
304
368
  return /*#__PURE__*/_jsxs(_Fragment, {
305
369
  children: [/*#__PURE__*/_jsxs("button", {
306
- ref: mergeRefs(triggerButtonRef, setTriggerEl, ref),
370
+ ref: mergeRefs(triggerButtonRef, ref),
307
371
  type: "button",
308
372
  ...popover.triggerProps,
309
- ...hoverTriggerProps,
373
+ // Native invoker prevents trigger light-dismiss.
374
+ popoverTarget: popover.id,
375
+ onClick: handleClick,
376
+ onMouseEnter: handleTriggerMouseEnter,
377
+ onMouseLeave: handleMouseLeave,
310
378
  ...mergeProps(themeProps('top-nav-mega-menu'), focusOutlineProps.focusVisible(styles.trigger, popover.isOpen && styles.triggerOpen)),
311
379
  children: [label, /*#__PURE__*/_jsx(Icon, {
312
380
  icon: "chevronDown",
@@ -318,10 +386,10 @@ function DefaultMegaMenu({
318
386
  // role="group" — a mega menu is a browsing grid of links, not an
319
387
  // ARIA menu of menuitems (per the WAI-ARIA APG, the menu role is
320
388
  // for action menus; link mega menus are the documented anti-case).
321
- ref: menuRef,
322
389
  role: "group",
323
390
  "aria-label": label,
324
- ...contentProps,
391
+ onMouseEnter: handlePanelMouseEnter,
392
+ onMouseLeave: handleMouseLeave,
325
393
  ...{
326
394
  className: "x1prclbq x11xkdxz x13fuv20 x1pc3f07 x1hviunn x1i5ehqx xb3r6kr x78zum5 xdt5ytf x2lwn1j"
327
395
  },
@@ -1 +1 @@
1
- {"version":3,"file":"TopNavMenu.d.ts","sourceRoot":"","sources":["../../src/TopNav/TopNavMenu.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,EAKZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAQf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA8L5C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAMD,MAAM,WAAW,eAAgB,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IACnE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAE5B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,wBAAgB,UAAU,CAAC,EACzB,GAAG,EACH,KAAK,EACL,KAAK,EACL,KAAW,EACX,SAAe,GAChB,EAAE,eAAe,4BA4OjB;yBAlPe,UAAU"}
1
+ {"version":3,"file":"TopNavMenu.d.ts","sourceRoot":"","sources":["../../src/TopNav/TopNavMenu.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,EAKZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAQf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA8L5C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAMD,MAAM,WAAW,eAAgB,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IACnE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAE5B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,wBAAgB,UAAU,CAAC,EACzB,GAAG,EACH,KAAK,EACL,KAAK,EACL,KAAW,EACX,SAAe,GAChB,EAAE,eAAe,4BAwOjB;yBA9Oe,UAAU"}
@@ -197,18 +197,14 @@ export function TopNavMenu({
197
197
  triggerProps,
198
198
  contentProps,
199
199
  menuRef,
200
- setTriggerEl,
201
- close
200
+ setTriggerEl
202
201
  } = useMenuHover({
203
202
  show: popover.show,
204
203
  hide: popover.hide,
205
204
  isOpen: popover.isOpen,
206
205
  isEnabled: true,
207
206
  showDelay: delay,
208
- hideDelay,
209
- // Trigger sits outside an auto popover; the invoker relationship exempts
210
- // it from light dismiss.
211
- popoverId: popover.id
207
+ hideDelay
212
208
  });
213
209
  const setTriggerRef = mergeRefs(triggerButtonRef, popover.triggerRef, setTriggerEl, ref);
214
210
 
@@ -225,8 +221,7 @@ export function TopNavMenu({
225
221
  } = useListFocus({
226
222
  itemSelector: '[role="menuitem"]',
227
223
  hasRovingTabIndex: true,
228
- // Not popover.hide: Escape must also restore focus to the trigger.
229
- onEscape: close
224
+ onEscape: popover.hide
230
225
  });
231
226
 
232
227
  // First-character typeahead over the menu items (menus-11).