@iris-ui-kit/vue 0.2.26 → 0.2.27

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/admin.d.cts CHANGED
@@ -200,6 +200,7 @@ declare const IrisAdminLayout: vue.DefineComponent<vue.ExtractPropTypes<{
200
200
  /** Public input/event surface inferred from the runtime Vue component. */
201
201
  type IrisAdminLayoutProps = InstanceType<typeof IrisAdminLayout>['$props'];
202
202
 
203
+ /** Hover open/close latencies for flyouts (horizontal mode + collapsed rail). */
203
204
  /**
204
205
  * Data-driven nested navigation menu — the sidebar nav of the admin shell.
205
206
  * Renders a normalized `NavNode[]` tree as an accordion of expandable branches
package/dist/admin.d.ts CHANGED
@@ -200,6 +200,7 @@ declare const IrisAdminLayout: vue.DefineComponent<vue.ExtractPropTypes<{
200
200
  /** Public input/event surface inferred from the runtime Vue component. */
201
201
  type IrisAdminLayoutProps = InstanceType<typeof IrisAdminLayout>['$props'];
202
202
 
203
+ /** Hover open/close latencies for flyouts (horizontal mode + collapsed rail). */
203
204
  /**
204
205
  * Data-driven nested navigation menu — the sidebar nav of the admin shell.
205
206
  * Renders a normalized `NavNode[]` tree as an accordion of expandable branches
package/dist/admin.js CHANGED
@@ -1,4 +1,4 @@
1
- export { IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisNavMenu, createAdminPreferences, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, localStorageAdminPreferencesStorage, nodeAllowsRoles, useAdminPreferences, useAdminShell, useTabsNav, visibleNav } from './chunk-ZWFSGMOB.js';
1
+ export { IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisNavMenu, createAdminPreferences, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, localStorageAdminPreferencesStorage, nodeAllowsRoles, useAdminPreferences, useAdminShell, useTabsNav, visibleNav } from './chunk-ASFDGYIR.js';
2
2
  import './chunk-IPR5JAVF.js';
3
3
  import './chunk-NNOF7KUH.js';
4
4
  import './chunk-F77KIPOH.js';
@@ -6,8 +6,8 @@ import { IrisThemeKey } from './chunk-ZTPIXL7Q.js';
6
6
  import { IrisSortable } from './chunk-W3B6E5GN.js';
7
7
  import { useStore } from './chunk-75GSYEJS.js';
8
8
  import { useI18n } from './chunk-TCAXRWT6.js';
9
- import { defineComponent, inject, h, computed, shallowRef, onBeforeUnmount, watch, ref, provide, useId, nextTick, Teleport, Fragment, watchEffect, toValue, onMounted } from 'vue';
10
- import { visibleNav, findNavPath, createExpansion, branchTrail, isBranch, createFloatingMachine, findNavNode, firstLeaf, matchTypeahead, nextEnabledIndex, isClosable, createAdminShell } from '@iris-ui-kit/core';
9
+ import { defineComponent, inject, h, computed, shallowRef, onBeforeUnmount, watch, provide, useId, ref, nextTick, Teleport, Fragment, watchEffect, toValue, onMounted } from 'vue';
10
+ import { visibleNav, findNavPath, createExpansion, branchTrail, createFloatingMachine, isBranch, findNavNode, firstLeaf, matchTypeahead, nextEnabledIndex, isClosable, createAdminShell } from '@iris-ui-kit/core';
11
11
  export { createAdminPreferences, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, localStorageAdminPreferencesStorage, nodeAllowsRoles, visibleNav } from '@iris-ui-kit/core';
12
12
  import { defaultIconRegistry, resolveThemedIcon } from '@iris-ui-kit/icons';
13
13
 
@@ -348,10 +348,317 @@ function installNavMenuStyles() {
348
348
  document.head.appendChild(style);
349
349
  installed = true;
350
350
  }
351
+ var HOVER_OPEN_DELAY_VAL = 60;
352
+ var HOVER_CLOSE_DELAY_VAL = 150;
353
+ function createNavMenuFlyout(ctx) {
354
+ const hovered = ref(null);
355
+ const hoveredBranches = ref([]);
356
+ const focusedBranches = ref([]);
357
+ const clickedBranches = ref([]);
358
+ let suppressFocusOpen = false;
359
+ let openTimer = null;
360
+ const closeTimers = /* @__PURE__ */ new Map();
361
+ const clearOpenTimer = () => {
362
+ if (openTimer) {
363
+ clearTimeout(openTimer);
364
+ openTimer = null;
365
+ }
366
+ };
367
+ const cancelClose = (key) => {
368
+ const timer = closeTimers.get(key);
369
+ if (timer) {
370
+ clearTimeout(timer);
371
+ closeTimers.delete(key);
372
+ }
373
+ };
374
+ const cancelAllTimers = () => {
375
+ clearOpenTimer();
376
+ for (const timer of closeTimers.values()) clearTimeout(timer);
377
+ closeTimers.clear();
378
+ };
379
+ onBeforeUnmount(cancelAllTimers);
380
+ const flyoutTriggers = /* @__PURE__ */ new Map();
381
+ const flyoutPanels = /* @__PURE__ */ new Map();
382
+ const setFlyoutRef = (map, key, el) => {
383
+ if (el instanceof HTMLElement) map.set(key, el);
384
+ else map.delete(key);
385
+ };
386
+ const positionFlyout = (key) => {
387
+ const trigger = flyoutTriggers.get(key);
388
+ const panel = flyoutPanels.get(key);
389
+ if (!trigger || !panel || typeof document === "undefined") return;
390
+ const rect = trigger.getBoundingClientRect();
391
+ const dir = getComputedStyle(trigger).direction;
392
+ panel.style.position = "fixed";
393
+ panel.style.top = `${ctx.collapsed ? rect.top : rect.bottom}px`;
394
+ if (dir === "rtl") {
395
+ panel.style.left = "";
396
+ panel.style.right = `${window.innerWidth - (ctx.collapsed ? rect.left : rect.right)}px`;
397
+ } else {
398
+ panel.style.right = "";
399
+ panel.style.left = `${ctx.collapsed ? rect.right : rect.left}px`;
400
+ }
401
+ };
402
+ onBeforeUnmount(() => {
403
+ detachViewportListeners();
404
+ flyoutTriggers.clear();
405
+ flyoutPanels.clear();
406
+ });
407
+ const isolateAtDepth = (key) => {
408
+ const depth = findNavPath(ctx.items, key).length - 1;
409
+ const isSameDepthOther = (k) => k !== key && findNavPath(ctx.items, k).length - 1 === depth;
410
+ for (const state of [hoveredBranches, clickedBranches, focusedBranches]) {
411
+ if (state.value.some(isSameDepthOther)) {
412
+ state.value = state.value.filter((k) => !isSameDepthOther(k));
413
+ }
414
+ }
415
+ };
416
+ const scheduleOpen = (key) => {
417
+ clearOpenTimer();
418
+ openTimer = setTimeout(() => {
419
+ openTimer = null;
420
+ isolateAtDepth(key);
421
+ setBranchInteraction(hoveredBranches, key, true);
422
+ }, HOVER_OPEN_DELAY_VAL);
423
+ };
424
+ const scheduleClose = (key) => {
425
+ cancelClose(key);
426
+ closeTimers.set(
427
+ key,
428
+ setTimeout(() => {
429
+ closeTimers.delete(key);
430
+ setBranchInteraction(hoveredBranches, key, false);
431
+ }, HOVER_CLOSE_DELAY_VAL)
432
+ );
433
+ };
434
+ const closeHorizontalMenus = () => {
435
+ if (!ctx.flyoutMode.value) return;
436
+ cancelAllTimers();
437
+ hovered.value = null;
438
+ hoveredBranches.value = [];
439
+ focusedBranches.value = [];
440
+ clickedBranches.value = [];
441
+ };
442
+ const select = (node) => {
443
+ if (node.disabled) return;
444
+ closeHorizontalMenus();
445
+ ctx.emitSelect(node.key, node);
446
+ };
447
+ const toggleFlyout = (key) => {
448
+ cancelAllTimers();
449
+ const current = clickedBranches.value;
450
+ if (current.includes(key)) {
451
+ clickedBranches.value = current.filter((k) => k !== key);
452
+ setBranchInteraction(hoveredBranches, key, false);
453
+ setBranchInteraction(focusedBranches, key, false);
454
+ } else {
455
+ isolateAtDepth(key);
456
+ clickedBranches.value = [...clickedBranches.value, key];
457
+ }
458
+ };
459
+ const openFlyout = (key) => {
460
+ cancelAllTimers();
461
+ isolateAtDepth(key);
462
+ setBranchInteraction(focusedBranches, key, true);
463
+ };
464
+ const setBranchInteraction = (state, key, enabled) => {
465
+ const current = state.value;
466
+ if (enabled) {
467
+ if (!current.includes(key)) state.value = [...current, key];
468
+ } else if (current.includes(key)) {
469
+ state.value = current.filter((item) => item !== key);
470
+ }
471
+ };
472
+ const keepsPointerInside = (container, event) => {
473
+ if (!container || !(container instanceof HTMLElement)) return false;
474
+ const next = event.relatedTarget;
475
+ return !!(next && next instanceof Node && container.contains(next));
476
+ };
477
+ const interactionKeyAtDepth = (keys, depth) => keys.find((key) => findNavPath(ctx.items, key).length === depth + 1);
478
+ const horizontalBranchVisible = (key, depth) => {
479
+ const hoveredAtDepth = interactionKeyAtDepth(hoveredBranches.value, depth);
480
+ if (hoveredAtDepth) return hoveredAtDepth === key;
481
+ const clickedAtDepth = interactionKeyAtDepth(clickedBranches.value, depth);
482
+ if (clickedAtDepth) return clickedAtDepth === key;
483
+ return interactionKeyAtDepth(focusedBranches.value, depth) === key;
484
+ };
485
+ const shownFlyoutKeys = computed(() => {
486
+ if (!ctx.flyoutMode.value) return [];
487
+ const keys = [];
488
+ for (const node of ctx.tree.value) {
489
+ if (isBranch(node) && horizontalBranchVisible(node.key, 0)) keys.push(node.key);
490
+ }
491
+ return keys;
492
+ });
493
+ const repositionOpenFlyouts = () => {
494
+ for (const key of shownFlyoutKeys.value) positionFlyout(key);
495
+ };
496
+ let viewportListenersActive = false;
497
+ const attachViewportListeners = () => {
498
+ if (viewportListenersActive || typeof document === "undefined") return;
499
+ viewportListenersActive = true;
500
+ document.addEventListener("scroll", repositionOpenFlyouts, true);
501
+ window.addEventListener("resize", repositionOpenFlyouts);
502
+ };
503
+ const detachViewportListeners = () => {
504
+ if (!viewportListenersActive || typeof document === "undefined") return;
505
+ viewportListenersActive = false;
506
+ document.removeEventListener("scroll", repositionOpenFlyouts, true);
507
+ window.removeEventListener("resize", repositionOpenFlyouts);
508
+ };
509
+ watch(
510
+ shownFlyoutKeys,
511
+ (keys, prev) => {
512
+ if (keys.length > 0 && prev.length === 0) attachViewportListeners();
513
+ else if (keys.length === 0 && prev.length > 0) detachViewportListeners();
514
+ for (const key of keys) positionFlyout(key);
515
+ },
516
+ { flush: "post" }
517
+ );
518
+ return {
519
+ hovered,
520
+ hoveredBranches,
521
+ focusedBranches,
522
+ clickedBranches,
523
+ select,
524
+ toggleFlyout,
525
+ openFlyout,
526
+ closeHorizontalMenus,
527
+ setBranchInteraction,
528
+ keepsPointerInside,
529
+ interactionKeyAtDepth,
530
+ horizontalBranchVisible,
531
+ shownFlyoutKeys,
532
+ setFlyoutRef,
533
+ flyoutTriggers,
534
+ flyoutPanels,
535
+ suppressFocusOpen: { get: () => suppressFocusOpen },
536
+ setSuppressFocusOpen: (value) => {
537
+ suppressFocusOpen = value;
538
+ },
539
+ cancelClose,
540
+ scheduleOpen,
541
+ scheduleClose,
542
+ cancelAllTimers
543
+ };
544
+ }
545
+ function createNavMenuKeydownHandler(deps) {
546
+ const {
547
+ items,
548
+ flyoutMode,
549
+ expanded,
550
+ toggle,
551
+ openFlyout,
552
+ closeHorizontalMenus,
553
+ horizontalBranchVisible,
554
+ setBranchInteraction,
555
+ focusedBranches,
556
+ collapsed,
557
+ hoveredBranches,
558
+ clickedBranches,
559
+ setSuppressFocusOpen
560
+ } = deps;
561
+ return (e) => {
562
+ const root = e.currentTarget;
563
+ const buttons = Array.from(root.querySelectorAll("[data-iris-nav-item]")).filter(
564
+ (button) => !button.closest('[data-iris-nav-children][aria-hidden="true"]')
565
+ );
566
+ if (buttons.length === 0) return;
567
+ const idx = buttons.indexOf(document.activeElement);
568
+ const focusAt = (i) => buttons[(i + buttons.length) % buttons.length]?.focus();
569
+ const key = idx >= 0 ? buttons[idx]?.getAttribute("data-key") : void 0;
570
+ const node = key ? findNavNode(items, key) : void 0;
571
+ const depth = idx >= 0 ? Number(buttons[idx]?.getAttribute("data-depth") ?? 0) : 0;
572
+ switch (e.key) {
573
+ case "ArrowDown": {
574
+ e.preventDefault();
575
+ if (flyoutMode() && node && isBranch(node)) {
576
+ openFlyout(key);
577
+ const group = buttons[idx].closest("[data-iris-nav-group]");
578
+ void nextTick(() => {
579
+ group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
580
+ });
581
+ return;
582
+ }
583
+ focusAt(idx < 0 ? 0 : idx + 1);
584
+ return;
585
+ }
586
+ case "ArrowUp": {
587
+ e.preventDefault();
588
+ if (flyoutMode() && node && isBranch(node) && horizontalBranchVisible(key, depth)) {
589
+ setBranchInteraction(focusedBranches, key, false);
590
+ setBranchInteraction(clickedBranches, key, false);
591
+ return;
592
+ }
593
+ focusAt(idx < 0 ? buttons.length - 1 : idx - 1);
594
+ return;
595
+ }
596
+ case "Home":
597
+ e.preventDefault();
598
+ buttons[0]?.focus();
599
+ return;
600
+ case "End":
601
+ e.preventDefault();
602
+ buttons[buttons.length - 1]?.focus();
603
+ return;
604
+ case "Escape": {
605
+ if (flyoutMode()) {
606
+ const openKeys = [
607
+ ...hoveredBranches.value,
608
+ ...clickedBranches.value,
609
+ ...focusedBranches.value
610
+ ];
611
+ if (openKeys.length > 0) {
612
+ e.preventDefault();
613
+ const openTop = openKeys.find((k) => findNavPath(items, k).length === 1);
614
+ closeHorizontalMenus();
615
+ if (openTop) {
616
+ setSuppressFocusOpen(true);
617
+ buttons.find((b) => b.getAttribute("data-key") === openTop)?.focus();
618
+ void nextTick(() => {
619
+ setSuppressFocusOpen(false);
620
+ });
621
+ }
622
+ }
623
+ }
624
+ return;
625
+ }
626
+ }
627
+ if (collapsed || idx < 0 || !key || !node) return;
628
+ if (e.key === "ArrowRight") {
629
+ e.preventDefault();
630
+ if (isBranch(node)) {
631
+ if (flyoutMode() || !expanded().includes(key)) {
632
+ if (flyoutMode()) openFlyout(key);
633
+ else toggle(key);
634
+ const group = buttons[idx].closest("[data-iris-nav-group]");
635
+ void nextTick(() => {
636
+ group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
637
+ });
638
+ } else {
639
+ focusAt(idx + 1);
640
+ }
641
+ }
642
+ } else if (e.key === "ArrowLeft") {
643
+ e.preventDefault();
644
+ if (flyoutMode() && isBranch(node)) {
645
+ setBranchInteraction(focusedBranches, key, false);
646
+ setBranchInteraction(clickedBranches, key, false);
647
+ return;
648
+ }
649
+ if (isBranch(node) && expanded().includes(key)) {
650
+ toggle(key);
651
+ } else {
652
+ const parentKey = findNavPath(items, key).at(-2)?.key;
653
+ if (parentKey) {
654
+ buttons.find((b) => b.getAttribute("data-key") === parentKey)?.focus();
655
+ }
656
+ }
657
+ }
658
+ };
659
+ }
351
660
 
352
661
  // src/admin/NavMenu.ts
353
- var HOVER_OPEN_DELAY = 100;
354
- var HOVER_CLOSE_DELAY = 150;
355
662
  var IrisNavMenu = defineComponent({
356
663
  name: "IrisNavMenu",
357
664
  inheritAttrs: false,
@@ -413,170 +720,35 @@ var IrisNavMenu = defineComponent({
413
720
  emit("update:expandedKeys", model.get());
414
721
  }
415
722
  };
416
- const hovered = ref(null);
417
- const hoveredBranches = ref([]);
418
- const focusedBranches = ref([]);
419
- const clickedBranches = ref([]);
420
- let suppressFocusOpen = false;
421
- let openTimer = null;
422
- const closeTimers = /* @__PURE__ */ new Map();
423
- const clearOpenTimer = () => {
424
- if (openTimer) {
425
- clearTimeout(openTimer);
426
- openTimer = null;
427
- }
428
- };
429
- const cancelClose = (key) => {
430
- const timer = closeTimers.get(key);
431
- if (timer) {
432
- clearTimeout(timer);
433
- closeTimers.delete(key);
434
- }
435
- };
436
- const cancelAllTimers = () => {
437
- clearOpenTimer();
438
- for (const timer of closeTimers.values()) clearTimeout(timer);
439
- closeTimers.clear();
440
- };
441
- onBeforeUnmount(cancelAllTimers);
442
- const flyoutTriggers = /* @__PURE__ */ new Map();
443
- const flyoutPanels = /* @__PURE__ */ new Map();
444
- const setFlyoutRef = (map, key, el) => {
445
- if (el instanceof HTMLElement) map.set(key, el);
446
- else map.delete(key);
447
- };
448
- const positionFlyout = (key) => {
449
- const trigger = flyoutTriggers.get(key);
450
- const panel = flyoutPanels.get(key);
451
- if (!trigger || !panel || typeof document === "undefined") return;
452
- const rect = trigger.getBoundingClientRect();
453
- const dir = getComputedStyle(trigger).direction;
454
- panel.style.position = "fixed";
455
- panel.style.top = `${props.collapsed ? rect.top : rect.bottom}px`;
456
- if (dir === "rtl") {
457
- panel.style.left = "";
458
- panel.style.right = `${window.innerWidth - (props.collapsed ? rect.left : rect.right)}px`;
459
- } else {
460
- panel.style.right = "";
461
- panel.style.left = `${props.collapsed ? rect.right : rect.left}px`;
462
- }
463
- };
464
- onBeforeUnmount(() => {
465
- detachViewportListeners();
466
- flyoutTriggers.clear();
467
- flyoutPanels.clear();
723
+ const flyout = createNavMenuFlyout({
724
+ items: props.items,
725
+ tree,
726
+ expanded,
727
+ flyoutMode: { value: flyoutMode.value },
728
+ collapsed: props.collapsed,
729
+ toggle,
730
+ emitSelect: (key, node) => emit("select", key, node)
468
731
  });
469
- const isolateAtDepth = (key) => {
470
- const depth = findNavPath(props.items, key).length - 1;
471
- const isSameDepthOther = (k) => k !== key && findNavPath(props.items, k).length - 1 === depth;
472
- for (const state of [hoveredBranches, clickedBranches, focusedBranches]) {
473
- if (state.value.some(isSameDepthOther)) {
474
- state.value = state.value.filter((k) => !isSameDepthOther(k));
475
- }
476
- }
477
- };
478
- const scheduleOpen = (key) => {
479
- clearOpenTimer();
480
- openTimer = setTimeout(() => {
481
- openTimer = null;
482
- isolateAtDepth(key);
483
- setBranchInteraction(hoveredBranches, key, true);
484
- }, HOVER_OPEN_DELAY);
485
- };
486
- const scheduleClose = (key) => {
487
- cancelClose(key);
488
- closeTimers.set(
489
- key,
490
- setTimeout(() => {
491
- closeTimers.delete(key);
492
- setBranchInteraction(hoveredBranches, key, false);
493
- }, HOVER_CLOSE_DELAY)
494
- );
495
- };
496
- const closeHorizontalMenus = () => {
497
- if (!flyoutMode.value) return;
498
- cancelAllTimers();
499
- hovered.value = null;
500
- hoveredBranches.value = [];
501
- focusedBranches.value = [];
502
- clickedBranches.value = [];
503
- };
504
- const select = (node) => {
505
- if (node.disabled) return;
506
- closeHorizontalMenus();
507
- emit("select", node.key, node);
508
- };
509
- const toggleFlyout = (key) => {
510
- cancelAllTimers();
511
- const current = clickedBranches.value;
512
- if (current.includes(key)) {
513
- clickedBranches.value = current.filter((k) => k !== key);
514
- setBranchInteraction(hoveredBranches, key, false);
515
- setBranchInteraction(focusedBranches, key, false);
516
- } else {
517
- isolateAtDepth(key);
518
- clickedBranches.value = [...clickedBranches.value, key];
519
- }
520
- };
521
- const openFlyout = (key) => {
522
- cancelAllTimers();
523
- isolateAtDepth(key);
524
- setBranchInteraction(focusedBranches, key, true);
525
- };
526
- const setBranchInteraction = (state, key, enabled) => {
527
- const current = state.value;
528
- if (enabled) {
529
- if (!current.includes(key)) state.value = [...current, key];
530
- } else if (current.includes(key)) {
531
- state.value = current.filter((item) => item !== key);
532
- }
533
- };
534
- const keepsPointerInside = (container, event) => {
535
- if (!container || !(container instanceof HTMLElement)) return false;
536
- const next = event.relatedTarget;
537
- return !!(next && next instanceof Node && container.contains(next));
538
- };
539
- const interactionKeyAtDepth = (keys, depth) => keys.find((key) => findNavPath(props.items, key).length === depth + 1);
540
- const horizontalBranchVisible = (key, depth) => {
541
- const hoveredAtDepth = interactionKeyAtDepth(hoveredBranches.value, depth);
542
- if (hoveredAtDepth) return hoveredAtDepth === key;
543
- const clickedAtDepth = interactionKeyAtDepth(clickedBranches.value, depth);
544
- if (clickedAtDepth) return clickedAtDepth === key;
545
- return interactionKeyAtDepth(focusedBranches.value, depth) === key;
546
- };
547
- const shownFlyoutKeys = computed(() => {
548
- if (!flyoutMode.value) return [];
549
- const keys = [];
550
- for (const node of tree.value) {
551
- if (isBranch(node) && horizontalBranchVisible(node.key, 0)) keys.push(node.key);
552
- }
553
- return keys;
554
- });
555
- const repositionOpenFlyouts = () => {
556
- for (const key of shownFlyoutKeys.value) positionFlyout(key);
557
- };
558
- let viewportListenersActive = false;
559
- const attachViewportListeners = () => {
560
- if (viewportListenersActive || typeof document === "undefined") return;
561
- viewportListenersActive = true;
562
- document.addEventListener("scroll", repositionOpenFlyouts, true);
563
- window.addEventListener("resize", repositionOpenFlyouts);
564
- };
565
- const detachViewportListeners = () => {
566
- if (!viewportListenersActive || typeof document === "undefined") return;
567
- viewportListenersActive = false;
568
- document.removeEventListener("scroll", repositionOpenFlyouts, true);
569
- window.removeEventListener("resize", repositionOpenFlyouts);
570
- };
571
- watch(
572
- shownFlyoutKeys,
573
- (keys, prev) => {
574
- if (keys.length > 0 && prev.length === 0) attachViewportListeners();
575
- else if (keys.length === 0 && prev.length > 0) detachViewportListeners();
576
- for (const key of keys) positionFlyout(key);
577
- },
578
- { flush: "post" }
579
- );
732
+ const {
733
+ hovered,
734
+ hoveredBranches,
735
+ focusedBranches,
736
+ clickedBranches,
737
+ select,
738
+ toggleFlyout,
739
+ openFlyout,
740
+ closeHorizontalMenus,
741
+ setBranchInteraction,
742
+ keepsPointerInside,
743
+ horizontalBranchVisible,
744
+ setFlyoutRef,
745
+ flyoutTriggers,
746
+ flyoutPanels,
747
+ suppressFocusOpen,
748
+ cancelClose,
749
+ scheduleOpen,
750
+ scheduleClose
751
+ } = flyout;
580
752
  const itemClass = (opts) => {
581
753
  const depth = Math.min(9, opts.depth);
582
754
  return [
@@ -712,7 +884,7 @@ var IrisNavMenu = defineComponent({
712
884
  }
713
885
  },
714
886
  onFocusin: () => {
715
- if (suppressFocusOpen) return;
887
+ if (suppressFocusOpen.get()) return;
716
888
  setBranchInteraction(focusedBranches, node.key, true);
717
889
  },
718
890
  onFocusout: (event) => {
@@ -765,104 +937,23 @@ var IrisNavMenu = defineComponent({
765
937
  },
766
938
  { flush: "post" }
767
939
  );
768
- const onKeydown = (e) => {
769
- const root = e.currentTarget;
770
- const buttons = Array.from(root.querySelectorAll("[data-iris-nav-item]")).filter(
771
- (button) => !button.closest('[data-iris-nav-children][aria-hidden="true"]')
772
- );
773
- if (buttons.length === 0) return;
774
- const idx = buttons.indexOf(document.activeElement);
775
- const focusAt = (i) => buttons[(i + buttons.length) % buttons.length]?.focus();
776
- const key = idx >= 0 ? buttons[idx]?.getAttribute("data-key") : void 0;
777
- const node = key ? findNavNode(props.items, key) : void 0;
778
- const depth = idx >= 0 ? Number(buttons[idx]?.getAttribute("data-depth") ?? 0) : 0;
779
- switch (e.key) {
780
- case "ArrowDown": {
781
- e.preventDefault();
782
- if (flyoutMode.value && node && isBranch(node)) {
783
- openFlyout(key);
784
- const group = buttons[idx].closest("[data-iris-nav-group]");
785
- void nextTick(() => {
786
- group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
787
- });
788
- return;
789
- }
790
- focusAt(idx < 0 ? 0 : idx + 1);
791
- return;
792
- }
793
- case "ArrowUp": {
794
- e.preventDefault();
795
- if (flyoutMode.value && node && isBranch(node) && horizontalBranchVisible(key, depth)) {
796
- setBranchInteraction(focusedBranches, key, false);
797
- setBranchInteraction(clickedBranches, key, false);
798
- return;
799
- }
800
- focusAt(idx < 0 ? buttons.length - 1 : idx - 1);
801
- return;
802
- }
803
- case "Home":
804
- e.preventDefault();
805
- buttons[0]?.focus();
806
- return;
807
- case "End":
808
- e.preventDefault();
809
- buttons[buttons.length - 1]?.focus();
810
- return;
811
- case "Escape": {
812
- if (flyoutMode.value) {
813
- const openKeys = [
814
- ...hoveredBranches.value,
815
- ...clickedBranches.value,
816
- ...focusedBranches.value
817
- ];
818
- if (openKeys.length > 0) {
819
- e.preventDefault();
820
- const openTop = openKeys.find((k) => findNavPath(props.items, k).length === 1);
821
- closeHorizontalMenus();
822
- if (openTop) {
823
- suppressFocusOpen = true;
824
- buttons.find((b) => b.getAttribute("data-key") === openTop)?.focus();
825
- void nextTick(() => {
826
- suppressFocusOpen = false;
827
- });
828
- }
829
- }
830
- }
831
- return;
832
- }
833
- }
834
- if (props.collapsed || idx < 0 || !key || !node) return;
835
- if (e.key === "ArrowRight") {
836
- e.preventDefault();
837
- if (isBranch(node)) {
838
- if (flyoutMode.value || !expanded.value.includes(key)) {
839
- if (flyoutMode.value) openFlyout(key);
840
- else toggle(key);
841
- const group = buttons[idx].closest("[data-iris-nav-group]");
842
- void nextTick(() => {
843
- group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
844
- });
845
- } else {
846
- focusAt(idx + 1);
847
- }
848
- }
849
- } else if (e.key === "ArrowLeft") {
850
- e.preventDefault();
851
- if (flyoutMode.value && isBranch(node)) {
852
- setBranchInteraction(focusedBranches, key, false);
853
- setBranchInteraction(clickedBranches, key, false);
854
- return;
855
- }
856
- if (isBranch(node) && expanded.value.includes(key)) {
857
- toggle(key);
858
- } else {
859
- const parentKey = findNavPath(props.items, key).at(-2)?.key;
860
- if (parentKey) {
861
- buttons.find((b) => b.getAttribute("data-key") === parentKey)?.focus();
862
- }
863
- }
864
- }
865
- };
940
+ const onKeydown = createNavMenuKeydownHandler({
941
+ items: props.items,
942
+ flyoutMode: () => flyoutMode.value,
943
+ expanded: () => expanded.value,
944
+ toggle,
945
+ openFlyout,
946
+ closeHorizontalMenus,
947
+ horizontalBranchVisible,
948
+ setBranchInteraction,
949
+ focusedBranches,
950
+ hoveredBranches,
951
+ clickedBranches,
952
+ setSuppressFocusOpen: (v) => {
953
+ flyout.setSuppressFocusOpen(v);
954
+ },
955
+ collapsed: props.collapsed
956
+ });
866
957
  const menuClass = [
867
958
  "iris-nav-menu",
868
959
  `iris-nav-menu--${props.orientation}`,
@@ -2033,5 +2124,5 @@ function useAdminPreferences(preferences, hydrate = true) {
2033
2124
  }
2034
2125
 
2035
2126
  export { DropdownContextKey, IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisBreadcrumb, IrisBreadcrumbItem, IrisDropdown, IrisDropdownItem, IrisDropdownMenu, IrisDropdownSeparator, IrisDropdownTrigger, IrisIcon, IrisNavMenu, useAdminPreferences, useAdminShell, useTabsNav };
2036
- //# sourceMappingURL=chunk-ZWFSGMOB.js.map
2037
- //# sourceMappingURL=chunk-ZWFSGMOB.js.map
2127
+ //# sourceMappingURL=chunk-ASFDGYIR.js.map
2128
+ //# sourceMappingURL=chunk-ASFDGYIR.js.map