@sewadah/selaras 0.5.0 → 0.7.0

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 (46) hide show
  1. package/dist/agent/catalog.json +1 -1
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/components/DashboardSidebar.d.vue.ts +16 -1
  4. package/dist/runtime/components/DashboardSidebar.vue +44 -6
  5. package/dist/runtime/components/DashboardSidebar.vue.d.ts +16 -1
  6. package/dist/runtime/components/DatePicker.vue +1 -1
  7. package/dist/runtime/components/NavigationMenu.d.vue.ts +22 -11
  8. package/dist/runtime/components/NavigationMenu.vue +59 -29
  9. package/dist/runtime/components/NavigationMenu.vue.d.ts +22 -11
  10. package/dist/runtime/components/NavigationMenuAccordionItem.vue +2 -2
  11. package/dist/runtime/components/NavigationMenuFlyoutList.vue +2 -2
  12. package/dist/runtime/components/NavigationMenuFlyoutTrigger.d.vue.ts +2 -1
  13. package/dist/runtime/components/NavigationMenuFlyoutTrigger.vue +12 -2
  14. package/dist/runtime/components/NavigationMenuFlyoutTrigger.vue.d.ts +2 -1
  15. package/dist/runtime/components/ProsePre.d.vue.ts +2 -2
  16. package/dist/runtime/components/ProsePre.vue +13 -6
  17. package/dist/runtime/components/ProsePre.vue.d.ts +2 -2
  18. package/dist/runtime/components/SplitterPanel.vue +29 -3
  19. package/dist/runtime/components/Stepper.d.vue.ts +5 -0
  20. package/dist/runtime/components/Stepper.vue +12 -0
  21. package/dist/runtime/components/Stepper.vue.d.ts +5 -0
  22. package/dist/runtime/components/Table.vue +6 -1
  23. package/dist/runtime/internal/NavigationMenuTooltip.d.vue.ts +19 -0
  24. package/dist/runtime/internal/NavigationMenuTooltip.vue +13 -0
  25. package/dist/runtime/internal/NavigationMenuTooltip.vue.d.ts +19 -0
  26. package/dist/runtime/internal/stepper-root-element.d.ts +5 -0
  27. package/dist/runtime/internal/stepper-root-element.js +8 -0
  28. package/dist/runtime/structural.css +1 -1
  29. package/dist/runtime/theme/date-picker.d.ts +3 -0
  30. package/dist/runtime/theme/date-picker.js +1 -0
  31. package/dist/runtime/theme/navigation-menu.d.ts +18 -0
  32. package/dist/runtime/theme/navigation-menu.js +21 -28
  33. package/dist/runtime/theme/prose.d.ts +3 -0
  34. package/dist/runtime/theme/prose.js +7 -6
  35. package/dist/runtime/theme/stepper.d.ts +3 -0
  36. package/dist/runtime/theme/stepper.js +1 -0
  37. package/dist/runtime/theme/table.d.ts +6 -3
  38. package/dist/runtime/theme/table.js +5 -3
  39. package/dist/runtime/theme-config.d.ts +1 -1
  40. package/dist/runtime/types/index.d.ts +1 -1
  41. package/dist/runtime/utils/file-icons.js +1 -0
  42. package/dist/runtime/utils/messages.d.ts +4 -0
  43. package/dist/runtime/utils/messages.js +4 -0
  44. package/dist/runtime/utils/navigation-menu.d.ts +17 -0
  45. package/dist/runtime/utils/navigation-menu.js +25 -0
  46. package/package.json +3 -3
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "selaras",
3
3
  "configKey": "selaras",
4
- "version": "0.5.0",
4
+ "version": "0.7.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.3",
7
7
  "unbuild": "unknown"
@@ -2,6 +2,8 @@ import type { DashboardSidebarThemeSlots } from '../theme/dashboard-sidebar.js';
2
2
  import type { UiProp } from '../utils/ui.js';
3
3
  type __VLS_Slots = DashboardSidebarSlots;
4
4
  export interface DashboardSidebarProps {
5
+ /** Controlled desktop collapse state. Omit to keep resizing and persistence uncontrolled. */
6
+ collapsed?: boolean;
5
7
  /** @default 260 */
6
8
  defaultSize?: number;
7
9
  /** @default 200 */
@@ -16,6 +18,9 @@ export interface DashboardSidebarProps {
16
18
  sizeUnit?: 'px' | '%';
17
19
  ui?: UiProp<DashboardSidebarThemeSlots>;
18
20
  }
21
+ export interface DashboardSidebarEmits {
22
+ 'update:collapsed': [value: boolean];
23
+ }
19
24
  export interface DashboardSidebarSlots {
20
25
  /** `isCollapsed` lets a header (a logo mark, say) swap to a narrower version once the sidebar collapses to its icon rail - always `false` on mobile, where there's no in-between state, just the drawer open or closed. */
21
26
  header?: (props: {
@@ -29,7 +34,17 @@ export interface DashboardSidebarSlots {
29
34
  isCollapsed: boolean;
30
35
  }) => any;
31
36
  }
32
- declare const __VLS_base: import("vue").DefineComponent<DashboardSidebarProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DashboardSidebarProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
37
+ declare function toggle(): void;
38
+ declare const __VLS_base: import("vue").DefineComponent<DashboardSidebarProps, {
39
+ collapse: () => void;
40
+ expand: () => void;
41
+ toggle: typeof toggle;
42
+ isCollapsed: import("vue").ComputedRef<boolean>;
43
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
44
+ "update:collapsed": (value: boolean) => any;
45
+ }, string, import("vue").PublicProps, Readonly<DashboardSidebarProps> & Readonly<{
46
+ "onUpdate:collapsed"?: ((value: boolean) => any) | undefined;
47
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
33
48
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
34
49
  declare const _default: typeof __VLS_export;
35
50
  export default _default;
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import { computed, inject, onMounted, onUnmounted, ref, watch } from "vue";
2
+ import { computed, inject, nextTick, onMounted, onUnmounted, ref, watch } from "vue";
3
3
  import { dashboardSidebarTheme } from "../theme/dashboard-sidebar";
4
4
  import { DASHBOARD_INJECTION_KEY } from "../utils/injection-keys";
5
5
  import { resolveSlot, useComponentTheme, useRootProps } from "../utils/ui";
@@ -8,6 +8,7 @@ import ScrollArea from "./ScrollArea.vue";
8
8
  import SplitterPanel from "./SplitterPanel.vue";
9
9
  defineOptions({ inheritAttrs: false });
10
10
  const props = defineProps({
11
+ collapsed: { type: Boolean, required: false, default: void 0 },
11
12
  defaultSize: { type: Number, required: false, default: 260 },
12
13
  minSize: { type: Number, required: false, default: 200 },
13
14
  maxSize: { type: Number, required: false, default: 400 },
@@ -16,22 +17,48 @@ const props = defineProps({
16
17
  sizeUnit: { type: String, required: false, default: "px" },
17
18
  ui: { type: Object, required: false }
18
19
  });
20
+ const emit = defineEmits(["update:collapsed"]);
19
21
  defineSlots();
20
22
  const dashboard = inject(DASHBOARD_INJECTION_KEY, null);
21
23
  const isMobile = computed(() => dashboard?.isMobile.value ?? false);
22
24
  const panelRef = ref();
23
25
  const mobileOpen = ref(false);
26
+ let panelReady = false;
27
+ function setCollapsed(value) {
28
+ if (isMobile.value || !props.collapsible || !panelReady)
29
+ return;
30
+ if (value)
31
+ panelRef.value?.collapse();
32
+ else
33
+ panelRef.value?.expand();
34
+ }
35
+ watch(() => props.collapsed, (value) => {
36
+ if (value !== void 0)
37
+ setCollapsed(value);
38
+ });
39
+ defineExpose({
40
+ collapse: () => setCollapsed(true),
41
+ expand: () => setCollapsed(false),
42
+ toggle,
43
+ isCollapsed: computed(() => panelRef.value?.isCollapsed ?? false)
44
+ });
24
45
  function toggle() {
25
46
  if (isMobile.value) {
26
47
  mobileOpen.value = !mobileOpen.value;
27
48
  return;
28
49
  }
29
- if (panelRef.value?.isCollapsed)
30
- panelRef.value.expand();
31
- else
32
- panelRef.value?.collapse();
50
+ setCollapsed(!panelRef.value?.isCollapsed);
33
51
  }
34
52
  function handleResize(size) {
53
+ if (!panelReady) {
54
+ nextTick(() => {
55
+ if (!panelRef.value || panelReady)
56
+ return;
57
+ panelReady = true;
58
+ if (props.collapsed !== void 0)
59
+ setCollapsed(props.collapsed);
60
+ });
61
+ }
35
62
  if (dashboard)
36
63
  dashboard.lastDesktopWidthPx.value = size;
37
64
  }
@@ -40,11 +67,22 @@ onMounted(() => {
40
67
  if (isMobile.value || targetWidthPx === void 0)
41
68
  return;
42
69
  setTimeout(() => {
43
- panelRef.value?.resize(targetWidthPx);
70
+ if (props.collapsed === void 0)
71
+ panelRef.value?.resize(targetWidthPx);
44
72
  }, 50);
45
73
  });
46
74
  if (dashboard)
47
75
  dashboard.toggleSidebar.value = toggle;
76
+ watch(() => panelRef.value?.isCollapsed, (value) => {
77
+ if (!panelReady || isMobile.value || value === void 0)
78
+ return;
79
+ if (value !== props.collapsed)
80
+ emit("update:collapsed", value);
81
+ nextTick(() => {
82
+ if (props.collapsed !== void 0 && panelRef.value?.isCollapsed !== props.collapsed)
83
+ setCollapsed(props.collapsed);
84
+ });
85
+ }, { flush: "post" });
48
86
  const isSidebarCollapsed = computed(() => isMobile.value ? !mobileOpen.value : panelRef.value?.isCollapsed ?? false);
49
87
  if (dashboard) {
50
88
  watch(isSidebarCollapsed, (value) => {
@@ -2,6 +2,8 @@ import type { DashboardSidebarThemeSlots } from '../theme/dashboard-sidebar.js';
2
2
  import type { UiProp } from '../utils/ui.js';
3
3
  type __VLS_Slots = DashboardSidebarSlots;
4
4
  export interface DashboardSidebarProps {
5
+ /** Controlled desktop collapse state. Omit to keep resizing and persistence uncontrolled. */
6
+ collapsed?: boolean;
5
7
  /** @default 260 */
6
8
  defaultSize?: number;
7
9
  /** @default 200 */
@@ -16,6 +18,9 @@ export interface DashboardSidebarProps {
16
18
  sizeUnit?: 'px' | '%';
17
19
  ui?: UiProp<DashboardSidebarThemeSlots>;
18
20
  }
21
+ export interface DashboardSidebarEmits {
22
+ 'update:collapsed': [value: boolean];
23
+ }
19
24
  export interface DashboardSidebarSlots {
20
25
  /** `isCollapsed` lets a header (a logo mark, say) swap to a narrower version once the sidebar collapses to its icon rail - always `false` on mobile, where there's no in-between state, just the drawer open or closed. */
21
26
  header?: (props: {
@@ -29,7 +34,17 @@ export interface DashboardSidebarSlots {
29
34
  isCollapsed: boolean;
30
35
  }) => any;
31
36
  }
32
- declare const __VLS_base: import("vue").DefineComponent<DashboardSidebarProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DashboardSidebarProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
37
+ declare function toggle(): void;
38
+ declare const __VLS_base: import("vue").DefineComponent<DashboardSidebarProps, {
39
+ collapse: () => void;
40
+ expand: () => void;
41
+ toggle: typeof toggle;
42
+ isCollapsed: import("vue").ComputedRef<boolean>;
43
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
44
+ "update:collapsed": (value: boolean) => any;
45
+ }, string, import("vue").PublicProps, Readonly<DashboardSidebarProps> & Readonly<{
46
+ "onUpdate:collapsed"?: ((value: boolean) => any) | undefined;
47
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
33
48
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
34
49
  declare const _default: typeof __VLS_export;
35
50
  export default _default;
@@ -446,7 +446,7 @@ const timeBodyProps = computed(() => ({
446
446
  close: () => {
447
447
  onUpdateOpen(false);
448
448
  },
449
- timeSectionProps: timeSectionProps.value
449
+ timeSectionProps: resolveSlot(ui.value.timeSectionStandalone, props.ui?.timeSectionStandalone)
450
450
  }));
451
451
  const buttonTriggerUi = computed(() => ({
452
452
  base: [
@@ -1,17 +1,28 @@
1
1
  import type { NavigationMenuThemeSlots } from '../theme/navigation-menu.js';
2
2
  import type { ColorRole } from '../utils/color-registry.js';
3
- import type { NavigationMenuItem } from '../utils/navigation-menu.js';
3
+ import type { NavigationMenuItem, NavigationMenuPopover, NavigationMenuPositioning } from '../utils/navigation-menu.js';
4
4
  import type { UiProp } from '../utils/ui.js';
5
+ import type { TooltipProps } from './Tooltip.vue.js';
5
6
  export interface NavigationMenuProps {
6
7
  items: NavigationMenuItem[];
7
8
  /** Horizontal uses Reka's real shared-viewport flyout for a single level of dropdown children. Vertical falls back to a recursive accordion (see NavigationMenuAccordionItem.vue) for arbitrary depth - Reka's own NavigationMenuContent isn't built for deep nested trees. */
8
9
  orientation?: 'horizontal' | 'vertical';
10
+ /** Dropdown layout for horizontal navigation. @default 'vertical' */
11
+ contentOrientation?: 'horizontal' | 'vertical';
12
+ /** Alignment to the active trigger when contentOrientation is vertical. @default { align: 'center' } */
13
+ positioning?: NavigationMenuPositioning;
9
14
  color?: ColorRole;
10
15
  variant?: 'pill' | 'link';
11
16
  /** Draws a bar/underline next to the active item, in addition to its own color styling. */
12
17
  highlight?: boolean;
13
18
  /** Icon-only rail mode (vertical only) - labels stay in the DOM for assistive tech (`sr-only`, not removed) but visually collapse to just each item's own leading icon. A parent with children renders as a themed Popover trigger instead of an expandable accordion row - no room for a nested list in an icon rail, so its children surface in a flyout next to the icon instead (the common "collapsed sidebar" pattern - VSCode's activity bar, Linear, Notion). */
14
19
  collapsed?: boolean;
20
+ /** Presentation of section headings in a collapsed vertical rail. @default 'separator' */
21
+ collapsedGroups?: 'separator' | 'spacing' | 'none';
22
+ /** Labels for collapsed vertical leaf items. An object customizes Tooltip props. @default false */
23
+ tooltip?: boolean | TooltipProps;
24
+ /** Presentation of collapsed child flyouts; item settings override these defaults. */
25
+ popover?: NavigationMenuPopover;
15
26
  ui?: UiProp<NavigationMenuThemeSlots>;
16
27
  }
17
28
  declare var __VLS_8: {}, __VLS_17: string, __VLS_18: {
@@ -45,19 +56,19 @@ declare var __VLS_8: {}, __VLS_17: string, __VLS_18: {
45
56
  }, __VLS_124: string, __VLS_125: {
46
57
  item: NavigationMenuItem;
47
58
  active: boolean;
48
- }, __VLS_147: string, __VLS_148: {
59
+ }, __VLS_153: string, __VLS_154: {
49
60
  item: NavigationMenuItem;
50
61
  active: boolean;
51
- }, __VLS_151: string, __VLS_152: {
62
+ }, __VLS_157: string, __VLS_158: {
52
63
  item: NavigationMenuItem;
53
64
  active: boolean;
54
- }, __VLS_160: string, __VLS_161: {
65
+ }, __VLS_166: string, __VLS_167: {
55
66
  item: NavigationMenuItem;
56
67
  active: boolean;
57
- }, __VLS_164: string, __VLS_165: {
68
+ }, __VLS_170: string, __VLS_171: {
58
69
  item: NavigationMenuItem;
59
70
  active: boolean;
60
- }, __VLS_172: {};
71
+ }, __VLS_178: {};
61
72
  type __VLS_Slots = {} & {
62
73
  [K in NonNullable<typeof __VLS_17>]?: (props: typeof __VLS_18) => any;
63
74
  } & {
@@ -83,17 +94,17 @@ type __VLS_Slots = {} & {
83
94
  } & {
84
95
  [K in NonNullable<typeof __VLS_124>]?: (props: typeof __VLS_125) => any;
85
96
  } & {
86
- [K in NonNullable<typeof __VLS_147>]?: (props: typeof __VLS_148) => any;
97
+ [K in NonNullable<typeof __VLS_153>]?: (props: typeof __VLS_154) => any;
87
98
  } & {
88
- [K in NonNullable<typeof __VLS_151>]?: (props: typeof __VLS_152) => any;
99
+ [K in NonNullable<typeof __VLS_157>]?: (props: typeof __VLS_158) => any;
89
100
  } & {
90
- [K in NonNullable<typeof __VLS_160>]?: (props: typeof __VLS_161) => any;
101
+ [K in NonNullable<typeof __VLS_166>]?: (props: typeof __VLS_167) => any;
91
102
  } & {
92
- [K in NonNullable<typeof __VLS_164>]?: (props: typeof __VLS_165) => any;
103
+ [K in NonNullable<typeof __VLS_170>]?: (props: typeof __VLS_171) => any;
93
104
  } & {
94
105
  'list-leading'?: (props: typeof __VLS_8) => any;
95
106
  } & {
96
- 'list-trailing'?: (props: typeof __VLS_172) => any;
107
+ 'list-trailing'?: (props: typeof __VLS_178) => any;
97
108
  };
98
109
  declare const __VLS_base: import("vue").DefineComponent<NavigationMenuProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NavigationMenuProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
99
110
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -6,16 +6,18 @@ import {
6
6
  NavigationMenuRoot,
7
7
  NavigationMenuTrigger,
8
8
  NavigationMenuViewport,
9
- NavigationMenuItem as RekaNavigationMenuItem
9
+ NavigationMenuItem as RekaNavigationMenuItem,
10
+ useDirection
10
11
  } from "reka-ui";
11
12
  import { computed, ref, useSlots } from "vue";
12
13
  import { NuxtLink } from "#components";
13
14
  import { useRoute } from "#imports";
14
15
  import { useIcons } from "../composables/use-icons";
15
16
  import { vHotkey } from "../directives/hotkey";
17
+ import NavigationMenuTooltip from "../internal/NavigationMenuTooltip.vue";
16
18
  import ShortcutHint from "../internal/ShortcutHint.vue";
17
19
  import { navigationMenuTheme } from "../theme/navigation-menu";
18
- import { isNavigationMenuItemActive, navigationMenuHotkey } from "../utils/navigation-menu";
20
+ import { collapsedNavigationMenuEntries, isNavigationMenuItemActive, navigationMenuHotkey } from "../utils/navigation-menu";
19
21
  import { resolveRegisteredColorRole } from "../utils/registered-colors";
20
22
  import { resolveSlot, useComponentTheme } from "../utils/ui";
21
23
  import Icon from "./Icon.vue";
@@ -24,15 +26,28 @@ import NavigationMenuFlyoutTrigger from "./NavigationMenuFlyoutTrigger.vue";
24
26
  const props = defineProps({
25
27
  items: { type: Array, required: true },
26
28
  orientation: { type: String, required: false, default: "horizontal" },
29
+ contentOrientation: { type: String, required: false, default: "vertical" },
30
+ positioning: { type: Object, required: false },
27
31
  color: { type: null, required: false },
28
32
  variant: { type: String, required: false, default: "pill" },
29
33
  highlight: { type: Boolean, required: false },
30
34
  collapsed: { type: Boolean, required: false },
35
+ collapsedGroups: { type: String, required: false, default: "separator" },
36
+ tooltip: { type: [Boolean, Object], required: false },
37
+ popover: { type: Object, required: false },
31
38
  ui: { type: Object, required: false }
32
39
  });
40
+ const direction = useDirection();
33
41
  const route = useRoute();
34
42
  const icons = useIcons();
35
43
  const slots = useSlots();
44
+ const collapsedVertical = computed(() => props.orientation === "vertical" && props.collapsed);
45
+ const effectiveContentOrientation = computed(() => props.orientation === "horizontal" ? props.contentOrientation : "horizontal");
46
+ const viewportAlign = computed(() => {
47
+ const align = props.positioning?.align ?? "center";
48
+ return direction.value === "rtl" && align !== "center" ? align === "start" ? "end" : "start" : align;
49
+ });
50
+ const entries = computed(() => collapsedVertical.value ? collapsedNavigationMenuEntries(props.items, props.collapsedGroups) : props.items.map((item) => ({ item, boundary: false })));
36
51
  function slotName(item, suffix) {
37
52
  const named = item.slot ? `${item.slot}${suffix}` : void 0;
38
53
  if (named && slots[named])
@@ -41,17 +56,29 @@ function slotName(item, suffix) {
41
56
  }
42
57
  const theme = useComponentTheme("navigationMenu", navigationMenuTheme);
43
58
  const effectiveColor = computed(() => resolveRegisteredColorRole(props.color ?? "primary", "primary"));
44
- const ui = computed(() => theme.value({ orientation: props.orientation, color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: props.collapsed }));
59
+ const ui = computed(() => theme.value({ orientation: props.orientation, contentOrientation: effectiveContentOrientation.value, color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: props.collapsed }));
45
60
  const rootProps = computed(() => resolveSlot(ui.value.root, props.ui?.root));
46
61
  const listProps = computed(() => resolveSlot(ui.value.list, props.ui?.list));
62
+ function tooltipProps(item) {
63
+ const setting = item.tooltip ?? props.tooltip;
64
+ if (props.orientation !== "vertical" || !props.collapsed || item.disabled || !setting)
65
+ return void 0;
66
+ return {
67
+ text: item.ariaLabel ?? item.label,
68
+ side: direction.value === "rtl" ? "left" : "right",
69
+ delayDuration: 0,
70
+ ...typeof props.tooltip === "object" ? props.tooltip : {},
71
+ ...typeof item.tooltip === "object" ? item.tooltip : {}
72
+ };
73
+ }
47
74
  function isActive(item) {
48
75
  return isNavigationMenuItemActive(item, route.path);
49
76
  }
50
77
  function linkProps(item) {
51
- return resolveSlot(theme.value({ orientation: props.orientation, color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: props.collapsed, active: isActive(item), disabled: item.disabled }).link, props.ui?.link);
78
+ return resolveSlot(theme.value({ orientation: props.orientation, contentOrientation: effectiveContentOrientation.value, color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: props.collapsed, active: isActive(item), disabled: item.disabled }).link, props.ui?.link);
52
79
  }
53
80
  function childLinkProps(item) {
54
- return resolveSlot(theme.value({ orientation: props.orientation, color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: props.collapsed, active: isActive(item), disabled: item.disabled }).childLink, props.ui?.childLink);
81
+ return resolveSlot(theme.value({ orientation: props.orientation, contentOrientation: effectiveContentOrientation.value, color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: props.collapsed, active: isActive(item), disabled: item.disabled }).childLink, props.ui?.childLink);
55
82
  }
56
83
  const openFlyoutLabel = ref(null);
57
84
  function onFlyoutOpenChange(item, value) {
@@ -76,9 +103,10 @@ function onSelect(item, event) {
76
103
  <NavigationMenuRoot :orientation="orientation" :data-selaras-color="effectiveColor" v-bind="rootProps">
77
104
  <slot name="list-leading" />
78
105
  <NavigationMenuList v-bind="listProps">
79
- <template v-for="item in items" :key="item.label">
80
- <li v-if="item.type === 'separator'" role="separator" v-bind="resolveSlot(ui.separator, props.ui?.separator)" />
81
- <li v-else-if="item.type === 'label'" v-bind="resolveSlot(ui.groupLabel, props.ui?.groupLabel)">
106
+ <template v-for="({ item, boundary }, index) in entries" :key="boundary ? `boundary-${index}` : `item-${item.label}`">
107
+ <li v-if="boundary === 'spacing'" aria-hidden="true" v-bind="resolveSlot(ui.groupSpacer, props.ui?.groupSpacer)" />
108
+ <li v-else-if="item.type === 'separator'" role="separator" v-bind="resolveSlot(ui.separator, props.ui?.separator)" />
109
+ <li v-else-if="item.type === 'label'" v-bind="collapsedVertical ? resolveSlot(ui.collapsedGroupLabel, props.ui?.collapsedGroupLabel) : resolveSlot(ui.groupLabel, props.ui?.groupLabel)">
82
110
  <slot :name="slotName(item, '-label')" :item="item" :active="false">
83
111
  {{ item.label }}
84
112
  </slot>
@@ -93,7 +121,7 @@ function onSelect(item, event) {
93
121
  </template>
94
122
  <template v-else-if="orientation === 'vertical' && item.children?.length && collapsed">
95
123
  <NavigationMenuFlyoutTrigger
96
- :item="item" :color="color" :variant="variant" :highlight="highlight" :on-select="onSelect" :ui="props.ui"
124
+ :item="item" :color="color" :variant="variant" :highlight="highlight" :on-select="onSelect" :ui="props.ui" :popover="props.popover"
97
125
  :open="openFlyoutLabel === item.label" :another-flyout-open="anotherFlyoutOpen(item)"
98
126
  @update:open="onFlyoutOpenChange(item, $event)"
99
127
  >
@@ -144,31 +172,33 @@ function onSelect(item, event) {
144
172
  </slot>
145
173
  </NavigationMenuContent>
146
174
  </template>
147
- <NavigationMenuLink v-else as-child :active="isActive(item)">
148
- <component
149
- :is="item.to ? NuxtLink : 'button'" v-hotkey="navigationMenuHotkey(item)" :to="item.to"
150
- :type="item.to ? void 0 : 'button'" :disabled="item.to ? void 0 : item.disabled"
151
- v-bind="linkProps(item)" :target="item.target" :rel="item.rel"
152
- :aria-label="item.ariaLabel" :aria-disabled="item.to && item.disabled ? 'true' : void 0" @click="onSelect(item, $event)"
153
- >
154
- <slot :name="slotName(item, '')" :item="item" :active="isActive(item)">
155
- <slot :name="slotName(item, '-leading')" :item="item" :active="isActive(item)">
156
- <Icon v-if="item.icon" :name="item.icon" v-bind="resolveSlot(ui.linkIcon, props.ui?.linkIcon)" />
157
- <span v-else-if="collapsed" v-bind="resolveSlot(ui.linkIconFallback, props.ui?.linkIconFallback)" aria-hidden="true">{{ item.label.charAt(0) }}</span>
175
+ <NavigationMenuTooltip v-else :tooltip="tooltipProps(item)">
176
+ <NavigationMenuLink as-child :active="isActive(item)">
177
+ <component
178
+ :is="item.to ? NuxtLink : 'button'" v-hotkey="navigationMenuHotkey(item)" :to="item.to"
179
+ :type="item.to ? void 0 : 'button'" :disabled="item.to ? void 0 : item.disabled"
180
+ v-bind="linkProps(item)" :target="item.target" :rel="item.rel"
181
+ :aria-label="item.ariaLabel" :aria-disabled="item.to && item.disabled ? 'true' : void 0" @click="onSelect(item, $event)"
182
+ >
183
+ <slot :name="slotName(item, '')" :item="item" :active="isActive(item)">
184
+ <slot :name="slotName(item, '-leading')" :item="item" :active="isActive(item)">
185
+ <Icon v-if="item.icon" :name="item.icon" v-bind="resolveSlot(ui.linkIcon, props.ui?.linkIcon)" />
186
+ <span v-else-if="collapsed" v-bind="resolveSlot(ui.linkIconFallback, props.ui?.linkIconFallback)" aria-hidden="true">{{ item.label.charAt(0) }}</span>
187
+ </slot>
188
+ <slot :name="slotName(item, '-label')" :item="item" :active="isActive(item)">
189
+ <span v-bind="resolveSlot(ui.linkLabel, props.ui?.linkLabel)">{{ item.label }}</span>
190
+ </slot>
191
+ <slot :name="slotName(item, '-trailing')" :item="item" :active="isActive(item)" />
192
+ <ShortcutHint :shortcut="collapsed ? void 0 : item.shortcut" />
158
193
  </slot>
159
- <slot :name="slotName(item, '-label')" :item="item" :active="isActive(item)">
160
- <span v-bind="resolveSlot(ui.linkLabel, props.ui?.linkLabel)">{{ item.label }}</span>
161
- </slot>
162
- <slot :name="slotName(item, '-trailing')" :item="item" :active="isActive(item)" />
163
- <ShortcutHint :shortcut="collapsed ? void 0 : item.shortcut" />
164
- </slot>
165
- </component>
166
- </NavigationMenuLink>
194
+ </component>
195
+ </NavigationMenuLink>
196
+ </NavigationMenuTooltip>
167
197
  </RekaNavigationMenuItem>
168
198
  </template>
169
199
  </NavigationMenuList>
170
200
  <slot name="list-trailing" />
171
201
 
172
- <NavigationMenuViewport v-if="orientation === 'horizontal'" v-bind="resolveSlot(ui.viewport, props.ui?.viewport)" />
202
+ <NavigationMenuViewport v-if="orientation === 'horizontal'" :align="viewportAlign" v-bind="resolveSlot(ui.viewport, props.ui?.viewport)" />
173
203
  </NavigationMenuRoot>
174
204
  </template>
@@ -1,17 +1,28 @@
1
1
  import type { NavigationMenuThemeSlots } from '../theme/navigation-menu.js';
2
2
  import type { ColorRole } from '../utils/color-registry.js';
3
- import type { NavigationMenuItem } from '../utils/navigation-menu.js';
3
+ import type { NavigationMenuItem, NavigationMenuPopover, NavigationMenuPositioning } from '../utils/navigation-menu.js';
4
4
  import type { UiProp } from '../utils/ui.js';
5
+ import type { TooltipProps } from './Tooltip.vue.js';
5
6
  export interface NavigationMenuProps {
6
7
  items: NavigationMenuItem[];
7
8
  /** Horizontal uses Reka's real shared-viewport flyout for a single level of dropdown children. Vertical falls back to a recursive accordion (see NavigationMenuAccordionItem.vue) for arbitrary depth - Reka's own NavigationMenuContent isn't built for deep nested trees. */
8
9
  orientation?: 'horizontal' | 'vertical';
10
+ /** Dropdown layout for horizontal navigation. @default 'vertical' */
11
+ contentOrientation?: 'horizontal' | 'vertical';
12
+ /** Alignment to the active trigger when contentOrientation is vertical. @default { align: 'center' } */
13
+ positioning?: NavigationMenuPositioning;
9
14
  color?: ColorRole;
10
15
  variant?: 'pill' | 'link';
11
16
  /** Draws a bar/underline next to the active item, in addition to its own color styling. */
12
17
  highlight?: boolean;
13
18
  /** Icon-only rail mode (vertical only) - labels stay in the DOM for assistive tech (`sr-only`, not removed) but visually collapse to just each item's own leading icon. A parent with children renders as a themed Popover trigger instead of an expandable accordion row - no room for a nested list in an icon rail, so its children surface in a flyout next to the icon instead (the common "collapsed sidebar" pattern - VSCode's activity bar, Linear, Notion). */
14
19
  collapsed?: boolean;
20
+ /** Presentation of section headings in a collapsed vertical rail. @default 'separator' */
21
+ collapsedGroups?: 'separator' | 'spacing' | 'none';
22
+ /** Labels for collapsed vertical leaf items. An object customizes Tooltip props. @default false */
23
+ tooltip?: boolean | TooltipProps;
24
+ /** Presentation of collapsed child flyouts; item settings override these defaults. */
25
+ popover?: NavigationMenuPopover;
15
26
  ui?: UiProp<NavigationMenuThemeSlots>;
16
27
  }
17
28
  declare var __VLS_8: {}, __VLS_17: string, __VLS_18: {
@@ -45,19 +56,19 @@ declare var __VLS_8: {}, __VLS_17: string, __VLS_18: {
45
56
  }, __VLS_124: string, __VLS_125: {
46
57
  item: NavigationMenuItem;
47
58
  active: boolean;
48
- }, __VLS_147: string, __VLS_148: {
59
+ }, __VLS_153: string, __VLS_154: {
49
60
  item: NavigationMenuItem;
50
61
  active: boolean;
51
- }, __VLS_151: string, __VLS_152: {
62
+ }, __VLS_157: string, __VLS_158: {
52
63
  item: NavigationMenuItem;
53
64
  active: boolean;
54
- }, __VLS_160: string, __VLS_161: {
65
+ }, __VLS_166: string, __VLS_167: {
55
66
  item: NavigationMenuItem;
56
67
  active: boolean;
57
- }, __VLS_164: string, __VLS_165: {
68
+ }, __VLS_170: string, __VLS_171: {
58
69
  item: NavigationMenuItem;
59
70
  active: boolean;
60
- }, __VLS_172: {};
71
+ }, __VLS_178: {};
61
72
  type __VLS_Slots = {} & {
62
73
  [K in NonNullable<typeof __VLS_17>]?: (props: typeof __VLS_18) => any;
63
74
  } & {
@@ -83,17 +94,17 @@ type __VLS_Slots = {} & {
83
94
  } & {
84
95
  [K in NonNullable<typeof __VLS_124>]?: (props: typeof __VLS_125) => any;
85
96
  } & {
86
- [K in NonNullable<typeof __VLS_147>]?: (props: typeof __VLS_148) => any;
97
+ [K in NonNullable<typeof __VLS_153>]?: (props: typeof __VLS_154) => any;
87
98
  } & {
88
- [K in NonNullable<typeof __VLS_151>]?: (props: typeof __VLS_152) => any;
99
+ [K in NonNullable<typeof __VLS_157>]?: (props: typeof __VLS_158) => any;
89
100
  } & {
90
- [K in NonNullable<typeof __VLS_160>]?: (props: typeof __VLS_161) => any;
101
+ [K in NonNullable<typeof __VLS_166>]?: (props: typeof __VLS_167) => any;
91
102
  } & {
92
- [K in NonNullable<typeof __VLS_164>]?: (props: typeof __VLS_165) => any;
103
+ [K in NonNullable<typeof __VLS_170>]?: (props: typeof __VLS_171) => any;
93
104
  } & {
94
105
  'list-leading'?: (props: typeof __VLS_8) => any;
95
106
  } & {
96
- 'list-trailing'?: (props: typeof __VLS_172) => any;
107
+ 'list-trailing'?: (props: typeof __VLS_178) => any;
97
108
  };
98
109
  declare const __VLS_base: import("vue").DefineComponent<NavigationMenuProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NavigationMenuProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
99
110
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -29,7 +29,7 @@ function slotName(item, suffix) {
29
29
  }
30
30
  const theme = useComponentTheme("navigationMenu", navigationMenuTheme);
31
31
  const effectiveColor = computed(() => resolveRegisteredColorRole(props.color ?? "primary", "primary"));
32
- const ui = computed(() => theme.value({ orientation: "vertical", color: effectiveColor.value, variant: props.variant, highlight: props.highlight }));
32
+ const ui = computed(() => theme.value({ orientation: "vertical", contentOrientation: "horizontal", color: effectiveColor.value, variant: props.variant, highlight: props.highlight }));
33
33
  const accordionValue = useId();
34
34
  const accordionItems = computed(() => [{ value: accordionValue, label: props.item.label, disabled: props.item.disabled }]);
35
35
  const groupUi = computed(() => ({
@@ -43,7 +43,7 @@ function isActive(item) {
43
43
  return isNavigationMenuItemActive(item, route.path);
44
44
  }
45
45
  function linkProps(child) {
46
- return resolveSlot(theme.value({ orientation: "vertical", color: effectiveColor.value, variant: props.variant, highlight: props.highlight, active: isActive(child), disabled: child.disabled }).link, props.ui?.link);
46
+ return resolveSlot(theme.value({ orientation: "vertical", contentOrientation: "horizontal", color: effectiveColor.value, variant: props.variant, highlight: props.highlight, active: isActive(child), disabled: child.disabled }).link, props.ui?.link);
47
47
  }
48
48
  function onSelect(item, event) {
49
49
  if (item.disabled) {
@@ -26,9 +26,9 @@ function isActive(item) {
26
26
  const theme = useComponentTheme("navigationMenu", navigationMenuTheme);
27
27
  const themeBindings = useThemeBindings();
28
28
  const effectiveColor = computed(() => resolveRegisteredColorRole(props.color ?? "primary", "primary"));
29
- const ui = computed(() => theme.value({ orientation: "vertical", color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: false, flyoutRoot: props.root }));
29
+ const ui = computed(() => theme.value({ orientation: "vertical", contentOrientation: "horizontal", color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: false, flyoutRoot: props.root }));
30
30
  function linkProps(item) {
31
- return resolveSlot(theme.value({ orientation: "vertical", color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: false, active: isActive(item), disabled: item.disabled }).link, props.ui?.link);
31
+ return resolveSlot(theme.value({ orientation: "vertical", contentOrientation: "horizontal", color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: false, active: isActive(item), disabled: item.disabled }).link, props.ui?.link);
32
32
  }
33
33
  </script>
34
34
 
@@ -1,9 +1,10 @@
1
1
  import type { NavigationMenuThemeSlots } from '../theme/navigation-menu.js';
2
2
  import type { ColorRole } from '../utils/color-registry.js';
3
- import type { NavigationMenuItem } from '../utils/navigation-menu.js';
3
+ import type { NavigationMenuItem, NavigationMenuPopover } from '../utils/navigation-menu.js';
4
4
  import type { UiProp } from '../utils/ui.js';
5
5
  export interface NavigationMenuFlyoutTriggerProps {
6
6
  item: NavigationMenuItem;
7
+ popover?: NavigationMenuPopover;
7
8
  color?: ColorRole;
8
9
  variant?: 'pill' | 'link';
9
10
  highlight?: boolean;
@@ -1,4 +1,5 @@
1
1
  <script setup>
2
+ import { useDirection } from "reka-ui";
2
3
  import { computed, ref, useSlots } from "vue";
3
4
  import { navigationMenuTheme } from "../theme/navigation-menu";
4
5
  import { resolveRegisteredColorRole } from "../utils/registered-colors";
@@ -8,6 +9,7 @@ import NavigationMenuFlyoutList from "./NavigationMenuFlyoutList.vue";
8
9
  import Popover from "./Popover.vue";
9
10
  const props = defineProps({
10
11
  item: { type: Object, required: true },
12
+ popover: { type: Object, required: false },
11
13
  color: { type: null, required: false },
12
14
  variant: { type: String, required: false },
13
15
  highlight: { type: Boolean, required: false },
@@ -18,6 +20,14 @@ const props = defineProps({
18
20
  });
19
21
  const emit = defineEmits(["update:open"]);
20
22
  const slots = useSlots();
23
+ const direction = useDirection();
24
+ const popoverProps = computed(() => ({
25
+ side: direction.value === "rtl" ? "left" : "right",
26
+ align: "start",
27
+ ...props.popover,
28
+ ...props.item.popover,
29
+ ui: { content: "p-2", ...props.popover?.ui, ...props.item.popover?.ui }
30
+ }));
21
31
  function slotName(item, suffix) {
22
32
  const named = item.slot ? `${item.slot}${suffix}` : void 0;
23
33
  if (named && slots[named])
@@ -27,7 +37,7 @@ function slotName(item, suffix) {
27
37
  const theme = useComponentTheme("navigationMenu", navigationMenuTheme);
28
38
  const themeBindings = useThemeBindings();
29
39
  const effectiveColor = computed(() => resolveRegisteredColorRole(props.color ?? "primary", "primary"));
30
- const ui = computed(() => theme.value({ orientation: "vertical", color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: true, active: false, disabled: props.item.disabled }));
40
+ const ui = computed(() => theme.value({ orientation: "vertical", contentOrientation: "horizontal", color: effectiveColor.value, variant: props.variant, highlight: props.highlight, collapsed: true, active: false, disabled: props.item.disabled }));
31
41
  let closeTimer;
32
42
  function cancelClose() {
33
43
  if (closeTimer !== void 0) {
@@ -88,7 +98,7 @@ function onContentKeydown(event) {
88
98
 
89
99
  <template>
90
100
  <Popover
91
- :open="open" side="right" align="start" :return-focus-on-close="!anotherFlyoutOpen" :ui="{ content: 'p-2' }"
101
+ v-bind="popoverProps" :open="open" :return-focus-on-close="!anotherFlyoutOpen"
92
102
  @update:open="emit('update:open', $event)" @open-auto-focus="onOpenAutoFocus"
93
103
  >
94
104
  <button
@@ -1,9 +1,10 @@
1
1
  import type { NavigationMenuThemeSlots } from '../theme/navigation-menu.js';
2
2
  import type { ColorRole } from '../utils/color-registry.js';
3
- import type { NavigationMenuItem } from '../utils/navigation-menu.js';
3
+ import type { NavigationMenuItem, NavigationMenuPopover } from '../utils/navigation-menu.js';
4
4
  import type { UiProp } from '../utils/ui.js';
5
5
  export interface NavigationMenuFlyoutTriggerProps {
6
6
  item: NavigationMenuItem;
7
+ popover?: NavigationMenuPopover;
7
8
  color?: ColorRole;
8
9
  variant?: 'pill' | 'link';
9
10
  highlight?: boolean;
@@ -9,9 +9,9 @@ export interface ProsePreProps {
9
9
  highlights?: number[];
10
10
  meta?: string;
11
11
  }
12
- declare var __VLS_18: {};
12
+ declare var __VLS_13: {};
13
13
  type __VLS_Slots = {} & {
14
- default?: (props: typeof __VLS_18) => any;
14
+ default?: (props: typeof __VLS_13) => any;
15
15
  };
16
16
  declare const __VLS_base: import("vue").DefineComponent<ProsePreProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ProsePreProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
17
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;