@conduction/nextcloud-vue 2.62.0 → 2.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js +10 -0
  2. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js.map +1 -0
  3. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js +189 -0
  4. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js.map +1 -0
  5. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js +72 -0
  6. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js.map +1 -0
  7. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +271 -32
  8. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +99 -9
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +39 -18
  13. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  14. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  15. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +53 -37
  16. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  17. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  18. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -2
  19. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  20. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js +10 -0
  21. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js.map +1 -0
  22. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +293 -0
  23. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -0
  24. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +189 -0
  25. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -0
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +216 -8
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  28. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +118 -50
  29. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  30. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
  32. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  33. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js +3 -1
  34. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js.map +1 -1
  35. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js +1 -1
  36. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js.map +1 -1
  37. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +234 -9
  38. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  39. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +96 -57
  40. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  41. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +51 -2
  42. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  43. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +5 -1
  44. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  45. package/dist/esm/components/CnIndexPage/selfModeActions.js +3 -3
  46. package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
  47. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +36 -1
  48. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  49. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -1
  50. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  51. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js +121 -12
  52. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js.map +1 -1
  53. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js +3 -1
  54. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js.map +1 -1
  55. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +326 -1
  56. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  57. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +112 -26
  58. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  59. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +10 -0
  60. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js.map +1 -0
  61. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +337 -0
  62. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -0
  63. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +102 -0
  64. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -0
  65. package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js +34 -0
  66. package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js.map +1 -0
  67. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +131 -8
  68. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
  69. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js +40 -38
  70. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
  71. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +8 -6
  72. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  73. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  74. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  75. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +19 -0
  76. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
  77. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js +1 -1
  78. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
  79. package/dist/esm/composables/useDependencyCheck.js +65 -0
  80. package/dist/esm/composables/useDependencyCheck.js.map +1 -0
  81. package/dist/esm/composables/useWalkthrough.js +218 -6
  82. package/dist/esm/composables/useWalkthrough.js.map +1 -1
  83. package/dist/esm/index.js +2 -0
  84. package/dist/esm/index.js.map +1 -1
  85. package/dist/esm/l10n/en.json.js +43 -0
  86. package/dist/esm/l10n/en.json.js.map +1 -1
  87. package/dist/esm/l10n/nl.json.js +43 -0
  88. package/dist/esm/l10n/nl.json.js.map +1 -1
  89. package/dist/esm/nextcloud-vue.css +449 -0
  90. package/dist/esm/store/useObjectStore.js +8 -0
  91. package/dist/esm/store/useObjectStore.js.map +1 -1
  92. package/dist/esm/utils/columnFilters.js +311 -0
  93. package/dist/esm/utils/columnFilters.js.map +1 -0
  94. package/dist/esm/utils/groupAutocomplete.js +123 -0
  95. package/dist/esm/utils/groupAutocomplete.js.map +1 -0
  96. package/dist/esm/utils/libraryWidgetKeys.js +2 -0
  97. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  98. package/dist/esm/utils/pickerOptions.js +198 -0
  99. package/dist/esm/utils/pickerOptions.js.map +1 -0
  100. package/dist/esm/utils/schema.js +83 -7
  101. package/dist/esm/utils/schema.js.map +1 -1
  102. package/dist/esm/utils/timelineEvents.js +174 -0
  103. package/dist/esm/utils/timelineEvents.js.map +1 -0
  104. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  105. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  106. package/dist/esm/utils/walkthroughSignals.js +88 -0
  107. package/dist/esm/utils/walkthroughSignals.js.map +1 -0
  108. package/dist/nextcloud-vue.cjs.js +9369 -5193
  109. package/dist/nextcloud-vue.cjs.js.map +2 -2
  110. package/dist/nextcloud-vue.css +449 -0
  111. package/l10n/en.json +43 -0
  112. package/l10n/nl.json +43 -0
  113. package/package.json +1 -1
  114. package/src/components/CnAdminActionCard/CnAdminActionCard.vue +234 -0
  115. package/src/components/CnAdminActionCard/index.js +4 -0
  116. package/src/components/CnAppNav/CnAppNav.vue +271 -32
  117. package/src/components/CnAppRoot/CnAppRoot.vue +129 -15
  118. package/src/components/CnChoiceCards/CnChoiceCards.vue +56 -31
  119. package/src/components/CnDashboardPage/CnDashboardPage.vue +2 -2
  120. package/src/components/CnDataTable/CnColumnFilterPopover.vue +524 -0
  121. package/src/components/CnDataTable/CnDataTable.md +3 -0
  122. package/src/components/CnDataTable/CnDataTable.vue +260 -4
  123. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
  124. package/src/components/CnFieldHelper/CnFieldHelper.vue +4 -2
  125. package/src/components/CnFormDialog/CnFormDialog.vue +290 -31
  126. package/src/components/CnIndexPage/CnIndexPage.vue +55 -2
  127. package/src/components/CnIndexPage/selfModeActions.js +3 -3
  128. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +39 -1
  129. package/src/components/CnResourceSelect/CnResourceSelect.vue +123 -12
  130. package/src/components/CnSetupWizard/CnSetupWizard.vue +392 -3
  131. package/src/components/CnTimelineWidget/CnTimelineWidget.vue +477 -0
  132. package/src/components/CnTimelineWidget/dashboardRegistration.js +32 -0
  133. package/src/components/CnTimelineWidget/index.js +4 -0
  134. package/src/components/CnWalkthrough/CnWalkthrough.vue +143 -10
  135. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  136. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  137. package/src/components/CnWizardDialog/CnWizardDialog.vue +20 -1
  138. package/src/components/index.js +2 -0
  139. package/src/composables/useDependencyCheck.js +61 -0
  140. package/src/composables/useWalkthrough.js +217 -5
  141. package/src/css/dashboard.css +49 -0
  142. package/src/css/detail-page.css +19 -0
  143. package/src/css/table.css +81 -0
  144. package/src/index.js +2 -0
  145. package/src/schemas/app-manifest-v2.schema.json +23 -1
  146. package/src/store/useObjectStore.js +8 -0
  147. package/src/utils/columnFilters.js +324 -0
  148. package/src/utils/groupAutocomplete.js +119 -0
  149. package/src/utils/libraryWidgetKeys.js +2 -0
  150. package/src/utils/pickerOptions.js +194 -0
  151. package/src/utils/schema.js +83 -7
  152. package/src/utils/timelineEvents.js +171 -0
  153. package/src/utils/validateManifestV2.compiled.js +2 -2
  154. package/src/utils/walkthroughSignals.js +94 -0
@@ -0,0 +1,311 @@
1
+ import { schemaRefSlug } from './schemaRefSlug.js';
2
+
3
+ /**
4
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
5
+ * SPDX-License-Identifier: EUPL-1.2
6
+ */
7
+
8
+
9
+ /** Column keys that mark a value computed on the client, never stored. */
10
+ const COMPUTED_MARKERS = ['aggregate', 'compute', 'computed', 'virtual'];
11
+
12
+ /**
13
+ * Normalise a `$ref` to the referenced schema slug, or null.
14
+ *
15
+ * @param {unknown} ref A `$ref` value.
16
+ * @return {string|number|null} The slug.
17
+ */
18
+ function refSlug(ref) {
19
+ if (typeof ref === 'number' && !Number.isNaN(ref)) {
20
+ return ref
21
+ }
22
+ if (typeof ref !== 'string' || ref === '') {
23
+ return null
24
+ }
25
+ const tail = ref.includes('/') ? ref.substring(ref.lastIndexOf('/') + 1) : ref;
26
+ const slug = tail === '' ? '' : schemaRefSlug(tail);
27
+ return slug !== '' ? slug : null
28
+ }
29
+
30
+ /**
31
+ * The schema property behind a column, or null when the column has none.
32
+ *
33
+ * @param {object} column The column definition.
34
+ * @param {object|null} schema The table's schema.
35
+ * @return {object|null} The property.
36
+ */
37
+ function columnProperty(column, schema) {
38
+ if (!column || !schema || !schema.properties || typeof column.key !== 'string') {
39
+ return null
40
+ }
41
+ const prop = schema.properties[column.key];
42
+ return (prop && typeof prop === 'object') ? prop : null
43
+ }
44
+
45
+ /**
46
+ * Whether a column computes its value on the client, so the server cannot
47
+ * sort or filter on it.
48
+ *
49
+ * @param {object} column The column definition.
50
+ * @return {boolean} True for a computed column.
51
+ */
52
+ function isComputedColumn(column) {
53
+ return COMPUTED_MARKERS.some((marker) => column[marker] !== undefined && column[marker] !== null && column[marker] !== false)
54
+ }
55
+
56
+ /**
57
+ * Whether a column header sorts.
58
+ *
59
+ * `sortable: false` always wins, and so does an explicit `sortable: true`.
60
+ * Otherwise, with a schema, a column sorts when a stored schema property backs
61
+ * it: OpenRegister orders on schema properties, so a widget or computed column
62
+ * with no property behind it stays unsortable. Without a schema nothing says
63
+ * the server can sort, so only an explicit flag makes the column sortable,
64
+ * which is how such tables behaved before.
65
+ *
66
+ * @param {object} column The column definition.
67
+ * @param {object|null} schema The table's schema.
68
+ * @return {boolean} True when the header sorts.
69
+ * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-sorts-by-default
70
+ */
71
+ function isColumnSortable(column, schema) {
72
+ if (!column || typeof column !== 'object') {
73
+ return false
74
+ }
75
+ if (column.sortable === false) {
76
+ return false
77
+ }
78
+ if (column.sortable === true) {
79
+ return true
80
+ }
81
+ if (!schema || isComputedColumn(column)) {
82
+ return false
83
+ }
84
+ const prop = columnProperty(column, schema);
85
+ return !!prop && prop.type !== 'object'
86
+ }
87
+
88
+ /**
89
+ * Display options for an enum filter: schema `enum` (or `items.enum`) with
90
+ * `x-enum-labels`, else a column `enum` hint, else a badge column's colour
91
+ * map keys.
92
+ *
93
+ * @param {object} column The column definition.
94
+ * @param {object|null} prop The schema property.
95
+ * @return {Array<{value: string, label: string}>} The options, or [].
96
+ */
97
+ function enumOptions(column, prop) {
98
+ const p = prop || {};
99
+ const labels = p.enumLabels || p['x-enum-labels'] || (p.items && (p.items.enumLabels || p.items['x-enum-labels'])) || {};
100
+ let values = null;
101
+ if (Array.isArray(p.enum) && p.enum.length > 0) {
102
+ values = p.enum;
103
+ } else if (p.items && Array.isArray(p.items.enum) && p.items.enum.length > 0) {
104
+ values = p.items.enum;
105
+ } else if (Array.isArray(column.enum) && column.enum.length > 0) {
106
+ values = column.enum;
107
+ } else if (column.widget === 'badge' && column.widgetProps && column.widgetProps.colorMap) {
108
+ values = Object.keys(column.widgetProps.colorMap);
109
+ }
110
+ if (!values) {
111
+ return []
112
+ }
113
+ return values
114
+ .filter((v) => v !== null && v !== undefined && v !== '')
115
+ .map((v) => ({ value: String(v), label: String(labels[v] !== undefined ? labels[v] : v) }))
116
+ }
117
+
118
+ /**
119
+ * The reference a column points at, for a searchable object filter: a schema
120
+ * `$ref` (or `items.$ref`), else an `fkResolve` cell widget's config.
121
+ *
122
+ * @param {object} column The column definition.
123
+ * @param {object|null} prop The schema property.
124
+ * @return {{schema: (string|number), register: string, labelField: string}|null} The reference.
125
+ */
126
+ function referenceTarget(column, prop) {
127
+ const widgetProps = (column.widget === 'fkResolve' && column.widgetProps) || null;
128
+ const p = prop || {};
129
+ const schemaRef = refSlug(p.$ref) ?? (p.items ? refSlug(p.items.$ref) : null);
130
+ const schema = schemaRef ?? (widgetProps && widgetProps.schema ? widgetProps.schema : null);
131
+ if (schema === null || schema === undefined || schema === '') {
132
+ return null
133
+ }
134
+ const register = (widgetProps && widgetProps.register)
135
+ || p['x-external-register']
136
+ || (p.items && p.items['x-external-register'])
137
+ || '';
138
+ const labelField = (widgetProps && widgetProps.labelField) || '';
139
+ return { schema, register, labelField }
140
+ }
141
+
142
+ /**
143
+ * How a column filters, or null when it does not.
144
+ *
145
+ * Kinds: `enum` (checkbox list), `boolean` (yes, no, any), `reference`
146
+ * (searchable list of the referenced objects), `number` and `date` (from and
147
+ * to), and `string` (equals). `filterable: false` on the column turns it
148
+ * off. A column with no schema property behind it only filters when it
149
+ * carries an enum hint (`enum`, or a badge widget's colour map), because the
150
+ * server cannot filter on a value it does not store.
151
+ *
152
+ * @param {object} column The column definition.
153
+ * @param {object|null} schema The table's schema.
154
+ * @return {{key: string, kind: string, label: string, options: Array<{value: string, label: string}>, reference: object|null, dateTime: boolean}|null} The filter definition.
155
+ * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header
156
+ */
157
+ function columnFilterDef(column, schema) {
158
+ if (!column || typeof column !== 'object' || typeof column.key !== 'string' || column.key === '') {
159
+ return null
160
+ }
161
+ if (column.filterable === false || isComputedColumn(column)) {
162
+ return null
163
+ }
164
+ const prop = columnProperty(column, schema);
165
+ const base = { key: column.key, label: column.label || (prop && prop.title) || column.key, options: [], reference: null, dateTime: false };
166
+
167
+ const options = enumOptions(column, prop);
168
+ if (options.length > 0) {
169
+ return { ...base, kind: 'enum', options }
170
+ }
171
+ if (!prop) {
172
+ return null
173
+ }
174
+ const reference = referenceTarget(column, prop);
175
+ if (reference) {
176
+ return { ...base, kind: 'reference', reference }
177
+ }
178
+ const type = Array.isArray(prop.type) ? prop.type.find((t) => t !== 'null') : prop.type;
179
+ const format = prop.format || '';
180
+ if (type === 'boolean') {
181
+ return { ...base, kind: 'boolean' }
182
+ }
183
+ if (type === 'number' || type === 'integer') {
184
+ return { ...base, kind: 'number' }
185
+ }
186
+ if (type === 'string' && (format === 'date' || format === 'date-time')) {
187
+ return { ...base, kind: 'date', dateTime: format === 'date-time' }
188
+ }
189
+ if (type === 'string') {
190
+ return { ...base, kind: 'string' }
191
+ }
192
+ return null
193
+ }
194
+
195
+ /**
196
+ * Read one active-filter entry as a list of strings.
197
+ *
198
+ * @param {unknown} raw An active-filter value: an array, a scalar, or absent.
199
+ * @return {Array<string>} The values.
200
+ */
201
+ function asList(raw) {
202
+ if (raw === undefined || raw === null || raw === '') {
203
+ return []
204
+ }
205
+ return (Array.isArray(raw) ? raw : [raw]).filter((v) => v !== undefined && v !== null && v !== '').map((v) => String(v))
206
+ }
207
+
208
+ /**
209
+ * The parameter keys one column filter owns.
210
+ *
211
+ * @param {object} def A filter definition from `columnFilterDef`.
212
+ * @return {Array<string>} The keys.
213
+ */
214
+ function columnFilterParamKeys(def) {
215
+ if (!def) {
216
+ return []
217
+ }
218
+ if (def.kind === 'number' || def.kind === 'date') {
219
+ return [`${def.key}[gte]`, `${def.key}[lte]`]
220
+ }
221
+ return [def.key]
222
+ }
223
+
224
+ /**
225
+ * A column filter's current state, read from the active-filter map.
226
+ *
227
+ * @param {object} def A filter definition.
228
+ * @param {object} activeFilters The active-filter map (`{ paramKey: values }`).
229
+ * @return {object} `{ values }` for enum and reference, `{ value }` for boolean and string, `{ from, to }` for number and date.
230
+ */
231
+ function columnFilterState(def, activeFilters) {
232
+ const filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {};
233
+ if (!def) {
234
+ return {}
235
+ }
236
+ if (def.kind === 'enum' || def.kind === 'reference') {
237
+ return { values: asList(filters[def.key]) }
238
+ }
239
+ if (def.kind === 'number' || def.kind === 'date') {
240
+ const from = asList(filters[`${def.key}[gte]`])[0] || '';
241
+ const to = asList(filters[`${def.key}[lte]`])[0] || '';
242
+ // A date-time column is queried up to the end of the day; the input
243
+ // shows the date the person picked.
244
+ return {
245
+ from: def.kind === 'date' ? from.slice(0, 10) : from,
246
+ to: def.kind === 'date' ? to.slice(0, 10) : to,
247
+ }
248
+ }
249
+ return { value: asList(filters[def.key])[0] || '' }
250
+ }
251
+
252
+ /**
253
+ * Whether a column filter is active.
254
+ *
255
+ * @param {object} def A filter definition.
256
+ * @param {object} activeFilters The active-filter map.
257
+ * @return {boolean} True when any of its parameters is set.
258
+ */
259
+ function isColumnFilterActive(def, activeFilters) {
260
+ const filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {};
261
+ return columnFilterParamKeys(def).some((key) => asList(filters[key]).length > 0)
262
+ }
263
+
264
+ /**
265
+ * The active-filter entries a column filter state produces. Every key the
266
+ * column owns is present; an empty list clears that key, so changing a range
267
+ * from "from and to" to "from" drops the old upper bound.
268
+ *
269
+ * @param {object} def A filter definition.
270
+ * @param {object} state The column filter state (see `columnFilterState`).
271
+ * @return {object} `{ paramKey: values[] }`.
272
+ * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-a-header-filter-speaks-the-sidebars-query-language
273
+ */
274
+ function columnFilterParams(def, state) {
275
+ const s = state || {};
276
+ if (!def) {
277
+ return {}
278
+ }
279
+ if (def.kind === 'enum' || def.kind === 'reference') {
280
+ return { [def.key]: asList(s.values) }
281
+ }
282
+ if (def.kind === 'number' || def.kind === 'date') {
283
+ const from = s.from === undefined || s.from === null ? '' : String(s.from).trim();
284
+ let to = s.to === undefined || s.to === null ? '' : String(s.to).trim();
285
+ if (def.kind === 'date' && def.dateTime && to !== '' && to.length === 10) {
286
+ to = `${to}T23:59:59`;
287
+ }
288
+ return {
289
+ [`${def.key}[gte]`]: from === '' ? [] : [from],
290
+ [`${def.key}[lte]`]: to === '' ? [] : [to],
291
+ }
292
+ }
293
+ if (def.kind === 'boolean') {
294
+ return { [def.key]: (s.value === 'true' || s.value === 'false') ? [s.value] : [] }
295
+ }
296
+ const value = s.value === undefined || s.value === null ? '' : String(s.value).trim();
297
+ return { [def.key]: value === '' ? [] : [value] }
298
+ }
299
+
300
+ /**
301
+ * Clear-all entries for one column: every key it owns, emptied.
302
+ *
303
+ * @param {object} def A filter definition.
304
+ * @return {object} `{ paramKey: [] }`.
305
+ */
306
+ function clearedColumnFilterParams(def) {
307
+ return Object.fromEntries(columnFilterParamKeys(def).map((key) => [key, []]))
308
+ }
309
+
310
+ export { clearedColumnFilterParams, columnFilterDef, columnFilterParamKeys, columnFilterParams, columnFilterState, columnProperty, isColumnFilterActive, isColumnSortable };
311
+ //# sourceMappingURL=columnFilters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"columnFilters.js","sources":["../../../src/utils/columnFilters.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\n\n/**\n * Per-column sorting and filtering for table headers.\n *\n * Pure helpers: which column can sort, which can filter and how, and the\n * translation between a header filter's state and the OpenRegister query\n * parameters the facet sidebar already sends. A header filter writes into the\n * SAME active-filter map (`{ paramKey: values[] }`) the sidebar uses, so the\n * fetch, the route query and saved views need nothing new.\n *\n * Parameter shapes, as OpenRegister reads them:\n * - equality and \"any of\": `key=value`, `key[]=a&key[]=b`\n * - ranges: `key[gte]=from`, `key[lte]=to`\n *\n * @module utils/columnFilters\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md\n */\n\nimport { schemaRefSlug } from './schemaRefSlug.js'\n\n/** Column keys that mark a value computed on the client, never stored. */\nconst COMPUTED_MARKERS = ['aggregate', 'compute', 'computed', 'virtual']\n\n/**\n * Normalise a `$ref` to the referenced schema slug, or null.\n *\n * @param {unknown} ref A `$ref` value.\n * @return {string|number|null} The slug.\n */\nfunction refSlug(ref) {\n\tif (typeof ref === 'number' && !Number.isNaN(ref)) {\n\t\treturn ref\n\t}\n\tif (typeof ref !== 'string' || ref === '') {\n\t\treturn null\n\t}\n\tconst tail = ref.includes('/') ? ref.substring(ref.lastIndexOf('/') + 1) : ref\n\tconst slug = tail === '' ? '' : schemaRefSlug(tail)\n\treturn slug !== '' ? slug : null\n}\n\n/**\n * The schema property behind a column, or null when the column has none.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {object|null} The property.\n */\nexport function columnProperty(column, schema) {\n\tif (!column || !schema || !schema.properties || typeof column.key !== 'string') {\n\t\treturn null\n\t}\n\tconst prop = schema.properties[column.key]\n\treturn (prop && typeof prop === 'object') ? prop : null\n}\n\n/**\n * Whether a column computes its value on the client, so the server cannot\n * sort or filter on it.\n *\n * @param {object} column The column definition.\n * @return {boolean} True for a computed column.\n */\nfunction isComputedColumn(column) {\n\treturn COMPUTED_MARKERS.some((marker) => column[marker] !== undefined && column[marker] !== null && column[marker] !== false)\n}\n\n/**\n * Whether a column header sorts.\n *\n * `sortable: false` always wins, and so does an explicit `sortable: true`.\n * Otherwise, with a schema, a column sorts when a stored schema property backs\n * it: OpenRegister orders on schema properties, so a widget or computed column\n * with no property behind it stays unsortable. Without a schema nothing says\n * the server can sort, so only an explicit flag makes the column sortable,\n * which is how such tables behaved before.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {boolean} True when the header sorts.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-sorts-by-default\n */\nexport function isColumnSortable(column, schema) {\n\tif (!column || typeof column !== 'object') {\n\t\treturn false\n\t}\n\tif (column.sortable === false) {\n\t\treturn false\n\t}\n\tif (column.sortable === true) {\n\t\treturn true\n\t}\n\tif (!schema || isComputedColumn(column)) {\n\t\treturn false\n\t}\n\tconst prop = columnProperty(column, schema)\n\treturn !!prop && prop.type !== 'object'\n}\n\n/**\n * Display options for an enum filter: schema `enum` (or `items.enum`) with\n * `x-enum-labels`, else a column `enum` hint, else a badge column's colour\n * map keys.\n *\n * @param {object} column The column definition.\n * @param {object|null} prop The schema property.\n * @return {Array<{value: string, label: string}>} The options, or [].\n */\nfunction enumOptions(column, prop) {\n\tconst p = prop || {}\n\tconst labels = p.enumLabels || p['x-enum-labels'] || (p.items && (p.items.enumLabels || p.items['x-enum-labels'])) || {}\n\tlet values = null\n\tif (Array.isArray(p.enum) && p.enum.length > 0) {\n\t\tvalues = p.enum\n\t} else if (p.items && Array.isArray(p.items.enum) && p.items.enum.length > 0) {\n\t\tvalues = p.items.enum\n\t} else if (Array.isArray(column.enum) && column.enum.length > 0) {\n\t\tvalues = column.enum\n\t} else if (column.widget === 'badge' && column.widgetProps && column.widgetProps.colorMap) {\n\t\tvalues = Object.keys(column.widgetProps.colorMap)\n\t}\n\tif (!values) {\n\t\treturn []\n\t}\n\treturn values\n\t\t.filter((v) => v !== null && v !== undefined && v !== '')\n\t\t.map((v) => ({ value: String(v), label: String(labels[v] !== undefined ? labels[v] : v) }))\n}\n\n/**\n * The reference a column points at, for a searchable object filter: a schema\n * `$ref` (or `items.$ref`), else an `fkResolve` cell widget's config.\n *\n * @param {object} column The column definition.\n * @param {object|null} prop The schema property.\n * @return {{schema: (string|number), register: string, labelField: string}|null} The reference.\n */\nfunction referenceTarget(column, prop) {\n\tconst widgetProps = (column.widget === 'fkResolve' && column.widgetProps) || null\n\tconst p = prop || {}\n\tconst schemaRef = refSlug(p.$ref) ?? (p.items ? refSlug(p.items.$ref) : null)\n\tconst schema = schemaRef ?? (widgetProps && widgetProps.schema ? widgetProps.schema : null)\n\tif (schema === null || schema === undefined || schema === '') {\n\t\treturn null\n\t}\n\tconst register = (widgetProps && widgetProps.register)\n\t\t|| p['x-external-register']\n\t\t|| (p.items && p.items['x-external-register'])\n\t\t|| ''\n\tconst labelField = (widgetProps && widgetProps.labelField) || ''\n\treturn { schema, register, labelField }\n}\n\n/**\n * How a column filters, or null when it does not.\n *\n * Kinds: `enum` (checkbox list), `boolean` (yes, no, any), `reference`\n * (searchable list of the referenced objects), `number` and `date` (from and\n * to), and `string` (equals). `filterable: false` on the column turns it\n * off. A column with no schema property behind it only filters when it\n * carries an enum hint (`enum`, or a badge widget's colour map), because the\n * server cannot filter on a value it does not store.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {{key: string, kind: string, label: string, options: Array<{value: string, label: string}>, reference: object|null, dateTime: boolean}|null} The filter definition.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header\n */\nexport function columnFilterDef(column, schema) {\n\tif (!column || typeof column !== 'object' || typeof column.key !== 'string' || column.key === '') {\n\t\treturn null\n\t}\n\tif (column.filterable === false || isComputedColumn(column)) {\n\t\treturn null\n\t}\n\tconst prop = columnProperty(column, schema)\n\tconst base = { key: column.key, label: column.label || (prop && prop.title) || column.key, options: [], reference: null, dateTime: false }\n\n\tconst options = enumOptions(column, prop)\n\tif (options.length > 0) {\n\t\treturn { ...base, kind: 'enum', options }\n\t}\n\tif (!prop) {\n\t\treturn null\n\t}\n\tconst reference = referenceTarget(column, prop)\n\tif (reference) {\n\t\treturn { ...base, kind: 'reference', reference }\n\t}\n\tconst type = Array.isArray(prop.type) ? prop.type.find((t) => t !== 'null') : prop.type\n\tconst format = prop.format || ''\n\tif (type === 'boolean') {\n\t\treturn { ...base, kind: 'boolean' }\n\t}\n\tif (type === 'number' || type === 'integer') {\n\t\treturn { ...base, kind: 'number' }\n\t}\n\tif (type === 'string' && (format === 'date' || format === 'date-time')) {\n\t\treturn { ...base, kind: 'date', dateTime: format === 'date-time' }\n\t}\n\tif (type === 'string') {\n\t\treturn { ...base, kind: 'string' }\n\t}\n\treturn null\n}\n\n/**\n * Read one active-filter entry as a list of strings.\n *\n * @param {unknown} raw An active-filter value: an array, a scalar, or absent.\n * @return {Array<string>} The values.\n */\nfunction asList(raw) {\n\tif (raw === undefined || raw === null || raw === '') {\n\t\treturn []\n\t}\n\treturn (Array.isArray(raw) ? raw : [raw]).filter((v) => v !== undefined && v !== null && v !== '').map((v) => String(v))\n}\n\n/**\n * The parameter keys one column filter owns.\n *\n * @param {object} def A filter definition from `columnFilterDef`.\n * @return {Array<string>} The keys.\n */\nexport function columnFilterParamKeys(def) {\n\tif (!def) {\n\t\treturn []\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\treturn [`${def.key}[gte]`, `${def.key}[lte]`]\n\t}\n\treturn [def.key]\n}\n\n/**\n * A column filter's current state, read from the active-filter map.\n *\n * @param {object} def A filter definition.\n * @param {object} activeFilters The active-filter map (`{ paramKey: values }`).\n * @return {object} `{ values }` for enum and reference, `{ value }` for boolean and string, `{ from, to }` for number and date.\n */\nexport function columnFilterState(def, activeFilters) {\n\tconst filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {}\n\tif (!def) {\n\t\treturn {}\n\t}\n\tif (def.kind === 'enum' || def.kind === 'reference') {\n\t\treturn { values: asList(filters[def.key]) }\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\tconst from = asList(filters[`${def.key}[gte]`])[0] || ''\n\t\tconst to = asList(filters[`${def.key}[lte]`])[0] || ''\n\t\t// A date-time column is queried up to the end of the day; the input\n\t\t// shows the date the person picked.\n\t\treturn {\n\t\t\tfrom: def.kind === 'date' ? from.slice(0, 10) : from,\n\t\t\tto: def.kind === 'date' ? to.slice(0, 10) : to,\n\t\t}\n\t}\n\treturn { value: asList(filters[def.key])[0] || '' }\n}\n\n/**\n * Whether a column filter is active.\n *\n * @param {object} def A filter definition.\n * @param {object} activeFilters The active-filter map.\n * @return {boolean} True when any of its parameters is set.\n */\nexport function isColumnFilterActive(def, activeFilters) {\n\tconst filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {}\n\treturn columnFilterParamKeys(def).some((key) => asList(filters[key]).length > 0)\n}\n\n/**\n * The active-filter entries a column filter state produces. Every key the\n * column owns is present; an empty list clears that key, so changing a range\n * from \"from and to\" to \"from\" drops the old upper bound.\n *\n * @param {object} def A filter definition.\n * @param {object} state The column filter state (see `columnFilterState`).\n * @return {object} `{ paramKey: values[] }`.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-a-header-filter-speaks-the-sidebars-query-language\n */\nexport function columnFilterParams(def, state) {\n\tconst s = state || {}\n\tif (!def) {\n\t\treturn {}\n\t}\n\tif (def.kind === 'enum' || def.kind === 'reference') {\n\t\treturn { [def.key]: asList(s.values) }\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\tconst from = s.from === undefined || s.from === null ? '' : String(s.from).trim()\n\t\tlet to = s.to === undefined || s.to === null ? '' : String(s.to).trim()\n\t\tif (def.kind === 'date' && def.dateTime && to !== '' && to.length === 10) {\n\t\t\tto = `${to}T23:59:59`\n\t\t}\n\t\treturn {\n\t\t\t[`${def.key}[gte]`]: from === '' ? [] : [from],\n\t\t\t[`${def.key}[lte]`]: to === '' ? [] : [to],\n\t\t}\n\t}\n\tif (def.kind === 'boolean') {\n\t\treturn { [def.key]: (s.value === 'true' || s.value === 'false') ? [s.value] : [] }\n\t}\n\tconst value = s.value === undefined || s.value === null ? '' : String(s.value).trim()\n\treturn { [def.key]: value === '' ? [] : [value] }\n}\n\n/**\n * Clear-all entries for one column: every key it owns, emptied.\n *\n * @param {object} def A filter definition.\n * @return {object} `{ paramKey: [] }`.\n */\nexport function clearedColumnFilterParams(def) {\n\treturn Object.fromEntries(columnFilterParamKeys(def).map((key) => [key, []]))\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ import axios__default from '@nextcloud/axios';
2
+ import { generateOcsUrl } from '@nextcloud/router';
3
+
4
+ /**
5
+ * Nextcloud group search for the schema-driven form's group picker
6
+ * (a property marked `format: 'nc-group'` or `referenceType: 'nextcloud-group'`).
7
+ *
8
+ * Asks the core autocomplete OCS endpoint for groups first: every signed-in
9
+ * user may call it, and it returns the group's display name. The provisioning
10
+ * endpoint `cloud/groups` is the fallback; it also answers non-admins, but it
11
+ * returns only group ids, so the id doubles as the label there.
12
+ *
13
+ * Options are `{ id: <gid>, label: <display name> }`: the form stores the gid.
14
+ *
15
+ * @module utils/groupAutocomplete
16
+ */
17
+
18
+
19
+ const OCS_HEADERS = {
20
+ 'OCS-APIRequest': 'true',
21
+ Accept: 'application/json',
22
+ };
23
+
24
+ /**
25
+ * Read the `ocs.data` payload, tolerating a bare body.
26
+ *
27
+ * @param {object} response An axios response.
28
+ * @return {unknown} The data.
29
+ */
30
+ function ocsData(response) {
31
+ const body = response && response.data;
32
+ return body && body.ocs ? body.ocs.data : body
33
+ }
34
+
35
+ /**
36
+ * Map one autocomplete suggestion to an option, keeping only groups.
37
+ *
38
+ * @param {object} suggestion An autocomplete suggestion.
39
+ * @return {{id: string, label: string}|null} The option, or null when it is not a group.
40
+ */
41
+ function toGroupOption(suggestion) {
42
+ if (!suggestion || typeof suggestion !== 'object') {
43
+ return null
44
+ }
45
+ const isGroup = suggestion.source === 'groups'
46
+ || suggestion.shareType === 1
47
+ || (suggestion.value && suggestion.value.shareType === 1);
48
+ if (!isGroup) {
49
+ return null
50
+ }
51
+ const gid = suggestion.id || (suggestion.value && suggestion.value.shareWith);
52
+ if (gid === undefined || gid === null || gid === '') {
53
+ return null
54
+ }
55
+ return { id: String(gid), label: suggestion.label || String(gid) }
56
+ }
57
+
58
+ /**
59
+ * Search Nextcloud groups by name or id. Fails soft: an error yields `[]`.
60
+ *
61
+ * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
62
+ * @param {string} [query] The search term (empty loads a first page).
63
+ * @param {object} [options] Tuning options.
64
+ * @param {number} [options.limit] Max results (default 25).
65
+ * @return {Promise<Array<{id: string, label: string}>>} Picker options.
66
+ */
67
+ async function searchNextcloudGroups(query = '', options = {}) {
68
+ const { limit = 25 } = options;
69
+ try {
70
+ const response = await axios__default.get(generateOcsUrl('core/autocomplete/get'), {
71
+ headers: OCS_HEADERS,
72
+ params: {
73
+ search: query || '',
74
+ itemType: ' ',
75
+ itemId: ' ',
76
+ 'shareTypes[]': 1,
77
+ limit,
78
+ },
79
+ });
80
+ const list = ocsData(response);
81
+ const groups = (Array.isArray(list) ? list : [])
82
+ .map(toGroupOption)
83
+ .filter((opt) => opt !== null);
84
+ if (groups.length > 0) {
85
+ return groups
86
+ }
87
+ } catch {
88
+ // Fall through to the provisioning endpoint.
89
+ }
90
+ try {
91
+ const response = await axios__default.get(generateOcsUrl('cloud/groups'), {
92
+ headers: OCS_HEADERS,
93
+ params: { search: query || '', limit },
94
+ });
95
+ const data = ocsData(response);
96
+ const gids = data && Array.isArray(data.groups) ? data.groups : [];
97
+ return gids
98
+ .filter((gid) => typeof gid === 'string' && gid !== '')
99
+ .map((gid) => ({ id: gid, label: gid }))
100
+ } catch {
101
+ return []
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Resolve a stored gid to `{ id, label }` for edit-mode display. Falls back
107
+ * to the gid itself when the name cannot be looked up.
108
+ *
109
+ * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
110
+ * @param {string} gid The stored group id.
111
+ * @return {Promise<{id: string, label: string}>} The resolved option.
112
+ */
113
+ async function resolveNextcloudGroup(gid) {
114
+ const fallback = { id: String(gid), label: String(gid) };
115
+ if (gid === undefined || gid === null || gid === '') {
116
+ return fallback
117
+ }
118
+ const results = await searchNextcloudGroups(String(gid));
119
+ return results.find((opt) => opt.id === String(gid)) || fallback
120
+ }
121
+
122
+ export { resolveNextcloudGroup, searchNextcloudGroups };
123
+ //# sourceMappingURL=groupAutocomplete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groupAutocomplete.js","sources":["../../../src/utils/groupAutocomplete.js"],"sourcesContent":["/**\n * Nextcloud group search for the schema-driven form's group picker\n * (a property marked `format: 'nc-group'` or `referenceType: 'nextcloud-group'`).\n *\n * Asks the core autocomplete OCS endpoint for groups first: every signed-in\n * user may call it, and it returns the group's display name. The provisioning\n * endpoint `cloud/groups` is the fallback; it also answers non-admins, but it\n * returns only group ids, so the id doubles as the label there.\n *\n * Options are `{ id: <gid>, label: <display name> }`: the form stores the gid.\n *\n * @module utils/groupAutocomplete\n */\n\nimport axios from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\n\nconst OCS_HEADERS = {\n\t'OCS-APIRequest': 'true',\n\tAccept: 'application/json',\n}\n\n/**\n * Read the `ocs.data` payload, tolerating a bare body.\n *\n * @param {object} response An axios response.\n * @return {unknown} The data.\n */\nfunction ocsData(response) {\n\tconst body = response && response.data\n\treturn body && body.ocs ? body.ocs.data : body\n}\n\n/**\n * Map one autocomplete suggestion to an option, keeping only groups.\n *\n * @param {object} suggestion An autocomplete suggestion.\n * @return {{id: string, label: string}|null} The option, or null when it is not a group.\n */\nfunction toGroupOption(suggestion) {\n\tif (!suggestion || typeof suggestion !== 'object') {\n\t\treturn null\n\t}\n\tconst isGroup = suggestion.source === 'groups'\n\t\t|| suggestion.shareType === 1\n\t\t|| (suggestion.value && suggestion.value.shareType === 1)\n\tif (!isGroup) {\n\t\treturn null\n\t}\n\tconst gid = suggestion.id || (suggestion.value && suggestion.value.shareWith)\n\tif (gid === undefined || gid === null || gid === '') {\n\t\treturn null\n\t}\n\treturn { id: String(gid), label: suggestion.label || String(gid) }\n}\n\n/**\n * Search Nextcloud groups by name or id. Fails soft: an error yields `[]`.\n *\n * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n * @param {string} [query] The search term (empty loads a first page).\n * @param {object} [options] Tuning options.\n * @param {number} [options.limit] Max results (default 25).\n * @return {Promise<Array<{id: string, label: string}>>} Picker options.\n */\nexport async function searchNextcloudGroups(query = '', options = {}) {\n\tconst { limit = 25 } = options\n\ttry {\n\t\tconst response = await axios.get(generateOcsUrl('core/autocomplete/get'), {\n\t\t\theaders: OCS_HEADERS,\n\t\t\tparams: {\n\t\t\t\tsearch: query || '',\n\t\t\t\titemType: ' ',\n\t\t\t\titemId: ' ',\n\t\t\t\t'shareTypes[]': 1,\n\t\t\t\tlimit,\n\t\t\t},\n\t\t})\n\t\tconst list = ocsData(response)\n\t\tconst groups = (Array.isArray(list) ? list : [])\n\t\t\t.map(toGroupOption)\n\t\t\t.filter((opt) => opt !== null)\n\t\tif (groups.length > 0) {\n\t\t\treturn groups\n\t\t}\n\t} catch {\n\t\t// Fall through to the provisioning endpoint.\n\t}\n\ttry {\n\t\tconst response = await axios.get(generateOcsUrl('cloud/groups'), {\n\t\t\theaders: OCS_HEADERS,\n\t\t\tparams: { search: query || '', limit },\n\t\t})\n\t\tconst data = ocsData(response)\n\t\tconst gids = data && Array.isArray(data.groups) ? data.groups : []\n\t\treturn gids\n\t\t\t.filter((gid) => typeof gid === 'string' && gid !== '')\n\t\t\t.map((gid) => ({ id: gid, label: gid }))\n\t} catch {\n\t\treturn []\n\t}\n}\n\n/**\n * Resolve a stored gid to `{ id, label }` for edit-mode display. Falls back\n * to the gid itself when the name cannot be looked up.\n *\n * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n * @param {string} gid The stored group id.\n * @return {Promise<{id: string, label: string}>} The resolved option.\n */\nexport async function resolveNextcloudGroup(gid) {\n\tconst fallback = { id: String(gid), label: String(gid) }\n\tif (gid === undefined || gid === null || gid === '') {\n\t\treturn fallback\n\t}\n\tconst results = await searchNextcloudGroups(String(gid))\n\treturn results.find((opt) => opt.id === String(gid)) || fallback\n}\n"],"names":["axios"],"mappings":"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"}
@@ -68,6 +68,7 @@ const BUILT_IN_WIDGET_KEYS = Object.freeze([
68
68
  'integration',
69
69
  'banner',
70
70
  'audit-trail',
71
+ 'timeline',
71
72
  'header',
72
73
  'text',
73
74
  'divider',
@@ -132,6 +133,7 @@ const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([
132
133
  'tasks',
133
134
  'text',
134
135
  'tile',
136
+ 'timeline',
135
137
  'video',
136
138
  'week-strip',
137
139
  'workspace-filter',
@@ -1 +1 @@
1
- {"version":3,"file":"libraryWidgetKeys.js","sources":["../../../src/utils/libraryWidgetKeys.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * libraryWidgetKeys — the one vocabulary of widget keys the LIBRARY itself\n * renders, and the only list `validateManifestV2` is allowed to consult.\n *\n * WHY THIS MODULE EXISTS\n * ----------------------\n * The validator used to carry its own hand-written array of \"library built-in\n * widget keys\", with a comment instructing whoever added a built-in to append\n * it \"in the same PR\". That instruction was followed for five keys and missed\n * for six, so the validator knew 11 keys while the library actually rendered\n * 45. Nothing failed when the two disagreed, because the only consumer is an\n * exemption: a key the validator does not recognise is treated as a CUSTOM\n * registry component, which makes a legitimate single-widget library dashboard\n * fail the ADR-036 Decision 1 anti-pattern rule with advice (\"declare it as\n * type:'custom'\") that is wrong for a library widget.\n *\n * A comment cannot enforce anything, so the array moved here and\n * `tests/utils/libraryWidgetKeys.spec.js` now binds it to the two registries\n * that actually resolve a key at runtime:\n *\n * 1. `BUILT_IN_WIDGETS` (components/CnWidgetGrid/builtInWidgets.js) — the v2\n * manifest widget set `CnWidgetGrid` resolves a `widgetKey` against.\n * 2. The dashboard widget catalog (`dashboardWidgetRegistry`), populated by\n * `registerDashboardWidgets.js`. `CnWidgetGrid` falls back to it (third in\n * the chain, after the consumer registry and `BUILT_IN_WIDGETS`), so a\n * catalog key renders on a v2 page exactly like a `BUILT_IN_WIDGETS` one.\n *\n * The test asserts SET EQUALITY in both directions against the live registries,\n * so adding a widget to either registry without touching this file fails CI,\n * and deleting one without removing it here fails too. The lists below are the\n * validator's view; the registries are the truth the test holds them to.\n *\n * THE CATALOG IS NOT LEGACY. It is a live half of the same vocabulary, and the\n * old array already conceded the point by exempting `chart` and `stats-block`,\n * which are catalog-only keys that `BUILT_IN_WIDGETS` has never held. What it\n * got wrong was doing that for two keys and not the other thirty-seven.\n * `widgetDispatch.js` unified the two resolution paths for the same reason: a\n * widget authored against one vocabulary was invisible to the other and said\n * nothing about why (101 of 236 detail widgets on hrmq).\n *\n * @module utils/libraryWidgetKeys\n */\n\nimport { WIDGET_TYPE_ALIASES } from './widgetTypeAliases.js'\n\n/**\n * Keys of `BUILT_IN_WIDGETS` — the v2 manifest widget set.\n *\n * Kept in the registry's own declaration order so a diff against\n * `builtInWidgets.js` reads straight down.\n *\n * @type {string[]} Frozen.\n */\nexport const BUILT_IN_WIDGET_KEYS = Object.freeze([\n\t'object-table',\n\t'form-renderer',\n\t'map-viewer',\n\t'object-geo',\n\t'card-grid',\n\t'nav-card-grid',\n\t'data',\n\t'metadata',\n\t'related',\n\t'integration',\n\t'banner',\n\t'audit-trail',\n\t'header',\n\t'text',\n\t'divider',\n])\n\n/**\n * Widget types the LIBRARY registers into the dashboard catalog at import time\n * (`registerDashboardWidgets.js` and the widget `index.js` modules it pulls in).\n *\n * Deliberately a static list rather than a read of `dashboardWidgetRegistry`:\n * that object is mutable and consumer-extensible, so deriving the exemption\n * from it at call time would let an app register its own full-page component in\n * the catalog and walk straight through the ADR-036 Decision 1 rule the\n * exemption exists to enforce. Only keys the library itself ships are exempt.\n *\n * Sorted, because unlike `BUILT_IN_WIDGETS` these registrations are spread over\n * ~30 files and have no meaningful declaration order.\n *\n * @type {string[]} Frozen.\n */\nexport const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([\n\t'audit-trail',\n\t'banner',\n\t'calendar',\n\t'chart',\n\t'container',\n\t'conversation',\n\t'countdown',\n\t'data',\n\t'delta',\n\t'divider',\n\t'document-review',\n\t'files',\n\t'flow-runs',\n\t'gauge',\n\t'header',\n\t'image',\n\t'interaction-form',\n\t'kb-search',\n\t'label',\n\t'link',\n\t'links',\n\t'map',\n\t'menu',\n\t'nc-widget',\n\t'next-step',\n\t// `news` was here until the widget moved to LaunchPad, which owns the feed\n\t// fetching. An app that renders one now gets it from LaunchPad's registry.\n\t'object-geo',\n\t'object-list',\n\t'object-table',\n\t'people',\n\t'quicklinks',\n\t'related',\n\t'spend-analytics',\n\t'stacked-bar',\n\t'stages',\n\t'stat',\n\t'stats-block',\n\t'table',\n\t'tabs',\n\t'tasks',\n\t'text',\n\t'tile',\n\t'video',\n\t'week-strip',\n\t'workspace-filter',\n])\n\n/**\n * Expand a key set over `WIDGET_TYPE_ALIASES` so both spellings of an aliased\n * widget are present whenever either one is.\n *\n * `CnWidgetGrid` resolves a key and its canonical form (`object-table` ⇄\n * `table`, `map-viewer` ⇄ `map`), so both spellings render and both must be\n * exempt. Today each registry happens to hold one side of each pair, which is\n * exactly the accident that would silently stop being true when the next alias\n * lands.\n *\n * @param {Set<string>} keys The key set to expand, mutated in place.\n * @return {Set<string>} The same set.\n */\nfunction expandAliases(keys) {\n\tfor (const [alias, canonical] of Object.entries(WIDGET_TYPE_ALIASES)) {\n\t\tif (keys.has(alias) || keys.has(canonical)) {\n\t\t\tkeys.add(alias)\n\t\t\tkeys.add(canonical)\n\t\t}\n\t}\n\treturn keys\n}\n\n/**\n * Every widget key the library renders itself: both registries, plus both\n * spellings of every alias.\n *\n * A `widgetKey` in this list is served by a library `Cn*` SFC and does NOT\n * count as a custom registry component for the single-12×12-widget dashboard\n * rule (ADR-036 Decision 1).\n *\n * @type {string[]} Frozen.\n */\nexport const LIBRARY_WIDGET_KEYS = Object.freeze([\n\t...expandAliases(new Set([\n\t\t...BUILT_IN_WIDGET_KEYS,\n\t\t...DASHBOARD_CATALOG_WIDGET_KEYS,\n\t])),\n])\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"libraryWidgetKeys.js","sources":["../../../src/utils/libraryWidgetKeys.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * libraryWidgetKeys — the one vocabulary of widget keys the LIBRARY itself\n * renders, and the only list `validateManifestV2` is allowed to consult.\n *\n * WHY THIS MODULE EXISTS\n * ----------------------\n * The validator used to carry its own hand-written array of \"library built-in\n * widget keys\", with a comment instructing whoever added a built-in to append\n * it \"in the same PR\". That instruction was followed for five keys and missed\n * for six, so the validator knew 11 keys while the library actually rendered\n * 45. Nothing failed when the two disagreed, because the only consumer is an\n * exemption: a key the validator does not recognise is treated as a CUSTOM\n * registry component, which makes a legitimate single-widget library dashboard\n * fail the ADR-036 Decision 1 anti-pattern rule with advice (\"declare it as\n * type:'custom'\") that is wrong for a library widget.\n *\n * A comment cannot enforce anything, so the array moved here and\n * `tests/utils/libraryWidgetKeys.spec.js` now binds it to the two registries\n * that actually resolve a key at runtime:\n *\n * 1. `BUILT_IN_WIDGETS` (components/CnWidgetGrid/builtInWidgets.js) — the v2\n * manifest widget set `CnWidgetGrid` resolves a `widgetKey` against.\n * 2. The dashboard widget catalog (`dashboardWidgetRegistry`), populated by\n * `registerDashboardWidgets.js`. `CnWidgetGrid` falls back to it (third in\n * the chain, after the consumer registry and `BUILT_IN_WIDGETS`), so a\n * catalog key renders on a v2 page exactly like a `BUILT_IN_WIDGETS` one.\n *\n * The test asserts SET EQUALITY in both directions against the live registries,\n * so adding a widget to either registry without touching this file fails CI,\n * and deleting one without removing it here fails too. The lists below are the\n * validator's view; the registries are the truth the test holds them to.\n *\n * THE CATALOG IS NOT LEGACY. It is a live half of the same vocabulary, and the\n * old array already conceded the point by exempting `chart` and `stats-block`,\n * which are catalog-only keys that `BUILT_IN_WIDGETS` has never held. What it\n * got wrong was doing that for two keys and not the other thirty-seven.\n * `widgetDispatch.js` unified the two resolution paths for the same reason: a\n * widget authored against one vocabulary was invisible to the other and said\n * nothing about why (101 of 236 detail widgets on hrmq).\n *\n * @module utils/libraryWidgetKeys\n */\n\nimport { WIDGET_TYPE_ALIASES } from './widgetTypeAliases.js'\n\n/**\n * Keys of `BUILT_IN_WIDGETS` — the v2 manifest widget set.\n *\n * Kept in the registry's own declaration order so a diff against\n * `builtInWidgets.js` reads straight down.\n *\n * @type {string[]} Frozen.\n */\nexport const BUILT_IN_WIDGET_KEYS = Object.freeze([\n\t'object-table',\n\t'form-renderer',\n\t'map-viewer',\n\t'object-geo',\n\t'card-grid',\n\t'nav-card-grid',\n\t'data',\n\t'metadata',\n\t'related',\n\t'integration',\n\t'banner',\n\t'audit-trail',\n\t'timeline',\n\t'header',\n\t'text',\n\t'divider',\n])\n\n/**\n * Widget types the LIBRARY registers into the dashboard catalog at import time\n * (`registerDashboardWidgets.js` and the widget `index.js` modules it pulls in).\n *\n * Deliberately a static list rather than a read of `dashboardWidgetRegistry`:\n * that object is mutable and consumer-extensible, so deriving the exemption\n * from it at call time would let an app register its own full-page component in\n * the catalog and walk straight through the ADR-036 Decision 1 rule the\n * exemption exists to enforce. Only keys the library itself ships are exempt.\n *\n * Sorted, because unlike `BUILT_IN_WIDGETS` these registrations are spread over\n * ~30 files and have no meaningful declaration order.\n *\n * @type {string[]} Frozen.\n */\nexport const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([\n\t'audit-trail',\n\t'banner',\n\t'calendar',\n\t'chart',\n\t'container',\n\t'conversation',\n\t'countdown',\n\t'data',\n\t'delta',\n\t'divider',\n\t'document-review',\n\t'files',\n\t'flow-runs',\n\t'gauge',\n\t'header',\n\t'image',\n\t'interaction-form',\n\t'kb-search',\n\t'label',\n\t'link',\n\t'links',\n\t'map',\n\t'menu',\n\t'nc-widget',\n\t'next-step',\n\t// `news` was here until the widget moved to LaunchPad, which owns the feed\n\t// fetching. An app that renders one now gets it from LaunchPad's registry.\n\t'object-geo',\n\t'object-list',\n\t'object-table',\n\t'people',\n\t'quicklinks',\n\t'related',\n\t'spend-analytics',\n\t'stacked-bar',\n\t'stages',\n\t'stat',\n\t'stats-block',\n\t'table',\n\t'tabs',\n\t'tasks',\n\t'text',\n\t'tile',\n\t'timeline',\n\t'video',\n\t'week-strip',\n\t'workspace-filter',\n])\n\n/**\n * Expand a key set over `WIDGET_TYPE_ALIASES` so both spellings of an aliased\n * widget are present whenever either one is.\n *\n * `CnWidgetGrid` resolves a key and its canonical form (`object-table` ⇄\n * `table`, `map-viewer` ⇄ `map`), so both spellings render and both must be\n * exempt. Today each registry happens to hold one side of each pair, which is\n * exactly the accident that would silently stop being true when the next alias\n * lands.\n *\n * @param {Set<string>} keys The key set to expand, mutated in place.\n * @return {Set<string>} The same set.\n */\nfunction expandAliases(keys) {\n\tfor (const [alias, canonical] of Object.entries(WIDGET_TYPE_ALIASES)) {\n\t\tif (keys.has(alias) || keys.has(canonical)) {\n\t\t\tkeys.add(alias)\n\t\t\tkeys.add(canonical)\n\t\t}\n\t}\n\treturn keys\n}\n\n/**\n * Every widget key the library renders itself: both registries, plus both\n * spellings of every alias.\n *\n * A `widgetKey` in this list is served by a library `Cn*` SFC and does NOT\n * count as a custom registry component for the single-12×12-widget dashboard\n * rule (ADR-036 Decision 1).\n *\n * @type {string[]} Frozen.\n */\nexport const LIBRARY_WIDGET_KEYS = Object.freeze([\n\t...expandAliases(new Set([\n\t\t...BUILT_IN_WIDGET_KEYS,\n\t\t...DASHBOARD_CATALOG_WIDGET_KEYS,\n\t])),\n])\n"],"names":[],"mappings":"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"}