@conduction/nextcloud-vue 2.96.0 → 2.97.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.
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +33 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +383 -359
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +17 -3
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +61 -43
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +159 -18
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js +9 -7
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +48 -3
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +46 -12
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +144 -0
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +488 -399
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +8 -2
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +301 -18
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +29 -7
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +2 -2
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +2 -2
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useNamedSource.js +1 -1
- package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +13 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +5 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/composables/indexSources.js +182 -3
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/l10n/en.json.js +7 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +7 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +301 -177
- package/dist/esm/utils/queryPresets.js +13 -2
- package/dist/esm/utils/queryPresets.js.map +1 -1
- package/dist/esm/utils/scopeListLayout.js +37 -1
- package/dist/esm/utils/scopeListLayout.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +3009 -1907
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +301 -177
- package/l10n/en.json +7 -1
- package/l10n/nl.json +7 -1
- package/package.json +1 -1
- package/src/components/CnCellRenderer/CnCellRenderer.vue +57 -1
- package/src/components/CnConversationThread/CnConversationThread.md +12 -0
- package/src/components/CnConversationThread/CnConversationThread.vue +30 -4
- package/src/components/CnConversationThread/CnConversationWidget.vue +167 -24
- package/src/components/CnFactsWidget/CnFactsWidget.vue +114 -3
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +508 -236
- package/src/components/CnFilesBrowser/filesBrowserColumns.js +8 -2
- package/src/components/CnIndexPage/CnIndexPage.vue +324 -24
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +13 -4
- package/src/components/CnIndexPage/useNamedSource.js +1 -1
- package/src/components/CnObjectSidebar/CnFilesTab.vue +17 -0
- package/src/composables/indexSources.js +182 -3
- package/src/schemas/app-manifest-v2.schema.json +11 -4
- package/src/utils/queryPresets.js +13 -2
- package/src/utils/scopeListLayout.js +37 -1
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -144,7 +144,8 @@ function attributePropertiesFor(columns) {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
|
-
* The value one column shows for one node.
|
|
147
|
+
* The value one column shows for one node. A `row` column's key may be a
|
|
148
|
+
* dotted path into the host's record.
|
|
148
149
|
*
|
|
149
150
|
* @param {object} column A resolved column.
|
|
150
151
|
* @param {object} node The node.
|
|
@@ -169,7 +170,12 @@ function fileColumnValue(column, node, rowData = null) {
|
|
|
169
170
|
return undefined
|
|
170
171
|
}
|
|
171
172
|
const entry = byId[node.fileid] ?? byId[String(node.fileid)];
|
|
172
|
-
|
|
173
|
+
if (!entry) {
|
|
174
|
+
return undefined
|
|
175
|
+
}
|
|
176
|
+
// A key the record holds as written wins; otherwise a dotted key reads
|
|
177
|
+
// a nested field (`scanVerdict.state`, `integrity.value`).
|
|
178
|
+
return Object.hasOwn(entry, column.key) ? entry[column.key] : atPath(entry, column.key)
|
|
173
179
|
}
|
|
174
180
|
return atPath(node, column.nodeKey || column.key)
|
|
175
181
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesBrowserColumns.js","sources":["../../../../src/components/CnFilesBrowser/filesBrowserColumns.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\n/**\n * The built-in columns a host may name by string, and what each one reads.\n *\n * `nodeKey` is the property on the @nextcloud/files node. `sortKey` is what\n * `sortNodes` understands, so a built-in sorts the way the Files app sorts;\n * a built-in with no `sortKey` sorts client-side like a declared column.\n *\n * @type {object}\n */\nexport const BUILT_IN_FILE_COLUMNS = {\n\tname: { key: 'name', source: 'node', nodeKey: 'basename', sortKey: 'basename', sortable: true },\n\tsize: { key: 'size', source: 'node', nodeKey: 'size', sortKey: 'size', sortable: true },\n\tmodified: { key: 'modified', source: 'node', nodeKey: 'mtime', sortKey: 'mtime', sortable: true },\n\towner: { key: 'owner', source: 'node', nodeKey: 'owner', sortable: true },\n\ttype: { key: 'type', source: 'node', nodeKey: 'mime', sortable: true },\n\ttags: { key: 'tags', source: 'node', nodeKey: 'attributes.system-tags', sortable: false },\n}\n\n/**\n * The columns a browser shows when its host declares none: what the table has\n * always shown, in the order it has always shown it.\n *\n * @type {string[]}\n */\nexport const DEFAULT_FILE_COLUMNS = ['name', 'size', 'modified']\n\n/**\n * Read a dotted path off a node without throwing on a missing segment.\n *\n * @param {object} node The node.\n * @param {string} path A dotted path.\n * @return {(Array|object|string|number|boolean|undefined)} The value.\n */\nfunction atPath(node, path) {\n\tif (!node || typeof path !== 'string' || path === '') {\n\t\treturn undefined\n\t}\n\treturn path.split('.').reduce((acc, key) => (acc === null || acc === undefined ? undefined : acc[key]), node)\n}\n\n/**\n * Whether a string names a built-in column.\n *\n * @param {string} name The candidate name.\n * @return {boolean} True when it is a built-in.\n */\nexport function isBuiltInFileColumn(name) {\n\treturn typeof name === 'string' && Object.hasOwn(BUILT_IN_FILE_COLUMNS, name)\n}\n\n/**\n * Resolve a host's `columns` declaration into column objects.\n *\n * A string names a built-in. An object declares its own: `key`, `label`,\n * `source` (`node`, `attribute` or `row`), `attribute`, `formatter` and\n * `sortable`. An entry naming an unknown built-in is dropped here and refused\n * by the manifest validator, so an author hears about it at authoring time\n * rather than finding a column missing at runtime.\n *\n * @param {(Array|null|undefined)} columns The declaration, or nothing.\n * @return {Array<object>} The resolved columns, in the declared order.\n */\nexport function normaliseFileColumns(columns) {\n\tconst declared = Array.isArray(columns) && columns.length > 0 ? columns : DEFAULT_FILE_COLUMNS\n\tconst out = []\n\tdeclared.forEach((entry) => {\n\t\tif (typeof entry === 'string') {\n\t\t\tif (isBuiltInFileColumn(entry)) {\n\t\t\t\tout.push({ ...BUILT_IN_FILE_COLUMNS[entry], builtIn: true })\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif (!entry || typeof entry !== 'object' || typeof entry.key !== 'string' || entry.key === '') {\n\t\t\treturn\n\t\t}\n\t\tif (isBuiltInFileColumn(entry.key) && entry.source === undefined) {\n\t\t\tout.push({ ...BUILT_IN_FILE_COLUMNS[entry.key], ...entry, builtIn: true })\n\t\t\treturn\n\t\t}\n\t\tout.push({\n\t\t\tsource: 'node',\n\t\t\tsortable: false,\n\t\t\t...entry,\n\t\t\tbuiltIn: false,\n\t\t})\n\t})\n\treturn out\n}\n\n/**\n * The columns a browser renders: the host's declaration, narrowed by what this\n * user chose to see.\n *\n * Which side wins on what:\n *\n * - The HOST decides MEMBERSHIP. The result is built by walking the\n * declaration, so a column exists only because the host declared it, in the\n * order the host declared it.\n * - The USER decides VISIBILITY, and only downward. A stored preference can\n * hide a declared column; it can never add one. A key in the stored list\n * that the host no longer declares is passed over, so unticking a column\n * last month cannot bring it back once the host removed it, and neither can\n * a preference written while a different column set was declared.\n *\n * A stored value of `null` is a user who has never chosen, which shows\n * everything. An empty list is a user who hid everything, which is a choice\n * and is honoured.\n *\n * @param {Array<object>} declared The resolved declared columns.\n * @param {(string[]|null|undefined)} visibleKeys The user's stored visible keys.\n * @return {Array<object>} The columns to render.\n */\nexport function visibleFileColumns(declared, visibleKeys) {\n\tconst columns = Array.isArray(declared) ? declared : []\n\tif (!Array.isArray(visibleKeys)) {\n\t\treturn columns\n\t}\n\tconst wanted = new Set(visibleKeys)\n\treturn columns.filter((column) => wanted.has(column.key))\n}\n\n/**\n * The DAV properties a declaration needs in the PROPFIND, so an `attribute`\n * column's value is there when the folder lists rather than one request later.\n *\n * @param {Array<object>} columns The resolved columns.\n * @return {string[]} The property names, deduplicated, in declaration order.\n */\nexport function attributePropertiesFor(columns) {\n\tconst out = []\n\t;(Array.isArray(columns) ? columns : []).forEach((column) => {\n\t\tif (column && column.source === 'attribute' && typeof column.attribute === 'string' && column.attribute !== '') {\n\t\t\tif (!out.includes(column.attribute)) {\n\t\t\t\tout.push(column.attribute)\n\t\t\t}\n\t\t}\n\t})\n\treturn out\n}\n\n/**\n * The value one column shows for one node.\n *\n * @param {object} column A resolved column.\n * @param {object} node The node.\n * @param {object} [rowData] The host's per-file data, keyed by file id.\n * @return {(Array|object|string|number|boolean|undefined)} The cell value.\n */\nexport function fileColumnValue(column, node, rowData = null) {\n\tif (!column || !node) {\n\t\treturn undefined\n\t}\n\tif (column.source === 'attribute') {\n\t\t// Read the key directly, NOT through the dotted-path reader: a DAV\n\t\t// property name is `{http://owncloud.org/ns}av-status`, and splitting\n\t\t// that on dots looks for a property called `org/ns}av-status` inside\n\t\t// one called `owncloud`, which is undefined every time.\n\t\tconst attributes = node.attributes\n\t\treturn attributes && typeof attributes === 'object' ? attributes[column.attribute] : undefined\n\t}\n\tif (column.source === 'row') {\n\t\tconst byId = rowData && typeof rowData === 'object' ? rowData : null\n\t\tif (!byId) {\n\t\t\treturn undefined\n\t\t}\n\t\tconst entry = byId[node.fileid] ?? byId[String(node.fileid)]\n\t\treturn entry ? entry[column.key] : undefined\n\t}\n\treturn atPath(node, column.nodeKey || column.key)\n}\n\n/**\n * Sort nodes on a declared column's resolved value, within the listed folder.\n *\n * Folders stay first, the way the Files app lists them and the way the\n * built-in sorts already do, so switching to a declared column does not\n * reshuffle the folders into the files.\n *\n * @param {Array<object>} nodes The nodes.\n * @param {object} column The resolved column.\n * @param {boolean} ascending Sort direction.\n * @param {object} [rowData] The host's per-file data.\n * @param {(node: object) => boolean} [isFolder] Whether a node is a folder.\n * @return {Array<object>} A new, sorted array.\n */\nexport function sortNodesByColumn(nodes, column, ascending, rowData = null, isFolder = () => false) {\n\tconst list = Array.isArray(nodes) ? [...nodes] : []\n\tif (!column) {\n\t\treturn list\n\t}\n\tconst direction = ascending ? 1 : -1\n\treturn list.sort((a, b) => {\n\t\tconst folderA = isFolder(a)\n\t\tconst folderB = isFolder(b)\n\t\tif (folderA !== folderB) {\n\t\t\treturn folderA ? -1 : 1\n\t\t}\n\t\tconst left = fileColumnValue(column, a, rowData)\n\t\tconst right = fileColumnValue(column, b, rowData)\n\t\tconst leftMissing = left === undefined || left === null || left === ''\n\t\tconst rightMissing = right === undefined || right === null || right === ''\n\t\t// A value nobody filled in sorts last in BOTH directions. Letting it\n\t\t// ride to the top on a descending sort hides the rows that do carry\n\t\t// one, which is the opposite of what sorting was asked for.\n\t\tif (leftMissing !== rightMissing) {\n\t\t\treturn leftMissing ? 1 : -1\n\t\t}\n\t\tif (leftMissing && rightMissing) {\n\t\t\treturn 0\n\t\t}\n\t\tif (typeof left === 'number' && typeof right === 'number') {\n\t\t\treturn (left - right) * direction\n\t\t}\n\t\treturn String(left).localeCompare(String(right)) * direction\n\t})\n}\n\n/**\n * Whether a cell value counts as filled in.\n *\n * @param {(Array|object|string|number|boolean|null|undefined)} value The value.\n * @return {boolean} False for nothing, an empty string or an empty list.\n */\nfunction isFilled(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn false\n\t}\n\treturn !(Array.isArray(value) && value.length === 0)\n}\n\n/**\n * The single value a node is grouped under, as a string key: a list groups\n * under its first entry, an object under its label, title, name or id.\n *\n * @param {(Array|object|string|number|boolean|null|undefined)} value The cell value.\n * @return {(string|null)} The group key, or null when nothing is filled in.\n */\nfunction groupKeyOf(value) {\n\tconst first = Array.isArray(value) ? value[0] : value\n\tif (!isFilled(first)) {\n\t\treturn null\n\t}\n\tif (typeof first === 'object') {\n\t\tconst label = first.label ?? first.title ?? first.name ?? first.id\n\t\treturn isFilled(label) ? String(label) : null\n\t}\n\treturn String(first)\n}\n\n/**\n * The values a node carries in a column, as strings, for counting and\n * filtering: every entry of a list, the one value otherwise.\n *\n * @param {object} column A resolved column.\n * @param {object} node The node.\n * @param {object} [rowData] The host's per-file data.\n * @return {string[]} The values, deduplicated.\n */\nfunction facetValuesOf(column, node, rowData) {\n\tconst value = fileColumnValue(column, node, rowData)\n\tconst entries = Array.isArray(value) ? value : [value]\n\tconst out = []\n\tentries.forEach((entry) => {\n\t\tconst key = groupKeyOf(entry)\n\t\tif (key !== null && !out.includes(key)) {\n\t\t\tout.push(key)\n\t\t}\n\t})\n\treturn out\n}\n\n/**\n * Group nodes on a column's value, in the order each value is first met.\n * Nodes with no value come last, in one group whose value is null.\n *\n * @param {Array<object>} nodes The nodes, already in display order.\n * @param {object} column The resolved column.\n * @param {object} [rowData] The host's per-file data.\n * @return {Array<{value: (string|null), nodes: Array<object>}>} The groups.\n */\nexport function groupNodes(nodes, column, rowData = null) {\n\tconst groups = []\n\tconst byKey = new Map()\n\tconst empty = { value: null, nodes: [] }\n\t;(Array.isArray(nodes) ? nodes : []).forEach((node) => {\n\t\tconst key = column ? groupKeyOf(fileColumnValue(column, node, rowData)) : null\n\t\tif (key === null) {\n\t\t\tempty.nodes.push(node)\n\t\t\treturn\n\t\t}\n\t\tif (!byKey.has(key)) {\n\t\t\tconst group = { value: key, nodes: [] }\n\t\t\tbyKey.set(key, group)\n\t\t\tgroups.push(group)\n\t\t}\n\t\tbyKey.get(key).nodes.push(node)\n\t})\n\tif (empty.nodes.length > 0) {\n\t\tgroups.push(empty)\n\t}\n\treturn groups\n}\n\n/**\n * The values in use in a column across the nodes, with how many nodes carry\n * each; a list counts once per entry. Most used first, then in first-seen\n * order.\n *\n * @param {Array<object>} nodes The nodes.\n * @param {object} column The resolved column.\n * @param {object} [rowData] The host's per-file data.\n * @return {Array<{value: string, count: number}>} The values and counts.\n */\nexport function facetCounts(nodes, column, rowData = null) {\n\tconst counts = []\n\tconst byValue = new Map()\n\t;(Array.isArray(nodes) ? nodes : []).forEach((node) => {\n\t\tfacetValuesOf(column, node, rowData).forEach((value) => {\n\t\t\tif (!byValue.has(value)) {\n\t\t\t\tconst entry = { value, count: 0 }\n\t\t\t\tbyValue.set(value, entry)\n\t\t\t\tcounts.push(entry)\n\t\t\t}\n\t\t\tbyValue.get(value).count += 1\n\t\t})\n\t})\n\treturn counts\n\t\t.map((entry, index) => ({ entry, index }))\n\t\t.sort((a, b) => (b.entry.count - a.entry.count) || (a.index - b.index))\n\t\t.map(({ entry }) => entry)\n}\n\n/**\n * Whether a node passes the chosen facet values: within one facet any chosen\n * value will do, and every facet with a choice must pass. A facet with\n * nothing chosen does not filter.\n *\n * @param {object} node The node.\n * @param {object} selections Chosen values by column key.\n * @param {object} columnsByKey Resolved columns by key.\n * @param {object} [rowData] The host's per-file data.\n * @return {boolean} True when the node stays in the list.\n */\nexport function nodeMatchesFacets(node, selections, columnsByKey, rowData = null) {\n\treturn Object.entries(selections || {}).every(([key, chosen]) => {\n\t\tif (!Array.isArray(chosen) || chosen.length === 0) {\n\t\t\treturn true\n\t\t}\n\t\tconst column = columnsByKey && columnsByKey[key]\n\t\tif (!column) {\n\t\t\treturn true\n\t\t}\n\t\tconst values = facetValuesOf(column, node, rowData)\n\t\treturn chosen.some((value) => values.includes(value))\n\t})\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"filesBrowserColumns.js","sources":["../../../../src/components/CnFilesBrowser/filesBrowserColumns.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\n/**\n * The built-in columns a host may name by string, and what each one reads.\n *\n * `nodeKey` is the property on the @nextcloud/files node. `sortKey` is what\n * `sortNodes` understands, so a built-in sorts the way the Files app sorts;\n * a built-in with no `sortKey` sorts client-side like a declared column.\n *\n * @type {object}\n */\nexport const BUILT_IN_FILE_COLUMNS = {\n\tname: { key: 'name', source: 'node', nodeKey: 'basename', sortKey: 'basename', sortable: true },\n\tsize: { key: 'size', source: 'node', nodeKey: 'size', sortKey: 'size', sortable: true },\n\tmodified: { key: 'modified', source: 'node', nodeKey: 'mtime', sortKey: 'mtime', sortable: true },\n\towner: { key: 'owner', source: 'node', nodeKey: 'owner', sortable: true },\n\ttype: { key: 'type', source: 'node', nodeKey: 'mime', sortable: true },\n\ttags: { key: 'tags', source: 'node', nodeKey: 'attributes.system-tags', sortable: false },\n}\n\n/**\n * The columns a browser shows when its host declares none: what the table has\n * always shown, in the order it has always shown it.\n *\n * @type {string[]}\n */\nexport const DEFAULT_FILE_COLUMNS = ['name', 'size', 'modified']\n\n/**\n * Read a dotted path off a node without throwing on a missing segment.\n *\n * @param {object} node The node.\n * @param {string} path A dotted path.\n * @return {(Array|object|string|number|boolean|undefined)} The value.\n */\nfunction atPath(node, path) {\n\tif (!node || typeof path !== 'string' || path === '') {\n\t\treturn undefined\n\t}\n\treturn path.split('.').reduce((acc, key) => (acc === null || acc === undefined ? undefined : acc[key]), node)\n}\n\n/**\n * Whether a string names a built-in column.\n *\n * @param {string} name The candidate name.\n * @return {boolean} True when it is a built-in.\n */\nexport function isBuiltInFileColumn(name) {\n\treturn typeof name === 'string' && Object.hasOwn(BUILT_IN_FILE_COLUMNS, name)\n}\n\n/**\n * Resolve a host's `columns` declaration into column objects.\n *\n * A string names a built-in. An object declares its own: `key`, `label`,\n * `source` (`node`, `attribute` or `row`), `attribute`, `formatter` and\n * `sortable`. An entry naming an unknown built-in is dropped here and refused\n * by the manifest validator, so an author hears about it at authoring time\n * rather than finding a column missing at runtime.\n *\n * @param {(Array|null|undefined)} columns The declaration, or nothing.\n * @return {Array<object>} The resolved columns, in the declared order.\n */\nexport function normaliseFileColumns(columns) {\n\tconst declared = Array.isArray(columns) && columns.length > 0 ? columns : DEFAULT_FILE_COLUMNS\n\tconst out = []\n\tdeclared.forEach((entry) => {\n\t\tif (typeof entry === 'string') {\n\t\t\tif (isBuiltInFileColumn(entry)) {\n\t\t\t\tout.push({ ...BUILT_IN_FILE_COLUMNS[entry], builtIn: true })\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif (!entry || typeof entry !== 'object' || typeof entry.key !== 'string' || entry.key === '') {\n\t\t\treturn\n\t\t}\n\t\tif (isBuiltInFileColumn(entry.key) && entry.source === undefined) {\n\t\t\tout.push({ ...BUILT_IN_FILE_COLUMNS[entry.key], ...entry, builtIn: true })\n\t\t\treturn\n\t\t}\n\t\tout.push({\n\t\t\tsource: 'node',\n\t\t\tsortable: false,\n\t\t\t...entry,\n\t\t\tbuiltIn: false,\n\t\t})\n\t})\n\treturn out\n}\n\n/**\n * The columns a browser renders: the host's declaration, narrowed by what this\n * user chose to see.\n *\n * Which side wins on what:\n *\n * - The HOST decides MEMBERSHIP. The result is built by walking the\n * declaration, so a column exists only because the host declared it, in the\n * order the host declared it.\n * - The USER decides VISIBILITY, and only downward. A stored preference can\n * hide a declared column; it can never add one. A key in the stored list\n * that the host no longer declares is passed over, so unticking a column\n * last month cannot bring it back once the host removed it, and neither can\n * a preference written while a different column set was declared.\n *\n * A stored value of `null` is a user who has never chosen, which shows\n * everything. An empty list is a user who hid everything, which is a choice\n * and is honoured.\n *\n * @param {Array<object>} declared The resolved declared columns.\n * @param {(string[]|null|undefined)} visibleKeys The user's stored visible keys.\n * @return {Array<object>} The columns to render.\n */\nexport function visibleFileColumns(declared, visibleKeys) {\n\tconst columns = Array.isArray(declared) ? declared : []\n\tif (!Array.isArray(visibleKeys)) {\n\t\treturn columns\n\t}\n\tconst wanted = new Set(visibleKeys)\n\treturn columns.filter((column) => wanted.has(column.key))\n}\n\n/**\n * The DAV properties a declaration needs in the PROPFIND, so an `attribute`\n * column's value is there when the folder lists rather than one request later.\n *\n * @param {Array<object>} columns The resolved columns.\n * @return {string[]} The property names, deduplicated, in declaration order.\n */\nexport function attributePropertiesFor(columns) {\n\tconst out = []\n\t;(Array.isArray(columns) ? columns : []).forEach((column) => {\n\t\tif (column && column.source === 'attribute' && typeof column.attribute === 'string' && column.attribute !== '') {\n\t\t\tif (!out.includes(column.attribute)) {\n\t\t\t\tout.push(column.attribute)\n\t\t\t}\n\t\t}\n\t})\n\treturn out\n}\n\n/**\n * The value one column shows for one node. A `row` column's key may be a\n * dotted path into the host's record.\n *\n * @param {object} column A resolved column.\n * @param {object} node The node.\n * @param {object} [rowData] The host's per-file data, keyed by file id.\n * @return {(Array|object|string|number|boolean|undefined)} The cell value.\n */\nexport function fileColumnValue(column, node, rowData = null) {\n\tif (!column || !node) {\n\t\treturn undefined\n\t}\n\tif (column.source === 'attribute') {\n\t\t// Read the key directly, NOT through the dotted-path reader: a DAV\n\t\t// property name is `{http://owncloud.org/ns}av-status`, and splitting\n\t\t// that on dots looks for a property called `org/ns}av-status` inside\n\t\t// one called `owncloud`, which is undefined every time.\n\t\tconst attributes = node.attributes\n\t\treturn attributes && typeof attributes === 'object' ? attributes[column.attribute] : undefined\n\t}\n\tif (column.source === 'row') {\n\t\tconst byId = rowData && typeof rowData === 'object' ? rowData : null\n\t\tif (!byId) {\n\t\t\treturn undefined\n\t\t}\n\t\tconst entry = byId[node.fileid] ?? byId[String(node.fileid)]\n\t\tif (!entry) {\n\t\t\treturn undefined\n\t\t}\n\t\t// A key the record holds as written wins; otherwise a dotted key reads\n\t\t// a nested field (`scanVerdict.state`, `integrity.value`).\n\t\treturn Object.hasOwn(entry, column.key) ? entry[column.key] : atPath(entry, column.key)\n\t}\n\treturn atPath(node, column.nodeKey || column.key)\n}\n\n/**\n * Sort nodes on a declared column's resolved value, within the listed folder.\n *\n * Folders stay first, the way the Files app lists them and the way the\n * built-in sorts already do, so switching to a declared column does not\n * reshuffle the folders into the files.\n *\n * @param {Array<object>} nodes The nodes.\n * @param {object} column The resolved column.\n * @param {boolean} ascending Sort direction.\n * @param {object} [rowData] The host's per-file data.\n * @param {(node: object) => boolean} [isFolder] Whether a node is a folder.\n * @return {Array<object>} A new, sorted array.\n */\nexport function sortNodesByColumn(nodes, column, ascending, rowData = null, isFolder = () => false) {\n\tconst list = Array.isArray(nodes) ? [...nodes] : []\n\tif (!column) {\n\t\treturn list\n\t}\n\tconst direction = ascending ? 1 : -1\n\treturn list.sort((a, b) => {\n\t\tconst folderA = isFolder(a)\n\t\tconst folderB = isFolder(b)\n\t\tif (folderA !== folderB) {\n\t\t\treturn folderA ? -1 : 1\n\t\t}\n\t\tconst left = fileColumnValue(column, a, rowData)\n\t\tconst right = fileColumnValue(column, b, rowData)\n\t\tconst leftMissing = left === undefined || left === null || left === ''\n\t\tconst rightMissing = right === undefined || right === null || right === ''\n\t\t// A value nobody filled in sorts last in BOTH directions. Letting it\n\t\t// ride to the top on a descending sort hides the rows that do carry\n\t\t// one, which is the opposite of what sorting was asked for.\n\t\tif (leftMissing !== rightMissing) {\n\t\t\treturn leftMissing ? 1 : -1\n\t\t}\n\t\tif (leftMissing && rightMissing) {\n\t\t\treturn 0\n\t\t}\n\t\tif (typeof left === 'number' && typeof right === 'number') {\n\t\t\treturn (left - right) * direction\n\t\t}\n\t\treturn String(left).localeCompare(String(right)) * direction\n\t})\n}\n\n/**\n * Whether a cell value counts as filled in.\n *\n * @param {(Array|object|string|number|boolean|null|undefined)} value The value.\n * @return {boolean} False for nothing, an empty string or an empty list.\n */\nfunction isFilled(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn false\n\t}\n\treturn !(Array.isArray(value) && value.length === 0)\n}\n\n/**\n * The single value a node is grouped under, as a string key: a list groups\n * under its first entry, an object under its label, title, name or id.\n *\n * @param {(Array|object|string|number|boolean|null|undefined)} value The cell value.\n * @return {(string|null)} The group key, or null when nothing is filled in.\n */\nfunction groupKeyOf(value) {\n\tconst first = Array.isArray(value) ? value[0] : value\n\tif (!isFilled(first)) {\n\t\treturn null\n\t}\n\tif (typeof first === 'object') {\n\t\tconst label = first.label ?? first.title ?? first.name ?? first.id\n\t\treturn isFilled(label) ? String(label) : null\n\t}\n\treturn String(first)\n}\n\n/**\n * The values a node carries in a column, as strings, for counting and\n * filtering: every entry of a list, the one value otherwise.\n *\n * @param {object} column A resolved column.\n * @param {object} node The node.\n * @param {object} [rowData] The host's per-file data.\n * @return {string[]} The values, deduplicated.\n */\nfunction facetValuesOf(column, node, rowData) {\n\tconst value = fileColumnValue(column, node, rowData)\n\tconst entries = Array.isArray(value) ? value : [value]\n\tconst out = []\n\tentries.forEach((entry) => {\n\t\tconst key = groupKeyOf(entry)\n\t\tif (key !== null && !out.includes(key)) {\n\t\t\tout.push(key)\n\t\t}\n\t})\n\treturn out\n}\n\n/**\n * Group nodes on a column's value, in the order each value is first met.\n * Nodes with no value come last, in one group whose value is null.\n *\n * @param {Array<object>} nodes The nodes, already in display order.\n * @param {object} column The resolved column.\n * @param {object} [rowData] The host's per-file data.\n * @return {Array<{value: (string|null), nodes: Array<object>}>} The groups.\n */\nexport function groupNodes(nodes, column, rowData = null) {\n\tconst groups = []\n\tconst byKey = new Map()\n\tconst empty = { value: null, nodes: [] }\n\t;(Array.isArray(nodes) ? nodes : []).forEach((node) => {\n\t\tconst key = column ? groupKeyOf(fileColumnValue(column, node, rowData)) : null\n\t\tif (key === null) {\n\t\t\tempty.nodes.push(node)\n\t\t\treturn\n\t\t}\n\t\tif (!byKey.has(key)) {\n\t\t\tconst group = { value: key, nodes: [] }\n\t\t\tbyKey.set(key, group)\n\t\t\tgroups.push(group)\n\t\t}\n\t\tbyKey.get(key).nodes.push(node)\n\t})\n\tif (empty.nodes.length > 0) {\n\t\tgroups.push(empty)\n\t}\n\treturn groups\n}\n\n/**\n * The values in use in a column across the nodes, with how many nodes carry\n * each; a list counts once per entry. Most used first, then in first-seen\n * order.\n *\n * @param {Array<object>} nodes The nodes.\n * @param {object} column The resolved column.\n * @param {object} [rowData] The host's per-file data.\n * @return {Array<{value: string, count: number}>} The values and counts.\n */\nexport function facetCounts(nodes, column, rowData = null) {\n\tconst counts = []\n\tconst byValue = new Map()\n\t;(Array.isArray(nodes) ? nodes : []).forEach((node) => {\n\t\tfacetValuesOf(column, node, rowData).forEach((value) => {\n\t\t\tif (!byValue.has(value)) {\n\t\t\t\tconst entry = { value, count: 0 }\n\t\t\t\tbyValue.set(value, entry)\n\t\t\t\tcounts.push(entry)\n\t\t\t}\n\t\t\tbyValue.get(value).count += 1\n\t\t})\n\t})\n\treturn counts\n\t\t.map((entry, index) => ({ entry, index }))\n\t\t.sort((a, b) => (b.entry.count - a.entry.count) || (a.index - b.index))\n\t\t.map(({ entry }) => entry)\n}\n\n/**\n * Whether a node passes the chosen facet values: within one facet any chosen\n * value will do, and every facet with a choice must pass. A facet with\n * nothing chosen does not filter.\n *\n * @param {object} node The node.\n * @param {object} selections Chosen values by column key.\n * @param {object} columnsByKey Resolved columns by key.\n * @param {object} [rowData] The host's per-file data.\n * @return {boolean} True when the node stays in the list.\n */\nexport function nodeMatchesFacets(node, selections, columnsByKey, rowData = null) {\n\treturn Object.entries(selections || {}).every(([key, chosen]) => {\n\t\tif (!Array.isArray(chosen) || chosen.length === 0) {\n\t\t\treturn true\n\t\t}\n\t\tconst column = columnsByKey && columnsByKey[key]\n\t\tif (!column) {\n\t\t\treturn true\n\t\t}\n\t\tconst values = facetValuesOf(column, node, rowData)\n\t\treturn chosen.some((value) => values.includes(value))\n\t})\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,qBAAqB,GAAG;AACrC,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE;AAChG,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;AACxF,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE;AAClG,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE;AAC1E,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;AACvE,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,EAAE,QAAQ,EAAE,KAAK,EAAE;AAC1F,EAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,oBAAoB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAC;AAChE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE;AAC5B,CAAC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE,EAAE;AACvD,EAAE,OAAO,SAAS;AAClB,EAAE;AACF,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,GAAG,SAAS,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC;AAC9G,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,mBAAmB,CAAC,IAAI,EAAE;AAC1C,CAAC,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,IAAI,CAAC;AAC9E,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,oBAAoB,CAAC,OAAO,EAAE;AAC9C,CAAC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,OAAO,GAAG,qBAAoB;AAC/F,CAAC,MAAM,GAAG,GAAG,GAAE;AACf,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK;AAC7B,EAAE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AACjC,GAAG,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE;AACnC,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,qBAAqB,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAC;AAChE,IAAI;AACJ,GAAG,MAAM;AACT,GAAG;AACH,EAAE,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,GAAG,KAAK,EAAE,EAAE;AAChG,GAAG,MAAM;AACT,GAAG;AACH,EAAE,IAAI,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE;AACpE,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAC;AAC7E,GAAG,MAAM;AACT,GAAG;AACH,EAAE,GAAG,CAAC,IAAI,CAAC;AACX,GAAG,MAAM,EAAE,MAAM;AACjB,GAAG,QAAQ,EAAE,KAAK;AAClB,GAAG,GAAG,KAAK;AACX,GAAG,OAAO,EAAE,KAAK;AACjB,GAAG,EAAC;AACJ,EAAE,EAAC;AACH,CAAC,OAAO,GAAG;AACX,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE;AAC1D,CAAC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,GAAG,GAAE;AACxD,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;AAClC,EAAE,OAAO,OAAO;AAChB,EAAE;AACF,CAAC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,WAAW,EAAC;AACpC,CAAC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC1D,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,sBAAsB,CAAC,OAAO,EAAE;AAChD,CAAC,MAAM,GAAG,GAAG,EAAE;AACf,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC,MAAM,KAAK;AAC9D,EAAE,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,KAAK,EAAE,EAAE;AAClH,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE;AACxC,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAC;AAC9B,IAAI;AACJ,GAAG;AACH,EAAE,EAAC;AACH,CAAC,OAAO,GAAG;AACX,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE;AAC9D,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE;AACvB,EAAE,OAAO,SAAS;AAClB,EAAE;AACF,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE;AACpC;AACA;AACA;AACA;AACA,EAAE,MAAM,UAAU,GAAG,IAAI,CAAC,WAAU;AACpC,EAAE,OAAO,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,SAAS;AAChG,EAAE;AACF,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,EAAE;AAC9B,EAAE,MAAM,IAAI,GAAG,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,GAAG,OAAO,GAAG,KAAI;AACtE,EAAE,IAAI,CAAC,IAAI,EAAE;AACb,GAAG,OAAO,SAAS;AACnB,GAAG;AACH,EAAE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAC;AAC9D,EAAE,IAAI,CAAC,KAAK,EAAE;AACd,GAAG,OAAO,SAAS;AACnB,GAAG;AACH;AACA;AACA,EAAE,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;AACzF,EAAE;AACF,CAAC,OAAO,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC;AAClD,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,EAAE,QAAQ,GAAG,MAAM,KAAK,EAAE;AACpG,CAAC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,GAAE;AACpD,CAAC,IAAI,CAAC,MAAM,EAAE;AACd,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,SAAS,GAAG,SAAS,GAAG,CAAC,GAAG,CAAC,EAAC;AACrC,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK;AAC5B,EAAE,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,EAAC;AAC7B,EAAE,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,EAAC;AAC7B,EAAE,IAAI,OAAO,KAAK,OAAO,EAAE;AAC3B,GAAG,OAAO,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC;AAC1B,GAAG;AACH,EAAE,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,EAAC;AAClD,EAAE,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,EAAC;AACnD,EAAE,MAAM,WAAW,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,GAAE;AACxE,EAAE,MAAM,YAAY,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,GAAE;AAC5E;AACA;AACA;AACA,EAAE,IAAI,WAAW,KAAK,YAAY,EAAE;AACpC,GAAG,OAAO,WAAW,GAAG,CAAC,GAAG,CAAC,CAAC;AAC9B,GAAG;AACH,EAAE,IAAI,WAAW,IAAI,YAAY,EAAE;AACnC,GAAG,OAAO,CAAC;AACX,GAAG;AACH,EAAE,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC7D,GAAG,OAAO,CAAC,IAAI,GAAG,KAAK,IAAI,SAAS;AACpC,GAAG;AACH,EAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS;AAC9D,EAAE,CAAC;AACH,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,QAAQ,CAAC,KAAK,EAAE;AACzB,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE;AAC5D,EAAE,OAAO,KAAK;AACd,EAAE;AACF,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;AACrD,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU,CAAC,KAAK,EAAE;AAC3B,CAAC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,MAAK;AACtD,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;AACvB,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAChC,EAAE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,GAAE;AACpE,EAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI;AAC/C,EAAE;AACF,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC;AACrB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE;AAC9C,CAAC,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAC;AACrD,CAAC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,EAAC;AACvD,CAAC,MAAM,GAAG,GAAG,GAAE;AACf,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK;AAC5B,EAAE,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,EAAC;AAC/B,EAAE,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;AAC1C,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,EAAC;AAChB,GAAG;AACH,EAAE,EAAC;AACH,CAAC,OAAO,GAAG;AACX,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,EAAE;AAC1D,CAAC,MAAM,MAAM,GAAG,GAAE;AAClB,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,GAAE;AACxB,CAAC,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;AACzC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC,IAAI,KAAK;AACxD,EAAE,MAAM,GAAG,GAAG,MAAM,GAAG,UAAU,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,GAAG,KAAI;AAChF,EAAE,IAAI,GAAG,KAAK,IAAI,EAAE;AACpB,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAC;AACzB,GAAG,MAAM;AACT,GAAG;AACH,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AACvB,GAAG,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,GAAE;AAC1C,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAC;AACxB,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAC;AACrB,GAAG;AACH,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAC;AACjC,EAAE,EAAC;AACH,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;AAC7B,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAC;AACpB,EAAE;AACF,CAAC,OAAO,MAAM;AACd,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,EAAE;AAC3D,CAAC,MAAM,MAAM,GAAG,GAAE;AAClB,CAAC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAE;AAC1B,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC,IAAI,KAAK;AACxD,EAAE,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK;AAC1D,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;AAC5B,IAAI,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,GAAE;AACrC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAC;AAC7B,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,EAAC;AACtB,IAAI;AACJ,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,EAAC;AAChC,GAAG,EAAC;AACJ,EAAE,EAAC;AACH,CAAC,OAAO,MAAM;AACd,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5C,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AACzE,GAAG,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,CAAC;AAC5B,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,GAAG,IAAI,EAAE;AAClF,CAAC,OAAO,MAAM,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK;AAClE,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;AACrD,GAAG,OAAO,IAAI;AACd,GAAG;AACH,EAAE,MAAM,MAAM,GAAG,YAAY,IAAI,YAAY,CAAC,GAAG,EAAC;AAClD,EAAE,IAAI,CAAC,MAAM,EAAE;AACf,GAAG,OAAO,IAAI;AACd,GAAG;AACH,EAAE,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAC;AACrD,EAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACvD,EAAE,CAAC;AACH;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getCurrentUser } from '@nextcloud/auth';
|
|
2
2
|
import { translate, translatePlural } from '@nextcloud/l10n';
|
|
3
|
-
import { NcLoadingIcon, NcActions, NcActionLink, NcActionButton, NcActionCaption, NcActionSeparator, NcActionCheckbox, NcButton, NcCheckboxRadioSwitch } from '@nextcloud/vue';
|
|
3
|
+
import { NcNoteCard, NcLoadingIcon, NcActions, NcActionLink, NcActionButton, NcActionCaption, NcActionSeparator, NcActionCheckbox, NcButton, NcCheckboxRadioSwitch } from '@nextcloud/vue';
|
|
4
4
|
import { ref, getCurrentInstance, markRaw, inject } from 'vue';
|
|
5
5
|
import AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue';
|
|
6
6
|
import Close from 'vue-material-design-icons/Close.vue';
|
|
@@ -31,7 +31,7 @@ import { resolveRegisteredHandler, buildOnSuccessRoute } from '../../utils/actio
|
|
|
31
31
|
import { columnFieldsOf, refIdOf, buildValueStages, buildReferenceStages, columnKeyOfRow, resolveDropValue, fullTargetOf, referenceTargetOf, fetchReferenceObjects } from '../../utils/boardReferenceColumns.js';
|
|
32
32
|
import { reportBindingProblems } from '../../utils/diagnostics.js';
|
|
33
33
|
import { fetchGroupedCounts } from '../../utils/fetchAggregate.js';
|
|
34
|
-
import { fetchFilterCounts } from '../../utils/fetchFilterCounts.js';
|
|
34
|
+
import { fetchListTotal, fetchFilterCounts } from '../../utils/fetchFilterCounts.js';
|
|
35
35
|
import { buildExportUrl } from '../../utils/indexExportHelpers.js';
|
|
36
36
|
import { lensUnavailableText } from '../../utils/lensAvailability.js';
|
|
37
37
|
import { isModifiedClick, openRowTarget } from '../../utils/linkNavigation.js';
|
|
@@ -50,7 +50,7 @@ import { isNewTabClick, preventMiddleClickAutoscroll, isRowMiddleClick, isNewTab
|
|
|
50
50
|
import { DEFAULT_ROW_INDICATOR_CAP } from '../../utils/rowIndicators.js';
|
|
51
51
|
import { viewMatchesScope, extractViewState, buildRouteQueryFromViewState, extractViewStateFromRouteQuery, buildViewCreatePayload, savedViewScope, normalizeSharedWith } from '../../utils/savedViewHelpers.js';
|
|
52
52
|
import { fieldsFromSchema, columnsFromSchema } from '../../utils/schema.js';
|
|
53
|
-
import { resolveScopeLayout } from '../../utils/scopeListLayout.js';
|
|
53
|
+
import { resolveScopeLayout, resolveScopePresentation } from '../../utils/scopeListLayout.js';
|
|
54
54
|
import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js';
|
|
55
55
|
import __sfc_main$6 from '../CnActionButtons/CnActionButtons.vue.js';
|
|
56
56
|
import __sfc_main$7 from '../CnActionsBar/CnActionsBar.vue.js';
|
|
@@ -325,6 +325,7 @@ var script = {
|
|
|
325
325
|
|
|
326
326
|
components: {
|
|
327
327
|
CnIndexPageWidgets: __sfc_main,
|
|
328
|
+
NcNoteCard,
|
|
328
329
|
CnBuildiqEditButton: __sfc_main$1,
|
|
329
330
|
CnFollowToggle: __sfc_main$2,
|
|
330
331
|
NcLoadingIcon,
|
|
@@ -493,6 +494,35 @@ var script = {
|
|
|
493
494
|
default: '',
|
|
494
495
|
},
|
|
495
496
|
|
|
497
|
+
/**
|
|
498
|
+
* Tabs to sibling pages above the toolbar (manifest `config.pageTabs`),
|
|
499
|
+
* each `{ id?, label, route?, params?, query?, href?, countFrom?: {
|
|
500
|
+
* register, schema, filter? } }`: DqPrivacy's "AVG-verzoeken |
|
|
501
|
+
* Datalekken". The current page's tab is marked; `countFrom` adds the
|
|
502
|
+
* count of that list. Wins over a named source's own page tabs.
|
|
503
|
+
*
|
|
504
|
+
* @type {Array<object>|null}
|
|
505
|
+
* @spec openspec/changes/index-page-tabs-notice/specs/index-page/spec.md#requirement-an-index-page-can-link-its-sibling-pages-as-tabs
|
|
506
|
+
*/
|
|
507
|
+
pageTabs: {
|
|
508
|
+
type: Array,
|
|
509
|
+
default: null,
|
|
510
|
+
},
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* A notice under the list (manifest `config.notice`): `{ text,
|
|
514
|
+
* variant?: 'info' | 'success' | 'warning' }`, a band that explains
|
|
515
|
+
* the list, such as who decides what. The text goes through the host
|
|
516
|
+
* translate.
|
|
517
|
+
*
|
|
518
|
+
* @type {{text: string, variant?: string}|null}
|
|
519
|
+
* @spec openspec/changes/index-page-tabs-notice/specs/index-page/spec.md#requirement-an-index-page-can-show-a-notice-under-its-list
|
|
520
|
+
*/
|
|
521
|
+
notice: {
|
|
522
|
+
type: Object,
|
|
523
|
+
default: null,
|
|
524
|
+
},
|
|
525
|
+
|
|
496
526
|
/** Optional description shown below the title */
|
|
497
527
|
description: {
|
|
498
528
|
type: String,
|
|
@@ -1880,6 +1910,33 @@ var script = {
|
|
|
1880
1910
|
default: () => [],
|
|
1881
1911
|
},
|
|
1882
1912
|
|
|
1913
|
+
/**
|
|
1914
|
+
* Cards after the list (manifest `config.afterList`): dashboard widget
|
|
1915
|
+
* definitions `{ id, type, title?, content, headerLink? }` stacked
|
|
1916
|
+
* under the list in the listed order, the counterpart of `statRow`
|
|
1917
|
+
* above it. A query preset or a scope can set its own.
|
|
1918
|
+
*
|
|
1919
|
+
* @spec openspec/changes/index-scope-presentation/specs/index-page/spec.md#requirement-an-index-page-can-draw-widgets-after-its-list
|
|
1920
|
+
* @type {Array<object>}
|
|
1921
|
+
*/
|
|
1922
|
+
afterList: {
|
|
1923
|
+
type: Array,
|
|
1924
|
+
default: () => [],
|
|
1925
|
+
},
|
|
1926
|
+
|
|
1927
|
+
/**
|
|
1928
|
+
* The route query pairs that chose this page's query preset, set by
|
|
1929
|
+
* CnPageRenderer. They scope the list, so they show no removable
|
|
1930
|
+
* active-filter chip.
|
|
1931
|
+
*
|
|
1932
|
+
* @spec openspec/changes/index-scope-presentation/specs/index-page/spec.md#requirement-the-query-that-chose-a-preset-is-not-a-removable-filter
|
|
1933
|
+
* @type {object|null}
|
|
1934
|
+
*/
|
|
1935
|
+
fixedFilters: {
|
|
1936
|
+
type: Object,
|
|
1937
|
+
default: null,
|
|
1938
|
+
},
|
|
1939
|
+
|
|
1883
1940
|
/**
|
|
1884
1941
|
* Where a row carries the actions the server says this caller may run
|
|
1885
1942
|
* on it: a dotted path, read off the rows the list already fetched, so
|
|
@@ -2480,6 +2537,8 @@ var script = {
|
|
|
2480
2537
|
tabCounts: null,
|
|
2481
2538
|
// The counts of the `filterStrip` pills, by pill key ('all', 'v0', ...).
|
|
2482
2539
|
filterStripCounts: null,
|
|
2540
|
+
/** The `countFrom` counts of the manifest page tabs, by index. */
|
|
2541
|
+
pageTabCounts: {},
|
|
2483
2542
|
/** The tabs `quickFiltersFrom` produced, or null before they load. */
|
|
2484
2543
|
generatedQuickFilters: null,
|
|
2485
2544
|
/** Count per saved-view id, for the views control; null = none. */
|
|
@@ -3029,7 +3088,8 @@ var script = {
|
|
|
3029
3088
|
}
|
|
3030
3089
|
// The index boards draw all four segments, so the board look does
|
|
3031
3090
|
// too unless the manifest names its own list.
|
|
3032
|
-
const
|
|
3091
|
+
const viewSwitch = this.presented('viewSwitch');
|
|
3092
|
+
const listed = Array.isArray(viewSwitch) ? viewSwitch : ['table', 'cards', 'board', 'map'];
|
|
3033
3093
|
const offered = [...this.effectiveToggleModes, ...(this.showMapSegment ? ['map'] : [])];
|
|
3034
3094
|
return listed.filter((mode) => ['table', 'cards', 'board', 'map'].includes(mode) && !offered.includes(mode))
|
|
3035
3095
|
},
|
|
@@ -3500,6 +3560,58 @@ var script = {
|
|
|
3500
3560
|
})
|
|
3501
3561
|
},
|
|
3502
3562
|
|
|
3563
|
+
/**
|
|
3564
|
+
* This page's own value of each presentation key, which a query preset
|
|
3565
|
+
* may have set: what `presented()` falls back to without a scope.
|
|
3566
|
+
*
|
|
3567
|
+
* @return {object}
|
|
3568
|
+
* @spec openspec/changes/index-scope-presentation/specs/index-page/spec.md#requirement-a-preset-or-a-scope-sets-the-list-s-presentation
|
|
3569
|
+
*/
|
|
3570
|
+
ownPresentation() {
|
|
3571
|
+
return {
|
|
3572
|
+
title: this.title,
|
|
3573
|
+
quickFilters: this.quickFilters,
|
|
3574
|
+
cardFields: this.cardFields,
|
|
3575
|
+
countText: this.countText,
|
|
3576
|
+
searchPlaceholder: this.searchPlaceholder,
|
|
3577
|
+
footerNote: this.footerNote,
|
|
3578
|
+
viewSwitch: this.viewSwitch,
|
|
3579
|
+
addLabel: this.addLabel,
|
|
3580
|
+
headerButtons: this.headerButtons,
|
|
3581
|
+
breadcrumb: this.breadcrumb,
|
|
3582
|
+
headerMeta: this.headerMeta,
|
|
3583
|
+
afterList: this.afterList,
|
|
3584
|
+
}
|
|
3585
|
+
},
|
|
3586
|
+
|
|
3587
|
+
/**
|
|
3588
|
+
* The presentation the active scope declares (`title`, `countText`,
|
|
3589
|
+
* `quickFilters`, `addLabel`, `afterList` and the other query preset
|
|
3590
|
+
* keys), from its folder entry or its row's `x-index`. Empty without a
|
|
3591
|
+
* scope, so the page's own values stand.
|
|
3592
|
+
*
|
|
3593
|
+
* @return {object}
|
|
3594
|
+
* @spec openspec/changes/index-scope-presentation/specs/index-page/spec.md#requirement-a-preset-or-a-scope-sets-the-list-s-presentation
|
|
3595
|
+
*/
|
|
3596
|
+
activeScopePresentation() {
|
|
3597
|
+
if (!this.folderSidebar) {
|
|
3598
|
+
return {}
|
|
3599
|
+
}
|
|
3600
|
+
return resolveScopePresentation({ scope: this.activeScope, row: this.activeScopeRow })
|
|
3601
|
+
},
|
|
3602
|
+
|
|
3603
|
+
/**
|
|
3604
|
+
* The widgets drawn after the list: the active scope's `afterList`,
|
|
3605
|
+
* else the page's.
|
|
3606
|
+
*
|
|
3607
|
+
* @return {Array<object>}
|
|
3608
|
+
* @spec openspec/changes/index-scope-presentation/specs/index-page/spec.md#requirement-an-index-page-can-draw-widgets-after-its-list
|
|
3609
|
+
*/
|
|
3610
|
+
presentedAfterList() {
|
|
3611
|
+
const list = this.presented('afterList');
|
|
3612
|
+
return Array.isArray(list) ? list : []
|
|
3613
|
+
},
|
|
3614
|
+
|
|
3503
3615
|
/**
|
|
3504
3616
|
* The fields the search box searches over while a scope declaring
|
|
3505
3617
|
* `searchFields` is active. Read by `useSelfFetchList`'s fixed-filter
|
|
@@ -3959,8 +4071,9 @@ var script = {
|
|
|
3959
4071
|
* @return {?Array<string>}
|
|
3960
4072
|
*/
|
|
3961
4073
|
resolvedCardFields() {
|
|
3962
|
-
|
|
3963
|
-
|
|
4074
|
+
const cardFields = this.presented('cardFields');
|
|
4075
|
+
if (Array.isArray(cardFields) && cardFields.length > 0) {
|
|
4076
|
+
return cardFields
|
|
3964
4077
|
}
|
|
3965
4078
|
if (!this.isBoardLook) {
|
|
3966
4079
|
return null
|
|
@@ -3999,6 +4112,10 @@ var script = {
|
|
|
3999
4112
|
}
|
|
4000
4113
|
});
|
|
4001
4114
|
}
|
|
4115
|
+
// An `actions` column: the page's row actions it names, as buttons.
|
|
4116
|
+
cols = cols.map((col) => (col && typeof col === 'object' && col.widget === 'actions'
|
|
4117
|
+
? { ...col, sortable: false, widgetProps: { ...(col.widgetProps || {}), items: (row) => this.actionColumnItems(col, row), run: this.runColumnAction } }
|
|
4118
|
+
: col));
|
|
4002
4119
|
// The Recent lens owns the order: no column sorts while it is on.
|
|
4003
4120
|
if (this.recentLensActive) {
|
|
4004
4121
|
cols = cols.map((col) => (col && typeof col === 'object' ? { ...col, sortable: false } : col));
|
|
@@ -4875,6 +4992,51 @@ var script = {
|
|
|
4875
4992
|
return (source.pageTabs() || []).filter((tab) => tab && tab.to && tab.label)
|
|
4876
4993
|
},
|
|
4877
4994
|
|
|
4995
|
+
/**
|
|
4996
|
+
* The page tabs to draw: the manifest's `pageTabs`, each with its
|
|
4997
|
+
* `countFrom` count once known, else a named source's own.
|
|
4998
|
+
*
|
|
4999
|
+
* @return {Array<object>} The tabs, `{ id, label, to, count? }`.
|
|
5000
|
+
* @spec openspec/changes/index-page-tabs-notice/specs/index-page/spec.md#requirement-an-index-page-can-link-its-sibling-pages-as-tabs
|
|
5001
|
+
*/
|
|
5002
|
+
/**
|
|
5003
|
+
* The notice band's NcNoteCard type: its `variant`, else info.
|
|
5004
|
+
*
|
|
5005
|
+
* @return {string}
|
|
5006
|
+
* @spec openspec/changes/index-page-tabs-notice/specs/index-page/spec.md#requirement-an-index-page-can-show-a-notice-under-its-list
|
|
5007
|
+
*/
|
|
5008
|
+
noticeType() {
|
|
5009
|
+
const variant = this.notice && this.notice.variant;
|
|
5010
|
+
return ['success', 'warning', 'error'].includes(variant) ? variant : 'info'
|
|
5011
|
+
},
|
|
5012
|
+
|
|
5013
|
+
effectivePageTabs() {
|
|
5014
|
+
if (!Array.isArray(this.pageTabs) || this.pageTabs.length === 0) {
|
|
5015
|
+
return this.sourcePageTabs
|
|
5016
|
+
}
|
|
5017
|
+
return this.pageTabs
|
|
5018
|
+
.filter((tab) => tab && typeof tab.label === 'string' && tab.label !== '' && (tab.route || tab.to || tab.href))
|
|
5019
|
+
.map((tab, index) => {
|
|
5020
|
+
const to = tab.route
|
|
5021
|
+
? { name: tab.route, ...(tab.params ? { params: tab.params } : {}), ...(tab.query ? { query: tab.query } : {}) }
|
|
5022
|
+
: (tab.to || tab.href);
|
|
5023
|
+
const count = this.pageTabCounts[index];
|
|
5024
|
+
return { id: tab.id || `page-tab-${index}`, label: this.cnTranslate(tab.label), to, ...(typeof count === 'number' ? { count } : {}) }
|
|
5025
|
+
})
|
|
5026
|
+
},
|
|
5027
|
+
|
|
5028
|
+
/**
|
|
5029
|
+
* What the page tab counts depend on, as one key.
|
|
5030
|
+
*
|
|
5031
|
+
* @return {string}
|
|
5032
|
+
* @spec openspec/changes/index-page-tabs-notice/specs/index-page/spec.md#requirement-an-index-page-can-link-its-sibling-pages-as-tabs
|
|
5033
|
+
*/
|
|
5034
|
+
pageTabCountKey() {
|
|
5035
|
+
const tabs = Array.isArray(this.pageTabs) ? this.pageTabs : [];
|
|
5036
|
+
const sources = tabs.map((tab) => (tab && tab.countFrom && tab.countFrom.register && tab.countFrom.schema ? tab.countFrom : null));
|
|
5037
|
+
return sources.some(Boolean) ? JSON.stringify(sources) : ''
|
|
5038
|
+
},
|
|
5039
|
+
|
|
4878
5040
|
/**
|
|
4879
5041
|
* The numbers on the quick filters: a named source counts its own
|
|
4880
5042
|
* tabs (`tabCounts()`), a self-fetching page asks OpenRegister.
|
|
@@ -4896,11 +5058,15 @@ var script = {
|
|
|
4896
5058
|
* @return {string} The note, translated, or ''.
|
|
4897
5059
|
*/
|
|
4898
5060
|
effectiveFooterNote() {
|
|
4899
|
-
|
|
4900
|
-
|
|
5061
|
+
// The scope's note, else the page's (a preset may have set it),
|
|
5062
|
+
// else the named source's.
|
|
5063
|
+
const scoped = this.activeScopePresentation ? this.activeScopePresentation.footerNote : undefined;
|
|
5064
|
+
const footerNote = scoped !== undefined ? scoped : this.footerNote;
|
|
5065
|
+
if (footerNote) {
|
|
5066
|
+
return this.cnTranslate(footerNote)
|
|
4901
5067
|
}
|
|
4902
5068
|
const source = this.isNamedSource ? this.namedSource : null;
|
|
4903
|
-
return (source && source.footerNote) ||
|
|
5069
|
+
return (source && source.footerNote) || footerNote
|
|
4904
5070
|
},
|
|
4905
5071
|
|
|
4906
5072
|
/**
|
|
@@ -4911,7 +5077,7 @@ var script = {
|
|
|
4911
5077
|
* @return {Array<object>|null} The tabs, or null when there are none.
|
|
4912
5078
|
*/
|
|
4913
5079
|
effectiveQuickFilters() {
|
|
4914
|
-
const own = withPersonalLenses(this.quickFilters || this.generatedQuickFilters, this.personalLenses);
|
|
5080
|
+
const own = withPersonalLenses(this.presented('quickFilters') || this.generatedQuickFilters, this.personalLenses);
|
|
4915
5081
|
if (own && own.length > 0) {
|
|
4916
5082
|
return own
|
|
4917
5083
|
}
|
|
@@ -4977,7 +5143,8 @@ var script = {
|
|
|
4977
5143
|
* @return {Array<{key: string, label: string, action: string, variant: string, icon: string, format: string}>}
|
|
4978
5144
|
*/
|
|
4979
5145
|
resolvedHeaderButtons() {
|
|
4980
|
-
const
|
|
5146
|
+
const headerButtons = this.presented('headerButtons');
|
|
5147
|
+
const declared = Array.isArray(headerButtons) ? headerButtons : [];
|
|
4981
5148
|
return declared
|
|
4982
5149
|
.filter((button) => button && typeof button === 'object' && typeof button.action === 'string' && button.action !== '')
|
|
4983
5150
|
.map((button, index) => {
|
|
@@ -5102,6 +5269,7 @@ var script = {
|
|
|
5102
5269
|
for (const [key, raw] of Object.entries(this.effectiveActiveFilters || {})) {
|
|
5103
5270
|
const values = (Array.isArray(raw) ? raw : [raw])
|
|
5104
5271
|
.filter((value) => value !== null && value !== undefined && value !== '')
|
|
5272
|
+
.filter((value) => !this.isFixedFilter(key, value))
|
|
5105
5273
|
.map((value) => this.activeFilterValueLabel(key, value));
|
|
5106
5274
|
if (values.length === 0) {
|
|
5107
5275
|
continue
|
|
@@ -5285,11 +5453,12 @@ var script = {
|
|
|
5285
5453
|
* @return {string}
|
|
5286
5454
|
*/
|
|
5287
5455
|
headerMetaText() {
|
|
5288
|
-
|
|
5456
|
+
const headerMeta = this.presented('headerMeta');
|
|
5457
|
+
if (!headerMeta) {
|
|
5289
5458
|
return ''
|
|
5290
5459
|
}
|
|
5291
5460
|
const total = this.effectivePagination?.total;
|
|
5292
|
-
return this.cnTranslate(
|
|
5461
|
+
return this.cnTranslate(headerMeta)
|
|
5293
5462
|
.replace('{shown}', String(this.effectiveObjects.length))
|
|
5294
5463
|
.replace('{total}', typeof total === 'number' ? String(total) : '')
|
|
5295
5464
|
},
|
|
@@ -5303,11 +5472,12 @@ var script = {
|
|
|
5303
5472
|
// The default count line is the library's own string, so it
|
|
5304
5473
|
// reads "14 van 14" in Dutch; a page's own template goes
|
|
5305
5474
|
// through the app's label lookup.
|
|
5306
|
-
|
|
5475
|
+
const countText = this.presented('countText');
|
|
5476
|
+
if (!countText && !this.countSubtitle) {
|
|
5307
5477
|
return translate('nextcloud-vue', '{shown} of {total}', { shown, total })
|
|
5308
5478
|
}
|
|
5309
|
-
if (
|
|
5310
|
-
return this.cnTranslate(
|
|
5479
|
+
if (countText) {
|
|
5480
|
+
return this.cnTranslate(countText)
|
|
5311
5481
|
.replace('{shown}', String(shown))
|
|
5312
5482
|
.replace('{total}', String(total))
|
|
5313
5483
|
}
|
|
@@ -5321,8 +5491,10 @@ var script = {
|
|
|
5321
5491
|
|
|
5322
5492
|
/** Add button label — derived from schema.title if not explicitly set */
|
|
5323
5493
|
resolvedAddLabel() {
|
|
5324
|
-
|
|
5325
|
-
|
|
5494
|
+
const scoped = this.activeScopePresentation ? this.activeScopePresentation.addLabel : undefined;
|
|
5495
|
+
const addLabel = scoped !== undefined ? scoped : this.addLabel;
|
|
5496
|
+
if (addLabel) {
|
|
5497
|
+
return this.cnTranslate(addLabel)
|
|
5326
5498
|
}
|
|
5327
5499
|
// A named source names its own create action. Without this the button
|
|
5328
5500
|
// falls back to a schema-derived noun, and a named source has no
|
|
@@ -5547,6 +5719,14 @@ var script = {
|
|
|
5547
5719
|
},
|
|
5548
5720
|
},
|
|
5549
5721
|
|
|
5722
|
+
pageTabCountKey: {
|
|
5723
|
+
immediate: true,
|
|
5724
|
+
/** @spec openspec/changes/index-page-tabs-notice/specs/index-page/spec.md#requirement-an-index-page-can-link-its-sibling-pages-as-tabs */
|
|
5725
|
+
handler() {
|
|
5726
|
+
this.loadPageTabCounts();
|
|
5727
|
+
},
|
|
5728
|
+
},
|
|
5729
|
+
|
|
5550
5730
|
filterStripCountKey: {
|
|
5551
5731
|
immediate: true,
|
|
5552
5732
|
/** @spec openspec/changes/screens-filter-strip/specs/index-filter-strip/spec.md#requirement-an-index-page-can-draw-a-second-filter-strip */
|
|
@@ -5814,6 +5994,36 @@ var script = {
|
|
|
5814
5994
|
},
|
|
5815
5995
|
|
|
5816
5996
|
methods: {
|
|
5997
|
+
/**
|
|
5998
|
+
* A presentation value: the active scope's when it declares the key,
|
|
5999
|
+
* else this page's own prop (which a query preset may have set).
|
|
6000
|
+
*
|
|
6001
|
+
* @param {string} key One of the query preset keys.
|
|
6002
|
+
* @return {unknown} The value.
|
|
6003
|
+
* @spec openspec/changes/index-scope-presentation/specs/index-page/spec.md#requirement-a-preset-or-a-scope-sets-the-list-s-presentation
|
|
6004
|
+
*/
|
|
6005
|
+
presented(key) {
|
|
6006
|
+
const scoped = this.activeScopePresentation;
|
|
6007
|
+
return scoped[key] !== undefined ? scoped[key] : this.ownPresentation[key]
|
|
6008
|
+
},
|
|
6009
|
+
|
|
6010
|
+
/**
|
|
6011
|
+
* Whether a filter value is one of the query pairs that chose the
|
|
6012
|
+
* preset, so it shows no removable chip.
|
|
6013
|
+
*
|
|
6014
|
+
* @param {string} key The filter key.
|
|
6015
|
+
* @param {unknown} value One filter value.
|
|
6016
|
+
* @return {boolean}
|
|
6017
|
+
* @spec openspec/changes/index-scope-presentation/specs/index-page/spec.md#requirement-the-query-that-chose-a-preset-is-not-a-removable-filter
|
|
6018
|
+
*/
|
|
6019
|
+
isFixedFilter(key, value) {
|
|
6020
|
+
const fixed = this.fixedFilters;
|
|
6021
|
+
if (!fixed || !Object.hasOwn(fixed, key)) {
|
|
6022
|
+
return false
|
|
6023
|
+
}
|
|
6024
|
+
return String(fixed[key]) === String(value)
|
|
6025
|
+
},
|
|
6026
|
+
|
|
5817
6027
|
/**
|
|
5818
6028
|
* The manifest page that shows one object of a schema, for a linked
|
|
5819
6029
|
* label. Null without a manifest or a matching detail page.
|
|
@@ -5927,6 +6137,32 @@ var script = {
|
|
|
5927
6137
|
];
|
|
5928
6138
|
},
|
|
5929
6139
|
|
|
6140
|
+
/**
|
|
6141
|
+
* Count the lists behind the manifest page tabs (`countFrom`), one list
|
|
6142
|
+
* request each. A failed count shows no number.
|
|
6143
|
+
*
|
|
6144
|
+
* @return {Promise<void>}
|
|
6145
|
+
* @spec openspec/changes/index-page-tabs-notice/specs/index-page/spec.md#requirement-an-index-page-can-link-its-sibling-pages-as-tabs
|
|
6146
|
+
*/
|
|
6147
|
+
async loadPageTabCounts() {
|
|
6148
|
+
const key = this.pageTabCountKey;
|
|
6149
|
+
if (key === '') {
|
|
6150
|
+
this.pageTabCounts = {};
|
|
6151
|
+
return
|
|
6152
|
+
}
|
|
6153
|
+
const ctx = typeof this.selfFetchTokenCtx === 'function' ? this.selfFetchTokenCtx() : {};
|
|
6154
|
+
const counts = {};
|
|
6155
|
+
await Promise.all(this.pageTabs.map(async (tab, index) => {
|
|
6156
|
+
const from = tab && tab.countFrom;
|
|
6157
|
+
if (from && from.register && from.schema) {
|
|
6158
|
+
counts[index] = await fetchListTotal(from.register, from.schema, from.filter || {}, ctx).catch(() => null);
|
|
6159
|
+
}
|
|
6160
|
+
}));
|
|
6161
|
+
if (key === this.pageTabCountKey) {
|
|
6162
|
+
this.pageTabCounts = counts;
|
|
6163
|
+
}
|
|
6164
|
+
},
|
|
6165
|
+
|
|
5930
6166
|
/**
|
|
5931
6167
|
* Read the counts of the `filterStrip` pills in one grouped request,
|
|
5932
6168
|
* narrowed by the page filter and the active quick filter. A failed
|
|
@@ -7360,6 +7596,50 @@ var script = {
|
|
|
7360
7596
|
this.$emit('quick-edit-save', { id, patch });
|
|
7361
7597
|
},
|
|
7362
7598
|
|
|
7599
|
+
/**
|
|
7600
|
+
* The buttons an `actions` column shows on a row: the row actions its
|
|
7601
|
+
* `widgetProps.actions` names (by id, else by label), in that order,
|
|
7602
|
+
* that are visible for the row.
|
|
7603
|
+
*
|
|
7604
|
+
* @param {object} col The column.
|
|
7605
|
+
* @param {object} row The row.
|
|
7606
|
+
* @return {Array<{key: string, label: string, variant: string, disabled: boolean}>}
|
|
7607
|
+
* @spec openspec/changes/table-actions-column/specs/index-page/spec.md#requirement-a-column-can-show-row-actions-as-buttons
|
|
7608
|
+
*/
|
|
7609
|
+
actionColumnItems(col, row) {
|
|
7610
|
+
const wanted = Array.isArray(col.widgetProps && col.widgetProps.actions) ? col.widgetProps.actions : [];
|
|
7611
|
+
const available = this.rowActionsFor(row);
|
|
7612
|
+
return wanted
|
|
7613
|
+
.map((ref) => available.find((action) => action.id === ref) || available.find((action) => action.label === ref))
|
|
7614
|
+
.filter((action) => action && isRowActionVisible(action, row))
|
|
7615
|
+
.map((action, index) => ({
|
|
7616
|
+
key: action.id || action.label,
|
|
7617
|
+
label: this.cnTranslate(action.label),
|
|
7618
|
+
variant: action.variant || (index === 0 ? 'secondary' : 'tertiary'),
|
|
7619
|
+
disabled: typeof action.disabled === 'function' ? !!action.disabled(row) : action.disabled === true,
|
|
7620
|
+
}))
|
|
7621
|
+
},
|
|
7622
|
+
|
|
7623
|
+
/**
|
|
7624
|
+
* Run one `actions` column button: its handler, then the same `action`
|
|
7625
|
+
* event a row menu entry emits.
|
|
7626
|
+
*
|
|
7627
|
+
* @param {string} key The action's id, else its label.
|
|
7628
|
+
* @param {object} row The row.
|
|
7629
|
+
* @return {void}
|
|
7630
|
+
* @spec openspec/changes/table-actions-column/specs/index-page/spec.md#requirement-a-column-can-show-row-actions-as-buttons
|
|
7631
|
+
*/
|
|
7632
|
+
runColumnAction(key, row) {
|
|
7633
|
+
const action = this.rowActionsFor(row).find((entry) => (entry.id || entry.label) === key);
|
|
7634
|
+
if (!action) {
|
|
7635
|
+
return
|
|
7636
|
+
}
|
|
7637
|
+
if (typeof action.handler === 'function') {
|
|
7638
|
+
action.handler(row);
|
|
7639
|
+
}
|
|
7640
|
+
this.onRowAction(rowActionPayload(action, row));
|
|
7641
|
+
},
|
|
7642
|
+
|
|
7363
7643
|
/**
|
|
7364
7644
|
* Take the server's version of a row after a conflict. Nothing of this
|
|
7365
7645
|
* person's edit is written, and the row in the list is refreshed to
|
|
@@ -8212,6 +8492,9 @@ var script = {
|
|
|
8212
8492
|
* @return {boolean} True for the current page.
|
|
8213
8493
|
*/
|
|
8214
8494
|
isActivePageTab(tab) {
|
|
8495
|
+
if (tab.to && typeof tab.to === 'object') {
|
|
8496
|
+
return !!this.$route && tab.to.name !== undefined && this.$route.name === tab.to.name
|
|
8497
|
+
}
|
|
8215
8498
|
const path = this.$route && this.$route.path;
|
|
8216
8499
|
return !!path && String(path).replace(/\/+$/, '') === String(tab.to).replace(/\/+$/, '')
|
|
8217
8500
|
},
|