@revenexx/studio-shared 0.4.0 → 0.4.1

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 (110) hide show
  1. package/dist/module.d.mts +5 -3
  2. package/dist/module.json +2 -2
  3. package/dist/module.mjs +7 -3
  4. package/dist/runtime/components/Dashboard/DashboardConfirmDialog.vue +1 -1
  5. package/dist/runtime/components/Dashboard/DashboardGrid.vue +45 -1
  6. package/dist/runtime/components/Dashboard/DashboardNameDialog.vue +1 -1
  7. package/dist/runtime/components/Dashboard/DashboardPickerDialog.vue +1 -1
  8. package/dist/runtime/components/Dashboard/DashboardShareDialog.vue +1 -1
  9. package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.vue +1 -1
  10. package/dist/runtime/components/Dashboard/DashboardWidgetFrame.d.vue.ts +2 -2
  11. package/dist/runtime/components/Dashboard/DashboardWidgetFrame.vue +14 -3
  12. package/dist/runtime/components/Dashboard/DashboardWidgetFrame.vue.d.ts +2 -2
  13. package/dist/runtime/components/DataTable/DataTable.d.vue.ts +73 -34
  14. package/dist/runtime/components/DataTable/DataTable.vue +352 -52
  15. package/dist/runtime/components/DataTable/DataTable.vue.d.ts +73 -34
  16. package/dist/runtime/components/DataTable/FacetPanel.vue +57 -8
  17. package/dist/runtime/components/DataTable/FilterMenu.vue +5 -5
  18. package/dist/runtime/components/DataTable/SavedViewsMenu.vue +61 -12
  19. package/dist/runtime/components/DataTable/SortMenu.vue +4 -1
  20. package/dist/runtime/components/DataTable/ToolbarLabelTrigger.d.vue.ts +23 -0
  21. package/dist/runtime/components/DataTable/ToolbarLabelTrigger.vue +29 -0
  22. package/dist/runtime/components/DataTable/ToolbarLabelTrigger.vue.d.ts +23 -0
  23. package/dist/runtime/components/DataTable/types.d.ts +17 -0
  24. package/dist/runtime/components/Form/FieldGrid.vue +1 -1
  25. package/dist/runtime/components/Form/SaveBar.d.vue.ts +1 -1
  26. package/dist/runtime/components/Form/SaveBar.vue.d.ts +1 -1
  27. package/dist/runtime/components/Media/MediaFolderTree.d.vue.ts +4 -4
  28. package/dist/runtime/components/Media/MediaFolderTree.vue +1 -2
  29. package/dist/runtime/components/Media/MediaFolderTree.vue.d.ts +4 -4
  30. package/dist/runtime/components/Media/MediaPickerDialog.d.vue.ts +11 -0
  31. package/dist/runtime/components/Media/MediaPickerDialog.vue +206 -54
  32. package/dist/runtime/components/Media/MediaPickerDialog.vue.d.ts +11 -0
  33. package/dist/runtime/components/Media/MediaUploader.vue +1 -1
  34. package/dist/runtime/components/Shared/AppIcon.d.vue.ts +1 -1
  35. package/dist/runtime/components/Shared/AppIcon.vue.d.ts +1 -1
  36. package/dist/runtime/components/Shared/CardSizeToggle.d.vue.ts +11 -0
  37. package/dist/runtime/components/Shared/CardSizeToggle.vue +19 -0
  38. package/dist/runtime/components/Shared/CardSizeToggle.vue.d.ts +11 -0
  39. package/dist/runtime/components/Shared/DetailSheet.d.vue.ts +1 -1
  40. package/dist/runtime/components/Shared/DetailSheet.vue +9 -5
  41. package/dist/runtime/components/Shared/DetailSheet.vue.d.ts +1 -1
  42. package/dist/runtime/components/Shared/EmptyNote.d.vue.ts +19 -0
  43. package/dist/runtime/components/Shared/EmptyNote.vue +25 -0
  44. package/dist/runtime/components/Shared/EmptyNote.vue.d.ts +19 -0
  45. package/dist/runtime/components/Shared/KpiTile.d.vue.ts +6 -0
  46. package/dist/runtime/components/Shared/KpiTile.vue +10 -9
  47. package/dist/runtime/components/Shared/KpiTile.vue.d.ts +6 -0
  48. package/dist/runtime/components/Shared/PageHeader.vue +15 -4
  49. package/dist/runtime/components/Shared/RecordLayout.vue +1 -2
  50. package/dist/runtime/components/Shared/RecordRailCard.vue +3 -3
  51. package/dist/runtime/components/Shared/SelectionBar.d.vue.ts +22 -0
  52. package/dist/runtime/components/Shared/SelectionBar.vue +40 -0
  53. package/dist/runtime/components/Shared/SelectionBar.vue.d.ts +22 -0
  54. package/dist/runtime/components/Shared/SidebarNav.d.vue.ts +36 -2
  55. package/dist/runtime/components/Shared/SidebarNav.vue +71 -25
  56. package/dist/runtime/components/Shared/SidebarNav.vue.d.ts +36 -2
  57. package/dist/runtime/components/Shared/SidebarNavAreas.d.vue.ts +7 -0
  58. package/dist/runtime/components/Shared/SidebarNavAreas.vue +291 -0
  59. package/dist/runtime/components/Shared/SidebarNavAreas.vue.d.ts +7 -0
  60. package/dist/runtime/components/Shared/SidebarNavLink.d.vue.ts +1 -9
  61. package/dist/runtime/components/Shared/SidebarNavLink.vue +31 -5
  62. package/dist/runtime/components/Shared/SidebarNavLink.vue.d.ts +1 -9
  63. package/dist/runtime/components/Shared/SortHeader.vue +6 -2
  64. package/dist/runtime/components/Shared/StateCard.vue +1 -1
  65. package/dist/runtime/components/Shared/TabBar.vue +6 -7
  66. package/dist/runtime/components/Shared/Wizard.d.vue.ts +11 -1
  67. package/dist/runtime/components/Shared/Wizard.vue +9 -5
  68. package/dist/runtime/components/Shared/Wizard.vue.d.ts +11 -1
  69. package/dist/runtime/composables/useMediaLibrary.d.ts +12 -0
  70. package/dist/runtime/composables/useSavedViews.d.ts +11 -1
  71. package/dist/runtime/composables/useSavedViews.js +4 -4
  72. package/dist/runtime/composables/useWidgetFigures.d.ts +12 -0
  73. package/dist/runtime/composables/useWidgetFigures.js +24 -0
  74. package/dist/runtime/composables/useWidgetHeader.d.ts +15 -0
  75. package/dist/runtime/composables/useWidgetHeader.js +15 -0
  76. package/dist/runtime/lib/auditEnvelope.d.ts +43 -0
  77. package/dist/runtime/lib/auditEnvelope.js +55 -0
  78. package/dist/runtime/lib/cardSize.d.ts +13 -0
  79. package/dist/runtime/lib/cardSize.js +20 -0
  80. package/dist/runtime/lib/dashboardIcons.js +2 -0
  81. package/dist/runtime/lib/dataTableFilters.d.ts +22 -0
  82. package/dist/runtime/lib/dataTableFilters.js +5 -0
  83. package/dist/runtime/lib/facets.d.ts +13 -0
  84. package/dist/runtime/lib/formLayout.d.ts +74 -4
  85. package/dist/runtime/lib/formLayout.js +53 -6
  86. package/dist/runtime/lib/gridTracks.d.ts +14 -1
  87. package/dist/runtime/lib/gridTracks.js +13 -2
  88. package/dist/runtime/lib/isoLists.d.ts +37 -2
  89. package/dist/runtime/lib/isoLists.js +54 -1
  90. package/dist/runtime/lib/notice.d.ts +14 -0
  91. package/dist/runtime/lib/notice.js +19 -0
  92. package/dist/runtime/lib/pageSize.d.ts +15 -0
  93. package/dist/runtime/lib/pageSize.js +26 -0
  94. package/dist/runtime/lib/sidebarAreas.d.ts +46 -0
  95. package/dist/runtime/lib/sidebarAreas.js +25 -0
  96. package/dist/runtime/lib/sidebarNav.d.ts +21 -2
  97. package/dist/runtime/lib/sidebarNav.js +23 -4
  98. package/dist/runtime/lib/solarIconMap.d.ts +2 -1
  99. package/dist/runtime/lib/solarIconMap.js +50 -4
  100. package/dist/runtime/lib/swipeToClose.d.ts +25 -0
  101. package/dist/runtime/lib/swipeToClose.js +118 -0
  102. package/dist/runtime/lib/tableExport.d.ts +31 -0
  103. package/dist/runtime/lib/tableExport.js +143 -0
  104. package/dist/runtime/lib/talkback.d.ts +5 -0
  105. package/dist/runtime/lib/talkback.js +8 -0
  106. package/dist/runtime/lib/talkbackTokenBatch.d.ts +35 -0
  107. package/dist/runtime/lib/talkbackTokenBatch.js +101 -0
  108. package/dist/runtime/types/nav.d.ts +9 -0
  109. package/dist/types.d.mts +4 -0
  110. package/package.json +4 -3
@@ -0,0 +1,25 @@
1
+ <script setup>
2
+ import { splitNotice } from "../../lib/notice";
3
+ const props = defineProps({
4
+ title: { type: [String, null], required: false },
5
+ description: { type: [String, null], required: false },
6
+ text: { type: [String, null], required: false }
7
+ });
8
+ const parts = computed(() => {
9
+ if (props.title) return { title: props.title, description: props.description || null };
10
+ const split = splitNotice(props.text || props.description || "");
11
+ return { title: split.title, description: split.description };
12
+ });
13
+ </script>
14
+
15
+ <template>
16
+ <div v-if="parts.title || parts.description" class="min-w-0 text-left" data-slot="empty-note">
17
+ <p v-if="parts.title" class="text-sm font-medium text-foreground">
18
+ {{ parts.title }}
19
+ </p>
20
+ <p v-if="parts.description" class="mt-0.5 text-xs text-muted-foreground">
21
+ {{ parts.description }}
22
+ </p>
23
+ <slot />
24
+ </div>
25
+ </template>
@@ -0,0 +1,19 @@
1
+ type __VLS_Props = {
2
+ title?: string | null;
3
+ description?: string | null;
4
+ /** One paragraph, split into headline and subtitle at its first sentence. */
5
+ text?: string | null;
6
+ };
7
+ declare var __VLS_1: {};
8
+ type __VLS_Slots = {} & {
9
+ default?: (props: typeof __VLS_1) => any;
10
+ };
11
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
13
+ declare const _default: typeof __VLS_export;
14
+ export default _default;
15
+ type __VLS_WithSlots<T, S> = T & {
16
+ new (): {
17
+ $slots: S;
18
+ };
19
+ };
@@ -7,6 +7,12 @@ type __VLS_Props = {
7
7
  up?: boolean;
8
8
  /** Muted line under the value (e.g. "vs last month"). */
9
9
  sub?: string;
10
+ /**
11
+ * Keep the sub line to one line and cut it with an ellipsis; the caller puts
12
+ * the full text in a tooltip (the Commerce tiles, Michael 19.10.: „kompakter,
13
+ * der Untertitel … bricht um“).
14
+ */
15
+ subOneLine?: boolean;
10
16
  to?: string;
11
17
  };
12
18
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -7,26 +7,27 @@ defineProps({
7
7
  delta: { type: String, required: false },
8
8
  up: { type: Boolean, required: false },
9
9
  sub: { type: String, required: false },
10
+ subOneLine: { type: Boolean, required: false },
10
11
  to: { type: String, required: false }
11
12
  });
12
13
  </script>
13
14
 
14
15
  <template>
16
+ <!-- The home dashboard's KPI card (KpiStrip), shared: 2-layer glass, mono
17
+ caps label, big figure, change + note in one line, centred. The icon is
18
+ no longer drawn — the dashboard design has none (Michael 12.10.). -->
15
19
  <component
16
20
  :is="to ? NuxtLink : 'div'"
17
21
  :to="to"
18
22
  class="block rounded-lg border border-gray-400/10 outline-1 outline-white/30"
19
23
  :class="to ? 'transition-transform hover:-translate-y-0.5' : ''"
20
24
  >
21
- <div class="relative h-full overflow-hidden rounded-lg bg-white p-4">
22
- <div class="flex items-center justify-between gap-2">
23
- <span class="text-xs text-muted-foreground truncate">{{ label }}</span>
24
- <span v-if="icon" class="flex size-7 items-center justify-center rounded-md bg-primary/10 text-primary shrink-0"><component :is="icon" :size="14" /></span>
25
- </div>
26
- <div class="mt-1.5 text-2xl font-semibold leading-none tabular-nums text-[#0B0522]">{{ value }}</div>
27
- <div v-if="delta || sub" class="mt-1.5 text-xs">
28
- <span v-if="delta" class="font-medium tabular-nums" :class="up ? 'text-emerald-600' : 'text-rose-600'">{{ delta }}</span>
29
- <span v-if="sub" class="text-muted-foreground" :class="delta ? 'ml-1' : ''">{{ sub }}</span>
25
+ <div class="relative flex h-full flex-col justify-start overflow-hidden rounded-lg bg-white p-4">
26
+ <div class="text-mono line-clamp-2 leading-[1.4] text-muted-foreground">{{ label }}</div>
27
+ <div class="mt-1 truncate text-2xl font-semibold tabular-nums text-[#0B0522]">{{ value }}</div>
28
+ <div v-if="delta || sub" class="mt-1 flex min-w-0 items-start gap-1 text-sm">
29
+ <span v-if="delta" class="shrink-0 font-medium tabular-nums" :class="up ? 'text-emerald-600' : 'text-rose-600'">{{ delta }}</span>
30
+ <span v-if="sub" class="text-muted-foreground" :class="subOneLine ? 'min-w-0 truncate' : 'line-clamp-2'">{{ delta ? "\xB7 " : "" }}{{ sub }}</span>
30
31
  </div>
31
32
  </div>
32
33
  </component>
@@ -7,6 +7,12 @@ type __VLS_Props = {
7
7
  up?: boolean;
8
8
  /** Muted line under the value (e.g. "vs last month"). */
9
9
  sub?: string;
10
+ /**
11
+ * Keep the sub line to one line and cut it with an ellipsis; the caller puts
12
+ * the full text in a tooltip (the Commerce tiles, Michael 19.10.: „kompakter,
13
+ * der Untertitel … bricht um“).
14
+ */
15
+ subOneLine?: boolean;
10
16
  to?: string;
11
17
  };
12
18
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -7,8 +7,14 @@ defineProps({
7
7
  </script>
8
8
 
9
9
  <template>
10
- <div class="sticky top-0 z-20 border-b border-black/[0.06] bg-white/40 backdrop-blur-xl">
11
- <div class="flex items-center gap-2 px-4 pt-4 pb-3">
10
+ <!-- Opaque, in the shell's ground: a see-through header let the record's
11
+ content run visibly behind its title on scroll (customers-09; tried
12
+ translucent again 13.10., dropped). The fallback keeps it opaque in a
13
+ host that defines no shell token. -->
14
+ <div class="sticky top-0 z-20 border-b border-black/[0.06] bg-[var(--color-shell,#f3f1fa)]">
15
+ <!-- Phone: the title keeps its own row and the actions wrap below it
16
+ (R3/R5) instead of squeezing the title to nothing. -->
17
+ <div class="flex min-h-14 items-center gap-2 px-4 pt-4 pb-3" data-rx-header-row>
12
18
  <component
13
19
  :is="icon"
14
20
  v-if="icon"
@@ -16,13 +22,18 @@ defineProps({
16
22
  :stroke-width="2"
17
23
  class="shrink-0 text-foreground/70"
18
24
  />
19
- <h1 class="text-xl font-medium text-[#0B0522] truncate">
25
+ <h1 class="min-w-0 text-xl font-medium text-[#0B0522] truncate">
20
26
  <slot name="title">{{ title }}</slot>
21
27
  </h1>
22
28
 
23
29
  <div class="flex-1" />
24
30
 
25
- <slot name="actions" />
31
+ <!-- One rhythm for every page: 8px between buttons, and the 32px
32
+ buttons tucked into the 28px title line so the header's hairline
33
+ meets the sidebar's whatever a page puts here (Michael 13.10.). -->
34
+ <div v-if="$slots.actions" class="-my-0.5 flex shrink-0 items-center gap-2">
35
+ <slot name="actions" />
36
+ </div>
26
37
  </div>
27
38
 
28
39
  <div v-if="$slots.tabs" class="flex items-center gap-1 px-3 pb-2 overflow-x-auto">
@@ -37,8 +37,7 @@ const narrowOpen = ref(props.railOpenOnNarrow);
37
37
  <AltArrowDown
38
38
  :size="14"
39
39
  class="shrink-0 transition-transform"
40
- :class="narrowOpen ? 'rotate-180' : ''"
41
- />
40
+ :class="narrowOpen ? 'rotate-180' : ''" weight="Bold" />
42
41
  </button>
43
42
 
44
43
  <div :class="narrowOpen ? 'space-y-3' : 'hidden space-y-3 xl:block'">
@@ -5,12 +5,12 @@ defineProps({
5
5
  </script>
6
6
 
7
7
  <template>
8
- <section class="overflow-hidden rounded-lg border border-gray-400/10 bg-white/60 outline-1 outline-white/30 backdrop-blur-sm">
8
+ <section class="rx-card">
9
9
  <div
10
10
  v-if="title || $slots.actions"
11
- class="flex items-center justify-between gap-2 border-b border-black/[0.06] px-4 py-2.5"
11
+ class="flex items-center justify-between gap-2 rx-card-head px-4"
12
12
  >
13
- <h3 class="truncate text-xs font-semibold uppercase tracking-wide text-muted-foreground">
13
+ <h3 class="truncate text-sm font-semibold text-[#0B0522]">
14
14
  {{ title }}
15
15
  </h3>
16
16
  <slot name="actions" />
@@ -0,0 +1,22 @@
1
+ type __VLS_Props = {
2
+ /** How many are picked; 0 hides the box. */
3
+ count: number;
4
+ busy?: boolean;
5
+ };
6
+ declare var __VLS_15: {};
7
+ type __VLS_Slots = {} & {
8
+ default?: (props: typeof __VLS_15) => any;
9
+ };
10
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ clear: () => any;
12
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
13
+ onClear?: (() => any) | undefined;
14
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
18
+ type __VLS_WithSlots<T, S> = T & {
19
+ new (): {
20
+ $slots: S;
21
+ };
22
+ };
@@ -0,0 +1,40 @@
1
+ <script setup>
2
+ import { Button } from "@revenexx/studio";
3
+ import { useT } from "../../composables/useT";
4
+ const props = defineProps({
5
+ count: { type: Number, required: true },
6
+ busy: { type: Boolean, required: false }
7
+ });
8
+ const emit = defineEmits(["clear"]);
9
+ const t = useT();
10
+ </script>
11
+
12
+ <template>
13
+ <Transition
14
+ enter-active-class="transition duration-150 ease-out"
15
+ enter-from-class="translate-y-2 opacity-0"
16
+ leave-active-class="transition duration-100 ease-in"
17
+ leave-to-class="translate-y-2 opacity-0"
18
+ >
19
+ <div
20
+ v-if="props.count > 0"
21
+ class="pointer-events-none sticky bottom-[max(1rem,env(safe-area-inset-bottom))] z-20 -mt-14 flex justify-center px-2"
22
+ data-test="selection-bar"
23
+ >
24
+ <div
25
+ role="region"
26
+ :aria-label="t('shared.selection.label')"
27
+ class="pointer-events-auto flex max-w-full flex-wrap items-center gap-2 rounded-lg border border-black/[0.06] bg-white px-3 py-2 shadow-[0_8px_24px_rgba(11,5,34,0.14)]"
28
+ >
29
+ <span class="px-1 text-sm font-medium tabular-nums text-[#0B0522]" aria-live="polite">
30
+ {{ t("shared.selection.count", { n: props.count }, props.count) }}
31
+ </span>
32
+ <Button variant="ghost" size="sm" :disabled="busy" data-test="selection-clear" @click="emit('clear')">
33
+ {{ t("shared.selection.clear") }}
34
+ </Button>
35
+ <span v-if="$slots.default" class="hidden h-5 w-px bg-black/[0.08] sm:block" aria-hidden="true" />
36
+ <slot />
37
+ </div>
38
+ </div>
39
+ </Transition>
40
+ </template>
@@ -0,0 +1,22 @@
1
+ type __VLS_Props = {
2
+ /** How many are picked; 0 hides the box. */
3
+ count: number;
4
+ busy?: boolean;
5
+ };
6
+ declare var __VLS_15: {};
7
+ type __VLS_Slots = {} & {
8
+ default?: (props: typeof __VLS_15) => any;
9
+ };
10
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ clear: () => any;
12
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
13
+ onClear?: (() => any) | undefined;
14
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
18
+ type __VLS_WithSlots<T, S> = T & {
19
+ new (): {
20
+ $slots: S;
21
+ };
22
+ };
@@ -22,13 +22,47 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
22
22
  sections: SidebarNavSection[];
23
23
  /** Hide the header icon and use a lighter header title weight. */
24
24
  minimal?: boolean;
25
- }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
25
+ /**
26
+ * Icon-only rail (tablet / small laptop, R2): rows shrink to their icon
27
+ * with the label as tooltip, section labels become hairlines.
28
+ */
29
+ collapsed?: boolean;
30
+ /** Show the collapse/expand toggle in the header. */
31
+ collapsible?: boolean;
32
+ /** Drop the header row (the mobile drawer brings its own). */
33
+ hideHeader?: boolean;
34
+ /**
35
+ * Area mode: only the route's section shows its rows, the others are one
36
+ * row each that lays a panel next to the sidebar (`SidebarNavAreas`). The
37
+ * icon rail and the drawer keep the full list.
38
+ */
39
+ areas?: boolean;
40
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
41
+ toggle: () => any;
42
+ }, string, import("vue").PublicProps, Readonly<{
26
43
  title: string;
27
44
  icon?: Component;
28
45
  sections: SidebarNavSection[];
29
46
  /** Hide the header icon and use a lighter header title weight. */
30
47
  minimal?: boolean;
31
- }> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
48
+ /**
49
+ * Icon-only rail (tablet / small laptop, R2): rows shrink to their icon
50
+ * with the label as tooltip, section labels become hairlines.
51
+ */
52
+ collapsed?: boolean;
53
+ /** Show the collapse/expand toggle in the header. */
54
+ collapsible?: boolean;
55
+ /** Drop the header row (the mobile drawer brings its own). */
56
+ hideHeader?: boolean;
57
+ /**
58
+ * Area mode: only the route's section shows its rows, the others are one
59
+ * row each that lays a panel next to the sidebar (`SidebarNavAreas`). The
60
+ * icon rail and the drawer keep the full list.
61
+ */
62
+ areas?: boolean;
63
+ }> & Readonly<{
64
+ onToggle?: (() => any) | undefined;
65
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
32
66
  icon?: (props: {}) => any;
33
67
  } & {
34
68
  top?: (props: {}) => any;
@@ -3,46 +3,92 @@
3
3
  </script>
4
4
 
5
5
  <script setup>
6
- import { sidebarItemKey } from "../../lib/sidebarNav";
6
+ import { SidebarMinimalistic as CollapseIcon } from "#solar-icons";
7
+ import { SIDEBAR_BEST_MATCH, sidebarBestMatch, sidebarItemKey } from "../../lib/sidebarNav";
7
8
  import SidebarNavLink from "./SidebarNavLink.vue";
8
- defineProps({
9
+ import SidebarNavAreas from "./SidebarNavAreas.vue";
10
+ const props = defineProps({
9
11
  title: { type: String, required: true },
10
12
  icon: { type: null, required: false },
11
13
  sections: { type: Array, required: true },
12
- minimal: { type: Boolean, required: false }
14
+ minimal: { type: Boolean, required: false },
15
+ collapsed: { type: Boolean, required: false },
16
+ collapsible: { type: Boolean, required: false },
17
+ hideHeader: { type: Boolean, required: false },
18
+ areas: { type: Boolean, required: false }
13
19
  });
20
+ const emit = defineEmits(["toggle"]);
21
+ const areaMode = computed(() => !!props.areas && !props.collapsed && !props.hideHeader);
22
+ const t = useT();
23
+ const scroller = ref(null);
24
+ const route = useRoute();
25
+ function revealCurrent() {
26
+ nextTick(() => {
27
+ const row = scroller.value?.querySelector("[data-nav-current]");
28
+ row?.scrollIntoView({ block: "nearest" });
29
+ });
30
+ }
31
+ provide(SIDEBAR_BEST_MATCH, computed(() => sidebarBestMatch(props.sections, route.fullPath)));
32
+ onMounted(revealCurrent);
33
+ watch(() => [route.fullPath, props.sections], revealCurrent);
14
34
  </script>
15
35
 
16
36
  <template>
17
37
  <aside class="h-full flex flex-col">
18
- <div class="flex items-center gap-2 px-4 pt-4 pb-3 shrink-0 border-b border-black/[0.06]">
19
- <slot v-if="!minimal" name="icon">
20
- <component :is="icon" v-if="icon" :size="18" :stroke-width="2" class="text-foreground/70 shrink-0" />
21
- </slot>
22
- <h2 class="text-lg text-[#0B0522] truncate" :class="minimal ? 'font-medium' : 'font-semibold'">
23
- {{ title }}
24
- </h2>
38
+ <div
39
+ v-if="!hideHeader"
40
+ class="flex items-center gap-2 pt-4 pb-3 shrink-0 border-b border-black/[0.06]"
41
+ :class="collapsed ? 'justify-center px-2' : 'px-4'"
42
+ >
43
+ <template v-if="!collapsed">
44
+ <slot v-if="!minimal" name="icon">
45
+ <component :is="icon" v-if="icon" :size="18" :stroke-width="2" class="text-foreground/70 shrink-0" />
46
+ </slot>
47
+ <h2 class="min-w-0 flex-1 text-lg text-[#0B0522] truncate" :class="minimal ? 'font-medium' : 'font-semibold'">
48
+ {{ title }}
49
+ </h2>
50
+ </template>
51
+ <button
52
+ v-if="collapsible"
53
+ type="button"
54
+ class="flex size-7 shrink-0 items-center justify-center rounded-md text-foreground/60 transition-colors hover:bg-primary/5 hover:text-primary"
55
+ :title="collapsed ? t('shared.nav.action.expand') : t('shared.nav.action.collapse')"
56
+ :aria-label="collapsed ? t('shared.nav.action.expand') : t('shared.nav.action.collapse')"
57
+ :aria-expanded="!collapsed"
58
+ @click="emit('toggle')"
59
+ >
60
+ <CollapseIcon :size="16" :class="collapsed ? 'rotate-180' : ''" />
61
+ </button>
25
62
  </div>
26
63
 
27
- <div class="flex-1 overflow-y-auto pt-3 pb-3">
28
- <slot name="top" />
64
+ <!-- Area mode spaces its own blocks (py-1 between hairlines), so the first
65
+ row sits as far below the title rule as above the next hairline. -->
66
+ <div ref="scroller" class="flex-1 overflow-y-auto pb-3" :class="areaMode ? '' : 'pt-3'">
67
+ <slot v-if="!collapsed" name="top" />
29
68
 
30
- <template v-for="section in sections" :key="section.key">
31
- <div v-if="section.label" class="mt-3 px-4 pb-1">
32
- <div class="text-mono text-muted-foreground">
33
- {{ section.label }}
69
+ <SidebarNavAreas v-if="areaMode" :sections="sections" />
70
+ <template v-else>
71
+ <template v-for="(section, i) in sections" :key="section.key">
72
+ <template v-if="collapsed">
73
+ <div v-if="i > 0" class="mx-3 my-2 border-t border-black/[0.06]" />
74
+ </template>
75
+ <div v-else-if="section.label" class="mt-3 px-4 pb-1">
76
+ <div class="text-mono text-muted-foreground">
77
+ {{ section.label }}
78
+ </div>
34
79
  </div>
35
- </div>
36
- <nav class="flex flex-col gap-0.5 px-2">
37
- <SidebarNavLink
38
- v-for="item in section.items"
39
- :key="sidebarItemKey(item)"
40
- :item="item"
41
- />
42
- </nav>
80
+ <nav class="flex flex-col gap-0.5" :class="collapsed ? 'items-center px-1.5' : 'px-2'">
81
+ <template v-for="item in section.items" :key="sidebarItemKey(item)">
82
+ <div v-if="item.heading && !collapsed" class="px-2 pb-0.5 pt-2 text-mono text-muted-foreground">
83
+ {{ item.heading }}
84
+ </div>
85
+ <SidebarNavLink :item="item" :collapsed="collapsed" />
86
+ </template>
87
+ </nav>
88
+ </template>
43
89
  </template>
44
90
 
45
- <slot name="notice" />
91
+ <slot v-if="!collapsed" name="notice" />
46
92
  </div>
47
93
  </aside>
48
94
  </template>
@@ -22,13 +22,47 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
22
22
  sections: SidebarNavSection[];
23
23
  /** Hide the header icon and use a lighter header title weight. */
24
24
  minimal?: boolean;
25
- }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
25
+ /**
26
+ * Icon-only rail (tablet / small laptop, R2): rows shrink to their icon
27
+ * with the label as tooltip, section labels become hairlines.
28
+ */
29
+ collapsed?: boolean;
30
+ /** Show the collapse/expand toggle in the header. */
31
+ collapsible?: boolean;
32
+ /** Drop the header row (the mobile drawer brings its own). */
33
+ hideHeader?: boolean;
34
+ /**
35
+ * Area mode: only the route's section shows its rows, the others are one
36
+ * row each that lays a panel next to the sidebar (`SidebarNavAreas`). The
37
+ * icon rail and the drawer keep the full list.
38
+ */
39
+ areas?: boolean;
40
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
41
+ toggle: () => any;
42
+ }, string, import("vue").PublicProps, Readonly<{
26
43
  title: string;
27
44
  icon?: Component;
28
45
  sections: SidebarNavSection[];
29
46
  /** Hide the header icon and use a lighter header title weight. */
30
47
  minimal?: boolean;
31
- }> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
48
+ /**
49
+ * Icon-only rail (tablet / small laptop, R2): rows shrink to their icon
50
+ * with the label as tooltip, section labels become hairlines.
51
+ */
52
+ collapsed?: boolean;
53
+ /** Show the collapse/expand toggle in the header. */
54
+ collapsible?: boolean;
55
+ /** Drop the header row (the mobile drawer brings its own). */
56
+ hideHeader?: boolean;
57
+ /**
58
+ * Area mode: only the route's section shows its rows, the others are one
59
+ * row each that lays a panel next to the sidebar (`SidebarNavAreas`). The
60
+ * icon rail and the drawer keep the full list.
61
+ */
62
+ areas?: boolean;
63
+ }> & Readonly<{
64
+ onToggle?: (() => any) | undefined;
65
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
32
66
  icon?: (props: {}) => any;
33
67
  } & {
34
68
  top?: (props: {}) => any;
@@ -0,0 +1,7 @@
1
+ import type { SidebarNavSection } from '../../types/nav.js';
2
+ type __VLS_Props = {
3
+ sections: SidebarNavSection[];
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
+ declare const _default: typeof __VLS_export;
7
+ export default _default;