@iris-ui-kit/vue 0.2.25 → 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 +547 -236
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +4 -2
- package/dist/admin.d.ts +4 -2
- package/dist/admin.js +2 -2
- package/dist/behaviors.cjs +49 -0
- package/dist/behaviors.cjs.map +1 -1
- package/dist/behaviors.d.cts +57 -1
- package/dist/behaviors.d.ts +57 -1
- package/dist/behaviors.js +1 -1
- package/dist/{chunk-QQ25RKOO.js → chunk-ASFDGYIR.js} +543 -240
- package/dist/chunk-ASFDGYIR.js.map +1 -0
- package/dist/{chunk-WL67XCH5.js → chunk-IN3DQ55U.js} +2 -2
- package/dist/chunk-IN3DQ55U.js.map +1 -0
- package/dist/{chunk-7L6UB6MD.js → chunk-IOMKC5OI.js} +53 -5
- package/dist/chunk-IOMKC5OI.js.map +1 -0
- package/dist/{chunk-3FJUCHCC.js → chunk-IPR5JAVF.js} +11 -3
- package/dist/chunk-IPR5JAVF.js.map +1 -0
- package/dist/{chunk-BEPH6PPL.js → chunk-LWABTQCB.js} +57 -37
- package/dist/chunk-LWABTQCB.js.map +1 -0
- package/dist/{chunk-DNRQVM3Q.js → chunk-QBFYIDY7.js} +3 -2
- package/dist/chunk-QBFYIDY7.js.map +1 -0
- package/dist/data.cjs +1 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.cts +4 -2
- package/dist/data.d.ts +4 -2
- package/dist/data.js +1 -1
- package/dist/floating.cjs +9 -1
- package/dist/floating.cjs.map +1 -1
- package/dist/floating.d.cts +6 -0
- package/dist/floating.d.ts +6 -0
- package/dist/floating.js +1 -1
- package/dist/index.cjs +2063 -745
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +172 -8
- package/dist/index.d.ts +172 -8
- package/dist/index.js +1412 -475
- package/dist/index.js.map +1 -1
- package/dist/layouts.cjs.map +1 -1
- package/dist/layouts.d.cts +1 -1
- package/dist/layouts.d.ts +1 -1
- package/dist/layouts.js +1 -1
- package/dist/skeletons.cjs +55 -35
- package/dist/skeletons.cjs.map +1 -1
- package/dist/skeletons.js +1 -1
- package/package.json +5 -5
- package/dist/chunk-3FJUCHCC.js.map +0 -1
- package/dist/chunk-7L6UB6MD.js.map +0 -1
- package/dist/chunk-BEPH6PPL.js.map +0 -1
- package/dist/chunk-DNRQVM3Q.js.map +0 -1
- package/dist/chunk-QQ25RKOO.js.map +0 -1
- package/dist/chunk-WL67XCH5.js.map +0 -1
package/dist/admin.cjs
CHANGED
|
@@ -351,7 +351,7 @@ var __NAV_MENU_STYLE_ID = "iris-nav-menu-styles";
|
|
|
351
351
|
var CSS = `
|
|
352
352
|
:where(.iris-nav-menu) {
|
|
353
353
|
--iris-nav-indent-step: 16px;
|
|
354
|
-
--iris-nav-item-padding-inline:
|
|
354
|
+
--iris-nav-item-padding-inline: 12px;
|
|
355
355
|
--iris-nav-item-padding-block: 8px;
|
|
356
356
|
--iris-nav-item-border-radius: var(--iris-radius-md, 6px);
|
|
357
357
|
--iris-nav-item-hover: var(--iris-surface-hover, var(--iris-surface));
|
|
@@ -359,7 +359,7 @@ var CSS = `
|
|
|
359
359
|
display: flex;
|
|
360
360
|
flex-direction: column;
|
|
361
361
|
align-items: stretch;
|
|
362
|
-
gap:
|
|
362
|
+
gap: var(--iris-space-xxs, 4px);
|
|
363
363
|
width: 100%;
|
|
364
364
|
}
|
|
365
365
|
|
|
@@ -384,16 +384,16 @@ var CSS = `
|
|
|
384
384
|
border: none;
|
|
385
385
|
border-radius: var(--iris-nav-item-border-radius);
|
|
386
386
|
background: transparent;
|
|
387
|
-
color: var(--iris-foreground
|
|
387
|
+
color: var(--iris-foreground);
|
|
388
388
|
font: inherit;
|
|
389
|
-
font-size: 14px;
|
|
389
|
+
font-size: var(--iris-font-size-md, 14px);
|
|
390
390
|
line-height: 1.2;
|
|
391
391
|
font-weight: 400;
|
|
392
392
|
text-align: start;
|
|
393
393
|
cursor: pointer;
|
|
394
394
|
opacity: 1;
|
|
395
395
|
margin: 0;
|
|
396
|
-
gap:
|
|
396
|
+
gap: var(--iris-space-sm, 12px);
|
|
397
397
|
padding-block: var(--iris-nav-item-padding-block);
|
|
398
398
|
padding-inline: var(--iris-nav-item-padding-inline);
|
|
399
399
|
text-overflow: ellipsis;
|
|
@@ -406,7 +406,7 @@ var CSS = `
|
|
|
406
406
|
}
|
|
407
407
|
|
|
408
408
|
:where(.iris-nav-menu-item[data-depth='0']) {
|
|
409
|
-
--iris-nav-item-padding-inline-start:
|
|
409
|
+
--iris-nav-item-padding-inline-start: 12px;
|
|
410
410
|
}
|
|
411
411
|
|
|
412
412
|
:where(.iris-nav-menu-item[data-depth='1']) {
|
|
@@ -454,7 +454,7 @@ var CSS = `
|
|
|
454
454
|
background: var(--iris-nav-item-hover);
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
-
|
|
457
|
+
.iris-nav-menu-item[data-active='true'] {
|
|
458
458
|
background: var(--iris-primary);
|
|
459
459
|
color: var(--iris-primary-foreground, #fff);
|
|
460
460
|
font-weight: 600;
|
|
@@ -483,13 +483,13 @@ var CSS = `
|
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
:where(.iris-nav-menu-badge) {
|
|
486
|
-
margin-inline-start:
|
|
487
|
-
font-size:
|
|
486
|
+
margin-inline-start: var(--iris-space-xs, 8px);
|
|
487
|
+
font-size: var(--iris-font-size-xs, 12px);
|
|
488
488
|
line-height: 1;
|
|
489
|
-
padding:
|
|
489
|
+
padding: var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px);
|
|
490
490
|
border-radius: 999px;
|
|
491
|
-
background: var(--iris-danger, #
|
|
492
|
-
color: #fff;
|
|
491
|
+
background: var(--iris-danger, #ef4444);
|
|
492
|
+
color: var(--iris-primary-foreground, #fff);
|
|
493
493
|
flex: 0 0 auto;
|
|
494
494
|
}
|
|
495
495
|
|
|
@@ -519,44 +519,68 @@ var CSS = `
|
|
|
519
519
|
opacity: 0.75;
|
|
520
520
|
}
|
|
521
521
|
|
|
522
|
-
|
|
523
|
-
|
|
522
|
+
/* Vertical branches stay in the DOM and animate height via the grid-rows
|
|
523
|
+
trick (0fr \u2192 1fr); the inner wrapper provides the overflow clipping so the
|
|
524
|
+
transition is a smooth accordion without a max-height cap. */
|
|
525
|
+
:where(.iris-nav-menu--vertical .iris-nav-menu-group > .iris-nav-menu-children) {
|
|
526
|
+
display: grid;
|
|
527
|
+
grid-template-rows: 0fr;
|
|
528
|
+
opacity: 0;
|
|
529
|
+
visibility: hidden;
|
|
530
|
+
transition:
|
|
531
|
+
grid-template-rows 200ms ease,
|
|
532
|
+
opacity 150ms ease,
|
|
533
|
+
visibility 0s linear 200ms;
|
|
524
534
|
}
|
|
525
535
|
|
|
526
|
-
:where(.iris-nav-menu-group
|
|
527
|
-
:
|
|
528
|
-
:
|
|
529
|
-
:where(.iris-nav-menu-group--depth-4 > .iris-nav-menu-children),
|
|
530
|
-
:where(.iris-nav-menu-group--depth-5 > .iris-nav-menu-children),
|
|
531
|
-
:where(.iris-nav-menu-group--depth-6 > .iris-nav-menu-children),
|
|
532
|
-
:where(.iris-nav-menu-group--depth-7 > .iris-nav-menu-children),
|
|
533
|
-
:where(.iris-nav-menu-group--depth-8 > .iris-nav-menu-children),
|
|
534
|
-
:where(.iris-nav-menu-group--depth-9 > .iris-nav-menu-children),
|
|
535
|
-
:where(.iris-nav-menu-group > .iris-nav-menu-children) {
|
|
536
|
-
display: none;
|
|
536
|
+
:where(.iris-nav-menu--vertical .iris-nav-menu-group > .iris-nav-menu-children > .iris-nav-menu-children-inner) {
|
|
537
|
+
overflow: hidden;
|
|
538
|
+
min-height: 0;
|
|
537
539
|
}
|
|
538
540
|
|
|
539
|
-
:where(.iris-nav-menu-group[data-open='true'] > .iris-nav-menu-children) {
|
|
540
|
-
|
|
541
|
+
:where(.iris-nav-menu--vertical .iris-nav-menu-group[data-open='true'] > .iris-nav-menu-children) {
|
|
542
|
+
grid-template-rows: 1fr;
|
|
543
|
+
opacity: 1;
|
|
544
|
+
visibility: visible;
|
|
545
|
+
transition:
|
|
546
|
+
grid-template-rows 200ms ease,
|
|
547
|
+
opacity 150ms ease;
|
|
541
548
|
}
|
|
542
549
|
|
|
543
|
-
|
|
550
|
+
/* Flyout popups (horizontal mode + the collapsed rail) share the popup chrome
|
|
551
|
+
and are hard-hidden when closed (no height animation needed). */
|
|
552
|
+
:where(.iris-nav-menu--horizontal .iris-nav-menu-group > .iris-nav-menu-children),
|
|
553
|
+
:where(.iris-nav-menu--collapsed .iris-nav-menu-group > .iris-nav-menu-children) {
|
|
554
|
+
display: none;
|
|
544
555
|
position: absolute;
|
|
545
556
|
min-width: 220px;
|
|
546
|
-
padding:
|
|
557
|
+
padding: var(--iris-space-xs, 8px);
|
|
547
558
|
border: 1px solid var(--iris-border);
|
|
548
559
|
border-radius: var(--iris-radius-md, 6px);
|
|
549
560
|
background: var(--iris-surface);
|
|
550
561
|
box-shadow: var(--iris-shadow-md);
|
|
551
562
|
}
|
|
552
563
|
|
|
564
|
+
:where(.iris-nav-menu--horizontal .iris-nav-menu-group[data-open='true'] > .iris-nav-menu-children),
|
|
565
|
+
:where(.iris-nav-menu--collapsed .iris-nav-menu-group[data-open='true'] > .iris-nav-menu-children) {
|
|
566
|
+
display: block;
|
|
567
|
+
}
|
|
568
|
+
|
|
553
569
|
:where(.iris-nav-menu--horizontal .iris-nav-menu-group[data-depth='0'] > .iris-nav-menu-children) {
|
|
554
570
|
inset-block-start: 100%;
|
|
555
571
|
inset-inline-start: 0;
|
|
556
572
|
z-index: 60;
|
|
557
573
|
}
|
|
558
574
|
|
|
559
|
-
|
|
575
|
+
/* A9: the collapsed rail popup opens to the right of the icon. */
|
|
576
|
+
:where(.iris-nav-menu--collapsed .iris-nav-menu-group[data-depth='0'] > .iris-nav-menu-children) {
|
|
577
|
+
inset-block-start: 0;
|
|
578
|
+
inset-inline-start: 100%;
|
|
579
|
+
z-index: 60;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
:where(.iris-nav-menu--horizontal .iris-nav-menu-group[data-depth]:not([data-depth='0']) > .iris-nav-menu-children),
|
|
583
|
+
:where(.iris-nav-menu--collapsed .iris-nav-menu-group[data-depth]:not([data-depth='0']) > .iris-nav-menu-children) {
|
|
560
584
|
inset-block-start: 0;
|
|
561
585
|
inset-inline-start: 100%;
|
|
562
586
|
z-index: 61;
|
|
@@ -572,10 +596,20 @@ var CSS = `
|
|
|
572
596
|
padding-inline: var(--iris-nav-item-padding-inline);
|
|
573
597
|
}
|
|
574
598
|
|
|
599
|
+
/* A9: the collapsed rail stays centered even though rows carry data-depth
|
|
600
|
+
(the vertical indent rules would otherwise win the specificity tie). */
|
|
601
|
+
:where(.iris-nav-menu--collapsed .iris-nav-menu-item[data-depth]) {
|
|
602
|
+
justify-content: center;
|
|
603
|
+
padding-inline: var(--iris-nav-item-padding-inline);
|
|
604
|
+
}
|
|
605
|
+
|
|
575
606
|
@media (prefers-reduced-motion: reduce) {
|
|
576
607
|
:where(.iris-nav-menu-arrow) {
|
|
577
608
|
transition: none;
|
|
578
609
|
}
|
|
610
|
+
:where(.iris-nav-menu--vertical .iris-nav-menu-group > .iris-nav-menu-children) {
|
|
611
|
+
transition: none;
|
|
612
|
+
}
|
|
579
613
|
}
|
|
580
614
|
`.trim();
|
|
581
615
|
var installed = false;
|
|
@@ -592,6 +626,315 @@ function installNavMenuStyles() {
|
|
|
592
626
|
document.head.appendChild(style);
|
|
593
627
|
installed = true;
|
|
594
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
|
+
}
|
|
595
938
|
|
|
596
939
|
// src/admin/NavMenu.ts
|
|
597
940
|
var IrisNavMenu = vue.defineComponent({
|
|
@@ -625,6 +968,7 @@ var IrisNavMenu = vue.defineComponent({
|
|
|
625
968
|
const activePath = vue.computed(
|
|
626
969
|
() => props.activeKey ? core.findNavPath(props.items, props.activeKey).map((n) => n.key) : []
|
|
627
970
|
);
|
|
971
|
+
const flyoutMode = vue.computed(() => props.orientation === "horizontal" || props.collapsed);
|
|
628
972
|
const isControlled = vue.computed(() => props.expandedKeys !== void 0);
|
|
629
973
|
const model = core.createExpansion({
|
|
630
974
|
mode: "multiple",
|
|
@@ -639,12 +983,9 @@ var IrisNavMenu = vue.defineComponent({
|
|
|
639
983
|
const expanded = vue.computed(
|
|
640
984
|
() => isControlled.value ? props.expandedKeys : internalExpanded.value
|
|
641
985
|
);
|
|
642
|
-
vue.watch(
|
|
643
|
-
()
|
|
644
|
-
|
|
645
|
-
if (!isControlled.value) model.merge(key ? core.branchTrail(props.items, key) : []);
|
|
646
|
-
}
|
|
647
|
-
);
|
|
986
|
+
vue.watch([() => props.activeKey, () => props.items], ([key]) => {
|
|
987
|
+
if (!isControlled.value) model.merge(key ? core.branchTrail(props.items, key) : []);
|
|
988
|
+
});
|
|
648
989
|
const toggle = (key) => {
|
|
649
990
|
if (isControlled.value) {
|
|
650
991
|
const cur = props.expandedKeys;
|
|
@@ -657,39 +998,35 @@ var IrisNavMenu = vue.defineComponent({
|
|
|
657
998
|
emit("update:expandedKeys", model.get());
|
|
658
999
|
}
|
|
659
1000
|
};
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
};
|
|
669
|
-
const
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
const hoveredAtDepth = interactionKeyAtDepth(hoveredBranches.value, depth);
|
|
690
|
-
if (hoveredAtDepth) return hoveredAtDepth === key;
|
|
691
|
-
return interactionKeyAtDepth(focusedBranches.value, depth) === key;
|
|
692
|
-
};
|
|
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)
|
|
1009
|
+
});
|
|
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;
|
|
693
1030
|
const itemClass = (opts) => {
|
|
694
1031
|
const depth = Math.min(9, opts.depth);
|
|
695
1032
|
return [
|
|
@@ -712,81 +1049,47 @@ var IrisNavMenu = vue.defineComponent({
|
|
|
712
1049
|
},
|
|
713
1050
|
String(node.badge)
|
|
714
1051
|
) : null;
|
|
715
|
-
const
|
|
1052
|
+
const renderItem = (node, depth) => {
|
|
716
1053
|
const branch = core.isBranch(node);
|
|
717
|
-
const
|
|
718
|
-
|
|
1054
|
+
const horizontal = flyoutMode.value;
|
|
1055
|
+
const open = expanded.value.includes(node.key);
|
|
1056
|
+
const shown = horizontal ? horizontalBranchVisible(node.key, depth) : open;
|
|
1057
|
+
const active = node.key === props.activeKey;
|
|
1058
|
+
const trail = branch && !active && activePath.value.includes(node.key);
|
|
1059
|
+
const rowHovered = hovered.value === node.key || horizontal && shown;
|
|
1060
|
+
const arrowReversed = branch && !node.disabled && (active || trail || shown || rowHovered);
|
|
1061
|
+
const isFlyoutRoot = horizontal && branch && depth === 0;
|
|
1062
|
+
const row = vue.h(
|
|
719
1063
|
"button",
|
|
720
1064
|
{
|
|
721
|
-
key: node.key,
|
|
722
1065
|
type: "button",
|
|
1066
|
+
ref: (el) => {
|
|
1067
|
+
if (isFlyoutRoot) setFlyoutRef(flyoutTriggers, node.key, el);
|
|
1068
|
+
},
|
|
723
1069
|
class: [
|
|
724
1070
|
...itemClass({
|
|
725
|
-
depth
|
|
1071
|
+
depth,
|
|
726
1072
|
active,
|
|
727
|
-
trail
|
|
728
|
-
hovered:
|
|
729
|
-
open: false,
|
|
1073
|
+
trail,
|
|
1074
|
+
hovered: rowHovered,
|
|
1075
|
+
open: branch ? shown : false,
|
|
730
1076
|
disabled: !!node.disabled,
|
|
731
1077
|
branch
|
|
732
1078
|
}),
|
|
733
|
-
"is-collapsed"
|
|
1079
|
+
...props.collapsed && depth === 0 ? ["is-collapsed"] : []
|
|
734
1080
|
],
|
|
735
1081
|
"data-iris-nav-item": "",
|
|
736
1082
|
"data-key": node.key,
|
|
737
|
-
"data-active": active ? "true" : void 0,
|
|
738
|
-
"data-branch": branch ? "true" : void 0,
|
|
739
|
-
"data-hovered": hovered.value === node.key ? "true" : void 0,
|
|
740
|
-
disabled: node.disabled,
|
|
741
|
-
title: node.title,
|
|
742
|
-
"aria-label": branch ? `${node.title} (section)` : node.title,
|
|
743
|
-
"aria-current": active && !branch ? "page" : void 0,
|
|
744
|
-
onMouseenter: () => {
|
|
745
|
-
if (!node.disabled) hovered.value = node.key;
|
|
746
|
-
},
|
|
747
|
-
onMouseleave: (event) => {
|
|
748
|
-
if (hovered.value === node.key && !keepsPointerInside(event.currentTarget, event)) {
|
|
749
|
-
hovered.value = null;
|
|
750
|
-
}
|
|
751
|
-
},
|
|
752
|
-
onClick: () => select(branch ? core.firstLeaf(node) : node)
|
|
753
|
-
},
|
|
754
|
-
[
|
|
755
|
-
iconNode(node, 20) ?? vue.h("span", { class: "iris-nav-menu-fallback-icon" }, node.title.charAt(0))
|
|
756
|
-
]
|
|
757
|
-
);
|
|
758
|
-
});
|
|
759
|
-
const renderItem = (node, depth) => {
|
|
760
|
-
const branch = core.isBranch(node);
|
|
761
|
-
const open = expanded.value.includes(node.key);
|
|
762
|
-
const horizontal = props.orientation === "horizontal";
|
|
763
|
-
const shown = horizontal ? horizontalBranchVisible(node.key, depth) : open;
|
|
764
|
-
const active = node.key === props.activeKey;
|
|
765
|
-
const trail = branch && !active && activePath.value.includes(node.key);
|
|
766
|
-
const arrowReversed = branch && !node.disabled && (active || trail || shown || hovered.value === node.key);
|
|
767
|
-
const row = vue.h(
|
|
768
|
-
"button",
|
|
769
|
-
{
|
|
770
|
-
type: "button",
|
|
771
|
-
class: itemClass({
|
|
772
|
-
depth,
|
|
773
|
-
active,
|
|
774
|
-
trail,
|
|
775
|
-
hovered: hovered.value === node.key,
|
|
776
|
-
open: branch ? shown : false,
|
|
777
|
-
disabled: !!node.disabled,
|
|
778
|
-
branch
|
|
779
|
-
}),
|
|
780
|
-
"data-iris-nav-item": "",
|
|
781
|
-
"data-key": node.key,
|
|
782
1083
|
"data-branch": branch ? "true" : void 0,
|
|
783
1084
|
"data-active": active ? "true" : void 0,
|
|
784
1085
|
"data-active-trail": trail ? "true" : void 0,
|
|
785
1086
|
"data-open": branch && shown ? "true" : void 0,
|
|
786
|
-
"data-hovered":
|
|
1087
|
+
"data-hovered": rowHovered ? "true" : void 0,
|
|
787
1088
|
"data-depth": String(depth),
|
|
788
1089
|
"data-orientation": props.orientation,
|
|
789
1090
|
disabled: node.disabled,
|
|
1091
|
+
title: props.collapsed ? node.title : void 0,
|
|
1092
|
+
"aria-label": props.collapsed ? branch ? `${node.title} (section)` : node.title : void 0,
|
|
790
1093
|
"aria-expanded": branch ? shown ? "true" : "false" : void 0,
|
|
791
1094
|
"aria-current": active ? "page" : void 0,
|
|
792
1095
|
onMouseenter: () => {
|
|
@@ -797,7 +1100,17 @@ var IrisNavMenu = vue.defineComponent({
|
|
|
797
1100
|
hovered.value = null;
|
|
798
1101
|
}
|
|
799
1102
|
},
|
|
800
|
-
onClick: () =>
|
|
1103
|
+
onClick: () => {
|
|
1104
|
+
if (props.collapsed && branch) {
|
|
1105
|
+
select(core.firstLeaf(node));
|
|
1106
|
+
} else if (horizontal && branch) {
|
|
1107
|
+
toggleFlyout(node.key);
|
|
1108
|
+
} else if (branch) {
|
|
1109
|
+
toggle(node.key);
|
|
1110
|
+
} else {
|
|
1111
|
+
select(node);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
801
1114
|
},
|
|
802
1115
|
[
|
|
803
1116
|
iconNode(node),
|
|
@@ -809,54 +1122,49 @@ var IrisNavMenu = vue.defineComponent({
|
|
|
809
1122
|
node.title
|
|
810
1123
|
),
|
|
811
1124
|
badgeNode(node),
|
|
812
|
-
branch ? vue.h(IrisIcon, {
|
|
813
|
-
name: horizontal && depth === 0 ? "chevron-down" : "chevron-right",
|
|
1125
|
+
branch && !(props.collapsed && depth === 0) ? vue.h(IrisIcon, {
|
|
1126
|
+
name: horizontal && depth === 0 && !props.collapsed ? "chevron-down" : "chevron-right",
|
|
814
1127
|
size: 16,
|
|
815
1128
|
class: "iris-nav-menu-arrow",
|
|
816
1129
|
"data-reversed": arrowReversed ? "true" : void 0
|
|
817
1130
|
}) : null
|
|
818
1131
|
]
|
|
819
1132
|
);
|
|
820
|
-
if (!branch || !horizontal && !open)
|
|
821
|
-
return vue.h(
|
|
822
|
-
"div",
|
|
823
|
-
{
|
|
824
|
-
key: node.key,
|
|
825
|
-
"data-iris-nav-group": branch ? "" : void 0,
|
|
826
|
-
class: ["iris-nav-menu-group", `iris-nav-menu-group--depth-${Math.min(9, depth)}`]
|
|
827
|
-
},
|
|
828
|
-
[row]
|
|
829
|
-
);
|
|
830
1133
|
return vue.h(
|
|
831
1134
|
"div",
|
|
832
1135
|
{
|
|
833
1136
|
key: node.key,
|
|
834
|
-
"data-iris-nav-group": "",
|
|
835
|
-
"data-open": shown ? "true" : void 0,
|
|
1137
|
+
"data-iris-nav-group": branch ? "" : void 0,
|
|
1138
|
+
"data-open": branch && shown ? "true" : void 0,
|
|
836
1139
|
"data-depth": String(depth),
|
|
837
1140
|
class: ["iris-nav-menu-group", `iris-nav-menu-group--depth-${Math.min(9, depth)}`],
|
|
838
|
-
...horizontal ? {
|
|
1141
|
+
...horizontal && branch ? {
|
|
839
1142
|
onMouseenter: (event) => {
|
|
840
|
-
if (!keepsPointerInside(event.currentTarget, event)) {
|
|
841
|
-
|
|
1143
|
+
if (!node.disabled && !keepsPointerInside(event.currentTarget, event)) {
|
|
1144
|
+
cancelClose(node.key);
|
|
1145
|
+
scheduleOpen(node.key);
|
|
842
1146
|
}
|
|
843
1147
|
},
|
|
844
1148
|
onMouseover: (event) => {
|
|
845
|
-
if (!keepsPointerInside(event.currentTarget, event)) {
|
|
846
|
-
|
|
1149
|
+
if (!node.disabled && !keepsPointerInside(event.currentTarget, event)) {
|
|
1150
|
+
cancelClose(node.key);
|
|
1151
|
+
scheduleOpen(node.key);
|
|
847
1152
|
}
|
|
848
1153
|
},
|
|
849
1154
|
onMouseout: (event) => {
|
|
850
|
-
if (!keepsPointerInside(event.currentTarget, event)) {
|
|
851
|
-
|
|
1155
|
+
if (!node.disabled && !keepsPointerInside(event.currentTarget, event)) {
|
|
1156
|
+
scheduleClose(node.key);
|
|
852
1157
|
}
|
|
853
1158
|
},
|
|
854
1159
|
onMouseleave: (event) => {
|
|
855
|
-
if (!keepsPointerInside(event.currentTarget, event)) {
|
|
856
|
-
|
|
1160
|
+
if (!node.disabled && !keepsPointerInside(event.currentTarget, event)) {
|
|
1161
|
+
scheduleClose(node.key);
|
|
857
1162
|
}
|
|
858
1163
|
},
|
|
859
|
-
onFocusin: () =>
|
|
1164
|
+
onFocusin: () => {
|
|
1165
|
+
if (suppressFocusOpen.get()) return;
|
|
1166
|
+
setBranchInteraction(focusedBranches, node.key, true);
|
|
1167
|
+
},
|
|
860
1168
|
onFocusout: (event) => {
|
|
861
1169
|
const group = event.currentTarget;
|
|
862
1170
|
const next = event.relatedTarget;
|
|
@@ -868,66 +1176,62 @@ var IrisNavMenu = vue.defineComponent({
|
|
|
868
1176
|
},
|
|
869
1177
|
[
|
|
870
1178
|
row,
|
|
871
|
-
vue.h(
|
|
1179
|
+
branch ? vue.h(
|
|
872
1180
|
"div",
|
|
873
1181
|
{
|
|
874
1182
|
class: "iris-nav-menu-children",
|
|
875
1183
|
"data-iris-nav-children": "",
|
|
1184
|
+
ref: (el) => {
|
|
1185
|
+
if (isFlyoutRoot) setFlyoutRef(flyoutPanels, node.key, el);
|
|
1186
|
+
},
|
|
876
1187
|
role: "group",
|
|
877
|
-
"aria-hidden":
|
|
1188
|
+
"aria-hidden": shown ? void 0 : "true"
|
|
878
1189
|
},
|
|
879
|
-
|
|
880
|
-
|
|
1190
|
+
[
|
|
1191
|
+
vue.h(
|
|
1192
|
+
"div",
|
|
1193
|
+
{ class: "iris-nav-menu-children-inner" },
|
|
1194
|
+
(node.children ?? []).map((child) => renderItem(child, depth + 1))
|
|
1195
|
+
)
|
|
1196
|
+
]
|
|
1197
|
+
) : null
|
|
881
1198
|
]
|
|
882
1199
|
);
|
|
883
1200
|
};
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
return;
|
|
897
|
-
case "ArrowUp":
|
|
898
|
-
e.preventDefault();
|
|
899
|
-
focusAt(idx < 0 ? buttons.length - 1 : idx - 1);
|
|
900
|
-
return;
|
|
901
|
-
case "Home":
|
|
902
|
-
e.preventDefault();
|
|
903
|
-
buttons[0]?.focus();
|
|
904
|
-
return;
|
|
905
|
-
case "End":
|
|
906
|
-
e.preventDefault();
|
|
907
|
-
buttons[buttons.length - 1]?.focus();
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
if (props.collapsed || idx < 0) return;
|
|
911
|
-
const key = buttons[idx]?.getAttribute("data-key");
|
|
912
|
-
if (!key) return;
|
|
913
|
-
const node = core.findNavNode(props.items, key);
|
|
914
|
-
if (e.key === "ArrowRight" && node && core.isBranch(node)) {
|
|
915
|
-
e.preventDefault();
|
|
916
|
-
if (!expanded.value.includes(key)) toggle(key);
|
|
917
|
-
else focusAt(idx + 1);
|
|
918
|
-
} else if (e.key === "ArrowLeft") {
|
|
919
|
-
if (node && core.isBranch(node) && expanded.value.includes(key)) {
|
|
920
|
-
e.preventDefault();
|
|
921
|
-
toggle(key);
|
|
922
|
-
} else {
|
|
923
|
-
const parentKey = core.findNavPath(props.items, key).at(-2)?.key;
|
|
924
|
-
if (parentKey) {
|
|
925
|
-
e.preventDefault();
|
|
926
|
-
buttons.find((b) => b.getAttribute("data-key") === parentKey)?.focus();
|
|
927
|
-
}
|
|
1201
|
+
const rootRef = vue.shallowRef(null);
|
|
1202
|
+
vue.watch(
|
|
1203
|
+
[() => props.activeKey, () => props.items],
|
|
1204
|
+
() => {
|
|
1205
|
+
if (!props.activeKey) return;
|
|
1206
|
+
const root = rootRef.value;
|
|
1207
|
+
if (!root) return;
|
|
1208
|
+
const el = root.querySelector('[data-iris-nav-item][data-active="true"]');
|
|
1209
|
+
if (!el || typeof el.scrollIntoView !== "function") return;
|
|
1210
|
+
let behavior = "auto";
|
|
1211
|
+
if (typeof window !== "undefined" && typeof window.matchMedia === "function" && !window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
1212
|
+
behavior = "smooth";
|
|
928
1213
|
}
|
|
929
|
-
|
|
930
|
-
|
|
1214
|
+
el.scrollIntoView({ block: "nearest", inline: "nearest", behavior });
|
|
1215
|
+
},
|
|
1216
|
+
{ flush: "post" }
|
|
1217
|
+
);
|
|
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
|
+
});
|
|
931
1235
|
const menuClass = [
|
|
932
1236
|
"iris-nav-menu",
|
|
933
1237
|
`iris-nav-menu--${props.orientation}`,
|
|
@@ -937,6 +1241,9 @@ var IrisNavMenu = vue.defineComponent({
|
|
|
937
1241
|
"nav",
|
|
938
1242
|
{
|
|
939
1243
|
...attrs,
|
|
1244
|
+
ref: (el) => {
|
|
1245
|
+
rootRef.value = el ?? null;
|
|
1246
|
+
},
|
|
940
1247
|
class: [...menuClass, attrs.class],
|
|
941
1248
|
"data-iris-nav-menu": "",
|
|
942
1249
|
"data-collapsed": props.collapsed ? "true" : void 0,
|
|
@@ -945,7 +1252,7 @@ var IrisNavMenu = vue.defineComponent({
|
|
|
945
1252
|
onKeydown,
|
|
946
1253
|
style: attrs.style
|
|
947
1254
|
},
|
|
948
|
-
|
|
1255
|
+
tree.value.map((node) => renderItem(node, 0))
|
|
949
1256
|
);
|
|
950
1257
|
}
|
|
951
1258
|
});
|
|
@@ -1015,11 +1322,11 @@ var IrisBreadcrumb = vue.defineComponent({
|
|
|
1015
1322
|
style: {
|
|
1016
1323
|
display: "inline-flex",
|
|
1017
1324
|
alignItems: "center",
|
|
1018
|
-
gap: "
|
|
1325
|
+
gap: "var(--iris-space-xs, 8px)",
|
|
1019
1326
|
margin: "0",
|
|
1020
1327
|
padding: "0",
|
|
1021
1328
|
listStyle: "none",
|
|
1022
|
-
fontSize: "14px"
|
|
1329
|
+
fontSize: "var(--iris-font-size-md, 14px)"
|
|
1023
1330
|
}
|
|
1024
1331
|
},
|
|
1025
1332
|
items
|
|
@@ -1352,6 +1659,7 @@ var IrisDropdownTrigger = vue.defineComponent({
|
|
|
1352
1659
|
function useFloating(options) {
|
|
1353
1660
|
const x = vue.ref(0);
|
|
1354
1661
|
const y = vue.ref(0);
|
|
1662
|
+
const positioned = vue.ref(false);
|
|
1355
1663
|
const strategy = vue.ref(options.strategy ?? "absolute");
|
|
1356
1664
|
const finalPlacement = vue.ref(options.placement ?? "bottom");
|
|
1357
1665
|
const arrowX = vue.ref();
|
|
@@ -1397,6 +1705,7 @@ function useFloating(options) {
|
|
|
1397
1705
|
if (token !== epoch) return;
|
|
1398
1706
|
x.value = result.x;
|
|
1399
1707
|
y.value = result.y;
|
|
1708
|
+
positioned.value = true;
|
|
1400
1709
|
finalPlacement.value = result.placement;
|
|
1401
1710
|
const arrowData = result.middlewareData.arrow;
|
|
1402
1711
|
arrowX.value = arrowData?.x;
|
|
@@ -1409,6 +1718,7 @@ function useFloating(options) {
|
|
|
1409
1718
|
cleanupAutoUpdate = null;
|
|
1410
1719
|
epoch++;
|
|
1411
1720
|
if (isOpen && anchorEl && floatingEl) {
|
|
1721
|
+
positioned.value = false;
|
|
1412
1722
|
cleanupAutoUpdate = dom.autoUpdate(anchorEl, floatingEl, () => {
|
|
1413
1723
|
void update();
|
|
1414
1724
|
});
|
|
@@ -1435,7 +1745,11 @@ function useFloating(options) {
|
|
|
1435
1745
|
top: "0",
|
|
1436
1746
|
left: "0",
|
|
1437
1747
|
transform: `translate3d(${Math.round(x.value)}px, ${Math.round(y.value)}px, 0)`,
|
|
1438
|
-
width: "max-content"
|
|
1748
|
+
width: "max-content",
|
|
1749
|
+
// Hidden until the first positioning cycle lands — prevents a flash at
|
|
1750
|
+
// the viewport origin. visibility (not display:none) keeps the panel
|
|
1751
|
+
// measurable so Floating UI can compute its real coordinates.
|
|
1752
|
+
visibility: positioned.value ? "visible" : "hidden"
|
|
1439
1753
|
}));
|
|
1440
1754
|
return { x, y, strategy, finalPlacement, floatingStyles, update, arrowX, arrowY, arrowSide };
|
|
1441
1755
|
}
|
|
@@ -1454,6 +1768,7 @@ function useDismiss(options) {
|
|
|
1454
1768
|
const onPointerDown = (event) => {
|
|
1455
1769
|
if (!options.enabled.value) return;
|
|
1456
1770
|
if (isExcluded(event.target)) return;
|
|
1771
|
+
if (options.excludePredicate?.(event.target)) return;
|
|
1457
1772
|
options.onDismiss();
|
|
1458
1773
|
};
|
|
1459
1774
|
const onKeyDown = (event) => {
|
|
@@ -1601,12 +1916,12 @@ var IrisDropdownMenu = vue.defineComponent({
|
|
|
1601
1916
|
onKeydown: onKeyDown,
|
|
1602
1917
|
style: {
|
|
1603
1918
|
...floatingStyles.value,
|
|
1604
|
-
background: "var(--iris-surface)",
|
|
1919
|
+
background: "var(--iris-surface-floating)",
|
|
1605
1920
|
color: "var(--iris-foreground)",
|
|
1606
1921
|
border: "1px solid var(--iris-border)",
|
|
1607
1922
|
borderRadius: "var(--iris-radius-md)",
|
|
1608
1923
|
padding: "var(--iris-padding-sm)",
|
|
1609
|
-
boxShadow: "
|
|
1924
|
+
boxShadow: "var(--iris-shadow-lg)",
|
|
1610
1925
|
minWidth: "160px",
|
|
1611
1926
|
outline: "none",
|
|
1612
1927
|
zIndex: "1000",
|
|
@@ -1671,14 +1986,14 @@ var IrisDropdownItem = vue.defineComponent({
|
|
|
1671
1986
|
display: "flex",
|
|
1672
1987
|
alignItems: "center",
|
|
1673
1988
|
gap: "var(--iris-gap-sm)",
|
|
1674
|
-
padding: "6px var(--iris-padding-md)",
|
|
1989
|
+
padding: "var(--iris-padding-sm, 6px) var(--iris-padding-md)",
|
|
1675
1990
|
borderRadius: "var(--iris-radius-sm)",
|
|
1676
1991
|
cursor: props.disabled ? "not-allowed" : "pointer",
|
|
1677
1992
|
opacity: props.disabled ? "0.5" : "1",
|
|
1678
1993
|
background: hovered.value && !props.disabled ? "var(--iris-surface-hover)" : "transparent",
|
|
1679
1994
|
color: "inherit",
|
|
1680
1995
|
outline: "none",
|
|
1681
|
-
fontSize: "14px",
|
|
1996
|
+
fontSize: "var(--iris-font-size-md, 14px)",
|
|
1682
1997
|
transition: "background-color 80ms ease",
|
|
1683
1998
|
...attrs.style ?? {}
|
|
1684
1999
|
}
|
|
@@ -1953,12 +2268,12 @@ var IrisAdminTabs = vue.defineComponent({
|
|
|
1953
2268
|
style: {
|
|
1954
2269
|
display: "inline-flex",
|
|
1955
2270
|
alignItems: "center",
|
|
1956
|
-
gap: "
|
|
2271
|
+
gap: "var(--iris-space-xs, 8px)",
|
|
1957
2272
|
border: "none",
|
|
1958
2273
|
background: "transparent",
|
|
1959
2274
|
color: "inherit",
|
|
1960
2275
|
font: "inherit",
|
|
1961
|
-
fontSize: "13px",
|
|
2276
|
+
fontSize: "var(--iris-font-size-sm, 13px)",
|
|
1962
2277
|
cursor: "pointer",
|
|
1963
2278
|
padding: "0"
|
|
1964
2279
|
},
|
|
@@ -2008,7 +2323,7 @@ var IrisAdminTabs = vue.defineComponent({
|
|
|
2008
2323
|
display: "inline-flex",
|
|
2009
2324
|
alignItems: "center",
|
|
2010
2325
|
gap: "4px",
|
|
2011
|
-
padding: "
|
|
2326
|
+
padding: "var(--iris-padding-sm, 6px) var(--iris-space-xs, 8px) var(--iris-padding-sm, 6px) var(--iris-space-sm, 12px)",
|
|
2012
2327
|
borderRadius: "var(--iris-radius-md, 6px)",
|
|
2013
2328
|
border: `1px solid ${active ? "var(--iris-primary)" : "var(--iris-border)"}`,
|
|
2014
2329
|
background: active ? "var(--iris-primary)" : "var(--iris-surface)",
|
|
@@ -2024,27 +2339,23 @@ var IrisAdminTabs = vue.defineComponent({
|
|
|
2024
2339
|
default: () => [
|
|
2025
2340
|
vue.h(
|
|
2026
2341
|
IrisDropdownTrigger,
|
|
2027
|
-
{ "aria-label": tr("admin.tabActions") },
|
|
2028
2342
|
{
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
},
|
|
2046
|
-
[vue.h(IrisIcon, { name: "more-horizontal", size: 16 })]
|
|
2047
|
-
)
|
|
2343
|
+
"aria-label": tr("admin.tabActions"),
|
|
2344
|
+
style: {
|
|
2345
|
+
display: "inline-flex",
|
|
2346
|
+
alignItems: "center",
|
|
2347
|
+
justifyContent: "center",
|
|
2348
|
+
width: "28px",
|
|
2349
|
+
height: "28px",
|
|
2350
|
+
borderRadius: "var(--iris-radius-md, 6px)",
|
|
2351
|
+
border: "1px solid var(--iris-border)",
|
|
2352
|
+
background: "var(--iris-surface)",
|
|
2353
|
+
color: "var(--iris-foreground)",
|
|
2354
|
+
cursor: "pointer"
|
|
2355
|
+
}
|
|
2356
|
+
},
|
|
2357
|
+
{
|
|
2358
|
+
default: () => vue.h(IrisIcon, { name: "more-horizontal", size: 16 })
|
|
2048
2359
|
}
|
|
2049
2360
|
),
|
|
2050
2361
|
vue.h(IrisDropdownMenu, null, {
|
|
@@ -2073,8 +2384,8 @@ var IrisAdminTabs = vue.defineComponent({
|
|
|
2073
2384
|
style: {
|
|
2074
2385
|
display: "flex",
|
|
2075
2386
|
alignItems: "center",
|
|
2076
|
-
gap: "
|
|
2077
|
-
padding: "
|
|
2387
|
+
gap: "var(--iris-space-xs, 8px)",
|
|
2388
|
+
padding: "var(--iris-space-xs, 8px) var(--iris-space-sm, 12px)",
|
|
2078
2389
|
borderBottom: "1px solid var(--iris-border)",
|
|
2079
2390
|
background: "var(--iris-background)",
|
|
2080
2391
|
...attrs.style ?? {}
|
|
@@ -2104,7 +2415,7 @@ var IrisAdminTabs = vue.defineComponent({
|
|
|
2104
2415
|
orientation: "horizontal",
|
|
2105
2416
|
style: {
|
|
2106
2417
|
alignItems: "center",
|
|
2107
|
-
gap: "
|
|
2418
|
+
gap: "var(--iris-space-xs, 8px)",
|
|
2108
2419
|
width: "max-content"
|
|
2109
2420
|
}
|
|
2110
2421
|
},
|
|
@@ -2238,12 +2549,12 @@ var IrisAdminLayout = vue.defineComponent({
|
|
|
2238
2549
|
style: {
|
|
2239
2550
|
display: "flex",
|
|
2240
2551
|
alignItems: "center",
|
|
2241
|
-
gap: "
|
|
2552
|
+
gap: "var(--iris-space-sm, 12px)",
|
|
2242
2553
|
height: "52px",
|
|
2243
|
-
padding: "0 16px",
|
|
2554
|
+
padding: "0 var(--iris-space-md, 16px)",
|
|
2244
2555
|
color: "var(--iris-foreground)",
|
|
2245
2556
|
fontWeight: "700",
|
|
2246
|
-
fontSize: "16px",
|
|
2557
|
+
fontSize: "var(--iris-font-size-lg, 16px)",
|
|
2247
2558
|
whiteSpace: "nowrap",
|
|
2248
2559
|
overflow: "hidden",
|
|
2249
2560
|
flexShrink: 0
|