@conduction/nextcloud-vue 2.105.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 (153) hide show
  1. package/dist/esm/components/CnCardGrid/CnCardGrid.vue.js +1 -1
  2. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +18 -0
  3. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  4. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +3 -1
  5. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  6. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
  7. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +18 -0
  8. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  9. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +1 -1
  10. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  11. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +53 -4
  12. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  13. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  14. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +9 -0
  15. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js.map +1 -0
  16. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js +24 -0
  17. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -0
  18. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js +49 -0
  19. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -0
  20. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  21. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +36 -18
  22. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  23. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +17 -38
  24. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  25. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  26. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  27. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  28. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  29. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +53 -2
  30. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  31. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +119 -111
  32. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  33. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue.js +1 -1
  34. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +62 -1
  35. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
  36. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
  37. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +159 -50
  38. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  39. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +121 -98
  40. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  41. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +1 -1
  42. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +24 -0
  43. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -1
  44. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +20 -4
  45. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -1
  46. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +22 -4
  47. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  48. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +112 -1
  49. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  50. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +100 -44
  51. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  52. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue.js +1 -1
  53. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js +67 -4
  54. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js.map +1 -1
  55. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js +1 -1
  56. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js.map +1 -1
  57. package/dist/esm/components/CnObjectCard/CnObjectCard.vue.js +1 -1
  58. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +295 -12
  59. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  60. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +267 -144
  61. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  62. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
  63. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +4 -0
  64. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  65. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +3 -1
  66. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  67. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  68. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +93 -1
  69. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  70. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +322 -246
  71. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  72. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue.js +1 -1
  73. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +122 -7
  74. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
  75. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js +189 -93
  76. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js.map +1 -1
  77. package/dist/esm/components/CnTabs/CnTabs.vue.js +1 -1
  78. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  79. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  80. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  81. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +42 -0
  82. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  83. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +38 -19
  84. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  85. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue.js +9 -0
  86. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue.js.map +1 -0
  87. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue2.js +258 -0
  88. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue2.js.map +1 -0
  89. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue3.js +69 -0
  90. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue3.js.map +1 -0
  91. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
  92. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  93. package/dist/esm/index.js +1 -0
  94. package/dist/esm/index.js.map +1 -1
  95. package/dist/esm/l10n/en.json.js +21 -1
  96. package/dist/esm/l10n/en.json.js.map +1 -1
  97. package/dist/esm/l10n/nl.json.js +21 -1
  98. package/dist/esm/l10n/nl.json.js.map +1 -1
  99. package/dist/esm/library-style.js +1 -1
  100. package/dist/esm/nextcloud-vue.css +803 -453
  101. package/dist/esm/utils/dueText.js +18 -3
  102. package/dist/esm/utils/dueText.js.map +1 -1
  103. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  104. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  105. package/dist/esm/utils/sortOrder.js +11 -1
  106. package/dist/esm/utils/sortOrder.js.map +1 -1
  107. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  108. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  109. package/dist/nextcloud-vue.cjs.js +8281 -6350
  110. package/dist/nextcloud-vue.cjs.js.map +2 -2
  111. package/dist/nextcloud-vue.css +803 -453
  112. package/l10n/en.json +21 -1
  113. package/l10n/nl.json +21 -1
  114. package/package.json +1 -1
  115. package/src/components/CnCardGrid/CnCardGrid.md +2 -0
  116. package/src/components/CnCardGrid/CnCardGrid.vue +20 -0
  117. package/src/components/CnCellRenderer/CnCellRenderer.vue +19 -1
  118. package/src/components/CnDetailPage/CnDetailPage.md +2 -2
  119. package/src/components/CnDetailPage/CnDetailPage.vue +53 -4
  120. package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +60 -0
  121. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +36 -29
  122. package/src/components/CnFactsWidget/CnFactsWidget.vue +4 -0
  123. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +70 -4
  124. package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +62 -1
  125. package/src/components/CnIndexPage/CnIndexPage.md +7 -5
  126. package/src/components/CnIndexPage/CnIndexPage.vue +187 -58
  127. package/src/components/CnIndexPage/CnIndexPageWidgets.vue +36 -1
  128. package/src/components/CnIndexPage/useSelfFetchList.js +22 -4
  129. package/src/components/CnIndexSidebar/CnIndexSidebar.md +2 -0
  130. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +148 -1
  131. package/src/components/CnNcWidgetWidget/CnNcWidgetWidget.vue +68 -5
  132. package/src/components/CnObjectCard/CnObjectCard.md +1 -0
  133. package/src/components/CnObjectCard/CnObjectCard.vue +354 -13
  134. package/src/components/CnObjectSidebar/CnFilesTab.vue +6 -0
  135. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +190 -4
  136. package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +209 -9
  137. package/src/components/CnTabs/CnTabs.vue +11 -0
  138. package/src/components/CnTabsWidget/CnTabsWidget.vue +91 -18
  139. package/src/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.md +11 -0
  140. package/src/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue +382 -0
  141. package/src/components/CnTeamWorkloadWidget/index.js +14 -0
  142. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
  143. package/src/components/index.js +1 -0
  144. package/src/css/index-page.css +14 -2
  145. package/src/css/look-board-detail.css +48 -4
  146. package/src/css/look-board-index.css +64 -2
  147. package/src/css/look-board.css +25 -2
  148. package/src/index.js +1 -1
  149. package/src/schemas/app-manifest-v2.schema.json +75 -6
  150. package/src/utils/dueText.js +18 -3
  151. package/src/utils/libraryWidgetKeys.js +1 -0
  152. package/src/utils/sortOrder.js +10 -0
  153. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -105,9 +105,10 @@ import { translate as t } from '@nextcloud/l10n'
105
105
  import { NcLoadingIcon } from '@nextcloud/vue'
106
106
  import { computed, inject, ref } from 'vue'
107
107
  import { useEndpointSource } from '../../composables/useEndpointSource.js'
108
+ import { dispatchAction } from '../../utils/actionsDispatcher.js'
108
109
  import { resolveObjectTokenContext } from '../../utils/detailObjectContext.js'
109
110
  import { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'
110
- import { resolveFilterValue } from '../../utils/resolveFilterTokens.js'
111
+ import { resolveDeepTokens, resolveFilterValue } from '../../utils/resolveFilterTokens.js'
111
112
 
112
113
  /**
113
114
  * The OpenRegister endpoint that lists the caller's live flow runs.
@@ -205,6 +206,8 @@ export default {
205
206
  * so a library string localises through the consuming app's l10n.
206
207
  */
207
208
  cnTranslate: { default: () => (key) => key },
209
+ /** The page's action dispatcher (CnPageRenderer), for `headerAction`. */
210
+ cnDispatchAction: { default: null },
208
211
  },
209
212
 
210
213
  props: {
@@ -228,6 +231,10 @@ export default {
228
231
  * subject's finished runs render below it. Absent = today's org-wide
229
232
  * widget, unchanged.
230
233
  * - `emptyText` — override for the "nothing running" line.
234
+ * - `headerAction` — an action descriptor drawn in the card header
235
+ * (`{ label, icon?, ...action }`, "Flow uitvoeren"), reported through
236
+ * the `header` event and dispatched with the page's dispatcher; its
237
+ * tokens resolve against the record. The runs are read again after it.
231
238
  *
232
239
  * @type {{limit?: number, pollSeconds?: number, rowRoute?: string, runRoute?: string, subject?: string, emptyText?: string, title?: string}}
233
240
  */
@@ -257,6 +264,8 @@ export default {
257
264
  },
258
265
  },
259
266
 
267
+ emits: ['header'],
268
+
260
269
  setup(props) {
261
270
  // The shared endpoint engine owns request dedup, the short-TTL cache
262
271
  // and the cn:page:refresh / cn:widget:refresh subscriptions — the same
@@ -352,6 +361,7 @@ export default {
352
361
  refetchCompleted,
353
362
  resolvedSubject,
354
363
  injectedTranslate,
364
+ tokenCtx,
355
365
  }
356
366
  },
357
367
 
@@ -521,7 +531,17 @@ export default {
521
531
  },
522
532
  },
523
533
 
534
+ watch: {
535
+ 'content.headerAction': {
536
+ deep: true,
537
+ handler() {
538
+ this.emitHeader()
539
+ },
540
+ },
541
+ },
542
+
524
543
  mounted() {
544
+ this.emitHeader()
525
545
  this.startPolling()
526
546
  document.addEventListener('visibilitychange', this.onVisibilityChange)
527
547
  },
@@ -531,6 +551,47 @@ export default {
531
551
  },
532
552
 
533
553
  methods: {
554
+ /**
555
+ * Tell the card around the runs what its header shows: the header
556
+ * action, if any.
557
+ *
558
+ * @return {void}
559
+ */
560
+ emitHeader() {
561
+ const action = this.content && this.content.headerAction
562
+ const usable = action && typeof action === 'object' && typeof action.label === 'string' && action.label !== ''
563
+ /**
564
+ * @event header The card header's parts: `{ link, note, action, pill }`, `action` being `{ label, icon, run }` or null.
565
+ */
566
+ this.$emit('header', {
567
+ link: null,
568
+ note: '',
569
+ action: usable ? { label: this.tr(action.label), icon: action.icon || '', run: () => this.runHeaderAction() } : null,
570
+ pill: null,
571
+ })
572
+ },
573
+
574
+ /**
575
+ * Run the header action with the record's tokens resolved, then read the
576
+ * runs again so a started run shows.
577
+ *
578
+ * @return {Promise<void>}
579
+ */
580
+ async runHeaderAction() {
581
+ const { label, icon, ...action } = resolveDeepTokens(this.content.headerAction, this.tokenCtx())
582
+ try {
583
+ const outcome = await (typeof this.cnDispatchAction === 'function'
584
+ ? this.cnDispatchAction(action)
585
+ : dispatchAction(action, { router: this.$router || null }))
586
+ if (outcome === false) {
587
+ return
588
+ }
589
+ } catch {
590
+ return
591
+ }
592
+ this.refetchAll()
593
+ },
594
+
534
595
  /**
535
596
  * Start (or restart) the refetch interval, unless polling is disabled.
536
597
  *
@@ -416,7 +416,7 @@ export default {
416
416
  | `quickFilterMode` | String | `'chips'` | How the quick filters render: `'chips'` (pill strip) or `'dropdown'` (a single `NcSelect`). Fed from `pages[].config.quickFilterMode`. |
417
417
  | `quickFilterMultiple` | Boolean | `false` | Allow several quick filters active at once; selected tabs' filters are OR-ed into the fetch (same field → `field[]=` array). Fed from `pages[].config.quickFilterMultiple`. |
418
418
  | `quickFilterMaxVisible` | Number | `0` | Chips mode only — how many quick-filter pills render inline before the rest move behind one more chip — a `⋯` pill that opens a panel of the hidden lenses. `0` renders every tab, which wraps the actions bar onto a second line once a page declares more than a handful. The visible set is the first *n* entries of `quickFilters`. Fed from `pages[].config.quickFilterMaxVisible`. |
419
- | `quickFilterPlacement` | String | `''` | Where the quick-filter chips sit. `'toolbar'` keeps them on one row with the Filter button and the view switch, in whatever width that row has beside a side panel (DqTeamwachtrij): the chips take the room the buttons leave, and those that do not fit move behind the overflow chip. Fed from `pages[].config.quickFilterPlacement`. |
419
+ | `quickFilterPlacement` | String | `''` | Where the quick-filter chips sit. `'toolbar'` keeps them on one row with the Filter button and the view switch, in whatever width that row has beside a side panel (DqTeamwachtrij): the chips take the room the buttons leave, and those that do not fit move behind the overflow chip. Fed from `pages[].config.quickFilterPlacement`. `'row'` puts them on a row of their own above the toolbar, wrapping onto more lines instead of folding into the overflow chip (DqTeamwachtrij); beside a side panel the row sits in the list column. |
420
420
  | `cardComponent` | String | `''` | Optional name of a consumer-provided card component (registered in the v2 `registry` — any kind carrying a `component` — or in the legacy `customComponents` map on `CnAppRoot`) to render in place of the default `CnObjectCard` when the page is in card-grid view mode. Resolution priority: `#card` scoped slot → `cardComponent` registry entry → default `CnObjectCard`. Unknown names log `console.warn` once and fall back to the default. |
421
421
  | `customComponents` | Object | `null` | Optional explicit `customComponents` registry. Overrides the registry injected from `CnAppRoot` via `cnCustomComponents`. Mostly used by unit tests; production consumers register components on `CnAppRoot` instead. |
422
422
  | `subscribe` | Boolean | `true` | Self-fetch mode only. When `register` + `schema` are set (and no `objects` prop), the page subscribes to the collection's `or-collection-{register}-{schema}` live-update scope on mount and refetches on an update event; released on unmount. Set `false` (manifest: `config.subscribe: false`) for static / read-once views. No-op in consumer-managed mode or on stores without live-updates support. |
@@ -879,9 +879,9 @@ The list view (`view-mode="list"`) and standalone sort dropdown add these props:
879
879
  | `listIcon` | String | `''` | MDI icon for the list view-toggle option. |
880
880
  | `listConfig` | Object | `{}` | Field mapping for the default list rows (`CnObjectRow`). |
881
881
  | `listComponent` | String | `''` | Custom row component, resolved against the v2 `registry` (any kind carrying a `component`) and then the legacy `customComponents` map. |
882
- | `showSortSelect` | Boolean | `false` | Show a standalone sort dropdown in the actions bar. |
883
- | `sortSelectOptions` | Array | `[]` | Options `{ value, label, order? }` for the sort dropdown. On a self-fetch page a choice orders the list as a header click does (`_order`, kept in the address): by the option's `order`, else its `value` read as an order (`"-createdAt"`, `"priority:desc,title"`). |
884
- | `sortSelectValue` | String | `''` | Selected sort dropdown value. Empty lets a self-fetch page show the option matching its current order, so a header click that matches an option shows it. |
882
+ | `showSortSelect` | Boolean | `false` | Deprecated, without effect: the toolbar draws no sort select. The table sorts by its column headers; the list, card and map views by the Sort section in the index sidebar. |
883
+ | `sortSelectOptions` | Array | `[]` | The orders the index sidebar's Sort section offers in the list, card and map views: `{ value, label, order? }`, the order its `order` or its `value` read as one (`"-createdAt"`, `"priority:desc,title"`). Without them the section offers the sortable columns. |
884
+ | `sortSelectValue` | String | `''` | Deprecated, without effect: the Sort section shows the current order. |
885
885
  | `defaultOrder` | String, Object or Array | `''` | The order a self-fetch page opens on when neither the address nor `sortKeys`/`sortKey` gives one (manifest `config.defaultOrder`): `"-createdAt"`, `"priority:desc,title"`, an `_order` map `{ "priority": "desc" }` or a `[{ key, order }]` list. |
886
886
 
887
887
  The `#list-item`, `#row-icon`, `#row-badges`, and `#row-actions` slots override the list rows (see [CnObjectList](./cn-object-list.md)). Emits `@sort-change` with the chosen sort value.
@@ -1097,6 +1097,8 @@ Sources: `register` (fetch the folder list from an OpenRegister `register`/`sche
1097
1097
  }
1098
1098
  ```
1099
1099
 
1100
+ `scopeViews` limits the scope's look (columns, title, count line and the other scope keys) to the view modes it names, e.g. `["table", "list"]`, so a board view keeps the page's generic look (DqWerkbord) while the scope still filters it.
1101
+
1100
1102
  `pane: false` draws no pane. The folder still comes from the deep-link filter (`/cases?caseType=<uuid>`) and its scope look (title, count line, columns, quick filters, `afterList` from the folder or its row's `x-index`) still applies, so a list narrowed to one case type looks like that case type's list without a folder column (boards DqWooVerzoeken, DqKlachten):
1101
1103
 
1102
1104
  ```json
@@ -1149,7 +1151,7 @@ columns are right.
1149
1151
 
1150
1152
  ## Board look
1151
1153
 
1152
- With `look: "board"` (the page's `look` prop, `config.look`, or the app's) the index page takes the DqZaken setup: a header with the count line from `countText` (`{shown}`, `{total}`), the buttons Download, Actions, the buildiq square and the primary one in that order, a two-row toolbar (saved-view chips, Filter with a count and the view switch; then search and active filter chips), a bulk band between toolbar and table (`bulkHint`), the table as a white card with one menu button per row, and a footer inside the card (`footerNote`). `cardFields` names the facts of each card in the cards view. Without the look nothing changes.
1154
+ With `look: "board"` (the page's `look` prop, `config.look`, or the app's) the index page takes the DqZaken setup: a header with the count line from `countText` (`{shown}`, `{total}`), the buttons Download, Actions, the buildiq square and the primary one in that order, a two-row toolbar (saved-view chips, Filter with a count and the view switch; then search and active filter chips), a bulk band between toolbar and table (`bulkHint`), the table as a white card with one menu button per row, and a footer inside the card (`footerNote`). `cardFields` names the facts of each card in the cards view, and `cardLayout` draws each card as a record instead: a pill and the number on the head row, the title, a meta line, and a footer with a pill and a date, every part from a field (`{ headPill: { field, colorMap, labels }, number, title, meta: [...], footerPill, footerDate: { field, label } }`). Without the look nothing changes.
1153
1155
 
1154
1156
  `bulkBandAlways` (`config.bulkBandAlways`) draws the board bulk band before a selection too, its buttons
1155
1157
  disabled until rows are selected (DqZaken, DqWerkbord).
@@ -157,6 +157,25 @@
157
157
  :widgets="sidePanel" />
158
158
 
159
159
  <!-- Actions bar -->
160
+ <!-- `quickFilterPlacement: "row"`: the chips on a row of their own above
161
+ the toolbar, wrapping onto more lines (DqTeamwachtrij). -->
162
+ <div
163
+ v-if="quickFilterPlacement === 'row' && tabStripEntries && tabStripEntries.length > 0"
164
+ class="cn-index-page__quick-filter-row"
165
+ data-testid="cn-index-page-quick-filter-row">
166
+ <CnQuickFilterBar
167
+ inline
168
+ :tabs="tabStripEntries"
169
+ :mode="quickFilterMode"
170
+ :maxVisible="quickFilterMaxVisible"
171
+ :fit="false"
172
+ :multiple="quickFilterMultiple"
173
+ :activeIndex="activeQuickFilterIndex"
174
+ :selectedIndices="selectedQuickFilterIndices"
175
+ :counts="effectiveTabCounts"
176
+ @update:activeIndex="onQuickFilterChange"
177
+ @update:selectedIndices="onQuickFilterMultiChange" />
178
+ </div>
160
179
  <CnActionsBar
161
180
  :layout="isBoardLook ? 'board' : 'nextcloud'"
162
181
  :activeFilterChips="activeFilterChips"
@@ -189,9 +208,6 @@
189
208
  :mapLabel="mapLabel"
190
209
  :mapIcon="mapIcon"
191
210
  :listIcon="listIcon"
192
- :showSortSelect="showSortSelect"
193
- :sortOptions="sortSelectOptions"
194
- :sortValue="effectiveSortSelectValue"
195
211
  :showSearch="search !== false && (inlineSearch || isBoardLook)"
196
212
  :searchPlacement="searchPlacement"
197
213
  :searchValue="effectiveSearchValue"
@@ -210,7 +226,6 @@
210
226
  :bulkActions="mergedBulkActions"
211
227
  :documentationUrl="documentationUrl"
212
228
  :documentationLabel="documentationLabel || undefined"
213
- @sortChange="onSortSelectChange"
214
229
  @add="onAddClick"
215
230
  @clearSelection="onSelect([])"
216
231
  @removeFilter="onRemoveActiveFilter"
@@ -365,7 +380,7 @@
365
380
  (between the view toggle and the actions) when the manifest
366
381
  declares `config.quickFilters`. Switching tabs re-fetches with
367
382
  the merged filter; @event quick-filter-change. -->
368
- <template v-if="tabStripEntries && tabStripEntries.length > 0" #filters>
383
+ <template v-if="tabStripEntries && tabStripEntries.length > 0 && quickFilterPlacement !== 'row'" #filters>
369
384
  <CnQuickFilterBar
370
385
  inline
371
386
  :tabs="tabStripEntries"
@@ -373,7 +388,7 @@
373
388
  :maxVisible="quickFilterMaxVisible"
374
389
  :fit="quickFilterPlacement === 'toolbar'"
375
390
  :multiple="quickFilterMultiple"
376
- :activeIndex="activeQuickFilterIndex"
391
+ :activeIndex="shownQuickFilterIndex"
377
392
  :selectedIndices="selectedQuickFilterIndices"
378
393
  :counts="effectiveTabCounts"
379
394
  @update:activeIndex="onQuickFilterChange"
@@ -772,7 +787,7 @@
772
787
  :rowTitle="rowTitle"
773
788
  :showUnread="showUnreadRows"
774
789
  :filterable="tableHeaderFilters"
775
- :activeFilters="effectiveActiveFilters"
790
+ :activeFilters="tableActiveFilters"
776
791
  :filterRegister="typeof register === 'string' ? register : ''"
777
792
  @columnFilter="onColumnFilterEvent"
778
793
  @sort="onSortEvent"
@@ -1057,6 +1072,8 @@
1057
1072
  :selectable="selectable"
1058
1073
  :clickToView="rowClickOpens"
1059
1074
  :cardFields="resolvedCardFields"
1075
+ :cardLayout="cardLayout"
1076
+ :cardFieldColumns="cardFieldColumns"
1060
1077
  :accentOf="cardAccent"
1061
1078
  :selectedIds="internalSelectedIds"
1062
1079
  :rowKey="rowKey"
@@ -1229,6 +1246,8 @@
1229
1246
  :showMetadata="resolvedSidebar.showMetadata !== false"
1230
1247
  :personalColumns="personalColumns"
1231
1248
  :pinnedCount="pinnedColumnCount"
1249
+ :sortOptions="sidebarSortOptions"
1250
+ :currentSort="currentSortKeys"
1232
1251
  v-bind="sidebarSearchProps"
1233
1252
  @update:open="sidebarOpen = $event"
1234
1253
  @search="onSearchEvent"
@@ -1237,7 +1256,8 @@
1237
1256
  @pinChange="onPinChange"
1238
1257
  @columnsReset="onColumnsReset"
1239
1258
  @filterChange="onFilterEvent"
1240
- @clearFilters="onClearFilters" />
1259
+ @clearFilters="onClearFilters"
1260
+ @sortChange="onSidebarSortChange" />
1241
1261
  </div>
1242
1262
  </template>
1243
1263
 
@@ -1285,6 +1305,7 @@ import {
1285
1305
  refIdOf,
1286
1306
  resolveDropValue,
1287
1307
  } from '../../utils/boardReferenceColumns.js'
1308
+ import { isColumnSortable } from '../../utils/columnFilters.js'
1288
1309
  import { reportBindingProblems } from '../../utils/diagnostics.js'
1289
1310
  import { fetchGroupedCounts } from '../../utils/fetchAggregate.js'
1290
1311
  import { fetchFilterCounts, fetchListTotal } from '../../utils/fetchFilterCounts.js'
@@ -1311,7 +1332,7 @@ import { DEFAULT_ROW_INDICATOR_CAP } from '../../utils/rowIndicators.js'
1311
1332
  import { buildRouteQueryFromViewState, buildViewCreatePayload, extractViewState, extractViewStateFromRouteQuery, normalizeSharedWith, savedViewScope, viewMatchesScope } from '../../utils/savedViewHelpers.js'
1312
1333
  import { columnsFromSchema, fieldsFromSchema } from '../../utils/schema.js'
1313
1334
  import { resolveScopeLayout, resolveScopePresentation } from '../../utils/scopeListLayout.js'
1314
- import { orderToSortKeys, sameSortKeys } from '../../utils/sortOrder.js'
1335
+ import { orderToSortKeys } from '../../utils/sortOrder.js'
1315
1336
  import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js'
1316
1337
  import { CnActionButtons } from '../CnActionButtons/index.js'
1317
1338
  import { CnActionsBar } from '../CnActionsBar/index.js'
@@ -1350,7 +1371,7 @@ import { orderColumns, personalColumnsKey, reconcilePersonalColumns } from './pe
1350
1371
  import { createSelfModeActions, resolveRowTarget } from './selfModeActions.js'
1351
1372
  import { applyRowPatches, normalisePaneWidth, rowIdOf, splitLayoutFor } from './splitView.js'
1352
1373
  import { useNamedSource } from './useNamedSource.js'
1353
- import { BOARD_PAGE_SIZE, useSelfFetchList } from './useSelfFetchList.js'
1374
+ import { BOARD_PAGE_SIZE, resolveInitialQuickFilterIndex, useSelfFetchList } from './useSelfFetchList.js'
1354
1375
 
1355
1376
  /** Rows fetched for one month in calendar mode. */
1356
1377
  const CALENDAR_PAGE_SIZE = 200
@@ -1934,6 +1955,20 @@ export default {
1934
1955
  default: null,
1935
1956
  },
1936
1957
 
1958
+ /**
1959
+ * Board look: the record layout of each card in the cards view
1960
+ * (manifest key `config.cardLayout`): `{ headPill, number, title,
1961
+ * meta, footerPill, footerDate }`, each part naming a field. See
1962
+ * CnObjectCard `layout`.
1963
+ *
1964
+ * @type {object}
1965
+ * @spec openspec/changes/board-record-card-layout/specs/board-record-card-layout/spec.md#requirement-a-board-card-can-draw-the-record-layout-from-config
1966
+ */
1967
+ cardLayout: {
1968
+ type: Object,
1969
+ default: null,
1970
+ },
1971
+
1937
1972
  /**
1938
1973
  * Gives each card in the card view a status accent: a colored start
1939
1974
  * border and icon. See CnObjectCard `accent`.
@@ -2157,13 +2192,15 @@ export default {
2157
2192
  * the Filter button and the view switch, in whatever width that row has
2158
2193
  * beside a side panel (DqTeamwachtrij): the chips take the room the
2159
2194
  * buttons leave, and those that do not fit move into the overflow chip.
2195
+ * `'row'` puts them on a row of their own above the toolbar, wrapping
2196
+ * onto more lines instead of folding into the overflow chip.
2160
2197
  *
2161
- * @type {''|'toolbar'}
2198
+ * @type {''|'toolbar'|'row'}
2162
2199
  */
2163
2200
  quickFilterPlacement: {
2164
2201
  type: String,
2165
2202
  default: '',
2166
- validator: (v) => ['', 'toolbar'].includes(v),
2203
+ validator: (v) => ['', 'toolbar', 'row'].includes(v),
2167
2204
  },
2168
2205
 
2169
2206
  /**
@@ -3197,9 +3234,11 @@ export default {
3197
3234
  },
3198
3235
 
3199
3236
  /**
3200
- * Show a standalone sort dropdown in the actions bar (for card/list
3201
- * views without sortable headers). Emits `@sort-change` with the value.
3202
- * Fed from the manifest as `pages[].config.showSortSelect`.
3237
+ * Deprecated, without effect: the toolbar draws no sort select. The
3238
+ * table sorts by its column headers, the list, card and map views by
3239
+ * the Sort section in the index sidebar.
3240
+ *
3241
+ * @deprecated
3203
3242
  */
3204
3243
  showSortSelect: {
3205
3244
  type: Boolean,
@@ -3207,7 +3246,11 @@ export default {
3207
3246
  },
3208
3247
 
3209
3248
  /**
3210
- * Options for the standalone sort dropdown (manifest `config.sortSelectOptions`).
3249
+ * The orders the index sidebar's Sort section offers in the list, card
3250
+ * and map views (manifest `config.sortSelectOptions`): `{ value, label,
3251
+ * order? }`, the order its `order` (an `orderToSortKeys` form) or its
3252
+ * `value` read as one. Without them the section offers the sortable
3253
+ * columns.
3211
3254
  *
3212
3255
  * @type {Array<{ value: string, label: string }>}
3213
3256
  */
@@ -3217,8 +3260,10 @@ export default {
3217
3260
  },
3218
3261
 
3219
3262
  /**
3220
- * Selected value of the standalone sort dropdown. Empty lets a
3221
- * self-fetch page show the option matching its current order.
3263
+ * Deprecated, without effect: the toolbar sort select is gone; the
3264
+ * Sort section in the index sidebar shows the current order.
3265
+ *
3266
+ * @deprecated
3222
3267
  */
3223
3268
  sortSelectValue: {
3224
3269
  type: String,
@@ -3271,6 +3316,9 @@ export default {
3271
3316
  * defaults to `field`).
3272
3317
  * - `folders` — explicit folder list for `source:'custom'`.
3273
3318
  * - `allLabel` / `title` / `allowCreate` — passed to CnFolderSidebar.
3319
+ * - `scopeViews` — the view modes the scope's look (columns, title,
3320
+ * count line and the other scope keys) applies in, e.g. `["table",
3321
+ * "list"]`; other views keep the page's own look. Default: every view.
3274
3322
  * - `pane` — `false` draws no pane: the scope still comes from the
3275
3323
  * deep-link filter (`?caseType=<id>`) and still applies its look, as
3276
3324
  * a list narrowed to one case type does without a folder column.
@@ -5002,22 +5050,45 @@ export default {
5002
5050
  },
5003
5051
 
5004
5052
  /**
5005
- * The sort select's shown value: `sortSelectValue` when the host
5006
- * controls it, else on a self-fetch page the option whose order is the
5007
- * list's current order (a header click that matches one shows it).
5053
+ * The list's current order, `[{ key, order }]`: the self-fetched list's,
5054
+ * else the `sortKeys` / `sortKey` props.
5008
5055
  *
5009
- * @return {string} The option value, or '' when none matches.
5056
+ * @return {Array<{key: string, order: string}>} The order.
5010
5057
  */
5011
- effectiveSortSelectValue() {
5012
- if (this.sortSelectValue) {
5013
- return this.sortSelectValue
5058
+ currentSortKeys() {
5059
+ if (this.isSelfFetchMode && this.list && this.list.sortKeys) {
5060
+ return this.list.sortKeys.value || []
5014
5061
  }
5015
- if (!this.isSelfFetchMode || !this.list || !this.list.sortKeys) {
5016
- return ''
5062
+ if (Array.isArray(this.sortKeys) && this.sortKeys.length > 0) {
5063
+ return this.sortKeys
5017
5064
  }
5018
- const current = this.list.sortKeys.value || []
5019
- const match = (this.sortSelectOptions || []).find((o) => o && sameSortKeys(this.sortSelectKeys(o), current))
5020
- return match ? match.value : ''
5065
+ return this.sortKey ? [{ key: this.sortKey, order: this.sortOrder || 'asc' }] : []
5066
+ },
5067
+
5068
+ /**
5069
+ * The orders the index sidebar's Sort section offers: none in the table
5070
+ * view, which sorts by its column headers; else `sortSelectOptions`,
5071
+ * or else the sortable columns, ascending.
5072
+ *
5073
+ * @return {Array<{value: string, label: string, keys: Array<{key: string, order: string}>}>} The options.
5074
+ */
5075
+ sidebarSortOptions() {
5076
+ if (this.currentViewMode === 'table') {
5077
+ return []
5078
+ }
5079
+ if (Array.isArray(this.sortSelectOptions) && this.sortSelectOptions.length > 0) {
5080
+ return this.sortSelectOptions
5081
+ .filter((o) => o && o.value !== undefined && o.value !== null)
5082
+ .map((o) => ({
5083
+ value: String(o.value),
5084
+ label: this.cnTranslate(o.label || String(o.value)),
5085
+ keys: orderToSortKeys(o.order !== undefined ? o.order : o.value),
5086
+ }))
5087
+ .filter((o) => o.keys.length > 0)
5088
+ }
5089
+ return (this.tableColumns || [])
5090
+ .filter((c) => c && typeof c === 'object' && c.key && isColumnSortable(c, this.effectiveSchema))
5091
+ .map((c) => ({ value: c.key, label: this.cnTranslate(c.label || c.key), keys: [{ key: c.key, order: 'asc' }] }))
5021
5092
  },
5022
5093
 
5023
5094
  /**
@@ -5049,7 +5120,7 @@ export default {
5049
5120
  * @spec openspec/changes/index-columns-per-scope/specs/index-page/spec.md
5050
5121
  */
5051
5122
  activeScopeLayout() {
5052
- if (!this.folderSidebar) {
5123
+ if (!this.folderSidebar || !this.scopeShapesView) {
5053
5124
  return { columns: null, sortKeys: [], searchFields: [] }
5054
5125
  }
5055
5126
  return resolveScopeLayout({
@@ -5059,6 +5130,23 @@ export default {
5059
5130
  })
5060
5131
  },
5061
5132
 
5133
+ /**
5134
+ * Whether the active scope's look applies in the current view mode:
5135
+ * always, unless `folderSidebar.scopeViews` names the view modes it is
5136
+ * for (`["table", "list"]`), so the board keeps the page's generic
5137
+ * look (DqWerkbord). The scope's filter applies in every view.
5138
+ *
5139
+ * @spec openspec/changes/index-scope-views/specs/index-page/spec.md#requirement-a-scope-look-can-be-limited-to-view-modes
5140
+ * @return {boolean}
5141
+ */
5142
+ scopeShapesView() {
5143
+ const views = this.folderSidebar && this.folderSidebar.scopeViews
5144
+ if (!Array.isArray(views) || views.length === 0) {
5145
+ return true
5146
+ }
5147
+ return views.includes(this.currentViewMode)
5148
+ },
5149
+
5062
5150
  /**
5063
5151
  * This page's own value of each presentation key, which a query preset
5064
5152
  * may have set: what `presented()` falls back to without a scope.
@@ -5093,7 +5181,7 @@ export default {
5093
5181
  * @spec openspec/changes/index-scope-presentation/specs/index-page/spec.md#requirement-a-preset-or-a-scope-sets-the-list-s-presentation
5094
5182
  */
5095
5183
  activeScopePresentation() {
5096
- if (!this.folderSidebar) {
5184
+ if (!this.folderSidebar || !this.scopeShapesView) {
5097
5185
  return {}
5098
5186
  }
5099
5187
  return resolveScopePresentation({ scope: this.activeScope, row: this.activeScopeRow })
@@ -5335,6 +5423,20 @@ export default {
5335
5423
  * @return {(Array<object>|null)} The tabs.
5336
5424
  * @spec openspec/changes/working-list-row-actions/specs/index-page/spec.md
5337
5425
  */
5426
+ /**
5427
+ * The chip the strip lights: the active tab, else the default tab of
5428
+ * tabs that arrived after setup (`quickFiltersFrom`), which is the
5429
+ * one the fetch applies.
5430
+ *
5431
+ * @return {number|null} The tab index, or null without tabs.
5432
+ */
5433
+ shownQuickFilterIndex() {
5434
+ if (this.activeQuickFilterIndex !== null && this.activeQuickFilterIndex !== undefined) {
5435
+ return this.activeQuickFilterIndex
5436
+ }
5437
+ return this.quickFilterMultiple ? null : resolveInitialQuickFilterIndex(this.tabStripEntries)
5438
+ },
5439
+
5338
5440
  tabStripEntries() {
5339
5441
  if (this.lensTabs.length > 0) {
5340
5442
  return this.lensTabs
@@ -5561,6 +5663,22 @@ export default {
5561
5663
  return specs
5562
5664
  },
5563
5665
 
5666
+ /**
5667
+ * The rendered columns by key, so a card fact shows what its table
5668
+ * cell shows (label, formatter, widget).
5669
+ *
5670
+ * @return {object} Column definitions keyed by property.
5671
+ */
5672
+ cardFieldColumns() {
5673
+ const byKey = {}
5674
+ for (const col of this.renderedColumns || []) {
5675
+ if (col && typeof col === 'object' && typeof col.key === 'string') {
5676
+ byKey[col.key] = col
5677
+ }
5678
+ }
5679
+ return byKey
5680
+ },
5681
+
5564
5682
  /**
5565
5683
  * The property keys the board look's card facts show: the page's
5566
5684
  * `cardFields`, else the first four list columns. Null without the
@@ -6410,8 +6528,11 @@ export default {
6410
6528
 
6411
6529
  const present = new Set(cols.map((c) => (typeof c === 'string' ? c : c.key)))
6412
6530
  const byKey = new Map(this.governedColumns.map((c) => [c.key, c]))
6531
+ // A scope that sets its own columns does not get its folder field back:
6532
+ // every row of the scope holds the same value there.
6533
+ const scopeField = this.activeScopeLayout.columns ? this.folderFilterKey : ''
6413
6534
  visible.forEach((key) => {
6414
- if (present.has(key)) {
6535
+ if (present.has(key) || key === scopeField) {
6415
6536
  return
6416
6537
  }
6417
6538
  const def = byKey.get(key)
@@ -6886,6 +7007,28 @@ export default {
6886
7007
  return count > 0 ? `${name}, ${t('nextcloud-vue', '{count} active', { count })}` : name
6887
7008
  },
6888
7009
 
7010
+ /** @return {string} The field the folder sidebar filters by, or ''. */
7011
+ folderFilterKey() {
7012
+ return (this.folderSidebar && (this.folderSidebar.filterField || this.folderSidebar.field)) || ''
7013
+ },
7014
+
7015
+ /**
7016
+ * The filters the table's header filters show: every active filter but
7017
+ * the selected folder's own, which the Active chip already names, so
7018
+ * the table draws no summary chip for it.
7019
+ *
7020
+ * @return {object} The filters.
7021
+ */
7022
+ tableActiveFilters() {
7023
+ const filters = this.effectiveActiveFilters || {}
7024
+ const key = this.folderFilterKey
7025
+ if (!key || this.selectedFolderId === null || this.selectedFolderId === undefined || !Object.hasOwn(filters, key)) {
7026
+ return filters
7027
+ }
7028
+ const { [key]: _folder, ...rest } = filters
7029
+ return rest
7030
+ },
7031
+
6889
7032
  /**
6890
7033
  * The active filters as chips for the board toolbar's second row, one
6891
7034
  * per filter: "Team: Woo". A filter with no value is not active.
@@ -7289,6 +7432,8 @@ export default {
7289
7432
  showMetadata: this.resolvedSidebar.showMetadata !== false,
7290
7433
  personalColumns: this.personalColumns,
7291
7434
  pinnedCount: this.pinnedColumnCount,
7435
+ sortOptions: this.sidebarSortOptions,
7436
+ currentSort: this.currentSortKeys,
7292
7437
  ...this.sidebarSearchProps,
7293
7438
  }
7294
7439
  },
@@ -8761,35 +8906,18 @@ export default {
8761
8906
  },
8762
8907
 
8763
8908
  /**
8764
- * The sort keys a sort-select option stands for: its `order` (any
8765
- * `orderToSortKeys` form), else its `value` read as an order.
8766
- *
8767
- * @param {object} option A `sortSelectOptions` entry.
8768
- * @return {Array<{key: string, order: string}>} The sort keys.
8769
- */
8770
- sortSelectKeys(option) {
8771
- if (!option) {
8772
- return []
8773
- }
8774
- return orderToSortKeys(option.order !== undefined ? option.order : option.value)
8775
- },
8776
-
8777
- /**
8778
- * A choice in the sort select. On a self-fetch page it orders the list
8779
- * as a header click does (`_order`, kept in the address); every page
8780
- * also emits `sort-change` with the value.
8909
+ * An order chosen in the index sidebar's Sort section. It goes the way
8910
+ * a header click goes: the self-fetched list sorts (`_order`, kept in
8911
+ * the address), and `sort` is emitted for a host that sorts itself.
8781
8912
  *
8782
- * @param {string} value The chosen option's value.
8913
+ * @param {Array<{key: string, order: string}>} keys The chosen order.
8783
8914
  * @return {void}
8784
8915
  */
8785
- onSortSelectChange(value) {
8786
- const option = (this.sortSelectOptions || []).find((o) => o && o.value === value)
8787
- const keys = this.sortSelectKeys(option)
8788
- if (option && keys.length > 0 && this.isSelfFetchMode && typeof this.list.onSort === 'function') {
8789
- this.list.onSort({ key: keys[0].key, order: keys[0].order, keys })
8790
- this.persistViewStateToRoute(this.currentViewState())
8916
+ onSidebarSortChange(keys) {
8917
+ if (!Array.isArray(keys) || keys.length === 0) {
8918
+ return
8791
8919
  }
8792
- this.$emit('sort-change', value)
8920
+ this.onSortEvent({ key: keys[0].key, order: keys[0].order, keys })
8793
8921
  },
8794
8922
 
8795
8923
  /**
@@ -9977,6 +10105,7 @@ export default {
9977
10105
  'columns-reset': () => this.onColumnsReset(),
9978
10106
  'filter-change': (event) => this.onFilterEvent(event),
9979
10107
  'clear-filters': () => this.onClearFilters(),
10108
+ 'sort-change': (keys) => this.onSidebarSortChange(keys),
9980
10109
  },
9981
10110
  }
9982
10111
  },