@revenexx/studio-shared 0.2.0 → 0.3.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 (86) hide show
  1. package/dist/module.d.mts +7 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +37 -1
  4. package/dist/runtime/components/DataTable/ColumnsMenu.d.vue.ts +2 -0
  5. package/dist/runtime/components/DataTable/ColumnsMenu.vue +93 -36
  6. package/dist/runtime/components/DataTable/ColumnsMenu.vue.d.ts +2 -0
  7. package/dist/runtime/components/DataTable/DataTable.d.vue.ts +53 -4
  8. package/dist/runtime/components/DataTable/DataTable.vue +274 -118
  9. package/dist/runtime/components/DataTable/DataTable.vue.d.ts +53 -4
  10. package/dist/runtime/components/DataTable/FilterMenu.d.vue.ts +14 -3
  11. package/dist/runtime/components/DataTable/FilterMenu.vue +35 -29
  12. package/dist/runtime/components/DataTable/FilterMenu.vue.d.ts +14 -3
  13. package/dist/runtime/components/DataTable/SavedViewsMenu.d.vue.ts +6 -0
  14. package/dist/runtime/components/DataTable/SavedViewsMenu.vue +189 -0
  15. package/dist/runtime/components/DataTable/SavedViewsMenu.vue.d.ts +6 -0
  16. package/dist/runtime/components/DataTable/SortMenu.d.vue.ts +13 -0
  17. package/dist/runtime/components/DataTable/SortMenu.vue +47 -0
  18. package/dist/runtime/components/DataTable/SortMenu.vue.d.ts +13 -0
  19. package/dist/runtime/components/DataTable/ViewModeSwitch.d.vue.ts +20 -0
  20. package/dist/runtime/components/DataTable/ViewModeSwitch.vue +26 -0
  21. package/dist/runtime/components/DataTable/ViewModeSwitch.vue.d.ts +20 -0
  22. package/dist/runtime/components/DataTable/types.d.ts +49 -2
  23. package/dist/runtime/components/Media/MediaFolderTree.d.vue.ts +17 -0
  24. package/dist/runtime/components/Media/MediaFolderTree.vue +65 -0
  25. package/dist/runtime/components/Media/MediaFolderTree.vue.d.ts +17 -0
  26. package/dist/runtime/components/Media/MediaPickerDialog.d.vue.ts +18 -0
  27. package/dist/runtime/components/Media/MediaPickerDialog.vue +163 -0
  28. package/dist/runtime/components/Media/MediaPickerDialog.vue.d.ts +18 -0
  29. package/dist/runtime/components/Media/MediaUploader.d.vue.ts +29 -0
  30. package/dist/runtime/components/Media/MediaUploader.vue +161 -0
  31. package/dist/runtime/components/Media/MediaUploader.vue.d.ts +29 -0
  32. package/dist/runtime/components/Shared/BuildLogsFlyout.vue +14 -13
  33. package/dist/runtime/components/Shared/FloatingPanel.d.vue.ts +55 -0
  34. package/dist/runtime/components/Shared/FloatingPanel.vue +118 -0
  35. package/dist/runtime/components/Shared/FloatingPanel.vue.d.ts +55 -0
  36. package/dist/runtime/components/Shared/GitDeploymentSettings.vue +11 -10
  37. package/dist/runtime/components/Shared/ListSurface.d.vue.ts +38 -0
  38. package/dist/runtime/components/Shared/ListSurface.vue +27 -0
  39. package/dist/runtime/components/Shared/ListSurface.vue.d.ts +38 -0
  40. package/dist/runtime/components/Shared/RecordLayout.d.vue.ts +52 -0
  41. package/dist/runtime/components/Shared/RecordLayout.vue +63 -0
  42. package/dist/runtime/components/Shared/RecordLayout.vue.d.ts +52 -0
  43. package/dist/runtime/components/Shared/RecordRailCard.d.vue.ts +26 -0
  44. package/dist/runtime/components/Shared/RecordRailCard.vue +20 -0
  45. package/dist/runtime/components/Shared/RecordRailCard.vue.d.ts +26 -0
  46. package/dist/runtime/components/Shared/SidebarNav.d.vue.ts +5 -0
  47. package/dist/runtime/components/Shared/SidebarNav.vue +6 -26
  48. package/dist/runtime/components/Shared/SidebarNav.vue.d.ts +5 -0
  49. package/dist/runtime/components/Shared/SidebarNavLink.d.vue.ts +15 -0
  50. package/dist/runtime/components/Shared/SidebarNavLink.vue +49 -0
  51. package/dist/runtime/components/Shared/SidebarNavLink.vue.d.ts +15 -0
  52. package/dist/runtime/components/Shared/Wizard.vue +6 -3
  53. package/dist/runtime/composables/useFloatingPanel.d.ts +69 -0
  54. package/dist/runtime/composables/useFloatingPanel.js +112 -0
  55. package/dist/runtime/composables/useListRail.d.ts +9 -0
  56. package/dist/runtime/composables/useListRail.js +19 -0
  57. package/dist/runtime/composables/useMediaLibrary.d.ts +41 -0
  58. package/dist/runtime/composables/useMediaLibrary.js +36 -0
  59. package/dist/runtime/composables/usePlatformAuth.d.ts +18 -0
  60. package/dist/runtime/composables/usePlatformTalkback.d.ts +29 -0
  61. package/dist/runtime/composables/usePlatformTalkback.js +54 -0
  62. package/dist/runtime/composables/useT.d.ts +11 -0
  63. package/dist/runtime/composables/useT.js +8 -0
  64. package/dist/runtime/composables/useTalkbackChannels.d.ts +93 -0
  65. package/dist/runtime/composables/useTalkbackChannels.js +56 -0
  66. package/dist/runtime/lib/columnOrder.d.ts +45 -0
  67. package/dist/runtime/lib/columnOrder.js +28 -0
  68. package/dist/runtime/lib/columnVisibility.d.ts +41 -0
  69. package/dist/runtime/lib/columnVisibility.js +12 -0
  70. package/dist/runtime/lib/dataTableFilters.d.ts +83 -0
  71. package/dist/runtime/lib/dataTableFilters.js +23 -0
  72. package/dist/runtime/lib/formatBridge.d.ts +50 -0
  73. package/dist/runtime/lib/formatBridge.js +54 -0
  74. package/dist/runtime/lib/i18nBridge.d.ts +36 -0
  75. package/dist/runtime/lib/i18nBridge.js +17 -0
  76. package/dist/runtime/lib/isoLists.d.ts +61 -0
  77. package/dist/runtime/lib/isoLists.js +62 -0
  78. package/dist/runtime/lib/sidebarNav.d.ts +34 -0
  79. package/dist/runtime/lib/sidebarNav.js +22 -0
  80. package/dist/runtime/lib/talkback.d.ts +59 -0
  81. package/dist/runtime/lib/talkback.js +49 -0
  82. package/dist/runtime/types/nav.d.ts +15 -1
  83. package/dist/runtime/types/storage.d.ts +67 -0
  84. package/dist/runtime/types/storage.js +0 -0
  85. package/dist/types.d.mts +14 -0
  86. package/package.json +19 -3
@@ -0,0 +1,55 @@
1
+ type Mode = 'docked-left' | 'docked-right' | 'floating';
2
+ type __VLS_Props = {
3
+ /** What `useFloatingPanel` returned, passed through whole. */
4
+ panel: {
5
+ mode: {
6
+ value: Mode;
7
+ };
8
+ collapsed: {
9
+ value: boolean;
10
+ };
11
+ floatingStyle: {
12
+ value: Record<string, string>;
13
+ };
14
+ dragging: {
15
+ value: boolean;
16
+ };
17
+ startDrag: (ev: PointerEvent, el: HTMLElement | null) => void;
18
+ dock: (side: 'left' | 'right') => void;
19
+ floatHere: (el: HTMLElement | null) => void;
20
+ setCollapsed: (v: boolean) => void;
21
+ };
22
+ title: string;
23
+ /** Room the docked panel leaves at the bottom, for a floating control bar. */
24
+ bottomClass?: string;
25
+ /** Tailwind width, matching the `width` given to the composable. */
26
+ widthClass?: string;
27
+ /** Labels, so the package carries no copy of its own. */
28
+ labels: {
29
+ dockLeft: string;
30
+ dockRight: string;
31
+ floatHere: string;
32
+ collapse: string;
33
+ expand: string;
34
+ placement: string;
35
+ };
36
+ };
37
+ declare var __VLS_5: {}, __VLS_67: {}, __VLS_69: {};
38
+ type __VLS_Slots = {} & {
39
+ 'pill-icon'?: (props: typeof __VLS_5) => any;
40
+ } & {
41
+ default?: (props: typeof __VLS_67) => any;
42
+ } & {
43
+ footer?: (props: typeof __VLS_69) => any;
44
+ };
45
+ declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
46
+ bottomClass: string;
47
+ widthClass: string;
48
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
49
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
50
+ export default _default;
51
+ type __VLS_WithSlots<T, S> = T & {
52
+ new (): {
53
+ $slots: S;
54
+ };
55
+ };
@@ -20,38 +20,39 @@ defineProps({
20
20
  showPreviews: { type: Boolean, required: false, default: true }
21
21
  });
22
22
  defineEmits(["update:triggerMode", "update:previewsEnabled"]);
23
+ const t = useT();
23
24
  </script>
24
25
 
25
26
  <template>
26
27
  <Card class="bg-white/60 backdrop-blur-sm">
27
28
  <CardHeader>
28
- <CardTitle class="text-sm">Deployment triggers</CardTitle>
29
+ <CardTitle class="text-sm">{{ t("shared.git.settings.title") }}</CardTitle>
29
30
  <CardDescription class="text-sm">
30
- Control when a git push deploys this resource.
31
+ {{ t("shared.git.settings.description") }}
31
32
  </CardDescription>
32
33
  </CardHeader>
33
34
  <CardContent class="space-y-4">
34
35
  <div class="space-y-1">
35
- <Label for="vcs-trigger-mode">When to deploy</Label>
36
+ <Label for="vcs-trigger-mode">{{ t("shared.git.settings.field.when") }}</Label>
36
37
  <Select
37
38
  :model-value="triggerMode"
38
39
  :disabled="disabled"
39
40
  @update:model-value="(v) => $emit('update:triggerMode', v)"
40
41
  >
41
42
  <SelectTrigger id="vcs-trigger-mode">
42
- <SelectValue placeholder="On every push" />
43
+ <SelectValue :placeholder="t('shared.git.settings.select.placeholder')" />
43
44
  </SelectTrigger>
44
45
  <SelectContent>
45
- <SelectItem value="push">On every push</SelectItem>
46
- <SelectItem value="tag">Only on tag / release</SelectItem>
46
+ <SelectItem value="push">{{ t("shared.git.settings.option.push") }}</SelectItem>
47
+ <SelectItem value="tag">{{ t("shared.git.settings.option.tag") }}</SelectItem>
47
48
  </SelectContent>
48
49
  </Select>
49
50
  <p class="text-sm text-muted-foreground">
50
51
  <template v-if="triggerMode === 'tag'">
51
- Builds only when you push a tag or publish a GitHub release.
52
+ {{ t("shared.git.settings.hint.tag") }}
52
53
  </template>
53
54
  <template v-else>
54
- Builds on every push to the production branch.
55
+ {{ t("shared.git.settings.hint.push") }}
55
56
  </template>
56
57
  </p>
57
58
  </div>
@@ -63,9 +64,9 @@ defineEmits(["update:triggerMode", "update:previewsEnabled"]);
63
64
  @update:model-value="(v) => $emit('update:previewsEnabled', v)"
64
65
  />
65
66
  <div class="text-sm">
66
- <div class="font-medium">Preview deployments</div>
67
+ <div class="font-medium">{{ t("shared.git.settings.previews.title") }}</div>
67
68
  <div class="text-sm text-muted-foreground">
68
- Build a preview and comment the status on every pull request.
69
+ {{ t("shared.git.settings.previews.description") }}
69
70
  </div>
70
71
  </div>
71
72
  </div>
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The frame every list surface with a left rail shares: an optional,
3
+ * collapsible rail column, the list itself, and an optional aside (a detail
4
+ * flyout column).
5
+ *
6
+ * The collapse *control* lives in the DataTable toolbar (`rail` prop +
7
+ * `v-model:rail-open`) so a surface never grows a second control row; this
8
+ * component only reacts to the same boolean. Use `useListRail()` for a state
9
+ * that survives a reload.
10
+ */
11
+ type __VLS_Props = {
12
+ /** Rail visible? Bind the same ref the DataTable's `rail-open` model has. */
13
+ railOpen?: boolean;
14
+ /** Rail track width (default 240px). */
15
+ railWidth?: string;
16
+ /** Aside track width (default 280px). */
17
+ asideWidth?: string;
18
+ };
19
+ declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {};
20
+ type __VLS_Slots = {} & {
21
+ rail?: (props: typeof __VLS_1) => any;
22
+ } & {
23
+ default?: (props: typeof __VLS_3) => any;
24
+ } & {
25
+ aside?: (props: typeof __VLS_5) => any;
26
+ };
27
+ declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
28
+ railOpen: boolean;
29
+ railWidth: string;
30
+ asideWidth: string;
31
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
32
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
33
+ export default _default;
34
+ type __VLS_WithSlots<T, S> = T & {
35
+ new (): {
36
+ $slots: S;
37
+ };
38
+ };
@@ -0,0 +1,27 @@
1
+ <script setup>
2
+ defineProps({
3
+ railOpen: { type: Boolean, required: false, default: true },
4
+ railWidth: { type: String, required: false, default: "240px" },
5
+ asideWidth: { type: String, required: false, default: "280px" }
6
+ });
7
+ const slots = useSlots();
8
+ const hasAside = computed(() => !!slots.aside);
9
+ </script>
10
+
11
+ <template>
12
+ <div
13
+ class="grid grid-cols-1 items-start gap-3"
14
+ :style="{ '--rail-w': railWidth, '--aside-w': asideWidth }"
15
+ :class="railOpen ? hasAside ? 'lg:grid-cols-[var(--rail-w)_minmax(0,1fr)_var(--aside-w)]' : 'lg:grid-cols-[var(--rail-w)_minmax(0,1fr)]' : hasAside ? 'lg:grid-cols-[minmax(0,1fr)_var(--aside-w)]' : 'lg:grid-cols-1'"
16
+ >
17
+ <div v-if="railOpen" class="min-w-0">
18
+ <slot name="rail" />
19
+ </div>
20
+ <div class="min-w-0">
21
+ <slot />
22
+ </div>
23
+ <div v-if="hasAside" class="min-w-0">
24
+ <slot name="aside" />
25
+ </div>
26
+ </div>
27
+ </template>
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The frame every list surface with a left rail shares: an optional,
3
+ * collapsible rail column, the list itself, and an optional aside (a detail
4
+ * flyout column).
5
+ *
6
+ * The collapse *control* lives in the DataTable toolbar (`rail` prop +
7
+ * `v-model:rail-open`) so a surface never grows a second control row; this
8
+ * component only reacts to the same boolean. Use `useListRail()` for a state
9
+ * that survives a reload.
10
+ */
11
+ type __VLS_Props = {
12
+ /** Rail visible? Bind the same ref the DataTable's `rail-open` model has. */
13
+ railOpen?: boolean;
14
+ /** Rail track width (default 240px). */
15
+ railWidth?: string;
16
+ /** Aside track width (default 280px). */
17
+ asideWidth?: string;
18
+ };
19
+ declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {};
20
+ type __VLS_Slots = {} & {
21
+ rail?: (props: typeof __VLS_1) => any;
22
+ } & {
23
+ default?: (props: typeof __VLS_3) => any;
24
+ } & {
25
+ aside?: (props: typeof __VLS_5) => any;
26
+ };
27
+ declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
28
+ railOpen: boolean;
29
+ railWidth: string;
30
+ asideWidth: string;
31
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
32
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
33
+ export default _default;
34
+ type __VLS_WithSlots<T, S> = T & {
35
+ new (): {
36
+ $slots: S;
37
+ };
38
+ };
@@ -0,0 +1,52 @@
1
+ export interface RecordTab {
2
+ key: string;
3
+ label: string;
4
+ badge?: number | string | null;
5
+ }
6
+ type __VLS_Props = {
7
+ /** Tabs for the body column. Empty = render no tab bar. */
8
+ tabs?: RecordTab[];
9
+ /** Active tab key (`v-model`). */
10
+ modelValue?: string;
11
+ /** Rail track width. 320px reads as a column; below ~280 it reads as a list. */
12
+ railWidth?: string;
13
+ /**
14
+ * Height of the sticky PageHeader, in px — the rail (and the shared TabBar)
15
+ * park directly under it. 57 = `px-4 pt-4 pb-3` + the text-xl line + hairline;
16
+ * pass something else only together with a custom `#tabs` slot, because
17
+ * `TabBar` hard-codes the same offset.
18
+ */
19
+ stickyOffset?: number;
20
+ /** What the narrow-width disclosure calls the rail body. */
21
+ railLabel?: string;
22
+ /** Start the narrow-width disclosure expanded (initial value only). */
23
+ railOpenOnNarrow?: boolean;
24
+ };
25
+ declare var __VLS_1: {}, __VLS_7: {}, __VLS_9: {}, __VLS_18: {};
26
+ type __VLS_Slots = {} & {
27
+ 'rail-head'?: (props: typeof __VLS_1) => any;
28
+ } & {
29
+ rail?: (props: typeof __VLS_7) => any;
30
+ } & {
31
+ tabs?: (props: typeof __VLS_9) => any;
32
+ } & {
33
+ default?: (props: typeof __VLS_18) => any;
34
+ };
35
+ declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
36
+ "update:modelValue": (key: string) => any;
37
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
38
+ "onUpdate:modelValue"?: ((key: string) => any) | undefined;
39
+ }>, {
40
+ modelValue: string;
41
+ railWidth: string;
42
+ tabs: RecordTab[];
43
+ stickyOffset: number;
44
+ railOpenOnNarrow: boolean;
45
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
46
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
47
+ export default _default;
48
+ type __VLS_WithSlots<T, S> = T & {
49
+ new (): {
50
+ $slots: S;
51
+ };
52
+ };
@@ -0,0 +1,63 @@
1
+ <script setup>
2
+ import { AltArrowDown } from "#solar-icons";
3
+ import TabBar from "./TabBar.vue";
4
+ const props = defineProps({
5
+ tabs: { type: Array, required: false, default: () => [] },
6
+ modelValue: { type: String, required: false, default: "" },
7
+ railWidth: { type: String, required: false, default: "20rem" },
8
+ stickyOffset: { type: Number, required: false, default: 57 },
9
+ railLabel: { type: String, required: false },
10
+ railOpenOnNarrow: { type: Boolean, required: false, default: false }
11
+ });
12
+ const emit = defineEmits(["update:modelValue"]);
13
+ const t = useT();
14
+ const railLabelText = computed(() => props.railLabel ?? t("shared.recordlayout.rail.label.default"));
15
+ const narrowOpen = ref(props.railOpenOnNarrow);
16
+ </script>
17
+
18
+ <template>
19
+ <div
20
+ class="grid grid-cols-1 items-start gap-4 xl:grid-cols-[var(--record-rail-w)_minmax(0,1fr)]"
21
+ :style="{ '--record-rail-w': railWidth, '--record-rail-top': `${stickyOffset}px` }"
22
+ >
23
+ <aside
24
+ class="min-w-0 space-y-3 xl:sticky xl:top-[var(--record-rail-top)] xl:max-h-[calc(100svh_-_var(--record-rail-top)_-_5.5rem)] xl:overflow-y-auto xl:overscroll-contain xl:pb-1"
25
+ >
26
+ <slot name="rail-head" />
27
+
28
+ <!-- Narrow widths: the rail body folds away so the tabs stay reachable. -->
29
+ <button
30
+ v-if="$slots.rail"
31
+ type="button"
32
+ class="flex w-full cursor-pointer items-center justify-between gap-2 rounded-md border border-black/[0.06] bg-white/60 px-3 py-2 text-sm font-medium text-[#0B0522] transition-colors hover:text-primary xl:hidden"
33
+ :aria-expanded="narrowOpen"
34
+ @click="narrowOpen = !narrowOpen"
35
+ >
36
+ <span>{{ narrowOpen ? t("shared.recordlayout.rail.action.hide", { label: railLabelText }) : t("shared.recordlayout.rail.action.show", { label: railLabelText }) }}</span>
37
+ <AltArrowDown
38
+ :size="14"
39
+ class="shrink-0 transition-transform"
40
+ :class="narrowOpen ? 'rotate-180' : ''"
41
+ />
42
+ </button>
43
+
44
+ <div :class="narrowOpen ? 'space-y-3' : 'hidden space-y-3 xl:block'">
45
+ <slot name="rail" />
46
+ </div>
47
+ </aside>
48
+
49
+ <div class="min-w-0">
50
+ <slot name="tabs">
51
+ <TabBar
52
+ v-if="tabs.length"
53
+ :tabs="tabs"
54
+ :model-value="modelValue"
55
+ @update:model-value="(k) => emit('update:modelValue', k)"
56
+ />
57
+ </slot>
58
+ <div class="pt-4">
59
+ <slot />
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </template>
@@ -0,0 +1,52 @@
1
+ export interface RecordTab {
2
+ key: string;
3
+ label: string;
4
+ badge?: number | string | null;
5
+ }
6
+ type __VLS_Props = {
7
+ /** Tabs for the body column. Empty = render no tab bar. */
8
+ tabs?: RecordTab[];
9
+ /** Active tab key (`v-model`). */
10
+ modelValue?: string;
11
+ /** Rail track width. 320px reads as a column; below ~280 it reads as a list. */
12
+ railWidth?: string;
13
+ /**
14
+ * Height of the sticky PageHeader, in px — the rail (and the shared TabBar)
15
+ * park directly under it. 57 = `px-4 pt-4 pb-3` + the text-xl line + hairline;
16
+ * pass something else only together with a custom `#tabs` slot, because
17
+ * `TabBar` hard-codes the same offset.
18
+ */
19
+ stickyOffset?: number;
20
+ /** What the narrow-width disclosure calls the rail body. */
21
+ railLabel?: string;
22
+ /** Start the narrow-width disclosure expanded (initial value only). */
23
+ railOpenOnNarrow?: boolean;
24
+ };
25
+ declare var __VLS_1: {}, __VLS_7: {}, __VLS_9: {}, __VLS_18: {};
26
+ type __VLS_Slots = {} & {
27
+ 'rail-head'?: (props: typeof __VLS_1) => any;
28
+ } & {
29
+ rail?: (props: typeof __VLS_7) => any;
30
+ } & {
31
+ tabs?: (props: typeof __VLS_9) => any;
32
+ } & {
33
+ default?: (props: typeof __VLS_18) => any;
34
+ };
35
+ declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
36
+ "update:modelValue": (key: string) => any;
37
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
38
+ "onUpdate:modelValue"?: ((key: string) => any) | undefined;
39
+ }>, {
40
+ modelValue: string;
41
+ railWidth: string;
42
+ tabs: RecordTab[];
43
+ stickyOffset: number;
44
+ railOpenOnNarrow: boolean;
45
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
46
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
47
+ export default _default;
48
+ type __VLS_WithSlots<T, S> = T & {
49
+ new (): {
50
+ $slots: S;
51
+ };
52
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * A card sized for the `RecordLayout` rail (PE-250).
3
+ *
4
+ * Same glass shell as the page cards, but on the rail's budget: `px-4 py-3`
5
+ * instead of `px-6 py-4`, a `text-xs` section label instead of a headline, and
6
+ * an optional right-aligned `#actions` (usually the link that jumps to the tab
7
+ * that owns the full thing). Use it so every rail across the studio stacks the
8
+ * same way instead of each page re-rolling its own `<section>`.
9
+ */
10
+ type __VLS_Props = {
11
+ title?: string;
12
+ };
13
+ declare var __VLS_1: {}, __VLS_3: {};
14
+ type __VLS_Slots = {} & {
15
+ actions?: (props: typeof __VLS_1) => any;
16
+ } & {
17
+ default?: (props: typeof __VLS_3) => any;
18
+ };
19
+ declare const __VLS_component: 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>;
20
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
21
+ export default _default;
22
+ type __VLS_WithSlots<T, S> = T & {
23
+ new (): {
24
+ $slots: S;
25
+ };
26
+ };
@@ -0,0 +1,20 @@
1
+ <script setup>
2
+ defineProps({
3
+ title: { type: String, required: false }
4
+ });
5
+ </script>
6
+
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">
9
+ <div
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"
12
+ >
13
+ <h3 class="truncate text-xs font-semibold uppercase tracking-wide text-muted-foreground">
14
+ {{ title }}
15
+ </h3>
16
+ <slot name="actions" />
17
+ </div>
18
+ <slot />
19
+ </section>
20
+ </template>
@@ -0,0 +1,26 @@
1
+ /**
2
+ * A card sized for the `RecordLayout` rail (PE-250).
3
+ *
4
+ * Same glass shell as the page cards, but on the rail's budget: `px-4 py-3`
5
+ * instead of `px-6 py-4`, a `text-xs` section label instead of a headline, and
6
+ * an optional right-aligned `#actions` (usually the link that jumps to the tab
7
+ * that owns the full thing). Use it so every rail across the studio stacks the
8
+ * same way instead of each page re-rolling its own `<section>`.
9
+ */
10
+ type __VLS_Props = {
11
+ title?: string;
12
+ };
13
+ declare var __VLS_1: {}, __VLS_3: {};
14
+ type __VLS_Slots = {} & {
15
+ actions?: (props: typeof __VLS_1) => any;
16
+ } & {
17
+ default?: (props: typeof __VLS_3) => any;
18
+ };
19
+ declare const __VLS_component: 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>;
20
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
21
+ export default _default;
22
+ type __VLS_WithSlots<T, S> = T & {
23
+ new (): {
24
+ $slots: S;
25
+ };
26
+ };
@@ -5,6 +5,11 @@
5
5
  * studio sidebar (Settings, Integrations, Commerce, Apps, …) renders
6
6
  * through this so spacing, active state, badges and "soon" tags stay
7
7
  * identical. Pass `sections`; use `#notice` for loading/empty hints.
8
+ *
9
+ * Items may declare `children`. Those are **tabs of the page the item opens**,
10
+ * not sub-navigation: every item is exactly one row, and it stays highlighted
11
+ * while the route is on any of its tabs. A surface that should stand on its
12
+ * own in the sidebar is declared as its own item.
8
13
  */
9
14
  import type { Component } from 'vue';
10
15
  import type { SidebarNavSection } from '../../types/nav.js';
@@ -3,17 +3,14 @@
3
3
  </script>
4
4
 
5
5
  <script setup>
6
+ import { sidebarItemKey } from "../../lib/sidebarNav";
7
+ import SidebarNavLink from "./SidebarNavLink.vue";
6
8
  defineProps({
7
9
  title: { type: String, required: true },
8
10
  icon: { type: null, required: false },
9
11
  sections: { type: Array, required: true },
10
12
  minimal: { type: Boolean, required: false }
11
13
  });
12
- const route = useRoute();
13
- function isActive(item) {
14
- if (item.exact) return route.path === item.to;
15
- return route.path === item.to || route.path.startsWith(`${item.to}/`);
16
- }
17
14
  </script>
18
15
 
19
16
  <template>
@@ -37,28 +34,11 @@ function isActive(item) {
37
34
  </div>
38
35
  </div>
39
36
  <nav class="flex flex-col gap-0.5 px-2">
40
- <NuxtLink
37
+ <SidebarNavLink
41
38
  v-for="item in section.items"
42
- :key="item.to"
43
- :to="item.to"
44
- class="group flex items-center gap-2.5 rounded-md px-2 py-1.5 text-sm transition-colors hover:text-primary"
45
- :class="[
46
- isActive(item) ? 'bg-primary/10 text-[#0B0522] font-medium' : 'text-foreground/80 hover:bg-primary/5',
47
- item.preview ? 'opacity-70' : ''
48
- ]"
49
- >
50
- <component :is="item.icon" v-if="item.icon" :size="14" :stroke-width="2" class="shrink-0 text-foreground/60 group-hover:text-primary" />
51
- <span class="flex-1 truncate flex items-center gap-1.5">
52
- {{ item.label }}
53
- <span
54
- v-if="item.preview"
55
- class="rounded bg-muted px-1 text-mono text-muted-foreground"
56
- title="Coming soon"
57
- >
58
- soon
59
- </span>
60
- </span>
61
- </NuxtLink>
39
+ :key="sidebarItemKey(item)"
40
+ :item="item"
41
+ />
62
42
  </nav>
63
43
  </template>
64
44
 
@@ -5,6 +5,11 @@
5
5
  * studio sidebar (Settings, Integrations, Commerce, Apps, …) renders
6
6
  * through this so spacing, active state, badges and "soon" tags stay
7
7
  * identical. Pass `sections`; use `#notice` for loading/empty hints.
8
+ *
9
+ * Items may declare `children`. Those are **tabs of the page the item opens**,
10
+ * not sub-navigation: every item is exactly one row, and it stays highlighted
11
+ * while the route is on any of its tabs. A surface that should stand on its
12
+ * own in the sidebar is declared as its own item.
8
13
  */
9
14
  import type { Component } from 'vue';
10
15
  import type { SidebarNavSection } from '../../types/nav.js';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * One row of a `SidebarNav` section — always exactly one link, never a group.
3
+ *
4
+ * An item's `children` are the **tabs of the page this row opens**, not rows
5
+ * of their own (see `lib/sidebarNav.ts`), so there is nothing to expand here:
6
+ * the row links to the item's route, or — for a pure container that nominates
7
+ * no landing page — to its first tab, and stays highlighted while the route is
8
+ * on any of them.
9
+ */
10
+ import type { SidebarNavItem } from '../../types/nav.js';
11
+ type __VLS_Props = {
12
+ item: SidebarNavItem;
13
+ };
14
+ declare const _default: 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>;
15
+ export default _default;
@@ -0,0 +1,49 @@
1
+ <script setup>
2
+ import { sidebarItemIsCurrent, sidebarItemTarget } from "../../lib/sidebarNav";
3
+ const props = defineProps({
4
+ item: { type: Object, required: true }
5
+ });
6
+ const t = useT();
7
+ const route = useRoute();
8
+ const target = computed(() => sidebarItemTarget(props.item));
9
+ const current = computed(() => sidebarItemIsCurrent(props.item, route.path));
10
+ const rowClass = computed(() => [
11
+ "group flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm transition-colors hover:text-primary",
12
+ current.value ? "bg-primary/10 text-[#0B0522] font-medium" : "text-foreground/80 hover:bg-primary/5",
13
+ props.item.preview ? "opacity-70" : ""
14
+ ]);
15
+ </script>
16
+
17
+ <template>
18
+ <NuxtLink v-if="target" :to="target" :class="rowClass">
19
+ <component :is="item.icon" v-if="item.icon" :size="14" :stroke-width="2" class="shrink-0 text-foreground/60 group-hover:text-primary" />
20
+ <span class="min-w-0 flex-1 truncate flex items-center gap-1.5">
21
+ {{ item.label }}
22
+ <span
23
+ v-if="item.preview"
24
+ class="rounded bg-muted px-1 text-mono text-muted-foreground"
25
+ :title="t('shared.nav.badge.comingsoon.tooltip')"
26
+ >
27
+ {{ t("shared.nav.badge.comingsoon.label") }}
28
+ </span>
29
+ </span>
30
+ <!--
31
+ What is waiting behind the entry. A count sits at the end of the row so
32
+ the labels stay left-aligned and scannable; a dot only says "something",
33
+ which is the honest rendering when the source cannot be counted.
34
+ Resolved upstream — an item with nothing pending carries no badge at all
35
+ rather than a zero, because a zero reads as a queue worth opening.
36
+ -->
37
+ <span
38
+ v-if="item.badge === 'dot'"
39
+ class="size-1.5 shrink-0 rounded-full bg-primary"
40
+ aria-hidden="true"
41
+ />
42
+ <span
43
+ v-else-if="item.badge !== null && item.badge !== void 0"
44
+ class="shrink-0 rounded-full bg-muted px-1.5 text-[11px] font-medium tabular-nums text-muted-foreground"
45
+ >
46
+ {{ item.badge }}
47
+ </span>
48
+ </NuxtLink>
49
+ </template>
@@ -0,0 +1,15 @@
1
+ /**
2
+ * One row of a `SidebarNav` section — always exactly one link, never a group.
3
+ *
4
+ * An item's `children` are the **tabs of the page this row opens**, not rows
5
+ * of their own (see `lib/sidebarNav.ts`), so there is nothing to expand here:
6
+ * the row links to the item's route, or — for a pure container that nominates
7
+ * no landing page — to its first tab, and stays highlighted while the route is
8
+ * on any of them.
9
+ */
10
+ import type { SidebarNavItem } from '../../types/nav.js';
11
+ type __VLS_Props = {
12
+ item: SidebarNavItem;
13
+ };
14
+ declare const _default: 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>;
15
+ export default _default;
@@ -7,6 +7,7 @@ const props = defineProps({
7
7
  busy: { type: Boolean, required: false, default: false }
8
8
  });
9
9
  const emit = defineEmits(["update:modelValue", "finish"]);
10
+ const t = useT();
10
11
  const index = computed({
11
12
  get: () => props.modelValue,
12
13
  set: (v) => emit("update:modelValue", v)
@@ -40,7 +41,7 @@ function goTo(i) {
40
41
  @click="goTo(i)"
41
42
  >
42
43
  <div class="h-1 rounded-full transition-colors" :class="i <= index ? 'bg-primary' : 'bg-border'" />
43
- <div class="mt-2 text-xs font-medium" :class="i <= index ? 'text-primary' : 'text-muted-foreground'">Step {{ i + 1 }}</div>
44
+ <div class="mt-2 text-xs font-medium" :class="i <= index ? 'text-primary' : 'text-muted-foreground'">{{ t("shared.wizard.step.label", { number: i + 1 }) }}</div>
44
45
  <div class="text-sm" :class="i <= index ? 'text-[#0B0522]' : 'text-muted-foreground'">{{ s.title }}</div>
45
46
  </div>
46
47
  </div>
@@ -53,9 +54,11 @@ function goTo(i) {
53
54
  <!-- Custom footer (e.g. multiple finish actions on a review step). Falls
54
55
  back to the standard Back / Next·Finish pair. -->
55
56
  <slot name="footer" :back="back" :next="next" :is-first="isFirst" :is-last="isLast" :can-advance="canAdvance" :busy="busy" :index="index">
56
- <Button variant="ghost" :disabled="isFirst || busy" @click="back">Back</Button>
57
+ <Button variant="ghost" :disabled="isFirst || busy" @click="back">
58
+ {{ t("shared.wizard.action.back") }}
59
+ </Button>
57
60
  <Button :disabled="!canAdvance || busy" class="gap-1" @click="next">
58
- {{ isLast ? "Finish" : "Next" }}
61
+ {{ isLast ? t("shared.wizard.action.finish") : t("shared.wizard.action.next") }}
59
62
  </Button>
60
63
  </slot>
61
64
  </div>