@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.cjs CHANGED
@@ -626,10 +626,317 @@ function installNavMenuStyles() {
626
626
  document.head.appendChild(style);
627
627
  installed = true;
628
628
  }
629
+ var HOVER_OPEN_DELAY_VAL = 60;
630
+ var HOVER_CLOSE_DELAY_VAL = 150;
631
+ function createNavMenuFlyout(ctx) {
632
+ const hovered = vue.ref(null);
633
+ const hoveredBranches = vue.ref([]);
634
+ const focusedBranches = vue.ref([]);
635
+ const clickedBranches = vue.ref([]);
636
+ let suppressFocusOpen = false;
637
+ let openTimer = null;
638
+ const closeTimers = /* @__PURE__ */ new Map();
639
+ const clearOpenTimer = () => {
640
+ if (openTimer) {
641
+ clearTimeout(openTimer);
642
+ openTimer = null;
643
+ }
644
+ };
645
+ const cancelClose = (key) => {
646
+ const timer = closeTimers.get(key);
647
+ if (timer) {
648
+ clearTimeout(timer);
649
+ closeTimers.delete(key);
650
+ }
651
+ };
652
+ const cancelAllTimers = () => {
653
+ clearOpenTimer();
654
+ for (const timer of closeTimers.values()) clearTimeout(timer);
655
+ closeTimers.clear();
656
+ };
657
+ vue.onBeforeUnmount(cancelAllTimers);
658
+ const flyoutTriggers = /* @__PURE__ */ new Map();
659
+ const flyoutPanels = /* @__PURE__ */ new Map();
660
+ const setFlyoutRef = (map, key, el) => {
661
+ if (el instanceof HTMLElement) map.set(key, el);
662
+ else map.delete(key);
663
+ };
664
+ const positionFlyout = (key) => {
665
+ const trigger = flyoutTriggers.get(key);
666
+ const panel = flyoutPanels.get(key);
667
+ if (!trigger || !panel || typeof document === "undefined") return;
668
+ const rect = trigger.getBoundingClientRect();
669
+ const dir = getComputedStyle(trigger).direction;
670
+ panel.style.position = "fixed";
671
+ panel.style.top = `${ctx.collapsed ? rect.top : rect.bottom}px`;
672
+ if (dir === "rtl") {
673
+ panel.style.left = "";
674
+ panel.style.right = `${window.innerWidth - (ctx.collapsed ? rect.left : rect.right)}px`;
675
+ } else {
676
+ panel.style.right = "";
677
+ panel.style.left = `${ctx.collapsed ? rect.right : rect.left}px`;
678
+ }
679
+ };
680
+ vue.onBeforeUnmount(() => {
681
+ detachViewportListeners();
682
+ flyoutTriggers.clear();
683
+ flyoutPanels.clear();
684
+ });
685
+ const isolateAtDepth = (key) => {
686
+ const depth = core.findNavPath(ctx.items, key).length - 1;
687
+ const isSameDepthOther = (k) => k !== key && core.findNavPath(ctx.items, k).length - 1 === depth;
688
+ for (const state of [hoveredBranches, clickedBranches, focusedBranches]) {
689
+ if (state.value.some(isSameDepthOther)) {
690
+ state.value = state.value.filter((k) => !isSameDepthOther(k));
691
+ }
692
+ }
693
+ };
694
+ const scheduleOpen = (key) => {
695
+ clearOpenTimer();
696
+ openTimer = setTimeout(() => {
697
+ openTimer = null;
698
+ isolateAtDepth(key);
699
+ setBranchInteraction(hoveredBranches, key, true);
700
+ }, HOVER_OPEN_DELAY_VAL);
701
+ };
702
+ const scheduleClose = (key) => {
703
+ cancelClose(key);
704
+ closeTimers.set(
705
+ key,
706
+ setTimeout(() => {
707
+ closeTimers.delete(key);
708
+ setBranchInteraction(hoveredBranches, key, false);
709
+ }, HOVER_CLOSE_DELAY_VAL)
710
+ );
711
+ };
712
+ const closeHorizontalMenus = () => {
713
+ if (!ctx.flyoutMode.value) return;
714
+ cancelAllTimers();
715
+ hovered.value = null;
716
+ hoveredBranches.value = [];
717
+ focusedBranches.value = [];
718
+ clickedBranches.value = [];
719
+ };
720
+ const select = (node) => {
721
+ if (node.disabled) return;
722
+ closeHorizontalMenus();
723
+ ctx.emitSelect(node.key, node);
724
+ };
725
+ const toggleFlyout = (key) => {
726
+ cancelAllTimers();
727
+ const current = clickedBranches.value;
728
+ if (current.includes(key)) {
729
+ clickedBranches.value = current.filter((k) => k !== key);
730
+ setBranchInteraction(hoveredBranches, key, false);
731
+ setBranchInteraction(focusedBranches, key, false);
732
+ } else {
733
+ isolateAtDepth(key);
734
+ clickedBranches.value = [...clickedBranches.value, key];
735
+ }
736
+ };
737
+ const openFlyout = (key) => {
738
+ cancelAllTimers();
739
+ isolateAtDepth(key);
740
+ setBranchInteraction(focusedBranches, key, true);
741
+ };
742
+ const setBranchInteraction = (state, key, enabled) => {
743
+ const current = state.value;
744
+ if (enabled) {
745
+ if (!current.includes(key)) state.value = [...current, key];
746
+ } else if (current.includes(key)) {
747
+ state.value = current.filter((item) => item !== key);
748
+ }
749
+ };
750
+ const keepsPointerInside = (container, event) => {
751
+ if (!container || !(container instanceof HTMLElement)) return false;
752
+ const next = event.relatedTarget;
753
+ return !!(next && next instanceof Node && container.contains(next));
754
+ };
755
+ const interactionKeyAtDepth = (keys, depth) => keys.find((key) => core.findNavPath(ctx.items, key).length === depth + 1);
756
+ const horizontalBranchVisible = (key, depth) => {
757
+ const hoveredAtDepth = interactionKeyAtDepth(hoveredBranches.value, depth);
758
+ if (hoveredAtDepth) return hoveredAtDepth === key;
759
+ const clickedAtDepth = interactionKeyAtDepth(clickedBranches.value, depth);
760
+ if (clickedAtDepth) return clickedAtDepth === key;
761
+ return interactionKeyAtDepth(focusedBranches.value, depth) === key;
762
+ };
763
+ const shownFlyoutKeys = vue.computed(() => {
764
+ if (!ctx.flyoutMode.value) return [];
765
+ const keys = [];
766
+ for (const node of ctx.tree.value) {
767
+ if (core.isBranch(node) && horizontalBranchVisible(node.key, 0)) keys.push(node.key);
768
+ }
769
+ return keys;
770
+ });
771
+ const repositionOpenFlyouts = () => {
772
+ for (const key of shownFlyoutKeys.value) positionFlyout(key);
773
+ };
774
+ let viewportListenersActive = false;
775
+ const attachViewportListeners = () => {
776
+ if (viewportListenersActive || typeof document === "undefined") return;
777
+ viewportListenersActive = true;
778
+ document.addEventListener("scroll", repositionOpenFlyouts, true);
779
+ window.addEventListener("resize", repositionOpenFlyouts);
780
+ };
781
+ const detachViewportListeners = () => {
782
+ if (!viewportListenersActive || typeof document === "undefined") return;
783
+ viewportListenersActive = false;
784
+ document.removeEventListener("scroll", repositionOpenFlyouts, true);
785
+ window.removeEventListener("resize", repositionOpenFlyouts);
786
+ };
787
+ vue.watch(
788
+ shownFlyoutKeys,
789
+ (keys, prev) => {
790
+ if (keys.length > 0 && prev.length === 0) attachViewportListeners();
791
+ else if (keys.length === 0 && prev.length > 0) detachViewportListeners();
792
+ for (const key of keys) positionFlyout(key);
793
+ },
794
+ { flush: "post" }
795
+ );
796
+ return {
797
+ hovered,
798
+ hoveredBranches,
799
+ focusedBranches,
800
+ clickedBranches,
801
+ select,
802
+ toggleFlyout,
803
+ openFlyout,
804
+ closeHorizontalMenus,
805
+ setBranchInteraction,
806
+ keepsPointerInside,
807
+ interactionKeyAtDepth,
808
+ horizontalBranchVisible,
809
+ shownFlyoutKeys,
810
+ setFlyoutRef,
811
+ flyoutTriggers,
812
+ flyoutPanels,
813
+ suppressFocusOpen: { get: () => suppressFocusOpen },
814
+ setSuppressFocusOpen: (value) => {
815
+ suppressFocusOpen = value;
816
+ },
817
+ cancelClose,
818
+ scheduleOpen,
819
+ scheduleClose,
820
+ cancelAllTimers
821
+ };
822
+ }
823
+ function createNavMenuKeydownHandler(deps) {
824
+ const {
825
+ items,
826
+ flyoutMode,
827
+ expanded,
828
+ toggle,
829
+ openFlyout,
830
+ closeHorizontalMenus,
831
+ horizontalBranchVisible,
832
+ setBranchInteraction,
833
+ focusedBranches,
834
+ collapsed,
835
+ hoveredBranches,
836
+ clickedBranches,
837
+ setSuppressFocusOpen
838
+ } = deps;
839
+ return (e) => {
840
+ const root = e.currentTarget;
841
+ const buttons = Array.from(root.querySelectorAll("[data-iris-nav-item]")).filter(
842
+ (button) => !button.closest('[data-iris-nav-children][aria-hidden="true"]')
843
+ );
844
+ if (buttons.length === 0) return;
845
+ const idx = buttons.indexOf(document.activeElement);
846
+ const focusAt = (i) => buttons[(i + buttons.length) % buttons.length]?.focus();
847
+ const key = idx >= 0 ? buttons[idx]?.getAttribute("data-key") : void 0;
848
+ const node = key ? core.findNavNode(items, key) : void 0;
849
+ const depth = idx >= 0 ? Number(buttons[idx]?.getAttribute("data-depth") ?? 0) : 0;
850
+ switch (e.key) {
851
+ case "ArrowDown": {
852
+ e.preventDefault();
853
+ if (flyoutMode() && node && core.isBranch(node)) {
854
+ openFlyout(key);
855
+ const group = buttons[idx].closest("[data-iris-nav-group]");
856
+ void vue.nextTick(() => {
857
+ group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
858
+ });
859
+ return;
860
+ }
861
+ focusAt(idx < 0 ? 0 : idx + 1);
862
+ return;
863
+ }
864
+ case "ArrowUp": {
865
+ e.preventDefault();
866
+ if (flyoutMode() && node && core.isBranch(node) && horizontalBranchVisible(key, depth)) {
867
+ setBranchInteraction(focusedBranches, key, false);
868
+ setBranchInteraction(clickedBranches, key, false);
869
+ return;
870
+ }
871
+ focusAt(idx < 0 ? buttons.length - 1 : idx - 1);
872
+ return;
873
+ }
874
+ case "Home":
875
+ e.preventDefault();
876
+ buttons[0]?.focus();
877
+ return;
878
+ case "End":
879
+ e.preventDefault();
880
+ buttons[buttons.length - 1]?.focus();
881
+ return;
882
+ case "Escape": {
883
+ if (flyoutMode()) {
884
+ const openKeys = [
885
+ ...hoveredBranches.value,
886
+ ...clickedBranches.value,
887
+ ...focusedBranches.value
888
+ ];
889
+ if (openKeys.length > 0) {
890
+ e.preventDefault();
891
+ const openTop = openKeys.find((k) => core.findNavPath(items, k).length === 1);
892
+ closeHorizontalMenus();
893
+ if (openTop) {
894
+ setSuppressFocusOpen(true);
895
+ buttons.find((b) => b.getAttribute("data-key") === openTop)?.focus();
896
+ void vue.nextTick(() => {
897
+ setSuppressFocusOpen(false);
898
+ });
899
+ }
900
+ }
901
+ }
902
+ return;
903
+ }
904
+ }
905
+ if (collapsed || idx < 0 || !key || !node) return;
906
+ if (e.key === "ArrowRight") {
907
+ e.preventDefault();
908
+ if (core.isBranch(node)) {
909
+ if (flyoutMode() || !expanded().includes(key)) {
910
+ if (flyoutMode()) openFlyout(key);
911
+ else toggle(key);
912
+ const group = buttons[idx].closest("[data-iris-nav-group]");
913
+ void vue.nextTick(() => {
914
+ group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
915
+ });
916
+ } else {
917
+ focusAt(idx + 1);
918
+ }
919
+ }
920
+ } else if (e.key === "ArrowLeft") {
921
+ e.preventDefault();
922
+ if (flyoutMode() && core.isBranch(node)) {
923
+ setBranchInteraction(focusedBranches, key, false);
924
+ setBranchInteraction(clickedBranches, key, false);
925
+ return;
926
+ }
927
+ if (core.isBranch(node) && expanded().includes(key)) {
928
+ toggle(key);
929
+ } else {
930
+ const parentKey = core.findNavPath(items, key).at(-2)?.key;
931
+ if (parentKey) {
932
+ buttons.find((b) => b.getAttribute("data-key") === parentKey)?.focus();
933
+ }
934
+ }
935
+ }
936
+ };
937
+ }
629
938
 
630
939
  // src/admin/NavMenu.ts
631
- var HOVER_OPEN_DELAY = 100;
632
- var HOVER_CLOSE_DELAY = 150;
633
940
  var IrisNavMenu = vue.defineComponent({
634
941
  name: "IrisNavMenu",
635
942
  inheritAttrs: false,
@@ -691,170 +998,35 @@ var IrisNavMenu = vue.defineComponent({
691
998
  emit("update:expandedKeys", model.get());
692
999
  }
693
1000
  };
694
- const hovered = vue.ref(null);
695
- const hoveredBranches = vue.ref([]);
696
- const focusedBranches = vue.ref([]);
697
- const clickedBranches = vue.ref([]);
698
- let suppressFocusOpen = false;
699
- let openTimer = null;
700
- const closeTimers = /* @__PURE__ */ new Map();
701
- const clearOpenTimer = () => {
702
- if (openTimer) {
703
- clearTimeout(openTimer);
704
- openTimer = null;
705
- }
706
- };
707
- const cancelClose = (key) => {
708
- const timer = closeTimers.get(key);
709
- if (timer) {
710
- clearTimeout(timer);
711
- closeTimers.delete(key);
712
- }
713
- };
714
- const cancelAllTimers = () => {
715
- clearOpenTimer();
716
- for (const timer of closeTimers.values()) clearTimeout(timer);
717
- closeTimers.clear();
718
- };
719
- vue.onBeforeUnmount(cancelAllTimers);
720
- const flyoutTriggers = /* @__PURE__ */ new Map();
721
- const flyoutPanels = /* @__PURE__ */ new Map();
722
- const setFlyoutRef = (map, key, el) => {
723
- if (el instanceof HTMLElement) map.set(key, el);
724
- else map.delete(key);
725
- };
726
- const positionFlyout = (key) => {
727
- const trigger = flyoutTriggers.get(key);
728
- const panel = flyoutPanels.get(key);
729
- if (!trigger || !panel || typeof document === "undefined") return;
730
- const rect = trigger.getBoundingClientRect();
731
- const dir = getComputedStyle(trigger).direction;
732
- panel.style.position = "fixed";
733
- panel.style.top = `${props.collapsed ? rect.top : rect.bottom}px`;
734
- if (dir === "rtl") {
735
- panel.style.left = "";
736
- panel.style.right = `${window.innerWidth - (props.collapsed ? rect.left : rect.right)}px`;
737
- } else {
738
- panel.style.right = "";
739
- panel.style.left = `${props.collapsed ? rect.right : rect.left}px`;
740
- }
741
- };
742
- vue.onBeforeUnmount(() => {
743
- detachViewportListeners();
744
- flyoutTriggers.clear();
745
- flyoutPanels.clear();
746
- });
747
- const isolateAtDepth = (key) => {
748
- const depth = core.findNavPath(props.items, key).length - 1;
749
- const isSameDepthOther = (k) => k !== key && core.findNavPath(props.items, k).length - 1 === depth;
750
- for (const state of [hoveredBranches, clickedBranches, focusedBranches]) {
751
- if (state.value.some(isSameDepthOther)) {
752
- state.value = state.value.filter((k) => !isSameDepthOther(k));
753
- }
754
- }
755
- };
756
- const scheduleOpen = (key) => {
757
- clearOpenTimer();
758
- openTimer = setTimeout(() => {
759
- openTimer = null;
760
- isolateAtDepth(key);
761
- setBranchInteraction(hoveredBranches, key, true);
762
- }, HOVER_OPEN_DELAY);
763
- };
764
- const scheduleClose = (key) => {
765
- cancelClose(key);
766
- closeTimers.set(
767
- key,
768
- setTimeout(() => {
769
- closeTimers.delete(key);
770
- setBranchInteraction(hoveredBranches, key, false);
771
- }, HOVER_CLOSE_DELAY)
772
- );
773
- };
774
- const closeHorizontalMenus = () => {
775
- if (!flyoutMode.value) return;
776
- cancelAllTimers();
777
- hovered.value = null;
778
- hoveredBranches.value = [];
779
- focusedBranches.value = [];
780
- clickedBranches.value = [];
781
- };
782
- const select = (node) => {
783
- if (node.disabled) return;
784
- closeHorizontalMenus();
785
- emit("select", node.key, node);
786
- };
787
- const toggleFlyout = (key) => {
788
- cancelAllTimers();
789
- const current = clickedBranches.value;
790
- if (current.includes(key)) {
791
- clickedBranches.value = current.filter((k) => k !== key);
792
- setBranchInteraction(hoveredBranches, key, false);
793
- setBranchInteraction(focusedBranches, key, false);
794
- } else {
795
- isolateAtDepth(key);
796
- clickedBranches.value = [...clickedBranches.value, key];
797
- }
798
- };
799
- const openFlyout = (key) => {
800
- cancelAllTimers();
801
- isolateAtDepth(key);
802
- setBranchInteraction(focusedBranches, key, true);
803
- };
804
- const setBranchInteraction = (state, key, enabled) => {
805
- const current = state.value;
806
- if (enabled) {
807
- if (!current.includes(key)) state.value = [...current, key];
808
- } else if (current.includes(key)) {
809
- state.value = current.filter((item) => item !== key);
810
- }
811
- };
812
- const keepsPointerInside = (container, event) => {
813
- if (!container || !(container instanceof HTMLElement)) return false;
814
- const next = event.relatedTarget;
815
- return !!(next && next instanceof Node && container.contains(next));
816
- };
817
- const interactionKeyAtDepth = (keys, depth) => keys.find((key) => core.findNavPath(props.items, key).length === depth + 1);
818
- const horizontalBranchVisible = (key, depth) => {
819
- const hoveredAtDepth = interactionKeyAtDepth(hoveredBranches.value, depth);
820
- if (hoveredAtDepth) return hoveredAtDepth === key;
821
- const clickedAtDepth = interactionKeyAtDepth(clickedBranches.value, depth);
822
- if (clickedAtDepth) return clickedAtDepth === key;
823
- return interactionKeyAtDepth(focusedBranches.value, depth) === key;
824
- };
825
- const shownFlyoutKeys = vue.computed(() => {
826
- if (!flyoutMode.value) return [];
827
- const keys = [];
828
- for (const node of tree.value) {
829
- if (core.isBranch(node) && horizontalBranchVisible(node.key, 0)) keys.push(node.key);
830
- }
831
- return keys;
1001
+ const flyout = createNavMenuFlyout({
1002
+ items: props.items,
1003
+ tree,
1004
+ expanded,
1005
+ flyoutMode: { value: flyoutMode.value },
1006
+ collapsed: props.collapsed,
1007
+ toggle,
1008
+ emitSelect: (key, node) => emit("select", key, node)
832
1009
  });
833
- const repositionOpenFlyouts = () => {
834
- for (const key of shownFlyoutKeys.value) positionFlyout(key);
835
- };
836
- let viewportListenersActive = false;
837
- const attachViewportListeners = () => {
838
- if (viewportListenersActive || typeof document === "undefined") return;
839
- viewportListenersActive = true;
840
- document.addEventListener("scroll", repositionOpenFlyouts, true);
841
- window.addEventListener("resize", repositionOpenFlyouts);
842
- };
843
- const detachViewportListeners = () => {
844
- if (!viewportListenersActive || typeof document === "undefined") return;
845
- viewportListenersActive = false;
846
- document.removeEventListener("scroll", repositionOpenFlyouts, true);
847
- window.removeEventListener("resize", repositionOpenFlyouts);
848
- };
849
- vue.watch(
850
- shownFlyoutKeys,
851
- (keys, prev) => {
852
- if (keys.length > 0 && prev.length === 0) attachViewportListeners();
853
- else if (keys.length === 0 && prev.length > 0) detachViewportListeners();
854
- for (const key of keys) positionFlyout(key);
855
- },
856
- { flush: "post" }
857
- );
1010
+ const {
1011
+ hovered,
1012
+ hoveredBranches,
1013
+ focusedBranches,
1014
+ clickedBranches,
1015
+ select,
1016
+ toggleFlyout,
1017
+ openFlyout,
1018
+ closeHorizontalMenus,
1019
+ setBranchInteraction,
1020
+ keepsPointerInside,
1021
+ horizontalBranchVisible,
1022
+ setFlyoutRef,
1023
+ flyoutTriggers,
1024
+ flyoutPanels,
1025
+ suppressFocusOpen,
1026
+ cancelClose,
1027
+ scheduleOpen,
1028
+ scheduleClose
1029
+ } = flyout;
858
1030
  const itemClass = (opts) => {
859
1031
  const depth = Math.min(9, opts.depth);
860
1032
  return [
@@ -990,7 +1162,7 @@ var IrisNavMenu = vue.defineComponent({
990
1162
  }
991
1163
  },
992
1164
  onFocusin: () => {
993
- if (suppressFocusOpen) return;
1165
+ if (suppressFocusOpen.get()) return;
994
1166
  setBranchInteraction(focusedBranches, node.key, true);
995
1167
  },
996
1168
  onFocusout: (event) => {
@@ -1043,104 +1215,23 @@ var IrisNavMenu = vue.defineComponent({
1043
1215
  },
1044
1216
  { flush: "post" }
1045
1217
  );
1046
- const onKeydown = (e) => {
1047
- const root = e.currentTarget;
1048
- const buttons = Array.from(root.querySelectorAll("[data-iris-nav-item]")).filter(
1049
- (button) => !button.closest('[data-iris-nav-children][aria-hidden="true"]')
1050
- );
1051
- if (buttons.length === 0) return;
1052
- const idx = buttons.indexOf(document.activeElement);
1053
- const focusAt = (i) => buttons[(i + buttons.length) % buttons.length]?.focus();
1054
- const key = idx >= 0 ? buttons[idx]?.getAttribute("data-key") : void 0;
1055
- const node = key ? core.findNavNode(props.items, key) : void 0;
1056
- const depth = idx >= 0 ? Number(buttons[idx]?.getAttribute("data-depth") ?? 0) : 0;
1057
- switch (e.key) {
1058
- case "ArrowDown": {
1059
- e.preventDefault();
1060
- if (flyoutMode.value && node && core.isBranch(node)) {
1061
- openFlyout(key);
1062
- const group = buttons[idx].closest("[data-iris-nav-group]");
1063
- void vue.nextTick(() => {
1064
- group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
1065
- });
1066
- return;
1067
- }
1068
- focusAt(idx < 0 ? 0 : idx + 1);
1069
- return;
1070
- }
1071
- case "ArrowUp": {
1072
- e.preventDefault();
1073
- if (flyoutMode.value && node && core.isBranch(node) && horizontalBranchVisible(key, depth)) {
1074
- setBranchInteraction(focusedBranches, key, false);
1075
- setBranchInteraction(clickedBranches, key, false);
1076
- return;
1077
- }
1078
- focusAt(idx < 0 ? buttons.length - 1 : idx - 1);
1079
- return;
1080
- }
1081
- case "Home":
1082
- e.preventDefault();
1083
- buttons[0]?.focus();
1084
- return;
1085
- case "End":
1086
- e.preventDefault();
1087
- buttons[buttons.length - 1]?.focus();
1088
- return;
1089
- case "Escape": {
1090
- if (flyoutMode.value) {
1091
- const openKeys = [
1092
- ...hoveredBranches.value,
1093
- ...clickedBranches.value,
1094
- ...focusedBranches.value
1095
- ];
1096
- if (openKeys.length > 0) {
1097
- e.preventDefault();
1098
- const openTop = openKeys.find((k) => core.findNavPath(props.items, k).length === 1);
1099
- closeHorizontalMenus();
1100
- if (openTop) {
1101
- suppressFocusOpen = true;
1102
- buttons.find((b) => b.getAttribute("data-key") === openTop)?.focus();
1103
- void vue.nextTick(() => {
1104
- suppressFocusOpen = false;
1105
- });
1106
- }
1107
- }
1108
- }
1109
- return;
1110
- }
1111
- }
1112
- if (props.collapsed || idx < 0 || !key || !node) return;
1113
- if (e.key === "ArrowRight") {
1114
- e.preventDefault();
1115
- if (core.isBranch(node)) {
1116
- if (flyoutMode.value || !expanded.value.includes(key)) {
1117
- if (flyoutMode.value) openFlyout(key);
1118
- else toggle(key);
1119
- const group = buttons[idx].closest("[data-iris-nav-group]");
1120
- void vue.nextTick(() => {
1121
- group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
1122
- });
1123
- } else {
1124
- focusAt(idx + 1);
1125
- }
1126
- }
1127
- } else if (e.key === "ArrowLeft") {
1128
- e.preventDefault();
1129
- if (flyoutMode.value && core.isBranch(node)) {
1130
- setBranchInteraction(focusedBranches, key, false);
1131
- setBranchInteraction(clickedBranches, key, false);
1132
- return;
1133
- }
1134
- if (core.isBranch(node) && expanded.value.includes(key)) {
1135
- toggle(key);
1136
- } else {
1137
- const parentKey = core.findNavPath(props.items, key).at(-2)?.key;
1138
- if (parentKey) {
1139
- buttons.find((b) => b.getAttribute("data-key") === parentKey)?.focus();
1140
- }
1141
- }
1142
- }
1143
- };
1218
+ const onKeydown = createNavMenuKeydownHandler({
1219
+ items: props.items,
1220
+ flyoutMode: () => flyoutMode.value,
1221
+ expanded: () => expanded.value,
1222
+ toggle,
1223
+ openFlyout,
1224
+ closeHorizontalMenus,
1225
+ horizontalBranchVisible,
1226
+ setBranchInteraction,
1227
+ focusedBranches,
1228
+ hoveredBranches,
1229
+ clickedBranches,
1230
+ setSuppressFocusOpen: (v) => {
1231
+ flyout.setSuppressFocusOpen(v);
1232
+ },
1233
+ collapsed: props.collapsed
1234
+ });
1144
1235
  const menuClass = [
1145
1236
  "iris-nav-menu",
1146
1237
  `iris-nav-menu--${props.orientation}`,