@conduction/nextcloud-vue 2.62.0 → 2.64.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 (209) hide show
  1. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js +10 -0
  2. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js.map +1 -0
  3. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js +189 -0
  4. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js.map +1 -0
  5. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js +72 -0
  6. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js.map +1 -0
  7. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +425 -39
  8. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +119 -42
  10. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +186 -9
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  13. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +65 -42
  14. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  15. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +61 -16
  16. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
  17. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
  18. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js +20 -0
  19. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
  20. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +3 -2
  21. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
  22. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +33 -2
  23. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  24. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  25. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  26. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +53 -37
  27. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  28. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +29 -0
  29. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  30. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +152 -135
  31. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  32. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js +10 -0
  33. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js.map +1 -0
  34. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +293 -0
  35. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -0
  36. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +189 -0
  37. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -0
  38. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +253 -9
  39. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  40. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +126 -50
  41. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  42. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +66 -14
  43. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  44. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +23 -14
  45. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  46. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  47. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
  48. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  49. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js +3 -1
  50. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js.map +1 -1
  51. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js +1 -1
  52. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js.map +1 -1
  53. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +234 -9
  54. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  55. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +96 -57
  56. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  57. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +81 -4
  58. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
  59. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +22 -5
  60. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
  61. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +81 -2
  62. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  63. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +7 -3
  64. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  65. package/dist/esm/components/CnIndexPage/selfModeActions.js +3 -3
  66. package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
  67. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +41 -2
  68. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  69. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +8 -3
  70. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  71. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js +121 -12
  72. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js.map +1 -1
  73. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js +3 -1
  74. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js.map +1 -1
  75. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +326 -1
  76. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  77. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +112 -26
  78. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  79. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +63 -2
  80. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
  81. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +96 -37
  82. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
  83. package/dist/esm/components/CnStagesWidget/stagesModel.js +5 -2
  84. package/dist/esm/components/CnStagesWidget/stagesModel.js.map +1 -1
  85. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +52 -2
  86. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  87. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +6 -6
  88. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  89. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +14 -2
  90. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  91. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +2 -1
  92. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  93. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +10 -0
  94. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js.map +1 -0
  95. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +337 -0
  96. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -0
  97. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +102 -0
  98. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -0
  99. package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js +34 -0
  100. package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js.map +1 -0
  101. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +131 -8
  102. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
  103. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js +40 -38
  104. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
  105. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +8 -6
  106. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  107. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  108. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  109. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +73 -1
  110. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
  111. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +14 -2
  112. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
  113. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +19 -0
  114. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
  115. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js +1 -1
  116. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
  117. package/dist/esm/composables/useDependencyCheck.js +65 -0
  118. package/dist/esm/composables/useDependencyCheck.js.map +1 -0
  119. package/dist/esm/composables/useWalkthrough.js +218 -6
  120. package/dist/esm/composables/useWalkthrough.js.map +1 -1
  121. package/dist/esm/index.js +2 -0
  122. package/dist/esm/index.js.map +1 -1
  123. package/dist/esm/l10n/en.json.js +45 -1
  124. package/dist/esm/l10n/en.json.js.map +1 -1
  125. package/dist/esm/l10n/nl.json.js +45 -1
  126. package/dist/esm/l10n/nl.json.js.map +1 -1
  127. package/dist/esm/nextcloud-vue.css +780 -81
  128. package/dist/esm/store/useObjectStore.js +8 -0
  129. package/dist/esm/store/useObjectStore.js.map +1 -1
  130. package/dist/esm/utils/columnFilters.js +311 -0
  131. package/dist/esm/utils/columnFilters.js.map +1 -0
  132. package/dist/esm/utils/groupAutocomplete.js +123 -0
  133. package/dist/esm/utils/groupAutocomplete.js.map +1 -0
  134. package/dist/esm/utils/libraryWidgetKeys.js +2 -0
  135. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  136. package/dist/esm/utils/pickerOptions.js +198 -0
  137. package/dist/esm/utils/pickerOptions.js.map +1 -0
  138. package/dist/esm/utils/schema.js +83 -7
  139. package/dist/esm/utils/schema.js.map +1 -1
  140. package/dist/esm/utils/schemaRefSlug.js +19 -3
  141. package/dist/esm/utils/schemaRefSlug.js.map +1 -1
  142. package/dist/esm/utils/timelineEvents.js +174 -0
  143. package/dist/esm/utils/timelineEvents.js.map +1 -0
  144. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  145. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  146. package/dist/esm/utils/walkthroughSignals.js +88 -0
  147. package/dist/esm/utils/walkthroughSignals.js.map +1 -0
  148. package/dist/nextcloud-vue.cjs.js +13088 -7937
  149. package/dist/nextcloud-vue.cjs.js.map +2 -2
  150. package/dist/nextcloud-vue.css +780 -81
  151. package/l10n/en.json +45 -0
  152. package/l10n/nl.json +45 -0
  153. package/package.json +1 -1
  154. package/src/components/CnAdminActionCard/CnAdminActionCard.vue +234 -0
  155. package/src/components/CnAdminActionCard/index.js +4 -0
  156. package/src/components/CnAppNav/CnAppNav.vue +568 -43
  157. package/src/components/CnAppRoot/CnAppRoot.md +4 -0
  158. package/src/components/CnAppRoot/CnAppRoot.vue +223 -16
  159. package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +61 -16
  160. package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +22 -1
  161. package/src/components/CnCellRenderer/CnCellRenderer.vue +33 -2
  162. package/src/components/CnChoiceCards/CnChoiceCards.vue +56 -31
  163. package/src/components/CnDashboardPage/CnDashboardPage.vue +45 -4
  164. package/src/components/CnDataTable/CnColumnFilterPopover.vue +524 -0
  165. package/src/components/CnDataTable/CnDataTable.md +3 -0
  166. package/src/components/CnDataTable/CnDataTable.vue +305 -5
  167. package/src/components/CnDetailPage/CnDetailPage.md +4 -0
  168. package/src/components/CnDetailPage/CnDetailPage.vue +60 -1
  169. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
  170. package/src/components/CnFieldHelper/CnFieldHelper.vue +4 -2
  171. package/src/components/CnFormDialog/CnFormDialog.vue +290 -31
  172. package/src/components/CnHeaderWidget/CnHeaderWidget.vue +116 -6
  173. package/src/components/CnIndexPage/CnIndexPage.vue +87 -4
  174. package/src/components/CnIndexPage/selfModeActions.js +3 -3
  175. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +46 -2
  176. package/src/components/CnResourceSelect/CnResourceSelect.vue +123 -12
  177. package/src/components/CnSetupWizard/CnSetupWizard.vue +392 -3
  178. package/src/components/CnStagesWidget/CnStagesWidget.vue +180 -2
  179. package/src/components/CnStagesWidget/stagesModel.js +5 -2
  180. package/src/components/CnStatWidget/CnStatWidget.vue +57 -3
  181. package/src/components/CnTabsWidget/CnTabsWidget.vue +15 -2
  182. package/src/components/CnTimelineWidget/CnTimelineWidget.vue +477 -0
  183. package/src/components/CnTimelineWidget/dashboardRegistration.js +32 -0
  184. package/src/components/CnTimelineWidget/index.js +4 -0
  185. package/src/components/CnWalkthrough/CnWalkthrough.vue +143 -10
  186. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  187. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  188. package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +4 -0
  189. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +107 -1
  190. package/src/components/CnWizardDialog/CnWizardDialog.vue +20 -1
  191. package/src/components/index.js +2 -0
  192. package/src/composables/useDependencyCheck.js +61 -0
  193. package/src/composables/useWalkthrough.js +217 -5
  194. package/src/css/dashboard.css +49 -0
  195. package/src/css/detail-page.css +29 -4
  196. package/src/css/kpi-card.css +24 -1
  197. package/src/css/table.css +99 -6
  198. package/src/index.js +2 -0
  199. package/src/schemas/app-manifest-v2.schema.json +135 -3
  200. package/src/store/useObjectStore.js +8 -0
  201. package/src/utils/columnFilters.js +324 -0
  202. package/src/utils/groupAutocomplete.js +119 -0
  203. package/src/utils/libraryWidgetKeys.js +2 -0
  204. package/src/utils/pickerOptions.js +194 -0
  205. package/src/utils/schema.js +83 -7
  206. package/src/utils/schemaRefSlug.js +19 -3
  207. package/src/utils/timelineEvents.js +171 -0
  208. package/src/utils/validateManifestV2.compiled.js +2 -2
  209. package/src/utils/walkthroughSignals.js +94 -0
@@ -1,16 +1,20 @@
1
1
  import axios__default from '@nextcloud/axios';
2
- import { translate } from '@nextcloud/l10n';
2
+ import { translate, translatePlural } from '@nextcloud/l10n';
3
3
  import { generateUrl } from '@nextcloud/router';
4
4
  import { NcLoadingIcon, NcCheckboxRadioSwitch } from '@nextcloud/vue';
5
+ import FilterIcon from 'vue-material-design-icons/Filter.vue';
6
+ import FilterOutline from 'vue-material-design-icons/FilterOutline.vue';
7
+ import __sfc_main$1 from './CnColumnFilterPopover.vue.js';
5
8
  import { useClickDragGuard } from '../../composables/useClickDragGuard.js';
9
+ import { columnFilterDef, columnFilterState, isColumnFilterActive, columnFilterParams, clearedColumnFilterParams, isColumnSortable } from '../../utils/columnFilters.js';
6
10
  import { resolveHref, openRowTarget, followLinkClick } from '../../utils/linkNavigation.js';
7
11
  import { nextSortState } from '../../utils/multiColumnSort.js';
8
12
  import { preventMiddleClickAutoscroll, isRowMiddleClick, isNewTabHandled, markNewTabHandled, isNewTabClick } from '../../utils/rowAuxClick.js';
9
13
  import { DEFAULT_ROW_INDICATOR_CAP, resolveRowIndicators } from '../../utils/rowIndicators.js';
10
14
  import { columnsFromSchema } from '../../utils/schema.js';
11
15
  import __sfc_main from '../CnCellRenderer/CnCellRenderer.vue.js';
12
- import __sfc_main$1 from '../CnIcon/CnIcon.vue.js';
13
- import __sfc_main$2 from '../CnLockIndicator/CnLockIndicator.vue.js';
16
+ import __sfc_main$2 from '../CnIcon/CnIcon.vue.js';
17
+ import __sfc_main$3 from '../CnLockIndicator/CnLockIndicator.vue.js';
14
18
 
15
19
  /**
16
20
  * CnDataTable — Generic sortable data table for list views.
@@ -68,8 +72,11 @@ var script = {
68
72
  NcLoadingIcon,
69
73
  NcCheckboxRadioSwitch,
70
74
  CnCellRenderer: __sfc_main,
71
- CnIcon: __sfc_main$1,
72
- CnLockIndicator: __sfc_main$2,
75
+ CnColumnFilterPopover: __sfc_main$1,
76
+ CnIcon: __sfc_main$2,
77
+ CnLockIndicator: __sfc_main$3,
78
+ FilterIcon,
79
+ FilterOutline,
73
80
  },
74
81
 
75
82
  inject: {
@@ -100,7 +107,13 @@ var script = {
100
107
  * reader is looking. `columnsFromSchema` fills it from the JSON Schema property
101
108
  * description automatically, so schema-driven tables get it for free.
102
109
  *
103
- * @type {Array<{key: string, label: string, description: string, sortable: boolean, width: string, class: string, cellClass: string}|string>}
110
+ * `secondary` draws a second, muted line under the cell's value: a field
111
+ * key (`"number"`), a template with `{field}` placeholders
112
+ * (`"{number} · {requester}"`, dotted paths allowed), or a function of
113
+ * the row. Empty fields leave their placeholder blank; a line that
114
+ * resolves to nothing is not drawn.
115
+ *
116
+ * @type {Array<{key: string, label: string, description: string, sortable: boolean, width: string, class: string, cellClass: string, secondary: (string|((row: object) => string))}|string>}
104
117
  */
105
118
  columns: {
106
119
  type: Array,
@@ -438,9 +451,39 @@ var script = {
438
451
  type: Boolean,
439
452
  default: false,
440
453
  },
454
+
455
+ /**
456
+ * Show a filter button in every header whose column can filter (see
457
+ * `columnFilterDef`): enum, boolean, text, number, date and reference
458
+ * columns backed by a schema property. A column opts out with
459
+ * `filterable: false`. Applying a filter emits `column-filter`; the
460
+ * host owns the filter state and passes it back as `activeFilters`.
461
+ *
462
+ * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header
463
+ */
464
+ filterable: {
465
+ type: Boolean,
466
+ default: false,
467
+ },
468
+
469
+ /**
470
+ * The active filter map, `{ paramKey: values[] }`: the same map the
471
+ * facet sidebar writes, so a header filter and a sidebar filter on one
472
+ * field show the same state.
473
+ */
474
+ activeFilters: {
475
+ type: Object,
476
+ default: () => ({}),
477
+ },
478
+
479
+ /** Register slug a reference column's filter searches in when the column names none. */
480
+ filterRegister: {
481
+ type: String,
482
+ default: '',
483
+ },
441
484
  },
442
485
 
443
- emits: ['row-click', 'row-aux-click', 'row-context-menu', 'select', 'select-all', 'sort', 'view-all'],
486
+ emits: ['row-click', 'row-aux-click', 'row-context-menu', 'select', 'select-all', 'sort', 'view-all', 'column-filter'],
444
487
 
445
488
  setup() {
446
489
  // Tell a deliberate row click apart from a text-selection drag.
@@ -449,6 +492,10 @@ var script = {
449
492
 
450
493
  data() {
451
494
  return {
495
+ /** Key of the column whose filter panel is open, or ''. */
496
+ openFilterKey: '',
497
+ /** The open filter button's bounding rect, to place the panel. */
498
+ filterAnchorRect: null,
452
499
  /**
453
500
  * Resolved aggregate-column values, keyed by `String(row[rowKey])`
454
501
  * then by column key. Populated by `loadAggregates()` for columns
@@ -566,8 +613,12 @@ var script = {
566
613
  overrides: this.columnOverrides,
567
614
  })
568
615
  : this.columns;
616
+ // Every column backed by a schema property sorts unless it says
617
+ // `sortable: false`; object columns from a manifest used to need an
618
+ // explicit flag and so could not sort at all.
619
+ const withSort = (c) => (c && typeof c === 'object' ? { ...c, sortable: isColumnSortable(c, this.schema) } : c);
569
620
  if (!(cols || []).some((c) => typeof c === 'string')) {
570
- return cols || []
621
+ return (cols || []).map(withSort)
571
622
  }
572
623
  const schemaCols = this.schema
573
624
  ? columnsFromSchema(this.schema, { overrides: this.columnOverrides })
@@ -575,7 +626,7 @@ var script = {
575
626
  const byKey = new Map(schemaCols.map((c) => [c.key, c]));
576
627
  return (cols || []).map((c) => {
577
628
  if (typeof c !== 'string') {
578
- return c
629
+ return withSort(c)
579
630
  }
580
631
  return byKey.get(c) || { key: c, label: c, sortable: true }
581
632
  })
@@ -616,6 +667,40 @@ var script = {
616
667
  return links
617
668
  },
618
669
 
670
+ /**
671
+ * The column whose filter panel is open, or null.
672
+ *
673
+ * @return {object|null}
674
+ */
675
+ openFilterColumn() {
676
+ if (!this.openFilterKey) {
677
+ return null
678
+ }
679
+ return this.effectiveColumns.find((c) => c && c.key === this.openFilterKey && this.filterDefFor(c)) || null
680
+ },
681
+
682
+ /**
683
+ * One chip per column with an active header filter.
684
+ *
685
+ * @return {Array<{key: string, col: object, text: string, removeLabel: string}>}
686
+ */
687
+ activeColumnFilterChips() {
688
+ if (!this.filterable) {
689
+ return []
690
+ }
691
+ return this.effectiveColumns
692
+ .filter((c) => c && this.isColumnFiltered(c))
693
+ .map((col) => {
694
+ const label = this.translateLabel(col.label);
695
+ const text = translate('nextcloud-vue', '{column}: {value}', { column: label, value: this.columnFilterSummary(col) });
696
+ return { key: col.key, col, text, removeLabel: translate('nextcloud-vue', 'Remove filter {column}', { column: label }) }
697
+ })
698
+ },
699
+
700
+ activeFiltersLabel() {
701
+ return translate('nextcloud-vue', 'Active filters')
702
+ },
703
+
619
704
  effectiveSortKeys() {
620
705
  if (this.sortKeys && this.sortKeys.length > 0) {
621
706
  return this.sortKeys
@@ -999,6 +1084,36 @@ var script = {
999
1084
  * @param {object} col The column definition.
1000
1085
  * @return {unknown} The value handed to the slot / CnCellRenderer.
1001
1086
  */
1087
+ /**
1088
+ * The secondary line of a cell, from `col.secondary`: a function is
1089
+ * called with the row; a string with `{field}` placeholders is a
1090
+ * template filled from the row (dotted paths allowed); any other
1091
+ * string is a field key. Returns '' when there is nothing to draw.
1092
+ *
1093
+ * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-table-column-carries-a-secondary-line
1094
+ * @param {object} row The row.
1095
+ * @param {object} col The column definition.
1096
+ * @return {string} The secondary text, or ''.
1097
+ */
1098
+ secondaryValue(row, col) {
1099
+ const spec = col?.secondary;
1100
+ if (typeof spec === 'function') {
1101
+ const out = spec(row);
1102
+ return out === null || out === undefined ? '' : String(out)
1103
+ }
1104
+ if (typeof spec !== 'string' || spec === '') {
1105
+ return ''
1106
+ }
1107
+ const asText = (v) => (v === null || v === undefined ? '' : String(v));
1108
+ if (spec.includes('{')) {
1109
+ const filled = spec.replace(/\{([^}]+)\}/g, (_, key) => asText(this.getCellValue(row, key.trim())));
1110
+ // A template whose every field is empty leaves only separators:
1111
+ // nothing worth a line.
1112
+ return /[\p{L}\p{N}]/u.test(filled) ? filled.trim() : ''
1113
+ }
1114
+ return asText(this.getCellValue(row, spec))
1115
+ },
1116
+
1002
1117
  cellValue(row, col) {
1003
1118
  if (col && col.aggregate) {
1004
1119
  const cached = this.aggregateValues[String(row[this.rowKey])];
@@ -1287,6 +1402,135 @@ var script = {
1287
1402
  * @param {string} key Column key.
1288
1403
  * @param {MouseEvent} [event] The originating click event.
1289
1404
  */
1405
+ /**
1406
+ * The filter definition for a column, or null when it does not filter
1407
+ * (header filters off, `filterable: false`, or nothing to filter on).
1408
+ *
1409
+ * @param {object} col The column.
1410
+ * @return {object|null}
1411
+ */
1412
+ filterDefFor(col) {
1413
+ if (!this.filterable || !col) {
1414
+ return null
1415
+ }
1416
+ return columnFilterDef(col, this.schema)
1417
+ },
1418
+
1419
+ columnFilterStateFor(col) {
1420
+ return columnFilterState(this.filterDefFor(col), this.activeFilters)
1421
+ },
1422
+
1423
+ isColumnFiltered(col) {
1424
+ const def = this.filterDefFor(col);
1425
+ return !!def && isColumnFilterActive(def, this.activeFilters)
1426
+ },
1427
+
1428
+ filterButtonLabel(col) {
1429
+ const column = this.translateLabel(col.label);
1430
+ return this.isColumnFiltered(col)
1431
+ ? translate('nextcloud-vue', 'Filter {column}, active', { column })
1432
+ : translate('nextcloud-vue', 'Filter {column}', { column })
1433
+ },
1434
+
1435
+ /**
1436
+ * Short text for an active filter: the picked labels, or the range.
1437
+ *
1438
+ * @param {object} col The column.
1439
+ * @return {string}
1440
+ */
1441
+ columnFilterSummary(col) {
1442
+ const def = this.filterDefFor(col);
1443
+ const state = this.columnFilterStateFor(col);
1444
+ if (!def) {
1445
+ return ''
1446
+ }
1447
+ if (def.kind === 'enum') {
1448
+ const labels = Object.fromEntries((def.options || []).map((o) => [o.value, o.label]));
1449
+ return state.values.map((v) => this.translateLabel(labels[v] || v)).join(', ')
1450
+ }
1451
+ if (def.kind === 'reference') {
1452
+ return translatePlural('nextcloud-vue', '{count} selected', '{count} selected', state.values.length, { count: state.values.length })
1453
+ }
1454
+ if (def.kind === 'number' || def.kind === 'date') {
1455
+ if (state.from && state.to) {
1456
+ return translate('nextcloud-vue', '{from} to {to}', { from: state.from, to: state.to })
1457
+ }
1458
+ return state.from
1459
+ ? translate('nextcloud-vue', 'from {value}', { value: state.from })
1460
+ : translate('nextcloud-vue', 'up to {value}', { value: state.to })
1461
+ }
1462
+ if (def.kind === 'boolean') {
1463
+ return state.value === 'true' ? translate('nextcloud-vue', 'Yes') : translate('nextcloud-vue', 'No')
1464
+ }
1465
+ return state.value
1466
+ },
1467
+
1468
+ /**
1469
+ * Open or close a column's filter panel under its button.
1470
+ *
1471
+ * @param {object} col The column.
1472
+ * @param {Event} event The click.
1473
+ * @return {void}
1474
+ */
1475
+ toggleColumnFilter(col, event) {
1476
+ if (this.openFilterKey === col.key) {
1477
+ this.closeColumnFilter();
1478
+ return
1479
+ }
1480
+ const target = event && event.currentTarget;
1481
+ this.filterAnchorRect = target && typeof target.getBoundingClientRect === 'function'
1482
+ ? target.getBoundingClientRect()
1483
+ : null;
1484
+ this.openFilterKey = col.key;
1485
+ },
1486
+
1487
+ /**
1488
+ * Close the open panel and give focus back to its button.
1489
+ *
1490
+ * @return {void}
1491
+ */
1492
+ closeColumnFilter() {
1493
+ const key = this.openFilterKey;
1494
+ this.openFilterKey = '';
1495
+ if (!key) {
1496
+ return
1497
+ }
1498
+ this.$nextTick(() => {
1499
+ const ref = this.$refs['filterButton-' + key];
1500
+ const button = Array.isArray(ref) ? ref[0] : ref;
1501
+ if (button && typeof button.focus === 'function') {
1502
+ button.focus();
1503
+ }
1504
+ });
1505
+ },
1506
+
1507
+ /**
1508
+ * Apply a panel's state: emit the query parameters it stands for.
1509
+ *
1510
+ * @param {object} col The column.
1511
+ * @param {object} state The panel state.
1512
+ * @return {void}
1513
+ * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-a-header-filter-speaks-the-sidebars-query-language
1514
+ */
1515
+ applyColumnFilter(col, state) {
1516
+ const def = this.filterDefFor(col);
1517
+ if (def) {
1518
+ /**
1519
+ * @event column-filter A header filter was applied or cleared.
1520
+ * @type {{ key: string, params: object }} `params` maps each query parameter the column owns to its values; an empty list clears it.
1521
+ */
1522
+ this.$emit('column-filter', { key: col.key, params: columnFilterParams(def, state) });
1523
+ }
1524
+ this.closeColumnFilter();
1525
+ },
1526
+
1527
+ clearColumnFilter(col) {
1528
+ const def = this.filterDefFor(col);
1529
+ if (def) {
1530
+ this.$emit('column-filter', { key: col.key, params: clearedColumnFilterParams(def) });
1531
+ }
1532
+ },
1533
+
1290
1534
  onHeaderClick(key, event) {
1291
1535
  this.applySort(key, !!(event && event.shiftKey));
1292
1536
  },