@revenexx/studio-shared 0.3.3 → 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 (205) hide show
  1. package/dist/module.d.mts +7 -3
  2. package/dist/module.json +2 -2
  3. package/dist/module.mjs +10 -4
  4. package/dist/runtime/components/Dashboard/DashboardConfirmDialog.d.vue.ts +2 -1
  5. package/dist/runtime/components/Dashboard/DashboardConfirmDialog.vue +1 -1
  6. package/dist/runtime/components/Dashboard/DashboardConfirmDialog.vue.d.ts +2 -1
  7. package/dist/runtime/components/Dashboard/DashboardControls.d.vue.ts +8 -7
  8. package/dist/runtime/components/Dashboard/DashboardControls.vue.d.ts +8 -7
  9. package/dist/runtime/components/Dashboard/DashboardGrid.d.vue.ts +2 -1
  10. package/dist/runtime/components/Dashboard/DashboardGrid.vue +45 -1
  11. package/dist/runtime/components/Dashboard/DashboardGrid.vue.d.ts +2 -1
  12. package/dist/runtime/components/Dashboard/DashboardNameDialog.d.vue.ts +2 -1
  13. package/dist/runtime/components/Dashboard/DashboardNameDialog.vue +1 -1
  14. package/dist/runtime/components/Dashboard/DashboardNameDialog.vue.d.ts +2 -1
  15. package/dist/runtime/components/Dashboard/DashboardPickerDialog.d.vue.ts +2 -1
  16. package/dist/runtime/components/Dashboard/DashboardPickerDialog.vue +1 -1
  17. package/dist/runtime/components/Dashboard/DashboardPickerDialog.vue.d.ts +2 -1
  18. package/dist/runtime/components/Dashboard/DashboardShareDialog.d.vue.ts +2 -1
  19. package/dist/runtime/components/Dashboard/DashboardShareDialog.vue +1 -1
  20. package/dist/runtime/components/Dashboard/DashboardShareDialog.vue.d.ts +2 -1
  21. package/dist/runtime/components/Dashboard/DashboardTopBar.d.vue.ts +9 -4
  22. package/dist/runtime/components/Dashboard/DashboardTopBar.vue.d.ts +9 -4
  23. package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.d.vue.ts +8 -6
  24. package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.vue +1 -1
  25. package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.vue.d.ts +8 -6
  26. package/dist/runtime/components/Dashboard/DashboardWidgetFrame.d.vue.ts +5 -4
  27. package/dist/runtime/components/Dashboard/DashboardWidgetFrame.vue +14 -3
  28. package/dist/runtime/components/Dashboard/DashboardWidgetFrame.vue.d.ts +5 -4
  29. package/dist/runtime/components/Dashboard/DashboardWidgetHost.d.vue.ts +2 -1
  30. package/dist/runtime/components/Dashboard/DashboardWidgetHost.vue.d.ts +2 -1
  31. package/dist/runtime/components/Dashboard/DashboardWidgetIcon.d.vue.ts +2 -1
  32. package/dist/runtime/components/Dashboard/DashboardWidgetIcon.vue.d.ts +2 -1
  33. package/dist/runtime/components/Dashboard/DashboardWidgetLibrary.d.vue.ts +2 -1
  34. package/dist/runtime/components/Dashboard/DashboardWidgetLibrary.vue.d.ts +2 -1
  35. package/dist/runtime/components/Dashboard/DashboardWidgetPreview.d.vue.ts +2 -1
  36. package/dist/runtime/components/Dashboard/DashboardWidgetPreview.vue.d.ts +2 -1
  37. package/dist/runtime/components/DataTable/ColumnsMenu.d.vue.ts +2 -1
  38. package/dist/runtime/components/DataTable/ColumnsMenu.vue.d.ts +2 -1
  39. package/dist/runtime/components/DataTable/DataTable.d.vue.ts +120 -37
  40. package/dist/runtime/components/DataTable/DataTable.vue +493 -53
  41. package/dist/runtime/components/DataTable/DataTable.vue.d.ts +120 -37
  42. package/dist/runtime/components/DataTable/FacetChips.d.vue.ts +2 -1
  43. package/dist/runtime/components/DataTable/FacetChips.vue.d.ts +2 -1
  44. package/dist/runtime/components/DataTable/FacetPanel.d.vue.ts +6 -5
  45. package/dist/runtime/components/DataTable/FacetPanel.vue +57 -8
  46. package/dist/runtime/components/DataTable/FacetPanel.vue.d.ts +6 -5
  47. package/dist/runtime/components/DataTable/FilterMenu.d.vue.ts +6 -5
  48. package/dist/runtime/components/DataTable/FilterMenu.vue +5 -5
  49. package/dist/runtime/components/DataTable/FilterMenu.vue.d.ts +6 -5
  50. package/dist/runtime/components/DataTable/SavedViewsMenu.d.vue.ts +2 -1
  51. package/dist/runtime/components/DataTable/SavedViewsMenu.vue +61 -12
  52. package/dist/runtime/components/DataTable/SavedViewsMenu.vue.d.ts +2 -1
  53. package/dist/runtime/components/DataTable/SortMenu.d.vue.ts +4 -2
  54. package/dist/runtime/components/DataTable/SortMenu.vue +4 -1
  55. package/dist/runtime/components/DataTable/SortMenu.vue.d.ts +4 -2
  56. package/dist/runtime/components/DataTable/ToolbarLabelTrigger.d.vue.ts +23 -0
  57. package/dist/runtime/components/DataTable/ToolbarLabelTrigger.vue +29 -0
  58. package/dist/runtime/components/DataTable/ToolbarLabelTrigger.vue.d.ts +23 -0
  59. package/dist/runtime/components/DataTable/ViewModeSwitch.d.vue.ts +4 -2
  60. package/dist/runtime/components/DataTable/ViewModeSwitch.vue.d.ts +4 -2
  61. package/dist/runtime/components/DataTable/types.d.ts +17 -0
  62. package/dist/runtime/components/Form/FieldCell.d.vue.ts +32 -0
  63. package/dist/runtime/components/Form/FieldCell.vue +46 -0
  64. package/dist/runtime/components/Form/FieldCell.vue.d.ts +32 -0
  65. package/dist/runtime/components/Form/FieldGrid.d.vue.ts +13 -0
  66. package/dist/runtime/components/Form/FieldGrid.vue +11 -0
  67. package/dist/runtime/components/Form/FieldGrid.vue.d.ts +13 -0
  68. package/dist/runtime/components/Form/FormContextCard.d.vue.ts +33 -0
  69. package/dist/runtime/components/Form/FormContextCard.vue +56 -0
  70. package/dist/runtime/components/Form/FormContextCard.vue.d.ts +33 -0
  71. package/dist/runtime/components/Form/FormLayout.d.vue.ts +47 -0
  72. package/dist/runtime/components/Form/FormLayout.vue +169 -0
  73. package/dist/runtime/components/Form/FormLayout.vue.d.ts +47 -0
  74. package/dist/runtime/components/Form/FormSection.d.vue.ts +25 -0
  75. package/dist/runtime/components/Form/FormSection.vue +48 -0
  76. package/dist/runtime/components/Form/FormSection.vue.d.ts +25 -0
  77. package/dist/runtime/components/Form/SaveBar.d.vue.ts +44 -0
  78. package/dist/runtime/components/Form/SaveBar.vue +71 -0
  79. package/dist/runtime/components/Form/SaveBar.vue.d.ts +44 -0
  80. package/dist/runtime/components/Media/MediaFolderTree.d.vue.ts +6 -5
  81. package/dist/runtime/components/Media/MediaFolderTree.vue +1 -2
  82. package/dist/runtime/components/Media/MediaFolderTree.vue.d.ts +6 -5
  83. package/dist/runtime/components/Media/MediaPickerDialog.d.vue.ts +13 -1
  84. package/dist/runtime/components/Media/MediaPickerDialog.vue +206 -54
  85. package/dist/runtime/components/Media/MediaPickerDialog.vue.d.ts +13 -1
  86. package/dist/runtime/components/Media/MediaUploader.d.vue.ts +2 -1
  87. package/dist/runtime/components/Media/MediaUploader.vue +1 -1
  88. package/dist/runtime/components/Media/MediaUploader.vue.d.ts +2 -1
  89. package/dist/runtime/components/Shared/AppIcon.d.vue.ts +21 -0
  90. package/dist/runtime/components/Shared/AppIcon.vue +47 -0
  91. package/dist/runtime/components/Shared/AppIcon.vue.d.ts +21 -0
  92. package/dist/runtime/components/Shared/BuildLogsFlyout.d.vue.ts +2 -1
  93. package/dist/runtime/components/Shared/BuildLogsFlyout.vue.d.ts +2 -1
  94. package/dist/runtime/components/Shared/CardSizeToggle.d.vue.ts +11 -0
  95. package/dist/runtime/components/Shared/CardSizeToggle.vue +19 -0
  96. package/dist/runtime/components/Shared/CardSizeToggle.vue.d.ts +11 -0
  97. package/dist/runtime/components/Shared/DetailSheet.d.vue.ts +60 -0
  98. package/dist/runtime/components/Shared/DetailSheet.vue +211 -0
  99. package/dist/runtime/components/Shared/DetailSheet.vue.d.ts +60 -0
  100. package/dist/runtime/components/Shared/EmptyNote.d.vue.ts +19 -0
  101. package/dist/runtime/components/Shared/EmptyNote.vue +25 -0
  102. package/dist/runtime/components/Shared/EmptyNote.vue.d.ts +19 -0
  103. package/dist/runtime/components/Shared/FloatingPanel.d.vue.ts +7 -6
  104. package/dist/runtime/components/Shared/FloatingPanel.vue.d.ts +7 -6
  105. package/dist/runtime/components/Shared/GitDeploymentSettings.d.vue.ts +2 -1
  106. package/dist/runtime/components/Shared/GitDeploymentSettings.vue.d.ts +2 -1
  107. package/dist/runtime/components/Shared/KpiTile.d.vue.ts +8 -1
  108. package/dist/runtime/components/Shared/KpiTile.vue +10 -9
  109. package/dist/runtime/components/Shared/KpiTile.vue.d.ts +8 -1
  110. package/dist/runtime/components/Shared/ListSurface.d.vue.ts +3 -2
  111. package/dist/runtime/components/Shared/ListSurface.vue.d.ts +3 -2
  112. package/dist/runtime/components/Shared/PageHeader.d.vue.ts +7 -6
  113. package/dist/runtime/components/Shared/PageHeader.vue +15 -4
  114. package/dist/runtime/components/Shared/PageHeader.vue.d.ts +7 -6
  115. package/dist/runtime/components/Shared/RecordLayout.d.vue.ts +8 -7
  116. package/dist/runtime/components/Shared/RecordLayout.vue +1 -2
  117. package/dist/runtime/components/Shared/RecordLayout.vue.d.ts +8 -7
  118. package/dist/runtime/components/Shared/RecordRailCard.d.vue.ts +3 -2
  119. package/dist/runtime/components/Shared/RecordRailCard.vue +3 -3
  120. package/dist/runtime/components/Shared/RecordRailCard.vue.d.ts +3 -2
  121. package/dist/runtime/components/Shared/SelectionBar.d.vue.ts +22 -0
  122. package/dist/runtime/components/Shared/SelectionBar.vue +40 -0
  123. package/dist/runtime/components/Shared/SelectionBar.vue.d.ts +22 -0
  124. package/dist/runtime/components/Shared/SidebarNav.d.vue.ts +39 -4
  125. package/dist/runtime/components/Shared/SidebarNav.vue +71 -25
  126. package/dist/runtime/components/Shared/SidebarNav.vue.d.ts +39 -4
  127. package/dist/runtime/components/Shared/SidebarNavAreas.d.vue.ts +7 -0
  128. package/dist/runtime/components/Shared/SidebarNavAreas.vue +291 -0
  129. package/dist/runtime/components/Shared/SidebarNavAreas.vue.d.ts +7 -0
  130. package/dist/runtime/components/Shared/SidebarNavLink.d.vue.ts +3 -10
  131. package/dist/runtime/components/Shared/SidebarNavLink.vue +31 -5
  132. package/dist/runtime/components/Shared/SidebarNavLink.vue.d.ts +3 -10
  133. package/dist/runtime/components/Shared/SortHeader.d.vue.ts +3 -2
  134. package/dist/runtime/components/Shared/SortHeader.vue +6 -2
  135. package/dist/runtime/components/Shared/SortHeader.vue.d.ts +3 -2
  136. package/dist/runtime/components/Shared/StateCard.d.vue.ts +6 -5
  137. package/dist/runtime/components/Shared/StateCard.vue +1 -1
  138. package/dist/runtime/components/Shared/StateCard.vue.d.ts +6 -5
  139. package/dist/runtime/components/Shared/StudioCard.d.vue.ts +5 -4
  140. package/dist/runtime/components/Shared/StudioCard.vue.d.ts +5 -4
  141. package/dist/runtime/components/Shared/TabBar.d.vue.ts +4 -21
  142. package/dist/runtime/components/Shared/TabBar.vue +30 -13
  143. package/dist/runtime/components/Shared/TabBar.vue.d.ts +4 -21
  144. package/dist/runtime/components/Shared/ViewTab.d.vue.ts +2 -1
  145. package/dist/runtime/components/Shared/ViewTab.vue.d.ts +2 -1
  146. package/dist/runtime/components/Shared/Wizard.d.vue.ts +20 -7
  147. package/dist/runtime/components/Shared/Wizard.vue +9 -5
  148. package/dist/runtime/components/Shared/Wizard.vue.d.ts +20 -7
  149. package/dist/runtime/composables/useDetailSheet.d.ts +20 -0
  150. package/dist/runtime/composables/useDetailSheet.js +50 -0
  151. package/dist/runtime/composables/useMediaLibrary.d.ts +12 -0
  152. package/dist/runtime/composables/usePlatformTenant.d.ts +6 -0
  153. package/dist/runtime/composables/useSavedViews.d.ts +11 -1
  154. package/dist/runtime/composables/useSavedViews.js +4 -4
  155. package/dist/runtime/composables/useWidgetFigures.d.ts +12 -0
  156. package/dist/runtime/composables/useWidgetFigures.js +24 -0
  157. package/dist/runtime/composables/useWidgetHeader.d.ts +15 -0
  158. package/dist/runtime/composables/useWidgetHeader.js +15 -0
  159. package/dist/runtime/lib/activeTenant.d.ts +58 -0
  160. package/dist/runtime/lib/activeTenant.js +45 -0
  161. package/dist/runtime/lib/appIcon.d.ts +48 -0
  162. package/dist/runtime/lib/appIcon.js +27 -0
  163. package/dist/runtime/lib/auditEnvelope.d.ts +43 -0
  164. package/dist/runtime/lib/auditEnvelope.js +55 -0
  165. package/dist/runtime/lib/cardSize.d.ts +13 -0
  166. package/dist/runtime/lib/cardSize.js +20 -0
  167. package/dist/runtime/lib/dashboardIcons.js +2 -0
  168. package/dist/runtime/lib/dataTableFilters.d.ts +22 -0
  169. package/dist/runtime/lib/dataTableFilters.js +5 -0
  170. package/dist/runtime/lib/detailSheet.d.ts +43 -0
  171. package/dist/runtime/lib/detailSheet.js +34 -0
  172. package/dist/runtime/lib/facets.d.ts +13 -0
  173. package/dist/runtime/lib/formLayout.d.ts +164 -0
  174. package/dist/runtime/lib/formLayout.js +117 -0
  175. package/dist/runtime/lib/formLayoutContext.d.ts +26 -0
  176. package/dist/runtime/lib/formLayoutContext.js +1 -0
  177. package/dist/runtime/lib/gridTracks.d.ts +14 -1
  178. package/dist/runtime/lib/gridTracks.js +13 -2
  179. package/dist/runtime/lib/isoLists.d.ts +37 -2
  180. package/dist/runtime/lib/isoLists.js +54 -1
  181. package/dist/runtime/lib/notice.d.ts +14 -0
  182. package/dist/runtime/lib/notice.js +19 -0
  183. package/dist/runtime/lib/pageSize.d.ts +15 -0
  184. package/dist/runtime/lib/pageSize.js +26 -0
  185. package/dist/runtime/lib/rowMove.d.ts +38 -0
  186. package/dist/runtime/lib/rowMove.js +20 -0
  187. package/dist/runtime/lib/sidebarAreas.d.ts +46 -0
  188. package/dist/runtime/lib/sidebarAreas.js +25 -0
  189. package/dist/runtime/lib/sidebarNav.d.ts +21 -2
  190. package/dist/runtime/lib/sidebarNav.js +23 -4
  191. package/dist/runtime/lib/solarIconMap.d.ts +16 -0
  192. package/dist/runtime/lib/solarIconMap.js +326 -0
  193. package/dist/runtime/lib/solarIcons.d.ts +37 -0
  194. package/dist/runtime/lib/solarIcons.js +25 -0
  195. package/dist/runtime/lib/swipeToClose.d.ts +25 -0
  196. package/dist/runtime/lib/swipeToClose.js +118 -0
  197. package/dist/runtime/lib/tableExport.d.ts +31 -0
  198. package/dist/runtime/lib/tableExport.js +143 -0
  199. package/dist/runtime/lib/talkback.d.ts +5 -0
  200. package/dist/runtime/lib/talkback.js +8 -0
  201. package/dist/runtime/lib/talkbackTokenBatch.d.ts +35 -0
  202. package/dist/runtime/lib/talkbackTokenBatch.js +101 -0
  203. package/dist/runtime/types/nav.d.ts +9 -0
  204. package/dist/types.d.mts +8 -0
  205. package/package.json +4 -3
@@ -0,0 +1,169 @@
1
+ <script setup>
2
+ import { computed, nextTick, onBeforeUnmount, onBeforeUpdate, onMounted, provide, ref, shallowRef, useSlots, watch } from "vue";
3
+ import { useT } from "../../composables/useT";
4
+ import {
5
+ activeSectionId,
6
+ CONTEXT_COLUMN_CLASS,
7
+ isScrolledToEnd,
8
+ layoutGridClass,
9
+ NAV_COLUMN_CLASS,
10
+ showSectionNav
11
+ } from "../../lib/formLayout";
12
+ import { FORM_LAYOUT_KEY } from "../../lib/formLayoutContext";
13
+ const props = defineProps({
14
+ nav: { type: [Boolean, String], required: false, default: "auto" },
15
+ context: { type: Boolean, required: false, default: void 0 },
16
+ stickyOffset: { type: Number, required: false, default: 57 },
17
+ navLabel: { type: String, required: false },
18
+ contextLabel: { type: String, required: false }
19
+ });
20
+ const slots = useSlots();
21
+ const t = useT();
22
+ const navLabelText = computed(() => props.navLabel ?? t("shared.form.nav.label"));
23
+ const contextLabelText = computed(() => props.contextLabel ?? t("shared.form.context.label"));
24
+ const sections = shallowRef([]);
25
+ function sortByDocument(list) {
26
+ return [...list].sort((a, b) => {
27
+ const ea = a.el();
28
+ const eb = b.el();
29
+ if (!ea || !eb) return 0;
30
+ return ea.compareDocumentPosition(eb) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
31
+ });
32
+ }
33
+ function register(entry) {
34
+ const rest = sections.value.filter((s) => s.id !== entry.id);
35
+ sections.value = sortByDocument([...rest, entry]);
36
+ void nextTick(updateActive);
37
+ }
38
+ function unregister(id) {
39
+ sections.value = sections.value.filter((s) => s.id !== id);
40
+ }
41
+ const hasSlot = ref({ nav: !!slots.nav, context: !!slots.context });
42
+ onBeforeUpdate(() => {
43
+ const next = { nav: !!slots.nav, context: !!slots.context };
44
+ if (next.nav !== hasSlot.value.nav || next.context !== hasSlot.value.context) hasSlot.value = next;
45
+ });
46
+ const navVisible = computed(() => hasSlot.value.nav || showSectionNav(props.nav, sections.value.length));
47
+ const contextVisible = computed(() => props.context ?? hasSlot.value.context);
48
+ const gridClass = computed(() => layoutGridClass(navVisible.value, contextVisible.value));
49
+ provide(FORM_LAYOUT_KEY, { register, unregister, gridClass, navVisible, contextVisible });
50
+ const root = ref(null);
51
+ const activeId = ref(null);
52
+ let pinnedId = null;
53
+ let pinTimer = null;
54
+ let scroller = null;
55
+ function findScroller(el) {
56
+ let node = el?.parentElement ?? null;
57
+ while (node) {
58
+ const { overflowY } = getComputedStyle(node);
59
+ if (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") return node;
60
+ node = node.parentElement;
61
+ }
62
+ return window;
63
+ }
64
+ function updateActive() {
65
+ if (pinnedId) return;
66
+ if (!scroller) {
67
+ activeId.value = sections.value[0]?.id ?? null;
68
+ return;
69
+ }
70
+ const isWindow = scroller === window;
71
+ const viewportTop = isWindow ? 0 : scroller.getBoundingClientRect().top;
72
+ const offsets = sections.value.flatMap((s) => {
73
+ const el = s.el();
74
+ return el ? [{ id: s.id, top: el.getBoundingClientRect().top - viewportTop }] : [];
75
+ });
76
+ const metrics = isWindow ? { top: window.scrollY, client: window.innerHeight, height: document.documentElement.scrollHeight } : { top: scroller.scrollTop, client: scroller.clientHeight, height: scroller.scrollHeight };
77
+ activeId.value = activeSectionId(offsets, {
78
+ // The line just under the sticky header, plus the breathing room a
79
+ // section is scrolled to (see `scroll-margin-top` in FormSection).
80
+ threshold: props.stickyOffset + 48,
81
+ atEnd: isScrolledToEnd(metrics.top, metrics.client, metrics.height)
82
+ });
83
+ }
84
+ function releasePin() {
85
+ pinnedId = null;
86
+ if (pinTimer) clearTimeout(pinTimer);
87
+ pinTimer = null;
88
+ }
89
+ function onScrollEnd() {
90
+ if (pinnedId) releasePin();
91
+ }
92
+ function goTo(id, event) {
93
+ event?.preventDefault();
94
+ const entry = sections.value.find((s) => s.id === id);
95
+ const el = entry?.el();
96
+ if (!el) return;
97
+ activeId.value = id;
98
+ pinnedId = id;
99
+ if (pinTimer) clearTimeout(pinTimer);
100
+ pinTimer = setTimeout(onScrollEnd, 900);
101
+ const reduce = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
102
+ el.scrollIntoView({ behavior: reduce ? "auto" : "smooth", block: "start" });
103
+ el.focus({ preventScroll: true });
104
+ }
105
+ onMounted(() => {
106
+ scroller = findScroller(root.value);
107
+ scroller.addEventListener("scroll", updateActive, { passive: true });
108
+ scroller.addEventListener("scrollend", onScrollEnd);
109
+ window.addEventListener("resize", updateActive, { passive: true });
110
+ updateActive();
111
+ });
112
+ onBeforeUnmount(() => {
113
+ scroller?.removeEventListener("scroll", updateActive);
114
+ scroller?.removeEventListener("scrollend", onScrollEnd);
115
+ window.removeEventListener("resize", updateActive);
116
+ releasePin();
117
+ });
118
+ watch(() => sections.value.map((s) => s.id).join("|"), () => void nextTick(updateActive));
119
+ defineExpose({ activeId, goTo });
120
+ </script>
121
+
122
+ <template>
123
+ <div
124
+ ref="root"
125
+ class="@container flex flex-col"
126
+ :style="{ '--form-sticky-top': `${stickyOffset}px`, 'minHeight': `calc(100% - ${stickyOffset}px)` }"
127
+ >
128
+ <div class="flex-1 px-4 pt-4 pb-6" :class="gridClass">
129
+ <nav
130
+ v-if="navVisible"
131
+ :aria-label="navLabelText"
132
+ class="sticky top-[calc(var(--form-sticky-top)+1rem)] self-start"
133
+ :class="NAV_COLUMN_CLASS"
134
+ >
135
+ <slot name="nav" :sections="sections" :active-id="activeId" :go-to="goTo">
136
+ <p aria-hidden="true" class="mb-2 px-2.5 text-[11px] font-medium uppercase tracking-[0.08em] text-muted-foreground">
137
+ {{ navLabelText }}
138
+ </p>
139
+ <ul class="space-y-0.5">
140
+ <li v-for="section in sections" :key="section.id">
141
+ <a
142
+ :href="`#${section.id}`"
143
+ :aria-current="activeId === section.id ? 'true' : void 0"
144
+ class="block rounded-md border-l-2 px-2.5 py-1.5 text-sm transition-colors"
145
+ :class="activeId === section.id ? 'border-primary bg-accent font-medium text-[#0B0522]' : 'border-transparent text-muted-foreground hover:text-[#0B0522]'"
146
+ @click="goTo(section.id, $event)"
147
+ >{{ section.title }}</a>
148
+ </li>
149
+ </ul>
150
+ </slot>
151
+ </nav>
152
+
153
+ <div class="min-w-0 space-y-3.5">
154
+ <slot />
155
+ </div>
156
+
157
+ <aside
158
+ v-if="contextVisible"
159
+ :aria-label="contextLabelText"
160
+ class="sticky top-[calc(var(--form-sticky-top)+1rem)] self-start"
161
+ :class="CONTEXT_COLUMN_CLASS"
162
+ >
163
+ <slot name="context" />
164
+ </aside>
165
+ </div>
166
+
167
+ <slot name="savebar" />
168
+ </div>
169
+ </template>
@@ -0,0 +1,47 @@
1
+ import type { FormSectionEntry } from '../../lib/formLayoutContext.js';
2
+ type __VLS_Props = {
3
+ /** `auto` = from three sections on. `false` hides it, `true` forces it from one. */
4
+ nav?: boolean | 'auto';
5
+ /** Draw the context column. Defaults to "when a `#context` slot is given". */
6
+ context?: boolean;
7
+ /**
8
+ * Height of the sticky page header in px. The nav and context column park
9
+ * under it, sections scroll to just below it. 57 = PageHeader without tabs.
10
+ */
11
+ stickyOffset?: number;
12
+ /** Accessible name of the navigation. Defaults to "Sections". */
13
+ navLabel?: string;
14
+ /** Accessible name of the context column. Defaults to "Summary". */
15
+ contextLabel?: string;
16
+ };
17
+ declare function goTo(id: string, event?: Event): void;
18
+ declare var __VLS_1: {
19
+ sections: FormSectionEntry[];
20
+ activeId: string | null;
21
+ goTo: typeof goTo;
22
+ }, __VLS_3: {}, __VLS_5: {}, __VLS_7: {};
23
+ type __VLS_Slots = {} & {
24
+ nav?: (props: typeof __VLS_1) => any;
25
+ } & {
26
+ default?: (props: typeof __VLS_3) => any;
27
+ } & {
28
+ context?: (props: typeof __VLS_5) => any;
29
+ } & {
30
+ savebar?: (props: typeof __VLS_7) => any;
31
+ };
32
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
33
+ activeId: import("vue").Ref<string | null, string | null>;
34
+ goTo: typeof goTo;
35
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
36
+ nav: boolean | "auto";
37
+ context: boolean;
38
+ stickyOffset: number;
39
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
41
+ declare const _default: typeof __VLS_export;
42
+ export default _default;
43
+ type __VLS_WithSlots<T, S> = T & {
44
+ new (): {
45
+ $slots: S;
46
+ };
47
+ };
@@ -0,0 +1,25 @@
1
+ type __VLS_Props = {
2
+ /** Anchor id — the nav links to `#id`. Unique on the page. */
3
+ id: string;
4
+ title?: string;
5
+ /** One sentence: what this group is for. Not a field hint. */
6
+ description?: string;
7
+ headingLevel?: 2 | 3 | 4;
8
+ };
9
+ declare var __VLS_7: {}, __VLS_9: {};
10
+ type __VLS_Slots = {} & {
11
+ actions?: (props: typeof __VLS_7) => any;
12
+ } & {
13
+ default?: (props: typeof __VLS_9) => any;
14
+ };
15
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
16
+ headingLevel: 2 | 3 | 4;
17
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;
21
+ type __VLS_WithSlots<T, S> = T & {
22
+ new (): {
23
+ $slots: S;
24
+ };
25
+ };
@@ -0,0 +1,48 @@
1
+ <script setup>
2
+ import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from "vue";
3
+ import { FORM_LAYOUT_KEY } from "../../lib/formLayoutContext";
4
+ const props = defineProps({
5
+ id: { type: String, required: true },
6
+ title: { type: String, required: false },
7
+ description: { type: String, required: false },
8
+ headingLevel: { type: Number, required: false, default: 2 }
9
+ });
10
+ const el = ref(null);
11
+ const layout = inject(FORM_LAYOUT_KEY, null);
12
+ const headingId = computed(() => props.title ? `${props.id}-title` : void 0);
13
+ const headingTag = computed(() => `h${props.headingLevel}`);
14
+ onMounted(() => {
15
+ if (props.title) layout?.register({ id: props.id, title: props.title, el: () => el.value });
16
+ });
17
+ watch(() => [props.id, props.title], ([id, title], [oldId]) => {
18
+ if (!layout) return;
19
+ layout.unregister(oldId);
20
+ if (title) layout.register({ id, title, el: () => el.value });
21
+ });
22
+ onBeforeUnmount(() => layout?.unregister(props.id));
23
+ </script>
24
+
25
+ <template>
26
+ <section
27
+ :id="id"
28
+ ref="el"
29
+ tabindex="-1"
30
+ :aria-labelledby="headingId"
31
+ class="scroll-mt-[calc(var(--form-sticky-top,0px)+1rem)] rounded-lg border border-black/[0.06] bg-white/70 p-[18px] outline-none backdrop-blur-sm"
32
+ >
33
+ <div v-if="title || description || $slots.actions" class="mb-3.5 flex items-start gap-3">
34
+ <div class="min-w-0 flex-1">
35
+ <component :is="headingTag" v-if="title" :id="headingId" class="text-[14.5px] font-medium text-[#0B0522]">
36
+ {{ title }}
37
+ </component>
38
+ <p v-if="description" class="mt-0.5 text-[12.5px] text-muted-foreground">
39
+ {{ description }}
40
+ </p>
41
+ </div>
42
+ <div v-if="$slots.actions" class="flex shrink-0 items-center gap-2">
43
+ <slot name="actions" />
44
+ </div>
45
+ </div>
46
+ <slot />
47
+ </section>
48
+ </template>
@@ -0,0 +1,25 @@
1
+ type __VLS_Props = {
2
+ /** Anchor id — the nav links to `#id`. Unique on the page. */
3
+ id: string;
4
+ title?: string;
5
+ /** One sentence: what this group is for. Not a field hint. */
6
+ description?: string;
7
+ headingLevel?: 2 | 3 | 4;
8
+ };
9
+ declare var __VLS_7: {}, __VLS_9: {};
10
+ type __VLS_Slots = {} & {
11
+ actions?: (props: typeof __VLS_7) => any;
12
+ } & {
13
+ default?: (props: typeof __VLS_9) => any;
14
+ };
15
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
16
+ headingLevel: 2 | 3 | 4;
17
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;
21
+ type __VLS_WithSlots<T, S> = T & {
22
+ new (): {
23
+ $slots: S;
24
+ };
25
+ };
@@ -0,0 +1,44 @@
1
+ type __VLS_Props = {
2
+ /** Something differs from what is stored. Save is disabled without it. */
3
+ dirty?: boolean;
4
+ /** A save is in flight: both buttons disabled, the save shows a spinner. */
5
+ busy?: boolean;
6
+ /** Extra gate on save (a required field empty). */
7
+ canSubmit?: boolean;
8
+ /** Keep save enabled on a clean form — a create, where "nothing changed" is not the point. */
9
+ allowClean?: boolean;
10
+ submitLabel?: string;
11
+ busyLabel?: string;
12
+ cancelLabel?: string;
13
+ /** Hide the cancel button (nothing to go back to). */
14
+ hideCancel?: boolean;
15
+ /** Id of a `<form>` to submit natively instead of emitting `submit`. */
16
+ form?: string;
17
+ };
18
+ declare var __VLS_1: {}, __VLS_3: {};
19
+ type __VLS_Slots = {} & {
20
+ status?: (props: typeof __VLS_1) => any;
21
+ } & {
22
+ actions?: (props: typeof __VLS_3) => any;
23
+ };
24
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
25
+ submit: () => any;
26
+ cancel: () => any;
27
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
28
+ onSubmit?: (() => any) | undefined;
29
+ onCancel?: (() => any) | undefined;
30
+ }>, {
31
+ dirty: boolean;
32
+ busy: boolean;
33
+ canSubmit: boolean;
34
+ allowClean: boolean;
35
+ hideCancel: boolean;
36
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
37
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
38
+ declare const _default: typeof __VLS_export;
39
+ export default _default;
40
+ type __VLS_WithSlots<T, S> = T & {
41
+ new (): {
42
+ $slots: S;
43
+ };
44
+ };
@@ -0,0 +1,71 @@
1
+ <script setup>
2
+ import { Button } from "@revenexx/studio";
3
+ import { RefreshCircle } from "#solar-icons";
4
+ import { computed, inject } from "vue";
5
+ import { useT } from "../../composables/useT";
6
+ import { FORM_LAYOUT_KEY } from "../../lib/formLayoutContext";
7
+ const props = defineProps({
8
+ dirty: { type: Boolean, required: false, default: false },
9
+ busy: { type: Boolean, required: false, default: false },
10
+ canSubmit: { type: Boolean, required: false, default: true },
11
+ allowClean: { type: Boolean, required: false, default: false },
12
+ submitLabel: { type: String, required: false },
13
+ busyLabel: { type: String, required: false },
14
+ cancelLabel: { type: String, required: false },
15
+ hideCancel: { type: Boolean, required: false, default: false },
16
+ form: { type: String, required: false }
17
+ });
18
+ const emit = defineEmits(["submit", "cancel"]);
19
+ const t = useT();
20
+ const layout = inject(FORM_LAYOUT_KEY, null);
21
+ const submitText = computed(() => props.submitLabel ?? t("shared.form.savebar.action.save"));
22
+ const busyText = computed(() => props.busyLabel ?? t("shared.form.savebar.action.saving"));
23
+ const cancelText = computed(() => props.cancelLabel ?? t("shared.form.savebar.action.cancel"));
24
+ const statusText = computed(() => props.dirty ? t("shared.form.savebar.status.dirty") : t("shared.form.savebar.status.clean"));
25
+ const submitDisabled = computed(() => props.busy || !props.canSubmit || !props.dirty && !props.allowClean);
26
+ function onSubmit() {
27
+ if (props.form || submitDisabled.value) return;
28
+ emit("submit");
29
+ }
30
+ </script>
31
+
32
+ <template>
33
+ <div class="sticky bottom-0 z-10 border-t border-black/[0.06] bg-white/85 backdrop-blur">
34
+ <div class="px-4" :class="layout?.gridClass.value">
35
+ <div v-if="layout?.navVisible.value" aria-hidden="true" class="hidden @3xl:block" />
36
+ <div class="flex min-w-0 flex-wrap items-center gap-2 py-2.5">
37
+ <p class="mr-auto min-w-0 text-[12.5px] text-muted-foreground" role="status" aria-live="polite">
38
+ <slot name="status">
39
+ <span class="inline-flex items-center gap-1.5">
40
+ <span v-if="dirty" aria-hidden="true" class="size-1.5 shrink-0 rounded-full bg-amber-500" />
41
+ {{ statusText }}
42
+ </span>
43
+ </slot>
44
+ </p>
45
+ <slot name="actions" />
46
+ <Button
47
+ v-if="!hideCancel"
48
+ type="button"
49
+ variant="outline"
50
+ size="sm"
51
+ :disabled="busy"
52
+ @click="emit('cancel')"
53
+ >
54
+ {{ cancelText }}
55
+ </Button>
56
+ <Button
57
+ :type="form ? 'submit' : 'button'"
58
+ :form="form"
59
+ size="sm"
60
+ :disabled="submitDisabled"
61
+ :aria-busy="busy || void 0"
62
+ @click="onSubmit"
63
+ >
64
+ <RefreshCircle v-if="busy" :size="14" class="animate-spin" aria-hidden="true" />
65
+ {{ busy ? busyText : submitText }}
66
+ </Button>
67
+ </div>
68
+ <div v-if="layout?.contextVisible.value" aria-hidden="true" class="hidden @5xl:block" />
69
+ </div>
70
+ </div>
71
+ </template>
@@ -0,0 +1,44 @@
1
+ type __VLS_Props = {
2
+ /** Something differs from what is stored. Save is disabled without it. */
3
+ dirty?: boolean;
4
+ /** A save is in flight: both buttons disabled, the save shows a spinner. */
5
+ busy?: boolean;
6
+ /** Extra gate on save (a required field empty). */
7
+ canSubmit?: boolean;
8
+ /** Keep save enabled on a clean form — a create, where "nothing changed" is not the point. */
9
+ allowClean?: boolean;
10
+ submitLabel?: string;
11
+ busyLabel?: string;
12
+ cancelLabel?: string;
13
+ /** Hide the cancel button (nothing to go back to). */
14
+ hideCancel?: boolean;
15
+ /** Id of a `<form>` to submit natively instead of emitting `submit`. */
16
+ form?: string;
17
+ };
18
+ declare var __VLS_1: {}, __VLS_3: {};
19
+ type __VLS_Slots = {} & {
20
+ status?: (props: typeof __VLS_1) => any;
21
+ } & {
22
+ actions?: (props: typeof __VLS_3) => any;
23
+ };
24
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
25
+ submit: () => any;
26
+ cancel: () => any;
27
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
28
+ onSubmit?: (() => any) | undefined;
29
+ onCancel?: (() => any) | undefined;
30
+ }>, {
31
+ dirty: boolean;
32
+ busy: boolean;
33
+ canSubmit: boolean;
34
+ allowClean: boolean;
35
+ hideCancel: boolean;
36
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
37
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
38
+ declare const _default: typeof __VLS_export;
39
+ export default _default;
40
+ type __VLS_WithSlots<T, S> = T & {
41
+ new (): {
42
+ $slots: S;
43
+ };
44
+ };
@@ -1,7 +1,7 @@
1
1
  import type { StorageFolderNode } from '../../types/storage.js';
2
2
  /**
3
- * Recursive folder tree for the media browser sidebar. Emits `select` with a
4
- * folder id (or null for the synthetic root). Self-renders for children.
3
+ * Recursive folder tree for the media browser sidebar. Emits `select` with the
4
+ * clicked folder's id. Self-renders for children.
5
5
  */
6
6
  type __VLS_Props = {
7
7
  nodes: StorageFolderNode[];
@@ -9,9 +9,10 @@ type __VLS_Props = {
9
9
  /** Internal: nesting depth for indentation. */
10
10
  depth?: number;
11
11
  };
12
- declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
- select: (id: string | null) => any;
12
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
+ select: (id: string) => any;
14
14
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
- onSelect?: ((id: string | null) => any) | undefined;
15
+ onSelect?: ((id: string) => any) | undefined;
16
16
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
+ declare const _default: typeof __VLS_export;
17
18
  export default _default;
@@ -38,8 +38,7 @@ const t = useT();
38
38
  <AltArrowRight
39
39
  :size="12"
40
40
  class="transition-transform"
41
- :class="{ 'rotate-90': expanded.has(node.id) }"
42
- />
41
+ :class="{ 'rotate-90': expanded.has(node.id) }" weight="Bold" />
43
42
  </button>
44
43
  <component
45
44
  :is="currentId === node.id ? FolderOpen : FolderIcon"
@@ -1,7 +1,7 @@
1
1
  import type { StorageFolderNode } from '../../types/storage.js';
2
2
  /**
3
- * Recursive folder tree for the media browser sidebar. Emits `select` with a
4
- * folder id (or null for the synthetic root). Self-renders for children.
3
+ * Recursive folder tree for the media browser sidebar. Emits `select` with the
4
+ * clicked folder's id. Self-renders for children.
5
5
  */
6
6
  type __VLS_Props = {
7
7
  nodes: StorageFolderNode[];
@@ -9,9 +9,10 @@ type __VLS_Props = {
9
9
  /** Internal: nesting depth for indentation. */
10
10
  depth?: number;
11
11
  };
12
- declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
- select: (id: string | null) => any;
12
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
+ select: (id: string) => any;
14
14
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
- onSelect?: ((id: string | null) => any) | undefined;
15
+ onSelect?: ((id: string) => any) | undefined;
16
16
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
+ declare const _default: typeof __VLS_export;
17
18
  export default _default;
@@ -1,18 +1,30 @@
1
1
  import type { StorageAsset } from '../../types/storage.js';
2
+ export interface PickerCatalogueEntry {
3
+ /** What the record is called — shown instead of the file name. */
4
+ title: string;
5
+ /** One quiet line under it: type, licence. */
6
+ sub?: string;
7
+ }
2
8
  type __VLS_Props = {
3
9
  open: boolean;
4
10
  multiple?: boolean;
5
11
  /** Restrict the grid to these asset kinds (e.g. ['image']). */
6
12
  kinds?: string[];
13
+ /** Records that manage these files, keyed by storage id — see the header. */
14
+ catalogue?: Record<string, PickerCatalogueEntry> | null;
7
15
  };
8
- declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
16
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
17
  "update:open": (value: boolean) => any;
10
18
  select: (assets: StorageAsset[]) => any;
19
+ uploaded: (assets: StorageAsset[]) => any;
11
20
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
21
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
13
22
  onSelect?: ((assets: StorageAsset[]) => any) | undefined;
23
+ onUploaded?: ((assets: StorageAsset[]) => any) | undefined;
14
24
  }>, {
15
25
  multiple: boolean;
16
26
  kinds: string[];
27
+ catalogue: Record<string, PickerCatalogueEntry> | null;
17
28
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
29
+ declare const _default: typeof __VLS_export;
18
30
  export default _default;