@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.
Files changed (52) hide show
  1. package/dist/admin.cjs +547 -236
  2. package/dist/admin.cjs.map +1 -1
  3. package/dist/admin.d.cts +4 -2
  4. package/dist/admin.d.ts +4 -2
  5. package/dist/admin.js +2 -2
  6. package/dist/behaviors.cjs +49 -0
  7. package/dist/behaviors.cjs.map +1 -1
  8. package/dist/behaviors.d.cts +57 -1
  9. package/dist/behaviors.d.ts +57 -1
  10. package/dist/behaviors.js +1 -1
  11. package/dist/{chunk-QQ25RKOO.js → chunk-ASFDGYIR.js} +543 -240
  12. package/dist/chunk-ASFDGYIR.js.map +1 -0
  13. package/dist/{chunk-WL67XCH5.js → chunk-IN3DQ55U.js} +2 -2
  14. package/dist/chunk-IN3DQ55U.js.map +1 -0
  15. package/dist/{chunk-7L6UB6MD.js → chunk-IOMKC5OI.js} +53 -5
  16. package/dist/chunk-IOMKC5OI.js.map +1 -0
  17. package/dist/{chunk-3FJUCHCC.js → chunk-IPR5JAVF.js} +11 -3
  18. package/dist/chunk-IPR5JAVF.js.map +1 -0
  19. package/dist/{chunk-BEPH6PPL.js → chunk-LWABTQCB.js} +57 -37
  20. package/dist/chunk-LWABTQCB.js.map +1 -0
  21. package/dist/{chunk-DNRQVM3Q.js → chunk-QBFYIDY7.js} +3 -2
  22. package/dist/chunk-QBFYIDY7.js.map +1 -0
  23. package/dist/data.cjs +1 -0
  24. package/dist/data.cjs.map +1 -1
  25. package/dist/data.d.cts +4 -2
  26. package/dist/data.d.ts +4 -2
  27. package/dist/data.js +1 -1
  28. package/dist/floating.cjs +9 -1
  29. package/dist/floating.cjs.map +1 -1
  30. package/dist/floating.d.cts +6 -0
  31. package/dist/floating.d.ts +6 -0
  32. package/dist/floating.js +1 -1
  33. package/dist/index.cjs +2063 -745
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +172 -8
  36. package/dist/index.d.ts +172 -8
  37. package/dist/index.js +1412 -475
  38. package/dist/index.js.map +1 -1
  39. package/dist/layouts.cjs.map +1 -1
  40. package/dist/layouts.d.cts +1 -1
  41. package/dist/layouts.d.ts +1 -1
  42. package/dist/layouts.js +1 -1
  43. package/dist/skeletons.cjs +55 -35
  44. package/dist/skeletons.cjs.map +1 -1
  45. package/dist/skeletons.js +1 -1
  46. package/package.json +5 -5
  47. package/dist/chunk-3FJUCHCC.js.map +0 -1
  48. package/dist/chunk-7L6UB6MD.js.map +0 -1
  49. package/dist/chunk-BEPH6PPL.js.map +0 -1
  50. package/dist/chunk-DNRQVM3Q.js.map +0 -1
  51. package/dist/chunk-QQ25RKOO.js.map +0 -1
  52. 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: 10px;
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: 2px;
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, var(--iris-color-text, 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: 10px;
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: 10px;
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
- :where(.iris-nav-menu-item[data-active='true']) {
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: 6px;
487
- font-size: 11px;
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: 2px 6px;
489
+ padding: var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px);
490
490
  border-radius: 999px;
491
- background: var(--iris-danger, #e5484d);
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
- :where(.iris-nav-menu-group--depth-0 > .iris-nav-menu-children) {
523
- display: none;
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--depth-1 > .iris-nav-menu-children),
527
- :where(.iris-nav-menu-group--depth-2 > .iris-nav-menu-children),
528
- :where(.iris-nav-menu-group--depth-3 > .iris-nav-menu-children),
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
- display: block;
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
- :where(.iris-nav-menu--horizontal .iris-nav-menu-group > .iris-nav-menu-children) {
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: 6px;
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
- :where(.iris-nav-menu--horizontal .iris-nav-menu-group[data-depth]:not([data-depth='0']) > .iris-nav-menu-children) {
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
- () => props.activeKey,
644
- (key) => {
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 hovered = vue.ref(null);
661
- const hoveredBranches = vue.ref([]);
662
- const focusedBranches = vue.ref([]);
663
- const closeHorizontalMenus = () => {
664
- if (props.orientation !== "horizontal") return;
665
- hovered.value = null;
666
- hoveredBranches.value = [];
667
- focusedBranches.value = [];
668
- };
669
- const select = (node) => {
670
- if (node.disabled) return;
671
- closeHorizontalMenus();
672
- emit("select", node.key, node);
673
- };
674
- const setBranchInteraction = (state, key, enabled) => {
675
- const current = state.value;
676
- if (enabled) {
677
- if (!current.includes(key)) state.value = [...current, key];
678
- } else if (current.includes(key)) {
679
- state.value = current.filter((item) => item !== key);
680
- }
681
- };
682
- const keepsPointerInside = (container, event) => {
683
- if (!container || !(container instanceof HTMLElement)) return false;
684
- const next = event.relatedTarget;
685
- return !!(next && next instanceof Node && container.contains(next));
686
- };
687
- const interactionKeyAtDepth = (keys, depth) => keys.find((key) => core.findNavPath(props.items, key).length === depth + 1);
688
- const horizontalBranchVisible = (key, depth) => {
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 renderCollapsed = () => tree.value.map((node) => {
1052
+ const renderItem = (node, depth) => {
716
1053
  const branch = core.isBranch(node);
717
- const active = branch ? activePath.value.includes(node.key) : node.key === props.activeKey;
718
- return vue.h(
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: 0,
1071
+ depth,
726
1072
  active,
727
- trail: false,
728
- hovered: hovered.value === node.key,
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": hovered.value === node.key ? "true" : void 0,
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: () => branch ? toggle(node.key) : select(node)
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
- setBranchInteraction(hoveredBranches, node.key, true);
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
- setBranchInteraction(hoveredBranches, node.key, true);
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
- setBranchInteraction(hoveredBranches, node.key, false);
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
- setBranchInteraction(hoveredBranches, node.key, false);
1160
+ if (!node.disabled && !keepsPointerInside(event.currentTarget, event)) {
1161
+ scheduleClose(node.key);
857
1162
  }
858
1163
  },
859
- onFocusin: () => setBranchInteraction(focusedBranches, node.key, true),
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": horizontal && !shown ? "true" : void 0
1188
+ "aria-hidden": shown ? void 0 : "true"
878
1189
  },
879
- (node.children ?? []).map((child) => renderItem(child, depth + 1))
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 onKeydown = (e) => {
885
- const root = e.currentTarget;
886
- const buttons = Array.from(root.querySelectorAll("[data-iris-nav-item]")).filter(
887
- (button) => !button.closest('[data-iris-nav-children][aria-hidden="true"]')
888
- );
889
- if (buttons.length === 0) return;
890
- const idx = buttons.indexOf(document.activeElement);
891
- const focusAt = (i) => buttons[(i + buttons.length) % buttons.length]?.focus();
892
- switch (e.key) {
893
- case "ArrowDown":
894
- e.preventDefault();
895
- focusAt(idx < 0 ? 0 : idx + 1);
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
- props.collapsed ? renderCollapsed() : tree.value.map((node) => renderItem(node, 0))
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: "6px",
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: "0 8px 24px -8px rgba(0, 0, 0, 0.16), 0 4px 8px -2px rgba(0, 0, 0, 0.08)",
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: "6px",
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: "5px 8px 5px 10px",
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
- default: () => vue.h(
2030
- "span",
2031
- {
2032
- "data-iris-tab-actions": "",
2033
- style: {
2034
- display: "inline-flex",
2035
- alignItems: "center",
2036
- justifyContent: "center",
2037
- width: "28px",
2038
- height: "28px",
2039
- borderRadius: "var(--iris-radius-md, 6px)",
2040
- border: "1px solid var(--iris-border)",
2041
- background: "var(--iris-surface)",
2042
- color: "var(--iris-foreground)",
2043
- cursor: "pointer"
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: "6px",
2077
- padding: "6px 10px",
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: "6px",
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: "10px",
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