@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.
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +151 -13
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +3 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +38 -42
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowDetail/CnRunDetailSidebar.vue2.js +2 -0
- package/dist/esm/components/CnFlowDetail/CnRunDetailSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowDetail/CnRunDetailSidebar.vue3.js +7 -0
- package/dist/esm/components/CnFlowDetail/CnRunDetailSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue.js +10 -0
- package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue.js.map +1 -0
- package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue2.js +150 -0
- package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue2.js.map +1 -0
- package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue3.js +54 -0
- package/dist/esm/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue3.js.map +1 -0
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +29 -2
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +71 -34
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +22 -0
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +46 -32
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +30 -9
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/composables/useWalkthrough.js +11 -3
- package/dist/esm/composables/useWalkthrough.js.map +1 -1
- package/dist/esm/dialogs/CnFlowNodeEditModal.vue2.js +7 -10
- package/dist/esm/dialogs/CnFlowNodeEditModal.vue2.js.map +1 -1
- package/dist/esm/dialogs/CnFlowNodeEditModal.vue3.js +23 -13
- package/dist/esm/dialogs/CnFlowNodeEditModal.vue3.js.map +1 -1
- package/dist/esm/dialogs/CnRunNodeDialog.vue2.js +8 -6
- package/dist/esm/dialogs/CnRunNodeDialog.vue2.js.map +1 -1
- package/dist/esm/dialogs/CnRunNodeDialog.vue3.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +15 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +15 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +117 -0
- package/dist/esm/utils/columnFilters.js +54 -4
- package/dist/esm/utils/columnFilters.js.map +1 -1
- package/dist/esm/utils/selectOptions.js +57 -0
- package/dist/esm/utils/selectOptions.js.map +1 -0
- package/dist/esm/utils/userAutocomplete.js +69 -3
- package/dist/esm/utils/userAutocomplete.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +6560 -5880
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +117 -0
- package/l10n/en.json +15 -1
- package/l10n/nl.json +15 -1
- package/package.json +1 -1
- package/src/components/CnAppRoot/CnAppRoot.vue +153 -13
- package/src/components/CnDashboardPage/CnDashboardPage.vue +20 -18
- package/src/components/CnFlowDetail/CnRunDetailSidebar.vue +6 -0
- package/src/components/CnFlowStepOutcomes/CnFlowStepOutcomes.vue +250 -0
- package/src/components/CnFlowStepOutcomes/index.js +8 -0
- package/src/components/CnFormDialog/CnFormDialog.vue +62 -2
- package/src/components/CnIndexPage/CnIndexPage.vue +23 -1
- package/src/components/CnObjectSidebar/CnNotesTab.vue +1 -1
- package/src/components/CnSetupWizard/CnSetupWizard.vue +7 -0
- package/src/components/CnWalkthrough/CnWalkthrough.vue +30 -9
- package/src/components/index.js +1 -0
- package/src/composables/useWalkthrough.js +11 -3
- package/src/css/dashboard.css +44 -0
- package/src/dialogs/CnFlowNodeEditModal.vue +41 -20
- package/src/dialogs/CnRunNodeDialog.vue +8 -6
- package/src/index.js +1 -0
- package/src/schemas/app-manifest-v2.schema.json +2 -1
- package/src/utils/columnFilters.js +54 -4
- package/src/utils/selectOptions.js +54 -0
- package/src/utils/userAutocomplete.js +68 -2
- 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';
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/esm/l10n/en.json.js
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/esm/l10n/nl.json.js
CHANGED
|
@@ -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.
|
|
152
|
-
* carries an enum hint (`enum`, or a badge
|
|
153
|
-
* server cannot filter on a value it does
|
|
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":"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"}
|
|
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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"}
|