@conduction/nextcloud-vue 2.111.0 → 2.112.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 (127) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +3 -2
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +4 -2
  5. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  6. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js +1 -1
  7. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +3 -2
  8. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js +2 -1
  10. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -1
  11. package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
  12. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +54 -5
  13. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  14. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +16 -2
  15. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  16. package/dist/esm/components/CnCardGrid/CnCardGrid.vue.js +1 -1
  17. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +10 -0
  18. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  19. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +2 -1
  20. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  21. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
  22. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +22 -2
  23. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  24. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +17 -3
  25. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  26. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  27. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  28. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +32 -8
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +31 -3
  32. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  33. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  34. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +48 -11
  35. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  36. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +160 -97
  37. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  38. package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
  39. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  40. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +2 -2
  41. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  42. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +166 -2
  43. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  44. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +19 -4
  45. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  46. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js +1 -1
  47. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js +21 -1
  48. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js.map +1 -1
  49. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js.map +1 -1
  50. package/dist/esm/components/CnObjectCard/CnObjectCard.vue.js +1 -1
  51. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +36 -2
  52. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  53. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +44 -5
  54. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  55. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js +1 -0
  56. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js.map +1 -1
  57. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js +22 -2
  58. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js.map +1 -1
  59. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js +36 -16
  60. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js.map +1 -1
  61. package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue.js +1 -1
  62. package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue2.js.map +1 -1
  63. package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue3.js.map +1 -1
  64. package/dist/esm/composables/useRefLabels.js +26 -6
  65. package/dist/esm/composables/useRefLabels.js.map +1 -1
  66. package/dist/esm/integrations/registry.js +4 -0
  67. package/dist/esm/integrations/registry.js.map +1 -1
  68. package/dist/esm/l10n/en.json.js +3 -1
  69. package/dist/esm/l10n/en.json.js.map +1 -1
  70. package/dist/esm/l10n/nl.json.js +3 -1
  71. package/dist/esm/l10n/nl.json.js.map +1 -1
  72. package/dist/esm/library-style.js +1 -1
  73. package/dist/esm/nextcloud-vue.css +533 -273
  74. package/dist/esm/store/plugins/liveUpdates.js +3 -1
  75. package/dist/esm/store/plugins/liveUpdates.js.map +1 -1
  76. package/dist/esm/store/plugins/logs.js +2 -1
  77. package/dist/esm/store/plugins/logs.js.map +1 -1
  78. package/dist/esm/utils/detailActionModel.js +28 -1
  79. package/dist/esm/utils/detailActionModel.js.map +1 -1
  80. package/dist/esm/utils/fillTemplate.js +13 -5
  81. package/dist/esm/utils/fillTemplate.js.map +1 -1
  82. package/dist/esm/utils/headerFieldChips.js +21 -2
  83. package/dist/esm/utils/headerFieldChips.js.map +1 -1
  84. package/dist/esm/utils/mapList.js +26 -11
  85. package/dist/esm/utils/mapList.js.map +1 -1
  86. package/dist/esm/utils/savedViewMenu.js +12 -5
  87. package/dist/esm/utils/savedViewMenu.js.map +1 -1
  88. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  89. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  90. package/dist/nextcloud-vue.cjs.js +2562 -1886
  91. package/dist/nextcloud-vue.cjs.js.map +2 -2
  92. package/dist/nextcloud-vue.css +533 -273
  93. package/l10n/en.json +3 -1
  94. package/l10n/nl.json +3 -1
  95. package/package.json +1 -1
  96. package/src/components/CnActionDialog/CnActionDialog.vue +32 -9
  97. package/src/components/CnAssignDialog/CnAssignDialog.vue +4 -2
  98. package/src/components/CnBoardView/CnBoardView.vue +58 -5
  99. package/src/components/CnCardGrid/CnCardGrid.md +1 -0
  100. package/src/components/CnCardGrid/CnCardGrid.vue +11 -0
  101. package/src/components/CnCellRenderer/CnCellRenderer.vue +27 -3
  102. package/src/components/CnChartWidget/CnChartWidget.vue +6 -0
  103. package/src/components/CnDetailPage/CnDetailPage.vue +42 -9
  104. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +100 -5
  105. package/src/components/CnFormDialog/CnFormDialog.vue +1 -1
  106. package/src/components/CnIndexPage/CnIndexPage.vue +174 -4
  107. package/src/components/CnNcDashboardList/CnNcDashboardList.vue +21 -1
  108. package/src/components/CnObjectCard/CnObjectCard.md +1 -0
  109. package/src/components/CnObjectCard/CnObjectCard.vue +51 -5
  110. package/src/components/CnPartyPicker/CnPartyPicker.vue +62 -16
  111. package/src/components/CnSearchSelect/CnSearchSelect.vue +8 -0
  112. package/src/composables/useRefLabels.js +25 -5
  113. package/src/css/dialog.css +53 -3
  114. package/src/css/index-page.css +26 -1
  115. package/src/css/look-board-detail.css +36 -15
  116. package/src/css/look-board-index.css +69 -3
  117. package/src/css/look-board.css +30 -2
  118. package/src/integrations/registry.js +4 -0
  119. package/src/schemas/app-manifest-v2.schema.json +11 -5
  120. package/src/store/plugins/liveUpdates.js +3 -1
  121. package/src/store/plugins/logs.js +2 -1
  122. package/src/utils/detailActionModel.js +27 -0
  123. package/src/utils/fillTemplate.js +13 -5
  124. package/src/utils/headerFieldChips.js +21 -2
  125. package/src/utils/mapList.js +26 -11
  126. package/src/utils/savedViewMenu.js +12 -5
  127. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"headerFieldChips.js","sources":["../../../src/utils/headerFieldChips.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Pure helpers for the detail-page header field chips\n * (manifest `config.headerFields`).\n *\n * @spec openspec/changes/detail-header-field-chips/specs/detail-page-header/spec.md\n */\n\n/** The renderers a header chip can use. */\nexport const HEADER_CHIP_FORMATS = Object.freeze(['text', 'mono', 'badge', 'user', 'date'])\n\n/** The variants a colour name maps onto; anything else is neutral. */\nconst COLOR_VARIANTS = Object.freeze({\n\tsuccess: 'success',\n\twarning: 'warning',\n\terror: 'error',\n\tinfo: 'info',\n\tprimary: 'primary',\n\tneutral: 'default',\n})\n\n/**\n * Normalise `headerFields`: a string is `{ key }`, an object keeps its known\n * keys, and anything without a usable `key` or with an unknown `format` is\n * dropped. A repeated key keeps its first entry.\n *\n * @param {Array<string|object>|null|undefined} fields The declared entries.\n * @return {Array<{key: string, format: string, labelField: string, colorField: string, warnWhenPast: boolean}>} The usable entries, in order.\n */\nexport function normalizeHeaderFields(fields) {\n\tif (!Array.isArray(fields)) {\n\t\treturn []\n\t}\n\tconst seen = new Set()\n\tconst out = []\n\tfor (const entry of fields) {\n\t\tconst raw = typeof entry === 'string' ? { key: entry } : entry\n\t\tif (!raw || typeof raw !== 'object' || typeof raw.key !== 'string' || raw.key === '' || seen.has(raw.key)) {\n\t\t\tcontinue\n\t\t}\n\t\tconst format = raw.format === undefined ? 'text' : raw.format\n\t\tif (!HEADER_CHIP_FORMATS.includes(format)) {\n\t\t\tcontinue\n\t\t}\n\t\tseen.add(raw.key)\n\t\tout.push({\n\t\t\tkey: raw.key,\n\t\t\tformat,\n\t\t\tlabelField: typeof raw.labelField === 'string' ? raw.labelField : '',\n\t\t\tcolorField: typeof raw.colorField === 'string' ? raw.colorField : '',\n\t\t\twarnWhenPast: raw.warnWhenPast === true,\n\t\t})\n\t}\n\treturn out\n}\n\n/**\n * Whether a field value has nothing to show.\n *\n * @param {*} value The raw value.\n * @return {boolean} True for null, undefined, an empty string or an empty array.\n */\nexport function isEmptyChipValue(value) {\n\treturn value === null || value === undefined || value === '' || (Array.isArray(value) && value.length === 0)\n}\n\n/**\n * Map a colour name read off a referenced object onto a badge variant.\n *\n * @param {*} name The colour name (`success`, `warning`, `error`, `info`, `neutral`).\n * @return {string} A `CnStatusBadge` variant; `default` (neutral) when unknown.\n */\nexport function variantForColor(name) {\n\tconst key = typeof name === 'string' ? name.trim().toLowerCase() : ''\n\treturn COLOR_VARIANTS[key] || 'default'\n}\n\n/**\n * The display label of a referenced object: `labelField`, then `title`, then\n * `name`, then `@self.name`. A per-language map collapses to its first value.\n *\n * @param {object|null} obj The referenced object.\n * @param {string} [labelField] The preferred property.\n * @return {string} The label, or '' when none is usable.\n */\nexport function pickRefLabel(obj, labelField = '') {\n\tif (!obj || typeof obj !== 'object') {\n\t\treturn ''\n\t}\n\tconst candidates = [labelField ? obj[labelField] : undefined, obj.title, obj.name, obj['@self'] && obj['@self'].name]\n\tfor (const raw of candidates) {\n\t\tif (typeof raw === 'string' && raw !== '') {\n\t\t\treturn raw\n\t\t}\n\t\tif (typeof raw === 'number') {\n\t\t\treturn String(raw)\n\t\t}\n\t\tif (raw && typeof raw === 'object' && !Array.isArray(raw)) {\n\t\t\tconst first = Object.values(raw).find((v) => typeof v === 'string' && v !== '')\n\t\t\tif (first) {\n\t\t\t\treturn first\n\t\t\t}\n\t\t}\n\t}\n\treturn ''\n}\n\n/**\n * Whether a date value lies before now.\n *\n * @param {*} value A date string, number or Date.\n * @param {number} [now] The current time in ms (injectable for tests).\n * @return {boolean} True when it parses and is in the past.\n */\nexport function isPastDate(value, now = Date.now()) {\n\tconst time = value instanceof Date ? value.getTime() : new Date(value).getTime()\n\treturn !Number.isNaN(time) && time < now\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"headerFieldChips.js","sources":["../../../src/utils/headerFieldChips.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Pure helpers for the detail-page header field chips\n * (manifest `config.headerFields`).\n *\n * @spec openspec/changes/detail-header-field-chips/specs/detail-page-header/spec.md\n */\n\n/** The renderers a header chip can use. */\nexport const HEADER_CHIP_FORMATS = Object.freeze(['text', 'mono', 'badge', 'user', 'date'])\n\n/**\n * The variants a colour name maps onto; anything else is neutral. A name is a\n * badge role (`success`, `warning`, ...) or an NL Design System hue as a\n * register stores it (`red`, `blue-light`, ...): a hue and its light tint\n * take the same tone.\n */\nconst COLOR_VARIANTS = Object.freeze({\n\tsuccess: 'success',\n\twarning: 'warning',\n\terror: 'error',\n\tinfo: 'info',\n\tprimary: 'primary',\n\tneutral: 'default',\n\tblue: 'info',\n\t'blue-light': 'info',\n\tgreen: 'success',\n\t'green-light': 'success',\n\torange: 'warning',\n\t'orange-light': 'warning',\n\tred: 'error',\n\t'red-light': 'error',\n\tpurple: 'purple',\n\t'purple-light': 'purple',\n\tteal: 'teal',\n\t'teal-light': 'teal',\n\tgrey: 'default',\n\t'grey-light': 'default',\n})\n\n/**\n * Normalise `headerFields`: a string is `{ key }`, an object keeps its known\n * keys, and anything without a usable `key` or with an unknown `format` is\n * dropped. A repeated key keeps its first entry.\n *\n * @param {Array<string|object>|null|undefined} fields The declared entries.\n * @return {Array<{key: string, format: string, labelField: string, colorField: string, warnWhenPast: boolean}>} The usable entries, in order.\n */\nexport function normalizeHeaderFields(fields) {\n\tif (!Array.isArray(fields)) {\n\t\treturn []\n\t}\n\tconst seen = new Set()\n\tconst out = []\n\tfor (const entry of fields) {\n\t\tconst raw = typeof entry === 'string' ? { key: entry } : entry\n\t\tif (!raw || typeof raw !== 'object' || typeof raw.key !== 'string' || raw.key === '' || seen.has(raw.key)) {\n\t\t\tcontinue\n\t\t}\n\t\tconst format = raw.format === undefined ? 'text' : raw.format\n\t\tif (!HEADER_CHIP_FORMATS.includes(format)) {\n\t\t\tcontinue\n\t\t}\n\t\tseen.add(raw.key)\n\t\tout.push({\n\t\t\tkey: raw.key,\n\t\t\tformat,\n\t\t\tlabelField: typeof raw.labelField === 'string' ? raw.labelField : '',\n\t\t\tcolorField: typeof raw.colorField === 'string' ? raw.colorField : '',\n\t\t\twarnWhenPast: raw.warnWhenPast === true,\n\t\t})\n\t}\n\treturn out\n}\n\n/**\n * Whether a field value has nothing to show.\n *\n * @param {*} value The raw value.\n * @return {boolean} True for null, undefined, an empty string or an empty array.\n */\nexport function isEmptyChipValue(value) {\n\treturn value === null || value === undefined || value === '' || (Array.isArray(value) && value.length === 0)\n}\n\n/**\n * Map a colour name read off a referenced object onto a badge variant.\n *\n * @param {*} name The colour name: a badge role (`success`, `warning`, `error`, `info`, `neutral`) or an NL Design System hue (`red`, `blue-light`, ...).\n * @return {string} A `CnStatusBadge` variant; `default` (neutral) when unknown.\n */\nexport function variantForColor(name) {\n\tconst key = typeof name === 'string' ? name.trim().toLowerCase() : ''\n\treturn COLOR_VARIANTS[key] || 'default'\n}\n\n/**\n * The display label of a referenced object: `labelField`, then `title`, then\n * `name`, then `@self.name`. A per-language map collapses to its first value.\n *\n * @param {object|null} obj The referenced object.\n * @param {string} [labelField] The preferred property.\n * @return {string} The label, or '' when none is usable.\n */\nexport function pickRefLabel(obj, labelField = '') {\n\tif (!obj || typeof obj !== 'object') {\n\t\treturn ''\n\t}\n\tconst candidates = [labelField ? obj[labelField] : undefined, obj.title, obj.name, obj['@self'] && obj['@self'].name]\n\tfor (const raw of candidates) {\n\t\tif (typeof raw === 'string' && raw !== '') {\n\t\t\treturn raw\n\t\t}\n\t\tif (typeof raw === 'number') {\n\t\t\treturn String(raw)\n\t\t}\n\t\tif (raw && typeof raw === 'object' && !Array.isArray(raw)) {\n\t\t\tconst first = Object.values(raw).find((v) => typeof v === 'string' && v !== '')\n\t\t\tif (first) {\n\t\t\t\treturn first\n\t\t\t}\n\t\t}\n\t}\n\treturn ''\n}\n\n/**\n * Whether a date value lies before now.\n *\n * @param {*} value A date string, number or Date.\n * @param {number} [now] The current time in ms (injectable for tests).\n * @return {boolean} True when it parses and is in the past.\n */\nexport function isPastDate(value, now = Date.now()) {\n\tconst time = value instanceof Date ? value.getTime() : new Date(value).getTime()\n\treturn !Number.isNaN(time) && time < now\n}\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { isBadgeVariant } from './badgeVariants.js';
2
- import { fillTemplate } from './fillTemplate.js';
2
+ import { pillLook } from './detailActionModel.js';
3
+ import { fillTemplate, firstFilled } from './fillTemplate.js';
3
4
  import { readPath } from './readPath.js';
4
5
 
5
6
  /**
@@ -13,10 +14,14 @@ import { readPath } from './readPath.js';
13
14
  * pin and the number in the list are the same, and the rows without one.
14
15
  *
15
16
  * `spec` is `mapConfig.list`: `{ title, sub?, pills?: [{ path?, text?, map?,
16
- * colorMap? }], due? }`. `title`, `sub` and `due` are field templates
17
- * (`"{title}"`, `"{address} · {identifier}"`, `"{deadline|date}"`), already
18
- * translated by the caller; a pill reads `path` (or shows `text`), maps it
19
- * through `map`, and takes its tone from `colorMap` by the raw value.
17
+ * formatter?, colorMap?, variant?, resolve? }], due? }`. `title`, `sub` and `due` are
18
+ * field templates (`"{title}"`, `"{address} · {identifier}"`,
19
+ * `"{deadline|date}"`), already translated by the caller; `sub` may list
20
+ * several, the first that fills wins. A pill reads `path` (or shows `text`),
21
+ * maps it through `map` or the app formatter `formatter` (a reference's
22
+ * display name), and takes its tone from `colorMap` by the raw value, or,
23
+ * with `resolve`, the tone and icon of the record the value points at
24
+ * (`options.looks`, read by CnIndexPage as for its other pills).
20
25
  *
21
26
  * @param {Array<object>} rows The rows, in display order.
22
27
  * @param {object} spec The list declaration.
@@ -24,9 +29,14 @@ import { readPath } from './readPath.js';
24
29
  * @param {string} options.rowKey The row's key field.
25
30
  * @param {Function} options.located Whether a row has a place on the map.
26
31
  * @param {Function} [options.translate] The host's translate, for pill labels.
32
+ * @param {object} [options.formatters] The app's named formatters, for pills and template tokens.
33
+ * @param {object} [options.looks] The referenced records' looks by field and id (`{ [path]: { [id]: { color, icon } } }`).
27
34
  * @return {{items: Array<{key: string, n: number, row: object, title: string, sub: string, due: string, pills: Array<{label: string, variant: string}>}>, missing: Array<object>}}
35
+ * @spec openspec/changes/map-list-formatters/specs/map-list-formatters/spec.md#requirement-a-map-list-reads-display-names-through-the-apps-formatters
36
+ * @spec openspec/changes/map-list-formatters/specs/map-list-formatters/spec.md#requirement-a-map-list-pill-takes-its-tone-and-icon-from-the-referenced-record
28
37
  */
29
- function mapListItems(rows, spec, { rowKey, located, translate = (text) => text }) {
38
+ function mapListItems(rows, spec, { rowKey, located, translate = (text) => text, formatters = null, looks = null }) {
39
+ const context = formatters ? { formatters } : null;
30
40
  const items = [];
31
41
  const missing = [];
32
42
  const list = spec && typeof spec === 'object' ? spec : {};
@@ -41,7 +51,9 @@ function mapListItems(rows, spec, { rowKey, located, translate = (text) => text
41
51
  return null
42
52
  }
43
53
  const raw = typeof pill.path === 'string' && pill.path !== '' ? readPath(row, pill.path) : undefined;
44
- const mapped = pill.map && (typeof raw === 'string' || typeof raw === 'number') && Object.hasOwn(pill.map, raw) ? pill.map[raw] : undefined;
54
+ const formatter = typeof pill.formatter === 'string' && formatters ? formatters[pill.formatter] : null;
55
+ const formatted = typeof formatter === 'function' && (typeof raw === 'string' || typeof raw === 'number') && raw !== '' ? formatter(raw, row) : undefined;
56
+ const mapped = pill.map && (typeof raw === 'string' || typeof raw === 'number') && Object.hasOwn(pill.map, raw) ? pill.map[raw] : formatted;
45
57
  const text = mapped ?? pill.text ?? raw;
46
58
  if (typeof text !== 'string' && typeof text !== 'number') {
47
59
  return null
@@ -51,16 +63,19 @@ function mapListItems(rows, spec, { rowKey, located, translate = (text) => text
51
63
  return null
52
64
  }
53
65
  const tone = pill.colorMap && (typeof raw === 'string' || typeof raw === 'number') ? pill.colorMap[raw] : pill.variant;
54
- return { label, variant: isBadgeVariant(tone) ? tone : 'default' }
66
+ const fieldLooks = pill.resolve && looks && typeof pill.path === 'string' ? looks[pill.path] : null;
67
+ const look = pillLook(fieldLooks && (typeof raw === 'string' || typeof raw === 'number') ? fieldLooks[String(raw)] : null);
68
+ const variant = look.variant || tone;
69
+ return { label, variant: isBadgeVariant(variant) ? variant : 'default', ...(look.icon ? { icon: look.icon } : {}) }
55
70
  })
56
71
  .filter(Boolean);
57
72
  items.push({
58
73
  key: String(row[rowKey]),
59
74
  n: items.length + 1,
60
75
  row,
61
- title: fillTemplate(list.title || '', row),
62
- sub: fillTemplate(list.sub || '', row),
63
- due: fillTemplate(list.due || '', row),
76
+ title: fillTemplate(list.title || '', row, undefined, context),
77
+ sub: list.sub ? firstFilled(list.sub, row, undefined, undefined, context) : '',
78
+ due: fillTemplate(list.due || '', row, undefined, context),
64
79
  pills,
65
80
  });
66
81
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mapList.js","sources":["../../../src/utils/mapList.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\nimport { isBadgeVariant } from './badgeVariants.js'\nimport { fillTemplate } from './fillTemplate.js'\nimport { readPath } from './readPath.js'\n\n/**\n * The numbered list beside an index map (DqZakenKaart): one entry per row\n * that has a place on the map, numbered in list order so the number on the\n * pin and the number in the list are the same, and the rows without one.\n *\n * `spec` is `mapConfig.list`: `{ title, sub?, pills?: [{ path?, text?, map?,\n * colorMap? }], due? }`. `title`, `sub` and `due` are field templates\n * (`\"{title}\"`, `\"{address} · {identifier}\"`, `\"{deadline|date}\"`), already\n * translated by the caller; a pill reads `path` (or shows `text`), maps it\n * through `map`, and takes its tone from `colorMap` by the raw value.\n *\n * @param {Array<object>} rows The rows, in display order.\n * @param {object} spec The list declaration.\n * @param {object} options How to read a row.\n * @param {string} options.rowKey The row's key field.\n * @param {Function} options.located Whether a row has a place on the map.\n * @param {Function} [options.translate] The host's translate, for pill labels.\n * @return {{items: Array<{key: string, n: number, row: object, title: string, sub: string, due: string, pills: Array<{label: string, variant: string}>}>, missing: Array<object>}}\n */\nexport function mapListItems(rows, spec, { rowKey, located, translate = (text) => text }) {\n\tconst items = []\n\tconst missing = []\n\tconst list = spec && typeof spec === 'object' ? spec : {}\n\tfor (const row of Array.isArray(rows) ? rows : []) {\n\t\tif (!located(row)) {\n\t\t\tmissing.push(row)\n\t\t\tcontinue\n\t\t}\n\t\tconst pills = (Array.isArray(list.pills) ? list.pills : [])\n\t\t\t.map((pill) => {\n\t\t\t\tif (!pill || typeof pill !== 'object') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst raw = typeof pill.path === 'string' && pill.path !== '' ? readPath(row, pill.path) : undefined\n\t\t\t\tconst mapped = pill.map && (typeof raw === 'string' || typeof raw === 'number') && Object.hasOwn(pill.map, raw) ? pill.map[raw] : undefined\n\t\t\t\tconst text = mapped ?? pill.text ?? raw\n\t\t\t\tif (typeof text !== 'string' && typeof text !== 'number') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst label = translate(String(text))\n\t\t\t\tif (label === '') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst tone = pill.colorMap && (typeof raw === 'string' || typeof raw === 'number') ? pill.colorMap[raw] : pill.variant\n\t\t\t\treturn { label, variant: isBadgeVariant(tone) ? tone : 'default' }\n\t\t\t})\n\t\t\t.filter(Boolean)\n\t\titems.push({\n\t\t\tkey: String(row[rowKey]),\n\t\t\tn: items.length + 1,\n\t\t\trow,\n\t\t\ttitle: fillTemplate(list.title || '', row),\n\t\t\tsub: fillTemplate(list.sub || '', row),\n\t\t\tdue: fillTemplate(list.due || '', row),\n\t\t\tpills,\n\t\t})\n\t}\n\treturn { items, missing }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mapList.js","sources":["../../../src/utils/mapList.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\nimport { isBadgeVariant } from './badgeVariants.js'\nimport { pillLook } from './detailActionModel.js'\nimport { fillTemplate, firstFilled } from './fillTemplate.js'\nimport { readPath } from './readPath.js'\n\n/**\n * The numbered list beside an index map (DqZakenKaart): one entry per row\n * that has a place on the map, numbered in list order so the number on the\n * pin and the number in the list are the same, and the rows without one.\n *\n * `spec` is `mapConfig.list`: `{ title, sub?, pills?: [{ path?, text?, map?,\n * formatter?, colorMap?, variant?, resolve? }], due? }`. `title`, `sub` and `due` are\n * field templates (`\"{title}\"`, `\"{address} · {identifier}\"`,\n * `\"{deadline|date}\"`), already translated by the caller; `sub` may list\n * several, the first that fills wins. A pill reads `path` (or shows `text`),\n * maps it through `map` or the app formatter `formatter` (a reference's\n * display name), and takes its tone from `colorMap` by the raw value, or,\n * with `resolve`, the tone and icon of the record the value points at\n * (`options.looks`, read by CnIndexPage as for its other pills).\n *\n * @param {Array<object>} rows The rows, in display order.\n * @param {object} spec The list declaration.\n * @param {object} options How to read a row.\n * @param {string} options.rowKey The row's key field.\n * @param {Function} options.located Whether a row has a place on the map.\n * @param {Function} [options.translate] The host's translate, for pill labels.\n * @param {object} [options.formatters] The app's named formatters, for pills and template tokens.\n * @param {object} [options.looks] The referenced records' looks by field and id (`{ [path]: { [id]: { color, icon } } }`).\n * @return {{items: Array<{key: string, n: number, row: object, title: string, sub: string, due: string, pills: Array<{label: string, variant: string}>}>, missing: Array<object>}}\n * @spec openspec/changes/map-list-formatters/specs/map-list-formatters/spec.md#requirement-a-map-list-reads-display-names-through-the-apps-formatters\n * @spec openspec/changes/map-list-formatters/specs/map-list-formatters/spec.md#requirement-a-map-list-pill-takes-its-tone-and-icon-from-the-referenced-record\n */\nexport function mapListItems(rows, spec, { rowKey, located, translate = (text) => text, formatters = null, looks = null }) {\n\tconst context = formatters ? { formatters } : null\n\tconst items = []\n\tconst missing = []\n\tconst list = spec && typeof spec === 'object' ? spec : {}\n\tfor (const row of Array.isArray(rows) ? rows : []) {\n\t\tif (!located(row)) {\n\t\t\tmissing.push(row)\n\t\t\tcontinue\n\t\t}\n\t\tconst pills = (Array.isArray(list.pills) ? list.pills : [])\n\t\t\t.map((pill) => {\n\t\t\t\tif (!pill || typeof pill !== 'object') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst raw = typeof pill.path === 'string' && pill.path !== '' ? readPath(row, pill.path) : undefined\n\t\t\t\tconst formatter = typeof pill.formatter === 'string' && formatters ? formatters[pill.formatter] : null\n\t\t\t\tconst formatted = typeof formatter === 'function' && (typeof raw === 'string' || typeof raw === 'number') && raw !== '' ? formatter(raw, row) : undefined\n\t\t\t\tconst mapped = pill.map && (typeof raw === 'string' || typeof raw === 'number') && Object.hasOwn(pill.map, raw) ? pill.map[raw] : formatted\n\t\t\t\tconst text = mapped ?? pill.text ?? raw\n\t\t\t\tif (typeof text !== 'string' && typeof text !== 'number') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst label = translate(String(text))\n\t\t\t\tif (label === '') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst tone = pill.colorMap && (typeof raw === 'string' || typeof raw === 'number') ? pill.colorMap[raw] : pill.variant\n\t\t\t\tconst fieldLooks = pill.resolve && looks && typeof pill.path === 'string' ? looks[pill.path] : null\n\t\t\t\tconst look = pillLook(fieldLooks && (typeof raw === 'string' || typeof raw === 'number') ? fieldLooks[String(raw)] : null)\n\t\t\t\tconst variant = look.variant || tone\n\t\t\t\treturn { label, variant: isBadgeVariant(variant) ? variant : 'default', ...(look.icon ? { icon: look.icon } : {}) }\n\t\t\t})\n\t\t\t.filter(Boolean)\n\t\titems.push({\n\t\t\tkey: String(row[rowKey]),\n\t\t\tn: items.length + 1,\n\t\t\trow,\n\t\t\ttitle: fillTemplate(list.title || '', row, undefined, context),\n\t\t\tsub: list.sub ? firstFilled(list.sub, row, undefined, undefined, context) : '',\n\t\t\tdue: fillTemplate(list.due || '', row, undefined, context),\n\t\t\tpills,\n\t\t})\n\t}\n\treturn { items, missing }\n}\n"],"names":[],"mappings":"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"}
@@ -65,6 +65,9 @@ function normalizeSavedViewMenu(entries) {
65
65
  }
66
66
  seen.add(`${page}|${id}`);
67
67
  const entry = { id, page, name: e && typeof e.name === 'string' && e.name !== '' ? e.name : id };
68
+ if (typeof e.icon === 'string' && e.icon !== '') {
69
+ entry.icon = e.icon;
70
+ }
68
71
  const badge = normalizeBadge(e.badge);
69
72
  if (badge) {
70
73
  entry.badge = badge;
@@ -88,12 +91,12 @@ function toggleSavedViewMenuEntry(entries, entry) {
88
91
  if (present) {
89
92
  return list.filter((e) => !(e.id === id && e.page === entry.page))
90
93
  }
91
- return normalizeSavedViewMenu([...list, { id, page: entry.page, name: entry.name, badge: entry.badge }])
94
+ return normalizeSavedViewMenu([...list, { id, page: entry.page, name: entry.name, icon: entry.icon, badge: entry.badge }])
92
95
  }
93
96
 
94
97
  /**
95
98
  * Bring one page's entries in line with the views that page can show:
96
- * current names and badges, and no entry for a view that was deleted or is
99
+ * current names, icons and badges, and no entry for a view that was deleted or is
97
100
  * no longer shared with the user. Other pages' entries are left alone. A
98
101
  * view's badge is read from its `menuBadge` (see `withSavedViewMenu`).
99
102
  *
@@ -111,9 +114,10 @@ function syncSavedViewMenu(entries, page, views) {
111
114
  return e
112
115
  }
113
116
  const view = byId.get(e.id);
114
- const { badge, ...rest } = e;
117
+ const { badge, icon, ...rest } = e;
115
118
  const next = normalizeBadge(view.menuBadge);
116
- return normalizeSavedViewMenu([{ ...rest, name: view.name || e.name, ...(next ? { badge: next } : {}) }])[0]
119
+ const nextIcon = typeof view.icon === 'string' && view.icon !== '' ? view.icon : icon;
120
+ return normalizeSavedViewMenu([{ ...rest, name: view.name || e.name, ...(nextIcon ? { icon: nextIcon } : {}), ...(next ? { badge: next } : {}) }])[0]
117
121
  })
118
122
  }
119
123
 
@@ -128,6 +132,9 @@ const SAVED_VIEW_MENU_EMPTY = 'saved-views-empty';
128
132
  * same place (Ruben's design rule, 19 Oct); it replaces app-specific groups
129
133
  * such as dossiq's "My case types".
130
134
  *
135
+ * An entry shows its view's `icon` (a registered icon name; a case type's
136
+ * own icon, say), else the filter icon.
137
+ *
131
138
  * An entry with a badge carries it as the menu entry's `count` (and
132
139
  * `counterVariant`), which CnAppRoot fetches like any filtered menu count.
133
140
  *
@@ -155,7 +162,7 @@ function withSavedViewMenu(menu, entries, options = {}) {
155
162
  label: e.name,
156
163
  route: e.page,
157
164
  query: { savedView: e.id },
158
- icon: 'FilterOutline',
165
+ icon: e.icon || 'FilterOutline',
159
166
  ...(e.badge || {}),
160
167
  }))
161
168
  : [{ id: SAVED_VIEW_MENU_EMPTY, type: 'hint', label: translate('nextcloud-vue', 'Add a view to the menu from Filters') }]),
@@ -1 +1 @@
1
- {"version":3,"file":"savedViewMenu.js","sources":["../../../src/utils/savedViewMenu.js"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 Conduction B.V.\n// SPDX-License-Identifier: EUPL-1.2\n\nimport { translate as t } from '@nextcloud/l10n'\n\n/**\n * Saved views a user added to the app menu.\n *\n * Per user, in the app's preferences (`cn_saved_view_menu`), as a list of\n * `{ id, page, name, badge? }`: the view id (or `default:<slug>`), the route\n * name of the index page it filters, the name to show, and the count the\n * entry shows when the view's `overrides.badge` asks for one. The view itself is not\n * touched, so a view shared with the user can be added like an own one, and\n * removing it from the menu never deletes it.\n *\n * A menu entry links to its page with `?savedView=<id>`; the page applies\n * that view. The entries form the \"My views\" group at the bottom of the menu.\n *\n * @spec openspec/changes/saved-views-manage-and-defaults/tasks.md#task-7\n */\n\n/** The preference key the list is stored under. */\nexport const SAVED_VIEW_MENU_KEY = 'cn_saved_view_menu'\n\n/** The id of the \"My views\" caption. */\nexport const SAVED_VIEW_MENU_CAPTION = 'saved-views-caption'\n\n/** The id prefix of a menu entry made from a saved view. */\nexport const SAVED_VIEW_MENU_PREFIX = 'saved-view-'\n\n/**\n * A menu badge as stored on an entry: `{ count, counterVariant? }`, where `count` is\n * a menu entry `count` (a number or `{ register, schema, filter? }`).\n *\n * @param {unknown} badge The stored value.\n * @return {{count: (number|object), counterVariant?: string}|null} The badge, or null.\n */\nfunction normalizeBadge(badge) {\n\tif (!badge || typeof badge !== 'object') {\n\t\treturn null\n\t}\n\tconst count = badge.count\n\tconst isSource = count && typeof count === 'object' && typeof count.register === 'string' && typeof count.schema === 'string'\n\tif (!isSource && !(typeof count === 'number' && count >= 0)) {\n\t\treturn null\n\t}\n\treturn badge.counterVariant === 'attention' ? { count, counterVariant: 'attention' } : { count }\n}\n\n/**\n * The stored list, cleaned: entries with an id and a page, no repeats.\n *\n * @param {unknown} entries The stored value.\n * @return {Array<{id: string, page: string, name: string, badge?: object}>} The list.\n */\nexport function normalizeSavedViewMenu(entries) {\n\tconst seen = new Set()\n\tconst out = []\n\tfor (const e of Array.isArray(entries) ? entries : []) {\n\t\tconst id = e && (typeof e.id === 'string' || typeof e.id === 'number') ? String(e.id) : ''\n\t\tconst page = e && typeof e.page === 'string' ? e.page : ''\n\t\tif (id === '' || page === '' || seen.has(`${page}|${id}`)) {\n\t\t\tcontinue\n\t\t}\n\t\tseen.add(`${page}|${id}`)\n\t\tconst entry = { id, page, name: e && typeof e.name === 'string' && e.name !== '' ? e.name : id }\n\t\tconst badge = normalizeBadge(e.badge)\n\t\tif (badge) {\n\t\t\tentry.badge = badge\n\t\t}\n\t\tout.push(entry)\n\t}\n\treturn out\n}\n\n/**\n * Add an entry, or remove it when it is already there.\n *\n * @param {Array<object>} entries The current list.\n * @param {{id: string, page: string, name: string, badge?: object}} entry The view.\n * @return {Array<{id: string, page: string, name: string, badge?: object}>} The new list.\n */\nexport function toggleSavedViewMenuEntry(entries, entry) {\n\tconst list = normalizeSavedViewMenu(entries)\n\tconst id = String(entry.id)\n\tconst present = list.some((e) => e.id === id && e.page === entry.page)\n\tif (present) {\n\t\treturn list.filter((e) => !(e.id === id && e.page === entry.page))\n\t}\n\treturn normalizeSavedViewMenu([...list, { id, page: entry.page, name: entry.name, badge: entry.badge }])\n}\n\n/**\n * Bring one page's entries in line with the views that page can show:\n * current names and badges, and no entry for a view that was deleted or is\n * no longer shared with the user. Other pages' entries are left alone. A\n * view's badge is read from its `menuBadge` (see `withSavedViewMenu`).\n *\n * @param {Array<object>} entries The current list.\n * @param {string} page The page's route name.\n * @param {Array<object>} views The views the page lists (defaults included).\n * @return {Array<{id: string, page: string, name: string}>} The new list.\n */\nexport function syncSavedViewMenu(entries, page, views) {\n\tconst byId = new Map((Array.isArray(views) ? views : []).map((v) => [String(v.id || v.slug), v]))\n\treturn normalizeSavedViewMenu(entries)\n\t\t.filter((e) => e.page !== page || byId.has(e.id))\n\t\t.map((e) => {\n\t\t\tif (e.page !== page) {\n\t\t\t\treturn e\n\t\t\t}\n\t\t\tconst view = byId.get(e.id)\n\t\t\tconst { badge, ...rest } = e\n\t\t\tconst next = normalizeBadge(view.menuBadge)\n\t\t\treturn normalizeSavedViewMenu([{ ...rest, name: view.name || e.name, ...(next ? { badge: next } : {}) }])[0]\n\t\t})\n}\n\n/** The id of the line the \"My views\" group shows while it is empty. */\nexport const SAVED_VIEW_MENU_EMPTY = 'saved-views-empty'\n\n/**\n * The manifest menu with the user's saved views in it, as the standard \"My\n * views\" group at the bottom of the menu: a caption and one entry per view,\n * after every main entry and so just above the footer entries (Help,\n * Advanced), which CnAppNav draws apart. Every app gets the same group in the\n * same place (Ruben's design rule, 19 Oct); it replaces app-specific groups\n * such as dossiq's \"My case types\".\n *\n * An entry with a badge carries it as the menu entry's `count` (and\n * `counterVariant`), which CnAppRoot fetches like any filtered menu count.\n *\n * With `showEmpty`, an app that offers saved views shows the group also while\n * the user pinned none, with one line saying how to add one, so the group can\n * be found.\n *\n * @param {Array<object>} menu The manifest's `menu`.\n * @param {Array<object>} entries The user's list.\n * @param {{showEmpty?: boolean}} [options] Options.\n * @return {Array<object>} A new menu, in manifest order; the given one when there is nothing to add.\n * @spec openspec/changes/my-views-menu-section/specs/app-nav/spec.md#requirement-every-app-menu-ends-with-my-views\n */\nexport function withSavedViewMenu(menu, entries, options = {}) {\n\tconst list = normalizeSavedViewMenu(entries)\n\tif (!Array.isArray(menu) || (list.length === 0 && options.showEmpty !== true)) {\n\t\treturn menu\n\t}\n\tconst sorted = menu.map((item, index) => ({ item, index })).sort((a, b) => byOrder(a.item, b.item) || a.index - b.index).map(({ item }) => item)\n\tconst group = [\n\t\t{ id: SAVED_VIEW_MENU_CAPTION, type: 'caption', label: t('nextcloud-vue', 'My views') },\n\t\t...(list.length > 0\n\t\t\t? list.map((e) => ({\n\t\t\t\t\tid: `${SAVED_VIEW_MENU_PREFIX}${e.id}`,\n\t\t\t\t\tlabel: e.name,\n\t\t\t\t\troute: e.page,\n\t\t\t\t\tquery: { savedView: e.id },\n\t\t\t\t\ticon: 'FilterOutline',\n\t\t\t\t\t...(e.badge || {}),\n\t\t\t\t}))\n\t\t\t: [{ id: SAVED_VIEW_MENU_EMPTY, type: 'hint', label: t('nextcloud-vue', 'Add a view to the menu from Filters') }]),\n\t]\n\t// Orders after the last ordered entry, so CnAppNav's own sort keeps the\n\t// group at the bottom.\n\tconst orders = sorted.map((item) => item && item.order).filter((order) => typeof order === 'number')\n\tif (orders.length > 0) {\n\t\tconst start = Math.max(...orders)\n\t\tgroup.forEach((item, k) => {\n\t\t\titem.order = start + k + 1\n\t\t})\n\t}\n\treturn [...sorted, ...group]\n}\n\n/**\n * Manifest menu order: entries with an `order` first, by it; the rest keep their place.\n *\n * @param {object} a First entry.\n * @param {object} b Second entry.\n * @return {number}\n */\nfunction byOrder(a, b) {\n\tconst aHas = typeof a?.order === 'number'\n\tconst bHas = typeof b?.order === 'number'\n\tif (aHas !== bHas) {\n\t\treturn aHas ? -1 : 1\n\t}\n\treturn aHas ? a.order - b.order : 0\n}\n"],"names":["t"],"mappings":"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"}
1
+ {"version":3,"file":"savedViewMenu.js","sources":["../../../src/utils/savedViewMenu.js"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 Conduction B.V.\n// SPDX-License-Identifier: EUPL-1.2\n\nimport { translate as t } from '@nextcloud/l10n'\n\n/**\n * Saved views a user added to the app menu.\n *\n * Per user, in the app's preferences (`cn_saved_view_menu`), as a list of\n * `{ id, page, name, badge? }`: the view id (or `default:<slug>`), the route\n * name of the index page it filters, the name to show, and the count the\n * entry shows when the view's `overrides.badge` asks for one. The view itself is not\n * touched, so a view shared with the user can be added like an own one, and\n * removing it from the menu never deletes it.\n *\n * A menu entry links to its page with `?savedView=<id>`; the page applies\n * that view. The entries form the \"My views\" group at the bottom of the menu.\n *\n * @spec openspec/changes/saved-views-manage-and-defaults/tasks.md#task-7\n */\n\n/** The preference key the list is stored under. */\nexport const SAVED_VIEW_MENU_KEY = 'cn_saved_view_menu'\n\n/** The id of the \"My views\" caption. */\nexport const SAVED_VIEW_MENU_CAPTION = 'saved-views-caption'\n\n/** The id prefix of a menu entry made from a saved view. */\nexport const SAVED_VIEW_MENU_PREFIX = 'saved-view-'\n\n/**\n * A menu badge as stored on an entry: `{ count, counterVariant? }`, where `count` is\n * a menu entry `count` (a number or `{ register, schema, filter? }`).\n *\n * @param {unknown} badge The stored value.\n * @return {{count: (number|object), counterVariant?: string}|null} The badge, or null.\n */\nfunction normalizeBadge(badge) {\n\tif (!badge || typeof badge !== 'object') {\n\t\treturn null\n\t}\n\tconst count = badge.count\n\tconst isSource = count && typeof count === 'object' && typeof count.register === 'string' && typeof count.schema === 'string'\n\tif (!isSource && !(typeof count === 'number' && count >= 0)) {\n\t\treturn null\n\t}\n\treturn badge.counterVariant === 'attention' ? { count, counterVariant: 'attention' } : { count }\n}\n\n/**\n * The stored list, cleaned: entries with an id and a page, no repeats.\n *\n * @param {unknown} entries The stored value.\n * @return {Array<{id: string, page: string, name: string, badge?: object}>} The list.\n */\nexport function normalizeSavedViewMenu(entries) {\n\tconst seen = new Set()\n\tconst out = []\n\tfor (const e of Array.isArray(entries) ? entries : []) {\n\t\tconst id = e && (typeof e.id === 'string' || typeof e.id === 'number') ? String(e.id) : ''\n\t\tconst page = e && typeof e.page === 'string' ? e.page : ''\n\t\tif (id === '' || page === '' || seen.has(`${page}|${id}`)) {\n\t\t\tcontinue\n\t\t}\n\t\tseen.add(`${page}|${id}`)\n\t\tconst entry = { id, page, name: e && typeof e.name === 'string' && e.name !== '' ? e.name : id }\n\t\tif (typeof e.icon === 'string' && e.icon !== '') {\n\t\t\tentry.icon = e.icon\n\t\t}\n\t\tconst badge = normalizeBadge(e.badge)\n\t\tif (badge) {\n\t\t\tentry.badge = badge\n\t\t}\n\t\tout.push(entry)\n\t}\n\treturn out\n}\n\n/**\n * Add an entry, or remove it when it is already there.\n *\n * @param {Array<object>} entries The current list.\n * @param {{id: string, page: string, name: string, badge?: object}} entry The view.\n * @return {Array<{id: string, page: string, name: string, badge?: object}>} The new list.\n */\nexport function toggleSavedViewMenuEntry(entries, entry) {\n\tconst list = normalizeSavedViewMenu(entries)\n\tconst id = String(entry.id)\n\tconst present = list.some((e) => e.id === id && e.page === entry.page)\n\tif (present) {\n\t\treturn list.filter((e) => !(e.id === id && e.page === entry.page))\n\t}\n\treturn normalizeSavedViewMenu([...list, { id, page: entry.page, name: entry.name, icon: entry.icon, badge: entry.badge }])\n}\n\n/**\n * Bring one page's entries in line with the views that page can show:\n * current names, icons and badges, and no entry for a view that was deleted or is\n * no longer shared with the user. Other pages' entries are left alone. A\n * view's badge is read from its `menuBadge` (see `withSavedViewMenu`).\n *\n * @param {Array<object>} entries The current list.\n * @param {string} page The page's route name.\n * @param {Array<object>} views The views the page lists (defaults included).\n * @return {Array<{id: string, page: string, name: string}>} The new list.\n */\nexport function syncSavedViewMenu(entries, page, views) {\n\tconst byId = new Map((Array.isArray(views) ? views : []).map((v) => [String(v.id || v.slug), v]))\n\treturn normalizeSavedViewMenu(entries)\n\t\t.filter((e) => e.page !== page || byId.has(e.id))\n\t\t.map((e) => {\n\t\t\tif (e.page !== page) {\n\t\t\t\treturn e\n\t\t\t}\n\t\t\tconst view = byId.get(e.id)\n\t\t\tconst { badge, icon, ...rest } = e\n\t\t\tconst next = normalizeBadge(view.menuBadge)\n\t\t\tconst nextIcon = typeof view.icon === 'string' && view.icon !== '' ? view.icon : icon\n\t\t\treturn normalizeSavedViewMenu([{ ...rest, name: view.name || e.name, ...(nextIcon ? { icon: nextIcon } : {}), ...(next ? { badge: next } : {}) }])[0]\n\t\t})\n}\n\n/** The id of the line the \"My views\" group shows while it is empty. */\nexport const SAVED_VIEW_MENU_EMPTY = 'saved-views-empty'\n\n/**\n * The manifest menu with the user's saved views in it, as the standard \"My\n * views\" group at the bottom of the menu: a caption and one entry per view,\n * after every main entry and so just above the footer entries (Help,\n * Advanced), which CnAppNav draws apart. Every app gets the same group in the\n * same place (Ruben's design rule, 19 Oct); it replaces app-specific groups\n * such as dossiq's \"My case types\".\n *\n * An entry shows its view's `icon` (a registered icon name; a case type's\n * own icon, say), else the filter icon.\n *\n * An entry with a badge carries it as the menu entry's `count` (and\n * `counterVariant`), which CnAppRoot fetches like any filtered menu count.\n *\n * With `showEmpty`, an app that offers saved views shows the group also while\n * the user pinned none, with one line saying how to add one, so the group can\n * be found.\n *\n * @param {Array<object>} menu The manifest's `menu`.\n * @param {Array<object>} entries The user's list.\n * @param {{showEmpty?: boolean}} [options] Options.\n * @return {Array<object>} A new menu, in manifest order; the given one when there is nothing to add.\n * @spec openspec/changes/my-views-menu-section/specs/app-nav/spec.md#requirement-every-app-menu-ends-with-my-views\n */\nexport function withSavedViewMenu(menu, entries, options = {}) {\n\tconst list = normalizeSavedViewMenu(entries)\n\tif (!Array.isArray(menu) || (list.length === 0 && options.showEmpty !== true)) {\n\t\treturn menu\n\t}\n\tconst sorted = menu.map((item, index) => ({ item, index })).sort((a, b) => byOrder(a.item, b.item) || a.index - b.index).map(({ item }) => item)\n\tconst group = [\n\t\t{ id: SAVED_VIEW_MENU_CAPTION, type: 'caption', label: t('nextcloud-vue', 'My views') },\n\t\t...(list.length > 0\n\t\t\t? list.map((e) => ({\n\t\t\t\t\tid: `${SAVED_VIEW_MENU_PREFIX}${e.id}`,\n\t\t\t\t\tlabel: e.name,\n\t\t\t\t\troute: e.page,\n\t\t\t\t\tquery: { savedView: e.id },\n\t\t\t\t\ticon: e.icon || 'FilterOutline',\n\t\t\t\t\t...(e.badge || {}),\n\t\t\t\t}))\n\t\t\t: [{ id: SAVED_VIEW_MENU_EMPTY, type: 'hint', label: t('nextcloud-vue', 'Add a view to the menu from Filters') }]),\n\t]\n\t// Orders after the last ordered entry, so CnAppNav's own sort keeps the\n\t// group at the bottom.\n\tconst orders = sorted.map((item) => item && item.order).filter((order) => typeof order === 'number')\n\tif (orders.length > 0) {\n\t\tconst start = Math.max(...orders)\n\t\tgroup.forEach((item, k) => {\n\t\t\titem.order = start + k + 1\n\t\t})\n\t}\n\treturn [...sorted, ...group]\n}\n\n/**\n * Manifest menu order: entries with an `order` first, by it; the rest keep their place.\n *\n * @param {object} a First entry.\n * @param {object} b Second entry.\n * @return {number}\n */\nfunction byOrder(a, b) {\n\tconst aHas = typeof a?.order === 'number'\n\tconst bHas = typeof b?.order === 'number'\n\tif (aHas !== bHas) {\n\t\treturn aHas ? -1 : 1\n\t}\n\treturn aHas ? a.order - b.order : 0\n}\n"],"names":["t"],"mappings":"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"}