@conduction/nextcloud-vue 2.70.0 → 2.72.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 (85) hide show
  1. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +151 -13
  2. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +3 -1
  4. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  5. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  6. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +38 -42
  7. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  8. package/dist/esm/components/CnFlowDetail/CnRunDetailSidebar.vue2.js +2 -0
  9. package/dist/esm/components/CnFlowDetail/CnRunDetailSidebar.vue2.js.map +1 -1
  10. package/dist/esm/components/CnFlowDetail/CnRunDetailSidebar.vue3.js +7 -0
  11. package/dist/esm/components/CnFlowDetail/CnRunDetailSidebar.vue3.js.map +1 -1
  12. package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue.js +10 -0
  13. package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue.js.map +1 -0
  14. package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue2.js +150 -0
  15. package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue2.js.map +1 -0
  16. package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue3.js +54 -0
  17. package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue3.js.map +1 -0
  18. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +29 -2
  19. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  20. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +71 -34
  21. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  22. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +22 -0
  23. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  24. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +1 -1
  25. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  26. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +1 -1
  27. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  28. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  29. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  30. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +46 -32
  31. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  32. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +30 -9
  33. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
  34. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
  35. package/dist/esm/composables/useWalkthrough.js +11 -3
  36. package/dist/esm/composables/useWalkthrough.js.map +1 -1
  37. package/dist/esm/dialogs/CnFlowNodeEditModal.vue2.js +7 -10
  38. package/dist/esm/dialogs/CnFlowNodeEditModal.vue2.js.map +1 -1
  39. package/dist/esm/dialogs/CnFlowNodeEditModal.vue3.js +23 -13
  40. package/dist/esm/dialogs/CnFlowNodeEditModal.vue3.js.map +1 -1
  41. package/dist/esm/dialogs/CnRunNodeDialog.vue2.js +8 -6
  42. package/dist/esm/dialogs/CnRunNodeDialog.vue2.js.map +1 -1
  43. package/dist/esm/dialogs/CnRunNodeDialog.vue3.js.map +1 -1
  44. package/dist/esm/index.js +1 -0
  45. package/dist/esm/index.js.map +1 -1
  46. package/dist/esm/l10n/en.json.js +15 -1
  47. package/dist/esm/l10n/en.json.js.map +1 -1
  48. package/dist/esm/l10n/nl.json.js +15 -1
  49. package/dist/esm/l10n/nl.json.js.map +1 -1
  50. package/dist/esm/nextcloud-vue.css +117 -0
  51. package/dist/esm/utils/columnFilters.js +54 -4
  52. package/dist/esm/utils/columnFilters.js.map +1 -1
  53. package/dist/esm/utils/selectOptions.js +57 -0
  54. package/dist/esm/utils/selectOptions.js.map +1 -0
  55. package/dist/esm/utils/userAutocomplete.js +69 -3
  56. package/dist/esm/utils/userAutocomplete.js.map +1 -1
  57. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  58. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  59. package/dist/nextcloud-vue.cjs.js +6560 -5880
  60. package/dist/nextcloud-vue.cjs.js.map +2 -2
  61. package/dist/nextcloud-vue.css +117 -0
  62. package/l10n/en.json +15 -1
  63. package/l10n/nl.json +15 -1
  64. package/package.json +1 -1
  65. package/src/components/CnAppRoot/CnAppRoot.vue +153 -13
  66. package/src/components/CnDashboardPage/CnDashboardPage.vue +20 -18
  67. package/src/components/CnFlowDetail/CnRunDetailSidebar.vue +6 -0
  68. package/src/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue +250 -0
  69. package/src/components/CnFlowStepOutcomes/index.js +8 -0
  70. package/src/components/CnFormDialog/CnFormDialog.vue +62 -2
  71. package/src/components/CnIndexPage/CnIndexPage.vue +23 -1
  72. package/src/components/CnObjectSidebar/CnNotesTab.vue +1 -1
  73. package/src/components/CnSetupWizard/CnSetupWizard.vue +7 -0
  74. package/src/components/CnWalkthrough/CnWalkthrough.vue +30 -9
  75. package/src/components/index.js +1 -0
  76. package/src/composables/useWalkthrough.js +11 -3
  77. package/src/css/dashboard.css +44 -0
  78. package/src/dialogs/CnFlowNodeEditModal.vue +41 -20
  79. package/src/dialogs/CnRunNodeDialog.vue +8 -6
  80. package/src/index.js +1 -0
  81. package/src/schemas/app-manifest-v2.schema.json +2 -1
  82. package/src/utils/columnFilters.js +54 -4
  83. package/src/utils/selectOptions.js +54 -0
  84. package/src/utils/userAutocomplete.js +68 -2
  85. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"CnRunNodeDialog.vue2.js","sources":["../../../src/dialogs/CnRunNodeDialog.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n -->\n\n<template>\n\t<NcDialog\n\t\t:name=\"tr(title)\"\n\t\tsize=\"normal\"\n\t\t:noClose=\"loading\"\n\t\tdata-testid=\"cn-run-node-dialog\"\n\t\t@closing=\"$emit('close')\">\n\t\t<NcNoteCard v-if=\"loadError\" type=\"error\" data-testid=\"cn-run-node-dialog-error\">\n\t\t\t{{ loadError }}\n\t\t</NcNoteCard>\n\n\t\t<div v-else class=\"cn-run-node-dialog__form\">\n\t\t\t<div\n\t\t\t\tv-for=\"field in fields\"\n\t\t\t\t:key=\"field.key\"\n\t\t\t\t:data-cn-field=\"field.key\"\n\t\t\t\tclass=\"cn-run-node-dialog__field\">\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\tv-if=\"field.type === 'boolean'\"\n\t\t\t\t\t:modelValue=\"values[field.key] === true\"\n\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\">\n\t\t\t\t\t{{ tr(field.label) }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-else-if=\"field.type === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"optionsFor(field)\"\n\t\t\t\t\t:inputLabel=\"tr(field.label)\"\n\t\t\t\t\t:loading=\"optionsLoading[field.key] === true\"\n\t\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Pick one…')\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event ? $event.id : '')\" />\n\n\t\t\t\t<NcTextArea\n\t\t\t\t\tv-else-if=\"field.type === 'textarea'\"\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\trows=\"4\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else-if=\"field.type === 'number'\"\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\t@update:modelValue=\"setNumberValue(field.key, $event)\" />\n\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\t\t\t</div>\n\n\t\t\t<p v-if=\"!fields.length\" class=\"cn-run-node-dialog__hint\">\n\t\t\t\t{{ t('nextcloud-vue', 'This step has no options.') }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"loading\" @click=\"$emit('close')\">\n\t\t\t\t{{ t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"!loadError\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"loading || !requiredFieldsFilled\"\n\t\t\t\t@click=\"$emit('confirm', values)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"loading\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Run') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</NcDialog>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcDialog, NcLoadingIcon, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport { prefixUrl } from '../utils/headers.js'\n\n/**\n * The dialog `run-node` opens to collect a directly-invoked flow node's own\n * declared config, before POSTing to OpenRegister's\n * `/api/flows/{flowId}/nodes/{nodeId}/run` (manifest-run-node-action / RN-4).\n *\n * WHY THIS IS A NEW, SMALL RENDERER AND NOT `CnFlowNodeEditModal`'S ONE\n * ----------------------------------------------------------------------\n * design.md says \"render it with the SAME field renderer CnFlowSidebar\n * [reads: CnFlowNodeEditModal] uses\" as the INTENT — no new form-rendering\n * VOCABULARY — and this component honours that: the type-to-widget mapping\n * (switch/select/textarea/number/text) and the `optionsFrom` fetch-and-\n * normalise behaviour below are the SAME rules that modal applies. What it\n * does NOT reuse is that modal's component/methods directly, because that\n * renderer solves a materially different problem: it guesses a widget for\n * ANY engine config key via `widgetFor()` heuristics (including keys no\n * `configForm()` ever declared — cron expressions, HTTP methods, principal\n * pickers, a `runAs` identity field, a raw-JSON fallback), because the flow\n * EDITOR must let an author set every key the engine reads whether or not\n * its type ever described one. A run-node CALLER dialog has no such need —\n * it renders ONLY the fields `configForm()` explicitly declares (this\n * interface's whole contract, per its own docblock: \"a field over a key the\n * node ignores looks like it works and changes nothing\") — text / textarea /\n * number / boolean / select, nothing else. Extracting a shared component\n * from the editor's modal is a reasonable follow-up; it was not done here to\n * avoid an invasive refactor of an unrelated, already-shipped surface for a\n * dialog whose actual field vocabulary is a fifth its size.\n *\n * @spec openspec/changes/manifest-run-node-action/specs/manifest-run-node-action/spec.md#requirement-the-nodes-own-config-form-drives-the-dialog-not-a-new-token\n */\nexport default {\n\tname: 'CnRunNodeDialog',\n\n\tcomponents: { NcButton, NcCheckboxRadioSwitch, NcDialog, NcLoadingIcon, NcNoteCard, NcSelect, NcTextArea, NcTextField },\n\n\tprops: {\n\t\t/** Pre-translated dialog title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/**\n\t\t * The node's declared config form — `IFlowNodeConfigForm::configForm()`'s\n\t\t * shape, unchanged: `[{key, label, type, help?, required?, optionsFrom?}]`.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\tfields: { type: Array, default: () => [] },\n\t\t/** Whether a run is in flight — disables Cancel/Run and shows a spinner. */\n\t\tloading: { type: Boolean, default: false },\n\t\t/** Set when the node's form could not be resolved; replaces the form with an error and hides Run. */\n\t\tloadError: { type: String, default: '' },\n\t\t/** The consumer's bound `t()`, matching CnActionButtons' own `cnTranslate` inject. */\n\t\ttranslate: { type: Function, default: null },\n\t},\n\n\temits: ['close', 'confirm'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Live field values, keyed by field.key. */\n\t\t\tvalues: {},\n\t\t\t/** Fetched `{id, label}` options per select field.key. */\n\t\t\toptions: {},\n\t\t\t/** In-flight flag per select field.key. */\n\t\t\toptionsLoading: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Every `required: true` field carries a non-empty value. */\n\t\trequiredFieldsFilled() {\n\t\t\treturn this.fields.every((field) => {\n\t\t\t\tif (field.required !== true) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tconst value = this.values[field.key]\n\t\t\t\treturn value !== undefined && value !== null && value !== ''\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tfields: {\n\t\t\timmediate: true,\n\t\t\thandler(fields) {\n\t\t\t\tconst seed = {}\n\t\t\t\tfor (const field of (fields || [])) {\n\t\t\t\t\tseed[field.key] = field.type === 'boolean' ? false : ''\n\t\t\t\t}\n\t\t\t\tthis.values = seed\n\t\t\t\tfor (const field of (fields || [])) {\n\t\t\t\t\tif (field.type === 'select' && field.optionsFrom) {\n\t\t\t\t\t\tthis.loadOptions(field)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Resolve a manifest/node-authored UI string through the host translate\n\t\t * function, matching CnActionButtons' own `tr()`.\n\t\t *\n\t\t * @param {string} [value] The English source string.\n\t\t * @return {string|undefined} The translated (or source) string.\n\t\t */\n\t\ttr(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\treturn typeof this.translate === 'function' ? this.translate(value) : value\n\t\t},\n\n\t\t/**\n\t\t * Write one field's value.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @param {unknown} value The new value.\n\t\t * @return {void}\n\t\t */\n\t\tsetValue(key, value) {\n\t\t\tthis.values = { ...this.values, [key]: value }\n\t\t},\n\n\t\t/**\n\t\t * Write a number field, coercing an empty string to '' (not 0 — an\n\t\t * empty required-number field must still fail `requiredFieldsFilled`)\n\t\t * and anything else to a JS number.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @param {string} raw The NcTextField string value.\n\t\t * @return {void}\n\t\t */\n\t\tsetNumberValue(key, raw) {\n\t\t\tthis.setValue(key, raw === '' ? '' : Number(raw))\n\t\t},\n\n\t\t/**\n\t\t * The NcSelect-shaped `{id, label}` currently selected for a field, or\n\t\t * null. Falls back to `{id: value, label: String(value)}` when the\n\t\t * stored value is not (yet, or no longer) among the loaded options —\n\t\t * the same graceful-degrade `CnFlowNodeEditModal` applies.\n\t\t *\n\t\t * @param {object} field The field declaration.\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\tconst value = this.values[field.key]\n\t\t\tif (value === undefined || value === null || value === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst opts = this.options[field.key] || []\n\t\t\treturn opts.find((o) => o.id === value) || { id: value, label: String(value) }\n\t\t},\n\n\t\t/**\n\t\t * The loaded `{id, label}` options for a select field.\n\t\t *\n\t\t * @param {object} field The field declaration.\n\t\t * @return {Array<object>}\n\t\t */\n\t\toptionsFor(field) {\n\t\t\treturn this.options[field.key] || []\n\t\t},\n\n\t\t/**\n\t\t * Fetch and normalise a select field's `optionsFrom` catalogue —\n\t\t * SAME acceptance shape as `CnFlowNodeEditModal`'s own picker (design.md's\n\t\t * point: reuse the FORM CONTRACT, not necessarily the component): a bare\n\t\t * array or `{results: [...]}`; each row read as `{id, label}`,\n\t\t * `{value, label}`, or an OpenRegister object (`@self.uuid`/`uuid`,\n\t\t * `name`/`title`). A row this dialog cannot make sense of is dropped\n\t\t * rather than shown as a blank option.\n\t\t *\n\t\t * @param {object} field The field declaration carrying `optionsFrom`.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadOptions(field) {\n\t\t\tthis.optionsLoading = { ...this.optionsLoading, [field.key]: true }\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }] = await Promise.all([import('@nextcloud/axios')])\n\t\t\t\tconst raw = String(field.optionsFrom)\n\t\t\t\tconst url = prefixUrl(raw)\n\t\t\t\tconst response = await axios.get(url)\n\t\t\t\tconst rows = Array.isArray(response.data) ? response.data : (response.data?.results || [])\n\t\t\t\tconst opts = rows.map((row) => {\n\t\t\t\t\tconst id = row.id ?? row.value ?? row['@self']?.uuid ?? row.uuid\n\t\t\t\t\treturn { id, label: row.label || row.name || row.title || String(id) }\n\t\t\t\t}).filter((o) => o.id !== undefined && o.id !== null && o.id !== '')\n\t\t\t\tthis.options = { ...this.options, [field.key]: opts }\n\t\t\t} catch (error) {\n\t\t\t\t// A picker that fails to load degrades to an empty list rather\n\t\t\t\t// than blocking the whole dialog — the field just shows no\n\t\t\t\t// choices, which is honest about what happened.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`cn-run-node-dialog: could not load options for \"${field.key}\"`, error)\n\t\t\t} finally {\n\t\t\t\tthis.optionsLoading = { ...this.optionsLoading, [field.key]: false }\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-run-node-dialog__form {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-run-node-dialog__hint {\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["t"],"mappings":"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"}
1
+ {"version":3,"file":"CnRunNodeDialog.vue2.js","sources":["../../../src/dialogs/CnRunNodeDialog.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n -->\n\n<template>\n\t<NcDialog\n\t\t:name=\"tr(title)\"\n\t\tsize=\"normal\"\n\t\t:noClose=\"loading\"\n\t\tdata-testid=\"cn-run-node-dialog\"\n\t\t@closing=\"$emit('close')\">\n\t\t<NcNoteCard v-if=\"loadError\" type=\"error\" data-testid=\"cn-run-node-dialog-error\">\n\t\t\t{{ loadError }}\n\t\t</NcNoteCard>\n\n\t\t<div v-else class=\"cn-run-node-dialog__form\">\n\t\t\t<div\n\t\t\t\tv-for=\"field in fields\"\n\t\t\t\t:key=\"field.key\"\n\t\t\t\t:data-cn-field=\"field.key\"\n\t\t\t\tclass=\"cn-run-node-dialog__field\">\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\tv-if=\"field.type === 'boolean'\"\n\t\t\t\t\t:modelValue=\"values[field.key] === true\"\n\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\">\n\t\t\t\t\t{{ tr(field.label) }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-else-if=\"field.type === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"optionsFor(field)\"\n\t\t\t\t\t:inputLabel=\"tr(field.label)\"\n\t\t\t\t\t:loading=\"optionsLoading[field.key] === true\"\n\t\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Pick one…')\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event ? $event.id : '')\" />\n\n\t\t\t\t<NcTextArea\n\t\t\t\t\tv-else-if=\"field.type === 'textarea'\"\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\trows=\"4\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else-if=\"field.type === 'number'\"\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\t@update:modelValue=\"setNumberValue(field.key, $event)\" />\n\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\t\t\t</div>\n\n\t\t\t<p v-if=\"!fields.length\" class=\"cn-run-node-dialog__hint\">\n\t\t\t\t{{ t('nextcloud-vue', 'This step has no options.') }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"loading\" @click=\"$emit('close')\">\n\t\t\t\t{{ t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"!loadError\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"loading || !requiredFieldsFilled\"\n\t\t\t\t@click=\"$emit('confirm', values)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"loading\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Run') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</NcDialog>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcDialog, NcLoadingIcon, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport { prefixUrl } from '../utils/headers.js'\nimport { hasStaticOptions, normaliseSelectOptions } from '../utils/selectOptions.js'\n\n/**\n * The dialog `run-node` opens to collect a directly-invoked flow node's own\n * declared config, before POSTing to OpenRegister's\n * `/api/flows/{flowId}/nodes/{nodeId}/run` (manifest-run-node-action / RN-4).\n *\n * WHY THIS IS A NEW, SMALL RENDERER AND NOT `CnFlowNodeEditModal`'S ONE\n * ----------------------------------------------------------------------\n * design.md says \"render it with the SAME field renderer CnFlowSidebar\n * [reads: CnFlowNodeEditModal] uses\" as the INTENT — no new form-rendering\n * VOCABULARY — and this component honours that: the type-to-widget mapping\n * (switch/select/textarea/number/text) and the `optionsFrom` fetch-and-\n * normalise behaviour below are the SAME rules that modal applies. What it\n * does NOT reuse is that modal's component/methods directly, because that\n * renderer solves a materially different problem: it guesses a widget for\n * ANY engine config key via `widgetFor()` heuristics (including keys no\n * `configForm()` ever declared — cron expressions, HTTP methods, principal\n * pickers, a `runAs` identity field, a raw-JSON fallback), because the flow\n * EDITOR must let an author set every key the engine reads whether or not\n * its type ever described one. A run-node CALLER dialog has no such need —\n * it renders ONLY the fields `configForm()` explicitly declares (this\n * interface's whole contract, per its own docblock: \"a field over a key the\n * node ignores looks like it works and changes nothing\") — text / textarea /\n * number / boolean / select, nothing else. Extracting a shared component\n * from the editor's modal is a reasonable follow-up; it was not done here to\n * avoid an invasive refactor of an unrelated, already-shipped surface for a\n * dialog whose actual field vocabulary is a fifth its size.\n *\n * @spec openspec/changes/manifest-run-node-action/specs/manifest-run-node-action/spec.md#requirement-the-nodes-own-config-form-drives-the-dialog-not-a-new-token\n */\nexport default {\n\tname: 'CnRunNodeDialog',\n\n\tcomponents: { NcButton, NcCheckboxRadioSwitch, NcDialog, NcLoadingIcon, NcNoteCard, NcSelect, NcTextArea, NcTextField },\n\n\tprops: {\n\t\t/** Pre-translated dialog title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/**\n\t\t * The node's declared config form — `IFlowNodeConfigForm::configForm()`'s\n\t\t * shape, unchanged: `[{key, label, type, help?, required?, optionsFrom?, options?}]`.\n\t\t * A `select` takes its choices from `optionsFrom` (a url) or `options`\n\t\t * (strings, or `{value, label}` pairs declared in the form itself).\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\tfields: { type: Array, default: () => [] },\n\t\t/** Whether a run is in flight — disables Cancel/Run and shows a spinner. */\n\t\tloading: { type: Boolean, default: false },\n\t\t/** Set when the node's form could not be resolved; replaces the form with an error and hides Run. */\n\t\tloadError: { type: String, default: '' },\n\t\t/** The consumer's bound `t()`, matching CnActionButtons' own `cnTranslate` inject. */\n\t\ttranslate: { type: Function, default: null },\n\t},\n\n\temits: ['close', 'confirm'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Live field values, keyed by field.key. */\n\t\t\tvalues: {},\n\t\t\t/** Fetched `{id, label}` options per select field.key. */\n\t\t\toptions: {},\n\t\t\t/** In-flight flag per select field.key. */\n\t\t\toptionsLoading: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Every `required: true` field carries a non-empty value. */\n\t\trequiredFieldsFilled() {\n\t\t\treturn this.fields.every((field) => {\n\t\t\t\tif (field.required !== true) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tconst value = this.values[field.key]\n\t\t\t\treturn value !== undefined && value !== null && value !== ''\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tfields: {\n\t\t\timmediate: true,\n\t\t\thandler(fields) {\n\t\t\t\tconst seed = {}\n\t\t\t\tfor (const field of (fields || [])) {\n\t\t\t\t\tseed[field.key] = field.type === 'boolean' ? false : ''\n\t\t\t\t}\n\t\t\t\tthis.values = seed\n\t\t\t\tfor (const field of (fields || [])) {\n\t\t\t\t\tif (hasStaticOptions(field)) {\n\t\t\t\t\t\tthis.options = { ...this.options, [field.key]: normaliseSelectOptions(field.options, (s) => this.tr(s)) }\n\t\t\t\t\t} else if (field.type === 'select' && field.optionsFrom) {\n\t\t\t\t\t\tthis.loadOptions(field)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Resolve a manifest/node-authored UI string through the host translate\n\t\t * function, matching CnActionButtons' own `tr()`.\n\t\t *\n\t\t * @param {string} [value] The English source string.\n\t\t * @return {string|undefined} The translated (or source) string.\n\t\t */\n\t\ttr(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\treturn typeof this.translate === 'function' ? this.translate(value) : value\n\t\t},\n\n\t\t/**\n\t\t * Write one field's value.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @param {unknown} value The new value.\n\t\t * @return {void}\n\t\t */\n\t\tsetValue(key, value) {\n\t\t\tthis.values = { ...this.values, [key]: value }\n\t\t},\n\n\t\t/**\n\t\t * Write a number field, coercing an empty string to '' (not 0 — an\n\t\t * empty required-number field must still fail `requiredFieldsFilled`)\n\t\t * and anything else to a JS number.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @param {string} raw The NcTextField string value.\n\t\t * @return {void}\n\t\t */\n\t\tsetNumberValue(key, raw) {\n\t\t\tthis.setValue(key, raw === '' ? '' : Number(raw))\n\t\t},\n\n\t\t/**\n\t\t * The NcSelect-shaped `{id, label}` currently selected for a field, or\n\t\t * null. Falls back to `{id: value, label: String(value)}` when the\n\t\t * stored value is not (yet, or no longer) among the loaded options —\n\t\t * the same graceful-degrade `CnFlowNodeEditModal` applies.\n\t\t *\n\t\t * @param {object} field The field declaration.\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\tconst value = this.values[field.key]\n\t\t\tif (value === undefined || value === null || value === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst opts = this.options[field.key] || []\n\t\t\treturn opts.find((o) => o.id === value) || { id: value, label: String(value) }\n\t\t},\n\n\t\t/**\n\t\t * The loaded `{id, label}` options for a select field.\n\t\t *\n\t\t * @param {object} field The field declaration.\n\t\t * @return {Array<object>}\n\t\t */\n\t\toptionsFor(field) {\n\t\t\treturn this.options[field.key] || []\n\t\t},\n\n\t\t/**\n\t\t * Fetch and normalise a select field's `optionsFrom` catalogue —\n\t\t * SAME acceptance shape as `CnFlowNodeEditModal`'s own picker (design.md's\n\t\t * point: reuse the FORM CONTRACT, not necessarily the component): a bare\n\t\t * array or `{results: [...]}`; each row read as `{id, label}`,\n\t\t * `{value, label}`, or an OpenRegister object (`@self.uuid`/`uuid`,\n\t\t * `name`/`title`). A row this dialog cannot make sense of is dropped\n\t\t * rather than shown as a blank option.\n\t\t *\n\t\t * @param {object} field The field declaration carrying `optionsFrom`.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadOptions(field) {\n\t\t\tthis.optionsLoading = { ...this.optionsLoading, [field.key]: true }\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }] = await Promise.all([import('@nextcloud/axios')])\n\t\t\t\tconst raw = String(field.optionsFrom)\n\t\t\t\tconst url = prefixUrl(raw)\n\t\t\t\tconst response = await axios.get(url)\n\t\t\t\tconst rows = Array.isArray(response.data) ? response.data : (response.data?.results || [])\n\t\t\t\tconst opts = normaliseSelectOptions(rows)\n\t\t\t\tthis.options = { ...this.options, [field.key]: opts }\n\t\t\t} catch (error) {\n\t\t\t\t// A picker that fails to load degrades to an empty list rather\n\t\t\t\t// than blocking the whole dialog — the field just shows no\n\t\t\t\t// choices, which is honest about what happened.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`cn-run-node-dialog: could not load options for \"${field.key}\"`, error)\n\t\t\t} finally {\n\t\t\t\tthis.optionsLoading = { ...this.optionsLoading, [field.key]: false }\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-run-node-dialog__form {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-run-node-dialog__hint {\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["t"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnRunNodeDialog.vue3.js","sources":["../../../src/dialogs/CnRunNodeDialog.vue?vue&type=template&id=3eb7c55d&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n -->\n\n<template>\n\t<NcDialog\n\t\t:name=\"tr(title)\"\n\t\tsize=\"normal\"\n\t\t:noClose=\"loading\"\n\t\tdata-testid=\"cn-run-node-dialog\"\n\t\t@closing=\"$emit('close')\">\n\t\t<NcNoteCard v-if=\"loadError\" type=\"error\" data-testid=\"cn-run-node-dialog-error\">\n\t\t\t{{ loadError }}\n\t\t</NcNoteCard>\n\n\t\t<div v-else class=\"cn-run-node-dialog__form\">\n\t\t\t<div\n\t\t\t\tv-for=\"field in fields\"\n\t\t\t\t:key=\"field.key\"\n\t\t\t\t:data-cn-field=\"field.key\"\n\t\t\t\tclass=\"cn-run-node-dialog__field\">\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\tv-if=\"field.type === 'boolean'\"\n\t\t\t\t\t:modelValue=\"values[field.key] === true\"\n\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\">\n\t\t\t\t\t{{ tr(field.label) }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-else-if=\"field.type === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"optionsFor(field)\"\n\t\t\t\t\t:inputLabel=\"tr(field.label)\"\n\t\t\t\t\t:loading=\"optionsLoading[field.key] === true\"\n\t\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Pick one…')\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event ? $event.id : '')\" />\n\n\t\t\t\t<NcTextArea\n\t\t\t\t\tv-else-if=\"field.type === 'textarea'\"\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\trows=\"4\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else-if=\"field.type === 'number'\"\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\t@update:modelValue=\"setNumberValue(field.key, $event)\" />\n\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\t\t\t</div>\n\n\t\t\t<p v-if=\"!fields.length\" class=\"cn-run-node-dialog__hint\">\n\t\t\t\t{{ t('nextcloud-vue', 'This step has no options.') }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"loading\" @click=\"$emit('close')\">\n\t\t\t\t{{ t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"!loadError\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"loading || !requiredFieldsFilled\"\n\t\t\t\t@click=\"$emit('confirm', values)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"loading\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Run') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</NcDialog>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcDialog, NcLoadingIcon, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport { prefixUrl } from '../utils/headers.js'\n\n/**\n * The dialog `run-node` opens to collect a directly-invoked flow node's own\n * declared config, before POSTing to OpenRegister's\n * `/api/flows/{flowId}/nodes/{nodeId}/run` (manifest-run-node-action / RN-4).\n *\n * WHY THIS IS A NEW, SMALL RENDERER AND NOT `CnFlowNodeEditModal`'S ONE\n * ----------------------------------------------------------------------\n * design.md says \"render it with the SAME field renderer CnFlowSidebar\n * [reads: CnFlowNodeEditModal] uses\" as the INTENT — no new form-rendering\n * VOCABULARY — and this component honours that: the type-to-widget mapping\n * (switch/select/textarea/number/text) and the `optionsFrom` fetch-and-\n * normalise behaviour below are the SAME rules that modal applies. What it\n * does NOT reuse is that modal's component/methods directly, because that\n * renderer solves a materially different problem: it guesses a widget for\n * ANY engine config key via `widgetFor()` heuristics (including keys no\n * `configForm()` ever declared — cron expressions, HTTP methods, principal\n * pickers, a `runAs` identity field, a raw-JSON fallback), because the flow\n * EDITOR must let an author set every key the engine reads whether or not\n * its type ever described one. A run-node CALLER dialog has no such need —\n * it renders ONLY the fields `configForm()` explicitly declares (this\n * interface's whole contract, per its own docblock: \"a field over a key the\n * node ignores looks like it works and changes nothing\") — text / textarea /\n * number / boolean / select, nothing else. Extracting a shared component\n * from the editor's modal is a reasonable follow-up; it was not done here to\n * avoid an invasive refactor of an unrelated, already-shipped surface for a\n * dialog whose actual field vocabulary is a fifth its size.\n *\n * @spec openspec/changes/manifest-run-node-action/specs/manifest-run-node-action/spec.md#requirement-the-nodes-own-config-form-drives-the-dialog-not-a-new-token\n */\nexport default {\n\tname: 'CnRunNodeDialog',\n\n\tcomponents: { NcButton, NcCheckboxRadioSwitch, NcDialog, NcLoadingIcon, NcNoteCard, NcSelect, NcTextArea, NcTextField },\n\n\tprops: {\n\t\t/** Pre-translated dialog title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/**\n\t\t * The node's declared config form — `IFlowNodeConfigForm::configForm()`'s\n\t\t * shape, unchanged: `[{key, label, type, help?, required?, optionsFrom?}]`.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\tfields: { type: Array, default: () => [] },\n\t\t/** Whether a run is in flight — disables Cancel/Run and shows a spinner. */\n\t\tloading: { type: Boolean, default: false },\n\t\t/** Set when the node's form could not be resolved; replaces the form with an error and hides Run. */\n\t\tloadError: { type: String, default: '' },\n\t\t/** The consumer's bound `t()`, matching CnActionButtons' own `cnTranslate` inject. */\n\t\ttranslate: { type: Function, default: null },\n\t},\n\n\temits: ['close', 'confirm'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Live field values, keyed by field.key. */\n\t\t\tvalues: {},\n\t\t\t/** Fetched `{id, label}` options per select field.key. */\n\t\t\toptions: {},\n\t\t\t/** In-flight flag per select field.key. */\n\t\t\toptionsLoading: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Every `required: true` field carries a non-empty value. */\n\t\trequiredFieldsFilled() {\n\t\t\treturn this.fields.every((field) => {\n\t\t\t\tif (field.required !== true) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tconst value = this.values[field.key]\n\t\t\t\treturn value !== undefined && value !== null && value !== ''\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tfields: {\n\t\t\timmediate: true,\n\t\t\thandler(fields) {\n\t\t\t\tconst seed = {}\n\t\t\t\tfor (const field of (fields || [])) {\n\t\t\t\t\tseed[field.key] = field.type === 'boolean' ? false : ''\n\t\t\t\t}\n\t\t\t\tthis.values = seed\n\t\t\t\tfor (const field of (fields || [])) {\n\t\t\t\t\tif (field.type === 'select' && field.optionsFrom) {\n\t\t\t\t\t\tthis.loadOptions(field)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Resolve a manifest/node-authored UI string through the host translate\n\t\t * function, matching CnActionButtons' own `tr()`.\n\t\t *\n\t\t * @param {string} [value] The English source string.\n\t\t * @return {string|undefined} The translated (or source) string.\n\t\t */\n\t\ttr(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\treturn typeof this.translate === 'function' ? this.translate(value) : value\n\t\t},\n\n\t\t/**\n\t\t * Write one field's value.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @param {unknown} value The new value.\n\t\t * @return {void}\n\t\t */\n\t\tsetValue(key, value) {\n\t\t\tthis.values = { ...this.values, [key]: value }\n\t\t},\n\n\t\t/**\n\t\t * Write a number field, coercing an empty string to '' (not 0 — an\n\t\t * empty required-number field must still fail `requiredFieldsFilled`)\n\t\t * and anything else to a JS number.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @param {string} raw The NcTextField string value.\n\t\t * @return {void}\n\t\t */\n\t\tsetNumberValue(key, raw) {\n\t\t\tthis.setValue(key, raw === '' ? '' : Number(raw))\n\t\t},\n\n\t\t/**\n\t\t * The NcSelect-shaped `{id, label}` currently selected for a field, or\n\t\t * null. Falls back to `{id: value, label: String(value)}` when the\n\t\t * stored value is not (yet, or no longer) among the loaded options —\n\t\t * the same graceful-degrade `CnFlowNodeEditModal` applies.\n\t\t *\n\t\t * @param {object} field The field declaration.\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\tconst value = this.values[field.key]\n\t\t\tif (value === undefined || value === null || value === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst opts = this.options[field.key] || []\n\t\t\treturn opts.find((o) => o.id === value) || { id: value, label: String(value) }\n\t\t},\n\n\t\t/**\n\t\t * The loaded `{id, label}` options for a select field.\n\t\t *\n\t\t * @param {object} field The field declaration.\n\t\t * @return {Array<object>}\n\t\t */\n\t\toptionsFor(field) {\n\t\t\treturn this.options[field.key] || []\n\t\t},\n\n\t\t/**\n\t\t * Fetch and normalise a select field's `optionsFrom` catalogue —\n\t\t * SAME acceptance shape as `CnFlowNodeEditModal`'s own picker (design.md's\n\t\t * point: reuse the FORM CONTRACT, not necessarily the component): a bare\n\t\t * array or `{results: [...]}`; each row read as `{id, label}`,\n\t\t * `{value, label}`, or an OpenRegister object (`@self.uuid`/`uuid`,\n\t\t * `name`/`title`). A row this dialog cannot make sense of is dropped\n\t\t * rather than shown as a blank option.\n\t\t *\n\t\t * @param {object} field The field declaration carrying `optionsFrom`.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadOptions(field) {\n\t\t\tthis.optionsLoading = { ...this.optionsLoading, [field.key]: true }\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }] = await Promise.all([import('@nextcloud/axios')])\n\t\t\t\tconst raw = String(field.optionsFrom)\n\t\t\t\tconst url = prefixUrl(raw)\n\t\t\t\tconst response = await axios.get(url)\n\t\t\t\tconst rows = Array.isArray(response.data) ? response.data : (response.data?.results || [])\n\t\t\t\tconst opts = rows.map((row) => {\n\t\t\t\t\tconst id = row.id ?? row.value ?? row['@self']?.uuid ?? row.uuid\n\t\t\t\t\treturn { id, label: row.label || row.name || row.title || String(id) }\n\t\t\t\t}).filter((o) => o.id !== undefined && o.id !== null && o.id !== '')\n\t\t\t\tthis.options = { ...this.options, [field.key]: opts }\n\t\t\t} catch (error) {\n\t\t\t\t// A picker that fails to load degrades to an empty list rather\n\t\t\t\t// than blocking the whole dialog — the field just shows no\n\t\t\t\t// choices, which is honest about what happened.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`cn-run-node-dialog: could not load options for \"${field.key}\"`, error)\n\t\t\t} finally {\n\t\t\t\tthis.optionsLoading = { ...this.optionsLoading, [field.key]: false }\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-run-node-dialog__form {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-run-node-dialog__hint {\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_createVNode","_createBlock","_openBlock","_createElementBlock","_Fragment","_renderList","_toDisplayString"],"mappings":"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"}
1
+ {"version":3,"file":"CnRunNodeDialog.vue3.js","sources":["../../../src/dialogs/CnRunNodeDialog.vue?vue&type=template&id=3eb7c55d&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n -->\n\n<template>\n\t<NcDialog\n\t\t:name=\"tr(title)\"\n\t\tsize=\"normal\"\n\t\t:noClose=\"loading\"\n\t\tdata-testid=\"cn-run-node-dialog\"\n\t\t@closing=\"$emit('close')\">\n\t\t<NcNoteCard v-if=\"loadError\" type=\"error\" data-testid=\"cn-run-node-dialog-error\">\n\t\t\t{{ loadError }}\n\t\t</NcNoteCard>\n\n\t\t<div v-else class=\"cn-run-node-dialog__form\">\n\t\t\t<div\n\t\t\t\tv-for=\"field in fields\"\n\t\t\t\t:key=\"field.key\"\n\t\t\t\t:data-cn-field=\"field.key\"\n\t\t\t\tclass=\"cn-run-node-dialog__field\">\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\tv-if=\"field.type === 'boolean'\"\n\t\t\t\t\t:modelValue=\"values[field.key] === true\"\n\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\">\n\t\t\t\t\t{{ tr(field.label) }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-else-if=\"field.type === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"optionsFor(field)\"\n\t\t\t\t\t:inputLabel=\"tr(field.label)\"\n\t\t\t\t\t:loading=\"optionsLoading[field.key] === true\"\n\t\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Pick one…')\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event ? $event.id : '')\" />\n\n\t\t\t\t<NcTextArea\n\t\t\t\t\tv-else-if=\"field.type === 'textarea'\"\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\trows=\"4\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else-if=\"field.type === 'number'\"\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\t@update:modelValue=\"setNumberValue(field.key, $event)\" />\n\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\t:modelValue=\"String(values[field.key] ?? '')\"\n\t\t\t\t\t:label=\"tr(field.label)\"\n\t\t\t\t\t:helperText=\"tr(field.help)\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\t\t\t</div>\n\n\t\t\t<p v-if=\"!fields.length\" class=\"cn-run-node-dialog__hint\">\n\t\t\t\t{{ t('nextcloud-vue', 'This step has no options.') }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"loading\" @click=\"$emit('close')\">\n\t\t\t\t{{ t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"!loadError\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"loading || !requiredFieldsFilled\"\n\t\t\t\t@click=\"$emit('confirm', values)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"loading\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Run') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</NcDialog>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcDialog, NcLoadingIcon, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport { prefixUrl } from '../utils/headers.js'\nimport { hasStaticOptions, normaliseSelectOptions } from '../utils/selectOptions.js'\n\n/**\n * The dialog `run-node` opens to collect a directly-invoked flow node's own\n * declared config, before POSTing to OpenRegister's\n * `/api/flows/{flowId}/nodes/{nodeId}/run` (manifest-run-node-action / RN-4).\n *\n * WHY THIS IS A NEW, SMALL RENDERER AND NOT `CnFlowNodeEditModal`'S ONE\n * ----------------------------------------------------------------------\n * design.md says \"render it with the SAME field renderer CnFlowSidebar\n * [reads: CnFlowNodeEditModal] uses\" as the INTENT — no new form-rendering\n * VOCABULARY — and this component honours that: the type-to-widget mapping\n * (switch/select/textarea/number/text) and the `optionsFrom` fetch-and-\n * normalise behaviour below are the SAME rules that modal applies. What it\n * does NOT reuse is that modal's component/methods directly, because that\n * renderer solves a materially different problem: it guesses a widget for\n * ANY engine config key via `widgetFor()` heuristics (including keys no\n * `configForm()` ever declared — cron expressions, HTTP methods, principal\n * pickers, a `runAs` identity field, a raw-JSON fallback), because the flow\n * EDITOR must let an author set every key the engine reads whether or not\n * its type ever described one. A run-node CALLER dialog has no such need —\n * it renders ONLY the fields `configForm()` explicitly declares (this\n * interface's whole contract, per its own docblock: \"a field over a key the\n * node ignores looks like it works and changes nothing\") — text / textarea /\n * number / boolean / select, nothing else. Extracting a shared component\n * from the editor's modal is a reasonable follow-up; it was not done here to\n * avoid an invasive refactor of an unrelated, already-shipped surface for a\n * dialog whose actual field vocabulary is a fifth its size.\n *\n * @spec openspec/changes/manifest-run-node-action/specs/manifest-run-node-action/spec.md#requirement-the-nodes-own-config-form-drives-the-dialog-not-a-new-token\n */\nexport default {\n\tname: 'CnRunNodeDialog',\n\n\tcomponents: { NcButton, NcCheckboxRadioSwitch, NcDialog, NcLoadingIcon, NcNoteCard, NcSelect, NcTextArea, NcTextField },\n\n\tprops: {\n\t\t/** Pre-translated dialog title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/**\n\t\t * The node's declared config form — `IFlowNodeConfigForm::configForm()`'s\n\t\t * shape, unchanged: `[{key, label, type, help?, required?, optionsFrom?, options?}]`.\n\t\t * A `select` takes its choices from `optionsFrom` (a url) or `options`\n\t\t * (strings, or `{value, label}` pairs declared in the form itself).\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\tfields: { type: Array, default: () => [] },\n\t\t/** Whether a run is in flight — disables Cancel/Run and shows a spinner. */\n\t\tloading: { type: Boolean, default: false },\n\t\t/** Set when the node's form could not be resolved; replaces the form with an error and hides Run. */\n\t\tloadError: { type: String, default: '' },\n\t\t/** The consumer's bound `t()`, matching CnActionButtons' own `cnTranslate` inject. */\n\t\ttranslate: { type: Function, default: null },\n\t},\n\n\temits: ['close', 'confirm'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Live field values, keyed by field.key. */\n\t\t\tvalues: {},\n\t\t\t/** Fetched `{id, label}` options per select field.key. */\n\t\t\toptions: {},\n\t\t\t/** In-flight flag per select field.key. */\n\t\t\toptionsLoading: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Every `required: true` field carries a non-empty value. */\n\t\trequiredFieldsFilled() {\n\t\t\treturn this.fields.every((field) => {\n\t\t\t\tif (field.required !== true) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tconst value = this.values[field.key]\n\t\t\t\treturn value !== undefined && value !== null && value !== ''\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tfields: {\n\t\t\timmediate: true,\n\t\t\thandler(fields) {\n\t\t\t\tconst seed = {}\n\t\t\t\tfor (const field of (fields || [])) {\n\t\t\t\t\tseed[field.key] = field.type === 'boolean' ? false : ''\n\t\t\t\t}\n\t\t\t\tthis.values = seed\n\t\t\t\tfor (const field of (fields || [])) {\n\t\t\t\t\tif (hasStaticOptions(field)) {\n\t\t\t\t\t\tthis.options = { ...this.options, [field.key]: normaliseSelectOptions(field.options, (s) => this.tr(s)) }\n\t\t\t\t\t} else if (field.type === 'select' && field.optionsFrom) {\n\t\t\t\t\t\tthis.loadOptions(field)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Resolve a manifest/node-authored UI string through the host translate\n\t\t * function, matching CnActionButtons' own `tr()`.\n\t\t *\n\t\t * @param {string} [value] The English source string.\n\t\t * @return {string|undefined} The translated (or source) string.\n\t\t */\n\t\ttr(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\treturn typeof this.translate === 'function' ? this.translate(value) : value\n\t\t},\n\n\t\t/**\n\t\t * Write one field's value.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @param {unknown} value The new value.\n\t\t * @return {void}\n\t\t */\n\t\tsetValue(key, value) {\n\t\t\tthis.values = { ...this.values, [key]: value }\n\t\t},\n\n\t\t/**\n\t\t * Write a number field, coercing an empty string to '' (not 0 — an\n\t\t * empty required-number field must still fail `requiredFieldsFilled`)\n\t\t * and anything else to a JS number.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @param {string} raw The NcTextField string value.\n\t\t * @return {void}\n\t\t */\n\t\tsetNumberValue(key, raw) {\n\t\t\tthis.setValue(key, raw === '' ? '' : Number(raw))\n\t\t},\n\n\t\t/**\n\t\t * The NcSelect-shaped `{id, label}` currently selected for a field, or\n\t\t * null. Falls back to `{id: value, label: String(value)}` when the\n\t\t * stored value is not (yet, or no longer) among the loaded options —\n\t\t * the same graceful-degrade `CnFlowNodeEditModal` applies.\n\t\t *\n\t\t * @param {object} field The field declaration.\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\tconst value = this.values[field.key]\n\t\t\tif (value === undefined || value === null || value === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst opts = this.options[field.key] || []\n\t\t\treturn opts.find((o) => o.id === value) || { id: value, label: String(value) }\n\t\t},\n\n\t\t/**\n\t\t * The loaded `{id, label}` options for a select field.\n\t\t *\n\t\t * @param {object} field The field declaration.\n\t\t * @return {Array<object>}\n\t\t */\n\t\toptionsFor(field) {\n\t\t\treturn this.options[field.key] || []\n\t\t},\n\n\t\t/**\n\t\t * Fetch and normalise a select field's `optionsFrom` catalogue —\n\t\t * SAME acceptance shape as `CnFlowNodeEditModal`'s own picker (design.md's\n\t\t * point: reuse the FORM CONTRACT, not necessarily the component): a bare\n\t\t * array or `{results: [...]}`; each row read as `{id, label}`,\n\t\t * `{value, label}`, or an OpenRegister object (`@self.uuid`/`uuid`,\n\t\t * `name`/`title`). A row this dialog cannot make sense of is dropped\n\t\t * rather than shown as a blank option.\n\t\t *\n\t\t * @param {object} field The field declaration carrying `optionsFrom`.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadOptions(field) {\n\t\t\tthis.optionsLoading = { ...this.optionsLoading, [field.key]: true }\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }] = await Promise.all([import('@nextcloud/axios')])\n\t\t\t\tconst raw = String(field.optionsFrom)\n\t\t\t\tconst url = prefixUrl(raw)\n\t\t\t\tconst response = await axios.get(url)\n\t\t\t\tconst rows = Array.isArray(response.data) ? response.data : (response.data?.results || [])\n\t\t\t\tconst opts = normaliseSelectOptions(rows)\n\t\t\t\tthis.options = { ...this.options, [field.key]: opts }\n\t\t\t} catch (error) {\n\t\t\t\t// A picker that fails to load degrades to an empty list rather\n\t\t\t\t// than blocking the whole dialog — the field just shows no\n\t\t\t\t// choices, which is honest about what happened.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`cn-run-node-dialog: could not load options for \"${field.key}\"`, error)\n\t\t\t} finally {\n\t\t\t\tthis.optionsLoading = { ...this.optionsLoading, [field.key]: false }\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-run-node-dialog__form {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-run-node-dialog__hint {\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_createVNode","_createBlock","_openBlock","_createElementBlock","_Fragment","_renderList","_toDisplayString"],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -142,6 +142,7 @@ export { default as CnFlowRunsWidget } from './components/CnFlowRunsWidget/CnFlo
142
142
  export { default as CnFlowRunsWidgetForm } from './components/CnFlowRunsWidgetForm/CnFlowRunsWidgetForm.vue.js';
143
143
  export { default as CnFlowSidebar } from './components/CnFlowDetail/CnFlowSidebar.vue.js';
144
144
  export { default as CnFlowsPage } from './components/CnFlowsPage/CnFlowsPage.vue.js';
145
+ export { default as CnFlowStepOutcomes } from './components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue.js';
145
146
  export { default as CnFolderSidebar } from './components/CnFolderSidebar/CnFolderSidebar.vue.js';
146
147
  export { default as CnFolderTree } from './components/CnFolderTree/CnFolderTree.vue.js';
147
148
  export { default as CnFormBuilder } from './components/CnFormBuilder/CnFormBuilder.vue.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1090,7 +1090,21 @@ var translations = {
1090
1090
  "Loading…": "Loading…",
1091
1091
  "A register and schema are required": "A register and schema are required",
1092
1092
  "Change the search or try again.": "Change the search or try again.",
1093
- "Try again later.": "Try again later."
1093
+ "Try again later.": "Try again later.",
1094
+ "Who this step reached": "Who this step reached",
1095
+ "Each list shows its first entries only.": "Each list shows its first entries only.",
1096
+ Delivered: "Delivered",
1097
+ "Opted out": "Opted out",
1098
+ "Not sent, the opt-out check did not answer": "Not sent, the opt-out check did not answer",
1099
+ "Skipped by their notification settings": "Skipped by their notification settings",
1100
+ "Skipped, sending is switched off": "Skipped, sending is switched off",
1101
+ "Held back by the send limit": "Held back by the send limit",
1102
+ "Refused by the step’s address rule": "Refused by the step’s address rule",
1103
+ "Not a known user or group": "Not a known user or group",
1104
+ Failed: "Failed",
1105
+ "external recipients are off": "external recipients are off",
1106
+ "not on the item": "not on the item",
1107
+ "not a valid address": "not a valid address"
1094
1108
  };
1095
1109
  var plurals = {
1096
1110
  "%n year": [
@@ -1 +1 @@
1
- {"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1142,7 +1142,21 @@ var translations = {
1142
1142
  "Loading…": "Laden…",
1143
1143
  "A register and schema are required": "Een register en schema zijn verplicht",
1144
1144
  "Change the search or try again.": "Pas de zoekopdracht aan of probeer het opnieuw.",
1145
- "Try again later.": "Probeer het later opnieuw."
1145
+ "Try again later.": "Probeer het later opnieuw.",
1146
+ "Who this step reached": "Wie deze stap bereikte",
1147
+ "Each list shows its first entries only.": "Elke lijst toont alleen de eerste vermeldingen.",
1148
+ Delivered: "Bezorgd",
1149
+ "Opted out": "Afgemeld",
1150
+ "Not sent, the opt-out check did not answer": "Niet verstuurd, de afmeldcontrole gaf geen antwoord",
1151
+ "Skipped by their notification settings": "Overgeslagen door hun meldingsinstellingen",
1152
+ "Skipped, sending is switched off": "Overgeslagen, verzenden staat uit",
1153
+ "Held back by the send limit": "Tegengehouden door de verzendlimiet",
1154
+ "Refused by the step’s address rule": "Geweigerd door de adresregel van de stap",
1155
+ "Not a known user or group": "Geen bekende gebruiker of groep",
1156
+ Failed: "Mislukt",
1157
+ "external recipients are off": "externe ontvangers staan uit",
1158
+ "not on the item": "staat niet op het item",
1159
+ "not a valid address": "geen geldig adres"
1146
1160
  };
1147
1161
  var plurals = {
1148
1162
  "%n year": [
@@ -1 +1 @@
1
- {"version":3,"file":"nl.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"nl.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -2841,6 +2841,50 @@ a.cn-kpi-card--stacked:focus-visible {
2841
2841
  .cn-requires-app.cn-requires-app .empty-content__action {
2842
2842
  margin-top: var(--default-grid-baseline, 4px);
2843
2843
  }
2844
+
2845
+ /* NcEmptyContent forces its icon's svg to 64px with !important while the
2846
+ * rule above lets the icon box shrink, so the svg spilled out of its box and
2847
+ * over the name. Hold the svg to the size the caller asked for. */
2848
+ .cn-requires-app.cn-requires-app .empty-content__icon svg {
2849
+ width: 32px !important;
2850
+ height: 32px !important;
2851
+ max-width: 32px !important;
2852
+ max-height: 32px !important;
2853
+ }
2854
+
2855
+ /* The dashboard's compact "Install <app>" placeholder: two lines of text and
2856
+ * the button, nothing else, so it fits a two-row tile even under a title row.
2857
+ * `safe center` keeps the top in view when a tile is smaller still, and the
2858
+ * body scrolls rather than clipping the button. */
2859
+ .cn-requires-app--compact {
2860
+ display: flex;
2861
+ flex-direction: column;
2862
+ align-items: center;
2863
+ justify-content: safe center;
2864
+ gap: calc(2 * var(--default-grid-baseline, 4px));
2865
+ box-sizing: border-box;
2866
+ height: 100%;
2867
+ min-height: 0;
2868
+ overflow: auto;
2869
+ padding: calc(2 * var(--default-grid-baseline, 4px));
2870
+ text-align: center;
2871
+ }
2872
+
2873
+ .cn-requires-app--compact .cn-requires-app__description {
2874
+ display: -webkit-box;
2875
+ margin: 0;
2876
+ overflow: hidden;
2877
+ color: var(--color-text-maxcontrast);
2878
+ font-size: 0.9em;
2879
+ line-height: 1.4;
2880
+ -webkit-box-orient: vertical;
2881
+ -webkit-line-clamp: 2;
2882
+ line-clamp: 2;
2883
+ }
2884
+
2885
+ .cn-requires-app--compact .button-vue {
2886
+ flex-shrink: 0;
2887
+ }
2844
2888
  /* ========================================
2845
2889
  @conduction/nextcloud-vue — CnDetailPage Styles
2846
2890
  ======================================== */
@@ -15645,6 +15689,15 @@ a.cn-xwiki-card__title[data-v-54e85fe3]:hover {
15645
15689
  }
15646
15690
 
15647
15691
 
15692
+ /* A select option breaks between words only, never inside one. */
15693
+ .cn-form-dialog__option[data-v-c3eeb0a2] {
15694
+ display: block;
15695
+ min-width: 0;
15696
+ white-space: normal;
15697
+ overflow-wrap: normal;
15698
+ word-break: normal;
15699
+ hyphens: manual;
15700
+ }
15648
15701
  .cn-form-dialog__draft-actions[data-v-c3eeb0a2] {
15649
15702
  display: flex;
15650
15703
  gap: 8px;
@@ -26529,6 +26582,18 @@ a.cn-flow-runs-widget__row-content:hover .cn-flow-runs-widget__name[data-v-75628
26529
26582
  font-size: 0.85em;
26530
26583
  color: var(--color-text-maxcontrast);
26531
26584
  }
26585
+
26586
+ /* Full width, like the text fields around it: at its natural width the
26587
+ picker cut every option label in half. */
26588
+ .cn-flow-node-edit__select[data-v-e107484a] {
26589
+ width: 100%;
26590
+ }
26591
+ .cn-flow-node-edit__field-help[data-v-e107484a] {
26592
+ margin: 4px 0 0;
26593
+ padding-inline-start: 8px;
26594
+ font-size: 0.85em;
26595
+ color: var(--color-text-maxcontrast);
26596
+ }
26532
26597
  .cn-flow-node-edit__advanced summary[data-v-e107484a] {
26533
26598
  cursor: pointer;
26534
26599
  color: var(--color-text-maxcontrast);
@@ -27983,6 +28048,58 @@ to {
27983
28048
  }
27984
28049
 
27985
28050
 
28051
+ .cn-flow-step-outcomes[data-v-5ac5ad4f] {
28052
+ margin: 4px 0 0;
28053
+ font-size: 0.9em;
28054
+ }
28055
+ .cn-flow-step-outcomes__list[data-v-5ac5ad4f] {
28056
+ margin: 0;
28057
+ padding: 0;
28058
+ }
28059
+
28060
+ /* Stacked, name over value: the run sidebar is narrow, and a side-by-side
28061
+ row wrapped mid-list. The host's dt/dd margins and alignment are reset. */
28062
+ .cn-flow-step-outcomes__row[data-v-5ac5ad4f] {
28063
+ display: block;
28064
+ margin-block: 4px;
28065
+ padding-block: 2px;
28066
+ padding-inline-start: 8px;
28067
+ border-inline-start: 3px solid var(--color-border);
28068
+ text-align: start;
28069
+ }
28070
+ .cn-flow-step-outcomes__row--success[data-v-5ac5ad4f] {
28071
+ border-inline-start-color: var(--color-success);
28072
+ }
28073
+ .cn-flow-step-outcomes__row--warning[data-v-5ac5ad4f] {
28074
+ border-inline-start-color: var(--color-warning);
28075
+ }
28076
+ .cn-flow-step-outcomes__row--error[data-v-5ac5ad4f] {
28077
+ border-inline-start-color: var(--color-error);
28078
+ }
28079
+ .cn-flow-step-outcomes__name[data-v-5ac5ad4f] {
28080
+ display: block;
28081
+ white-space: normal;
28082
+ overflow-wrap: anywhere;
28083
+ margin: 0;
28084
+ padding: 0;
28085
+ font-weight: bold;
28086
+ text-align: start;
28087
+ }
28088
+ .cn-flow-step-outcomes__value[data-v-5ac5ad4f] {
28089
+ display: block;
28090
+ white-space: normal;
28091
+ margin: 0;
28092
+ padding: 0;
28093
+ text-align: start;
28094
+ color: var(--color-main-text);
28095
+ overflow-wrap: anywhere;
28096
+ }
28097
+ .cn-flow-step-outcomes__truncated[data-v-5ac5ad4f] {
28098
+ margin: 4px 0 0;
28099
+ color: var(--color-text-maxcontrast);
28100
+ }
28101
+
28102
+
27986
28103
  .cn-run-sidebar[data-v-48e781f0] {
27987
28104
  display: flex;
27988
28105
  flex-direction: column;
@@ -142,15 +142,65 @@ function referenceTarget(column, prop) {
142
142
  return { schema, register, labelField }
143
143
  }
144
144
 
145
+ /**
146
+ * Whether a schema lists its properties. Without that the table knows its
147
+ * columns but not what backs them.
148
+ *
149
+ * @param {object|null} schema The table's schema.
150
+ * @return {boolean} True when the schema has a properties map.
151
+ */
152
+ function hasProperties(schema) {
153
+ return !!(schema && typeof schema === 'object' && schema.properties && typeof schema.properties === 'object')
154
+ }
155
+
156
+ /**
157
+ * A filter for a column when the table has no schema to read (a manifest
158
+ * page whose schema did not resolve, a host-fed table). The column's own
159
+ * hints decide the kind: an `fkResolve` widget gives a reference filter, a
160
+ * `type` or `format` on the column gives number, date or yes/no, and every
161
+ * other plain stored key gets a text filter on contains (`[like]`). A key
162
+ * that is a path (`a.b`) or a metadata field (`@self`, `_id`) gets none,
163
+ * because OpenRegister does not filter those by that name.
164
+ *
165
+ * @spec openspec/changes/audit-round-lib-fixes/specs/cn-data-table/spec.md
166
+ * @param {object} column The column definition.
167
+ * @param {object} base The shared part of the definition.
168
+ * @return {object|null} The filter definition.
169
+ */
170
+ function columnHintFilterDef(column, base) {
171
+ const key = column.key;
172
+ if (key.includes('.') || key.charAt(0) === '@' || key.charAt(0) === '_') {
173
+ return null
174
+ }
175
+ const reference = referenceTarget(column, null);
176
+ if (reference) {
177
+ return { ...base, kind: 'reference', reference }
178
+ }
179
+ const type = String(column.type || '').toLowerCase();
180
+ const format = String(column.format || '').toLowerCase();
181
+ if (type === 'boolean') {
182
+ return { ...base, kind: 'boolean' }
183
+ }
184
+ if (type === 'number' || type === 'integer') {
185
+ return { ...base, kind: 'number' }
186
+ }
187
+ if (type === 'date' || type === 'date-time' || format === 'date' || format === 'date-time') {
188
+ return { ...base, kind: 'date', dateTime: type === 'date-time' || format === 'date-time' }
189
+ }
190
+ return { ...base, kind: 'string' }
191
+ }
192
+
145
193
  /**
146
194
  * How a column filters, or null when it does not.
147
195
  *
148
196
  * Kinds: `enum` (checkbox list), `boolean` (yes, no, any), `reference`
149
197
  * (searchable list of the referenced objects), `number` and `date` (from and
150
198
  * to), and `string` (contains, through OpenRegister's `[like]`). `filterable: false` on the column turns it
151
- * off. A column with no schema property behind it only filters when it
152
- * carries an enum hint (`enum`, or a badge widget's colour map), because the
153
- * server cannot filter on a value it does not store.
199
+ * off. When the schema lists its properties, a column with no property
200
+ * behind it only filters when it carries an enum hint (`enum`, or a badge
201
+ * widget's colour map), because the server cannot filter on a value it does
202
+ * not store. When there is no schema to read, the column's own hints decide
203
+ * (see `columnHintFilterDef`), falling back to a text filter on contains.
154
204
  *
155
205
  * @param {object} column The column definition.
156
206
  * @param {object|null} schema The table's schema.
@@ -172,7 +222,7 @@ function columnFilterDef(column, schema) {
172
222
  return { ...base, kind: 'enum', options }
173
223
  }
174
224
  if (!prop) {
175
- return null
225
+ return hasProperties(schema) ? null : columnHintFilterDef(column, base)
176
226
  }
177
227
  const reference = referenceTarget(column, prop);
178
228
  if (reference) {
@@ -1 +1 @@
1
- {"version":3,"file":"columnFilters.js","sources":["../../../src/utils/columnFilters.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\n\n/**\n * Per-column sorting and filtering for table headers.\n *\n * Pure helpers: which column can sort, which can filter and how, and the\n * translation between a header filter's state and the OpenRegister query\n * parameters the facet sidebar already sends. A header filter writes into the\n * SAME active-filter map (`{ paramKey: values[] }`) the sidebar uses, so the\n * fetch, the route query and saved views need nothing new.\n *\n * Parameter shapes, as OpenRegister reads them:\n * - equality and \"any of\": `key=value`, `key[]=a&key[]=b`\n * - ranges: `key[gte]=from`, `key[lte]=to`\n * - contains (text): `key[like]=term`, case-insensitive (openregister#4430);\n * the server escapes `%`, `_` and `\\`, so the raw term goes out as typed\n *\n * @module utils/columnFilters\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md\n * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains\n */\n\nimport { schemaRefSlug } from './schemaRefSlug.js'\n\n/** OpenRegister's case-insensitive contains operator, used by the text filter. */\nexport const CONTAINS_OPERATOR = 'like'\n\n/** Column keys that mark a value computed on the client, never stored. */\nconst COMPUTED_MARKERS = ['aggregate', 'compute', 'computed', 'virtual']\n\n/**\n * Normalise a `$ref` to the referenced schema slug, or null.\n *\n * @param {unknown} ref A `$ref` value.\n * @return {string|number|null} The slug.\n */\nfunction refSlug(ref) {\n\tif (typeof ref === 'number' && !Number.isNaN(ref)) {\n\t\treturn ref\n\t}\n\tif (typeof ref !== 'string' || ref === '') {\n\t\treturn null\n\t}\n\tconst tail = ref.includes('/') ? ref.substring(ref.lastIndexOf('/') + 1) : ref\n\tconst slug = tail === '' ? '' : schemaRefSlug(tail)\n\treturn slug !== '' ? slug : null\n}\n\n/**\n * The schema property behind a column, or null when the column has none.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {object|null} The property.\n */\nexport function columnProperty(column, schema) {\n\tif (!column || !schema || !schema.properties || typeof column.key !== 'string') {\n\t\treturn null\n\t}\n\tconst prop = schema.properties[column.key]\n\treturn (prop && typeof prop === 'object') ? prop : null\n}\n\n/**\n * Whether a column computes its value on the client, so the server cannot\n * sort or filter on it.\n *\n * @param {object} column The column definition.\n * @return {boolean} True for a computed column.\n */\nfunction isComputedColumn(column) {\n\treturn COMPUTED_MARKERS.some((marker) => column[marker] !== undefined && column[marker] !== null && column[marker] !== false)\n}\n\n/**\n * Whether a column header sorts.\n *\n * `sortable: false` always wins, and so does an explicit `sortable: true`.\n * Otherwise, with a schema, a column sorts when a stored schema property backs\n * it: OpenRegister orders on schema properties, so a widget or computed column\n * with no property behind it stays unsortable. Without a schema nothing says\n * the server can sort, so only an explicit flag makes the column sortable,\n * which is how such tables behaved before.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {boolean} True when the header sorts.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-sorts-by-default\n */\nexport function isColumnSortable(column, schema) {\n\tif (!column || typeof column !== 'object') {\n\t\treturn false\n\t}\n\tif (column.sortable === false) {\n\t\treturn false\n\t}\n\tif (column.sortable === true) {\n\t\treturn true\n\t}\n\tif (!schema || isComputedColumn(column)) {\n\t\treturn false\n\t}\n\tconst prop = columnProperty(column, schema)\n\treturn !!prop && prop.type !== 'object'\n}\n\n/**\n * Display options for an enum filter: schema `enum` (or `items.enum`) with\n * `x-enum-labels`, else a column `enum` hint, else a badge column's colour\n * map keys.\n *\n * @param {object} column The column definition.\n * @param {object|null} prop The schema property.\n * @return {Array<{value: string, label: string}>} The options, or [].\n */\nfunction enumOptions(column, prop) {\n\tconst p = prop || {}\n\tconst labels = p.enumLabels || p['x-enum-labels'] || (p.items && (p.items.enumLabels || p.items['x-enum-labels'])) || {}\n\tlet values = null\n\tif (Array.isArray(p.enum) && p.enum.length > 0) {\n\t\tvalues = p.enum\n\t} else if (p.items && Array.isArray(p.items.enum) && p.items.enum.length > 0) {\n\t\tvalues = p.items.enum\n\t} else if (Array.isArray(column.enum) && column.enum.length > 0) {\n\t\tvalues = column.enum\n\t} else if (column.widget === 'badge' && column.widgetProps && column.widgetProps.colorMap) {\n\t\tvalues = Object.keys(column.widgetProps.colorMap)\n\t}\n\tif (!values) {\n\t\treturn []\n\t}\n\treturn values\n\t\t.filter((v) => v !== null && v !== undefined && v !== '')\n\t\t.map((v) => ({ value: String(v), label: String(labels[v] !== undefined ? labels[v] : v) }))\n}\n\n/**\n * The reference a column points at, for a searchable object filter: a schema\n * `$ref` (or `items.$ref`), else an `fkResolve` cell widget's config.\n *\n * @param {object} column The column definition.\n * @param {object|null} prop The schema property.\n * @return {{schema: (string|number), register: string, labelField: string}|null} The reference.\n */\nfunction referenceTarget(column, prop) {\n\tconst widgetProps = (column.widget === 'fkResolve' && column.widgetProps) || null\n\tconst p = prop || {}\n\tconst schemaRef = refSlug(p.$ref) ?? (p.items ? refSlug(p.items.$ref) : null)\n\tconst schema = schemaRef ?? (widgetProps && widgetProps.schema ? widgetProps.schema : null)\n\tif (schema === null || schema === undefined || schema === '') {\n\t\treturn null\n\t}\n\tconst register = (widgetProps && widgetProps.register)\n\t\t|| p['x-external-register']\n\t\t|| (p.items && p.items['x-external-register'])\n\t\t|| ''\n\tconst labelField = (widgetProps && widgetProps.labelField) || ''\n\treturn { schema, register, labelField }\n}\n\n/**\n * How a column filters, or null when it does not.\n *\n * Kinds: `enum` (checkbox list), `boolean` (yes, no, any), `reference`\n * (searchable list of the referenced objects), `number` and `date` (from and\n * to), and `string` (contains, through OpenRegister's `[like]`). `filterable: false` on the column turns it\n * off. A column with no schema property behind it only filters when it\n * carries an enum hint (`enum`, or a badge widget's colour map), because the\n * server cannot filter on a value it does not store.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {{key: string, kind: string, label: string, options: Array<{value: string, label: string}>, reference: object|null, dateTime: boolean}|null} The filter definition.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header\n */\nexport function columnFilterDef(column, schema) {\n\tif (!column || typeof column !== 'object' || typeof column.key !== 'string' || column.key === '') {\n\t\treturn null\n\t}\n\tif (column.filterable === false || isComputedColumn(column)) {\n\t\treturn null\n\t}\n\tconst prop = columnProperty(column, schema)\n\tconst base = { key: column.key, label: column.label || (prop && prop.title) || column.key, options: [], reference: null, dateTime: false }\n\n\tconst options = enumOptions(column, prop)\n\tif (options.length > 0) {\n\t\treturn { ...base, kind: 'enum', options }\n\t}\n\tif (!prop) {\n\t\treturn null\n\t}\n\tconst reference = referenceTarget(column, prop)\n\tif (reference) {\n\t\treturn { ...base, kind: 'reference', reference }\n\t}\n\tconst type = Array.isArray(prop.type) ? prop.type.find((t) => t !== 'null') : prop.type\n\tconst format = prop.format || ''\n\tif (type === 'boolean') {\n\t\treturn { ...base, kind: 'boolean' }\n\t}\n\tif (type === 'number' || type === 'integer') {\n\t\treturn { ...base, kind: 'number' }\n\t}\n\tif (type === 'string' && (format === 'date' || format === 'date-time')) {\n\t\treturn { ...base, kind: 'date', dateTime: format === 'date-time' }\n\t}\n\tif (type === 'string') {\n\t\treturn { ...base, kind: 'string' }\n\t}\n\treturn null\n}\n\n/**\n * Read one active-filter entry as a list of strings.\n *\n * @param {unknown} raw An active-filter value: an array, a scalar, or absent.\n * @return {Array<string>} The values.\n */\nfunction asList(raw) {\n\tif (raw === undefined || raw === null || raw === '') {\n\t\treturn []\n\t}\n\treturn (Array.isArray(raw) ? raw : [raw]).filter((v) => v !== undefined && v !== null && v !== '').map((v) => String(v))\n}\n\n/**\n * The parameter keys one column filter owns.\n *\n * @param {object} def A filter definition from `columnFilterDef`.\n * @return {Array<string>} The keys.\n */\nexport function columnFilterParamKeys(def) {\n\tif (!def) {\n\t\treturn []\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\treturn [`${def.key}[gte]`, `${def.key}[lte]`]\n\t}\n\tif (def.kind === 'string') {\n\t\treturn [containsKey(def)]\n\t}\n\treturn [def.key]\n}\n\n/**\n * The parameter a text filter writes: `{key}[like]`. An exact `key=value`\n * from the facet sidebar or a fixed filter is a different key, so the header\n * neither shows nor clears it.\n *\n * @param {object} def A filter definition of kind `string`.\n * @return {string} The parameter key.\n * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains\n */\nfunction containsKey(def) {\n\treturn `${def.key}[${CONTAINS_OPERATOR}]`\n}\n\n/**\n * A column filter's current state, read from the active-filter map.\n *\n * @param {object} def A filter definition.\n * @param {object} activeFilters The active-filter map (`{ paramKey: values }`).\n * @return {object} `{ values }` for enum and reference, `{ value }` for boolean and string, `{ from, to }` for number and date.\n */\nexport function columnFilterState(def, activeFilters) {\n\tconst filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {}\n\tif (!def) {\n\t\treturn {}\n\t}\n\tif (def.kind === 'enum' || def.kind === 'reference') {\n\t\treturn { values: asList(filters[def.key]) }\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\tconst from = asList(filters[`${def.key}[gte]`])[0] || ''\n\t\tconst to = asList(filters[`${def.key}[lte]`])[0] || ''\n\t\t// A date-time column is queried up to the end of the day; the input\n\t\t// shows the date the person picked.\n\t\treturn {\n\t\t\tfrom: def.kind === 'date' ? from.slice(0, 10) : from,\n\t\t\tto: def.kind === 'date' ? to.slice(0, 10) : to,\n\t\t}\n\t}\n\tif (def.kind === 'string') {\n\t\treturn { value: asList(filters[containsKey(def)])[0] || '' }\n\t}\n\treturn { value: asList(filters[def.key])[0] || '' }\n}\n\n/**\n * Whether a column filter is active.\n *\n * @param {object} def A filter definition.\n * @param {object} activeFilters The active-filter map.\n * @return {boolean} True when any of its parameters is set.\n */\nexport function isColumnFilterActive(def, activeFilters) {\n\tconst filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {}\n\treturn columnFilterParamKeys(def).some((key) => asList(filters[key]).length > 0)\n}\n\n/**\n * The active-filter entries a column filter state produces. Every key the\n * column owns is present; an empty list clears that key, so changing a range\n * from \"from and to\" to \"from\" drops the old upper bound.\n *\n * @param {object} def A filter definition.\n * @param {object} state The column filter state (see `columnFilterState`).\n * @return {object} `{ paramKey: values[] }`.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-a-header-filter-speaks-the-sidebars-query-language\n */\nexport function columnFilterParams(def, state) {\n\tconst s = state || {}\n\tif (!def) {\n\t\treturn {}\n\t}\n\tif (def.kind === 'enum' || def.kind === 'reference') {\n\t\treturn { [def.key]: asList(s.values) }\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\tconst from = s.from === undefined || s.from === null ? '' : String(s.from).trim()\n\t\tlet to = s.to === undefined || s.to === null ? '' : String(s.to).trim()\n\t\tif (def.kind === 'date' && def.dateTime && to !== '' && to.length === 10) {\n\t\t\tto = `${to}T23:59:59`\n\t\t}\n\t\treturn {\n\t\t\t[`${def.key}[gte]`]: from === '' ? [] : [from],\n\t\t\t[`${def.key}[lte]`]: to === '' ? [] : [to],\n\t\t}\n\t}\n\tif (def.kind === 'boolean') {\n\t\treturn { [def.key]: (s.value === 'true' || s.value === 'false') ? [s.value] : [] }\n\t}\n\t// Text: the raw term under `[like]`. No wildcards: OpenRegister wraps\n\t// the term itself and escapes `%`, `_` and `\\` in it.\n\tconst value = s.value === undefined || s.value === null ? '' : String(s.value).trim()\n\treturn { [containsKey(def)]: value === '' ? [] : [value] }\n}\n\n/**\n * Clear-all entries for one column: every key it owns, emptied.\n *\n * @param {object} def A filter definition.\n * @return {object} `{ paramKey: [] }`.\n */\nexport function clearedColumnFilterParams(def) {\n\treturn Object.fromEntries(columnFilterParamKeys(def).map((key) => [key, []]))\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AAsBA;AACA;AACY,MAAC,iBAAiB,GAAG,OAAM;AACvC;AACA;AACA,MAAM,gBAAgB,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAC;AACxE;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,OAAO,CAAC,GAAG,EAAE;AACtB,CAAC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;AACpD,EAAE,OAAO,GAAG;AACZ,EAAE;AACF,CAAC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,EAAE,EAAE;AAC5C,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAG;AAC/E,CAAC,MAAM,IAAI,GAAG,IAAI,KAAK,EAAE,GAAG,EAAE,GAAG,aAAa,CAAC,IAAI,EAAC;AACpD,CAAC,OAAO,IAAI,KAAK,EAAE,GAAG,IAAI,GAAG,IAAI;AACjC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE;AAC/C,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,EAAE;AACjF,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,EAAC;AAC3C,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,GAAG,IAAI;AACxD,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,gBAAgB,CAAC,MAAM,EAAE;AAClC,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC;AAC9H,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE;AACjD,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;AAC5C,EAAE,OAAO,KAAK;AACd,EAAE;AACF,CAAC,IAAI,MAAM,CAAC,QAAQ,KAAK,KAAK,EAAE;AAChC,EAAE,OAAO,KAAK;AACd,EAAE;AACF,CAAC,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE;AAC/B,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,IAAI,CAAC,MAAM,IAAI,gBAAgB,CAAC,MAAM,CAAC,EAAE;AAC1C,EAAE,OAAO,KAAK;AACd,EAAE;AACF,CAAC,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,EAAE,MAAM,EAAC;AAC5C,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;AACxC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE;AACnC,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,GAAE;AACrB,CAAC,MAAM,MAAM,GAAG,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,GAAE;AACzH,CAAC,IAAI,MAAM,GAAG,KAAI;AAClB,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;AACjD,EAAE,MAAM,GAAG,CAAC,CAAC,KAAI;AACjB,EAAE,MAAM,IAAI,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;AAC/E,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,KAAI;AACvB,EAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;AAClE,EAAE,MAAM,GAAG,MAAM,CAAC,KAAI;AACtB,EAAE,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE;AAC5F,EAAE,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAC;AACnD,EAAE;AACF,CAAC,IAAI,CAAC,MAAM,EAAE;AACd,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,OAAO,MAAM;AACd,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC;AAC3D,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AAC7F,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE;AACvC,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,WAAW,KAAK,KAAI;AAClF,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,GAAE;AACrB,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,EAAC;AAC9E,CAAC,MAAM,MAAM,GAAG,SAAS,KAAK,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,GAAG,IAAI,EAAC;AAC5F,CAAC,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE,EAAE;AAC/D,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,QAAQ,GAAG,CAAC,WAAW,IAAI,WAAW,CAAC,QAAQ;AACtD,KAAK,CAAC,CAAC,qBAAqB,CAAC;AAC7B,MAAM,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;AAChD,KAAK,GAAE;AACP,CAAC,MAAM,UAAU,GAAG,CAAC,WAAW,IAAI,WAAW,CAAC,UAAU,KAAK,GAAE;AACjE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE;AACxC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE;AAChD,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,GAAG,KAAK,EAAE,EAAE;AACnG,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,IAAI,MAAM,CAAC,UAAU,KAAK,KAAK,IAAI,gBAAgB,CAAC,MAAM,CAAC,EAAE;AAC9D,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,EAAE,MAAM,EAAC;AAC5C,CAAC,MAAM,IAAI,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,GAAE;AAC3I;AACA,CAAC,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,IAAI,EAAC;AAC1C,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;AACzB,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;AAC3C,EAAE;AACF,CAAC,IAAI,CAAC,IAAI,EAAE;AACZ,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE,IAAI,EAAC;AAChD,CAAC,IAAI,SAAS,EAAE;AAChB,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE;AAClD,EAAE;AACF,CAAC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,GAAG,IAAI,CAAC,KAAI;AACxF,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,GAAE;AACjC,CAAC,IAAI,IAAI,KAAK,SAAS,EAAE;AACzB,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;AACrC,EAAE;AACF,CAAC,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,EAAE;AAC9C,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;AACpC,EAAE;AACF,CAAC,IAAI,IAAI,KAAK,QAAQ,KAAK,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,WAAW,CAAC,EAAE;AACzE,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,WAAW,EAAE;AACpE,EAAE;AACF,CAAC,IAAI,IAAI,KAAK,QAAQ,EAAE;AACxB,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;AACpC,EAAE;AACF,CAAC,OAAO,IAAI;AACZ,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,MAAM,CAAC,GAAG,EAAE;AACrB,CAAC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,EAAE,EAAE;AACtD,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AACzH,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,qBAAqB,CAAC,GAAG,EAAE;AAC3C,CAAC,IAAI,CAAC,GAAG,EAAE;AACX,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE;AACnD,EAAE,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAC/C,EAAE;AACF,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE;AAC5B,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;AAC3B,EAAE;AACF,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;AACjB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,WAAW,CAAC,GAAG,EAAE;AAC1B,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAC1C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,GAAG,EAAE,aAAa,EAAE;AACtD,CAAC,MAAM,OAAO,GAAG,CAAC,aAAa,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,GAAG,GAAE;AAC1F,CAAC,IAAI,CAAC,GAAG,EAAE;AACX,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE;AACtD,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;AAC7C,EAAE;AACF,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE;AACnD,EAAE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAE;AAC1D,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAE;AACxD;AACA;AACA,EAAE,OAAO;AACT,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI;AACvD,GAAG,EAAE,EAAE,GAAG,CAAC,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE;AACjD,GAAG;AACH,EAAE;AACF,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE;AAC5B,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE;AAC9D,EAAE;AACF,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE;AACpD,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,oBAAoB,CAAC,GAAG,EAAE,aAAa,EAAE;AACzD,CAAC,MAAM,OAAO,GAAG,CAAC,aAAa,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,GAAG,GAAE;AAC1F,CAAC,OAAO,qBAAqB,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AACjF,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,GAAG,EAAE,KAAK,EAAE;AAC/C,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,GAAE;AACtB,CAAC,IAAI,CAAC,GAAG,EAAE;AACX,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE;AACtD,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE;AACxC,EAAE;AACF,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE;AACnD,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,GAAE;AACnF,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,KAAK,SAAS,IAAI,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAE;AACzE,EAAE,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,QAAQ,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE,EAAE;AAC5E,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,SAAS,EAAC;AACxB,GAAG;AACH,EAAE,OAAO;AACT,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC;AACjD,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;AAC7C,GAAG;AACH,EAAE;AACF,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE;AAC7B,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE;AACpF,EAAE;AACF;AACA;AACA,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,GAAE;AACtF,CAAC,OAAO,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,KAAK,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE;AAC3D,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,yBAAyB,CAAC,GAAG,EAAE;AAC/C,CAAC,OAAO,MAAM,CAAC,WAAW,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AAC9E;;;;"}
1
+ {"version":3,"file":"columnFilters.js","sources":["../../../src/utils/columnFilters.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\n\n/**\n * Per-column sorting and filtering for table headers.\n *\n * Pure helpers: which column can sort, which can filter and how, and the\n * translation between a header filter's state and the OpenRegister query\n * parameters the facet sidebar already sends. A header filter writes into the\n * SAME active-filter map (`{ paramKey: values[] }`) the sidebar uses, so the\n * fetch, the route query and saved views need nothing new.\n *\n * Parameter shapes, as OpenRegister reads them:\n * - equality and \"any of\": `key=value`, `key[]=a&key[]=b`\n * - ranges: `key[gte]=from`, `key[lte]=to`\n * - contains (text): `key[like]=term`, case-insensitive (openregister#4430);\n * the server escapes `%`, `_` and `\\`, so the raw term goes out as typed\n *\n * @module utils/columnFilters\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md\n * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains\n */\n\nimport { schemaRefSlug } from './schemaRefSlug.js'\n\n/** OpenRegister's case-insensitive contains operator, used by the text filter. */\nexport const CONTAINS_OPERATOR = 'like'\n\n/** Column keys that mark a value computed on the client, never stored. */\nconst COMPUTED_MARKERS = ['aggregate', 'compute', 'computed', 'virtual']\n\n/**\n * Normalise a `$ref` to the referenced schema slug, or null.\n *\n * @param {unknown} ref A `$ref` value.\n * @return {string|number|null} The slug.\n */\nfunction refSlug(ref) {\n\tif (typeof ref === 'number' && !Number.isNaN(ref)) {\n\t\treturn ref\n\t}\n\tif (typeof ref !== 'string' || ref === '') {\n\t\treturn null\n\t}\n\tconst tail = ref.includes('/') ? ref.substring(ref.lastIndexOf('/') + 1) : ref\n\tconst slug = tail === '' ? '' : schemaRefSlug(tail)\n\treturn slug !== '' ? slug : null\n}\n\n/**\n * The schema property behind a column, or null when the column has none.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {object|null} The property.\n */\nexport function columnProperty(column, schema) {\n\tif (!column || !schema || !schema.properties || typeof column.key !== 'string') {\n\t\treturn null\n\t}\n\tconst prop = schema.properties[column.key]\n\treturn (prop && typeof prop === 'object') ? prop : null\n}\n\n/**\n * Whether a column computes its value on the client, so the server cannot\n * sort or filter on it.\n *\n * @param {object} column The column definition.\n * @return {boolean} True for a computed column.\n */\nfunction isComputedColumn(column) {\n\treturn COMPUTED_MARKERS.some((marker) => column[marker] !== undefined && column[marker] !== null && column[marker] !== false)\n}\n\n/**\n * Whether a column header sorts.\n *\n * `sortable: false` always wins, and so does an explicit `sortable: true`.\n * Otherwise, with a schema, a column sorts when a stored schema property backs\n * it: OpenRegister orders on schema properties, so a widget or computed column\n * with no property behind it stays unsortable. Without a schema nothing says\n * the server can sort, so only an explicit flag makes the column sortable,\n * which is how such tables behaved before.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {boolean} True when the header sorts.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-sorts-by-default\n */\nexport function isColumnSortable(column, schema) {\n\tif (!column || typeof column !== 'object') {\n\t\treturn false\n\t}\n\tif (column.sortable === false) {\n\t\treturn false\n\t}\n\tif (column.sortable === true) {\n\t\treturn true\n\t}\n\tif (!schema || isComputedColumn(column)) {\n\t\treturn false\n\t}\n\tconst prop = columnProperty(column, schema)\n\treturn !!prop && prop.type !== 'object'\n}\n\n/**\n * Display options for an enum filter: schema `enum` (or `items.enum`) with\n * `x-enum-labels`, else a column `enum` hint, else a badge column's colour\n * map keys.\n *\n * @param {object} column The column definition.\n * @param {object|null} prop The schema property.\n * @return {Array<{value: string, label: string}>} The options, or [].\n */\nfunction enumOptions(column, prop) {\n\tconst p = prop || {}\n\tconst labels = p.enumLabels || p['x-enum-labels'] || (p.items && (p.items.enumLabels || p.items['x-enum-labels'])) || {}\n\tlet values = null\n\tif (Array.isArray(p.enum) && p.enum.length > 0) {\n\t\tvalues = p.enum\n\t} else if (p.items && Array.isArray(p.items.enum) && p.items.enum.length > 0) {\n\t\tvalues = p.items.enum\n\t} else if (Array.isArray(column.enum) && column.enum.length > 0) {\n\t\tvalues = column.enum\n\t} else if (column.widget === 'badge' && column.widgetProps && column.widgetProps.colorMap) {\n\t\tvalues = Object.keys(column.widgetProps.colorMap)\n\t}\n\tif (!values) {\n\t\treturn []\n\t}\n\treturn values\n\t\t.filter((v) => v !== null && v !== undefined && v !== '')\n\t\t.map((v) => ({ value: String(v), label: String(labels[v] !== undefined ? labels[v] : v) }))\n}\n\n/**\n * The reference a column points at, for a searchable object filter: a schema\n * `$ref` (or `items.$ref`), else an `fkResolve` cell widget's config.\n *\n * @param {object} column The column definition.\n * @param {object|null} prop The schema property.\n * @return {{schema: (string|number), register: string, labelField: string}|null} The reference.\n */\nfunction referenceTarget(column, prop) {\n\tconst widgetProps = (column.widget === 'fkResolve' && column.widgetProps) || null\n\tconst p = prop || {}\n\tconst schemaRef = refSlug(p.$ref) ?? (p.items ? refSlug(p.items.$ref) : null)\n\tconst schema = schemaRef ?? (widgetProps && widgetProps.schema ? widgetProps.schema : null)\n\tif (schema === null || schema === undefined || schema === '') {\n\t\treturn null\n\t}\n\tconst register = (widgetProps && widgetProps.register)\n\t\t|| p['x-external-register']\n\t\t|| (p.items && p.items['x-external-register'])\n\t\t|| ''\n\tconst labelField = (widgetProps && widgetProps.labelField) || ''\n\treturn { schema, register, labelField }\n}\n\n/**\n * Whether a schema lists its properties. Without that the table knows its\n * columns but not what backs them.\n *\n * @param {object|null} schema The table's schema.\n * @return {boolean} True when the schema has a properties map.\n */\nfunction hasProperties(schema) {\n\treturn !!(schema && typeof schema === 'object' && schema.properties && typeof schema.properties === 'object')\n}\n\n/**\n * A filter for a column when the table has no schema to read (a manifest\n * page whose schema did not resolve, a host-fed table). The column's own\n * hints decide the kind: an `fkResolve` widget gives a reference filter, a\n * `type` or `format` on the column gives number, date or yes/no, and every\n * other plain stored key gets a text filter on contains (`[like]`). A key\n * that is a path (`a.b`) or a metadata field (`@self`, `_id`) gets none,\n * because OpenRegister does not filter those by that name.\n *\n * @spec openspec/changes/audit-round-lib-fixes/specs/cn-data-table/spec.md\n * @param {object} column The column definition.\n * @param {object} base The shared part of the definition.\n * @return {object|null} The filter definition.\n */\nfunction columnHintFilterDef(column, base) {\n\tconst key = column.key\n\tif (key.includes('.') || key.charAt(0) === '@' || key.charAt(0) === '_') {\n\t\treturn null\n\t}\n\tconst reference = referenceTarget(column, null)\n\tif (reference) {\n\t\treturn { ...base, kind: 'reference', reference }\n\t}\n\tconst type = String(column.type || '').toLowerCase()\n\tconst format = String(column.format || '').toLowerCase()\n\tif (type === 'boolean') {\n\t\treturn { ...base, kind: 'boolean' }\n\t}\n\tif (type === 'number' || type === 'integer') {\n\t\treturn { ...base, kind: 'number' }\n\t}\n\tif (type === 'date' || type === 'date-time' || format === 'date' || format === 'date-time') {\n\t\treturn { ...base, kind: 'date', dateTime: type === 'date-time' || format === 'date-time' }\n\t}\n\treturn { ...base, kind: 'string' }\n}\n\n/**\n * How a column filters, or null when it does not.\n *\n * Kinds: `enum` (checkbox list), `boolean` (yes, no, any), `reference`\n * (searchable list of the referenced objects), `number` and `date` (from and\n * to), and `string` (contains, through OpenRegister's `[like]`). `filterable: false` on the column turns it\n * off. When the schema lists its properties, a column with no property\n * behind it only filters when it carries an enum hint (`enum`, or a badge\n * widget's colour map), because the server cannot filter on a value it does\n * not store. When there is no schema to read, the column's own hints decide\n * (see `columnHintFilterDef`), falling back to a text filter on contains.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {{key: string, kind: string, label: string, options: Array<{value: string, label: string}>, reference: object|null, dateTime: boolean}|null} The filter definition.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header\n */\nexport function columnFilterDef(column, schema) {\n\tif (!column || typeof column !== 'object' || typeof column.key !== 'string' || column.key === '') {\n\t\treturn null\n\t}\n\tif (column.filterable === false || isComputedColumn(column)) {\n\t\treturn null\n\t}\n\tconst prop = columnProperty(column, schema)\n\tconst base = { key: column.key, label: column.label || (prop && prop.title) || column.key, options: [], reference: null, dateTime: false }\n\n\tconst options = enumOptions(column, prop)\n\tif (options.length > 0) {\n\t\treturn { ...base, kind: 'enum', options }\n\t}\n\tif (!prop) {\n\t\treturn hasProperties(schema) ? null : columnHintFilterDef(column, base)\n\t}\n\tconst reference = referenceTarget(column, prop)\n\tif (reference) {\n\t\treturn { ...base, kind: 'reference', reference }\n\t}\n\tconst type = Array.isArray(prop.type) ? prop.type.find((t) => t !== 'null') : prop.type\n\tconst format = prop.format || ''\n\tif (type === 'boolean') {\n\t\treturn { ...base, kind: 'boolean' }\n\t}\n\tif (type === 'number' || type === 'integer') {\n\t\treturn { ...base, kind: 'number' }\n\t}\n\tif (type === 'string' && (format === 'date' || format === 'date-time')) {\n\t\treturn { ...base, kind: 'date', dateTime: format === 'date-time' }\n\t}\n\tif (type === 'string') {\n\t\treturn { ...base, kind: 'string' }\n\t}\n\treturn null\n}\n\n/**\n * Read one active-filter entry as a list of strings.\n *\n * @param {unknown} raw An active-filter value: an array, a scalar, or absent.\n * @return {Array<string>} The values.\n */\nfunction asList(raw) {\n\tif (raw === undefined || raw === null || raw === '') {\n\t\treturn []\n\t}\n\treturn (Array.isArray(raw) ? raw : [raw]).filter((v) => v !== undefined && v !== null && v !== '').map((v) => String(v))\n}\n\n/**\n * The parameter keys one column filter owns.\n *\n * @param {object} def A filter definition from `columnFilterDef`.\n * @return {Array<string>} The keys.\n */\nexport function columnFilterParamKeys(def) {\n\tif (!def) {\n\t\treturn []\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\treturn [`${def.key}[gte]`, `${def.key}[lte]`]\n\t}\n\tif (def.kind === 'string') {\n\t\treturn [containsKey(def)]\n\t}\n\treturn [def.key]\n}\n\n/**\n * The parameter a text filter writes: `{key}[like]`. An exact `key=value`\n * from the facet sidebar or a fixed filter is a different key, so the header\n * neither shows nor clears it.\n *\n * @param {object} def A filter definition of kind `string`.\n * @return {string} The parameter key.\n * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains\n */\nfunction containsKey(def) {\n\treturn `${def.key}[${CONTAINS_OPERATOR}]`\n}\n\n/**\n * A column filter's current state, read from the active-filter map.\n *\n * @param {object} def A filter definition.\n * @param {object} activeFilters The active-filter map (`{ paramKey: values }`).\n * @return {object} `{ values }` for enum and reference, `{ value }` for boolean and string, `{ from, to }` for number and date.\n */\nexport function columnFilterState(def, activeFilters) {\n\tconst filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {}\n\tif (!def) {\n\t\treturn {}\n\t}\n\tif (def.kind === 'enum' || def.kind === 'reference') {\n\t\treturn { values: asList(filters[def.key]) }\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\tconst from = asList(filters[`${def.key}[gte]`])[0] || ''\n\t\tconst to = asList(filters[`${def.key}[lte]`])[0] || ''\n\t\t// A date-time column is queried up to the end of the day; the input\n\t\t// shows the date the person picked.\n\t\treturn {\n\t\t\tfrom: def.kind === 'date' ? from.slice(0, 10) : from,\n\t\t\tto: def.kind === 'date' ? to.slice(0, 10) : to,\n\t\t}\n\t}\n\tif (def.kind === 'string') {\n\t\treturn { value: asList(filters[containsKey(def)])[0] || '' }\n\t}\n\treturn { value: asList(filters[def.key])[0] || '' }\n}\n\n/**\n * Whether a column filter is active.\n *\n * @param {object} def A filter definition.\n * @param {object} activeFilters The active-filter map.\n * @return {boolean} True when any of its parameters is set.\n */\nexport function isColumnFilterActive(def, activeFilters) {\n\tconst filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {}\n\treturn columnFilterParamKeys(def).some((key) => asList(filters[key]).length > 0)\n}\n\n/**\n * The active-filter entries a column filter state produces. Every key the\n * column owns is present; an empty list clears that key, so changing a range\n * from \"from and to\" to \"from\" drops the old upper bound.\n *\n * @param {object} def A filter definition.\n * @param {object} state The column filter state (see `columnFilterState`).\n * @return {object} `{ paramKey: values[] }`.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-a-header-filter-speaks-the-sidebars-query-language\n */\nexport function columnFilterParams(def, state) {\n\tconst s = state || {}\n\tif (!def) {\n\t\treturn {}\n\t}\n\tif (def.kind === 'enum' || def.kind === 'reference') {\n\t\treturn { [def.key]: asList(s.values) }\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\tconst from = s.from === undefined || s.from === null ? '' : String(s.from).trim()\n\t\tlet to = s.to === undefined || s.to === null ? '' : String(s.to).trim()\n\t\tif (def.kind === 'date' && def.dateTime && to !== '' && to.length === 10) {\n\t\t\tto = `${to}T23:59:59`\n\t\t}\n\t\treturn {\n\t\t\t[`${def.key}[gte]`]: from === '' ? [] : [from],\n\t\t\t[`${def.key}[lte]`]: to === '' ? [] : [to],\n\t\t}\n\t}\n\tif (def.kind === 'boolean') {\n\t\treturn { [def.key]: (s.value === 'true' || s.value === 'false') ? [s.value] : [] }\n\t}\n\t// Text: the raw term under `[like]`. No wildcards: OpenRegister wraps\n\t// the term itself and escapes `%`, `_` and `\\` in it.\n\tconst value = s.value === undefined || s.value === null ? '' : String(s.value).trim()\n\treturn { [containsKey(def)]: value === '' ? [] : [value] }\n}\n\n/**\n * Clear-all entries for one column: every key it owns, emptied.\n *\n * @param {object} def A filter definition.\n * @return {object} `{ paramKey: [] }`.\n */\nexport function clearedColumnFilterParams(def) {\n\treturn Object.fromEntries(columnFilterParamKeys(def).map((key) => [key, []]))\n}\n"],"names":[],"mappings":"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"}