@conduction/nextcloud-vue 2.104.0 → 2.106.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 (224) hide show
  1. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  2. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -1
  4. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  5. package/dist/esm/components/CnCardGrid/CnCardGrid.vue.js +1 -1
  6. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +18 -0
  7. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  8. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +3 -1
  9. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  10. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
  11. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +18 -0
  12. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  13. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +1 -1
  14. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  15. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  16. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +93 -1
  17. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  18. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  19. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  20. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +6 -3
  21. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  22. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +3 -3
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  24. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +112 -5
  25. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  26. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  27. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +9 -0
  28. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js.map +1 -0
  29. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js +24 -0
  30. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -0
  31. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js +49 -0
  32. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -0
  33. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  34. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +36 -18
  35. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  36. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +17 -38
  37. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  38. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  39. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  40. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  41. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue.js +1 -1
  42. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +1 -1
  43. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  44. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  45. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue.js +1 -1
  46. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js +2 -0
  47. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js.map +1 -1
  48. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js +32 -29
  49. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js.map +1 -1
  50. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  51. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +53 -2
  52. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  53. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +119 -111
  54. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  55. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue.js +1 -1
  56. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +62 -1
  57. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
  58. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
  59. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue.js +1 -1
  60. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js +73 -4
  61. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js.map +1 -1
  62. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js +74 -27
  63. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js.map +1 -1
  64. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +208 -46
  65. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  66. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +121 -98
  67. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  68. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +1 -1
  69. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +24 -0
  70. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -1
  71. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +20 -4
  72. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -1
  73. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +25 -6
  74. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  75. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +112 -1
  76. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  77. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +100 -44
  78. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  79. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue.js +1 -1
  80. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js +67 -4
  81. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js.map +1 -1
  82. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js +1 -1
  83. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js.map +1 -1
  84. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue.js +9 -0
  85. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue.js.map +1 -0
  86. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue2.js +88 -0
  87. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue2.js.map +1 -0
  88. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue3.js +39 -0
  89. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue3.js.map +1 -0
  90. package/dist/esm/components/CnObjectCard/CnObjectCard.vue.js +1 -1
  91. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +295 -12
  92. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  93. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +267 -144
  94. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  95. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
  96. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +4 -0
  97. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  98. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +3 -1
  99. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  100. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  101. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +93 -1
  102. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  103. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +322 -246
  104. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  105. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue.js +1 -1
  106. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +122 -7
  107. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
  108. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js +189 -93
  109. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js.map +1 -1
  110. package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +1 -1
  111. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -1
  112. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +3 -3
  113. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -1
  114. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
  115. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  116. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +16 -4
  117. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  118. package/dist/esm/components/CnTabs/CnTabs.vue.js +1 -1
  119. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  120. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  121. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  122. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +42 -0
  123. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  124. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +38 -19
  125. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  126. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue.js +9 -0
  127. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue.js.map +1 -0
  128. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue2.js +258 -0
  129. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue2.js.map +1 -0
  130. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue3.js +69 -0
  131. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue3.js.map +1 -0
  132. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
  133. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js +11 -0
  134. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
  135. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +4 -4
  136. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
  137. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +16 -14
  138. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  139. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
  140. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  141. package/dist/esm/index.js +2 -0
  142. package/dist/esm/index.js.map +1 -1
  143. package/dist/esm/l10n/en.json.js +24 -1
  144. package/dist/esm/l10n/en.json.js.map +1 -1
  145. package/dist/esm/l10n/nl.json.js +24 -1
  146. package/dist/esm/l10n/nl.json.js.map +1 -1
  147. package/dist/esm/library-style.js +1 -1
  148. package/dist/esm/nextcloud-vue.css +1369 -748
  149. package/dist/esm/utils/dueText.js +18 -3
  150. package/dist/esm/utils/dueText.js.map +1 -1
  151. package/dist/esm/utils/libraryWidgetKeys.js +2 -0
  152. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  153. package/dist/esm/utils/nextcloudApps.js +45 -0
  154. package/dist/esm/utils/nextcloudApps.js.map +1 -0
  155. package/dist/esm/utils/scopeListLayout.js +9 -2
  156. package/dist/esm/utils/scopeListLayout.js.map +1 -1
  157. package/dist/esm/utils/sortOrder.js +11 -1
  158. package/dist/esm/utils/sortOrder.js.map +1 -1
  159. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  160. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  161. package/dist/esm/utils/visibleWhen.js +12 -4
  162. package/dist/esm/utils/visibleWhen.js.map +1 -1
  163. package/dist/nextcloud-vue.cjs.js +8965 -6497
  164. package/dist/nextcloud-vue.cjs.js.map +2 -2
  165. package/dist/nextcloud-vue.css +1369 -748
  166. package/l10n/en.json +24 -1
  167. package/l10n/nl.json +24 -1
  168. package/package.json +1 -1
  169. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -1
  170. package/src/components/CnCardGrid/CnCardGrid.md +2 -0
  171. package/src/components/CnCardGrid/CnCardGrid.vue +20 -0
  172. package/src/components/CnCellRenderer/CnCellRenderer.vue +19 -1
  173. package/src/components/CnChartWidget/CnChartWidget.md +2 -0
  174. package/src/components/CnChartWidget/CnChartWidget.vue +93 -1
  175. package/src/components/CnDashboardPage/CnDashboardPage.vue +9 -6
  176. package/src/components/CnDetailPage/CnDetailPage.md +3 -2
  177. package/src/components/CnDetailPage/CnDetailPage.vue +112 -5
  178. package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +60 -0
  179. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +36 -29
  180. package/src/components/CnFactsWidget/CnFactsWidget.vue +4 -0
  181. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -1
  182. package/src/components/CnFeaturesBoard/CnFeaturesBoard.vue +4 -1
  183. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +70 -4
  184. package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +62 -1
  185. package/src/components/CnFlowsPage/CnFlowBoard.vue +132 -8
  186. package/src/components/CnIndexPage/CnIndexPage.md +8 -5
  187. package/src/components/CnIndexPage/CnIndexPage.vue +236 -54
  188. package/src/components/CnIndexPage/CnIndexPageWidgets.vue +36 -1
  189. package/src/components/CnIndexPage/useSelfFetchList.js +25 -6
  190. package/src/components/CnIndexSidebar/CnIndexSidebar.md +2 -0
  191. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +148 -1
  192. package/src/components/CnNcWidgetWidget/CnNcWidgetWidget.vue +68 -5
  193. package/src/components/CnNoticeWidget/CnNoticeWidget.vue +153 -0
  194. package/src/components/CnNoticeWidget/index.js +12 -0
  195. package/src/components/CnObjectCard/CnObjectCard.md +1 -0
  196. package/src/components/CnObjectCard/CnObjectCard.vue +354 -13
  197. package/src/components/CnObjectSidebar/CnFilesTab.vue +6 -0
  198. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +190 -4
  199. package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +209 -9
  200. package/src/components/CnSettingsField/CnSettingsField.vue +4 -1
  201. package/src/components/CnSetupWizard/CnSetupWizard.vue +9 -1
  202. package/src/components/CnTabs/CnTabs.vue +11 -0
  203. package/src/components/CnTabsWidget/CnTabsWidget.vue +91 -18
  204. package/src/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.md +11 -0
  205. package/src/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue +382 -0
  206. package/src/components/CnTeamWorkloadWidget/index.js +14 -0
  207. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +15 -4
  208. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  209. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
  210. package/src/components/index.js +2 -0
  211. package/src/css/dialog.css +20 -2
  212. package/src/css/index-page.css +14 -2
  213. package/src/css/look-board-detail.css +125 -8
  214. package/src/css/look-board-index.css +100 -4
  215. package/src/css/look-board.css +122 -14
  216. package/src/index.js +2 -1
  217. package/src/schemas/app-manifest-v2.schema.json +110 -8
  218. package/src/utils/dueText.js +18 -3
  219. package/src/utils/libraryWidgetKeys.js +2 -0
  220. package/src/utils/nextcloudApps.js +50 -0
  221. package/src/utils/scopeListLayout.js +9 -2
  222. package/src/utils/sortOrder.js +10 -0
  223. package/src/utils/validateManifestV2.compiled.js +3 -3
  224. package/src/utils/visibleWhen.js +12 -4
@@ -2,7 +2,7 @@ import script from './CnIndexPageWidgets.vue2.js';
2
2
  import { render } from './CnIndexPageWidgets.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-cf9fcc76";
5
+ script.__scopeId = "data-v-096a5eee";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -39,6 +39,13 @@ var script = {
39
39
  },
40
40
  },
41
41
 
42
+ data() {
43
+ return {
44
+ /** The header note each widget reported (`header` event), by widget id. */
45
+ headerNotes: {},
46
+ }
47
+ },
48
+
42
49
  computed: {
43
50
  /**
44
51
  * The renderable entries, in the listed order.
@@ -76,6 +83,23 @@ var script = {
76
83
  },
77
84
 
78
85
  methods: {
86
+ /**
87
+ * Keep the header note a widget reports ("4 van 5 aanwezig"); only a
88
+ * changed note is stored, so a report on render does not loop.
89
+ *
90
+ * @param {string} id The widget id.
91
+ * @param {object} header `{ note?, link? }` from the widget.
92
+ * @return {void}
93
+ * @spec openspec/changes/side-panel-header-note/specs/side-panel-header-note/spec.md#requirement-a-side-panel-card-shows-the-note-its-widget-reports
94
+ */
95
+ onHeader(id, header) {
96
+ const note = header && typeof header === 'object' && typeof header.note === 'string' ? header.note : '';
97
+ if ((this.headerNotes[id] || '') === note) {
98
+ return
99
+ }
100
+ this.headerNotes = { ...this.headerNotes, [id]: note };
101
+ },
102
+
79
103
  /**
80
104
  * The content keys an entry's renderer declares as props. An undeclared
81
105
  * key would fall through onto the renderer's root; the whole content
@@ -1 +1 @@
1
- {"version":3,"file":"CnIndexPageWidgets.vue2.js","sources":["../../../../src/components/CnIndexPage/CnIndexPageWidgets.vue"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n\n CnIndexPageWidgets — the widgets an index page draws around its list\n (screens-index-stat-row): the KPI tiles above it (`statRow`, an auto-fit row)\n and the cards beside it (`sidePanel`, a column). Each entry is a dashboard\n widget definition `{ id, type, title?, content, headerLink? }`, resolved like\n a dashboard widget: the app's registry, then the dashboard catalog, then the\n built-in widgets. Internal to CnIndexPage.\n-->\n<template>\n\t<component\n\t\t:is=\"variant === 'stat-row' ? 'CnKpiGrid' : (variant === 'after-list' ? 'section' : 'aside')\"\n\t\tv-if=\"entries.length > 0\"\n\t\t:columns=\"variant === 'stat-row' ? 'auto' : undefined\"\n\t\tclass=\"cn-index-page-widgets\"\n\t\t:class=\"['cn-index-page-widgets--' + variant]\"\n\t\t:data-testid=\"'cn-index-page-' + variant\">\n\t\t<div\n\t\t\tv-for=\"entry in entries\"\n\t\t\t:key=\"entry.id\"\n\t\t\tclass=\"cn-index-page-widgets__cell\"\n\t\t\t:data-testid=\"'cn-index-page-widget-' + entry.id\">\n\t\t\t<CnWidgetWrapper\n\t\t\t\t:widgetId=\"entry.id\"\n\t\t\t\t:title=\"entry.title\"\n\t\t\t\t:showTitle=\"variant !== 'stat-row' && entry.title !== ''\"\n\t\t\t\t:showActions=\"false\"\n\t\t\t\t:showRefresh=\"false\"\n\t\t\t\t:headerLink=\"entry.headerLink\"\n\t\t\t\t:flush=\"variant === 'stat-row'\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"entry.renderer\"\n\t\t\t\t\t:widgetId=\"entry.id\"\n\t\t\t\t\t:content=\"entry.content\"\n\t\t\t\t\tv-bind=\"declaredContent(entry)\" />\n\t\t\t</CnWidgetWrapper>\n\t\t</div>\n\t</component>\n</template>\n\n<script>\nimport CnKpiGrid from '../CnKpiGrid/CnKpiGrid.vue'\nimport CnWidgetWrapper from '../CnWidgetWrapper/CnWidgetWrapper.vue'\nimport { pickDeclaredProps } from '../../utils/declaredProps.js'\nimport { canonicalWidgetType } from '../../utils/widgetTypeAliases.js'\nimport { BUILT_IN_WIDGETS } from '../CnWidgetGrid/builtInWidgets.js'\nimport { getWidgetTypeEntry } from '../CnWidgetGrid/dashboardWidgetRegistry.js'\n\n// Populate the dashboard catalog (stat, table, people, …): an index page\n// may be the first surface that resolves a widget type.\nimport '../CnWidgetGrid/registerDashboardWidgets.js'\n\nexport default {\n\tname: 'CnIndexPageWidgets',\n\n\tcomponents: {\n\t\tCnKpiGrid,\n\t\tCnWidgetWrapper,\n\t},\n\n\tinject: {\n\t\t/** Consumer widget registry provided by CnAppRoot (custom over built-in). */\n\t\tcnRegistry: { default: () => ({}) },\n\t\t/** Host translate function provided by CnAppRoot. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Widget definitions `{ id, type, title?, content?, headerLink? }`.\n\t\t * An entry without an id or with a type no layer resolves is skipped.\n\t\t */\n\t\twidgets: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Where the widgets sit: `stat-row` above the list, `side-panel` beside it, `after-list` under it. */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: 'stat-row',\n\t\t\tvalidator: (v) => ['stat-row', 'side-panel', 'after-list'].includes(v),\n\t\t},\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The renderable entries, in the listed order.\n\t\t *\n\t\t * @spec openspec/changes/screens-index-stat-row/specs/index-page/spec.md#requirement-an-index-page-can-draw-a-stat-row-above-the-list\n\t\t * @return {Array<{id: string, type: string, title: string, content: object, headerLink: (object|null), renderer: object}>}\n\t\t */\n\t\tentries() {\n\t\t\tconst list = Array.isArray(this.widgets) ? this.widgets : []\n\t\t\tconst out = []\n\t\t\tfor (const def of list) {\n\t\t\t\tif (!def || typeof def.id !== 'string' || def.id === '' || typeof def.type !== 'string') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst renderer = this.resolveRenderer(def.type)\n\t\t\t\tif (!renderer) {\n\t\t\t\t\tif (process.env.NODE_ENV !== 'production') {\n\t\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\t\tconsole.warn(`[CnIndexPage] ${this.variant} widget \"${def.id}\" has type \"${def.type}\", which no widget registry resolves; skipped.`)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key\n\t\t\t\tout.push({\n\t\t\t\t\tid: def.id,\n\t\t\t\t\ttype: def.type,\n\t\t\t\t\ttitle: typeof def.title === 'string' && def.title !== '' ? translate(def.title) : '',\n\t\t\t\t\tcontent: (def.content && typeof def.content === 'object') ? def.content : {},\n\t\t\t\t\theaderLink: (def.headerLink && typeof def.headerLink === 'object') ? def.headerLink : null,\n\t\t\t\t\trenderer,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The content keys an entry's renderer declares as props. An undeclared\n\t\t * key would fall through onto the renderer's root; the whole content\n\t\t * still arrives as `content`.\n\t\t *\n\t\t * @param {{ renderer: object, content: object, type?: string, id: string }} entry A resolved entry.\n\t\t * @return {object} The declared content keys.\n\t\t * @spec openspec/changes/declared-props-detail-index-sidebar/specs/widget-hosts/spec.md#requirement-a-widget-host-passes-only-the-keys-a-widget-declares\n\t\t */\n\t\tdeclaredContent(entry) {\n\t\t\treturn pickDeclaredProps(entry.renderer, entry.content, entry.type || entry.id)\n\t\t},\n\n\t\t/**\n\t\t * Resolve a widget type: the consumer registry, then the dashboard\n\t\t * catalog (the type as written, then its alias), then the built-ins.\n\t\t *\n\t\t * @param {string} type The widget type.\n\t\t * @return {object|null} The component, or null.\n\t\t */\n\t\tresolveRenderer(type) {\n\t\t\tconst consumer = (this.cnRegistry || {})[type]\n\t\t\tif (consumer) {\n\t\t\t\treturn consumer.component ?? consumer\n\t\t\t}\n\t\t\tconst entry = getWidgetTypeEntry(type) || getWidgetTypeEntry(canonicalWidgetType(type))\n\t\t\tif (entry && entry.renderer) {\n\t\t\t\treturn entry.renderer\n\t\t\t}\n\t\t\treturn BUILT_IN_WIDGETS[type] || BUILT_IN_WIDGETS[canonicalWidgetType(type)] || null\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-index-page-widgets--stat-row {\n\tmargin-bottom: var(--cn-board-section-gap, 20px);\n}\n\n.cn-index-page-widgets__cell {\n\tdisplay: flex;\n\tmin-width: 0;\n}\n\n.cn-index-page-widgets__cell > * {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n/* The cards after the list, stacked full width (PqSocial). */\n.cn-index-page-widgets--after-list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--cn-board-section-gap, 16px);\n\tmargin-top: var(--cn-board-section-gap, 16px);\n\tmin-width: 0;\n}\n\n/* The side panel column (DqTeamwachtrij: 300px, 16px between cards). Its\n place beside the toolbar and the list is set by CnIndexPage\n (`cn-index-page--with-side-panel`, index-page.css). */\n.cn-index-page-widgets--side-panel {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tmin-width: 0;\n}\n</style>\n"],"names":["CnKpiGrid","CnWidgetWrapper"],"mappings":"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"}
1
+ {"version":3,"file":"CnIndexPageWidgets.vue2.js","sources":["../../../../src/components/CnIndexPage/CnIndexPageWidgets.vue"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n\n CnIndexPageWidgets — the widgets an index page draws around its list\n (screens-index-stat-row): the KPI tiles above it (`statRow`, an auto-fit row)\n and the cards beside it (`sidePanel`, a column). Each entry is a dashboard\n widget definition `{ id, type, title?, content, headerLink? }`, resolved like\n a dashboard widget: the app's registry, then the dashboard catalog, then the\n built-in widgets. Internal to CnIndexPage.\n-->\n<template>\n\t<component\n\t\t:is=\"variant === 'stat-row' ? 'CnKpiGrid' : (variant === 'after-list' ? 'section' : 'aside')\"\n\t\tv-if=\"entries.length > 0\"\n\t\t:columns=\"variant === 'stat-row' ? 'auto' : undefined\"\n\t\tclass=\"cn-index-page-widgets\"\n\t\t:class=\"['cn-index-page-widgets--' + variant]\"\n\t\t:data-testid=\"'cn-index-page-' + variant\">\n\t\t<div\n\t\t\tv-for=\"entry in entries\"\n\t\t\t:key=\"entry.id\"\n\t\t\tclass=\"cn-index-page-widgets__cell\"\n\t\t\t:data-testid=\"'cn-index-page-widget-' + entry.id\">\n\t\t\t<CnWidgetWrapper\n\t\t\t\t:widgetId=\"entry.id\"\n\t\t\t\t:title=\"entry.title\"\n\t\t\t\t:showTitle=\"variant !== 'stat-row' && entry.title !== ''\"\n\t\t\t\t:showActions=\"false\"\n\t\t\t\t:showRefresh=\"false\"\n\t\t\t\t:headerLink=\"entry.headerLink\"\n\t\t\t\t:flush=\"variant === 'stat-row'\">\n\t\t\t\t<template v-if=\"headerNotes[entry.id]\" #actions>\n\t\t\t\t\t<span class=\"cn-index-page-widgets__note\" data-testid=\"cn-index-page-widget-note\">{{ headerNotes[entry.id] }}</span>\n\t\t\t\t</template>\n\t\t\t\t<component\n\t\t\t\t\t:is=\"entry.renderer\"\n\t\t\t\t\t:widgetId=\"entry.id\"\n\t\t\t\t\t:content=\"entry.content\"\n\t\t\t\t\tv-bind=\"declaredContent(entry)\"\n\t\t\t\t\t@header=\"onHeader(entry.id, $event)\" />\n\t\t\t</CnWidgetWrapper>\n\t\t</div>\n\t</component>\n</template>\n\n<script>\nimport CnKpiGrid from '../CnKpiGrid/CnKpiGrid.vue'\nimport CnWidgetWrapper from '../CnWidgetWrapper/CnWidgetWrapper.vue'\nimport { pickDeclaredProps } from '../../utils/declaredProps.js'\nimport { canonicalWidgetType } from '../../utils/widgetTypeAliases.js'\nimport { BUILT_IN_WIDGETS } from '../CnWidgetGrid/builtInWidgets.js'\nimport { getWidgetTypeEntry } from '../CnWidgetGrid/dashboardWidgetRegistry.js'\n\n// Populate the dashboard catalog (stat, table, people, …): an index page\n// may be the first surface that resolves a widget type.\nimport '../CnWidgetGrid/registerDashboardWidgets.js'\n\nexport default {\n\tname: 'CnIndexPageWidgets',\n\n\tcomponents: {\n\t\tCnKpiGrid,\n\t\tCnWidgetWrapper,\n\t},\n\n\tinject: {\n\t\t/** Consumer widget registry provided by CnAppRoot (custom over built-in). */\n\t\tcnRegistry: { default: () => ({}) },\n\t\t/** Host translate function provided by CnAppRoot. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Widget definitions `{ id, type, title?, content?, headerLink? }`.\n\t\t * An entry without an id or with a type no layer resolves is skipped.\n\t\t */\n\t\twidgets: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Where the widgets sit: `stat-row` above the list, `side-panel` beside it, `after-list` under it. */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: 'stat-row',\n\t\t\tvalidator: (v) => ['stat-row', 'side-panel', 'after-list'].includes(v),\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\t/** The header note each widget reported (`header` event), by widget id. */\n\t\t\theaderNotes: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The renderable entries, in the listed order.\n\t\t *\n\t\t * @spec openspec/changes/screens-index-stat-row/specs/index-page/spec.md#requirement-an-index-page-can-draw-a-stat-row-above-the-list\n\t\t * @return {Array<{id: string, type: string, title: string, content: object, headerLink: (object|null), renderer: object}>}\n\t\t */\n\t\tentries() {\n\t\t\tconst list = Array.isArray(this.widgets) ? this.widgets : []\n\t\t\tconst out = []\n\t\t\tfor (const def of list) {\n\t\t\t\tif (!def || typeof def.id !== 'string' || def.id === '' || typeof def.type !== 'string') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst renderer = this.resolveRenderer(def.type)\n\t\t\t\tif (!renderer) {\n\t\t\t\t\tif (process.env.NODE_ENV !== 'production') {\n\t\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\t\tconsole.warn(`[CnIndexPage] ${this.variant} widget \"${def.id}\" has type \"${def.type}\", which no widget registry resolves; skipped.`)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key\n\t\t\t\tout.push({\n\t\t\t\t\tid: def.id,\n\t\t\t\t\ttype: def.type,\n\t\t\t\t\ttitle: typeof def.title === 'string' && def.title !== '' ? translate(def.title) : '',\n\t\t\t\t\tcontent: (def.content && typeof def.content === 'object') ? def.content : {},\n\t\t\t\t\theaderLink: (def.headerLink && typeof def.headerLink === 'object') ? def.headerLink : null,\n\t\t\t\t\trenderer,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Keep the header note a widget reports (\"4 van 5 aanwezig\"); only a\n\t\t * changed note is stored, so a report on render does not loop.\n\t\t *\n\t\t * @param {string} id The widget id.\n\t\t * @param {object} header `{ note?, link? }` from the widget.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/side-panel-header-note/specs/side-panel-header-note/spec.md#requirement-a-side-panel-card-shows-the-note-its-widget-reports\n\t\t */\n\t\tonHeader(id, header) {\n\t\t\tconst note = header && typeof header === 'object' && typeof header.note === 'string' ? header.note : ''\n\t\t\tif ((this.headerNotes[id] || '') === note) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.headerNotes = { ...this.headerNotes, [id]: note }\n\t\t},\n\n\t\t/**\n\t\t * The content keys an entry's renderer declares as props. An undeclared\n\t\t * key would fall through onto the renderer's root; the whole content\n\t\t * still arrives as `content`.\n\t\t *\n\t\t * @param {{ renderer: object, content: object, type?: string, id: string }} entry A resolved entry.\n\t\t * @return {object} The declared content keys.\n\t\t * @spec openspec/changes/declared-props-detail-index-sidebar/specs/widget-hosts/spec.md#requirement-a-widget-host-passes-only-the-keys-a-widget-declares\n\t\t */\n\t\tdeclaredContent(entry) {\n\t\t\treturn pickDeclaredProps(entry.renderer, entry.content, entry.type || entry.id)\n\t\t},\n\n\t\t/**\n\t\t * Resolve a widget type: the consumer registry, then the dashboard\n\t\t * catalog (the type as written, then its alias), then the built-ins.\n\t\t *\n\t\t * @param {string} type The widget type.\n\t\t * @return {object|null} The component, or null.\n\t\t */\n\t\tresolveRenderer(type) {\n\t\t\tconst consumer = (this.cnRegistry || {})[type]\n\t\t\tif (consumer) {\n\t\t\t\treturn consumer.component ?? consumer\n\t\t\t}\n\t\t\tconst entry = getWidgetTypeEntry(type) || getWidgetTypeEntry(canonicalWidgetType(type))\n\t\t\tif (entry && entry.renderer) {\n\t\t\t\treturn entry.renderer\n\t\t\t}\n\t\t\treturn BUILT_IN_WIDGETS[type] || BUILT_IN_WIDGETS[canonicalWidgetType(type)] || null\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-index-page-widgets__note {\n\talign-self: center;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n\twhite-space: nowrap;\n}\n\n.cn-index-page-widgets--stat-row {\n\tmargin-bottom: var(--cn-board-section-gap, 20px);\n}\n\n.cn-index-page-widgets__cell {\n\tdisplay: flex;\n\tmin-width: 0;\n}\n\n.cn-index-page-widgets__cell > * {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n/* The cards after the list, stacked full width (PqSocial). */\n.cn-index-page-widgets--after-list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--cn-board-section-gap, 16px);\n\tmargin-top: var(--cn-board-section-gap, 16px);\n\tmin-width: 0;\n}\n\n/* The side panel column (DqTeamwachtrij: 300px, 16px between cards). Its\n place beside the toolbar and the list is set by CnIndexPage\n (`cn-index-page--with-side-panel`, index-page.css). */\n.cn-index-page-widgets--side-panel {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tmin-width: 0;\n}\n</style>\n"],"names":["CnKpiGrid","CnWidgetWrapper"],"mappings":"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"}
@@ -1,6 +1,10 @@
1
- import { resolveComponent, openBlock, createBlock, resolveDynamicComponent, normalizeClass, withCtx, createElementBlock, Fragment, renderList, createVNode, mergeProps, createCommentVNode } from 'vue';
1
+ import { resolveComponent, openBlock, createBlock, resolveDynamicComponent, normalizeClass, withCtx, createElementBlock, Fragment, renderList, createVNode, createSlots, mergeProps, createElementVNode, toDisplayString, createCommentVNode } from 'vue';
2
2
 
3
3
  const _hoisted_1 = ["data-testid"];
4
+ const _hoisted_2 = {
5
+ class: "cn-index-page-widgets__note",
6
+ "data-testid": "cn-index-page-widget-note"
7
+ };
4
8
 
5
9
  function render(_ctx, _cache, $props, $setup, $data, $options) {
6
10
  const _component_CnWidgetWrapper = resolveComponent("CnWidgetWrapper");
@@ -27,15 +31,27 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
27
31
  showRefresh: false,
28
32
  headerLink: entry.headerLink,
29
33
  flush: $props.variant === 'stat-row'
30
- }, {
34
+ }, createSlots({
31
35
  default: withCtx(() => [
32
36
  (openBlock(), createBlock(resolveDynamicComponent(entry.renderer), mergeProps({
33
37
  widgetId: entry.id,
34
38
  content: entry.content
35
- }, { ref_for: true }, $options.declaredContent(entry)), null, 16 /* FULL_PROPS */, ["widgetId", "content"]))
39
+ }, { ref_for: true }, $options.declaredContent(entry), {
40
+ onHeader: $event => ($options.onHeader(entry.id, $event))
41
+ }), null, 16 /* FULL_PROPS */, ["widgetId", "content", "onHeader"]))
36
42
  ]),
37
43
  _: 2 /* DYNAMIC */
38
- }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["widgetId", "title", "showTitle", "headerLink", "flush"])
44
+ }, [
45
+ ($data.headerNotes[entry.id])
46
+ ? {
47
+ name: "actions",
48
+ fn: withCtx(() => [
49
+ createElementVNode("span", _hoisted_2, toDisplayString($data.headerNotes[entry.id]), 1 /* TEXT */)
50
+ ]),
51
+ key: "0"
52
+ }
53
+ : undefined
54
+ ]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["widgetId", "title", "showTitle", "headerLink", "flush"])
39
55
  ], 8 /* PROPS */, _hoisted_1))
40
56
  }), 128 /* KEYED_FRAGMENT */))
41
57
  ]),
@@ -1 +1 @@
1
- {"version":3,"file":"CnIndexPageWidgets.vue3.js","sources":["../../../../src/components/CnIndexPage/CnIndexPageWidgets.vue?vue&type=template&id=cf9fcc76&lang.js"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n\n CnIndexPageWidgets — the widgets an index page draws around its list\n (screens-index-stat-row): the KPI tiles above it (`statRow`, an auto-fit row)\n and the cards beside it (`sidePanel`, a column). Each entry is a dashboard\n widget definition `{ id, type, title?, content, headerLink? }`, resolved like\n a dashboard widget: the app's registry, then the dashboard catalog, then the\n built-in widgets. Internal to CnIndexPage.\n-->\n<template>\n\t<component\n\t\t:is=\"variant === 'stat-row' ? 'CnKpiGrid' : (variant === 'after-list' ? 'section' : 'aside')\"\n\t\tv-if=\"entries.length > 0\"\n\t\t:columns=\"variant === 'stat-row' ? 'auto' : undefined\"\n\t\tclass=\"cn-index-page-widgets\"\n\t\t:class=\"['cn-index-page-widgets--' + variant]\"\n\t\t:data-testid=\"'cn-index-page-' + variant\">\n\t\t<div\n\t\t\tv-for=\"entry in entries\"\n\t\t\t:key=\"entry.id\"\n\t\t\tclass=\"cn-index-page-widgets__cell\"\n\t\t\t:data-testid=\"'cn-index-page-widget-' + entry.id\">\n\t\t\t<CnWidgetWrapper\n\t\t\t\t:widgetId=\"entry.id\"\n\t\t\t\t:title=\"entry.title\"\n\t\t\t\t:showTitle=\"variant !== 'stat-row' && entry.title !== ''\"\n\t\t\t\t:showActions=\"false\"\n\t\t\t\t:showRefresh=\"false\"\n\t\t\t\t:headerLink=\"entry.headerLink\"\n\t\t\t\t:flush=\"variant === 'stat-row'\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"entry.renderer\"\n\t\t\t\t\t:widgetId=\"entry.id\"\n\t\t\t\t\t:content=\"entry.content\"\n\t\t\t\t\tv-bind=\"declaredContent(entry)\" />\n\t\t\t</CnWidgetWrapper>\n\t\t</div>\n\t</component>\n</template>\n\n<script>\nimport CnKpiGrid from '../CnKpiGrid/CnKpiGrid.vue'\nimport CnWidgetWrapper from '../CnWidgetWrapper/CnWidgetWrapper.vue'\nimport { pickDeclaredProps } from '../../utils/declaredProps.js'\nimport { canonicalWidgetType } from '../../utils/widgetTypeAliases.js'\nimport { BUILT_IN_WIDGETS } from '../CnWidgetGrid/builtInWidgets.js'\nimport { getWidgetTypeEntry } from '../CnWidgetGrid/dashboardWidgetRegistry.js'\n\n// Populate the dashboard catalog (stat, table, people, …): an index page\n// may be the first surface that resolves a widget type.\nimport '../CnWidgetGrid/registerDashboardWidgets.js'\n\nexport default {\n\tname: 'CnIndexPageWidgets',\n\n\tcomponents: {\n\t\tCnKpiGrid,\n\t\tCnWidgetWrapper,\n\t},\n\n\tinject: {\n\t\t/** Consumer widget registry provided by CnAppRoot (custom over built-in). */\n\t\tcnRegistry: { default: () => ({}) },\n\t\t/** Host translate function provided by CnAppRoot. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Widget definitions `{ id, type, title?, content?, headerLink? }`.\n\t\t * An entry without an id or with a type no layer resolves is skipped.\n\t\t */\n\t\twidgets: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Where the widgets sit: `stat-row` above the list, `side-panel` beside it, `after-list` under it. */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: 'stat-row',\n\t\t\tvalidator: (v) => ['stat-row', 'side-panel', 'after-list'].includes(v),\n\t\t},\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The renderable entries, in the listed order.\n\t\t *\n\t\t * @spec openspec/changes/screens-index-stat-row/specs/index-page/spec.md#requirement-an-index-page-can-draw-a-stat-row-above-the-list\n\t\t * @return {Array<{id: string, type: string, title: string, content: object, headerLink: (object|null), renderer: object}>}\n\t\t */\n\t\tentries() {\n\t\t\tconst list = Array.isArray(this.widgets) ? this.widgets : []\n\t\t\tconst out = []\n\t\t\tfor (const def of list) {\n\t\t\t\tif (!def || typeof def.id !== 'string' || def.id === '' || typeof def.type !== 'string') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst renderer = this.resolveRenderer(def.type)\n\t\t\t\tif (!renderer) {\n\t\t\t\t\tif (process.env.NODE_ENV !== 'production') {\n\t\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\t\tconsole.warn(`[CnIndexPage] ${this.variant} widget \"${def.id}\" has type \"${def.type}\", which no widget registry resolves; skipped.`)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key\n\t\t\t\tout.push({\n\t\t\t\t\tid: def.id,\n\t\t\t\t\ttype: def.type,\n\t\t\t\t\ttitle: typeof def.title === 'string' && def.title !== '' ? translate(def.title) : '',\n\t\t\t\t\tcontent: (def.content && typeof def.content === 'object') ? def.content : {},\n\t\t\t\t\theaderLink: (def.headerLink && typeof def.headerLink === 'object') ? def.headerLink : null,\n\t\t\t\t\trenderer,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The content keys an entry's renderer declares as props. An undeclared\n\t\t * key would fall through onto the renderer's root; the whole content\n\t\t * still arrives as `content`.\n\t\t *\n\t\t * @param {{ renderer: object, content: object, type?: string, id: string }} entry A resolved entry.\n\t\t * @return {object} The declared content keys.\n\t\t * @spec openspec/changes/declared-props-detail-index-sidebar/specs/widget-hosts/spec.md#requirement-a-widget-host-passes-only-the-keys-a-widget-declares\n\t\t */\n\t\tdeclaredContent(entry) {\n\t\t\treturn pickDeclaredProps(entry.renderer, entry.content, entry.type || entry.id)\n\t\t},\n\n\t\t/**\n\t\t * Resolve a widget type: the consumer registry, then the dashboard\n\t\t * catalog (the type as written, then its alias), then the built-ins.\n\t\t *\n\t\t * @param {string} type The widget type.\n\t\t * @return {object|null} The component, or null.\n\t\t */\n\t\tresolveRenderer(type) {\n\t\t\tconst consumer = (this.cnRegistry || {})[type]\n\t\t\tif (consumer) {\n\t\t\t\treturn consumer.component ?? consumer\n\t\t\t}\n\t\t\tconst entry = getWidgetTypeEntry(type) || getWidgetTypeEntry(canonicalWidgetType(type))\n\t\t\tif (entry && entry.renderer) {\n\t\t\t\treturn entry.renderer\n\t\t\t}\n\t\t\treturn BUILT_IN_WIDGETS[type] || BUILT_IN_WIDGETS[canonicalWidgetType(type)] || null\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-index-page-widgets--stat-row {\n\tmargin-bottom: var(--cn-board-section-gap, 20px);\n}\n\n.cn-index-page-widgets__cell {\n\tdisplay: flex;\n\tmin-width: 0;\n}\n\n.cn-index-page-widgets__cell > * {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n/* The cards after the list, stacked full width (PqSocial). */\n.cn-index-page-widgets--after-list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--cn-board-section-gap, 16px);\n\tmargin-top: var(--cn-board-section-gap, 16px);\n\tmin-width: 0;\n}\n\n/* The side panel column (DqTeamwachtrij: 300px, 16px between cards). Its\n place beside the toolbar and the list is set by CnIndexPage\n (`cn-index-page--with-side-panel`, index-page.css). */\n.cn-index-page-widgets--side-panel {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tmin-width: 0;\n}\n</style>\n"],"names":["_normalizeClass"],"mappings":";;;;;;;AAyBQ,EAAA,OAAA,CAAA,QAAA,CAAA,OAAO,CAAC,MAAM,GAAA,CAAA;wDADf,MAAO,CAAA,OAAA,KAAA,UAAA,GAAA,WAAA,IAAI,MAAA,CAAA,OAAA,KAAA,YAAA,GAAA,SAAA,GAAA,OAAA,CAAA,CAAA,EAAA;;QAEd,OAAM,EAAE,MAAO,CAAA,OAAA,KAAA,UAAA,GAAA,MAAA,GAAI,SAAA;aAChB,EAAAA,cAAA,CAAA,CAAC,uBAAuB,EAAA,CAAA,yBAAA,GACR,MAAA,CAAA,OAAA,CAAA,CAAA,CAAA;AACnB,QAAA,aAAU,qBAAS,MAAA,CAAA,OAAA;;+BAEI;0EAAP,QAAM,CAAA,OAAA,EAAA,CAAhB,KAAK,KAAA;;cACX,GAAG,EAAE,KAAI,CAAA,EAAA;AACR,cAAA,KAAG,EAAC,6BAAe;cACnB,aAAU,EAAA,uBAAA,GAAS,KAAA,CAAA,EAAA;;;gBAEnB,QAAQ,EAAE,KAAK,CAAC,EAAE;gBAClB,KAAK,EAAE,KAAK,CAAC,KAAE;AACf,gBAAA,SAAE,EAAA,MAAA,CAAA,OAAA,KAAA,UAAA,IAAA,KAAA,CAAA,KAAA,KAAA,EAAA;AACF,gBAAA,WAAO,EAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"CnIndexPageWidgets.vue3.js","sources":["../../../../src/components/CnIndexPage/CnIndexPageWidgets.vue?vue&type=template&id=096a5eee&lang.js"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n\n CnIndexPageWidgets — the widgets an index page draws around its list\n (screens-index-stat-row): the KPI tiles above it (`statRow`, an auto-fit row)\n and the cards beside it (`sidePanel`, a column). Each entry is a dashboard\n widget definition `{ id, type, title?, content, headerLink? }`, resolved like\n a dashboard widget: the app's registry, then the dashboard catalog, then the\n built-in widgets. Internal to CnIndexPage.\n-->\n<template>\n\t<component\n\t\t:is=\"variant === 'stat-row' ? 'CnKpiGrid' : (variant === 'after-list' ? 'section' : 'aside')\"\n\t\tv-if=\"entries.length > 0\"\n\t\t:columns=\"variant === 'stat-row' ? 'auto' : undefined\"\n\t\tclass=\"cn-index-page-widgets\"\n\t\t:class=\"['cn-index-page-widgets--' + variant]\"\n\t\t:data-testid=\"'cn-index-page-' + variant\">\n\t\t<div\n\t\t\tv-for=\"entry in entries\"\n\t\t\t:key=\"entry.id\"\n\t\t\tclass=\"cn-index-page-widgets__cell\"\n\t\t\t:data-testid=\"'cn-index-page-widget-' + entry.id\">\n\t\t\t<CnWidgetWrapper\n\t\t\t\t:widgetId=\"entry.id\"\n\t\t\t\t:title=\"entry.title\"\n\t\t\t\t:showTitle=\"variant !== 'stat-row' && entry.title !== ''\"\n\t\t\t\t:showActions=\"false\"\n\t\t\t\t:showRefresh=\"false\"\n\t\t\t\t:headerLink=\"entry.headerLink\"\n\t\t\t\t:flush=\"variant === 'stat-row'\">\n\t\t\t\t<template v-if=\"headerNotes[entry.id]\" #actions>\n\t\t\t\t\t<span class=\"cn-index-page-widgets__note\" data-testid=\"cn-index-page-widget-note\">{{ headerNotes[entry.id] }}</span>\n\t\t\t\t</template>\n\t\t\t\t<component\n\t\t\t\t\t:is=\"entry.renderer\"\n\t\t\t\t\t:widgetId=\"entry.id\"\n\t\t\t\t\t:content=\"entry.content\"\n\t\t\t\t\tv-bind=\"declaredContent(entry)\"\n\t\t\t\t\t@header=\"onHeader(entry.id, $event)\" />\n\t\t\t</CnWidgetWrapper>\n\t\t</div>\n\t</component>\n</template>\n\n<script>\nimport CnKpiGrid from '../CnKpiGrid/CnKpiGrid.vue'\nimport CnWidgetWrapper from '../CnWidgetWrapper/CnWidgetWrapper.vue'\nimport { pickDeclaredProps } from '../../utils/declaredProps.js'\nimport { canonicalWidgetType } from '../../utils/widgetTypeAliases.js'\nimport { BUILT_IN_WIDGETS } from '../CnWidgetGrid/builtInWidgets.js'\nimport { getWidgetTypeEntry } from '../CnWidgetGrid/dashboardWidgetRegistry.js'\n\n// Populate the dashboard catalog (stat, table, people, …): an index page\n// may be the first surface that resolves a widget type.\nimport '../CnWidgetGrid/registerDashboardWidgets.js'\n\nexport default {\n\tname: 'CnIndexPageWidgets',\n\n\tcomponents: {\n\t\tCnKpiGrid,\n\t\tCnWidgetWrapper,\n\t},\n\n\tinject: {\n\t\t/** Consumer widget registry provided by CnAppRoot (custom over built-in). */\n\t\tcnRegistry: { default: () => ({}) },\n\t\t/** Host translate function provided by CnAppRoot. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Widget definitions `{ id, type, title?, content?, headerLink? }`.\n\t\t * An entry without an id or with a type no layer resolves is skipped.\n\t\t */\n\t\twidgets: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Where the widgets sit: `stat-row` above the list, `side-panel` beside it, `after-list` under it. */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: 'stat-row',\n\t\t\tvalidator: (v) => ['stat-row', 'side-panel', 'after-list'].includes(v),\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\t/** The header note each widget reported (`header` event), by widget id. */\n\t\t\theaderNotes: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The renderable entries, in the listed order.\n\t\t *\n\t\t * @spec openspec/changes/screens-index-stat-row/specs/index-page/spec.md#requirement-an-index-page-can-draw-a-stat-row-above-the-list\n\t\t * @return {Array<{id: string, type: string, title: string, content: object, headerLink: (object|null), renderer: object}>}\n\t\t */\n\t\tentries() {\n\t\t\tconst list = Array.isArray(this.widgets) ? this.widgets : []\n\t\t\tconst out = []\n\t\t\tfor (const def of list) {\n\t\t\t\tif (!def || typeof def.id !== 'string' || def.id === '' || typeof def.type !== 'string') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst renderer = this.resolveRenderer(def.type)\n\t\t\t\tif (!renderer) {\n\t\t\t\t\tif (process.env.NODE_ENV !== 'production') {\n\t\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\t\tconsole.warn(`[CnIndexPage] ${this.variant} widget \"${def.id}\" has type \"${def.type}\", which no widget registry resolves; skipped.`)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key\n\t\t\t\tout.push({\n\t\t\t\t\tid: def.id,\n\t\t\t\t\ttype: def.type,\n\t\t\t\t\ttitle: typeof def.title === 'string' && def.title !== '' ? translate(def.title) : '',\n\t\t\t\t\tcontent: (def.content && typeof def.content === 'object') ? def.content : {},\n\t\t\t\t\theaderLink: (def.headerLink && typeof def.headerLink === 'object') ? def.headerLink : null,\n\t\t\t\t\trenderer,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Keep the header note a widget reports (\"4 van 5 aanwezig\"); only a\n\t\t * changed note is stored, so a report on render does not loop.\n\t\t *\n\t\t * @param {string} id The widget id.\n\t\t * @param {object} header `{ note?, link? }` from the widget.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/side-panel-header-note/specs/side-panel-header-note/spec.md#requirement-a-side-panel-card-shows-the-note-its-widget-reports\n\t\t */\n\t\tonHeader(id, header) {\n\t\t\tconst note = header && typeof header === 'object' && typeof header.note === 'string' ? header.note : ''\n\t\t\tif ((this.headerNotes[id] || '') === note) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.headerNotes = { ...this.headerNotes, [id]: note }\n\t\t},\n\n\t\t/**\n\t\t * The content keys an entry's renderer declares as props. An undeclared\n\t\t * key would fall through onto the renderer's root; the whole content\n\t\t * still arrives as `content`.\n\t\t *\n\t\t * @param {{ renderer: object, content: object, type?: string, id: string }} entry A resolved entry.\n\t\t * @return {object} The declared content keys.\n\t\t * @spec openspec/changes/declared-props-detail-index-sidebar/specs/widget-hosts/spec.md#requirement-a-widget-host-passes-only-the-keys-a-widget-declares\n\t\t */\n\t\tdeclaredContent(entry) {\n\t\t\treturn pickDeclaredProps(entry.renderer, entry.content, entry.type || entry.id)\n\t\t},\n\n\t\t/**\n\t\t * Resolve a widget type: the consumer registry, then the dashboard\n\t\t * catalog (the type as written, then its alias), then the built-ins.\n\t\t *\n\t\t * @param {string} type The widget type.\n\t\t * @return {object|null} The component, or null.\n\t\t */\n\t\tresolveRenderer(type) {\n\t\t\tconst consumer = (this.cnRegistry || {})[type]\n\t\t\tif (consumer) {\n\t\t\t\treturn consumer.component ?? consumer\n\t\t\t}\n\t\t\tconst entry = getWidgetTypeEntry(type) || getWidgetTypeEntry(canonicalWidgetType(type))\n\t\t\tif (entry && entry.renderer) {\n\t\t\t\treturn entry.renderer\n\t\t\t}\n\t\t\treturn BUILT_IN_WIDGETS[type] || BUILT_IN_WIDGETS[canonicalWidgetType(type)] || null\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-index-page-widgets__note {\n\talign-self: center;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n\twhite-space: nowrap;\n}\n\n.cn-index-page-widgets--stat-row {\n\tmargin-bottom: var(--cn-board-section-gap, 20px);\n}\n\n.cn-index-page-widgets__cell {\n\tdisplay: flex;\n\tmin-width: 0;\n}\n\n.cn-index-page-widgets__cell > * {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n/* The cards after the list, stacked full width (PqSocial). */\n.cn-index-page-widgets--after-list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--cn-board-section-gap, 16px);\n\tmargin-top: var(--cn-board-section-gap, 16px);\n\tmin-width: 0;\n}\n\n/* The side panel column (DqTeamwachtrij: 300px, 16px between cards). Its\n place beside the toolbar and the list is set by CnIndexPage\n (`cn-index-page--with-side-panel`, index-page.css). */\n.cn-index-page-widgets--side-panel {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tmin-width: 0;\n}\n</style>\n"],"names":["_normalizeClass","_createVNode"],"mappings":";;;;;;;;;;;AAyBQ,EAAA,OAAA,CAAA,QAAA,CAAA,OAAO,CAAC,MAAM,GAAA,CAAA;wDADf,MAAO,CAAA,OAAA,KAAA,UAAA,GAAA,WAAA,IAAI,MAAA,CAAA,OAAA,KAAA,YAAA,GAAA,SAAA,GAAA,OAAA,CAAA,CAAA,EAAA;;QAEd,OAAM,EAAE,MAAO,CAAA,OAAA,KAAA,UAAA,GAAA,MAAA,GAAI,SAAA;aAChB,EAAAA,cAAA,CAAA,CAAC,uBAAuB,EAAA,CAAA,yBAAA,GACR,MAAA,CAAA,OAAA,CAAA,CAAA,CAAA;AACnB,QAAA,aAAU,qBAAS,MAAA,CAAA,OAAA;;+BAEI;0EAAP,QAAM,CAAA,OAAA,EAAA,CAAhB,KAAK,KAAA;;cACX,GAAG,EAAE,KAAK,CAAC,EAAE;mBACT,EAAC,6BAA6B;cAClC,aAAU,EAAA,uBAAA,GAAA,KAAA,CAAA,EAAA;;cACVC;gBACC,QAAQ,EAAE,KAAK,CAAC,EAAE;gBAClB,KAAK,EAAE,KAAK,CAAC,KAAK;AAClB,gBAAA,SAAS,EAAE,MAAA,CAAA,OAAO,KAAK,UAAA,IAAA,KAAA,CAAA,KAAA,KAAA,EAAA;AACvB,gBAAA,WAAW,EAAE,KAAK;AAClB,gBAAA,WAAW,EAAE,KAAK;gBAClB,UAAU,EAAE,KAAG,CAAA,UAAA;AACf,gBAAA,KAAE,EAAA,MAAA,CAAA,OAAA,KAAA,UAAA;;;;;;;;;;;;iBACK,KAAA,CAAA,WAAA,CAAA,KAAA,CAAA,EAAA,CAAA;;4BAAA,SAAA;;;;;;;;;;;;;;;;;;"}
@@ -168,7 +168,8 @@ function useSelfFetchList(props, instance, inject, extras = {}) {
168
168
  const objCtx = unwrapCtx(objectCtxRaw);
169
169
  const base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {};
170
170
  base.workspace = unwrapCtx(workspaceCtxRaw) || {};
171
- base.config = unwrapCtx(appConfigRaw) || {};
171
+ // The page's own `appConfig` prop wins over a config an ancestor provides.
172
+ base.config = { ...(unwrapCtx(appConfigRaw) || {}), ...(props.appConfig || {}) };
172
173
  return base
173
174
  }
174
175
 
@@ -180,7 +181,7 @@ function useSelfFetchList(props, instance, inject, extras = {}) {
180
181
  // never again. Stringified (like CnDeltaWidget's `sourceKey`) so the
181
182
  // watcher fires on real content changes only, not object identity.
182
183
  const workspaceSignature = computed(() => JSON.stringify(unwrapCtx(workspaceCtxRaw) || {}));
183
- const appConfigSignature = computed(() => JSON.stringify(unwrapCtx(appConfigRaw) || {}));
184
+ const appConfigSignature = computed(() => JSON.stringify({ ...(unwrapCtx(appConfigRaw) || {}), ...(props.appConfig || {}) }));
184
185
 
185
186
  // Pass register/schema in their positional id slots (not as a {register, schema} object as
186
187
  // second arg) — that previously made fetch URLs go to `/api/objects/undefined/[object Object]`.
@@ -224,6 +225,22 @@ function useSelfFetchList(props, instance, inject, extras = {}) {
224
225
  ? props.sortKeys
225
226
  : (props.sortKey ? [{ key: props.sortKey, order: props.sortOrder || 'asc' }] : defaultOrderKeys);
226
227
  // The lens that is active on mount may set its own order.
228
+ /**
229
+ * The tabs the strip shows, so the fetch filters by the chip that is
230
+ * lit: the page's `effectiveQuickFilters` (declared, generated from
231
+ * `quickFiltersFrom`, or a named source's), else the prop while the
232
+ * page's computeds do not exist yet (setup).
233
+ *
234
+ * @return {Array<object>|null} The tabs, or null when there are none.
235
+ */
236
+ function shownQuickFilters() {
237
+ const shown = instance && instance.proxy ? instance.proxy.effectiveQuickFilters : undefined;
238
+ if (shown !== undefined) {
239
+ return shown
240
+ }
241
+ return withPersonalLenses(props.quickFilters, props.personalLenses)
242
+ }
243
+
227
244
  const initialTabs = withPersonalLenses(props.quickFilters, props.personalLenses);
228
245
  const initialTabSort = (!isMultiQuickFilter && Array.isArray(initialTabs))
229
246
  ? quickFilterSortKeys(initialTabs[activeQuickFilterIndex.value])
@@ -271,7 +288,7 @@ function useSelfFetchList(props, instance, inject, extras = {}) {
271
288
  // The visible calendar month, only while the page is in calendar mode.
272
289
  const month = (instance && instance.proxy && instance.proxy.calendarRangeFilter) || {};
273
290
  const scope = { ...(widen ? { ...scopeBase, _content_search: 'true' } : scopeBase), ...month };
274
- const lensed = withPersonalLenses(props.quickFilters, props.personalLenses);
291
+ const lensed = shownQuickFilters();
275
292
  const tabs = Array.isArray(lensed) ? lensed : null;
276
293
  if (!tabs) {
277
294
  return { ...queryFilters, ...scope, ...base }
@@ -286,7 +303,9 @@ function useSelfFetchList(props, instance, inject, extras = {}) {
286
303
 
287
304
  // Single mode: the active tab's filter spread last so it wins
288
305
  // over a colliding props.filter entry.
289
- const activeIdx = activeQuickFilterIndex.value;
306
+ // Tabs that arrived after setup (`quickFiltersFrom`) have no index
307
+ // yet: the default tab is the one in force, as the strip shows.
308
+ const activeIdx = activeQuickFilterIndex.value ?? resolveInitialQuickFilterIndex(tabs);
290
309
  const tabFilter = (activeIdx !== null && activeIdx !== undefined) ? tabs[activeIdx]?.filter : null;
291
310
  return { ...queryFilters, ...scope, ...base, ...resolveFilterMap(tabFilter, params, ctx) }
292
311
  },
@@ -306,7 +325,7 @@ function useSelfFetchList(props, instance, inject, extras = {}) {
306
325
  // for a lens without goes back to the page's own order. A column the
307
326
  // person sorts by afterwards still wins until the next switch.
308
327
  if (!isMultiQuickFilter && before && now[0] !== before[0]) {
309
- const tabs = withPersonalLenses(props.quickFilters, props.personalLenses) || [];
328
+ const tabs = shownQuickFilters() || [];
310
329
  const keys = quickFilterSortKeys(tabs[now[0]]) || (quickFilterSortKeys(tabs[before[0]]) ? pageSortKeys : null);
311
330
  if (keys && list.sortKeys) {
312
331
  list.sortKeys.value = keys;
@@ -351,5 +370,5 @@ function useSelfFetchList(props, instance, inject, extras = {}) {
351
370
  }
352
371
  }
353
372
 
354
- export { BOARD_PAGE_SIZE, quickFilterSortKeys, useSelfFetchList };
373
+ export { BOARD_PAGE_SIZE, quickFilterSortKeys, resolveInitialQuickFilterIndex, useSelfFetchList };
355
374
  //# sourceMappingURL=useSelfFetchList.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSelfFetchList.js","sources":["../../../../src/components/CnIndexPage/useSelfFetchList.js"],"sourcesContent":["import { computed, ref, watch } from 'vue'\nimport { useListView } from '../../composables/index.js'\nimport { useObjectSubscription } from '../../composables/useObjectSubscription.js'\nimport { useObjectStore } from '../../store/index.js'\n// Both filter resolvers live in `utils/routeFilters.js` so CnLogsPage applies\n// the same two grammars (route-param interpolation + `?key=value` deep links)\n// without pulling in this composable's index-only sidebar/subscription wiring.\nimport { withPersonalLenses } from '../../utils/personalLenses.js'\nimport { parseSortKeysFromQuery, resolveFilterMap, resolveQueryFilters } from '../../utils/routeFilters.js'\nimport { orderToSortKeys } from '../../utils/sortOrder.js'\n\nfunction resolveInitialQuickFilterIndex(quickFilters) {\n\tconst tabs = Array.isArray(quickFilters) ? quickFilters : null\n\tif (!tabs || tabs.length === 0) {\n\t\treturn null\n\t}\n\tconst di = tabs.findIndex((t) => t && t.default === true)\n\treturn di >= 0 ? di : 0\n}\n\n/**\n * OR-merge several quick-filter maps into one fetch filter. Values for the\n * same key collapse to an array (deduped) when more than one tab contributes\n * it — `buildQueryString` serialises an array as `key[]=a&key[]=b`, which the\n * OpenRegister API interprets as an IN / OR match.\n *\n * @param {Array<object>} filterMaps Already route-resolved filter maps.\n * @return {object} The merged filter (scalar when a key has one value, array when many).\n */\n/**\n * Rows one board fetch asks for. A board shows every matching row at once,\n * so it loads far more than a table page (OpenRegister serves up to 1000);\n * past this the board offers \"Load more\".\n *\n * @type {number}\n */\nexport const BOARD_PAGE_SIZE = 500\n\n/**\n * The sort a quick filter sets while it is active (`sort: { key, order }`, or a\n * list of those for a multi-column order), as sort keys, or null when it sets\n * none.\n *\n * @param {object|undefined} tab A quick filter.\n * @return {Array<{key: string, order: string}>|null} The sort keys.\n * @spec openspec/changes/screens-quick-filter-sort/specs/index-page/spec.md\n */\nexport function quickFilterSortKeys(tab) {\n\tconst sort = tab && tab.sort\n\tif (!sort) {\n\t\treturn null\n\t}\n\tconst keys = (Array.isArray(sort) ? sort : [sort])\n\t\t.filter((entry) => entry && typeof entry.key === 'string' && entry.key !== '')\n\t\t.map((entry) => ({ key: entry.key, order: entry.order === 'desc' ? 'desc' : 'asc' }))\n\treturn keys.length > 0 ? keys : null\n}\n\nfunction unionFilterMaps(filterMaps) {\n\tconst grouped = {}\n\tfor (const map of filterMaps) {\n\t\tif (!map || typeof map !== 'object') {\n\t\t\tcontinue\n\t\t}\n\t\tfor (const [k, v] of Object.entries(map)) {\n\t\t\tif (v === undefined || v === null || v === '') {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!grouped[k]) {\n\t\t\t\tgrouped[k] = []\n\t\t\t}\n\t\t\tfor (const item of (Array.isArray(v) ? v : [v])) {\n\t\t\t\tif (!grouped[k].includes(item)) {\n\t\t\t\t\tgrouped[k].push(item)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tconst out = {}\n\tfor (const [k, arr] of Object.entries(grouped)) {\n\t\tout[k] = arr.length === 1 ? arr[0] : arr\n\t}\n\treturn out\n}\n\n/**\n * Self-fetch mode for CnIndexPage: when register+schema are set but no\n * `objects` prop was passed (manifest-driven pages), drive the list ourselves.\n *\n * @param {object} props CnIndexPage props.\n * @param {import('vue').ComponentInternalInstance|null} instance Pass `getCurrentInstance()`.\n * @param {typeof import('vue').inject} inject Pass Vue's `inject`.\n * @param {{activeFolderSchema?: import('vue').Ref<?{schema: string, register?: string}>}} [extras] `activeFolderSchema`: a ref the page sets to the selected folder's `{ schema, register? }`, switching the loaded object type while it is non-null.\n * @return {object} { isSelfFetch, list, selfObjectStore, selfObjectType, activeQuickFilterIndex }\n */\nexport function useSelfFetchList(props, instance, inject, extras = {}) {\n\tconst objectsProvided = !!(\n\t\tinstance && instance.proxy && instance.proxy.$options && instance.proxy.$options.propsData\n\t\t&& Object.hasOwn(instance.proxy.$options.propsData, 'objects')\n\t)\n\t// A named source wins over register/schema. Without this the page would\n\t// fire an OpenRegister request whose rows it then discards in favour of the\n\t// source's — wasted, but worse than wasted if the manifest names both by\n\t// mistake, because the request succeeds and nothing says the two disagree.\n\tconst isSelfFetch = !!(props.register && props.schema) && !objectsProvided && !props.entitySource\n\n\t// \"Also search inside files\" switch, seeded from `?contentSearch=1` so a\n\t// shared link reproduces the list. Only meaningful when `searchInFiles` is on.\n\tconst routeAtSetup = instance && instance.proxy && instance.proxy.$route\n\tconst contentSearch = ref(props.searchInFiles === true && String(routeAtSetup?.query?.contentSearch) === '1')\n\n\tconst activeQuickFilterIndex = ref(resolveInitialQuickFilterIndex(withPersonalLenses(props.quickFilters, props.personalLenses)))\n\tconst selectedQuickFilterIndices = ref([])\n\tconst isMultiQuickFilter = props.quickFilterMultiple === true\n\n\tif (!isSelfFetch) {\n\t\treturn {\n\t\t\tisSelfFetch: false,\n\t\t\tlist: null,\n\t\t\tselfObjectStore: null,\n\t\t\tselfObjectType: '',\n\t\t\tactiveQuickFilterIndex,\n\t\t\tselectedQuickFilterIndices,\n\t\t\tcontentSearch,\n\t\t\tselfFetchTokenCtx: null,\n\t\t\tinitialQueryFilterKeys: [],\n\t\t}\n\t}\n\n\t// The object type follows the selected folder's own schema when it declares\n\t// one; otherwise the page's register and schema.\n\tconst activeFolderSchema = extras.activeFolderSchema || null\n\tconst resolvedTarget = computed(() => {\n\t\tconst folder = activeFolderSchema && activeFolderSchema.value\n\t\tif (folder && folder.schema) {\n\t\t\tconst register = folder.register || props.register\n\t\t\treturn { register, schema: folder.schema, type: `${register}-${folder.schema}` }\n\t\t}\n\t\t// A collection endpoint gets its own type key, so a plain registration of\n\t\t// the same pair elsewhere never resets it.\n\t\tconst collectionUrl = props.collectionUrl || ''\n\t\tconst type = `${props.register}-${props.schema}` + (collectionUrl ? `@${collectionUrl}` : '')\n\t\treturn { register: props.register, schema: props.schema, type, collectionUrl }\n\t})\n\tconst objectType = computed(() => resolvedTarget.value.type)\n\tconst sidebarState = inject('sidebarState', null) ?? inject('objectSidebarState', null)\n\tconst objectStore = useObjectStore()\n\n\t// Token-resolution context for `@workspace.<key>` / `@config.<key>` /\n\t// `@objectId` / `@object.<field>` inside `props.filter` / quick-filter\n\t// tab filters. Same injects + unwrap shape as CnObjectListWidget's\n\t// `objectCtx`/`workspaceCtx`/`tokenCtx` computeds (and CnDeltaWidget) —\n\t// `cnWorkspaceContext` is the reactive bag a dashboard/workspace-root\n\t// (e.g. hrmq's App.vue for multi-administratie) provides; `cnObjectContext`\n\t// is a detail-page's object context (rare on an index page, but harmless\n\t// to support); `cnAppConfig` is the page-level app config bag. All three\n\t// default to null/absent so an app that never provides them is unaffected.\n\tconst objectCtxRaw = inject('cnObjectContext', null)\n\tconst workspaceCtxRaw = inject('cnWorkspaceContext', null)\n\tconst appConfigRaw = inject('cnAppConfig', null)\n\tconst unwrapCtx = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\n\t/**\n\t * Build the current token-resolution ctx `{ objectId?, object?, workspace, config }`\n\t * from the injected bags, unwrapping Vue refs. Called fresh on every fetch\n\t * so it always reflects the latest workspace/config state.\n\t *\n\t * @return {object} The token ctx.\n\t */\n\tfunction tokenCtx() {\n\t\tconst objCtx = unwrapCtx(objectCtxRaw)\n\t\tconst base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {}\n\t\tbase.workspace = unwrapCtx(workspaceCtxRaw) || {}\n\t\tbase.config = unwrapCtx(appConfigRaw) || {}\n\t\treturn base\n\t}\n\n\t// Reactive signature of the workspace/config bags so a change (e.g. the\n\t// administration switcher writing `activeAdministrationId`) triggers a\n\t// re-fetch below — `fixedFilters` is a plain getter called at fetch time\n\t// (see useListView.resolveFixedFilters), it is NOT auto-tracked by Vue,\n\t// so without this the list would resolve the token once on mount and\n\t// never again. Stringified (like CnDeltaWidget's `sourceKey`) so the\n\t// watcher fires on real content changes only, not object identity.\n\tconst workspaceSignature = computed(() => JSON.stringify(unwrapCtx(workspaceCtxRaw) || {}))\n\tconst appConfigSignature = computed(() => JSON.stringify(unwrapCtx(appConfigRaw) || {}))\n\n\t// Pass register/schema in their positional id slots (not as a {register, schema} object as\n\t// second arg) — that previously made fetch URLs go to `/api/objects/undefined/[object Object]`.\n\t// Runs (sync, ahead of the list's own watcher) again whenever the type switches.\n\tfunction registerTarget() {\n\t\tconst { register, schema, type, collectionUrl } = resolvedTarget.value\n\t\tif (typeof objectStore.registerObjectType === 'function') {\n\t\t\tobjectStore.registerObjectType(type, schema, register, {\n\t\t\t\tregisterSlug: register,\n\t\t\t\tschemaSlug: schema,\n\t\t\t\t...(collectionUrl ? { collectionUrl } : {}),\n\t\t\t})\n\t\t}\n\t\t// A mixed list tells its own rows apart by `@self.register`, an id, so a slug register needs resolving.\n\t\tif (collectionUrl && !/^\\d+$/.test(String(register)) && typeof objectStore.fetchRegister === 'function') {\n\t\t\tobjectStore.fetchRegister(type)\n\t\t}\n\t}\n\tregisterTarget()\n\twatch(objectType, registerTarget, { flush: 'sync' })\n\n\t// Seed the visible-column set from the configured columns so the sidebar's\n\t// Columns tab reflects the curated default; null when none are configured\n\t// (schema-driven table — every column starts visible).\n\t// A column marked `hidden: true` is declared but starts outside the set,\n\t// so it is off until the person turns it on in the Columns tab.\n\tconst configuredColumnKeys = (props.columns || [])\n\t\t.filter((c) => !(c && typeof c === 'object' && c.hidden === true))\n\t\t.map((c) => (typeof c === 'string' ? c : c && c.key))\n\t\t.filter(Boolean)\n\n\t// Restore a persisted multi-column sort from the route query first (deep\n\t// link / reload); fall back to a host-passed `sortKeys` prop, then the\n\t// legacy single-key `sortKey`/`sortOrder` props.\n\tconst initialRoute = instance && instance.proxy && instance.proxy.$route\n\t// The page's own order, which a lens without a `sort` goes back to.\n\t// `defaultOrder` (manifest `config.defaultOrder`) is the order of last\n\t// resort, after `sortKeys` and `sortKey`.\n\tconst defaultOrderKeys = orderToSortKeys(props.defaultOrder)\n\tconst pageSortKeys = (Array.isArray(props.sortKeys) && props.sortKeys.length > 0)\n\t\t? props.sortKeys\n\t\t: (props.sortKey ? [{ key: props.sortKey, order: props.sortOrder || 'asc' }] : defaultOrderKeys)\n\t// The lens that is active on mount may set its own order.\n\tconst initialTabs = withPersonalLenses(props.quickFilters, props.personalLenses)\n\tconst initialTabSort = (!isMultiQuickFilter && Array.isArray(initialTabs))\n\t\t? quickFilterSortKeys(initialTabs[activeQuickFilterIndex.value])\n\t\t: null\n\tconst initialSortKeys = parseSortKeysFromQuery(initialRoute)\n\t\t|| initialTabSort\n\t\t|| (Array.isArray(props.sortKeys) && props.sortKeys.length > 0 ? props.sortKeys : undefined)\n\t\t|| (!props.sortKey && defaultOrderKeys.length > 0 ? defaultOrderKeys : undefined)\n\t// Same deep-link restore for facet filters and search, so opening a\n\t// shared/bookmarked URL reproduces the same view (CnIndexPage persists\n\t// both back to the route on change).\n\tconst initialActiveFilters = resolveQueryFilters(initialRoute && initialRoute.query, tokenCtx())\n\tconst initialSearchTerm = (initialRoute && typeof initialRoute.query?._search === 'string') ? initialRoute.query._search : ''\n\n\t// Set right after useListView returns; the fixed-filters getter reads it lazily.\n\tlet listHandle = null\n\tconst list = useListView(() => objectType.value, {\n\t\tobjectStore,\n\t\tsidebarState,\n\t\tdefaultSort: props.sortKey ? { key: props.sortKey, order: props.sortOrder || 'asc' } : undefined,\n\t\tdefaultSortKeys: initialSortKeys,\n\t\tdefaultActiveFilters: initialActiveFilters,\n\t\tdefaultSearchTerm: initialSearchTerm,\n\t\tdefaultPageSize: props.viewMode === 'board' ? BOARD_PAGE_SIZE : ((props.pagination && props.pagination.limit) || undefined),\n\t\tdefaultVisibleColumns: configuredColumnKeys.length ? configuredColumnKeys : null,\n\t\t// `pages[].config.extend` → OpenRegister's repeated `_extend[]`. A\n\t\t// getter so a reactive change re-scopes the next fetch, matching how\n\t\t// `fixedFilters` is read.\n\t\textend: () => (Array.isArray(props.extend) ? props.extend : []),\n\t\tfixedFilters: () => {\n\t\t\tconst route = instance && instance.proxy && instance.proxy.$route\n\t\t\tconst params = (route && route.params) || {}\n\t\t\tconst ctx = tokenCtx()\n\t\t\tconst queryFilters = resolveQueryFilters(route && route.query, ctx)\n\t\t\tconst base = resolveFilterMap(props.filter, params, ctx)\n\t\t\t// A folder-sidebar scope declaring `searchFields` narrows what the\n\t\t\t// search box searches over while that scope is active. Read off the\n\t\t\t// instance, like `$route` above, because the active scope is the\n\t\t\t// component's own state rather than a prop — and read through the\n\t\t\t// one fixed-filter getter rather than a second search path.\n\t\t\tconst scopeSearchFields = (instance && instance.proxy && instance.proxy.activeScopeSearchFields) || []\n\t\t\tconst scopeBase = scopeSearchFields.length > 0 ? { _searchFields: scopeSearchFields } : {}\n\t\t\t// File-content search widens a text search, so it rides only with a term.\n\t\t\tconst widen = props.searchInFiles === true && contentSearch.value && !!(listHandle && listHandle.searchTerm.value)\n\t\t\t// The visible calendar month, only while the page is in calendar mode.\n\t\t\tconst month = (instance && instance.proxy && instance.proxy.calendarRangeFilter) || {}\n\t\t\tconst scope = { ...(widen ? { ...scopeBase, _content_search: 'true' } : scopeBase), ...month }\n\t\t\tconst lensed = withPersonalLenses(props.quickFilters, props.personalLenses)\n\t\t\tconst tabs = Array.isArray(lensed) ? lensed : null\n\t\t\tif (!tabs) {\n\t\t\t\treturn { ...queryFilters, ...scope, ...base }\n\t\t\t}\n\n\t\t\t// Multiple mode: OR the selected tabs' filters together (union).\n\t\t\tif (isMultiQuickFilter) {\n\t\t\t\tconst maps = selectedQuickFilterIndices.value\n\t\t\t\t\t.map((i) => resolveFilterMap(tabs[i]?.filter, params, ctx))\n\t\t\t\treturn { ...queryFilters, ...scope, ...base, ...unionFilterMaps(maps) }\n\t\t\t}\n\n\t\t\t// Single mode: the active tab's filter spread last so it wins\n\t\t\t// over a colliding props.filter entry.\n\t\t\tconst activeIdx = activeQuickFilterIndex.value\n\t\t\tconst tabFilter = (activeIdx !== null && activeIdx !== undefined) ? tabs[activeIdx]?.filter : null\n\t\t\treturn { ...queryFilters, ...scope, ...base, ...resolveFilterMap(tabFilter, params, ctx) }\n\t\t},\n\t})\n\n\tlistHandle = list\n\n\t// Re-fetch when the quick-filter selection changes (pre-existing), OR\n\t// when the workspace/app-config bag content changes (e.g. the\n\t// administration switcher writes a new `activeAdministrationId`) — a\n\t// `@workspace.<key>`/`@config.<key>` token in `props.filter` must re-scope\n\t// the list without a manual reload. A change to `props.filter` itself (a\n\t// host toggling a filter checkbox) re-fetches the same way.\n\tconst filterSignature = computed(() => JSON.stringify(props.filter ?? null))\n\twatch([activeQuickFilterIndex, selectedQuickFilterIndices, contentSearch, workspaceSignature, appConfigSignature, filterSignature], (now, before) => {\n\t\t// Switching lens: a lens with a `sort` sets that order; leaving one\n\t\t// for a lens without goes back to the page's own order. A column the\n\t\t// person sorts by afterwards still wins until the next switch.\n\t\tif (!isMultiQuickFilter && before && now[0] !== before[0]) {\n\t\t\tconst tabs = withPersonalLenses(props.quickFilters, props.personalLenses) || []\n\t\t\tconst keys = quickFilterSortKeys(tabs[now[0]]) || (quickFilterSortKeys(tabs[before[0]]) ? pageSortKeys : null)\n\t\t\tif (keys && list.sortKeys) {\n\t\t\t\tlist.sortKeys.value = keys\n\t\t\t\tlist.sortKey.value = keys[0]?.key ?? null\n\t\t\t\tlist.sortOrder.value = keys[0]?.order ?? 'asc'\n\t\t\t}\n\t\t}\n\t\tif (list && typeof list.refresh === 'function') {\n\t\t\tlist.refresh(1)\n\t\t}\n\t})\n\n\t// Live collection updates (manifest-live-updates): subscribe to the\n\t// page's `or-collection-{register}-{schema}` scope so another user's\n\t// create/update/delete refreshes this list without a manual reload.\n\t// The subscription lifecycle (mount/unmount, in-flight dedupe, epoch\n\t// guard against stale async resolution) lives in useObjectSubscription;\n\t// the refetch-on-event (with the last fetch params, burst-coalesced)\n\t// lives in liveUpdatesPlugin. The `subscribe` prop (default true; from\n\t// a manifest: `config.subscribe: false`) is the opt-out, read through\n\t// a reactive getter so a runtime flip attaches/detaches accordingly.\n\t// Stores without live-updates support (no `subscribe` action) are a\n\t// silent no-op inside the composable, keeping this fully inert.\n\tuseObjectSubscription(objectStore, () => objectType.value, null, {\n\t\tenabled: () => props.subscribe !== false,\n\t})\n\n\treturn {\n\t\tisSelfFetch: true,\n\t\tlist,\n\t\tselfObjectStore: objectStore,\n\t\tselfObjectType: objectType,\n\t\tactiveQuickFilterIndex,\n\t\tselectedQuickFilterIndices,\n\t\tcontentSearch,\n\t\t// The page persists the view state back into the query, and needs both\n\t\t// to do it without trampling the rest of it: the keys it adopted from\n\t\t// the query on load (the only non-`_` ones it may clear), and the ctx\n\t\t// those keys' `@`-tokens resolve against.\n\t\tselfFetchTokenCtx: tokenCtx,\n\t\tinitialQueryFilterKeys: Object.keys(initialActiveFilters),\n\t}\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSelfFetchList.js","sources":["../../../../src/components/CnIndexPage/useSelfFetchList.js"],"sourcesContent":["import { computed, ref, watch } from 'vue'\nimport { useListView } from '../../composables/index.js'\nimport { useObjectSubscription } from '../../composables/useObjectSubscription.js'\nimport { useObjectStore } from '../../store/index.js'\n// Both filter resolvers live in `utils/routeFilters.js` so CnLogsPage applies\n// the same two grammars (route-param interpolation + `?key=value` deep links)\n// without pulling in this composable's index-only sidebar/subscription wiring.\nimport { withPersonalLenses } from '../../utils/personalLenses.js'\nimport { parseSortKeysFromQuery, resolveFilterMap, resolveQueryFilters } from '../../utils/routeFilters.js'\nimport { orderToSortKeys } from '../../utils/sortOrder.js'\n\nexport function resolveInitialQuickFilterIndex(quickFilters) {\n\tconst tabs = Array.isArray(quickFilters) ? quickFilters : null\n\tif (!tabs || tabs.length === 0) {\n\t\treturn null\n\t}\n\tconst di = tabs.findIndex((t) => t && t.default === true)\n\treturn di >= 0 ? di : 0\n}\n\n/**\n * OR-merge several quick-filter maps into one fetch filter. Values for the\n * same key collapse to an array (deduped) when more than one tab contributes\n * it — `buildQueryString` serialises an array as `key[]=a&key[]=b`, which the\n * OpenRegister API interprets as an IN / OR match.\n *\n * @param {Array<object>} filterMaps Already route-resolved filter maps.\n * @return {object} The merged filter (scalar when a key has one value, array when many).\n */\n/**\n * Rows one board fetch asks for. A board shows every matching row at once,\n * so it loads far more than a table page (OpenRegister serves up to 1000);\n * past this the board offers \"Load more\".\n *\n * @type {number}\n */\nexport const BOARD_PAGE_SIZE = 500\n\n/**\n * The sort a quick filter sets while it is active (`sort: { key, order }`, or a\n * list of those for a multi-column order), as sort keys, or null when it sets\n * none.\n *\n * @param {object|undefined} tab A quick filter.\n * @return {Array<{key: string, order: string}>|null} The sort keys.\n * @spec openspec/changes/screens-quick-filter-sort/specs/index-page/spec.md\n */\nexport function quickFilterSortKeys(tab) {\n\tconst sort = tab && tab.sort\n\tif (!sort) {\n\t\treturn null\n\t}\n\tconst keys = (Array.isArray(sort) ? sort : [sort])\n\t\t.filter((entry) => entry && typeof entry.key === 'string' && entry.key !== '')\n\t\t.map((entry) => ({ key: entry.key, order: entry.order === 'desc' ? 'desc' : 'asc' }))\n\treturn keys.length > 0 ? keys : null\n}\n\nfunction unionFilterMaps(filterMaps) {\n\tconst grouped = {}\n\tfor (const map of filterMaps) {\n\t\tif (!map || typeof map !== 'object') {\n\t\t\tcontinue\n\t\t}\n\t\tfor (const [k, v] of Object.entries(map)) {\n\t\t\tif (v === undefined || v === null || v === '') {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!grouped[k]) {\n\t\t\t\tgrouped[k] = []\n\t\t\t}\n\t\t\tfor (const item of (Array.isArray(v) ? v : [v])) {\n\t\t\t\tif (!grouped[k].includes(item)) {\n\t\t\t\t\tgrouped[k].push(item)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tconst out = {}\n\tfor (const [k, arr] of Object.entries(grouped)) {\n\t\tout[k] = arr.length === 1 ? arr[0] : arr\n\t}\n\treturn out\n}\n\n/**\n * Self-fetch mode for CnIndexPage: when register+schema are set but no\n * `objects` prop was passed (manifest-driven pages), drive the list ourselves.\n *\n * @param {object} props CnIndexPage props.\n * @param {import('vue').ComponentInternalInstance|null} instance Pass `getCurrentInstance()`.\n * @param {typeof import('vue').inject} inject Pass Vue's `inject`.\n * @param {{activeFolderSchema?: import('vue').Ref<?{schema: string, register?: string}>}} [extras] `activeFolderSchema`: a ref the page sets to the selected folder's `{ schema, register? }`, switching the loaded object type while it is non-null.\n * @return {object} { isSelfFetch, list, selfObjectStore, selfObjectType, activeQuickFilterIndex }\n */\nexport function useSelfFetchList(props, instance, inject, extras = {}) {\n\tconst objectsProvided = !!(\n\t\tinstance && instance.proxy && instance.proxy.$options && instance.proxy.$options.propsData\n\t\t&& Object.hasOwn(instance.proxy.$options.propsData, 'objects')\n\t)\n\t// A named source wins over register/schema. Without this the page would\n\t// fire an OpenRegister request whose rows it then discards in favour of the\n\t// source's — wasted, but worse than wasted if the manifest names both by\n\t// mistake, because the request succeeds and nothing says the two disagree.\n\tconst isSelfFetch = !!(props.register && props.schema) && !objectsProvided && !props.entitySource\n\n\t// \"Also search inside files\" switch, seeded from `?contentSearch=1` so a\n\t// shared link reproduces the list. Only meaningful when `searchInFiles` is on.\n\tconst routeAtSetup = instance && instance.proxy && instance.proxy.$route\n\tconst contentSearch = ref(props.searchInFiles === true && String(routeAtSetup?.query?.contentSearch) === '1')\n\n\tconst activeQuickFilterIndex = ref(resolveInitialQuickFilterIndex(withPersonalLenses(props.quickFilters, props.personalLenses)))\n\tconst selectedQuickFilterIndices = ref([])\n\tconst isMultiQuickFilter = props.quickFilterMultiple === true\n\n\tif (!isSelfFetch) {\n\t\treturn {\n\t\t\tisSelfFetch: false,\n\t\t\tlist: null,\n\t\t\tselfObjectStore: null,\n\t\t\tselfObjectType: '',\n\t\t\tactiveQuickFilterIndex,\n\t\t\tselectedQuickFilterIndices,\n\t\t\tcontentSearch,\n\t\t\tselfFetchTokenCtx: null,\n\t\t\tinitialQueryFilterKeys: [],\n\t\t}\n\t}\n\n\t// The object type follows the selected folder's own schema when it declares\n\t// one; otherwise the page's register and schema.\n\tconst activeFolderSchema = extras.activeFolderSchema || null\n\tconst resolvedTarget = computed(() => {\n\t\tconst folder = activeFolderSchema && activeFolderSchema.value\n\t\tif (folder && folder.schema) {\n\t\t\tconst register = folder.register || props.register\n\t\t\treturn { register, schema: folder.schema, type: `${register}-${folder.schema}` }\n\t\t}\n\t\t// A collection endpoint gets its own type key, so a plain registration of\n\t\t// the same pair elsewhere never resets it.\n\t\tconst collectionUrl = props.collectionUrl || ''\n\t\tconst type = `${props.register}-${props.schema}` + (collectionUrl ? `@${collectionUrl}` : '')\n\t\treturn { register: props.register, schema: props.schema, type, collectionUrl }\n\t})\n\tconst objectType = computed(() => resolvedTarget.value.type)\n\tconst sidebarState = inject('sidebarState', null) ?? inject('objectSidebarState', null)\n\tconst objectStore = useObjectStore()\n\n\t// Token-resolution context for `@workspace.<key>` / `@config.<key>` /\n\t// `@objectId` / `@object.<field>` inside `props.filter` / quick-filter\n\t// tab filters. Same injects + unwrap shape as CnObjectListWidget's\n\t// `objectCtx`/`workspaceCtx`/`tokenCtx` computeds (and CnDeltaWidget) —\n\t// `cnWorkspaceContext` is the reactive bag a dashboard/workspace-root\n\t// (e.g. hrmq's App.vue for multi-administratie) provides; `cnObjectContext`\n\t// is a detail-page's object context (rare on an index page, but harmless\n\t// to support); `cnAppConfig` is the page-level app config bag. All three\n\t// default to null/absent so an app that never provides them is unaffected.\n\tconst objectCtxRaw = inject('cnObjectContext', null)\n\tconst workspaceCtxRaw = inject('cnWorkspaceContext', null)\n\tconst appConfigRaw = inject('cnAppConfig', null)\n\tconst unwrapCtx = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\n\t/**\n\t * Build the current token-resolution ctx `{ objectId?, object?, workspace, config }`\n\t * from the injected bags, unwrapping Vue refs. Called fresh on every fetch\n\t * so it always reflects the latest workspace/config state.\n\t *\n\t * @return {object} The token ctx.\n\t */\n\tfunction tokenCtx() {\n\t\tconst objCtx = unwrapCtx(objectCtxRaw)\n\t\tconst base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {}\n\t\tbase.workspace = unwrapCtx(workspaceCtxRaw) || {}\n\t\t// The page's own `appConfig` prop wins over a config an ancestor provides.\n\t\tbase.config = { ...(unwrapCtx(appConfigRaw) || {}), ...(props.appConfig || {}) }\n\t\treturn base\n\t}\n\n\t// Reactive signature of the workspace/config bags so a change (e.g. the\n\t// administration switcher writing `activeAdministrationId`) triggers a\n\t// re-fetch below — `fixedFilters` is a plain getter called at fetch time\n\t// (see useListView.resolveFixedFilters), it is NOT auto-tracked by Vue,\n\t// so without this the list would resolve the token once on mount and\n\t// never again. Stringified (like CnDeltaWidget's `sourceKey`) so the\n\t// watcher fires on real content changes only, not object identity.\n\tconst workspaceSignature = computed(() => JSON.stringify(unwrapCtx(workspaceCtxRaw) || {}))\n\tconst appConfigSignature = computed(() => JSON.stringify({ ...(unwrapCtx(appConfigRaw) || {}), ...(props.appConfig || {}) }))\n\n\t// Pass register/schema in their positional id slots (not as a {register, schema} object as\n\t// second arg) — that previously made fetch URLs go to `/api/objects/undefined/[object Object]`.\n\t// Runs (sync, ahead of the list's own watcher) again whenever the type switches.\n\tfunction registerTarget() {\n\t\tconst { register, schema, type, collectionUrl } = resolvedTarget.value\n\t\tif (typeof objectStore.registerObjectType === 'function') {\n\t\t\tobjectStore.registerObjectType(type, schema, register, {\n\t\t\t\tregisterSlug: register,\n\t\t\t\tschemaSlug: schema,\n\t\t\t\t...(collectionUrl ? { collectionUrl } : {}),\n\t\t\t})\n\t\t}\n\t\t// A mixed list tells its own rows apart by `@self.register`, an id, so a slug register needs resolving.\n\t\tif (collectionUrl && !/^\\d+$/.test(String(register)) && typeof objectStore.fetchRegister === 'function') {\n\t\t\tobjectStore.fetchRegister(type)\n\t\t}\n\t}\n\tregisterTarget()\n\twatch(objectType, registerTarget, { flush: 'sync' })\n\n\t// Seed the visible-column set from the configured columns so the sidebar's\n\t// Columns tab reflects the curated default; null when none are configured\n\t// (schema-driven table — every column starts visible).\n\t// A column marked `hidden: true` is declared but starts outside the set,\n\t// so it is off until the person turns it on in the Columns tab.\n\tconst configuredColumnKeys = (props.columns || [])\n\t\t.filter((c) => !(c && typeof c === 'object' && c.hidden === true))\n\t\t.map((c) => (typeof c === 'string' ? c : c && c.key))\n\t\t.filter(Boolean)\n\n\t// Restore a persisted multi-column sort from the route query first (deep\n\t// link / reload); fall back to a host-passed `sortKeys` prop, then the\n\t// legacy single-key `sortKey`/`sortOrder` props.\n\tconst initialRoute = instance && instance.proxy && instance.proxy.$route\n\t// The page's own order, which a lens without a `sort` goes back to.\n\t// `defaultOrder` (manifest `config.defaultOrder`) is the order of last\n\t// resort, after `sortKeys` and `sortKey`.\n\tconst defaultOrderKeys = orderToSortKeys(props.defaultOrder)\n\tconst pageSortKeys = (Array.isArray(props.sortKeys) && props.sortKeys.length > 0)\n\t\t? props.sortKeys\n\t\t: (props.sortKey ? [{ key: props.sortKey, order: props.sortOrder || 'asc' }] : defaultOrderKeys)\n\t// The lens that is active on mount may set its own order.\n\t/**\n\t * The tabs the strip shows, so the fetch filters by the chip that is\n\t * lit: the page's `effectiveQuickFilters` (declared, generated from\n\t * `quickFiltersFrom`, or a named source's), else the prop while the\n\t * page's computeds do not exist yet (setup).\n\t *\n\t * @return {Array<object>|null} The tabs, or null when there are none.\n\t */\n\tfunction shownQuickFilters() {\n\t\tconst shown = instance && instance.proxy ? instance.proxy.effectiveQuickFilters : undefined\n\t\tif (shown !== undefined) {\n\t\t\treturn shown\n\t\t}\n\t\treturn withPersonalLenses(props.quickFilters, props.personalLenses)\n\t}\n\n\tconst initialTabs = withPersonalLenses(props.quickFilters, props.personalLenses)\n\tconst initialTabSort = (!isMultiQuickFilter && Array.isArray(initialTabs))\n\t\t? quickFilterSortKeys(initialTabs[activeQuickFilterIndex.value])\n\t\t: null\n\tconst initialSortKeys = parseSortKeysFromQuery(initialRoute)\n\t\t|| initialTabSort\n\t\t|| (Array.isArray(props.sortKeys) && props.sortKeys.length > 0 ? props.sortKeys : undefined)\n\t\t|| (!props.sortKey && defaultOrderKeys.length > 0 ? defaultOrderKeys : undefined)\n\t// Same deep-link restore for facet filters and search, so opening a\n\t// shared/bookmarked URL reproduces the same view (CnIndexPage persists\n\t// both back to the route on change).\n\tconst initialActiveFilters = resolveQueryFilters(initialRoute && initialRoute.query, tokenCtx())\n\tconst initialSearchTerm = (initialRoute && typeof initialRoute.query?._search === 'string') ? initialRoute.query._search : ''\n\n\t// Set right after useListView returns; the fixed-filters getter reads it lazily.\n\tlet listHandle = null\n\tconst list = useListView(() => objectType.value, {\n\t\tobjectStore,\n\t\tsidebarState,\n\t\tdefaultSort: props.sortKey ? { key: props.sortKey, order: props.sortOrder || 'asc' } : undefined,\n\t\tdefaultSortKeys: initialSortKeys,\n\t\tdefaultActiveFilters: initialActiveFilters,\n\t\tdefaultSearchTerm: initialSearchTerm,\n\t\tdefaultPageSize: props.viewMode === 'board' ? BOARD_PAGE_SIZE : ((props.pagination && props.pagination.limit) || undefined),\n\t\tdefaultVisibleColumns: configuredColumnKeys.length ? configuredColumnKeys : null,\n\t\t// `pages[].config.extend` → OpenRegister's repeated `_extend[]`. A\n\t\t// getter so a reactive change re-scopes the next fetch, matching how\n\t\t// `fixedFilters` is read.\n\t\textend: () => (Array.isArray(props.extend) ? props.extend : []),\n\t\tfixedFilters: () => {\n\t\t\tconst route = instance && instance.proxy && instance.proxy.$route\n\t\t\tconst params = (route && route.params) || {}\n\t\t\tconst ctx = tokenCtx()\n\t\t\tconst queryFilters = resolveQueryFilters(route && route.query, ctx)\n\t\t\tconst base = resolveFilterMap(props.filter, params, ctx)\n\t\t\t// A folder-sidebar scope declaring `searchFields` narrows what the\n\t\t\t// search box searches over while that scope is active. Read off the\n\t\t\t// instance, like `$route` above, because the active scope is the\n\t\t\t// component's own state rather than a prop — and read through the\n\t\t\t// one fixed-filter getter rather than a second search path.\n\t\t\tconst scopeSearchFields = (instance && instance.proxy && instance.proxy.activeScopeSearchFields) || []\n\t\t\tconst scopeBase = scopeSearchFields.length > 0 ? { _searchFields: scopeSearchFields } : {}\n\t\t\t// File-content search widens a text search, so it rides only with a term.\n\t\t\tconst widen = props.searchInFiles === true && contentSearch.value && !!(listHandle && listHandle.searchTerm.value)\n\t\t\t// The visible calendar month, only while the page is in calendar mode.\n\t\t\tconst month = (instance && instance.proxy && instance.proxy.calendarRangeFilter) || {}\n\t\t\tconst scope = { ...(widen ? { ...scopeBase, _content_search: 'true' } : scopeBase), ...month }\n\t\t\tconst lensed = shownQuickFilters()\n\t\t\tconst tabs = Array.isArray(lensed) ? lensed : null\n\t\t\tif (!tabs) {\n\t\t\t\treturn { ...queryFilters, ...scope, ...base }\n\t\t\t}\n\n\t\t\t// Multiple mode: OR the selected tabs' filters together (union).\n\t\t\tif (isMultiQuickFilter) {\n\t\t\t\tconst maps = selectedQuickFilterIndices.value\n\t\t\t\t\t.map((i) => resolveFilterMap(tabs[i]?.filter, params, ctx))\n\t\t\t\treturn { ...queryFilters, ...scope, ...base, ...unionFilterMaps(maps) }\n\t\t\t}\n\n\t\t\t// Single mode: the active tab's filter spread last so it wins\n\t\t\t// over a colliding props.filter entry.\n\t\t\t// Tabs that arrived after setup (`quickFiltersFrom`) have no index\n\t\t\t// yet: the default tab is the one in force, as the strip shows.\n\t\t\tconst activeIdx = activeQuickFilterIndex.value ?? resolveInitialQuickFilterIndex(tabs)\n\t\t\tconst tabFilter = (activeIdx !== null && activeIdx !== undefined) ? tabs[activeIdx]?.filter : null\n\t\t\treturn { ...queryFilters, ...scope, ...base, ...resolveFilterMap(tabFilter, params, ctx) }\n\t\t},\n\t})\n\n\tlistHandle = list\n\n\t// Re-fetch when the quick-filter selection changes (pre-existing), OR\n\t// when the workspace/app-config bag content changes (e.g. the\n\t// administration switcher writes a new `activeAdministrationId`) — a\n\t// `@workspace.<key>`/`@config.<key>` token in `props.filter` must re-scope\n\t// the list without a manual reload. A change to `props.filter` itself (a\n\t// host toggling a filter checkbox) re-fetches the same way.\n\tconst filterSignature = computed(() => JSON.stringify(props.filter ?? null))\n\twatch([activeQuickFilterIndex, selectedQuickFilterIndices, contentSearch, workspaceSignature, appConfigSignature, filterSignature], (now, before) => {\n\t\t// Switching lens: a lens with a `sort` sets that order; leaving one\n\t\t// for a lens without goes back to the page's own order. A column the\n\t\t// person sorts by afterwards still wins until the next switch.\n\t\tif (!isMultiQuickFilter && before && now[0] !== before[0]) {\n\t\t\tconst tabs = shownQuickFilters() || []\n\t\t\tconst keys = quickFilterSortKeys(tabs[now[0]]) || (quickFilterSortKeys(tabs[before[0]]) ? pageSortKeys : null)\n\t\t\tif (keys && list.sortKeys) {\n\t\t\t\tlist.sortKeys.value = keys\n\t\t\t\tlist.sortKey.value = keys[0]?.key ?? null\n\t\t\t\tlist.sortOrder.value = keys[0]?.order ?? 'asc'\n\t\t\t}\n\t\t}\n\t\tif (list && typeof list.refresh === 'function') {\n\t\t\tlist.refresh(1)\n\t\t}\n\t})\n\n\t// Live collection updates (manifest-live-updates): subscribe to the\n\t// page's `or-collection-{register}-{schema}` scope so another user's\n\t// create/update/delete refreshes this list without a manual reload.\n\t// The subscription lifecycle (mount/unmount, in-flight dedupe, epoch\n\t// guard against stale async resolution) lives in useObjectSubscription;\n\t// the refetch-on-event (with the last fetch params, burst-coalesced)\n\t// lives in liveUpdatesPlugin. The `subscribe` prop (default true; from\n\t// a manifest: `config.subscribe: false`) is the opt-out, read through\n\t// a reactive getter so a runtime flip attaches/detaches accordingly.\n\t// Stores without live-updates support (no `subscribe` action) are a\n\t// silent no-op inside the composable, keeping this fully inert.\n\tuseObjectSubscription(objectStore, () => objectType.value, null, {\n\t\tenabled: () => props.subscribe !== false,\n\t})\n\n\treturn {\n\t\tisSelfFetch: true,\n\t\tlist,\n\t\tselfObjectStore: objectStore,\n\t\tselfObjectType: objectType,\n\t\tactiveQuickFilterIndex,\n\t\tselectedQuickFilterIndices,\n\t\tcontentSearch,\n\t\t// The page persists the view state back into the query, and needs both\n\t\t// to do it without trampling the rest of it: the keys it adopted from\n\t\t// the query on load (the only non-`_` ones it may clear), and the ctx\n\t\t// those keys' `@`-tokens resolve against.\n\t\tselfFetchTokenCtx: tokenCtx,\n\t\tinitialQueryFilterKeys: Object.keys(initialActiveFilters),\n\t}\n}\n"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@ import { METADATA_COLUMNS } from '../../constants/metadata.js';
14
14
  import { facetOptions } from '../../utils/facets.js';
15
15
  import { labelLang } from '../../utils/manifestTranslate.js';
16
16
  import { columnsFromSchema, filtersFromSchema } from '../../utils/schema.js';
17
+ import { sameSortKeys, reverseSortKeys } from '../../utils/sortOrder.js';
17
18
  import __sfc_main from '../CnDateRangePicker/CnDateRangePicker.vue.js';
18
19
  import __sfc_main$1 from '../CnIcon/CnIcon.vue.js';
19
20
  import { DEFAULT_DATE_RANGE_PRESETS } from '../CnDateRangePicker/CnDateRangePicker.vue2.js';
@@ -108,6 +109,22 @@ var script = {
108
109
  default: 0,
109
110
  },
110
111
 
112
+ /**
113
+ * The orders the Sort section offers, `[{ value, label, keys }]`, each
114
+ * `keys` a `[{ key, order }]` list read as its ascending form. Empty
115
+ * draws no Sort section (the table view sorts by its headers).
116
+ */
117
+ sortOptions: {
118
+ type: Array,
119
+ default: () => [],
120
+ },
121
+
122
+ /** The list's current order, `[{ key, order }]`. */
123
+ currentSort: {
124
+ type: Array,
125
+ default: () => [],
126
+ },
127
+
111
128
  /** Current search term */
112
129
  searchValue: {
113
130
  type: String,
@@ -250,7 +267,7 @@ var script = {
250
267
  },
251
268
  },
252
269
 
253
- emits: ['clear-filters', 'columns-change', 'columns-reorder', 'columns-reset', 'pin-change', 'filter-change', 'search', 'tab-change', 'update:open'],
270
+ emits: ['clear-filters', 'columns-change', 'columns-reorder', 'columns-reset', 'pin-change', 'filter-change', 'search', 'sort-change', 'tab-change', 'update:open'],
254
271
 
255
272
  data() {
256
273
  return {
@@ -277,6 +294,56 @@ var script = {
277
294
  },
278
295
 
279
296
  computed: {
297
+ /**
298
+ * The Sort option the current order matches, as declared or reversed.
299
+ *
300
+ * @return {{option: object, direction: string}|null} The match, or null.
301
+ */
302
+ sortMatch() {
303
+ const current = Array.isArray(this.currentSort) ? this.currentSort : [];
304
+ for (const option of this.sortOptions) {
305
+ const keys = Array.isArray(option.keys) ? option.keys : [];
306
+ if (sameSortKeys(keys, current)) {
307
+ return { option, direction: 'asc' }
308
+ }
309
+ if (sameSortKeys(reverseSortKeys(keys), current)) {
310
+ return { option, direction: 'desc' }
311
+ }
312
+ }
313
+ return null
314
+ },
315
+
316
+ /** @return {object|null} The Sort option shown as chosen. */
317
+ sortSelected() {
318
+ return this.sortMatch ? this.sortMatch.option : null
319
+ },
320
+
321
+ /** @return {string} The chosen direction, `asc` or `desc`. */
322
+ sortDirection() {
323
+ return this.sortMatch ? this.sortMatch.direction : 'asc'
324
+ },
325
+
326
+ /**
327
+ * The order the list has now, in words: "Current order: Created,
328
+ * descending", or that it is not sorted.
329
+ *
330
+ * @return {string} The sentence.
331
+ */
332
+ currentSortText() {
333
+ const current = Array.isArray(this.currentSort) ? this.currentSort : [];
334
+ if (current.length === 0) {
335
+ return translate('nextcloud-vue', 'Not sorted')
336
+ }
337
+ const labelOf = (key) => {
338
+ const single = this.sortOptions.find((o) => Array.isArray(o.keys) && o.keys.length === 1 && o.keys[0].key === key);
339
+ return single ? single.label : key
340
+ };
341
+ const order = this.sortMatch && this.sortMatch.option.keys.length > 1
342
+ ? `${this.sortMatch.option.label}, ${this.sortDirection === 'desc' ? translate('nextcloud-vue', 'descending') : translate('nextcloud-vue', 'ascending')}`
343
+ : current.map((entry) => `${labelOf(entry.key)}, ${entry.order === 'desc' ? translate('nextcloud-vue', 'descending') : translate('nextcloud-vue', 'ascending')}`).join('; ');
344
+ return translate('nextcloud-vue', 'Current order: {order}', { order })
345
+ },
346
+
280
347
  /** Resolved icon — explicit prop overrides schema.icon */
281
348
  resolvedIcon() {
282
349
  return this.icon || this.schema?.icon || ''
@@ -434,6 +501,50 @@ var script = {
434
501
  },
435
502
 
436
503
  methods: {
504
+ t: translate,
505
+
506
+ /**
507
+ * A field chosen in the Sort section: its order, in the direction
508
+ * already chosen.
509
+ *
510
+ * @param {object|null} option The chosen option.
511
+ * @return {void}
512
+ */
513
+ onSortField(option) {
514
+ const chosen = option && this.sortOptions.find((o) => o.value === option.value);
515
+ if (!chosen) {
516
+ return
517
+ }
518
+ this.emitSort(chosen, this.sortDirection);
519
+ },
520
+
521
+ /**
522
+ * A direction chosen in the Sort section, for the chosen field.
523
+ *
524
+ * @param {string} direction `asc` or `desc`.
525
+ * @return {void}
526
+ */
527
+ onSortDirection(direction) {
528
+ if (!this.sortSelected) {
529
+ return
530
+ }
531
+ this.emitSort(this.sortSelected, direction === 'desc' ? 'desc' : 'asc');
532
+ },
533
+
534
+ /**
535
+ * Emit the order an option stands for in a direction.
536
+ *
537
+ * @param {object} option The Sort option.
538
+ * @param {string} direction `asc` or `desc`.
539
+ * @return {void}
540
+ */
541
+ emitSort(option, direction) {
542
+ const keys = Array.isArray(option.keys) ? option.keys : [];
543
+ /**
544
+ * @event sort-change The Sort section chose an order. Payload: `[{ key, order }]`.
545
+ */
546
+ this.$emit('sort-change', direction === 'desc' ? reverseSortKeys(keys) : keys.map((k) => ({ key: k.key, order: k.order || 'asc' })));
547
+ },
437
548
 
438
549
  /**
439
550
  * A manifest label through the injected lookup, or as written.