@conduction/nextcloud-vue 2.65.0 → 2.66.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 (61) hide show
  1. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +99 -2
  2. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  3. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +438 -383
  4. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  5. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +9 -3
  6. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -1
  7. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +1 -1
  8. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -1
  9. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +78 -21
  10. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  11. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +129 -96
  12. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  13. package/dist/esm/components/CnFkResolveCell/CnFkResolveCell.vue2.js +3 -1
  14. package/dist/esm/components/CnFkResolveCell/CnFkResolveCell.vue2.js.map +1 -1
  15. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +144 -4
  16. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  17. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  18. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +63 -7
  19. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
  20. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +2 -1
  21. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
  22. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js +11 -0
  23. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
  24. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js +2 -1
  25. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
  26. package/dist/esm/l10n/en.json.js +3 -0
  27. package/dist/esm/l10n/en.json.js.map +1 -1
  28. package/dist/esm/l10n/nl.json.js +3 -0
  29. package/dist/esm/l10n/nl.json.js.map +1 -1
  30. package/dist/esm/mixins/pageViews.js +400 -0
  31. package/dist/esm/mixins/pageViews.js.map +1 -0
  32. package/dist/esm/nextcloud-vue.css +18 -0
  33. package/dist/esm/utils/actionsDispatcher.js +11 -2
  34. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  35. package/dist/esm/utils/columnFilters.js +27 -3
  36. package/dist/esm/utils/columnFilters.js.map +1 -1
  37. package/dist/esm/utils/schema.js +40 -2
  38. package/dist/esm/utils/schema.js.map +1 -1
  39. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  40. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  41. package/dist/nextcloud-vue.cjs.js +1521 -596
  42. package/dist/nextcloud-vue.cjs.js.map +2 -2
  43. package/dist/nextcloud-vue.css +18 -0
  44. package/l10n/en.json +3 -0
  45. package/l10n/nl.json +3 -0
  46. package/package.json +1 -1
  47. package/src/components/CnDashboardPage/CnDashboardPage.vue +165 -8
  48. package/src/components/CnDataTable/CnColumnFilterPopover.vue +11 -5
  49. package/src/components/CnDetailPage/CnDetailPage.vue +94 -5
  50. package/src/components/CnFkResolveCell/CnFkResolveCell.vue +3 -1
  51. package/src/components/CnFormDialog/CnFormDialog.vue +144 -4
  52. package/src/components/CnHeaderWidget/CnHeaderWidget.vue +67 -8
  53. package/src/components/CnSegmentedControl/CnSegmentedControl.md +1 -1
  54. package/src/components/CnSegmentedControl/CnSegmentedControl.vue +12 -0
  55. package/src/css/detail-page.css +5 -0
  56. package/src/mixins/pageViews.js +396 -0
  57. package/src/schemas/app-manifest-v2.schema.json +29 -1
  58. package/src/utils/actionsDispatcher.js +11 -2
  59. package/src/utils/columnFilters.js +29 -2
  60. package/src/utils/schema.js +40 -2
  61. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -7,7 +7,7 @@ let popoverSeq = 0;
7
7
  *
8
8
  * Renders the control that fits the column: a checkbox list for an enum, yes,
9
9
  * no or any for a boolean, from and to for a number or a date, a searchable
10
- * list of referenced objects for a reference, and an equals box for text.
10
+ * list of referenced objects for a reference, and a contains box for text.
11
11
  * Real inputs throughout, so every control works from the keyboard. Escape
12
12
  * closes it and the host returns focus to the filter button.
13
13
  *
@@ -141,8 +141,14 @@ var script = {
141
141
  return translate('nextcloud-vue', 'To')
142
142
  },
143
143
 
144
- equalsLabel() {
145
- return translate('nextcloud-vue', 'Equals')
144
+ /**
145
+ * Label of the text box: the text filter matches on contains.
146
+ *
147
+ * @return {string}
148
+ * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains
149
+ */
150
+ containsLabel() {
151
+ return translate('nextcloud-vue', 'Contains')
146
152
  },
147
153
 
148
154
  clearLabel() {
@@ -1 +1 @@
1
- {"version":3,"file":"CnColumnFilterPopover.vue2.js","sources":["../../../../src/components/CnDataTable/CnColumnFilterPopover.vue"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<Teleport to=\"body\" :disabled=\"!teleport\">\n\t\t<div\n\t\t\tref=\"panel\"\n\t\t\tclass=\"cn-column-filter\"\n\t\t\trole=\"dialog\"\n\t\t\t:aria-labelledby=\"headingId\"\n\t\t\t:style=\"panelStyle\"\n\t\t\tdata-testid=\"cn-column-filter\"\n\t\t\t@keydown.esc.stop.prevent=\"close\"\n\t\t\t@keydown.enter=\"onEnter\">\n\t\t\t<p :id=\"headingId\" class=\"cn-column-filter__heading\">\n\t\t\t\t{{ headingText }}\n\t\t\t</p>\n\n\t\t\t<!-- enum and reference: a checkbox list of real inputs -->\n\t\t\t<template v-if=\"def.kind === 'enum' || def.kind === 'reference'\">\n\t\t\t\t<label v-if=\"def.kind === 'reference'\" class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ searchLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"query\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-search\"\n\t\t\t\t\t\t@input=\"onSearchInput\">\n\t\t\t\t</label>\n\t\t\t\t<p v-if=\"def.kind === 'reference' && referenceLoading\" class=\"cn-column-filter__hint\" role=\"status\">\n\t\t\t\t\t{{ loadingLabel }}\n\t\t\t\t</p>\n\t\t\t\t<p v-else-if=\"def.kind === 'reference' && referenceError\" class=\"cn-column-filter__hint\" role=\"status\">\n\t\t\t\t\t{{ referenceError }}\n\t\t\t\t</p>\n\t\t\t\t<fieldset class=\"cn-column-filter__options\">\n\t\t\t\t\t<legend class=\"cn-column-filter__sr-only\">\n\t\t\t\t\t\t{{ headingText }}\n\t\t\t\t\t</legend>\n\t\t\t\t\t<label\n\t\t\t\t\t\tv-for=\"option in listOptions\"\n\t\t\t\t\t\t:key=\"option.value\"\n\t\t\t\t\t\tclass=\"cn-column-filter__option\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t:value=\"option.value\"\n\t\t\t\t\t\t\t:checked=\"draft.values.includes(option.value)\"\n\t\t\t\t\t\t\tdata-testid=\"cn-column-filter-option\"\n\t\t\t\t\t\t\t@change=\"toggleValue(option.value)\">\n\t\t\t\t\t\t<span>{{ option.label }}</span>\n\t\t\t\t\t</label>\n\t\t\t\t\t<p v-if=\"listOptions.length === 0 && !referenceLoading\" class=\"cn-column-filter__hint\">\n\t\t\t\t\t\t{{ noOptionsLabel }}\n\t\t\t\t\t</p>\n\t\t\t\t</fieldset>\n\t\t\t</template>\n\n\t\t\t<!-- boolean: yes, no, any -->\n\t\t\t<fieldset v-else-if=\"def.kind === 'boolean'\" class=\"cn-column-filter__options\">\n\t\t\t\t<legend class=\"cn-column-filter__sr-only\">\n\t\t\t\t\t{{ headingText }}\n\t\t\t\t</legend>\n\t\t\t\t<label v-for=\"option in booleanOptions\" :key=\"option.value\" class=\"cn-column-filter__option\">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"radio\"\n\t\t\t\t\t\t:name=\"headingId + '-bool'\"\n\t\t\t\t\t\t:value=\"option.value\"\n\t\t\t\t\t\t:checked=\"draft.value === option.value\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-option\"\n\t\t\t\t\t\t@change=\"draft.value = option.value\">\n\t\t\t\t\t<span>{{ option.label }}</span>\n\t\t\t\t</label>\n\t\t\t</fieldset>\n\n\t\t\t<!-- number and date: from and to -->\n\t\t\t<div v-else-if=\"def.kind === 'number' || def.kind === 'date'\" class=\"cn-column-filter__range\">\n\t\t\t\t<label class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ fromLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"draft.from\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\t:type=\"def.kind === 'date' ? 'date' : 'number'\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-from\">\n\t\t\t\t</label>\n\t\t\t\t<label class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ toLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"draft.to\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\t:type=\"def.kind === 'date' ? 'date' : 'number'\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-to\">\n\t\t\t\t</label>\n\t\t\t</div>\n\n\t\t\t<!-- string: equals -->\n\t\t\t<label v-else class=\"cn-column-filter__field\">\n\t\t\t\t<span class=\"cn-column-filter__label\">{{ equalsLabel }}</span>\n\t\t\t\t<input\n\t\t\t\t\tv-model=\"draft.value\"\n\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-text\">\n\t\t\t</label>\n\n\t\t\t<div class=\"cn-column-filter__actions\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-column-filter__button\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-clear\"\n\t\t\t\t\t@click=\"clear\">\n\t\t\t\t\t{{ clearLabel }}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-column-filter__button cn-column-filter__button--primary\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-apply\"\n\t\t\t\t\t@click=\"apply\">\n\t\t\t\t\t{{ applyLabel }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</div>\n\t</Teleport>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\n\nlet popoverSeq = 0\n\n/**\n * CnColumnFilterPopover — the small panel a table header's filter button opens.\n *\n * Renders the control that fits the column: a checkbox list for an enum, yes,\n * no or any for a boolean, from and to for a number or a date, a searchable\n * list of referenced objects for a reference, and an equals box for text.\n * Real inputs throughout, so every control works from the keyboard. Escape\n * closes it and the host returns focus to the filter button.\n *\n * Internal to CnDataTable. It emits a state object; CnDataTable turns that\n * into query parameters with `columnFilterParams`.\n *\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header\n */\nexport default {\n\tname: 'CnColumnFilterPopover',\n\n\tprops: {\n\t\t/** The filter definition from `columnFilterDef`. */\n\t\tdef: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** The column's current state from `columnFilterState`. */\n\t\tstate: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The translated column label. */\n\t\tlabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The filter button's bounding rect, to place the panel under it. */\n\t\tanchorRect: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Register slug a reference column's objects live in, when the column names none. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Render into `<body>`, so a scrolling table cannot clip the panel. */\n\t\tteleport: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\t},\n\n\temits: ['apply', 'close'],\n\n\tdata() {\n\t\tpopoverSeq += 1\n\t\tconst s = this.state || {}\n\t\treturn {\n\t\t\theadingId: 'cn-column-filter-' + popoverSeq,\n\t\t\tdraft: {\n\t\t\t\tvalues: Array.isArray(s.values) ? [...s.values] : [],\n\t\t\t\tvalue: s.value !== undefined && s.value !== null ? String(s.value) : '',\n\t\t\t\tfrom: s.from || '',\n\t\t\t\tto: s.to || '',\n\t\t\t},\n\n\t\t\tquery: '',\n\t\t\treferenceOptions: [],\n\t\t\treferenceLabels: {},\n\t\t\treferenceLoading: false,\n\t\t\treferenceError: '',\n\t\t\tsearchTimer: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\theadingText() {\n\t\t\treturn t('nextcloud-vue', 'Filter {column}', { column: this.label })\n\t\t},\n\n\t\t/**\n\t\t * The options a list filter shows. For a reference, the search results\n\t\t * plus any picked object that is not among them, so a selection never\n\t\t * disappears from view.\n\t\t *\n\t\t * @return {Array<{value: string, label: string}>}\n\t\t */\n\t\tlistOptions() {\n\t\t\tif (this.def.kind !== 'reference') {\n\t\t\t\treturn this.def.options || []\n\t\t\t}\n\t\t\tconst shown = new Set(this.referenceOptions.map((o) => o.value))\n\t\t\tconst picked = this.draft.values\n\t\t\t\t.filter((v) => !shown.has(v))\n\t\t\t\t.map((v) => ({ value: v, label: this.referenceLabels[v] || v }))\n\t\t\treturn [...picked, ...this.referenceOptions]\n\t\t},\n\n\t\tbooleanOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: '', label: t('nextcloud-vue', 'Any') },\n\t\t\t\t{ value: 'true', label: t('nextcloud-vue', 'Yes') },\n\t\t\t\t{ value: 'false', label: t('nextcloud-vue', 'No') },\n\t\t\t]\n\t\t},\n\n\t\tpanelStyle() {\n\t\t\tif (!this.teleport || !this.anchorRect) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst width = 280\n\t\t\tconst viewport = typeof window !== 'undefined' ? window.innerWidth : 1024\n\t\t\tconst left = Math.max(8, Math.min(this.anchorRect.left, viewport - width - 8))\n\t\t\treturn { position: 'fixed', top: `${this.anchorRect.bottom + 4}px`, left: `${left}px`, width: `${width}px` }\n\t\t},\n\n\t\tsearchLabel() {\n\t\t\treturn t('nextcloud-vue', 'Search')\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading…')\n\t\t},\n\n\t\tnoOptionsLabel() {\n\t\t\treturn t('nextcloud-vue', 'Nothing to choose from here.')\n\t\t},\n\n\t\tfromLabel() {\n\t\t\treturn t('nextcloud-vue', 'From')\n\t\t},\n\n\t\ttoLabel() {\n\t\t\treturn t('nextcloud-vue', 'To')\n\t\t},\n\n\t\tequalsLabel() {\n\t\t\treturn t('nextcloud-vue', 'Equals')\n\t\t},\n\n\t\tclearLabel() {\n\t\t\treturn t('nextcloud-vue', 'Clear')\n\t\t},\n\n\t\tapplyLabel() {\n\t\t\treturn t('nextcloud-vue', 'Apply')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tdocument.addEventListener('mousedown', this.onOutsideMouseDown, true)\n\t\tif (this.def.kind === 'reference') {\n\t\t\tthis.loadReferenceOptions('')\n\t\t}\n\t\tthis.$nextTick(() => {\n\t\t\tconst first = this.$refs.panel && this.$refs.panel.querySelector('input, button')\n\t\t\tif (first && typeof first.focus === 'function') {\n\t\t\t\tfirst.focus()\n\t\t\t}\n\t\t})\n\t},\n\n\tbeforeUnmount() {\n\t\tdocument.removeEventListener('mousedown', this.onOutsideMouseDown, true)\n\t\tclearTimeout(this.searchTimer)\n\t},\n\n\tmethods: {\n\t\ttoggleValue(value) {\n\t\t\tthis.draft.values = this.draft.values.includes(value)\n\t\t\t\t? this.draft.values.filter((v) => v !== value)\n\t\t\t\t: [...this.draft.values, value]\n\t\t},\n\n\t\t/**\n\t\t * The state this panel applies, in the shape `columnFilterParams` reads.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tdraftState() {\n\t\t\tif (this.def.kind === 'enum' || this.def.kind === 'reference') {\n\t\t\t\treturn { values: [...this.draft.values] }\n\t\t\t}\n\t\t\tif (this.def.kind === 'number' || this.def.kind === 'date') {\n\t\t\t\treturn { from: this.draft.from, to: this.draft.to }\n\t\t\t}\n\t\t\treturn { value: this.draft.value }\n\t\t},\n\n\t\tapply() {\n\t\t\t/**\n\t\t\t * @event apply The person applied the filter.\n\t\t\t * @type {object} The column filter state.\n\t\t\t */\n\t\t\tthis.$emit('apply', this.draftState())\n\t\t},\n\n\t\tclear() {\n\t\t\tthis.$emit('apply', this.def.kind === 'enum' || this.def.kind === 'reference'\n\t\t\t\t? { values: [] }\n\t\t\t\t: (this.def.kind === 'number' || this.def.kind === 'date') ? { from: '', to: '' } : { value: '' })\n\t\t},\n\n\t\tclose() {\n\t\t\t/**\n\t\t\t * @event close The panel should close without applying.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\tonEnter(event) {\n\t\t\t// Enter in a text, number or date box applies, like a form submit.\n\t\t\t// On a checkbox or a button it keeps its own meaning.\n\t\t\tconst target = event && event.target\n\t\t\tif (target && target.tagName === 'INPUT' && ['text', 'number', 'date', 'search'].includes(target.type)) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.apply()\n\t\t\t}\n\t\t},\n\n\t\tonOutsideMouseDown(event) {\n\t\t\tconst panel = this.$refs.panel\n\t\t\tif (panel && event && event.target && !panel.contains(event.target)) {\n\t\t\t\tthis.close()\n\t\t\t}\n\t\t},\n\n\t\tonSearchInput() {\n\t\t\tclearTimeout(this.searchTimer)\n\t\t\tthis.searchTimer = setTimeout(() => this.loadReferenceOptions(this.query), 250)\n\t\t},\n\n\t\t/**\n\t\t * Search the referenced objects. The value is the object's uuid,\n\t\t * because that is what the server filters a reference on.\n\t\t *\n\t\t * @param {string} search The search term.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadReferenceOptions(search) {\n\t\t\tconst ref = this.def.reference || {}\n\t\t\tconst register = ref.register || this.register\n\t\t\tif (!register || !ref.schema) {\n\t\t\t\tthis.referenceError = t('nextcloud-vue', 'This column cannot list its objects.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.referenceLoading = true\n\t\t\tthis.referenceError = ''\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', {\n\t\t\t\t\tregister,\n\t\t\t\t\tschema: String(ref.schema),\n\t\t\t\t})\n\t\t\t\tconst params = { _limit: 50 }\n\t\t\t\tif (search) {\n\t\t\t\t\tparams._search = search\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(url, { params })\n\t\t\t\tconst rows = (response && response.data && (response.data.results || response.data)) || []\n\t\t\t\tconst list = Array.isArray(rows) ? rows : []\n\t\t\t\tconst options = list\n\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\tconst self = (row && row['@self']) || {}\n\t\t\t\t\t\tconst value = String(self.id || self.uuid || row.id || row.uuid || '')\n\t\t\t\t\t\tconst label = String((ref.labelField && row[ref.labelField]) || row.name || row.title || self.name || value)\n\t\t\t\t\t\treturn { value, label }\n\t\t\t\t\t})\n\t\t\t\t\t.filter((o) => o.value !== '')\n\t\t\t\tthis.referenceOptions = options\n\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...Object.fromEntries(options.map((o) => [o.value, o.label])) }\n\t\t\t} catch {\n\t\t\t\tthis.referenceOptions = []\n\t\t\t\tthis.referenceError = t('nextcloud-vue', 'The list could not load.')\n\t\t\t} finally {\n\t\t\t\tthis.referenceLoading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-column-filter {\n\tz-index: 10000;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tbox-sizing: border-box;\n\tmax-height: 60vh;\n\toverflow-y: auto;\n\tpadding: 12px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 2px 8px var(--color-box-shadow);\n\tfont-weight: normal;\n\ttext-align: start;\n}\n\n.cn-column-filter__heading {\n\tmargin: 0;\n\tfont-weight: bold;\n}\n\n.cn-column-filter__options {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: none;\n}\n\n.cn-column-filter__option {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmin-height: 32px;\n\tcursor: pointer;\n}\n\n.cn-column-filter__range {\n\tdisplay: flex;\n\tgap: 8px;\n}\n\n.cn-column-filter__field {\n\tdisplay: flex;\n\tflex: 1 1 0;\n\tflex-direction: column;\n\tgap: 4px;\n\tmin-width: 0;\n}\n\n.cn-column-filter__label {\n\tfont-size: var(--font-size-small, 13px);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-column-filter__input {\n\twidth: 100%;\n\tbox-sizing: border-box;\n\tmargin: 0;\n}\n\n.cn-column-filter__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-column-filter__actions {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tgap: 8px;\n}\n\n.cn-column-filter__button {\n\tmin-height: 34px;\n\tpadding: 0 12px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-element, 8px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tcursor: pointer;\n}\n\n.cn-column-filter__button--primary {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-column-filter__button:focus-visible,\n.cn-column-filter__input:focus-visible,\n.cn-column-filter__option input:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-column-filter__sr-only {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["t"],"mappings":";;AA8HA,IAAI,UAAS,GAAI,EAAA;;AAEjB;AACC;AACA;AACA;;;AAGA;;AAEA;AACA;AACA;AACA;AACA;;AAED,aAAe;CACd,IAAI,EAAE,uBAAuB;;AAE7B,CAAA,KAAK,EAAE;AACN;EACA,GAAG,EAAE;GACJ,IAAI,EAAE,MAAM;GACZ,QAAQ,EAAE,IAAI;GACd;;;AAGD,EAAA,KAAK,EAAE;GACN,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,OAAO,EAAE,CAAC;GACnB;;;AAGD,EAAA,KAAK,EAAE;GACN,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,EAAE;GACX;;;EAGD,UAAU,EAAE;GACX,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,IAAI;GACb;;AAED;AACA,EAAA,QAAQ,EAAE;GACT,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,EAAE;GACX;;;AAGD,EAAA,QAAQ,EAAE;GACT,IAAI,EAAE,OAAO;GACb,OAAO,EAAE,IAAI;GACb;EACD;;CAED,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;;AAEzB,CAAA,IAAI,GAAG;EACN,UAAW,IAAG,EAAA;AACd,EAAA,MAAM,CAAE,GAAE,IAAI,CAAC,KAAM,IAAG,GAAC;AACzB,EAAA,OAAO;AACN,GAAA,SAAS,EAAE,mBAAoB,GAAE,UAAU;AAC3C,GAAA,KAAK,EAAE;IACN,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAA,GAAI,EAAE;IACpD,KAAK,EAAE,CAAC,CAAC,KAAM,KAAI,SAAU,IAAG,CAAC,CAAC,KAAM,KAAI,IAAG,GAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAA,GAAI,EAAE;AACvE,IAAA,IAAI,EAAE,CAAC,CAAC,IAAG,IAAK,EAAE;IAClB,EAAE,EAAE,CAAC,CAAC,EAAG,IAAG,EAAE;IACd;;AAED,GAAA,KAAK,EAAE,EAAE;AACT,GAAA,gBAAgB,EAAE,EAAE;AACpB,GAAA,eAAe,EAAE,EAAE;GACnB,gBAAgB,EAAE,KAAK;AACvB,GAAA,cAAc,EAAE,EAAE;AAClB,GAAA,WAAW,EAAE,IAAI;AAClB,GAAA;EACA;;AAED,CAAA,QAAQ,EAAE;EACT,WAAW,GAAG;GACb,OAAOA,SAAC,CAAC,eAAe,EAAE,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,KAAI,EAAG,CAAA;GACnE;;AAED;;;AAGC;AACA;AACA;;EAED,WAAW,GAAG;AACb,GAAA,IAAI,IAAI,CAAC,GAAG,CAAC,IAAK,KAAI,WAAW,EAAE;IAClC,OAAO,IAAI,CAAC,GAAG,CAAC,OAAM,IAAK,EAAC;AAC7B,IAAA;GACA,MAAM,KAAM,GAAE,IAAI,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,EAAA;GAC/D,MAAM,MAAO,GAAE,IAAI,CAAC,KAAK,CAAC,MAAK;KAC7B,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;KAC3B,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,KAAK,CAAE,EAAC,CAAC,EAAA;AAChE,GAAA,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAA;GAC3C;;AAED,EAAA,cAAc,GAAG;AAChB,GAAA,OAAO;AACN,IAAA,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,KAAK,GAAG;AAC/C,IAAA,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,KAAK,GAAG;AACnD,IAAA,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,IAAI,GAAG;AACpD,IAAA;GACA;;EAED,UAAU,GAAG;AACZ,GAAA,IAAI,CAAC,IAAI,CAAC,QAAS,IAAG,CAAC,IAAI,CAAC,UAAU,EAAE;AACvC,IAAA,OAAO,EAAC;AACT,IAAA;GACA,MAAM,KAAI,GAAI,IAAE;AAChB,GAAA,MAAM,QAAO,GAAI,OAAO,MAAO,KAAI,WAAY,GAAE,MAAM,CAAC,UAAW,GAAE,KAAG;AACxE,GAAA,MAAM,IAAG,GAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,QAAS,GAAE,KAAM,GAAE,CAAC,CAAC,EAAA;GAC7E,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,MAAK,GAAI,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAE,EAAA;GAC3G;;EAED,WAAW,GAAG;AACb,GAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,QAAQ,CAAA;GAClC;;AAED,EAAA,YAAY,GAAG;AACd,GAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,UAAU,CAAA;GACpC;;AAED,EAAA,cAAc,GAAG;GAChB,OAAOA,SAAC,CAAC,eAAe,EAAE,8BAA8B,CAAA;GACxD;;EAED,SAAS,GAAG;AACX,GAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,MAAM,CAAA;GAChC;;AAED,EAAA,OAAO,GAAG;GACT,OAAOA,SAAC,CAAC,eAAe,EAAE,IAAI,CAAA;GAC9B;;EAED,WAAW,GAAG;AACb,GAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,QAAQ,CAAA;GAClC;;EAED,UAAU,GAAG;AACZ,GAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,OAAO,CAAA;GACjC;;EAED,UAAU,GAAG;AACZ,GAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,OAAO,CAAA;GACjC;EACD;;AAED,CAAA,OAAO,GAAG;AACT,EAAA,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,EAAA;AACpE,EAAA,IAAI,IAAI,CAAC,GAAG,CAAC,IAAK,KAAI,WAAW,EAAE;GAClC,IAAI,CAAC,oBAAoB,CAAC,EAAE,EAAA;AAC7B,GAAA;AACA,EAAA,IAAI,CAAC,SAAS,CAAC,MAAM;AACpB,GAAA,MAAM,KAAI,GAAI,IAAI,CAAC,KAAK,CAAC,KAAI,IAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,eAAe,EAAA;AAChF,GAAA,IAAI,KAAM,IAAG,OAAO,KAAK,CAAC,KAAM,KAAI,UAAU,EAAE;IAC/C,KAAK,CAAC,KAAK,GAAC;AACb,IAAA;GACA,EAAA;EACD;;AAED,CAAA,aAAa,GAAG;AACf,EAAA,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,EAAA;EACvE,YAAY,CAAC,IAAI,CAAC,WAAW,EAAA;EAC7B;;AAED,CAAA,OAAO,EAAE;AACR,EAAA,WAAW,CAAC,KAAK,EAAE;GAClB,IAAI,CAAC,KAAK,CAAC,MAAO,GAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAA;AACnD,MAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAA,KAAM,KAAK,CAAA;MAC3C,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAA;GAC/B;;AAED;;AAEC;AACA;;EAED,UAAU,GAAG;GACZ,IAAI,IAAI,CAAC,GAAG,CAAC,IAAG,KAAM,MAAK,IAAK,IAAI,CAAC,GAAG,CAAC,IAAK,KAAI,WAAW,EAAE;AAC9D,IAAA,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAE,EAAA;AACzC,IAAA;GACA,IAAI,IAAI,CAAC,GAAG,CAAC,IAAK,KAAI,YAAY,IAAI,CAAC,GAAG,CAAC,IAAG,KAAM,MAAM,EAAE;AAC3D,IAAA,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,EAAG,EAAA;AACnD,IAAA;GACA,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAM,EAAA;GACjC;;AAED,EAAA,KAAK,GAAG;AACP;AACC;AACA;;AAED,GAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,EAAA;GACrC;;AAED,EAAA,KAAK,GAAG;AACP,GAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,UAAU,IAAI,CAAC,GAAG,CAAC,IAAG,KAAM,WAAU;MACzE,EAAE,MAAM,EAAE,EAAG,EAAA;AACf,MAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAK,KAAI,QAAO,IAAK,IAAI,CAAC,GAAG,CAAC,IAAG,KAAM,MAAM,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAC,KAAM,EAAE,KAAK,EAAE,IAAI,EAAA;GAClG;;AAED,EAAA,KAAK,GAAG;AACP;;;AAGA,GAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAA;GAClB;;AAED,EAAA,OAAO,CAAC,KAAK,EAAE;;AAEd;AACA,GAAA,MAAM,MAAK,GAAI,KAAI,IAAK,KAAK,CAAC,OAAK;AACnC,GAAA,IAAI,MAAO,IAAG,MAAM,CAAC,OAAM,KAAM,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;AACvG,IAAA,KAAK,CAAC,cAAc,GAAC;IACrB,IAAI,CAAC,KAAK,GAAC;AACZ,IAAA;GACA;;EAED,kBAAkB,CAAC,KAAK,EAAE;GACzB,MAAM,KAAM,GAAE,IAAI,CAAC,KAAK,CAAC,MAAI;GAC7B,IAAI,KAAI,IAAK,KAAM,IAAG,KAAK,CAAC,MAAO,IAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE;IACpE,IAAI,CAAC,KAAK,GAAC;AACZ,IAAA;GACA;;AAED,EAAA,aAAa,GAAG;GACf,YAAY,CAAC,IAAI,CAAC,WAAW,EAAA;AAC7B,GAAA,IAAI,CAAC,WAAY,GAAE,UAAU,CAAC,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAA;GAC9E;;AAED;;;AAGC;AACA;;;AAGD,EAAA,MAAM,oBAAoB,CAAC,MAAM,EAAE;AAClC,GAAA,MAAM,GAAI,GAAE,IAAI,CAAC,GAAG,CAAC,SAAQ,IAAK,GAAC;AACnC,GAAA,MAAM,QAAS,GAAE,GAAG,CAAC,QAAS,IAAG,IAAI,CAAC,SAAO;GAC7C,IAAI,CAAC,QAAS,IAAG,CAAC,GAAG,CAAC,MAAM,EAAE;AAC7B,IAAA,IAAI,CAAC,cAAa,GAAIA,SAAC,CAAC,eAAe,EAAE,sCAAsC,EAAA;IAC/E,MAAK;AACN,IAAA;GACA,IAAI,CAAC,gBAAiB,GAAE,KAAG;AAC3B,GAAA,IAAI,CAAC,cAAa,GAAI,GAAC;AACvB,GAAA,IAAI;IACH,MAAM,CAAC,EAAE,OAAO,EAAE,KAAI,EAAG,EAAE,EAAE,WAAU,EAAG,CAAE,GAAE,MAAM,OAAO,CAAC,GAAG,CAAC;KAC/D,OAAO,kBAAkB,CAAC;KAC1B,OAAO,mBAAmB,CAAC;KAC3B,EAAA;AACD,IAAA,MAAM,GAAE,GAAI,WAAW,CAAC,oDAAoD,EAAE;AAC7E,KAAA,QAAQ;KACR,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;KAC1B,EAAA;IACD,MAAM,MAAK,GAAI,EAAE,MAAM,EAAE,EAAG,GAAA;IAC5B,IAAI,MAAM,EAAE;AACX,KAAA,MAAM,CAAC,OAAQ,GAAE,OAAK;AACvB,KAAA;AACA,IAAA,MAAM,QAAS,GAAE,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAA;AAChD,IAAA,MAAM,IAAG,GAAI,CAAC,QAAO,IAAK,QAAQ,CAAC,IAAG,KAAM,QAAQ,CAAC,IAAI,CAAC,OAAQ,IAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAC;AACzF,IAAA,MAAM,IAAK,GAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAA,GAAI,IAAG,GAAI,GAAC;AAC3C,IAAA,MAAM,OAAM,GAAI,IAAG;MACjB,GAAG,CAAC,CAAC,GAAG,KAAK;AACb,MAAA,MAAM,IAAG,GAAI,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,GAAC;MACvC,MAAM,KAAI,GAAI,MAAM,CAAC,IAAI,CAAC,EAAC,IAAK,IAAI,CAAC,IAAG,IAAK,GAAG,CAAC,EAAC,IAAK,GAAG,CAAC,IAAK,IAAG,EAAE,EAAA;MACrE,MAAM,KAAM,GAAE,MAAM,CAAC,CAAC,GAAG,CAAC,cAAc,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC,IAAG,IAAK,GAAG,CAAC,KAAM,IAAG,IAAI,CAAC,QAAQ,KAAK,EAAA;AAC3G,MAAA,OAAO,EAAE,KAAK,EAAE,KAAM,EAAA;MACtB,CAAA;MACA,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAA;IAC9B,IAAI,CAAC,gBAAe,GAAI,QAAM;IAC9B,IAAI,CAAC,eAAgB,GAAE,EAAE,GAAG,IAAI,CAAC,eAAe,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,GAAA;AACjH,IAAA,CAAE,MAAM;IACP,IAAI,CAAC,gBAAiB,GAAE,GAAC;IACzB,IAAI,CAAC,cAAe,GAAEA,SAAC,CAAC,eAAe,EAAE,0BAA0B,EAAA;AACpE,IAAA,SAAU;IACT,IAAI,CAAC,mBAAmB,MAAI;AAC7B,IAAA;GACA;EACD;AACF;;;;"}
1
+ {"version":3,"file":"CnColumnFilterPopover.vue2.js","sources":["../../../../src/components/CnDataTable/CnColumnFilterPopover.vue"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<Teleport to=\"body\" :disabled=\"!teleport\">\n\t\t<div\n\t\t\tref=\"panel\"\n\t\t\tclass=\"cn-column-filter\"\n\t\t\trole=\"dialog\"\n\t\t\t:aria-labelledby=\"headingId\"\n\t\t\t:style=\"panelStyle\"\n\t\t\tdata-testid=\"cn-column-filter\"\n\t\t\t@keydown.esc.stop.prevent=\"close\"\n\t\t\t@keydown.enter=\"onEnter\">\n\t\t\t<p :id=\"headingId\" class=\"cn-column-filter__heading\">\n\t\t\t\t{{ headingText }}\n\t\t\t</p>\n\n\t\t\t<!-- enum and reference: a checkbox list of real inputs -->\n\t\t\t<template v-if=\"def.kind === 'enum' || def.kind === 'reference'\">\n\t\t\t\t<label v-if=\"def.kind === 'reference'\" class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ searchLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"query\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-search\"\n\t\t\t\t\t\t@input=\"onSearchInput\">\n\t\t\t\t</label>\n\t\t\t\t<p v-if=\"def.kind === 'reference' && referenceLoading\" class=\"cn-column-filter__hint\" role=\"status\">\n\t\t\t\t\t{{ loadingLabel }}\n\t\t\t\t</p>\n\t\t\t\t<p v-else-if=\"def.kind === 'reference' && referenceError\" class=\"cn-column-filter__hint\" role=\"status\">\n\t\t\t\t\t{{ referenceError }}\n\t\t\t\t</p>\n\t\t\t\t<fieldset class=\"cn-column-filter__options\">\n\t\t\t\t\t<legend class=\"cn-column-filter__sr-only\">\n\t\t\t\t\t\t{{ headingText }}\n\t\t\t\t\t</legend>\n\t\t\t\t\t<label\n\t\t\t\t\t\tv-for=\"option in listOptions\"\n\t\t\t\t\t\t:key=\"option.value\"\n\t\t\t\t\t\tclass=\"cn-column-filter__option\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t:value=\"option.value\"\n\t\t\t\t\t\t\t:checked=\"draft.values.includes(option.value)\"\n\t\t\t\t\t\t\tdata-testid=\"cn-column-filter-option\"\n\t\t\t\t\t\t\t@change=\"toggleValue(option.value)\">\n\t\t\t\t\t\t<span>{{ option.label }}</span>\n\t\t\t\t\t</label>\n\t\t\t\t\t<p v-if=\"listOptions.length === 0 && !referenceLoading\" class=\"cn-column-filter__hint\">\n\t\t\t\t\t\t{{ noOptionsLabel }}\n\t\t\t\t\t</p>\n\t\t\t\t</fieldset>\n\t\t\t</template>\n\n\t\t\t<!-- boolean: yes, no, any -->\n\t\t\t<fieldset v-else-if=\"def.kind === 'boolean'\" class=\"cn-column-filter__options\">\n\t\t\t\t<legend class=\"cn-column-filter__sr-only\">\n\t\t\t\t\t{{ headingText }}\n\t\t\t\t</legend>\n\t\t\t\t<label v-for=\"option in booleanOptions\" :key=\"option.value\" class=\"cn-column-filter__option\">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"radio\"\n\t\t\t\t\t\t:name=\"headingId + '-bool'\"\n\t\t\t\t\t\t:value=\"option.value\"\n\t\t\t\t\t\t:checked=\"draft.value === option.value\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-option\"\n\t\t\t\t\t\t@change=\"draft.value = option.value\">\n\t\t\t\t\t<span>{{ option.label }}</span>\n\t\t\t\t</label>\n\t\t\t</fieldset>\n\n\t\t\t<!-- number and date: from and to -->\n\t\t\t<div v-else-if=\"def.kind === 'number' || def.kind === 'date'\" class=\"cn-column-filter__range\">\n\t\t\t\t<label class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ fromLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"draft.from\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\t:type=\"def.kind === 'date' ? 'date' : 'number'\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-from\">\n\t\t\t\t</label>\n\t\t\t\t<label class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ toLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"draft.to\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\t:type=\"def.kind === 'date' ? 'date' : 'number'\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-to\">\n\t\t\t\t</label>\n\t\t\t</div>\n\n\t\t\t<!-- string: contains (OpenRegister `[like]`, case-insensitive) -->\n\t\t\t<label v-else class=\"cn-column-filter__field\">\n\t\t\t\t<span class=\"cn-column-filter__label\">{{ containsLabel }}</span>\n\t\t\t\t<input\n\t\t\t\t\tv-model=\"draft.value\"\n\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-text\">\n\t\t\t</label>\n\n\t\t\t<div class=\"cn-column-filter__actions\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-column-filter__button\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-clear\"\n\t\t\t\t\t@click=\"clear\">\n\t\t\t\t\t{{ clearLabel }}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-column-filter__button cn-column-filter__button--primary\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-apply\"\n\t\t\t\t\t@click=\"apply\">\n\t\t\t\t\t{{ applyLabel }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</div>\n\t</Teleport>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\n\nlet popoverSeq = 0\n\n/**\n * CnColumnFilterPopover — the small panel a table header's filter button opens.\n *\n * Renders the control that fits the column: a checkbox list for an enum, yes,\n * no or any for a boolean, from and to for a number or a date, a searchable\n * list of referenced objects for a reference, and a contains box for text.\n * Real inputs throughout, so every control works from the keyboard. Escape\n * closes it and the host returns focus to the filter button.\n *\n * Internal to CnDataTable. It emits a state object; CnDataTable turns that\n * into query parameters with `columnFilterParams`.\n *\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header\n */\nexport default {\n\tname: 'CnColumnFilterPopover',\n\n\tprops: {\n\t\t/** The filter definition from `columnFilterDef`. */\n\t\tdef: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** The column's current state from `columnFilterState`. */\n\t\tstate: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The translated column label. */\n\t\tlabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The filter button's bounding rect, to place the panel under it. */\n\t\tanchorRect: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Register slug a reference column's objects live in, when the column names none. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Render into `<body>`, so a scrolling table cannot clip the panel. */\n\t\tteleport: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\t},\n\n\temits: ['apply', 'close'],\n\n\tdata() {\n\t\tpopoverSeq += 1\n\t\tconst s = this.state || {}\n\t\treturn {\n\t\t\theadingId: 'cn-column-filter-' + popoverSeq,\n\t\t\tdraft: {\n\t\t\t\tvalues: Array.isArray(s.values) ? [...s.values] : [],\n\t\t\t\tvalue: s.value !== undefined && s.value !== null ? String(s.value) : '',\n\t\t\t\tfrom: s.from || '',\n\t\t\t\tto: s.to || '',\n\t\t\t},\n\n\t\t\tquery: '',\n\t\t\treferenceOptions: [],\n\t\t\treferenceLabels: {},\n\t\t\treferenceLoading: false,\n\t\t\treferenceError: '',\n\t\t\tsearchTimer: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\theadingText() {\n\t\t\treturn t('nextcloud-vue', 'Filter {column}', { column: this.label })\n\t\t},\n\n\t\t/**\n\t\t * The options a list filter shows. For a reference, the search results\n\t\t * plus any picked object that is not among them, so a selection never\n\t\t * disappears from view.\n\t\t *\n\t\t * @return {Array<{value: string, label: string}>}\n\t\t */\n\t\tlistOptions() {\n\t\t\tif (this.def.kind !== 'reference') {\n\t\t\t\treturn this.def.options || []\n\t\t\t}\n\t\t\tconst shown = new Set(this.referenceOptions.map((o) => o.value))\n\t\t\tconst picked = this.draft.values\n\t\t\t\t.filter((v) => !shown.has(v))\n\t\t\t\t.map((v) => ({ value: v, label: this.referenceLabels[v] || v }))\n\t\t\treturn [...picked, ...this.referenceOptions]\n\t\t},\n\n\t\tbooleanOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: '', label: t('nextcloud-vue', 'Any') },\n\t\t\t\t{ value: 'true', label: t('nextcloud-vue', 'Yes') },\n\t\t\t\t{ value: 'false', label: t('nextcloud-vue', 'No') },\n\t\t\t]\n\t\t},\n\n\t\tpanelStyle() {\n\t\t\tif (!this.teleport || !this.anchorRect) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst width = 280\n\t\t\tconst viewport = typeof window !== 'undefined' ? window.innerWidth : 1024\n\t\t\tconst left = Math.max(8, Math.min(this.anchorRect.left, viewport - width - 8))\n\t\t\treturn { position: 'fixed', top: `${this.anchorRect.bottom + 4}px`, left: `${left}px`, width: `${width}px` }\n\t\t},\n\n\t\tsearchLabel() {\n\t\t\treturn t('nextcloud-vue', 'Search')\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading…')\n\t\t},\n\n\t\tnoOptionsLabel() {\n\t\t\treturn t('nextcloud-vue', 'Nothing to choose from here.')\n\t\t},\n\n\t\tfromLabel() {\n\t\t\treturn t('nextcloud-vue', 'From')\n\t\t},\n\n\t\ttoLabel() {\n\t\t\treturn t('nextcloud-vue', 'To')\n\t\t},\n\n\t\t/**\n\t\t * Label of the text box: the text filter matches on contains.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains\n\t\t */\n\t\tcontainsLabel() {\n\t\t\treturn t('nextcloud-vue', 'Contains')\n\t\t},\n\n\t\tclearLabel() {\n\t\t\treturn t('nextcloud-vue', 'Clear')\n\t\t},\n\n\t\tapplyLabel() {\n\t\t\treturn t('nextcloud-vue', 'Apply')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tdocument.addEventListener('mousedown', this.onOutsideMouseDown, true)\n\t\tif (this.def.kind === 'reference') {\n\t\t\tthis.loadReferenceOptions('')\n\t\t}\n\t\tthis.$nextTick(() => {\n\t\t\tconst first = this.$refs.panel && this.$refs.panel.querySelector('input, button')\n\t\t\tif (first && typeof first.focus === 'function') {\n\t\t\t\tfirst.focus()\n\t\t\t}\n\t\t})\n\t},\n\n\tbeforeUnmount() {\n\t\tdocument.removeEventListener('mousedown', this.onOutsideMouseDown, true)\n\t\tclearTimeout(this.searchTimer)\n\t},\n\n\tmethods: {\n\t\ttoggleValue(value) {\n\t\t\tthis.draft.values = this.draft.values.includes(value)\n\t\t\t\t? this.draft.values.filter((v) => v !== value)\n\t\t\t\t: [...this.draft.values, value]\n\t\t},\n\n\t\t/**\n\t\t * The state this panel applies, in the shape `columnFilterParams` reads.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tdraftState() {\n\t\t\tif (this.def.kind === 'enum' || this.def.kind === 'reference') {\n\t\t\t\treturn { values: [...this.draft.values] }\n\t\t\t}\n\t\t\tif (this.def.kind === 'number' || this.def.kind === 'date') {\n\t\t\t\treturn { from: this.draft.from, to: this.draft.to }\n\t\t\t}\n\t\t\treturn { value: this.draft.value }\n\t\t},\n\n\t\tapply() {\n\t\t\t/**\n\t\t\t * @event apply The person applied the filter.\n\t\t\t * @type {object} The column filter state.\n\t\t\t */\n\t\t\tthis.$emit('apply', this.draftState())\n\t\t},\n\n\t\tclear() {\n\t\t\tthis.$emit('apply', this.def.kind === 'enum' || this.def.kind === 'reference'\n\t\t\t\t? { values: [] }\n\t\t\t\t: (this.def.kind === 'number' || this.def.kind === 'date') ? { from: '', to: '' } : { value: '' })\n\t\t},\n\n\t\tclose() {\n\t\t\t/**\n\t\t\t * @event close The panel should close without applying.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\tonEnter(event) {\n\t\t\t// Enter in a text, number or date box applies, like a form submit.\n\t\t\t// On a checkbox or a button it keeps its own meaning.\n\t\t\tconst target = event && event.target\n\t\t\tif (target && target.tagName === 'INPUT' && ['text', 'number', 'date', 'search'].includes(target.type)) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.apply()\n\t\t\t}\n\t\t},\n\n\t\tonOutsideMouseDown(event) {\n\t\t\tconst panel = this.$refs.panel\n\t\t\tif (panel && event && event.target && !panel.contains(event.target)) {\n\t\t\t\tthis.close()\n\t\t\t}\n\t\t},\n\n\t\tonSearchInput() {\n\t\t\tclearTimeout(this.searchTimer)\n\t\t\tthis.searchTimer = setTimeout(() => this.loadReferenceOptions(this.query), 250)\n\t\t},\n\n\t\t/**\n\t\t * Search the referenced objects. The value is the object's uuid,\n\t\t * because that is what the server filters a reference on.\n\t\t *\n\t\t * @param {string} search The search term.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadReferenceOptions(search) {\n\t\t\tconst ref = this.def.reference || {}\n\t\t\tconst register = ref.register || this.register\n\t\t\tif (!register || !ref.schema) {\n\t\t\t\tthis.referenceError = t('nextcloud-vue', 'This column cannot list its objects.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.referenceLoading = true\n\t\t\tthis.referenceError = ''\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', {\n\t\t\t\t\tregister,\n\t\t\t\t\tschema: String(ref.schema),\n\t\t\t\t})\n\t\t\t\tconst params = { _limit: 50 }\n\t\t\t\tif (search) {\n\t\t\t\t\tparams._search = search\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(url, { params })\n\t\t\t\tconst rows = (response && response.data && (response.data.results || response.data)) || []\n\t\t\t\tconst list = Array.isArray(rows) ? rows : []\n\t\t\t\tconst options = list\n\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\tconst self = (row && row['@self']) || {}\n\t\t\t\t\t\tconst value = String(self.id || self.uuid || row.id || row.uuid || '')\n\t\t\t\t\t\tconst label = String((ref.labelField && row[ref.labelField]) || row.name || row.title || self.name || value)\n\t\t\t\t\t\treturn { value, label }\n\t\t\t\t\t})\n\t\t\t\t\t.filter((o) => o.value !== '')\n\t\t\t\tthis.referenceOptions = options\n\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...Object.fromEntries(options.map((o) => [o.value, o.label])) }\n\t\t\t} catch {\n\t\t\t\tthis.referenceOptions = []\n\t\t\t\tthis.referenceError = t('nextcloud-vue', 'The list could not load.')\n\t\t\t} finally {\n\t\t\t\tthis.referenceLoading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-column-filter {\n\tz-index: 10000;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tbox-sizing: border-box;\n\tmax-height: 60vh;\n\toverflow-y: auto;\n\tpadding: 12px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 2px 8px var(--color-box-shadow);\n\tfont-weight: normal;\n\ttext-align: start;\n}\n\n.cn-column-filter__heading {\n\tmargin: 0;\n\tfont-weight: bold;\n}\n\n.cn-column-filter__options {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: none;\n}\n\n.cn-column-filter__option {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmin-height: 32px;\n\tcursor: pointer;\n}\n\n.cn-column-filter__range {\n\tdisplay: flex;\n\tgap: 8px;\n}\n\n.cn-column-filter__field {\n\tdisplay: flex;\n\tflex: 1 1 0;\n\tflex-direction: column;\n\tgap: 4px;\n\tmin-width: 0;\n}\n\n.cn-column-filter__label {\n\tfont-size: var(--font-size-small, 13px);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-column-filter__input {\n\twidth: 100%;\n\tbox-sizing: border-box;\n\tmargin: 0;\n}\n\n.cn-column-filter__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-column-filter__actions {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tgap: 8px;\n}\n\n.cn-column-filter__button {\n\tmin-height: 34px;\n\tpadding: 0 12px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-element, 8px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tcursor: pointer;\n}\n\n.cn-column-filter__button--primary {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-column-filter__button:focus-visible,\n.cn-column-filter__input:focus-visible,\n.cn-column-filter__option input:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-column-filter__sr-only {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
@@ -157,7 +157,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
157
157
  ])
158
158
  ]))
159
159
  : (openBlock(), createElementBlock("label", _hoisted_21, [
160
- createElementVNode("span", _hoisted_22, toDisplayString($options.equalsLabel), 1 /* TEXT */),
160
+ createElementVNode("span", _hoisted_22, toDisplayString($options.containsLabel), 1 /* TEXT */),
161
161
  withDirectives(createElementVNode("input", {
162
162
  "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => (($data.draft.value) = $event)),
163
163
  class: "cn-column-filter__input",
@@ -1 +1 @@
1
- {"version":3,"file":"CnColumnFilterPopover.vue3.js","sources":["../../../../src/components/CnDataTable/CnColumnFilterPopover.vue?vue&type=template&id=2cb54356&lang.js"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<Teleport to=\"body\" :disabled=\"!teleport\">\n\t\t<div\n\t\t\tref=\"panel\"\n\t\t\tclass=\"cn-column-filter\"\n\t\t\trole=\"dialog\"\n\t\t\t:aria-labelledby=\"headingId\"\n\t\t\t:style=\"panelStyle\"\n\t\t\tdata-testid=\"cn-column-filter\"\n\t\t\t@keydown.esc.stop.prevent=\"close\"\n\t\t\t@keydown.enter=\"onEnter\">\n\t\t\t<p :id=\"headingId\" class=\"cn-column-filter__heading\">\n\t\t\t\t{{ headingText }}\n\t\t\t</p>\n\n\t\t\t<!-- enum and reference: a checkbox list of real inputs -->\n\t\t\t<template v-if=\"def.kind === 'enum' || def.kind === 'reference'\">\n\t\t\t\t<label v-if=\"def.kind === 'reference'\" class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ searchLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"query\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-search\"\n\t\t\t\t\t\t@input=\"onSearchInput\">\n\t\t\t\t</label>\n\t\t\t\t<p v-if=\"def.kind === 'reference' && referenceLoading\" class=\"cn-column-filter__hint\" role=\"status\">\n\t\t\t\t\t{{ loadingLabel }}\n\t\t\t\t</p>\n\t\t\t\t<p v-else-if=\"def.kind === 'reference' && referenceError\" class=\"cn-column-filter__hint\" role=\"status\">\n\t\t\t\t\t{{ referenceError }}\n\t\t\t\t</p>\n\t\t\t\t<fieldset class=\"cn-column-filter__options\">\n\t\t\t\t\t<legend class=\"cn-column-filter__sr-only\">\n\t\t\t\t\t\t{{ headingText }}\n\t\t\t\t\t</legend>\n\t\t\t\t\t<label\n\t\t\t\t\t\tv-for=\"option in listOptions\"\n\t\t\t\t\t\t:key=\"option.value\"\n\t\t\t\t\t\tclass=\"cn-column-filter__option\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t:value=\"option.value\"\n\t\t\t\t\t\t\t:checked=\"draft.values.includes(option.value)\"\n\t\t\t\t\t\t\tdata-testid=\"cn-column-filter-option\"\n\t\t\t\t\t\t\t@change=\"toggleValue(option.value)\">\n\t\t\t\t\t\t<span>{{ option.label }}</span>\n\t\t\t\t\t</label>\n\t\t\t\t\t<p v-if=\"listOptions.length === 0 && !referenceLoading\" class=\"cn-column-filter__hint\">\n\t\t\t\t\t\t{{ noOptionsLabel }}\n\t\t\t\t\t</p>\n\t\t\t\t</fieldset>\n\t\t\t</template>\n\n\t\t\t<!-- boolean: yes, no, any -->\n\t\t\t<fieldset v-else-if=\"def.kind === 'boolean'\" class=\"cn-column-filter__options\">\n\t\t\t\t<legend class=\"cn-column-filter__sr-only\">\n\t\t\t\t\t{{ headingText }}\n\t\t\t\t</legend>\n\t\t\t\t<label v-for=\"option in booleanOptions\" :key=\"option.value\" class=\"cn-column-filter__option\">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"radio\"\n\t\t\t\t\t\t:name=\"headingId + '-bool'\"\n\t\t\t\t\t\t:value=\"option.value\"\n\t\t\t\t\t\t:checked=\"draft.value === option.value\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-option\"\n\t\t\t\t\t\t@change=\"draft.value = option.value\">\n\t\t\t\t\t<span>{{ option.label }}</span>\n\t\t\t\t</label>\n\t\t\t</fieldset>\n\n\t\t\t<!-- number and date: from and to -->\n\t\t\t<div v-else-if=\"def.kind === 'number' || def.kind === 'date'\" class=\"cn-column-filter__range\">\n\t\t\t\t<label class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ fromLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"draft.from\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\t:type=\"def.kind === 'date' ? 'date' : 'number'\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-from\">\n\t\t\t\t</label>\n\t\t\t\t<label class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ toLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"draft.to\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\t:type=\"def.kind === 'date' ? 'date' : 'number'\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-to\">\n\t\t\t\t</label>\n\t\t\t</div>\n\n\t\t\t<!-- string: equals -->\n\t\t\t<label v-else class=\"cn-column-filter__field\">\n\t\t\t\t<span class=\"cn-column-filter__label\">{{ equalsLabel }}</span>\n\t\t\t\t<input\n\t\t\t\t\tv-model=\"draft.value\"\n\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-text\">\n\t\t\t</label>\n\n\t\t\t<div class=\"cn-column-filter__actions\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-column-filter__button\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-clear\"\n\t\t\t\t\t@click=\"clear\">\n\t\t\t\t\t{{ clearLabel }}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-column-filter__button cn-column-filter__button--primary\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-apply\"\n\t\t\t\t\t@click=\"apply\">\n\t\t\t\t\t{{ applyLabel }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</div>\n\t</Teleport>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\n\nlet popoverSeq = 0\n\n/**\n * CnColumnFilterPopover — the small panel a table header's filter button opens.\n *\n * Renders the control that fits the column: a checkbox list for an enum, yes,\n * no or any for a boolean, from and to for a number or a date, a searchable\n * list of referenced objects for a reference, and an equals box for text.\n * Real inputs throughout, so every control works from the keyboard. Escape\n * closes it and the host returns focus to the filter button.\n *\n * Internal to CnDataTable. It emits a state object; CnDataTable turns that\n * into query parameters with `columnFilterParams`.\n *\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header\n */\nexport default {\n\tname: 'CnColumnFilterPopover',\n\n\tprops: {\n\t\t/** The filter definition from `columnFilterDef`. */\n\t\tdef: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** The column's current state from `columnFilterState`. */\n\t\tstate: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The translated column label. */\n\t\tlabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The filter button's bounding rect, to place the panel under it. */\n\t\tanchorRect: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Register slug a reference column's objects live in, when the column names none. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Render into `<body>`, so a scrolling table cannot clip the panel. */\n\t\tteleport: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\t},\n\n\temits: ['apply', 'close'],\n\n\tdata() {\n\t\tpopoverSeq += 1\n\t\tconst s = this.state || {}\n\t\treturn {\n\t\t\theadingId: 'cn-column-filter-' + popoverSeq,\n\t\t\tdraft: {\n\t\t\t\tvalues: Array.isArray(s.values) ? [...s.values] : [],\n\t\t\t\tvalue: s.value !== undefined && s.value !== null ? String(s.value) : '',\n\t\t\t\tfrom: s.from || '',\n\t\t\t\tto: s.to || '',\n\t\t\t},\n\n\t\t\tquery: '',\n\t\t\treferenceOptions: [],\n\t\t\treferenceLabels: {},\n\t\t\treferenceLoading: false,\n\t\t\treferenceError: '',\n\t\t\tsearchTimer: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\theadingText() {\n\t\t\treturn t('nextcloud-vue', 'Filter {column}', { column: this.label })\n\t\t},\n\n\t\t/**\n\t\t * The options a list filter shows. For a reference, the search results\n\t\t * plus any picked object that is not among them, so a selection never\n\t\t * disappears from view.\n\t\t *\n\t\t * @return {Array<{value: string, label: string}>}\n\t\t */\n\t\tlistOptions() {\n\t\t\tif (this.def.kind !== 'reference') {\n\t\t\t\treturn this.def.options || []\n\t\t\t}\n\t\t\tconst shown = new Set(this.referenceOptions.map((o) => o.value))\n\t\t\tconst picked = this.draft.values\n\t\t\t\t.filter((v) => !shown.has(v))\n\t\t\t\t.map((v) => ({ value: v, label: this.referenceLabels[v] || v }))\n\t\t\treturn [...picked, ...this.referenceOptions]\n\t\t},\n\n\t\tbooleanOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: '', label: t('nextcloud-vue', 'Any') },\n\t\t\t\t{ value: 'true', label: t('nextcloud-vue', 'Yes') },\n\t\t\t\t{ value: 'false', label: t('nextcloud-vue', 'No') },\n\t\t\t]\n\t\t},\n\n\t\tpanelStyle() {\n\t\t\tif (!this.teleport || !this.anchorRect) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst width = 280\n\t\t\tconst viewport = typeof window !== 'undefined' ? window.innerWidth : 1024\n\t\t\tconst left = Math.max(8, Math.min(this.anchorRect.left, viewport - width - 8))\n\t\t\treturn { position: 'fixed', top: `${this.anchorRect.bottom + 4}px`, left: `${left}px`, width: `${width}px` }\n\t\t},\n\n\t\tsearchLabel() {\n\t\t\treturn t('nextcloud-vue', 'Search')\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading…')\n\t\t},\n\n\t\tnoOptionsLabel() {\n\t\t\treturn t('nextcloud-vue', 'Nothing to choose from here.')\n\t\t},\n\n\t\tfromLabel() {\n\t\t\treturn t('nextcloud-vue', 'From')\n\t\t},\n\n\t\ttoLabel() {\n\t\t\treturn t('nextcloud-vue', 'To')\n\t\t},\n\n\t\tequalsLabel() {\n\t\t\treturn t('nextcloud-vue', 'Equals')\n\t\t},\n\n\t\tclearLabel() {\n\t\t\treturn t('nextcloud-vue', 'Clear')\n\t\t},\n\n\t\tapplyLabel() {\n\t\t\treturn t('nextcloud-vue', 'Apply')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tdocument.addEventListener('mousedown', this.onOutsideMouseDown, true)\n\t\tif (this.def.kind === 'reference') {\n\t\t\tthis.loadReferenceOptions('')\n\t\t}\n\t\tthis.$nextTick(() => {\n\t\t\tconst first = this.$refs.panel && this.$refs.panel.querySelector('input, button')\n\t\t\tif (first && typeof first.focus === 'function') {\n\t\t\t\tfirst.focus()\n\t\t\t}\n\t\t})\n\t},\n\n\tbeforeUnmount() {\n\t\tdocument.removeEventListener('mousedown', this.onOutsideMouseDown, true)\n\t\tclearTimeout(this.searchTimer)\n\t},\n\n\tmethods: {\n\t\ttoggleValue(value) {\n\t\t\tthis.draft.values = this.draft.values.includes(value)\n\t\t\t\t? this.draft.values.filter((v) => v !== value)\n\t\t\t\t: [...this.draft.values, value]\n\t\t},\n\n\t\t/**\n\t\t * The state this panel applies, in the shape `columnFilterParams` reads.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tdraftState() {\n\t\t\tif (this.def.kind === 'enum' || this.def.kind === 'reference') {\n\t\t\t\treturn { values: [...this.draft.values] }\n\t\t\t}\n\t\t\tif (this.def.kind === 'number' || this.def.kind === 'date') {\n\t\t\t\treturn { from: this.draft.from, to: this.draft.to }\n\t\t\t}\n\t\t\treturn { value: this.draft.value }\n\t\t},\n\n\t\tapply() {\n\t\t\t/**\n\t\t\t * @event apply The person applied the filter.\n\t\t\t * @type {object} The column filter state.\n\t\t\t */\n\t\t\tthis.$emit('apply', this.draftState())\n\t\t},\n\n\t\tclear() {\n\t\t\tthis.$emit('apply', this.def.kind === 'enum' || this.def.kind === 'reference'\n\t\t\t\t? { values: [] }\n\t\t\t\t: (this.def.kind === 'number' || this.def.kind === 'date') ? { from: '', to: '' } : { value: '' })\n\t\t},\n\n\t\tclose() {\n\t\t\t/**\n\t\t\t * @event close The panel should close without applying.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\tonEnter(event) {\n\t\t\t// Enter in a text, number or date box applies, like a form submit.\n\t\t\t// On a checkbox or a button it keeps its own meaning.\n\t\t\tconst target = event && event.target\n\t\t\tif (target && target.tagName === 'INPUT' && ['text', 'number', 'date', 'search'].includes(target.type)) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.apply()\n\t\t\t}\n\t\t},\n\n\t\tonOutsideMouseDown(event) {\n\t\t\tconst panel = this.$refs.panel\n\t\t\tif (panel && event && event.target && !panel.contains(event.target)) {\n\t\t\t\tthis.close()\n\t\t\t}\n\t\t},\n\n\t\tonSearchInput() {\n\t\t\tclearTimeout(this.searchTimer)\n\t\t\tthis.searchTimer = setTimeout(() => this.loadReferenceOptions(this.query), 250)\n\t\t},\n\n\t\t/**\n\t\t * Search the referenced objects. The value is the object's uuid,\n\t\t * because that is what the server filters a reference on.\n\t\t *\n\t\t * @param {string} search The search term.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadReferenceOptions(search) {\n\t\t\tconst ref = this.def.reference || {}\n\t\t\tconst register = ref.register || this.register\n\t\t\tif (!register || !ref.schema) {\n\t\t\t\tthis.referenceError = t('nextcloud-vue', 'This column cannot list its objects.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.referenceLoading = true\n\t\t\tthis.referenceError = ''\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', {\n\t\t\t\t\tregister,\n\t\t\t\t\tschema: String(ref.schema),\n\t\t\t\t})\n\t\t\t\tconst params = { _limit: 50 }\n\t\t\t\tif (search) {\n\t\t\t\t\tparams._search = search\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(url, { params })\n\t\t\t\tconst rows = (response && response.data && (response.data.results || response.data)) || []\n\t\t\t\tconst list = Array.isArray(rows) ? rows : []\n\t\t\t\tconst options = list\n\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\tconst self = (row && row['@self']) || {}\n\t\t\t\t\t\tconst value = String(self.id || self.uuid || row.id || row.uuid || '')\n\t\t\t\t\t\tconst label = String((ref.labelField && row[ref.labelField]) || row.name || row.title || self.name || value)\n\t\t\t\t\t\treturn { value, label }\n\t\t\t\t\t})\n\t\t\t\t\t.filter((o) => o.value !== '')\n\t\t\t\tthis.referenceOptions = options\n\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...Object.fromEntries(options.map((o) => [o.value, o.label])) }\n\t\t\t} catch {\n\t\t\t\tthis.referenceOptions = []\n\t\t\t\tthis.referenceError = t('nextcloud-vue', 'The list could not load.')\n\t\t\t} finally {\n\t\t\t\tthis.referenceLoading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-column-filter {\n\tz-index: 10000;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tbox-sizing: border-box;\n\tmax-height: 60vh;\n\toverflow-y: auto;\n\tpadding: 12px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 2px 8px var(--color-box-shadow);\n\tfont-weight: normal;\n\ttext-align: start;\n}\n\n.cn-column-filter__heading {\n\tmargin: 0;\n\tfont-weight: bold;\n}\n\n.cn-column-filter__options {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: none;\n}\n\n.cn-column-filter__option {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmin-height: 32px;\n\tcursor: pointer;\n}\n\n.cn-column-filter__range {\n\tdisplay: flex;\n\tgap: 8px;\n}\n\n.cn-column-filter__field {\n\tdisplay: flex;\n\tflex: 1 1 0;\n\tflex-direction: column;\n\tgap: 4px;\n\tmin-width: 0;\n}\n\n.cn-column-filter__label {\n\tfont-size: var(--font-size-small, 13px);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-column-filter__input {\n\twidth: 100%;\n\tbox-sizing: border-box;\n\tmargin: 0;\n}\n\n.cn-column-filter__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-column-filter__actions {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tgap: 8px;\n}\n\n.cn-column-filter__button {\n\tmin-height: 34px;\n\tpadding: 0 12px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-element, 8px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tcursor: pointer;\n}\n\n.cn-column-filter__button--primary {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-column-filter__button:focus-visible,\n.cn-column-filter__input:focus-visible,\n.cn-column-filter__option input:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-column-filter__sr-only {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_createElementVNode","_Fragment","_toDisplayString","_createElementBlock","_renderList","_openBlock","_vModelDynamic","_vModelText"],"mappings":"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"}
1
+ {"version":3,"file":"CnColumnFilterPopover.vue3.js","sources":["../../../../src/components/CnDataTable/CnColumnFilterPopover.vue?vue&type=template&id=2cb54356&lang.js"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<Teleport to=\"body\" :disabled=\"!teleport\">\n\t\t<div\n\t\t\tref=\"panel\"\n\t\t\tclass=\"cn-column-filter\"\n\t\t\trole=\"dialog\"\n\t\t\t:aria-labelledby=\"headingId\"\n\t\t\t:style=\"panelStyle\"\n\t\t\tdata-testid=\"cn-column-filter\"\n\t\t\t@keydown.esc.stop.prevent=\"close\"\n\t\t\t@keydown.enter=\"onEnter\">\n\t\t\t<p :id=\"headingId\" class=\"cn-column-filter__heading\">\n\t\t\t\t{{ headingText }}\n\t\t\t</p>\n\n\t\t\t<!-- enum and reference: a checkbox list of real inputs -->\n\t\t\t<template v-if=\"def.kind === 'enum' || def.kind === 'reference'\">\n\t\t\t\t<label v-if=\"def.kind === 'reference'\" class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ searchLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"query\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-search\"\n\t\t\t\t\t\t@input=\"onSearchInput\">\n\t\t\t\t</label>\n\t\t\t\t<p v-if=\"def.kind === 'reference' && referenceLoading\" class=\"cn-column-filter__hint\" role=\"status\">\n\t\t\t\t\t{{ loadingLabel }}\n\t\t\t\t</p>\n\t\t\t\t<p v-else-if=\"def.kind === 'reference' && referenceError\" class=\"cn-column-filter__hint\" role=\"status\">\n\t\t\t\t\t{{ referenceError }}\n\t\t\t\t</p>\n\t\t\t\t<fieldset class=\"cn-column-filter__options\">\n\t\t\t\t\t<legend class=\"cn-column-filter__sr-only\">\n\t\t\t\t\t\t{{ headingText }}\n\t\t\t\t\t</legend>\n\t\t\t\t\t<label\n\t\t\t\t\t\tv-for=\"option in listOptions\"\n\t\t\t\t\t\t:key=\"option.value\"\n\t\t\t\t\t\tclass=\"cn-column-filter__option\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t:value=\"option.value\"\n\t\t\t\t\t\t\t:checked=\"draft.values.includes(option.value)\"\n\t\t\t\t\t\t\tdata-testid=\"cn-column-filter-option\"\n\t\t\t\t\t\t\t@change=\"toggleValue(option.value)\">\n\t\t\t\t\t\t<span>{{ option.label }}</span>\n\t\t\t\t\t</label>\n\t\t\t\t\t<p v-if=\"listOptions.length === 0 && !referenceLoading\" class=\"cn-column-filter__hint\">\n\t\t\t\t\t\t{{ noOptionsLabel }}\n\t\t\t\t\t</p>\n\t\t\t\t</fieldset>\n\t\t\t</template>\n\n\t\t\t<!-- boolean: yes, no, any -->\n\t\t\t<fieldset v-else-if=\"def.kind === 'boolean'\" class=\"cn-column-filter__options\">\n\t\t\t\t<legend class=\"cn-column-filter__sr-only\">\n\t\t\t\t\t{{ headingText }}\n\t\t\t\t</legend>\n\t\t\t\t<label v-for=\"option in booleanOptions\" :key=\"option.value\" class=\"cn-column-filter__option\">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"radio\"\n\t\t\t\t\t\t:name=\"headingId + '-bool'\"\n\t\t\t\t\t\t:value=\"option.value\"\n\t\t\t\t\t\t:checked=\"draft.value === option.value\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-option\"\n\t\t\t\t\t\t@change=\"draft.value = option.value\">\n\t\t\t\t\t<span>{{ option.label }}</span>\n\t\t\t\t</label>\n\t\t\t</fieldset>\n\n\t\t\t<!-- number and date: from and to -->\n\t\t\t<div v-else-if=\"def.kind === 'number' || def.kind === 'date'\" class=\"cn-column-filter__range\">\n\t\t\t\t<label class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ fromLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"draft.from\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\t:type=\"def.kind === 'date' ? 'date' : 'number'\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-from\">\n\t\t\t\t</label>\n\t\t\t\t<label class=\"cn-column-filter__field\">\n\t\t\t\t\t<span class=\"cn-column-filter__label\">{{ toLabel }}</span>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"draft.to\"\n\t\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\t\t:type=\"def.kind === 'date' ? 'date' : 'number'\"\n\t\t\t\t\t\tdata-testid=\"cn-column-filter-to\">\n\t\t\t\t</label>\n\t\t\t</div>\n\n\t\t\t<!-- string: contains (OpenRegister `[like]`, case-insensitive) -->\n\t\t\t<label v-else class=\"cn-column-filter__field\">\n\t\t\t\t<span class=\"cn-column-filter__label\">{{ containsLabel }}</span>\n\t\t\t\t<input\n\t\t\t\t\tv-model=\"draft.value\"\n\t\t\t\t\tclass=\"cn-column-filter__input\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-text\">\n\t\t\t</label>\n\n\t\t\t<div class=\"cn-column-filter__actions\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-column-filter__button\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-clear\"\n\t\t\t\t\t@click=\"clear\">\n\t\t\t\t\t{{ clearLabel }}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-column-filter__button cn-column-filter__button--primary\"\n\t\t\t\t\tdata-testid=\"cn-column-filter-apply\"\n\t\t\t\t\t@click=\"apply\">\n\t\t\t\t\t{{ applyLabel }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</div>\n\t</Teleport>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\n\nlet popoverSeq = 0\n\n/**\n * CnColumnFilterPopover — the small panel a table header's filter button opens.\n *\n * Renders the control that fits the column: a checkbox list for an enum, yes,\n * no or any for a boolean, from and to for a number or a date, a searchable\n * list of referenced objects for a reference, and a contains box for text.\n * Real inputs throughout, so every control works from the keyboard. Escape\n * closes it and the host returns focus to the filter button.\n *\n * Internal to CnDataTable. It emits a state object; CnDataTable turns that\n * into query parameters with `columnFilterParams`.\n *\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header\n */\nexport default {\n\tname: 'CnColumnFilterPopover',\n\n\tprops: {\n\t\t/** The filter definition from `columnFilterDef`. */\n\t\tdef: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** The column's current state from `columnFilterState`. */\n\t\tstate: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The translated column label. */\n\t\tlabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The filter button's bounding rect, to place the panel under it. */\n\t\tanchorRect: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Register slug a reference column's objects live in, when the column names none. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Render into `<body>`, so a scrolling table cannot clip the panel. */\n\t\tteleport: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\t},\n\n\temits: ['apply', 'close'],\n\n\tdata() {\n\t\tpopoverSeq += 1\n\t\tconst s = this.state || {}\n\t\treturn {\n\t\t\theadingId: 'cn-column-filter-' + popoverSeq,\n\t\t\tdraft: {\n\t\t\t\tvalues: Array.isArray(s.values) ? [...s.values] : [],\n\t\t\t\tvalue: s.value !== undefined && s.value !== null ? String(s.value) : '',\n\t\t\t\tfrom: s.from || '',\n\t\t\t\tto: s.to || '',\n\t\t\t},\n\n\t\t\tquery: '',\n\t\t\treferenceOptions: [],\n\t\t\treferenceLabels: {},\n\t\t\treferenceLoading: false,\n\t\t\treferenceError: '',\n\t\t\tsearchTimer: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\theadingText() {\n\t\t\treturn t('nextcloud-vue', 'Filter {column}', { column: this.label })\n\t\t},\n\n\t\t/**\n\t\t * The options a list filter shows. For a reference, the search results\n\t\t * plus any picked object that is not among them, so a selection never\n\t\t * disappears from view.\n\t\t *\n\t\t * @return {Array<{value: string, label: string}>}\n\t\t */\n\t\tlistOptions() {\n\t\t\tif (this.def.kind !== 'reference') {\n\t\t\t\treturn this.def.options || []\n\t\t\t}\n\t\t\tconst shown = new Set(this.referenceOptions.map((o) => o.value))\n\t\t\tconst picked = this.draft.values\n\t\t\t\t.filter((v) => !shown.has(v))\n\t\t\t\t.map((v) => ({ value: v, label: this.referenceLabels[v] || v }))\n\t\t\treturn [...picked, ...this.referenceOptions]\n\t\t},\n\n\t\tbooleanOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: '', label: t('nextcloud-vue', 'Any') },\n\t\t\t\t{ value: 'true', label: t('nextcloud-vue', 'Yes') },\n\t\t\t\t{ value: 'false', label: t('nextcloud-vue', 'No') },\n\t\t\t]\n\t\t},\n\n\t\tpanelStyle() {\n\t\t\tif (!this.teleport || !this.anchorRect) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst width = 280\n\t\t\tconst viewport = typeof window !== 'undefined' ? window.innerWidth : 1024\n\t\t\tconst left = Math.max(8, Math.min(this.anchorRect.left, viewport - width - 8))\n\t\t\treturn { position: 'fixed', top: `${this.anchorRect.bottom + 4}px`, left: `${left}px`, width: `${width}px` }\n\t\t},\n\n\t\tsearchLabel() {\n\t\t\treturn t('nextcloud-vue', 'Search')\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading…')\n\t\t},\n\n\t\tnoOptionsLabel() {\n\t\t\treturn t('nextcloud-vue', 'Nothing to choose from here.')\n\t\t},\n\n\t\tfromLabel() {\n\t\t\treturn t('nextcloud-vue', 'From')\n\t\t},\n\n\t\ttoLabel() {\n\t\t\treturn t('nextcloud-vue', 'To')\n\t\t},\n\n\t\t/**\n\t\t * Label of the text box: the text filter matches on contains.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains\n\t\t */\n\t\tcontainsLabel() {\n\t\t\treturn t('nextcloud-vue', 'Contains')\n\t\t},\n\n\t\tclearLabel() {\n\t\t\treturn t('nextcloud-vue', 'Clear')\n\t\t},\n\n\t\tapplyLabel() {\n\t\t\treturn t('nextcloud-vue', 'Apply')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tdocument.addEventListener('mousedown', this.onOutsideMouseDown, true)\n\t\tif (this.def.kind === 'reference') {\n\t\t\tthis.loadReferenceOptions('')\n\t\t}\n\t\tthis.$nextTick(() => {\n\t\t\tconst first = this.$refs.panel && this.$refs.panel.querySelector('input, button')\n\t\t\tif (first && typeof first.focus === 'function') {\n\t\t\t\tfirst.focus()\n\t\t\t}\n\t\t})\n\t},\n\n\tbeforeUnmount() {\n\t\tdocument.removeEventListener('mousedown', this.onOutsideMouseDown, true)\n\t\tclearTimeout(this.searchTimer)\n\t},\n\n\tmethods: {\n\t\ttoggleValue(value) {\n\t\t\tthis.draft.values = this.draft.values.includes(value)\n\t\t\t\t? this.draft.values.filter((v) => v !== value)\n\t\t\t\t: [...this.draft.values, value]\n\t\t},\n\n\t\t/**\n\t\t * The state this panel applies, in the shape `columnFilterParams` reads.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tdraftState() {\n\t\t\tif (this.def.kind === 'enum' || this.def.kind === 'reference') {\n\t\t\t\treturn { values: [...this.draft.values] }\n\t\t\t}\n\t\t\tif (this.def.kind === 'number' || this.def.kind === 'date') {\n\t\t\t\treturn { from: this.draft.from, to: this.draft.to }\n\t\t\t}\n\t\t\treturn { value: this.draft.value }\n\t\t},\n\n\t\tapply() {\n\t\t\t/**\n\t\t\t * @event apply The person applied the filter.\n\t\t\t * @type {object} The column filter state.\n\t\t\t */\n\t\t\tthis.$emit('apply', this.draftState())\n\t\t},\n\n\t\tclear() {\n\t\t\tthis.$emit('apply', this.def.kind === 'enum' || this.def.kind === 'reference'\n\t\t\t\t? { values: [] }\n\t\t\t\t: (this.def.kind === 'number' || this.def.kind === 'date') ? { from: '', to: '' } : { value: '' })\n\t\t},\n\n\t\tclose() {\n\t\t\t/**\n\t\t\t * @event close The panel should close without applying.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\tonEnter(event) {\n\t\t\t// Enter in a text, number or date box applies, like a form submit.\n\t\t\t// On a checkbox or a button it keeps its own meaning.\n\t\t\tconst target = event && event.target\n\t\t\tif (target && target.tagName === 'INPUT' && ['text', 'number', 'date', 'search'].includes(target.type)) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.apply()\n\t\t\t}\n\t\t},\n\n\t\tonOutsideMouseDown(event) {\n\t\t\tconst panel = this.$refs.panel\n\t\t\tif (panel && event && event.target && !panel.contains(event.target)) {\n\t\t\t\tthis.close()\n\t\t\t}\n\t\t},\n\n\t\tonSearchInput() {\n\t\t\tclearTimeout(this.searchTimer)\n\t\t\tthis.searchTimer = setTimeout(() => this.loadReferenceOptions(this.query), 250)\n\t\t},\n\n\t\t/**\n\t\t * Search the referenced objects. The value is the object's uuid,\n\t\t * because that is what the server filters a reference on.\n\t\t *\n\t\t * @param {string} search The search term.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadReferenceOptions(search) {\n\t\t\tconst ref = this.def.reference || {}\n\t\t\tconst register = ref.register || this.register\n\t\t\tif (!register || !ref.schema) {\n\t\t\t\tthis.referenceError = t('nextcloud-vue', 'This column cannot list its objects.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.referenceLoading = true\n\t\t\tthis.referenceError = ''\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', {\n\t\t\t\t\tregister,\n\t\t\t\t\tschema: String(ref.schema),\n\t\t\t\t})\n\t\t\t\tconst params = { _limit: 50 }\n\t\t\t\tif (search) {\n\t\t\t\t\tparams._search = search\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(url, { params })\n\t\t\t\tconst rows = (response && response.data && (response.data.results || response.data)) || []\n\t\t\t\tconst list = Array.isArray(rows) ? rows : []\n\t\t\t\tconst options = list\n\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\tconst self = (row && row['@self']) || {}\n\t\t\t\t\t\tconst value = String(self.id || self.uuid || row.id || row.uuid || '')\n\t\t\t\t\t\tconst label = String((ref.labelField && row[ref.labelField]) || row.name || row.title || self.name || value)\n\t\t\t\t\t\treturn { value, label }\n\t\t\t\t\t})\n\t\t\t\t\t.filter((o) => o.value !== '')\n\t\t\t\tthis.referenceOptions = options\n\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...Object.fromEntries(options.map((o) => [o.value, o.label])) }\n\t\t\t} catch {\n\t\t\t\tthis.referenceOptions = []\n\t\t\t\tthis.referenceError = t('nextcloud-vue', 'The list could not load.')\n\t\t\t} finally {\n\t\t\t\tthis.referenceLoading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-column-filter {\n\tz-index: 10000;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tbox-sizing: border-box;\n\tmax-height: 60vh;\n\toverflow-y: auto;\n\tpadding: 12px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 2px 8px var(--color-box-shadow);\n\tfont-weight: normal;\n\ttext-align: start;\n}\n\n.cn-column-filter__heading {\n\tmargin: 0;\n\tfont-weight: bold;\n}\n\n.cn-column-filter__options {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: none;\n}\n\n.cn-column-filter__option {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmin-height: 32px;\n\tcursor: pointer;\n}\n\n.cn-column-filter__range {\n\tdisplay: flex;\n\tgap: 8px;\n}\n\n.cn-column-filter__field {\n\tdisplay: flex;\n\tflex: 1 1 0;\n\tflex-direction: column;\n\tgap: 4px;\n\tmin-width: 0;\n}\n\n.cn-column-filter__label {\n\tfont-size: var(--font-size-small, 13px);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-column-filter__input {\n\twidth: 100%;\n\tbox-sizing: border-box;\n\tmargin: 0;\n}\n\n.cn-column-filter__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-column-filter__actions {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tgap: 8px;\n}\n\n.cn-column-filter__button {\n\tmin-height: 34px;\n\tpadding: 0 12px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-element, 8px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tcursor: pointer;\n}\n\n.cn-column-filter__button--primary {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-column-filter__button:focus-visible,\n.cn-column-filter__input:focus-visible,\n.cn-column-filter__option input:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-column-filter__sr-only {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_createElementVNode","_Fragment","_toDisplayString","_createElementBlock","_renderList","_openBlock","_vModelDynamic","_vModelText"],"mappings":"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"}
@@ -13,25 +13,28 @@ import InformationOutline from 'vue-material-design-icons/InformationOutline.vue
13
13
  import Pencil from 'vue-material-design-icons/Pencil.vue';
14
14
  import Plus from 'vue-material-design-icons/Plus.vue';
15
15
  import Refresh from 'vue-material-design-icons/Refresh.vue';
16
- import __sfc_main$h from '../../dialogs/CnRelationLinkModal.vue.js';
17
- import __sfc_main$g from '../../dialogs/CnWidgetStyleEditorModal.vue.js';
16
+ import ViewDashboardOutline from 'vue-material-design-icons/ViewDashboardOutline.vue';
17
+ import __sfc_main$i from '../../dialogs/CnRelationLinkModal.vue.js';
18
+ import __sfc_main$h from '../../dialogs/CnWidgetStyleEditorModal.vue.js';
18
19
  import __sfc_main$1 from '../CnActionsMenu/CnActionsMenu.vue.js';
19
- import __sfc_main$e from '../CnBodySections/CnBodySections.vue.js';
20
+ import __sfc_main$f from '../CnBodySections/CnBodySections.vue.js';
20
21
  import __sfc_main$2 from '../CnBuildiqEditButton/CnBuildiqEditButton.vue.js';
21
22
  import __sfc_main$7 from '../CnDashboardGrid/CnDashboardGrid.vue.js';
22
23
  import __sfc_main$5 from '../CnDetailWidgetHost/CnDetailWidgetHost.vue.js';
23
24
  import __sfc_main$6 from '../CnFormDialog/CnFormDialog.vue.js';
24
- import __sfc_main$8 from '../CnLifecycleActions/CnLifecycleActions.vue.js';
25
+ import __sfc_main$9 from '../CnLifecycleActions/CnLifecycleActions.vue.js';
25
26
  import __sfc_main$3 from '../CnLockedBanner/CnLockedBanner.vue.js';
26
27
  import __sfc_main$4 from '../CnNextStepCard/CnNextStepCard.vue.js';
27
- import __sfc_main$d from '../CnRelatedCollections/CnRelatedCollections.vue.js';
28
- import __sfc_main$c from '../CnSummaryAggregates/CnSummaryAggregates.vue.js';
29
- import __sfc_main$f from '../CnTranslatedBadge/CnTranslatedBadge.vue.js';
28
+ import __sfc_main$e from '../CnRelatedCollections/CnRelatedCollections.vue.js';
29
+ import __sfc_main$8 from '../CnSegmentedControl/CnSegmentedControl.vue.js';
30
+ import __sfc_main$d from '../CnSummaryAggregates/CnSummaryAggregates.vue.js';
31
+ import __sfc_main$g from '../CnTranslatedBadge/CnTranslatedBadge.vue.js';
30
32
  import { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js';
31
33
  import { useObjectLock } from '../../composables/useObjectLock.js';
32
34
  import { useObjectPresence } from '../../composables/useObjectPresence.js';
33
35
  import { useObjectSubscription } from '../../composables/useObjectSubscription.js';
34
36
  import { gridLayout } from '../../mixins/gridLayout.js';
37
+ import { pageViews } from '../../mixins/pageViews.js';
35
38
  import { isAppInstalled } from '../../utils/appInstalled.js';
36
39
  import { compactLayoutRows } from '../../utils/dashboardPlacement.js';
37
40
  import { defaultDetailGrid } from '../../utils/defaultDetailGrid.js';
@@ -39,10 +42,10 @@ import { stageOf, normalisePinnedAction, stageEntry, MAX_QUICK_ACTIONS, groupMen
39
42
  import { cnGridCellStyle, hasGridRow } from '../../utils/grid.js';
40
43
  import { slotRenders } from '../../utils/slotContent.js';
41
44
  import { isDataWidgetDef, isRelatedWidgetDef, isGeoWidgetDef, isContentOnlyWidgetDef, isCardWidgetDef, widgetContentOf, resolveRegistryRenderer, widgetTitleOf } from '../../utils/widgetDispatch.js';
42
- import __sfc_main$9 from '../CnActionButtons/CnActionButtons.vue.js';
43
- import __sfc_main$a from '../CnBreadcrumbs/CnBreadcrumbs.vue.js';
45
+ import __sfc_main$a from '../CnActionButtons/CnActionButtons.vue.js';
46
+ import __sfc_main$b from '../CnBreadcrumbs/CnBreadcrumbs.vue.js';
44
47
  import __sfc_main from '../CnIcon/CnIcon.vue.js';
45
- import __sfc_main$b from '../CnStatusBadge/CnStatusBadge.vue.js';
48
+ import __sfc_main$c from '../CnStatusBadge/CnStatusBadge.vue.js';
46
49
  import { getWidgetTypeEntry } from '../CnWidgetGrid/dashboardWidgetRegistry.js';
47
50
  import '../CnWidgetGrid/registerDashboardWidgets.js';
48
51
  import { useObjectStore } from '../../store/useObjectStore.js';
@@ -186,22 +189,24 @@ var script = {
186
189
  CnDetailWidgetHost: __sfc_main$5,
187
190
  CnFormDialog: __sfc_main$6,
188
191
  CnDashboardGrid: __sfc_main$7,
189
- CnLifecycleActions: __sfc_main$8,
190
- CnActionButtons: __sfc_main$9,
191
- CnBreadcrumbs: __sfc_main$a,
192
- CnStatusBadge: __sfc_main$b,
193
- CnSummaryAggregates: __sfc_main$c,
194
- CnRelatedCollections: __sfc_main$d,
195
- CnBodySections: __sfc_main$e,
196
- CnTranslatedBadge: __sfc_main$f,
197
- CnWidgetStyleEditorModal: __sfc_main$g,
198
- CnRelationLinkModal: __sfc_main$h,
192
+ CnSegmentedControl: __sfc_main$8,
193
+ ViewDashboardOutline,
194
+ CnLifecycleActions: __sfc_main$9,
195
+ CnActionButtons: __sfc_main$a,
196
+ CnBreadcrumbs: __sfc_main$b,
197
+ CnStatusBadge: __sfc_main$c,
198
+ CnSummaryAggregates: __sfc_main$d,
199
+ CnRelatedCollections: __sfc_main$e,
200
+ CnBodySections: __sfc_main$f,
201
+ CnTranslatedBadge: __sfc_main$g,
202
+ CnWidgetStyleEditorModal: __sfc_main$h,
203
+ CnRelationLinkModal: __sfc_main$i,
199
204
  Cog,
200
205
  Plus,
201
206
  Pencil,
202
207
  },
203
208
 
204
- mixins: [gridLayout],
209
+ mixins: [gridLayout, pageViews],
205
210
 
206
211
  inject: {
207
212
  /**
@@ -2421,6 +2426,7 @@ var script = {
2421
2426
  && this.currentObject
2422
2427
  && !this.hasDefaultSlotContent
2423
2428
  && !this.hasGridLayout
2429
+ && !this.hasViews
2424
2430
  },
2425
2431
 
2426
2432
  /**
@@ -2500,6 +2506,55 @@ var script = {
2500
2506
  return this.bodyGridLayout.length > 0
2501
2507
  },
2502
2508
 
2509
+ /**
2510
+ * The chosen view's layout as the grid draws it: items that draw
2511
+ * nothing leave and their rows close, as for the body grid.
2512
+ *
2513
+ * @return {Array<object>}
2514
+ * @spec openspec/changes/view-switch-containers/specs/view-switch-containers/spec.md#requirement-a-view-with-nothing-to-draw-says-so
2515
+ */
2516
+ viewDisplayLayout() {
2517
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- reading the flag IS the effect, as in bodyGridLayout
2518
+ this.editingBody;
2519
+ return this.activeView ? this.closeDroppedRows(this.activeView.layout) : []
2520
+ },
2521
+
2522
+ /**
2523
+ * Whether the chosen view has nothing to draw.
2524
+ *
2525
+ * @return {boolean}
2526
+ * @spec openspec/changes/view-switch-containers/specs/view-switch-containers/spec.md#requirement-a-view-with-nothing-to-draw-says-so
2527
+ */
2528
+ viewIsEmpty() {
2529
+ return this.hasViews && this.viewDisplayLayout.length === 0
2530
+ },
2531
+
2532
+ /**
2533
+ * The grids the body draws: the body grid when it has items, then the
2534
+ * chosen view's grid in the region the view switch controls (left out
2535
+ * when the view is empty; the empty state takes the region then).
2536
+ *
2537
+ * @return {Array<{key: string, view: boolean, layout: Array<object>, attrs: object}>}
2538
+ * @spec openspec/changes/view-switch-containers/specs/view-switch-containers/spec.md#requirement-a-page-declares-views-that-each-hold-a-widget-grid
2539
+ */
2540
+ bodyGridSections() {
2541
+ const sections = this.hasBodyGrid ? [{ key: 'body', view: false, layout: this.bodyGridLayout, attrs: {} }] : [];
2542
+ if (this.hasViews && !this.viewIsEmpty) {
2543
+ sections.push({
2544
+ key: `view-${this.activeViewId}`,
2545
+ view: true,
2546
+ layout: this.viewDisplayLayout,
2547
+ attrs: {
2548
+ id: this.viewRegionId,
2549
+ role: 'region',
2550
+ 'aria-label': this.activeView.label,
2551
+ 'data-testid': 'cn-detail-page-view-region',
2552
+ },
2553
+ });
2554
+ }
2555
+ return sections
2556
+ },
2557
+
2503
2558
  /**
2504
2559
  * Whether the sidebar is rendered externally (via objectSidebarState inject)
2505
2560
  * rather than inline. When external, CnDetailPage only manages state —
@@ -3721,6 +3776,8 @@ var script = {
3721
3776
  */
3722
3777
  findWidget(item) {
3723
3778
  return this.bodyGridWidgets.find((w) => w.id === item.widgetId)
3779
+ || this.findViewWidget(item.widgetId)
3780
+ || undefined
3724
3781
  },
3725
3782
 
3726
3783
  /**