@conduction/nextcloud-vue 2.139.0 → 2.140.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 +9 -2
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +17 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +2 -2
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +52 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +23 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +106 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js +30 -7
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +59 -3
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +44 -34
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +28 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js +7 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +5 -0
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +30 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +28 -3
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue2.js +10 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue3.js.map +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js +12 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue.js +1 -1
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue2.js +23 -1
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue2.js.map +1 -1
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue3.js +43 -13
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue3.js.map +1 -1
- package/dist/esm/composables/indexSources.js +5 -2
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/composables/useTaskInboxStore.js +4 -3
- package/dist/esm/composables/useTaskInboxStore.js.map +1 -1
- package/dist/esm/l10n/en.json.js +2 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +5 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +466 -371
- package/dist/esm/utils/builtInFormatters.js +23 -1
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +7 -1
- package/dist/esm/utils/fillTemplate.js.map +1 -1
- package/dist/esm/utils/myTeam.js +24 -4
- package/dist/esm/utils/myTeam.js.map +1 -1
- package/dist/esm/utils/resolveFilterTokens.js +5 -4
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/transitionInputs.js +43 -2
- package/dist/esm/utils/transitionInputs.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +771 -222
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +466 -371
- package/l10n/en.json +2 -1
- package/l10n/nl.json +5 -1
- package/package.json +1 -1
- package/src/components/CnAppRoot/CnAppRoot.vue +9 -2
- package/src/components/CnCellRenderer/CnCellRenderer.vue +19 -3
- package/src/components/CnChartWidget/CnChartWidget.md +1 -0
- package/src/components/CnChartWidget/CnChartWidget.vue +72 -1
- package/src/components/CnConversationThread/CnConversationWidget.vue +126 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +72 -5
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +28 -1
- package/src/components/CnFilesBrowser/filesBrowser.js +7 -1
- package/src/components/CnFilesBrowser/filesBrowserColumns.js +5 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +30 -2
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +28 -3
- package/src/components/CnRecordListWidget/CnRecordNoteRows.vue +10 -0
- package/src/components/CnStackedBarWidget/CnStackedBarWidget.vue +68 -2
- package/src/components/CnTransitionInputFields/CnTransitionInputFields.vue +43 -1
- package/src/composables/indexSources.js +5 -2
- package/src/composables/useTaskInboxStore.js +4 -3
- package/src/css/look-board.css +10 -2
- package/src/css/table.css +13 -0
- package/src/utils/builtInFormatters.js +22 -0
- package/src/utils/fillTemplate.js +7 -1
- package/src/utils/myTeam.js +24 -4
- package/src/utils/resolveFilterTokens.js +5 -4
- package/src/utils/transitionInputs.js +42 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnStackedBarWidget.vue3.js","sources":["../../../../src/components/CnStackedBarWidget/CnStackedBarWidget.vue?vue&type=template&id=455eb41f&lang.js"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-stacked-bar\" :class=\"{ 'cn-stacked-bar--inset': inset }\" data-testid=\"cn-stacked-bar\">\n\t\t<div v-if=\"isLoading\" class=\"cn-stacked-bar__state\" role=\"status\">\n\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t<span>{{ loadingLabel }}</span>\n\t\t</div>\n\t\t<p v-else-if=\"loadError\" class=\"cn-stacked-bar__state\" role=\"status\">\n\t\t\t{{ errorLabel }}\n\t\t</p>\n\t\t<!-- The designed empty state the list widgets use, compact: a bare\n\t\t line of text read as a broken widget in an empty card. -->\n\t\t<CnWidgetEmptyState\n\t\t\tv-else-if=\"total === 0\"\n\t\t\t:name=\"emptyLabel\"\n\t\t\tcompact\n\t\t\tclass=\"cn-stacked-bar__empty\"\n\t\t\tdata-testid=\"cn-stacked-bar-empty\" />\n\t\t<template v-else>\n\t\t\t<div v-if=\"heading\" class=\"cn-stacked-bar__heading\">\n\t\t\t\t<strong data-testid=\"cn-stacked-bar-heading\">{{ heading }}</strong>\n\t\t\t\t<span v-if=\"headingMeta\" class=\"cn-stacked-bar__heading-meta\" data-testid=\"cn-stacked-bar-heading-meta\">{{ headingMeta }}</span>\n\t\t\t</div>\n\t\t\t<!-- The bar is a picture of the legend below it. The legend holds\n\t\t\t every label and number, so the bar itself is hidden from\n\t\t\t assistive technology rather than described twice. -->\n\t\t\t<div class=\"cn-stacked-bar__bar\" aria-hidden=\"true\">\n\t\t\t\t<span\n\t\t\t\t\tv-for=\"segment in barSegments\"\n\t\t\t\t\t:key=\"segment.key\"\n\t\t\t\t\tclass=\"cn-stacked-bar__segment\"\n\t\t\t\t\t:style=\"{ flexGrow: segment.count, backgroundColor: segment.color }\" />\n\t\t\t</div>\n\t\t\t<ul class=\"cn-stacked-bar__legend\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"segment in segments\"\n\t\t\t\t\t:key=\"segment.key\"\n\t\t\t\t\tclass=\"cn-stacked-bar__legend-item\"\n\t\t\t\t\tdata-testid=\"cn-stacked-bar-legend-item\">\n\t\t\t\t\t<span class=\"cn-stacked-bar__label\">\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"cn-stacked-bar__dot\"\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t:style=\"{ backgroundColor: segment.color }\" />\n\t\t\t\t\t\t{{ segment.label }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<!-- The leading space keeps label and count two words for a\n\t\t\t\t\t screen reader; it is not drawn in this flex column. -->\n\t\t\t\t\t<strong class=\"cn-stacked-bar__count\">{{ ' ' + segment.count }}</strong>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { inject, isRef } from 'vue'\nimport CnWidgetEmptyState from '../CnWidgetEmptyState/CnWidgetEmptyState.vue'\nimport { useEndpointSource } from '../../composables/useEndpointSource.js'\nimport { fetchGroupedCounts } from '../../utils/fetchAggregate.js'\n\n/** The colour of a group's tone (`content.colors`); any other value is used as a CSS colour. */\nconst TONE_COLORS = {\n\tsuccess: 'var(--color-success)',\n\twarning: 'var(--color-warning)',\n\terror: 'var(--color-error)',\n\tinfo: 'var(--color-info, var(--color-primary-element))',\n\tneutral: 'var(--color-background-darker)',\n}\n\n/**\n * The value at a dot path of an object.\n *\n * @param {object} data The object.\n * @param {string} dotPath The path, '' for the object itself.\n * @return {unknown} The value, or undefined.\n */\nfunction pluck(data, dotPath) {\n\tif (!dotPath) {\n\t\treturn data\n\t}\n\treturn String(dotPath).split('.').reduce((node, key) => (node && typeof node === 'object' ? node[key] : undefined), data)\n}\n\n/** The lightest step keeps this share of the primary colour, so it still stands out from the page. */\nconst LIGHTEST_PRIMARY_SHARE = 70\n\n/**\n * The colour of step `index` out of `count`, on one ramp that runs from a\n * light tint of the primary colour, through the primary colour, to the text\n * colour. Steps differ in LIGHTNESS, so they stay apart for somebody who\n * cannot tell hues apart, and the ramp follows the theme because it is built\n * from Nextcloud's own variables.\n *\n * @param {number} index The step, 0-based.\n * @param {number} count The number of steps.\n * @return {string} A CSS colour.\n * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n */\nexport function rampColor(index, count) {\n\tif (count <= 1) {\n\t\treturn 'var(--color-primary-element)'\n\t}\n\tconst position = index / (count - 1)\n\tif (position < 0.5) {\n\t\tconst share = Math.round(LIGHTEST_PRIMARY_SHARE + (position / 0.5) * (100 - LIGHTEST_PRIMARY_SHARE))\n\t\treturn `color-mix(in srgb, var(--color-primary-element) ${share}%, var(--color-main-background))`\n\t}\n\tconst textShare = Math.round(((position - 0.5) / 0.5) * 100)\n\tif (textShare === 0) {\n\t\treturn 'var(--color-primary-element)'\n\t}\n\tif (textShare === 100) {\n\t\treturn 'var(--color-main-text)'\n\t}\n\treturn `color-mix(in srgb, var(--color-main-text) ${textShare}%, var(--color-primary-element))`\n}\n\n/**\n * CnStackedBarWidget shows how a total divides over a few groups: ONE bar cut\n * into segments, with a legend below it that names every segment and gives\n * its count. Use it for \"my cases per step\" or \"requests per channel\".\n *\n * Segments come from one grouped count request against an OpenRegister\n * source (`source.groupBy`), or from static `segments`. `order` fixes the\n * order of the segments, and `labels` gives a group key a readable name.\n * Colours come from one ramp built on the theme's primary colour; the steps\n * differ in lightness, never in hue alone. Resolved by its registry type key\n * `stacked-bar`.\n *\n * ```json\n * {\n * \"widgetKey\": \"stacked-bar\",\n * \"props\": {\n * \"content\": {\n * \"source\": { \"register\": \"dossiq\", \"schema\": \"case\", \"groupBy\": \"status\", \"filter\": { \"assignee\": \"@me\" } },\n * \"order\": [\"received\", \"in_progress\", \"decision\", \"publish\"],\n * \"labels\": { \"received\": \"Received\", \"in_progress\": \"In progress\", \"decision\": \"Decision\", \"publish\": \"Publish\" }\n * }\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnStackedBarWidget',\n\n\tcomponents: { CnWidgetEmptyState, NcLoadingIcon },\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot. Segment labels and\n\t\t * the empty text run through it. Identity by default.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's configuration. `source` names the OpenRegister\n\t\t * register and schema, the `groupBy` field and an optional `filter`\n\t\t * (the shared @-token grammar). `segments` are static entries\n\t\t * `{ key?, label, value }` used when there is no source. `order` lists\n\t\t * group keys in the order they render; groups it does not list follow\n\t\t * in the order they arrive, and a listed key without records renders\n\t\t * with a count of 0. `labels` maps a group key to its label.\n\t\t * `emptyText` is shown when there is nothing to count. `inset: true`\n\t\t * draws the bar and legend inside the board's inset (16px above, 24px\n\t\t * at the sides, 22px below) instead of from card edge to card edge.\n\t\t * `endpointSource` (`{ url, responsePath? }`, `@objectId` filled from\n\t\t * the page) reads the groups from an app endpoint instead: the counts\n\t\t * at `groupsPath` (a `{ key: count }` map or `[{ key, count }]`) and the\n\t\t * whole at `totalPath`. `colors` maps a group key to a tone (`success`,\n\t\t * `warning`, `error`, `info`, `neutral`) or a CSS colour. `remainder`\n\t\t * (`{ key?, label }`) adds what the groups leave of the whole as a last,\n\t\t * neutral segment. `heading` and `headingMeta` are templates over\n\t\t * `{counted}`, `{total}`, `{remaining}` and the endpoint's own fields,\n\t\t * drawn above the bar. `headerCount: true` reports the sum of the\n\t\t * segments to the card (`header` event, `count`), for a chip after the\n\t\t * title or a `{count}` in it.\n\t\t *\n\t\t * @type {{inset?: boolean, headerCount?: boolean, source?: {register?: string, schema?: string, groupBy?: string, filter?: object}, segments?: Array<{key?: string, label: string, value: number}>, order?: Array<string>, labels?: {[key: string]: string}, emptyText?: string}}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Translate function. Falls back to the injected `cnTranslate`. Pass it\n\t\t * when mounting the widget outside a CnAppRoot.\n\t\t *\n\t\t * @type {((key: string) => string)|null}\n\t\t */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The record on the page, for the `@objectId` token of `endpointSource`. */\n\t\tobjectId: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['header'],\n\n\tsetup(props) {\n\t\tconst objectCtxRaw = inject('cnObjectContext', null)\n\t\tconst { data, loading, error } = useEndpointSource(() => props.content?.endpointSource || null, {\n\t\t\tctx: () => {\n\t\t\t\tconst ctx = (isRef(objectCtxRaw) ? objectCtxRaw.value : objectCtxRaw) || {}\n\t\t\t\treturn { ...ctx, objectId: props.objectId ?? ctx.objectId ?? null, object: ctx.object || null }\n\t\t\t},\n\t\t})\n\t\treturn { epData: data, epLoading: loading, epError: error }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tgroups: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the widget draws the board's inset (`content.inset`). Off\n\t\t * by default, which keeps it edge to edge as before.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-strip-and-a-stacked-bar-can-take-the-board-inset\n\t\t * @return {boolean}\n\t\t */\n\t\tinset() {\n\t\t\treturn Boolean(this.content && this.content.inset === true)\n\t\t},\n\n\t\t/**\n\t\t * The translate function in use.\n\t\t *\n\t\t * @return {(key: string) => string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\teffectiveTranslate() {\n\t\t\treturn this.translate ?? this.cnTranslate\n\t\t},\n\n\t\t/**\n\t\t * Whether the segments come from OpenRegister.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\thasEndpoint() {\n\t\t\treturn Boolean(this.content.endpointSource && this.content.endpointSource.url)\n\t\t},\n\n\t\t/** @return {boolean} Whether the counts are still loading. */\n\t\tisLoading() {\n\t\t\treturn this.hasEndpoint ? Boolean(this.epLoading) && this.epData === null : this.loading\n\t\t},\n\n\t\t/** @return {string} The load error, or ''. */\n\t\tloadError() {\n\t\t\treturn this.hasEndpoint ? (this.epError || '') : this.error\n\t\t},\n\n\t\t/**\n\t\t * The endpoint's groups as `{ key, count }`, from a `{ key: count }`\n\t\t * map or a `[{ key, count }]` list at `groupsPath`.\n\t\t *\n\t\t * @return {Array<{key: string, count: number}>}\n\t\t */\n\t\tendpointGroups() {\n\t\t\tconst raw = pluck(this.epData, this.content.groupsPath || 'groups')\n\t\t\tconst entries = Array.isArray(raw)\n\t\t\t\t? raw.filter((entry) => entry && typeof entry === 'object').map((entry) => [entry.key, entry.count])\n\t\t\t\t: Object.entries(raw && typeof raw === 'object' ? raw : {})\n\t\t\treturn entries.map(([key, count]) => ({ key: String(key), count: Number(count) > 0 ? Number(count) : 0 }))\n\t\t},\n\n\t\t/**\n\t\t * The whole the groups are part of (`totalPath` of the endpoint), or\n\t\t * null when none is given.\n\t\t *\n\t\t * @return {number|null}\n\t\t */\n\t\twhole() {\n\t\t\tif (!this.hasEndpoint || !this.content.totalPath) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst value = Number(pluck(this.epData, this.content.totalPath))\n\t\t\treturn Number.isFinite(value) && value >= 0 ? value : null\n\t\t},\n\n\t\t/** @return {number} The sum of the groups, without the remainder. */\n\t\tcounted() {\n\t\t\treturn this.rawGroups.reduce((sum, group) => sum + group.count, 0)\n\t\t},\n\n\t\t/** @return {number} What the groups leave of the whole. */\n\t\tremaining() {\n\t\t\treturn this.whole === null ? 0 : Math.max(0, this.whole - this.counted)\n\t\t},\n\n\t\t/**\n\t\t * The values the heading templates read.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\ttemplateValues() {\n\t\t\tconst own = this.epData && typeof this.epData === 'object' && !Array.isArray(this.epData) ? this.epData : {}\n\t\t\treturn { ...own, counted: this.counted, total: this.whole === null ? this.counted : this.whole, remaining: this.remaining }\n\t\t},\n\n\t\t/** @return {string} The line above the bar, or ''. */\n\t\theading() {\n\t\t\treturn this.fillTemplate(this.content.heading)\n\t\t},\n\n\t\t/** @return {string} The muted line beside the heading, or ''. */\n\t\theadingMeta() {\n\t\t\treturn this.fillTemplate(this.content.headingMeta)\n\t\t},\n\n\t\thasSource() {\n\t\t\tconst s = this.content.source || {}\n\t\t\treturn Boolean(s.register && s.schema && s.groupBy)\n\t\t},\n\n\t\t/**\n\t\t * The raw groups, from the source or the static list, as `{ key, label, count }`.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, count: number}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\trawGroups() {\n\t\t\tif (this.hasEndpoint) {\n\t\t\t\treturn this.endpointGroups.map((group) => ({ key: group.key, label: group.key, count: group.count }))\n\t\t\t}\n\t\t\tif (this.hasSource) {\n\t\t\t\treturn this.groups.map((group) => ({ key: group.key, label: group.key, count: group.count }))\n\t\t\t}\n\t\t\tconst list = Array.isArray(this.content.segments) ? this.content.segments : []\n\t\t\treturn list\n\t\t\t\t.filter((segment) => segment && typeof segment === 'object')\n\t\t\t\t.map((segment, index) => {\n\t\t\t\t\tconst count = Number(segment.value)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkey: String(segment.key ?? segment.label ?? index),\n\t\t\t\t\t\tlabel: String(segment.label ?? segment.key ?? ''),\n\t\t\t\t\t\tcount: Number.isFinite(count) && count > 0 ? count : 0,\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The segments in render order, each with its label and colour.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, count: number, color: string}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\tsegments() {\n\t\t\tconst order = Array.isArray(this.content.order) ? this.content.order.map(String) : []\n\t\t\tconst labels = (this.content.labels && typeof this.content.labels === 'object') ? this.content.labels : {}\n\t\t\tconst byKey = {}\n\t\t\tfor (const group of this.rawGroups) {\n\t\t\t\tbyKey[group.key] = group\n\t\t\t}\n\t\t\tconst ordered = []\n\t\t\tfor (const key of order) {\n\t\t\t\tordered.push(byKey[key] || { key, label: key, count: 0 })\n\t\t\t}\n\t\t\tfor (const group of this.rawGroups) {\n\t\t\t\tif (!order.includes(group.key)) {\n\t\t\t\t\tordered.push(group)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst colors = (this.content.colors && typeof this.content.colors === 'object') ? this.content.colors : {}\n\t\t\tconst segments = ordered.map((group, index) => ({\n\t\t\t\tkey: group.key,\n\t\t\t\tlabel: this.effectiveTranslate(typeof labels[group.key] === 'string' ? labels[group.key] : group.label),\n\t\t\t\tcount: group.count,\n\t\t\t\tcolor: this.colorOf(colors[group.key]) || rampColor(index, ordered.length),\n\t\t\t}))\n\t\t\tconst remainder = this.content.remainder\n\t\t\tif (remainder && typeof remainder === 'object' && this.remaining > 0) {\n\t\t\t\tsegments.push({\n\t\t\t\t\tkey: String(remainder.key || 'remainder'),\n\t\t\t\t\tlabel: this.effectiveTranslate(String(remainder.label || '')),\n\t\t\t\t\tcount: this.remaining,\n\t\t\t\t\tcolor: this.colorOf(remainder.color) || TONE_COLORS.neutral,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn segments\n\t\t},\n\n\t\t/**\n\t\t * The segments that take up room in the bar.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\tbarSegments() {\n\t\t\treturn this.segments.filter((segment) => segment.count > 0)\n\t\t},\n\n\t\t/**\n\t\t * The total reported to the card under `headerCount: true`, or null\n\t\t * while loading, after an error or without `headerCount`.\n\t\t *\n\t\t * @return {number|null}\n\t\t * @spec openspec/changes/widget-title-count/specs/widget-title-count/spec.md#requirement-a-widget-title-can-carry-the-total-its-widget-reports\n\t\t */\n\t\theaderTotal() {\n\t\t\tif (this.content.headerCount !== true || this.isLoading || this.loadError) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.total\n\t\t},\n\n\t\t/**\n\t\t * The sum of all counts.\n\t\t *\n\t\t * @return {number}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\ttotal() {\n\t\t\treturn this.segments.reduce((sum, segment) => sum + segment.count, 0)\n\t\t},\n\n\t\t/**\n\t\t * The text shown when there is nothing to count.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\temptyLabel() {\n\t\t\tconst text = this.content.emptyText\n\t\t\treturn (typeof text === 'string' && text !== '')\n\t\t\t\t? this.effectiveTranslate(text)\n\t\t\t\t: t('nextcloud-vue', 'Nothing to show yet')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The loading text.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading…')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The text shown when the counts could not be loaded.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\terrorLabel() {\n\t\t\treturn t('nextcloud-vue', 'The numbers could not be loaded.')\n\t\t},\n\n\t\t/**\n\t\t * Changes whenever a new fetch is needed.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\tsourceKey() {\n\t\t\treturn JSON.stringify(this.content.source || {})\n\t\t},\n\t},\n\n\twatch: {\n\t\t/** @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget */\n\t\tsourceKey() {\n\t\t\tthis.fetchGroups()\n\t\t},\n\n\t\t/** @spec openspec/changes/widget-title-count/specs/widget-title-count/spec.md#requirement-a-widget-title-can-carry-the-total-its-widget-reports */\n\t\theaderTotal: {\n\t\t\timmediate: true,\n\t\t\thandler(total) {\n\t\t\t\tif (total !== null) {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event header With `headerCount: true`, once the counts have loaded: `{ link: null, note: '', count }`, the sum of the segments.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('header', { link: null, note: '', count: total })\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.fetchGroups()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The CSS colour of a tone or colour value.\n\t\t *\n\t\t * @param {string} value A tone name or a CSS colour.\n\t\t * @return {string} The colour, or '' for none.\n\t\t */\n\t\tcolorOf(value) {\n\t\t\tif (typeof value !== 'string' || value === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn TONE_COLORS[value] || value\n\t\t},\n\n\t\t/**\n\t\t * Translate a heading template and fill its `{name}` placeholders.\n\t\t *\n\t\t * @param {string} template The template.\n\t\t * @return {string} The line, or '' without a template.\n\t\t */\n\t\tfillTemplate(template) {\n\t\t\tif (typeof template !== 'string' || template === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst values = this.templateValues\n\t\t\treturn this.effectiveTranslate(template).replace(/\\{(\\w+)\\}/g, (match, name) => (\n\t\t\t\tvalues[name] === undefined || values[name] === null ? '' : String(values[name])\n\t\t\t))\n\t\t},\n\n\t\t/**\n\t\t * Load the count per group in one request.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\tasync fetchGroups() {\n\t\t\tif (!this.hasSource) {\n\t\t\t\tthis.groups = []\n\t\t\t\tthis.error = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst requestKey = this.sourceKey\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst groups = await fetchGroupedCounts(this.content.source)\n\t\t\t\tif (requestKey !== this.sourceKey) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.groups = groups\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnStackedBarWidget] failed to load counts:', e)\n\t\t\t\tthis.error = (e && e.message) || 'error'\n\t\t\t\tthis.groups = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-stacked-bar {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\twidth: 100%;\n}\n\n/* `content.inset: true`: the board's inset inside the card (zuiddrecht-pixel-\n gaps-3). A dashboard renders its widgets flush, so without it the strip ran\n from card edge to card edge. Theme hook: --cn-widget-board-inset. */\n.cn-stacked-bar--inset {\n\tbox-sizing: border-box;\n\tpadding: var(--cn-widget-board-inset, 16px 24px 22px);\n}\n\n.cn-stacked-bar__heading {\n\tdisplay: flex;\n\talign-items: baseline;\n\tjustify-content: space-between;\n\tgap: 12px;\n}\n\n.cn-stacked-bar__heading-meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-stacked-bar__state {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-stacked-bar__bar {\n\tdisplay: flex;\n\tgap: 3px;\n\theight: 12px;\n}\n\n.cn-stacked-bar__segment {\n\tflex-basis: 0;\n\tmin-width: 4px;\n\tborder-radius: 6px;\n}\n\n.cn-stacked-bar__legend {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n\tgap: 12px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-stacked-bar__legend-item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-stacked-bar__label {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tfont-size: 0.95em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow-wrap: anywhere;\n}\n\n.cn-stacked-bar__dot {\n\tflex: none;\n\twidth: 10px;\n\theight: 10px;\n\tborder-radius: 3px;\n}\n\n.cn-stacked-bar__count {\n\tfont-size: 1.5em;\n\tfont-weight: 700;\n\tline-height: 1.2;\n\tcolor: var(--color-main-text);\n}\n</style>\n"],"names":["_normalizeClass","_openBlock","_createElementBlock","_createVNode","_createElementVNode","_toDisplayString","_Fragment","_renderList","_normalizeStyle"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnStackedBarWidget.vue3.js","sources":["../../../../src/components/CnStackedBarWidget/CnStackedBarWidget.vue?vue&type=template&id=7bd1aead&lang.js"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-stacked-bar\" :class=\"{ 'cn-stacked-bar--inset': inset, 'cn-stacked-bar--compact': compact }\" data-testid=\"cn-stacked-bar\">\n\t\t<div v-if=\"isLoading\" class=\"cn-stacked-bar__state\" role=\"status\">\n\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t<span>{{ loadingLabel }}</span>\n\t\t</div>\n\t\t<p v-else-if=\"loadError\" class=\"cn-stacked-bar__state\" role=\"status\">\n\t\t\t{{ errorLabel }}\n\t\t</p>\n\t\t<!-- The designed empty state the list widgets use, compact: a bare\n\t\t line of text read as a broken widget in an empty card. -->\n\t\t<CnWidgetEmptyState\n\t\t\tv-else-if=\"total === 0\"\n\t\t\t:name=\"emptyLabel\"\n\t\t\tcompact\n\t\t\tclass=\"cn-stacked-bar__empty\"\n\t\t\tdata-testid=\"cn-stacked-bar-empty\" />\n\t\t<template v-else>\n\t\t\t<div v-if=\"heading\" class=\"cn-stacked-bar__heading\">\n\t\t\t\t<strong data-testid=\"cn-stacked-bar-heading\">{{ heading }}</strong>\n\t\t\t\t<span v-if=\"headingMeta\" class=\"cn-stacked-bar__heading-meta\" data-testid=\"cn-stacked-bar-heading-meta\">{{ headingMeta }}</span>\n\t\t\t</div>\n\t\t\t<!-- The bar is a picture of the legend below it. The legend holds\n\t\t\t every label and number, so the bar itself is hidden from\n\t\t\t assistive technology rather than described twice. -->\n\t\t\t<div class=\"cn-stacked-bar__bar\" aria-hidden=\"true\">\n\t\t\t\t<span\n\t\t\t\t\tv-for=\"segment in barSegments\"\n\t\t\t\t\t:key=\"segment.key\"\n\t\t\t\t\tclass=\"cn-stacked-bar__segment\"\n\t\t\t\t\t:style=\"{ flexGrow: segment.count, backgroundColor: segment.color }\" />\n\t\t\t</div>\n\t\t\t<ul class=\"cn-stacked-bar__legend\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"segment in segments\"\n\t\t\t\t\t:key=\"segment.key\"\n\t\t\t\t\tclass=\"cn-stacked-bar__legend-item\"\n\t\t\t\t\tdata-testid=\"cn-stacked-bar-legend-item\">\n\t\t\t\t\t<span class=\"cn-stacked-bar__label\">\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"cn-stacked-bar__dot\"\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t:style=\"{ backgroundColor: segment.color }\" />\n\t\t\t\t\t\t{{ segment.label }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<!-- The leading space keeps label and count two words for a\n\t\t\t\t\t screen reader; it is not drawn in this flex column. -->\n\t\t\t\t\t<strong class=\"cn-stacked-bar__count\">{{ ' ' + segment.count }}</strong>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { inject, isRef } from 'vue'\nimport CnWidgetEmptyState from '../CnWidgetEmptyState/CnWidgetEmptyState.vue'\nimport { useEndpointSource } from '../../composables/useEndpointSource.js'\nimport { fetchGroupedCounts } from '../../utils/fetchAggregate.js'\n\n/** The colour of a group's tone (`content.colors`); any other value is used as a CSS colour. */\nconst TONE_COLORS = {\n\tsuccess: 'var(--color-success)',\n\twarning: 'var(--color-warning)',\n\terror: 'var(--color-error)',\n\tinfo: 'var(--color-info, var(--color-primary-element))',\n\tneutral: 'var(--color-background-darker)',\n}\n\n/**\n * The value at a dot path of an object.\n *\n * @param {object} data The object.\n * @param {string} dotPath The path, '' for the object itself.\n * @return {unknown} The value, or undefined.\n */\nfunction pluck(data, dotPath) {\n\tif (!dotPath) {\n\t\treturn data\n\t}\n\treturn String(dotPath).split('.').reduce((node, key) => (node && typeof node === 'object' ? node[key] : undefined), data)\n}\n\n/** The lightest step keeps this share of the primary colour, so it still stands out from the page. */\nconst LIGHTEST_PRIMARY_SHARE = 70\n\n/**\n * The colour of step `index` out of `count`, on one ramp that runs from a\n * light tint of the primary colour, through the primary colour, to the text\n * colour. Steps differ in LIGHTNESS, so they stay apart for somebody who\n * cannot tell hues apart, and the ramp follows the theme because it is built\n * from Nextcloud's own variables.\n *\n * @param {number} index The step, 0-based.\n * @param {number} count The number of steps.\n * @return {string} A CSS colour.\n * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n */\nexport function rampColor(index, count) {\n\tif (count <= 1) {\n\t\treturn 'var(--color-primary-element)'\n\t}\n\tconst position = index / (count - 1)\n\tif (position < 0.5) {\n\t\tconst share = Math.round(LIGHTEST_PRIMARY_SHARE + (position / 0.5) * (100 - LIGHTEST_PRIMARY_SHARE))\n\t\treturn `color-mix(in srgb, var(--color-primary-element) ${share}%, var(--color-main-background))`\n\t}\n\tconst textShare = Math.round(((position - 0.5) / 0.5) * 100)\n\tif (textShare === 0) {\n\t\treturn 'var(--color-primary-element)'\n\t}\n\tif (textShare === 100) {\n\t\treturn 'var(--color-main-text)'\n\t}\n\treturn `color-mix(in srgb, var(--color-main-text) ${textShare}%, var(--color-primary-element))`\n}\n\n/**\n * CnStackedBarWidget shows how a total divides over a few groups: ONE bar cut\n * into segments, with a legend below it that names every segment and gives\n * its count. Use it for \"my cases per step\" or \"requests per channel\".\n *\n * Segments come from one grouped count request against an OpenRegister\n * source (`source.groupBy`), or from static `segments`. `order` fixes the\n * order of the segments, and `labels` gives a group key a readable name.\n * Colours come from one ramp built on the theme's primary colour; the steps\n * differ in lightness, never in hue alone. Resolved by its registry type key\n * `stacked-bar`.\n *\n * ```json\n * {\n * \"widgetKey\": \"stacked-bar\",\n * \"props\": {\n * \"content\": {\n * \"source\": { \"register\": \"dossiq\", \"schema\": \"case\", \"groupBy\": \"status\", \"filter\": { \"assignee\": \"@me\" } },\n * \"order\": [\"received\", \"in_progress\", \"decision\", \"publish\"],\n * \"labels\": { \"received\": \"Received\", \"in_progress\": \"In progress\", \"decision\": \"Decision\", \"publish\": \"Publish\" }\n * }\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnStackedBarWidget',\n\n\tcomponents: { CnWidgetEmptyState, NcLoadingIcon },\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot. Segment labels and\n\t\t * the empty text run through it. Identity by default.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's configuration. `source` names the OpenRegister\n\t\t * register and schema, the `groupBy` field and an optional `filter`\n\t\t * (the shared @-token grammar). `segments` are static entries\n\t\t * `{ key?, label, value }` used when there is no source. `order` lists\n\t\t * group keys in the order they render; groups it does not list follow\n\t\t * in the order they arrive, and a listed key without records renders\n\t\t * with a count of 0. `labels` maps a group key to its label.\n\t\t * `emptyText` is shown when there is nothing to count. `inset: true`\n\t\t * draws the bar and legend inside the board's inset (16px above, 24px\n\t\t * at the sides, 22px below) instead of from card edge to card edge.\n\t\t * `endpointSource` (`{ url, responsePath? }`, `@objectId` filled from\n\t\t * the page) reads the groups from an app endpoint instead: the counts\n\t\t * at `groupsPath` (a `{ key: count }` map or `[{ key, count }]`) and the\n\t\t * whole at `totalPath`. `colors` maps a group key to a tone (`success`,\n\t\t * `warning`, `error`, `info`, `neutral`) or a CSS colour. `remainder`\n\t\t * (`{ key?, label }`) adds what the groups leave of the whole as a last,\n\t\t * neutral segment. `heading` and `headingMeta` are templates over\n\t\t * `{counted}`, `{total}`, `{remaining}` and the endpoint's own fields,\n\t\t * drawn above the bar. `headerCount: true` reports the sum of the\n\t\t * segments to the card (`header` event, `count`), for a chip after the\n\t\t * title or a `{count}` in it.\n\t\t *\n\t\t * @type {{inset?: boolean, compact?: boolean, headerCount?: boolean, source?: {register?: string, schema?: string, groupBy?: string, filter?: object}, segments?: Array<{key?: string, label: string, value: number}>, order?: Array<string>, labels?: {[key: string]: string}, emptyText?: string}}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Translate function. Falls back to the injected `cnTranslate`. Pass it\n\t\t * when mounting the widget outside a CnAppRoot.\n\t\t *\n\t\t * @type {((key: string) => string)|null}\n\t\t */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The record on the page, for the `@objectId` token of `endpointSource`. */\n\t\tobjectId: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['header'],\n\n\tsetup(props) {\n\t\tconst objectCtxRaw = inject('cnObjectContext', null)\n\t\tconst { data, loading, error } = useEndpointSource(() => props.content?.endpointSource || null, {\n\t\t\tctx: () => {\n\t\t\t\tconst ctx = (isRef(objectCtxRaw) ? objectCtxRaw.value : objectCtxRaw) || {}\n\t\t\t\treturn { ...ctx, objectId: props.objectId ?? ctx.objectId ?? null, object: ctx.object || null }\n\t\t\t},\n\t\t})\n\t\treturn { epData: data, epLoading: loading, epError: error }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tgroups: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the widget draws the board's inset (`content.inset`). Off\n\t\t * by default, which keeps it edge to edge as before.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-strip-and-a-stacked-bar-can-take-the-board-inset\n\t\t * @return {boolean}\n\t\t */\n\t\tinset() {\n\t\t\treturn Boolean(this.content && this.content.inset === true)\n\t\t},\n\n\t\t/**\n\t\t * Whether the widget is the compact summary box (`content.compact`):\n\t\t * a 15px heading, one thin bar and the legend on one line.\n\t\t *\n\t\t * @spec openspec/changes/stacked-bar-compact/specs/stacked-bar-compact/spec.md#requirement-a-stacked-bar-can-be-drawn-as-the-compact-summary-box\n\t\t * @return {boolean}\n\t\t */\n\t\tcompact() {\n\t\t\treturn Boolean(this.content && this.content.compact === true)\n\t\t},\n\n\t\t/**\n\t\t * The translate function in use.\n\t\t *\n\t\t * @return {(key: string) => string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\teffectiveTranslate() {\n\t\t\treturn this.translate ?? this.cnTranslate\n\t\t},\n\n\t\t/**\n\t\t * Whether the segments come from OpenRegister.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\thasEndpoint() {\n\t\t\treturn Boolean(this.content.endpointSource && this.content.endpointSource.url)\n\t\t},\n\n\t\t/** @return {boolean} Whether the counts are still loading. */\n\t\tisLoading() {\n\t\t\treturn this.hasEndpoint ? Boolean(this.epLoading) && this.epData === null : this.loading\n\t\t},\n\n\t\t/** @return {string} The load error, or ''. */\n\t\tloadError() {\n\t\t\treturn this.hasEndpoint ? (this.epError || '') : this.error\n\t\t},\n\n\t\t/**\n\t\t * The endpoint's groups as `{ key, count }`, from a `{ key: count }`\n\t\t * map or a `[{ key, count }]` list at `groupsPath`.\n\t\t *\n\t\t * @return {Array<{key: string, count: number}>}\n\t\t */\n\t\tendpointGroups() {\n\t\t\tconst raw = pluck(this.epData, this.content.groupsPath || 'groups')\n\t\t\tconst entries = Array.isArray(raw)\n\t\t\t\t? raw.filter((entry) => entry && typeof entry === 'object').map((entry) => [entry.key, entry.count])\n\t\t\t\t: Object.entries(raw && typeof raw === 'object' ? raw : {})\n\t\t\treturn entries.map(([key, count]) => ({ key: String(key), count: Number(count) > 0 ? Number(count) : 0 }))\n\t\t},\n\n\t\t/**\n\t\t * The whole the groups are part of (`totalPath` of the endpoint), or\n\t\t * null when none is given.\n\t\t *\n\t\t * @return {number|null}\n\t\t */\n\t\twhole() {\n\t\t\tif (!this.hasEndpoint || !this.content.totalPath) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst value = Number(pluck(this.epData, this.content.totalPath))\n\t\t\treturn Number.isFinite(value) && value >= 0 ? value : null\n\t\t},\n\n\t\t/** @return {number} The sum of the groups, without the remainder. */\n\t\tcounted() {\n\t\t\treturn this.rawGroups.reduce((sum, group) => sum + group.count, 0)\n\t\t},\n\n\t\t/** @return {number} What the groups leave of the whole. */\n\t\tremaining() {\n\t\t\treturn this.whole === null ? 0 : Math.max(0, this.whole - this.counted)\n\t\t},\n\n\t\t/**\n\t\t * The values the heading templates read.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\ttemplateValues() {\n\t\t\tconst own = this.epData && typeof this.epData === 'object' && !Array.isArray(this.epData) ? this.epData : {}\n\t\t\treturn { ...own, counted: this.counted, total: this.whole === null ? this.counted : this.whole, remaining: this.remaining }\n\t\t},\n\n\t\t/** @return {string} The line above the bar, or ''. */\n\t\theading() {\n\t\t\treturn this.fillTemplate(this.content.heading)\n\t\t},\n\n\t\t/** @return {string} The muted line beside the heading, or ''. */\n\t\theadingMeta() {\n\t\t\treturn this.fillTemplate(this.content.headingMeta)\n\t\t},\n\n\t\thasSource() {\n\t\t\tconst s = this.content.source || {}\n\t\t\treturn Boolean(s.register && s.schema && s.groupBy)\n\t\t},\n\n\t\t/**\n\t\t * The raw groups, from the source or the static list, as `{ key, label, count }`.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, count: number}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\trawGroups() {\n\t\t\tif (this.hasEndpoint) {\n\t\t\t\treturn this.endpointGroups.map((group) => ({ key: group.key, label: group.key, count: group.count }))\n\t\t\t}\n\t\t\tif (this.hasSource) {\n\t\t\t\treturn this.groups.map((group) => ({ key: group.key, label: group.key, count: group.count }))\n\t\t\t}\n\t\t\tconst list = Array.isArray(this.content.segments) ? this.content.segments : []\n\t\t\treturn list\n\t\t\t\t.filter((segment) => segment && typeof segment === 'object')\n\t\t\t\t.map((segment, index) => {\n\t\t\t\t\tconst count = Number(segment.value)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkey: String(segment.key ?? segment.label ?? index),\n\t\t\t\t\t\tlabel: String(segment.label ?? segment.key ?? ''),\n\t\t\t\t\t\tcount: Number.isFinite(count) && count > 0 ? count : 0,\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The segments in render order, each with its label and colour.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, count: number, color: string}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\tsegments() {\n\t\t\tconst order = Array.isArray(this.content.order) ? this.content.order.map(String) : []\n\t\t\tconst labels = (this.content.labels && typeof this.content.labels === 'object') ? this.content.labels : {}\n\t\t\tconst byKey = {}\n\t\t\tfor (const group of this.rawGroups) {\n\t\t\t\tbyKey[group.key] = group\n\t\t\t}\n\t\t\tconst ordered = []\n\t\t\tfor (const key of order) {\n\t\t\t\tordered.push(byKey[key] || { key, label: key, count: 0 })\n\t\t\t}\n\t\t\tfor (const group of this.rawGroups) {\n\t\t\t\tif (!order.includes(group.key)) {\n\t\t\t\t\tordered.push(group)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst colors = (this.content.colors && typeof this.content.colors === 'object') ? this.content.colors : {}\n\t\t\tconst segments = ordered.map((group, index) => ({\n\t\t\t\tkey: group.key,\n\t\t\t\tlabel: this.effectiveTranslate(typeof labels[group.key] === 'string' ? labels[group.key] : group.label),\n\t\t\t\tcount: group.count,\n\t\t\t\tcolor: this.colorOf(colors[group.key]) || rampColor(index, ordered.length),\n\t\t\t}))\n\t\t\tconst remainder = this.content.remainder\n\t\t\tif (remainder && typeof remainder === 'object' && this.remaining > 0) {\n\t\t\t\tsegments.push({\n\t\t\t\t\tkey: String(remainder.key || 'remainder'),\n\t\t\t\t\tlabel: this.effectiveTranslate(String(remainder.label || '')),\n\t\t\t\t\tcount: this.remaining,\n\t\t\t\t\tcolor: this.colorOf(remainder.color) || TONE_COLORS.neutral,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn segments\n\t\t},\n\n\t\t/**\n\t\t * The segments that take up room in the bar.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\tbarSegments() {\n\t\t\treturn this.segments.filter((segment) => segment.count > 0)\n\t\t},\n\n\t\t/**\n\t\t * The total reported to the card under `headerCount: true`, or null\n\t\t * while loading, after an error or without `headerCount`.\n\t\t *\n\t\t * @return {number|null}\n\t\t * @spec openspec/changes/widget-title-count/specs/widget-title-count/spec.md#requirement-a-widget-title-can-carry-the-total-its-widget-reports\n\t\t */\n\t\theaderTotal() {\n\t\t\tif (this.content.headerCount !== true || this.isLoading || this.loadError) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.total\n\t\t},\n\n\t\t/**\n\t\t * The sum of all counts.\n\t\t *\n\t\t * @return {number}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\ttotal() {\n\t\t\treturn this.segments.reduce((sum, segment) => sum + segment.count, 0)\n\t\t},\n\n\t\t/**\n\t\t * The text shown when there is nothing to count.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\temptyLabel() {\n\t\t\tconst text = this.content.emptyText\n\t\t\treturn (typeof text === 'string' && text !== '')\n\t\t\t\t? this.effectiveTranslate(text)\n\t\t\t\t: t('nextcloud-vue', 'Nothing to show yet')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The loading text.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading…')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The text shown when the counts could not be loaded.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\terrorLabel() {\n\t\t\treturn t('nextcloud-vue', 'The numbers could not be loaded.')\n\t\t},\n\n\t\t/**\n\t\t * Changes whenever a new fetch is needed.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\tsourceKey() {\n\t\t\treturn JSON.stringify(this.content.source || {})\n\t\t},\n\t},\n\n\twatch: {\n\t\t/** @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget */\n\t\tsourceKey() {\n\t\t\tthis.fetchGroups()\n\t\t},\n\n\t\t/** @spec openspec/changes/widget-title-count/specs/widget-title-count/spec.md#requirement-a-widget-title-can-carry-the-total-its-widget-reports */\n\t\theaderTotal: {\n\t\t\timmediate: true,\n\t\t\thandler(total) {\n\t\t\t\tif (total !== null) {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event header With `headerCount: true`, once the counts have loaded: `{ link: null, note: '', count }`, the sum of the segments.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('header', { link: null, note: '', count: total })\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.fetchGroups()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The CSS colour of a tone or colour value.\n\t\t *\n\t\t * @param {string} value A tone name or a CSS colour.\n\t\t * @return {string} The colour, or '' for none.\n\t\t */\n\t\tcolorOf(value) {\n\t\t\tif (typeof value !== 'string' || value === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn TONE_COLORS[value] || value\n\t\t},\n\n\t\t/**\n\t\t * Translate a heading template and fill its `{name}` placeholders.\n\t\t *\n\t\t * @param {string} template The template.\n\t\t * @return {string} The line, or '' without a template.\n\t\t */\n\t\tfillTemplate(template) {\n\t\t\tif (typeof template !== 'string' || template === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst values = this.templateValues\n\t\t\treturn this.effectiveTranslate(template).replace(/\\{(\\w+)\\}/g, (match, name) => (\n\t\t\t\tvalues[name] === undefined || values[name] === null ? '' : String(values[name])\n\t\t\t))\n\t\t},\n\n\t\t/**\n\t\t * Load the count per group in one request.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-stacked-bar-widget\n\t\t */\n\t\tasync fetchGroups() {\n\t\t\tif (!this.hasSource) {\n\t\t\t\tthis.groups = []\n\t\t\t\tthis.error = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst requestKey = this.sourceKey\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst groups = await fetchGroupedCounts(this.content.source)\n\t\t\t\tif (requestKey !== this.sourceKey) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.groups = groups\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnStackedBarWidget] failed to load counts:', e)\n\t\t\t\tthis.error = (e && e.message) || 'error'\n\t\t\t\tthis.groups = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-stacked-bar {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\twidth: 100%;\n}\n\n/* `content.inset: true`: the board's inset inside the card (zuiddrecht-pixel-\n gaps-3). A dashboard renders its widgets flush, so without it the strip ran\n from card edge to card edge. Theme hook: --cn-widget-board-inset. */\n.cn-stacked-bar--inset {\n\tbox-sizing: border-box;\n\tpadding: var(--cn-widget-board-inset, 16px 24px 22px);\n}\n\n.cn-stacked-bar__heading {\n\tdisplay: flex;\n\talign-items: baseline;\n\tjustify-content: space-between;\n\tgap: 12px;\n}\n\n.cn-stacked-bar__heading-meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-stacked-bar__state {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-stacked-bar__bar {\n\tdisplay: flex;\n\tgap: 3px;\n\theight: 12px;\n}\n\n.cn-stacked-bar__segment {\n\tflex-basis: 0;\n\tmin-width: 4px;\n\tborder-radius: 6px;\n}\n\n.cn-stacked-bar__legend {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n\tgap: 12px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-stacked-bar__legend-item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-stacked-bar__label {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tfont-size: 0.95em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow-wrap: anywhere;\n}\n\n.cn-stacked-bar__dot {\n\tflex: none;\n\twidth: 10px;\n\theight: 10px;\n\tborder-radius: 3px;\n}\n\n.cn-stacked-bar__count {\n\tfont-size: 1.5em;\n\tfont-weight: 700;\n\tline-height: 1.2;\n\tcolor: var(--color-main-text);\n}\n\n/* `content.compact: true`: the summary box (DqZaakDocumenten \"4 van 7\n beoordeeld\"): the sunken ground, a 15px heading, one 8px bar of joined\n segments and the legend on one line, each count beside its label. */\n.cn-stacked-bar--compact {\n\tbox-sizing: border-box;\n\tgap: 10px;\n\tpadding: 14px 16px;\n\tborder-radius: 10px;\n\tbackground: var(--cn-board-sunken-ground, var(--color-background-hover));\n}\n\n.cn-stacked-bar--compact .cn-stacked-bar__heading {\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tfont-size: 15px;\n}\n\n.cn-stacked-bar--compact .cn-stacked-bar__bar {\n\tgap: 0;\n\theight: 8px;\n\tborder-radius: 4px;\n\toverflow: hidden;\n}\n\n.cn-stacked-bar--compact .cn-stacked-bar__segment {\n\tborder-radius: 0;\n}\n\n.cn-stacked-bar--compact .cn-stacked-bar__legend {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px 18px;\n\tfont-size: 14px;\n}\n\n.cn-stacked-bar--compact .cn-stacked-bar__legend-item {\n\tflex-direction: row;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-stacked-bar--compact .cn-stacked-bar__label {\n\tcolor: var(--color-main-text);\n\tfont-size: 1em;\n}\n\n.cn-stacked-bar--compact .cn-stacked-bar__dot {\n\tborder-radius: 50%;\n}\n\n.cn-stacked-bar--compact .cn-stacked-bar__count {\n\tfont-size: 1em;\n\tline-height: inherit;\n}\n</style>\n"],"names":["_normalizeClass","_openBlock","_createElementBlock","_createVNode","_createElementVNode","_toDisplayString","_Fragment","_renderList","_normalizeStyle"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnTransitionInputFields.vue2.js';
|
|
|
2
2
|
import { render } from './CnTransitionInputFields.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-731d1a6c";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -3,7 +3,7 @@ import { NcCheckboxRadioSwitch, NcSelect, NcTextArea, NcTextField } from '@nextc
|
|
|
3
3
|
import __sfc_main$1 from '../CnResourceSelect/CnResourceSelect.vue.js';
|
|
4
4
|
import __sfc_main$2 from '../CnTemplateSelect/CnTemplateSelect.vue.js';
|
|
5
5
|
import { readPath } from '../../utils/readPath.js';
|
|
6
|
-
import { resolveInputFields, isInputFilled, applyTemplateText } from '../../utils/transitionInputs.js';
|
|
6
|
+
import { resolveInputFields, daysUntilIso, isInputFilled, applyTemplateText } from '../../utils/transitionInputs.js';
|
|
7
7
|
import __sfc_main from '../CnIcon/CnIcon.vue.js';
|
|
8
8
|
|
|
9
9
|
let uid = 0;
|
|
@@ -299,6 +299,28 @@ var script = {
|
|
|
299
299
|
return this.markOptional && !field.required ? `${field.label} (${translate('nextcloud-vue', 'optional')})` : field.label
|
|
300
300
|
},
|
|
301
301
|
|
|
302
|
+
/**
|
|
303
|
+
* The label of a days input's date field (`dateField`), with the
|
|
304
|
+
* optional marker when asked for: the date is never required.
|
|
305
|
+
*
|
|
306
|
+
* @param {object} field The descriptor.
|
|
307
|
+
* @return {string}
|
|
308
|
+
*/
|
|
309
|
+
dateFieldLabel(field) {
|
|
310
|
+
const label = String(field.input.dateField.label || '');
|
|
311
|
+
return this.markOptional ? `${label} (${translate('nextcloud-vue', 'optional')})` : label
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* The days a chosen date stands for.
|
|
316
|
+
*
|
|
317
|
+
* @param {string} iso The date input's value.
|
|
318
|
+
* @return {string} The days, '' without a date.
|
|
319
|
+
*/
|
|
320
|
+
daysUntilIso(iso) {
|
|
321
|
+
return daysUntilIso(iso)
|
|
322
|
+
},
|
|
323
|
+
|
|
302
324
|
/**
|
|
303
325
|
* Label of a sub-field, with the required marker.
|
|
304
326
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnTransitionInputFields.vue2.js","sources":["../../../../src/components/CnTransitionInputFields/CnTransitionInputFields.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div\n\t\tclass=\"cn-transition-input-fields\"\n\t\t:class=\"{ 'cn-transition-input-fields--columns': columns === 2 }\">\n\t\t<template v-if=\"summaryShown\">\n\t\t\t<div v-if=\"summary.card\"\n\t\t\t\tclass=\"cn-transition-input-fields__card cn-transition-input-fields__wide\"\n\t\t\t\t:data-testid=\"summaryTestid('card')\">\n\t\t\t\t<component :is=\"summary.card.icon\"\n\t\t\t\t\tv-if=\"summary.card.icon\"\n\t\t\t\t\t:size=\"20\"\n\t\t\t\t\tclass=\"cn-transition-input-fields__card-icon\" />\n\t\t\t\t<span class=\"cn-transition-input-fields__card-text\">\n\t\t\t\t\t<strong>{{ contextText(summary.card.title) }}</strong>\n\t\t\t\t\t<span class=\"cn-transition-input-fields__muted\">{{ cardMeta }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span v-if=\"contextText(summary.card.badge)\" class=\"cn-transition-input-fields__badge\">\n\t\t\t\t\t{{ contextText(summary.card.badge) }}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<section v-if=\"Array.isArray(summary.facts) && summary.facts.length > 0\"\n\t\t\t\tclass=\"cn-transition-input-fields__wide\"\n\t\t\t\t:data-testid=\"summaryTestid('facts')\">\n\t\t\t\t<h3 v-if=\"summary.title\" class=\"cn-transition-input-fields__section-title\">\n\t\t\t\t\t{{ summary.title }}\n\t\t\t\t</h3>\n\t\t\t\t<dl class=\"cn-transition-input-fields__facts\">\n\t\t\t\t\t<div v-for=\"fact in summary.facts\" :key=\"fact.path || fact.label\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ contextText(fact.path) || fact.fallback || '' }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t</section>\n\t\t</template>\n\n\t\t<template v-for=\"field in shownFields\" :key=\"field.key\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-transition-input__field\"\n\t\t\t\t:class=\"{\n\t\t\t\t\t'cn-transition-input__field--refused': isRefused(field.key),\n\t\t\t\t\t'cn-transition-input-fields__wide': field.input.wide === true,\n\t\t\t\t}\"\n\t\t\t\t:data-testid=\"`cn-transition-input-${field.key}`\">\n\t\t\t\t<!-- A template fills another input; it is never sent itself. -->\n\t\t\t\t<CnTemplateSelect v-if=\"field.widget === 'template'\"\n\t\t\t\t\t:url=\"field.input.url || ''\"\n\t\t\t\t\t:schema=\"field.input.schema || ''\"\n\t\t\t\t\t:params=\"field.input.params || {}\"\n\t\t\t\t\t:mapOption=\"field.input.mapOption\"\n\t\t\t\t\t:inputLabel=\"field.label\"\n\t\t\t\t\t:placeholder=\"field.input.placeholder || ''\"\n\t\t\t\t\t:data-testid=\"field.input.testid || `cn-transition-input-template-${field.key}`\"\n\t\t\t\t\t@apply=\"applyTemplate(field, $event)\" />\n\n\t\t\t\t<!-- An input that declares its choices is picked from them. -->\n\t\t\t\t<NcSelect v-else-if=\"field.widget === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"field.options\"\n\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t:inputLabel=\"labelOf(field)\"\n\t\t\t\t\t:clearable=\"!field.required\"\n\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t:data-testid=\"field.input.testid || `cn-transition-input-select-${field.key}`\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event && $event.value !== undefined ? $event.value : '')\" />\n\n\t\t\t\t<!-- An input that points at another record is picked, never typed. -->\n\t\t\t\t<div v-else-if=\"field.widget === 'reference'\" class=\"cn-transition-input__reference\">\n\t\t\t\t\t<label class=\"cn-transition-input__label\">{{ labelOf(field) }}</label>\n\t\t\t\t\t<CnResourceSelect\n\t\t\t\t\t\t:register=\"field.referenceRegister\"\n\t\t\t\t\t\t:schema=\"String(field.reference.schema)\"\n\t\t\t\t\t\t:labelField=\"(field.picker && field.picker.labelField) || field.reference.labelField || 'name'\"\n\t\t\t\t\t\t:filter=\"(field.picker && field.picker.filter) || {}\"\n\t\t\t\t\t\t:exclude=\"excludedIds(field)\"\n\t\t\t\t\t\t:allowCreate=\"false\"\n\t\t\t\t\t\t:preload=\"true\"\n\t\t\t\t\t\t:inputLabel=\"labelOf(field)\"\n\t\t\t\t\t\t:modelValue=\"String(modelValue[field.key] ?? '')\"\n\t\t\t\t\t\t:data-testid=\"`cn-transition-input-picker-${field.key}`\"\n\t\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\t\t\t\t</div>\n\n\t\t\t\t<!-- An object input: its sub-properties, one field at a time. -->\n\t\t\t\t<fieldset v-else-if=\"field.widget === 'object'\" class=\"cn-transition-input__object\">\n\t\t\t\t\t<legend>{{ labelOf(field) }}</legend>\n\t\t\t\t\t<div v-for=\"sub in field.subFields\"\n\t\t\t\t\t\t:key=\"sub.key\"\n\t\t\t\t\t\tclass=\"cn-transition-input__subfield\"\n\t\t\t\t\t\t:data-testid=\"`cn-transition-input-${field.key}-${sub.key}`\">\n\t\t\t\t\t\t<NcTextArea v-if=\"sub.widget === 'textarea'\"\n\t\t\t\t\t\t\t:modelValue=\"String(subValue(field.key, sub.key) ?? '')\"\n\t\t\t\t\t\t\t:label=\"subLabel(sub)\"\n\t\t\t\t\t\t\trows=\"3\"\n\t\t\t\t\t\t\t@update:modelValue=\"setSubValue(field.key, sub.key, $event)\" />\n\t\t\t\t\t\t<NcCheckboxRadioSwitch v-else-if=\"sub.widget === 'checkbox'\"\n\t\t\t\t\t\t\t:modelValue=\"subValue(field.key, sub.key) === true\"\n\t\t\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t\t\t@update:modelValue=\"setSubValue(field.key, sub.key, $event === true)\">\n\t\t\t\t\t\t\t{{ subLabel(sub) }}\n\t\t\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t\t\t<NcTextField v-else\n\t\t\t\t\t\t\t:modelValue=\"String(subValue(field.key, sub.key) ?? '')\"\n\t\t\t\t\t\t\t:type=\"sub.widget === 'number' ? 'number' : 'text'\"\n\t\t\t\t\t\t\t:label=\"subLabel(sub)\"\n\t\t\t\t\t\t\t@update:modelValue=\"setSubValue(field.key, sub.key, $event)\" />\n\t\t\t\t\t\t<p v-if=\"isSubRefused(field.key, sub.key)\"\n\t\t\t\t\t\t\tclass=\"cn-transition-input__field-error\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-transition-input-error-${field.key}.${sub.key}`\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'This value was not accepted.') }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</fieldset>\n\n\t\t\t\t<NcCheckboxRadioSwitch v-else-if=\"field.widget === 'checkbox'\"\n\t\t\t\t\t:modelValue=\"modelValue[field.key] === true\"\n\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event === true)\">\n\t\t\t\t\t{{ labelOf(field) }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<template v-else>\n\t\t\t\t\t<!-- A label drawn above the field, as a form board draws it,\n\t\t\t\t\t is tied to the field by its id. -->\n\t\t\t\t\t<label v-if=\"field.input.labelOutside === true\"\n\t\t\t\t\t\tclass=\"cn-transition-input-fields__label\"\n\t\t\t\t\t\t:for=\"inputId(field)\">{{ labelOf(field) }}</label>\n\t\t\t\t\t<NcTextArea v-if=\"field.widget === 'textarea'\"\n\t\t\t\t\t\t:id=\"inputId(field)\"\n\t\t\t\t\t\t:modelValue=\"String(modelValue[field.key] ?? '')\"\n\t\t\t\t\t\t:label=\"labelOf(field)\"\n\t\t\t\t\t\t:labelOutside=\"field.input.labelOutside === true\"\n\t\t\t\t\t\t:helperText=\"field.description || ''\"\n\t\t\t\t\t\t:rows=\"field.input.rows || 4\"\n\t\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t\tresize=\"vertical\"\n\t\t\t\t\t\t:data-testid=\"field.input.testid || null\"\n\t\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\t\t\t\t\t<NcTextField v-else\n\t\t\t\t\t\t:id=\"inputId(field)\"\n\t\t\t\t\t\t:modelValue=\"String(modelValue[field.key] ?? '')\"\n\t\t\t\t\t\t:type=\"textType(field)\"\n\t\t\t\t\t\t:label=\"labelOf(field)\"\n\t\t\t\t\t\t:labelOutside=\"field.input.labelOutside === true\"\n\t\t\t\t\t\t:helperText=\"field.description || ''\"\n\t\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t\t:data-testid=\"field.input.testid || null\"\n\t\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\t\t\t\t\t<span v-if=\"field.input.note\" class=\"cn-transition-input-fields__muted\">\n\t\t\t\t\t\t{{ field.input.note }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\n\t\t\t\t<p v-if=\"isRefused(field.key)\"\n\t\t\t\t\tclass=\"cn-transition-input__field-error\"\n\t\t\t\t\t:data-testid=\"`cn-transition-input-error-${field.key}`\">\n\t\t\t\t\t{{ refusalFor(field) }}\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t<!-- What the input means once filled in, such as the day a paused\n\t\t\t term comes back. Shown only once it can be worked out. -->\n\t\t\t<div v-if=\"field.previewText\"\n\t\t\t\t:key=\"`${field.key}-preview`\"\n\t\t\t\tclass=\"cn-transition-input-fields__preview cn-transition-input-fields__wide\"\n\t\t\t\t:data-testid=\"field.input.preview.testid || `cn-transition-input-preview-${field.key}`\">\n\t\t\t\t<span class=\"cn-transition-input-fields__label\">{{ field.input.preview.label }}</span>\n\t\t\t\t<span :data-testid=\"`${field.input.preview.testid || `cn-transition-input-preview-${field.key}`}-value`\">{{ field.previewText }}</span>\n\t\t\t\t<span v-if=\"field.input.preview.note\" class=\"cn-transition-input-fields__muted\">\n\t\t\t\t\t{{ field.input.preview.note }}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- A tinted box with the act's own icon (DqArchiveren \"Daarna alleen\n\t\t lezen\", a lock), drawn here rather than as a note card: a note\n\t\t card on the tinted card of a chosen act disappears into it. -->\n\t\t<div v-if=\"notice && notice.text\"\n\t\t\tclass=\"cn-transition-input-fields__notice cn-transition-input-fields__wide\"\n\t\t\t:class=\"`cn-transition-input-fields__notice--${notice.type || 'info'}`\"\n\t\t\trole=\"note\"\n\t\t\tdata-testid=\"cn-transition-input-fields-notice\">\n\t\t\t<CnIcon :name=\"notice.icon || 'InformationOutline'\" :size=\"20\" class=\"cn-transition-input-fields__notice-icon\" />\n\t\t\t<span class=\"cn-transition-input-fields__notice-text\">\n\t\t\t\t<strong v-if=\"notice.heading\">{{ notice.heading }}</strong>\n\t\t\t\t<span>{{ notice.text }}</span>\n\t\t\t</span>\n\t\t</div>\n\n\t\t<!-- A key the refusal names that this form never offered is not a\n\t\t field anybody can correct here. Saying so beats saying nothing:\n\t\t the request sent something the action does not accept. -->\n\t\t<p v-for=\"key in unofferedRefusals\"\n\t\t\t:key=\"`unoffered-${key}`\"\n\t\t\tclass=\"cn-transition-input__field-error\"\n\t\t\t:data-testid=\"`cn-transition-input-error-${key}`\">\n\t\t\t{{ t('nextcloud-vue', 'This action does not accept the field \"{field}\".', { field: key }) }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcCheckboxRadioSwitch, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'\nimport CnTemplateSelect from '../CnTemplateSelect/CnTemplateSelect.vue'\nimport { readPath } from '../../utils/readPath.js'\nimport { applyTemplateText, isInputFilled, resolveInputFields } from '../../utils/transitionInputs.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\nlet uid = 0\n\n/**\n * CnTransitionInputFields — the declared inputs of a transition or an act,\n * drawn wherever the host puts them.\n *\n * CnTransitionInputDialog draws them in their own dialog. A host that lists\n * several acts and opens the chosen one's inputs in place (under its card)\n * draws them with this component, so both read the same declarations and\n * decide the same way. The values are a `v-model` object keyed by field;\n * whether they are complete is `inputFieldsComplete(resolveInputFields(...))`.\n *\n * Next to the schema-resolved widgets an input may declare `widget` (`text`,\n * `textarea`, `number`, `date`, `select`, `template`), `optionsSource` (a dot\n * path into `context`), `other`, `helperFrom`, `preview`, `default` and\n * presentation keys; see `resolveInputFields` in `utils/transitionInputs.js`.\n *\n * ```vue\n * <CnTransitionInputFields\n * v-model=\"values\"\n * :inputs=\"act.inputs\"\n * :context=\"{ pauseReasons }\"\n * :columns=\"2\" />\n * ```\n *\n * @spec openspec/changes/dq-abstract-forms/specs/transition-inputs/spec.md\n */\nexport default {\n\tname: 'CnTransitionInputFields',\n\n\tcomponents: {\n\t\tCnIcon,\n\t\tCnResourceSelect,\n\t\tCnTemplateSelect,\n\t\tNcCheckboxRadioSwitch,\n\t\tNcSelect,\n\t\tNcTextArea,\n\t\tNcTextField,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The declared inputs: `[{ field, required, label, widget, options,\n\t\t * optionsSource, optionValue, optionLabel, other, helperFrom, preview,\n\t\t * default, description, note, labelOutside, id, wide, testid, rows,\n\t\t * placeholder }]`, plus for a `template` input `url`, `schema`,\n\t\t * `params`, `mapOption` and `applyTo`.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\tinputs: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The values, keyed by field.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tmodelValue: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The object's JSON Schema, which labels and types the inputs that\n\t\t * declare no widget of their own.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The record the action runs on: `picker.excludeSelf` leaves its id\n\t\t * out, and an object input is merged over its present value.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tcurrentObject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Register slug the reference pickers look in when the schema property\n\t\t * names none of its own.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * What `optionsSource` and the summary paths read, such as the case\n\t\t * type's choices and the record shown in the summary.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontext: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The input keys a refusal named. Each one this form offers is marked\n\t\t * on its own field; each one it does not is reported as a key the\n\t\t * action does not accept.\n\t\t *\n\t\t * @type {string[]}\n\t\t */\n\t\tfieldErrors: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Whether the inputs are locked, such as while the action posts.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * 1 stacks the inputs; 2 sets them side by side, with `wide` inputs,\n\t\t * previews, the summary and the notice across both columns.\n\t\t *\n\t\t * @type {number}\n\t\t */\n\t\tcolumns: {\n\t\t\ttype: Number,\n\t\t\tdefault: 1,\n\t\t},\n\n\t\t/**\n\t\t * Whether a required input's label carries a ` *` marker.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tmarkRequired: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Whether an input that is not required says so, with ` (optional)`\n\t\t * after its label, as the boards mark them (DqVolgendeStap \"Dagen die\n\t\t * de aanvrager krijgt (niet verplicht)\").\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tmarkOptional: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * A read-only block above the inputs about what the action works on:\n\t\t * `{ when, card: { icon, title, meta: [], badge }, title, facts:\n\t\t * [{ label, path, fallback }], testid }`. `when`, `title`, `meta`,\n\t\t * `badge` and each fact's `path` are dot paths into `context`; the\n\t\t * block is shown only when `when` resolves.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tsummary: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * A note under the inputs: `{ type, heading, text, icon? }`. `type` is\n\t\t * info (the default), warning or error; `icon` names a CnIcon.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tnotice: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['update:modelValue'],\n\n\tdata() {\n\t\tuid += 1\n\t\treturn {\n\t\t\tuid,\n\t\t\t// The values with every change made since the host last passed\n\t\t\t// them in, so two changes in one tick both survive.\n\t\t\tlatest: { ...this.modelValue },\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Every declared input, resolved against the schema and the context. */\n\t\tfields() {\n\t\t\treturn resolveInputFields(this.inputs, {\n\t\t\t\tschema: this.schema,\n\t\t\t\tregister: this.register,\n\t\t\t\tcontext: this.context,\n\t\t\t\tvalues: this.modelValue,\n\t\t\t})\n\t\t},\n\n\t\t/** The inputs that are asked now. */\n\t\tshownFields() {\n\t\t\treturn this.fields.filter((field) => !field.hidden)\n\t\t},\n\n\t\t/** Whether the summary block shows. */\n\t\tsummaryShown() {\n\t\t\tif (!this.summary || typeof this.summary !== 'object') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn !this.summary.when || Boolean(readPath(this.context, this.summary.when))\n\t\t},\n\n\t\t/** The summary card's second line. */\n\t\tcardMeta() {\n\t\t\tconst meta = Array.isArray(this.summary?.card?.meta) ? this.summary.card.meta : []\n\t\t\treturn meta.map((path) => this.contextText(path)).filter(Boolean).join(' · ')\n\t\t},\n\n\t\t/** The refusal's field keys, normalised to strings. */\n\t\trefusedKeys() {\n\t\t\tif (!Array.isArray(this.fieldErrors)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.fieldErrors.filter((key) => typeof key === 'string' && key !== '')\n\t\t},\n\n\t\t/** The refused keys this form offers no field for. */\n\t\tunofferedRefusals() {\n\t\t\tconst offered = this.fields.map((field) => field.key)\n\t\t\treturn this.refusedKeys.filter((key) => !offered.includes(key))\n\t\t},\n\t},\n\n\twatch: {\n\t\tmodelValue(values) {\n\t\t\tthis.latest = { ...values }\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A context value as text, '' when absent.\n\t\t *\n\t\t * @param {string} path Dot path into the context.\n\t\t * @return {string}\n\t\t */\n\t\tcontextText(path) {\n\t\t\tif (!path) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst value = readPath(this.context, path)\n\t\t\treturn value === null || value === undefined ? '' : String(value)\n\t\t},\n\n\t\t/**\n\t\t * The test id of one part of the summary.\n\t\t *\n\t\t * @param {string} part `card` or `facts`.\n\t\t * @return {string}\n\t\t */\n\t\tsummaryTestid(part) {\n\t\t\treturn `${this.summary.testid || 'cn-transition-input-summary'}-${part}`\n\t\t},\n\n\t\t/**\n\t\t * The field's label, with the required or the optional marker when asked for.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {string}\n\t\t */\n\t\tlabelOf(field) {\n\t\t\tif (this.markRequired && field.required) {\n\t\t\t\treturn `${field.label} *`\n\t\t\t}\n\t\t\treturn this.markOptional && !field.required ? `${field.label} (${t('nextcloud-vue', 'optional')})` : field.label\n\t\t},\n\n\t\t/**\n\t\t * Label of a sub-field, with the required marker.\n\t\t *\n\t\t * @param {object} sub The sub-field descriptor.\n\t\t * @return {string}\n\t\t */\n\t\tsubLabel(sub) {\n\t\t\treturn this.markRequired && sub.required ? `${sub.label} *` : sub.label\n\t\t},\n\n\t\t/**\n\t\t * The element id of a text input: the declared one, or one unique to\n\t\t * this form.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {string}\n\t\t */\n\t\tinputId(field) {\n\t\t\treturn field.input.id || `cn-transition-input-${this.uid}-${field.key}`\n\t\t},\n\n\t\t/**\n\t\t * The native input type of a one-line input.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {string}\n\t\t */\n\t\ttextType(field) {\n\t\t\treturn field.widget === 'number' || field.widget === 'date' ? field.widget : 'text'\n\t\t},\n\n\t\t/**\n\t\t * The option a picker currently holds, or null.\n\t\t *\n\t\t * @param {object} field The select descriptor.\n\t\t * @return {{value: string, label: string}|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\tconst value = this.modelValue[field.key]\n\t\t\treturn field.options.find((option) => option.value === String(value ?? '')) || null\n\t\t},\n\n\t\t/**\n\t\t * Ids a reference picker must not offer: the current record, when the\n\t\t * input's `picker.excludeSelf` says so.\n\t\t *\n\t\t * @param {object} field The reference descriptor.\n\t\t * @return {string[]}\n\t\t */\n\t\texcludedIds(field) {\n\t\t\tif (!field.picker || field.picker.excludeSelf !== true || !this.currentObject) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst self = this.currentObject\n\t\t\treturn [self.id, self.uuid, self['@self'] && self['@self'].id].filter((id) => id !== undefined && id !== null).map(String)\n\t\t},\n\n\t\t/**\n\t\t * Whether the refusal named this field.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @return {boolean}\n\t\t */\n\t\tisRefused(key) {\n\t\t\treturn this.refusedKeys.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * What to say beside a refused field. An empty one is a missing input;\n\t\t * a filled one was refused for what is in it. The kind is decided here\n\t\t * rather than read out of the refusal's sentence, which may be reworded.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {string}\n\t\t */\n\t\trefusalFor(field) {\n\t\t\treturn isInputFilled(field, this.modelValue)\n\t\t\t\t? t('nextcloud-vue', 'This value was not accepted.')\n\t\t\t\t: t('nextcloud-vue', 'This field is required.')\n\t\t},\n\n\t\t/**\n\t\t * Whether a refusal named `<input>.<sub>`.\n\t\t *\n\t\t * @param {string} key The input key.\n\t\t * @param {string} sub The sub-field key.\n\t\t * @return {boolean}\n\t\t */\n\t\tisSubRefused(key, sub) {\n\t\t\treturn this.refusedKeys.includes(`${key}.${sub}`)\n\t\t},\n\n\t\t/**\n\t\t * A typed sub-value.\n\t\t *\n\t\t * @param {string} key The input key.\n\t\t * @param {string} sub The sub-field key.\n\t\t * @return {unknown}\n\t\t */\n\t\tsubValue(key, sub) {\n\t\t\tconst group = this.modelValue[key]\n\t\t\treturn group && typeof group === 'object' ? group[sub] : undefined\n\t\t},\n\n\t\t/**\n\t\t * Store one sub-value, keeping the rest of the object input's values.\n\t\t *\n\t\t * @param {string} key The input key.\n\t\t * @param {string} sub The sub-field key.\n\t\t * @param {unknown} value The new value.\n\t\t */\n\t\tsetSubValue(key, sub, value) {\n\t\t\tconst group = this.latest[key] && typeof this.latest[key] === 'object' ? this.latest[key] : {}\n\t\t\tthis.setValue(key, { ...group, [sub]: value })\n\t\t},\n\n\t\t/**\n\t\t * Store one input'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 */\n\t\tsetValue(key, value) {\n\t\t\tthis.latest = { ...this.latest, [key]: value }\n\t\t\t/**\n\t\t\t * @event update:modelValue The values with this one changed.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('update:modelValue', this.latest)\n\t\t},\n\n\t\t/**\n\t\t * Put a chosen template's text into the input it fills, unless that\n\t\t * input already holds text.\n\t\t *\n\t\t * @param {object} field The template descriptor.\n\t\t * @param {object|null} option The chosen template.\n\t\t */\n\t\tapplyTemplate(field, option) {\n\t\t\tconst next = applyTemplateText(this.latest, field.input.applyTo, option)\n\t\t\tif (next !== this.latest) {\n\t\t\t\tthis.latest = next\n\t\t\t\t/** @event update:modelValue The values with the template's text filled in. */\n\t\t\t\tthis.$emit('update:modelValue', next)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-transition-input-fields {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-transition-input-fields--columns {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(2, minmax(0, 1fr));\n\talign-items: start;\n}\n\n.cn-transition-input-fields--columns .cn-transition-input-fields__wide {\n\tgrid-column: 1 / -1;\n}\n\n.cn-transition-input__field,\n.cn-transition-input-fields__preview {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-transition-input__label {\n\tdisplay: block;\n\tmargin-bottom: 4px;\n}\n\n.cn-transition-input-fields__label {\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-transition-input-fields__muted {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-transition-input__field-error {\n\tmargin: 4px 0 0;\n\tfont-size: 0.9em;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-transition-input__field--refused {\n\tborder-inline-start: 2px solid var(--color-error);\n\tpadding-inline-start: 8px;\n}\n\n.cn-transition-input__object {\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 8px 12px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n}\n\n.cn-transition-input-fields__card {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 14px;\n\tpadding: 14px 16px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-transition-input-fields__card-icon {\n\tcolor: var(--color-primary-element);\n}\n\n.cn-transition-input-fields__card-text {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-transition-input-fields__badge {\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tbackground: var(--color-success-light, var(--color-background-dark));\n\tcolor: var(--color-success-text);\n\tfont-size: 13px;\n\tfont-weight: 600;\n}\n\n.cn-transition-input-fields__section-title {\n\tmargin: 0 0 8px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-transition-input-fields__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(2, minmax(0, 1fr));\n\tgap: 0 24px;\n\tmargin: 0;\n}\n\n.cn-transition-input-fields__facts > div {\n\tpadding: 10px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-transition-input-fields__facts dt,\n.cn-transition-input-fields__facts dd {\n\tdisplay: block;\n\twidth: auto;\n\tpadding: 0;\n\ttext-align: start;\n\twhite-space: normal;\n}\n\n.cn-transition-input-fields__facts dt {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-transition-input-fields__facts dd {\n\tmargin: 2px 0 0;\n\tfont-size: 15px;\n\tfont-weight: 600;\n}\n\n.cn-transition-input-fields__notice {\n\tdisplay: flex;\n\tgap: 12px;\n\talign-items: flex-start;\n\tpadding: 14px 16px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--color-primary-element-light);\n\tcolor: var(--color-main-text);\n}\n\n.cn-transition-input-fields__notice--warning {\n\tbackground: var(--color-warning-element-light, var(--color-background-dark));\n}\n\n.cn-transition-input-fields__notice--error {\n\tbackground: var(--color-error-element-light, var(--color-background-dark));\n}\n\n.cn-transition-input-fields__notice-icon {\n\tflex: none;\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n}\n\n.cn-transition-input-fields__notice-text {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-transition-input-fields__notice-text strong {\n\tcolor: var(--color-primary-element-light-text, var(--color-main-text));\n}\n</style>\n"],"names":["CnIcon","CnResourceSelect","CnTemplateSelect","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnTransitionInputFields.vue2.js","sources":["../../../../src/components/CnTransitionInputFields/CnTransitionInputFields.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div\n\t\tclass=\"cn-transition-input-fields\"\n\t\t:class=\"{ 'cn-transition-input-fields--columns': columns === 2 }\">\n\t\t<template v-if=\"summaryShown\">\n\t\t\t<div v-if=\"summary.card\"\n\t\t\t\tclass=\"cn-transition-input-fields__card cn-transition-input-fields__wide\"\n\t\t\t\t:data-testid=\"summaryTestid('card')\">\n\t\t\t\t<component :is=\"summary.card.icon\"\n\t\t\t\t\tv-if=\"summary.card.icon\"\n\t\t\t\t\t:size=\"20\"\n\t\t\t\t\tclass=\"cn-transition-input-fields__card-icon\" />\n\t\t\t\t<span class=\"cn-transition-input-fields__card-text\">\n\t\t\t\t\t<strong>{{ contextText(summary.card.title) }}</strong>\n\t\t\t\t\t<span class=\"cn-transition-input-fields__muted\">{{ cardMeta }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span v-if=\"contextText(summary.card.badge)\" class=\"cn-transition-input-fields__badge\">\n\t\t\t\t\t{{ contextText(summary.card.badge) }}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<section v-if=\"Array.isArray(summary.facts) && summary.facts.length > 0\"\n\t\t\t\tclass=\"cn-transition-input-fields__wide\"\n\t\t\t\t:data-testid=\"summaryTestid('facts')\">\n\t\t\t\t<h3 v-if=\"summary.title\" class=\"cn-transition-input-fields__section-title\">\n\t\t\t\t\t{{ summary.title }}\n\t\t\t\t</h3>\n\t\t\t\t<dl class=\"cn-transition-input-fields__facts\">\n\t\t\t\t\t<div v-for=\"fact in summary.facts\" :key=\"fact.path || fact.label\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ contextText(fact.path) || fact.fallback || '' }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t</section>\n\t\t</template>\n\n\t\t<template v-for=\"field in shownFields\" :key=\"field.key\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-transition-input__field\"\n\t\t\t\t:class=\"{\n\t\t\t\t\t'cn-transition-input__field--refused': isRefused(field.key),\n\t\t\t\t\t'cn-transition-input-fields__wide': field.input.wide === true,\n\t\t\t\t}\"\n\t\t\t\t:data-testid=\"`cn-transition-input-${field.key}`\">\n\t\t\t\t<!-- A template fills another input; it is never sent itself. -->\n\t\t\t\t<CnTemplateSelect v-if=\"field.widget === 'template'\"\n\t\t\t\t\t:url=\"field.input.url || ''\"\n\t\t\t\t\t:schema=\"field.input.schema || ''\"\n\t\t\t\t\t:params=\"field.input.params || {}\"\n\t\t\t\t\t:mapOption=\"field.input.mapOption\"\n\t\t\t\t\t:inputLabel=\"field.label\"\n\t\t\t\t\t:placeholder=\"field.input.placeholder || ''\"\n\t\t\t\t\t:data-testid=\"field.input.testid || `cn-transition-input-template-${field.key}`\"\n\t\t\t\t\t@apply=\"applyTemplate(field, $event)\" />\n\n\t\t\t\t<!-- An input that declares its choices is picked from them. -->\n\t\t\t\t<NcSelect v-else-if=\"field.widget === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"field.options\"\n\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t:inputLabel=\"labelOf(field)\"\n\t\t\t\t\t:clearable=\"!field.required\"\n\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t:data-testid=\"field.input.testid || `cn-transition-input-select-${field.key}`\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event && $event.value !== undefined ? $event.value : '')\" />\n\n\t\t\t\t<!-- An input that points at another record is picked, never typed. -->\n\t\t\t\t<div v-else-if=\"field.widget === 'reference'\" class=\"cn-transition-input__reference\">\n\t\t\t\t\t<label class=\"cn-transition-input__label\">{{ labelOf(field) }}</label>\n\t\t\t\t\t<CnResourceSelect\n\t\t\t\t\t\t:register=\"field.referenceRegister\"\n\t\t\t\t\t\t:schema=\"String(field.reference.schema)\"\n\t\t\t\t\t\t:labelField=\"(field.picker && field.picker.labelField) || field.reference.labelField || 'name'\"\n\t\t\t\t\t\t:filter=\"(field.picker && field.picker.filter) || {}\"\n\t\t\t\t\t\t:exclude=\"excludedIds(field)\"\n\t\t\t\t\t\t:allowCreate=\"false\"\n\t\t\t\t\t\t:preload=\"true\"\n\t\t\t\t\t\t:inputLabel=\"labelOf(field)\"\n\t\t\t\t\t\t:modelValue=\"String(modelValue[field.key] ?? '')\"\n\t\t\t\t\t\t:data-testid=\"`cn-transition-input-picker-${field.key}`\"\n\t\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\t\t\t\t</div>\n\n\t\t\t\t<!-- An object input: its sub-properties, one field at a time. -->\n\t\t\t\t<fieldset v-else-if=\"field.widget === 'object'\" class=\"cn-transition-input__object\">\n\t\t\t\t\t<legend>{{ labelOf(field) }}</legend>\n\t\t\t\t\t<div v-for=\"sub in field.subFields\"\n\t\t\t\t\t\t:key=\"sub.key\"\n\t\t\t\t\t\tclass=\"cn-transition-input__subfield\"\n\t\t\t\t\t\t:data-testid=\"`cn-transition-input-${field.key}-${sub.key}`\">\n\t\t\t\t\t\t<NcTextArea v-if=\"sub.widget === 'textarea'\"\n\t\t\t\t\t\t\t:modelValue=\"String(subValue(field.key, sub.key) ?? '')\"\n\t\t\t\t\t\t\t:label=\"subLabel(sub)\"\n\t\t\t\t\t\t\trows=\"3\"\n\t\t\t\t\t\t\t@update:modelValue=\"setSubValue(field.key, sub.key, $event)\" />\n\t\t\t\t\t\t<NcCheckboxRadioSwitch v-else-if=\"sub.widget === 'checkbox'\"\n\t\t\t\t\t\t\t:modelValue=\"subValue(field.key, sub.key) === true\"\n\t\t\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t\t\t@update:modelValue=\"setSubValue(field.key, sub.key, $event === true)\">\n\t\t\t\t\t\t\t{{ subLabel(sub) }}\n\t\t\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t\t\t<NcTextField v-else\n\t\t\t\t\t\t\t:modelValue=\"String(subValue(field.key, sub.key) ?? '')\"\n\t\t\t\t\t\t\t:type=\"sub.widget === 'number' ? 'number' : 'text'\"\n\t\t\t\t\t\t\t:label=\"subLabel(sub)\"\n\t\t\t\t\t\t\t@update:modelValue=\"setSubValue(field.key, sub.key, $event)\" />\n\t\t\t\t\t\t<p v-if=\"isSubRefused(field.key, sub.key)\"\n\t\t\t\t\t\t\tclass=\"cn-transition-input__field-error\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-transition-input-error-${field.key}.${sub.key}`\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'This value was not accepted.') }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</fieldset>\n\n\t\t\t\t<NcCheckboxRadioSwitch v-else-if=\"field.widget === 'checkbox'\"\n\t\t\t\t\t:modelValue=\"modelValue[field.key] === true\"\n\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event === true)\">\n\t\t\t\t\t{{ labelOf(field) }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<template v-else>\n\t\t\t\t\t<!-- A label drawn above the field, as a form board draws it,\n\t\t\t\t\t is tied to the field by its id. -->\n\t\t\t\t\t<label v-if=\"field.input.labelOutside === true\"\n\t\t\t\t\t\tclass=\"cn-transition-input-fields__label\"\n\t\t\t\t\t\t:for=\"inputId(field)\">{{ labelOf(field) }}</label>\n\t\t\t\t\t<NcTextArea v-if=\"field.widget === 'textarea'\"\n\t\t\t\t\t\t:id=\"inputId(field)\"\n\t\t\t\t\t\t:modelValue=\"String(modelValue[field.key] ?? '')\"\n\t\t\t\t\t\t:label=\"labelOf(field)\"\n\t\t\t\t\t\t:labelOutside=\"field.input.labelOutside === true\"\n\t\t\t\t\t\t:helperText=\"field.description || ''\"\n\t\t\t\t\t\t:rows=\"field.input.rows || 4\"\n\t\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t\tresize=\"vertical\"\n\t\t\t\t\t\t:data-testid=\"field.input.testid || null\"\n\t\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\t\t\t\t\t<NcTextField v-else\n\t\t\t\t\t\t:id=\"inputId(field)\"\n\t\t\t\t\t\t:modelValue=\"String(modelValue[field.key] ?? '')\"\n\t\t\t\t\t\t:type=\"textType(field)\"\n\t\t\t\t\t\t:label=\"labelOf(field)\"\n\t\t\t\t\t\t:labelOutside=\"field.input.labelOutside === true\"\n\t\t\t\t\t\t:helperText=\"field.description || ''\"\n\t\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t\t:data-testid=\"field.input.testid || null\"\n\t\t\t\t\t\t@update:modelValue=\"setValue(field.key, $event)\" />\n\t\t\t\t\t<span v-if=\"field.input.note\" class=\"cn-transition-input-fields__muted\">\n\t\t\t\t\t\t{{ field.input.note }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\n\t\t\t\t<p v-if=\"isRefused(field.key)\"\n\t\t\t\t\tclass=\"cn-transition-input__field-error\"\n\t\t\t\t\t:data-testid=\"`cn-transition-input-error-${field.key}`\">\n\t\t\t\t\t{{ refusalFor(field) }}\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t<!-- A days input set as a date (`dateField`): today plus the days,\n\t\t\t and a chosen date sets the days. -->\n\t\t\t<div v-if=\"field.input.dateField\"\n\t\t\t\t:key=\"`${field.key}-date`\"\n\t\t\t\tclass=\"cn-transition-input-fields__field cn-transition-input-fields__wide\"\n\t\t\t\t:data-testid=\"field.input.dateField.testid || `cn-transition-input-date-${field.key}`\">\n\t\t\t\t<label class=\"cn-transition-input-fields__label\"\n\t\t\t\t\t:for=\"`${inputId(field)}-date`\">{{ dateFieldLabel(field) }}</label>\n\t\t\t\t<NcTextField :id=\"`${inputId(field)}-date`\"\n\t\t\t\t\t:modelValue=\"field.dateValue\"\n\t\t\t\t\ttype=\"date\"\n\t\t\t\t\t:label=\"dateFieldLabel(field)\"\n\t\t\t\t\t:labelOutside=\"true\"\n\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t@update:modelValue=\"setValue(field.key, daysUntilIso($event))\" />\n\t\t\t\t<span v-if=\"field.input.dateField.note\" class=\"cn-transition-input-fields__muted\">\n\t\t\t\t\t{{ field.input.dateField.note }}\n\t\t\t\t</span>\n\t\t\t</div>\n\n\t\t\t<!-- What the input means once filled in, such as the day a paused\n\t\t\t term comes back. Shown only once it can be worked out. -->\n\t\t\t<div v-if=\"field.previewText\"\n\t\t\t\t:key=\"`${field.key}-preview`\"\n\t\t\t\tclass=\"cn-transition-input-fields__preview cn-transition-input-fields__wide\"\n\t\t\t\t:data-testid=\"field.input.preview.testid || `cn-transition-input-preview-${field.key}`\">\n\t\t\t\t<span class=\"cn-transition-input-fields__label\">{{ field.input.preview.label }}</span>\n\t\t\t\t<span :data-testid=\"`${field.input.preview.testid || `cn-transition-input-preview-${field.key}`}-value`\">{{ field.previewText }}</span>\n\t\t\t\t<span v-if=\"field.input.preview.note\" class=\"cn-transition-input-fields__muted\">\n\t\t\t\t\t{{ field.input.preview.note }}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- A tinted box with the act's own icon (DqArchiveren \"Daarna alleen\n\t\t lezen\", a lock), drawn here rather than as a note card: a note\n\t\t card on the tinted card of a chosen act disappears into it. -->\n\t\t<div v-if=\"notice && notice.text\"\n\t\t\tclass=\"cn-transition-input-fields__notice cn-transition-input-fields__wide\"\n\t\t\t:class=\"`cn-transition-input-fields__notice--${notice.type || 'info'}`\"\n\t\t\trole=\"note\"\n\t\t\tdata-testid=\"cn-transition-input-fields-notice\">\n\t\t\t<CnIcon :name=\"notice.icon || 'InformationOutline'\" :size=\"20\" class=\"cn-transition-input-fields__notice-icon\" />\n\t\t\t<span class=\"cn-transition-input-fields__notice-text\">\n\t\t\t\t<strong v-if=\"notice.heading\">{{ notice.heading }}</strong>\n\t\t\t\t<span>{{ notice.text }}</span>\n\t\t\t</span>\n\t\t</div>\n\n\t\t<!-- A key the refusal names that this form never offered is not a\n\t\t field anybody can correct here. Saying so beats saying nothing:\n\t\t the request sent something the action does not accept. -->\n\t\t<p v-for=\"key in unofferedRefusals\"\n\t\t\t:key=\"`unoffered-${key}`\"\n\t\t\tclass=\"cn-transition-input__field-error\"\n\t\t\t:data-testid=\"`cn-transition-input-error-${key}`\">\n\t\t\t{{ t('nextcloud-vue', 'This action does not accept the field \"{field}\".', { field: key }) }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcCheckboxRadioSwitch, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'\nimport CnTemplateSelect from '../CnTemplateSelect/CnTemplateSelect.vue'\nimport { readPath } from '../../utils/readPath.js'\nimport { applyTemplateText, daysUntilIso, isInputFilled, resolveInputFields } from '../../utils/transitionInputs.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\nlet uid = 0\n\n/**\n * CnTransitionInputFields — the declared inputs of a transition or an act,\n * drawn wherever the host puts them.\n *\n * CnTransitionInputDialog draws them in their own dialog. A host that lists\n * several acts and opens the chosen one's inputs in place (under its card)\n * draws them with this component, so both read the same declarations and\n * decide the same way. The values are a `v-model` object keyed by field;\n * whether they are complete is `inputFieldsComplete(resolveInputFields(...))`.\n *\n * Next to the schema-resolved widgets an input may declare `widget` (`text`,\n * `textarea`, `number`, `date`, `select`, `template`), `optionsSource` (a dot\n * path into `context`), `other`, `helperFrom`, `preview`, `default` and\n * presentation keys; see `resolveInputFields` in `utils/transitionInputs.js`.\n *\n * ```vue\n * <CnTransitionInputFields\n * v-model=\"values\"\n * :inputs=\"act.inputs\"\n * :context=\"{ pauseReasons }\"\n * :columns=\"2\" />\n * ```\n *\n * @spec openspec/changes/dq-abstract-forms/specs/transition-inputs/spec.md\n */\nexport default {\n\tname: 'CnTransitionInputFields',\n\n\tcomponents: {\n\t\tCnIcon,\n\t\tCnResourceSelect,\n\t\tCnTemplateSelect,\n\t\tNcCheckboxRadioSwitch,\n\t\tNcSelect,\n\t\tNcTextArea,\n\t\tNcTextField,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The declared inputs: `[{ field, required, label, widget, options,\n\t\t * optionsSource, optionValue, optionLabel, other, helperFrom, preview,\n\t\t * default, description, note, labelOutside, id, wide, testid, rows,\n\t\t * placeholder }]`, plus for a `template` input `url`, `schema`,\n\t\t * `params`, `mapOption` and `applyTo`.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\tinputs: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The values, keyed by field.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tmodelValue: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The object's JSON Schema, which labels and types the inputs that\n\t\t * declare no widget of their own.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The record the action runs on: `picker.excludeSelf` leaves its id\n\t\t * out, and an object input is merged over its present value.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tcurrentObject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Register slug the reference pickers look in when the schema property\n\t\t * names none of its own.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * What `optionsSource` and the summary paths read, such as the case\n\t\t * type's choices and the record shown in the summary.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontext: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The input keys a refusal named. Each one this form offers is marked\n\t\t * on its own field; each one it does not is reported as a key the\n\t\t * action does not accept.\n\t\t *\n\t\t * @type {string[]}\n\t\t */\n\t\tfieldErrors: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Whether the inputs are locked, such as while the action posts.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * 1 stacks the inputs; 2 sets them side by side, with `wide` inputs,\n\t\t * previews, the summary and the notice across both columns.\n\t\t *\n\t\t * @type {number}\n\t\t */\n\t\tcolumns: {\n\t\t\ttype: Number,\n\t\t\tdefault: 1,\n\t\t},\n\n\t\t/**\n\t\t * Whether a required input's label carries a ` *` marker.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tmarkRequired: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Whether an input that is not required says so, with ` (optional)`\n\t\t * after its label, as the boards mark them (DqVolgendeStap \"Dagen die\n\t\t * de aanvrager krijgt (niet verplicht)\").\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tmarkOptional: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * A read-only block above the inputs about what the action works on:\n\t\t * `{ when, card: { icon, title, meta: [], badge }, title, facts:\n\t\t * [{ label, path, fallback }], testid }`. `when`, `title`, `meta`,\n\t\t * `badge` and each fact's `path` are dot paths into `context`; the\n\t\t * block is shown only when `when` resolves.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tsummary: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * A note under the inputs: `{ type, heading, text, icon? }`. `type` is\n\t\t * info (the default), warning or error; `icon` names a CnIcon.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tnotice: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['update:modelValue'],\n\n\tdata() {\n\t\tuid += 1\n\t\treturn {\n\t\t\tuid,\n\t\t\t// The values with every change made since the host last passed\n\t\t\t// them in, so two changes in one tick both survive.\n\t\t\tlatest: { ...this.modelValue },\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Every declared input, resolved against the schema and the context. */\n\t\tfields() {\n\t\t\treturn resolveInputFields(this.inputs, {\n\t\t\t\tschema: this.schema,\n\t\t\t\tregister: this.register,\n\t\t\t\tcontext: this.context,\n\t\t\t\tvalues: this.modelValue,\n\t\t\t})\n\t\t},\n\n\t\t/** The inputs that are asked now. */\n\t\tshownFields() {\n\t\t\treturn this.fields.filter((field) => !field.hidden)\n\t\t},\n\n\t\t/** Whether the summary block shows. */\n\t\tsummaryShown() {\n\t\t\tif (!this.summary || typeof this.summary !== 'object') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn !this.summary.when || Boolean(readPath(this.context, this.summary.when))\n\t\t},\n\n\t\t/** The summary card's second line. */\n\t\tcardMeta() {\n\t\t\tconst meta = Array.isArray(this.summary?.card?.meta) ? this.summary.card.meta : []\n\t\t\treturn meta.map((path) => this.contextText(path)).filter(Boolean).join(' · ')\n\t\t},\n\n\t\t/** The refusal's field keys, normalised to strings. */\n\t\trefusedKeys() {\n\t\t\tif (!Array.isArray(this.fieldErrors)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.fieldErrors.filter((key) => typeof key === 'string' && key !== '')\n\t\t},\n\n\t\t/** The refused keys this form offers no field for. */\n\t\tunofferedRefusals() {\n\t\t\tconst offered = this.fields.map((field) => field.key)\n\t\t\treturn this.refusedKeys.filter((key) => !offered.includes(key))\n\t\t},\n\t},\n\n\twatch: {\n\t\tmodelValue(values) {\n\t\t\tthis.latest = { ...values }\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A context value as text, '' when absent.\n\t\t *\n\t\t * @param {string} path Dot path into the context.\n\t\t * @return {string}\n\t\t */\n\t\tcontextText(path) {\n\t\t\tif (!path) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst value = readPath(this.context, path)\n\t\t\treturn value === null || value === undefined ? '' : String(value)\n\t\t},\n\n\t\t/**\n\t\t * The test id of one part of the summary.\n\t\t *\n\t\t * @param {string} part `card` or `facts`.\n\t\t * @return {string}\n\t\t */\n\t\tsummaryTestid(part) {\n\t\t\treturn `${this.summary.testid || 'cn-transition-input-summary'}-${part}`\n\t\t},\n\n\t\t/**\n\t\t * The field's label, with the required or the optional marker when asked for.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {string}\n\t\t */\n\t\tlabelOf(field) {\n\t\t\tif (this.markRequired && field.required) {\n\t\t\t\treturn `${field.label} *`\n\t\t\t}\n\t\t\treturn this.markOptional && !field.required ? `${field.label} (${t('nextcloud-vue', 'optional')})` : field.label\n\t\t},\n\n\t\t/**\n\t\t * The label of a days input's date field (`dateField`), with the\n\t\t * optional marker when asked for: the date is never required.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {string}\n\t\t */\n\t\tdateFieldLabel(field) {\n\t\t\tconst label = String(field.input.dateField.label || '')\n\t\t\treturn this.markOptional ? `${label} (${t('nextcloud-vue', 'optional')})` : label\n\t\t},\n\n\t\t/**\n\t\t * The days a chosen date stands for.\n\t\t *\n\t\t * @param {string} iso The date input's value.\n\t\t * @return {string} The days, '' without a date.\n\t\t */\n\t\tdaysUntilIso(iso) {\n\t\t\treturn daysUntilIso(iso)\n\t\t},\n\n\t\t/**\n\t\t * Label of a sub-field, with the required marker.\n\t\t *\n\t\t * @param {object} sub The sub-field descriptor.\n\t\t * @return {string}\n\t\t */\n\t\tsubLabel(sub) {\n\t\t\treturn this.markRequired && sub.required ? `${sub.label} *` : sub.label\n\t\t},\n\n\t\t/**\n\t\t * The element id of a text input: the declared one, or one unique to\n\t\t * this form.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {string}\n\t\t */\n\t\tinputId(field) {\n\t\t\treturn field.input.id || `cn-transition-input-${this.uid}-${field.key}`\n\t\t},\n\n\t\t/**\n\t\t * The native input type of a one-line input.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {string}\n\t\t */\n\t\ttextType(field) {\n\t\t\treturn field.widget === 'number' || field.widget === 'date' ? field.widget : 'text'\n\t\t},\n\n\t\t/**\n\t\t * The option a picker currently holds, or null.\n\t\t *\n\t\t * @param {object} field The select descriptor.\n\t\t * @return {{value: string, label: string}|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\tconst value = this.modelValue[field.key]\n\t\t\treturn field.options.find((option) => option.value === String(value ?? '')) || null\n\t\t},\n\n\t\t/**\n\t\t * Ids a reference picker must not offer: the current record, when the\n\t\t * input's `picker.excludeSelf` says so.\n\t\t *\n\t\t * @param {object} field The reference descriptor.\n\t\t * @return {string[]}\n\t\t */\n\t\texcludedIds(field) {\n\t\t\tif (!field.picker || field.picker.excludeSelf !== true || !this.currentObject) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst self = this.currentObject\n\t\t\treturn [self.id, self.uuid, self['@self'] && self['@self'].id].filter((id) => id !== undefined && id !== null).map(String)\n\t\t},\n\n\t\t/**\n\t\t * Whether the refusal named this field.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @return {boolean}\n\t\t */\n\t\tisRefused(key) {\n\t\t\treturn this.refusedKeys.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * What to say beside a refused field. An empty one is a missing input;\n\t\t * a filled one was refused for what is in it. The kind is decided here\n\t\t * rather than read out of the refusal's sentence, which may be reworded.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {string}\n\t\t */\n\t\trefusalFor(field) {\n\t\t\treturn isInputFilled(field, this.modelValue)\n\t\t\t\t? t('nextcloud-vue', 'This value was not accepted.')\n\t\t\t\t: t('nextcloud-vue', 'This field is required.')\n\t\t},\n\n\t\t/**\n\t\t * Whether a refusal named `<input>.<sub>`.\n\t\t *\n\t\t * @param {string} key The input key.\n\t\t * @param {string} sub The sub-field key.\n\t\t * @return {boolean}\n\t\t */\n\t\tisSubRefused(key, sub) {\n\t\t\treturn this.refusedKeys.includes(`${key}.${sub}`)\n\t\t},\n\n\t\t/**\n\t\t * A typed sub-value.\n\t\t *\n\t\t * @param {string} key The input key.\n\t\t * @param {string} sub The sub-field key.\n\t\t * @return {unknown}\n\t\t */\n\t\tsubValue(key, sub) {\n\t\t\tconst group = this.modelValue[key]\n\t\t\treturn group && typeof group === 'object' ? group[sub] : undefined\n\t\t},\n\n\t\t/**\n\t\t * Store one sub-value, keeping the rest of the object input's values.\n\t\t *\n\t\t * @param {string} key The input key.\n\t\t * @param {string} sub The sub-field key.\n\t\t * @param {unknown} value The new value.\n\t\t */\n\t\tsetSubValue(key, sub, value) {\n\t\t\tconst group = this.latest[key] && typeof this.latest[key] === 'object' ? this.latest[key] : {}\n\t\t\tthis.setValue(key, { ...group, [sub]: value })\n\t\t},\n\n\t\t/**\n\t\t * Store one input'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 */\n\t\tsetValue(key, value) {\n\t\t\tthis.latest = { ...this.latest, [key]: value }\n\t\t\t/**\n\t\t\t * @event update:modelValue The values with this one changed.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('update:modelValue', this.latest)\n\t\t},\n\n\t\t/**\n\t\t * Put a chosen template's text into the input it fills, unless that\n\t\t * input already holds text.\n\t\t *\n\t\t * @param {object} field The template descriptor.\n\t\t * @param {object|null} option The chosen template.\n\t\t */\n\t\tapplyTemplate(field, option) {\n\t\t\tconst next = applyTemplateText(this.latest, field.input.applyTo, option)\n\t\t\tif (next !== this.latest) {\n\t\t\t\tthis.latest = next\n\t\t\t\t/** @event update:modelValue The values with the template's text filled in. */\n\t\t\t\tthis.$emit('update:modelValue', next)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-transition-input-fields {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-transition-input-fields--columns {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(2, minmax(0, 1fr));\n\talign-items: start;\n}\n\n.cn-transition-input-fields--columns .cn-transition-input-fields__wide {\n\tgrid-column: 1 / -1;\n}\n\n.cn-transition-input__field,\n.cn-transition-input-fields__preview {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-transition-input__label {\n\tdisplay: block;\n\tmargin-bottom: 4px;\n}\n\n.cn-transition-input-fields__label {\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-transition-input-fields__muted {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-transition-input__field-error {\n\tmargin: 4px 0 0;\n\tfont-size: 0.9em;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-transition-input__field--refused {\n\tborder-inline-start: 2px solid var(--color-error);\n\tpadding-inline-start: 8px;\n}\n\n.cn-transition-input__object {\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 8px 12px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n}\n\n.cn-transition-input-fields__card {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 14px;\n\tpadding: 14px 16px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-transition-input-fields__card-icon {\n\tcolor: var(--color-primary-element);\n}\n\n.cn-transition-input-fields__card-text {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-transition-input-fields__badge {\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tbackground: var(--color-success-light, var(--color-background-dark));\n\tcolor: var(--color-success-text);\n\tfont-size: 13px;\n\tfont-weight: 600;\n}\n\n.cn-transition-input-fields__section-title {\n\tmargin: 0 0 8px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-transition-input-fields__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(2, minmax(0, 1fr));\n\tgap: 0 24px;\n\tmargin: 0;\n}\n\n.cn-transition-input-fields__facts > div {\n\tpadding: 10px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-transition-input-fields__facts dt,\n.cn-transition-input-fields__facts dd {\n\tdisplay: block;\n\twidth: auto;\n\tpadding: 0;\n\ttext-align: start;\n\twhite-space: normal;\n}\n\n.cn-transition-input-fields__facts dt {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-transition-input-fields__facts dd {\n\tmargin: 2px 0 0;\n\tfont-size: 15px;\n\tfont-weight: 600;\n}\n\n.cn-transition-input-fields__notice {\n\tdisplay: flex;\n\tgap: 12px;\n\talign-items: flex-start;\n\tpadding: 14px 16px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--color-primary-element-light);\n\tcolor: var(--color-main-text);\n}\n\n.cn-transition-input-fields__notice--warning {\n\tbackground: var(--color-warning-element-light, var(--color-background-dark));\n}\n\n.cn-transition-input-fields__notice--error {\n\tbackground: var(--color-error-element-light, var(--color-background-dark));\n}\n\n.cn-transition-input-fields__notice-icon {\n\tflex: none;\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n}\n\n.cn-transition-input-fields__notice-text {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-transition-input-fields__notice-text strong {\n\tcolor: var(--color-primary-element-light-text, var(--color-main-text));\n}\n</style>\n"],"names":["CnIcon","CnResourceSelect","CnTemplateSelect","t"],"mappings":"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"}
|
|
@@ -32,15 +32,21 @@ const _hoisted_15 = {
|
|
|
32
32
|
};
|
|
33
33
|
const _hoisted_16 = ["data-testid"];
|
|
34
34
|
const _hoisted_17 = ["data-testid"];
|
|
35
|
-
const _hoisted_18 =
|
|
36
|
-
const _hoisted_19 =
|
|
37
|
-
const _hoisted_20 = {
|
|
35
|
+
const _hoisted_18 = ["for"];
|
|
36
|
+
const _hoisted_19 = {
|
|
38
37
|
key: 0,
|
|
39
38
|
class: "cn-transition-input-fields__muted"
|
|
40
39
|
};
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
40
|
+
const _hoisted_20 = ["data-testid"];
|
|
41
|
+
const _hoisted_21 = { class: "cn-transition-input-fields__label" };
|
|
42
|
+
const _hoisted_22 = ["data-testid"];
|
|
43
|
+
const _hoisted_23 = {
|
|
44
|
+
key: 0,
|
|
45
|
+
class: "cn-transition-input-fields__muted"
|
|
46
|
+
};
|
|
47
|
+
const _hoisted_24 = { class: "cn-transition-input-fields__notice-text" };
|
|
48
|
+
const _hoisted_25 = { key: 0 };
|
|
49
|
+
const _hoisted_26 = ["data-testid"];
|
|
44
50
|
|
|
45
51
|
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
46
52
|
const _component_CnTemplateSelect = resolveComponent("CnTemplateSelect");
|
|
@@ -258,20 +264,44 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
258
264
|
}, toDisplayString($options.refusalFor(field)), 9 /* TEXT, PROPS */, _hoisted_16))
|
|
259
265
|
: createCommentVNode("v-if", true)
|
|
260
266
|
], 10 /* CLASS, PROPS */, _hoisted_8),
|
|
267
|
+
(field.input.dateField)
|
|
268
|
+
? (openBlock(), createElementBlock("div", {
|
|
269
|
+
key: `${field.key}-date`,
|
|
270
|
+
class: "cn-transition-input-fields__field cn-transition-input-fields__wide",
|
|
271
|
+
"data-testid": field.input.dateField.testid || `cn-transition-input-date-${field.key}`
|
|
272
|
+
}, [
|
|
273
|
+
createElementVNode("label", {
|
|
274
|
+
class: "cn-transition-input-fields__label",
|
|
275
|
+
for: `${$options.inputId(field)}-date`
|
|
276
|
+
}, toDisplayString($options.dateFieldLabel(field)), 9 /* TEXT, PROPS */, _hoisted_18),
|
|
277
|
+
createVNode(_component_NcTextField, {
|
|
278
|
+
id: `${$options.inputId(field)}-date`,
|
|
279
|
+
modelValue: field.dateValue,
|
|
280
|
+
type: "date",
|
|
281
|
+
label: $options.dateFieldLabel(field),
|
|
282
|
+
labelOutside: true,
|
|
283
|
+
disabled: $props.disabled,
|
|
284
|
+
"onUpdate:modelValue": $event => ($options.setValue(field.key, $options.daysUntilIso($event)))
|
|
285
|
+
}, null, 8 /* PROPS */, ["id", "modelValue", "label", "disabled", "onUpdate:modelValue"]),
|
|
286
|
+
(field.input.dateField.note)
|
|
287
|
+
? (openBlock(), createElementBlock("span", _hoisted_19, toDisplayString(field.input.dateField.note), 1 /* TEXT */))
|
|
288
|
+
: createCommentVNode("v-if", true)
|
|
289
|
+
], 8 /* PROPS */, _hoisted_17))
|
|
290
|
+
: createCommentVNode("v-if", true),
|
|
261
291
|
(field.previewText)
|
|
262
292
|
? (openBlock(), createElementBlock("div", {
|
|
263
293
|
key: `${field.key}-preview`,
|
|
264
294
|
class: "cn-transition-input-fields__preview cn-transition-input-fields__wide",
|
|
265
295
|
"data-testid": field.input.preview.testid || `cn-transition-input-preview-${field.key}`
|
|
266
296
|
}, [
|
|
267
|
-
createElementVNode("span",
|
|
297
|
+
createElementVNode("span", _hoisted_21, toDisplayString(field.input.preview.label), 1 /* TEXT */),
|
|
268
298
|
createElementVNode("span", {
|
|
269
299
|
"data-testid": `${field.input.preview.testid || `cn-transition-input-preview-${field.key}`}-value`
|
|
270
|
-
}, toDisplayString(field.previewText), 9 /* TEXT, PROPS */,
|
|
300
|
+
}, toDisplayString(field.previewText), 9 /* TEXT, PROPS */, _hoisted_22),
|
|
271
301
|
(field.input.preview.note)
|
|
272
|
-
? (openBlock(), createElementBlock("span",
|
|
302
|
+
? (openBlock(), createElementBlock("span", _hoisted_23, toDisplayString(field.input.preview.note), 1 /* TEXT */))
|
|
273
303
|
: createCommentVNode("v-if", true)
|
|
274
|
-
], 8 /* PROPS */,
|
|
304
|
+
], 8 /* PROPS */, _hoisted_20))
|
|
275
305
|
: createCommentVNode("v-if", true)
|
|
276
306
|
], 64 /* STABLE_FRAGMENT */))
|
|
277
307
|
}), 128 /* KEYED_FRAGMENT */)),
|
|
@@ -287,9 +317,9 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
287
317
|
size: 20,
|
|
288
318
|
class: "cn-transition-input-fields__notice-icon"
|
|
289
319
|
}, null, 8 /* PROPS */, ["name"]),
|
|
290
|
-
createElementVNode("span",
|
|
320
|
+
createElementVNode("span", _hoisted_24, [
|
|
291
321
|
($props.notice.heading)
|
|
292
|
-
? (openBlock(), createElementBlock("strong",
|
|
322
|
+
? (openBlock(), createElementBlock("strong", _hoisted_25, toDisplayString($props.notice.heading), 1 /* TEXT */))
|
|
293
323
|
: createCommentVNode("v-if", true),
|
|
294
324
|
createElementVNode("span", null, toDisplayString($props.notice.text), 1 /* TEXT */)
|
|
295
325
|
])
|
|
@@ -300,7 +330,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
300
330
|
key: `unoffered-${key}`,
|
|
301
331
|
class: "cn-transition-input__field-error",
|
|
302
332
|
"data-testid": `cn-transition-input-error-${key}`
|
|
303
|
-
}, toDisplayString($options.t('nextcloud-vue', 'This action does not accept the field "{field}".', { field: key })), 9 /* TEXT, PROPS */,
|
|
333
|
+
}, toDisplayString($options.t('nextcloud-vue', 'This action does not accept the field "{field}".', { field: key })), 9 /* TEXT, PROPS */, _hoisted_26))
|
|
304
334
|
}), 128 /* KEYED_FRAGMENT */))
|
|
305
335
|
], 2 /* CLASS */))
|
|
306
336
|
}
|