@conduction/nextcloud-vue 2.90.0 → 2.92.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/CnDataTable/CnDataTable.vue2.js +10 -5
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +15 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnDialog/CnDialog.vue2.js +1 -1
- package/dist/esm/components/CnDialog/CnDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnDialog/CnDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnDialogHeader/CnDialogHeader.vue2.js +1 -1
- package/dist/esm/components/CnDialogHeader/CnDialogHeader.vue2.js.map +1 -1
- package/dist/esm/components/CnDialogHeader/CnDialogHeader.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +18 -10
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +16 -8
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +16 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -1
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js +1 -1
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js +156 -3
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js +168 -52
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnModuleCard/CnModuleCard.vue.js +9 -0
- package/dist/esm/components/CnModuleCard/CnModuleCard.vue.js.map +1 -0
- package/dist/esm/components/CnModuleCard/CnModuleCard.vue2.js +83 -0
- package/dist/esm/components/CnModuleCard/CnModuleCard.vue2.js.map +1 -0
- package/dist/esm/components/CnModuleCard/CnModuleCard.vue3.js +119 -0
- package/dist/esm/components/CnModuleCard/CnModuleCard.vue3.js.map +1 -0
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js +6 -3
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +33 -2
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTextWidget/CnTextWidget.vue.js +1 -1
- package/dist/esm/components/CnTextWidget/CnTextWidget.vue2.js +63 -4
- package/dist/esm/components/CnTextWidget/CnTextWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTextWidget/CnTextWidget.vue3.js +19 -2
- package/dist/esm/components/CnTextWidget/CnTextWidget.vue3.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +22 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +21 -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 +328 -114
- package/dist/esm/utils/boardDate.js +35 -1
- package/dist/esm/utils/boardDate.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +12 -3
- package/dist/esm/utils/fillTemplate.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +11569 -10871
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +328 -114
- package/l10n/en.json +22 -1
- package/l10n/nl.json +21 -1
- package/package.json +1 -1
- package/src/components/CnDataTable/CnDataTable.vue +10 -5
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +16 -2
- package/src/components/CnDialog/CnDialog.vue +1 -1
- package/src/components/CnDialog/index.js +7 -0
- package/src/components/CnDialogHeader/CnDialogHeader.vue +1 -1
- package/src/components/CnDialogHeader/index.js +7 -0
- package/src/components/CnFormDialog/CnFormDialog.md +1 -0
- package/src/components/CnFormDialog/CnFormDialog.vue +13 -0
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +17 -2
- package/src/components/CnLinkCardsPage/CnLinkCardsPage.vue +338 -5
- package/src/components/CnModuleCard/CnModuleCard.vue +258 -0
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +6 -3
- package/src/components/CnTabsWidget/CnTabsWidget.vue +33 -2
- package/src/components/CnTextWidget/CnTextWidget.vue +89 -4
- package/src/components/index.js +2 -0
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +59 -2
- package/src/utils/boardDate.js +34 -0
- package/src/utils/fillTemplate.js +12 -3
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -89,6 +89,40 @@ function formatBoardDate(value, now = new Date(), locale = boardDateLocale()) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* A date with its short weekday, the way an agenda writes it: "wo 7 okt", or
|
|
94
|
+
* "wo 14 feb 2024" outside the current year. With `today` set, today's date
|
|
95
|
+
* reads "Vandaag, wo 7 okt".
|
|
96
|
+
*
|
|
97
|
+
* @param {unknown} value A Date, a timestamp or an ISO string.
|
|
98
|
+
* @param {Date} [now] The reference moment (defaults to the current time).
|
|
99
|
+
* @param {object} [options] Options.
|
|
100
|
+
* @param {boolean} [options.today] Put "Today, " before today's date.
|
|
101
|
+
* @param {string} [options.locale] BCP 47 tag (defaults to `boardDateLocale()`).
|
|
102
|
+
* @return {string} The date, or '' when the value is not a date.
|
|
103
|
+
* @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language
|
|
104
|
+
*/
|
|
105
|
+
function formatBoardWeekday(value, now = new Date(), options = {}) {
|
|
106
|
+
const date = parseDateValue(value);
|
|
107
|
+
if (date === null) {
|
|
108
|
+
return ''
|
|
109
|
+
}
|
|
110
|
+
const parts = { weekday: 'short', day: 'numeric', month: 'short' };
|
|
111
|
+
if (date.getFullYear() !== now.getFullYear()) {
|
|
112
|
+
parts.year = 'numeric';
|
|
113
|
+
}
|
|
114
|
+
let text;
|
|
115
|
+
try {
|
|
116
|
+
text = new Intl.DateTimeFormat(options.locale || boardDateLocale(), parts).format(date);
|
|
117
|
+
} catch {
|
|
118
|
+
text = new Intl.DateTimeFormat(undefined, parts).format(date);
|
|
119
|
+
}
|
|
120
|
+
if (options.today === true && ageInDays(date, now) === 0) {
|
|
121
|
+
return translate('nextcloud-vue', 'Today, {date}', { date: text })
|
|
122
|
+
}
|
|
123
|
+
return text
|
|
124
|
+
}
|
|
125
|
+
|
|
92
126
|
/**
|
|
93
127
|
* Whole calendar days from the date until `now` (0 today, 1 yesterday), or
|
|
94
128
|
* null when the value is not a date. A future date gives a negative number.
|
|
@@ -237,5 +271,5 @@ function resolveAgeVariant(value, rules, now = new Date()) {
|
|
|
237
271
|
return ''
|
|
238
272
|
}
|
|
239
273
|
|
|
240
|
-
export { AGE_VARIANTS, ageInDays, boardDateLocale, formatAge, formatBoardDate, formatRelativeDate, formatTimeOfDay, resolveAgeVariant, shownAgeDays };
|
|
274
|
+
export { AGE_VARIANTS, ageInDays, boardDateLocale, formatAge, formatBoardDate, formatBoardWeekday, formatRelativeDate, formatTimeOfDay, resolveAgeVariant, shownAgeDays };
|
|
241
275
|
//# sourceMappingURL=boardDate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boardDate.js","sources":["../../../src/utils/boardDate.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * boardDate: the two short time readings the drawn boards put in a table cell.\n *\n * - A date (\"5 okt\", \"30 okt\", \"14 feb 2024\"): day and short month in the\n * user's language, the year only when it is not the current year.\n * - An age (\"4 uur\", \"1 dag\", \"3 dagen\"): how long something has been\n * waiting, in hours under a day and in whole calendar days after that.\n *\n * Both read the Nextcloud user LANGUAGE for the month and unit names. A\n * Nextcloud account keeps its locale (date order) apart from its language,\n * and a Dutch reader whose locale was left at the en_US default saw\n * \"Feb 14, 2024\": the language decides the words, so it decides here, and the\n * locale is only used when it is a region of that same language.\n *\n * @module utils/boardDate\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md\n */\n\nimport { getCanonicalLocale, getLanguage, translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { parseDateValue } from './dateVariant.js'\nimport { compareVisibleWhen } from './visibleWhen.js'\n\nconst MS_PER_HOUR = 3600000\nconst MS_PER_DAY = 86400000\n\n/** Variants an age rule may name. `danger` is accepted as an alias of `error`. */\nexport const AGE_VARIANTS = Object.freeze(['default', 'success', 'warning', 'error'])\n\n/**\n * Read a language or locale tag from an @nextcloud/l10n getter, which throws\n * when Nextcloud has not set one (outside a Nextcloud page, in a test).\n *\n * @param {() => string} getter `getLanguage` or `getCanonicalLocale`.\n * @return {string} The tag with `-` separators, or ''.\n */\nfunction readTag(getter) {\n\ttry {\n\t\treturn String(getter() || '').replace(/_/g, '-')\n\t} catch {\n\t\treturn ''\n\t}\n}\n\n/**\n * The BCP 47 tag the board dates are written in: the user's locale when it is\n * a region of the user's language (`nl-BE` for language `nl`), else the\n * language itself.\n *\n * @return {string|undefined} The tag, or undefined to let Intl choose.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language\n */\nexport function boardDateLocale() {\n\tconst language = readTag(getLanguage)\n\tconst locale = readTag(getCanonicalLocale)\n\tconst base = (tag) => tag.split('-')[0].toLowerCase()\n\tif (language && locale && base(language) === base(locale)) {\n\t\treturn locale\n\t}\n\treturn language || locale || undefined\n}\n\n/**\n * A date in the board's short form: \"5 okt\", or \"14 feb 2024\" outside the\n * current year.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} [now] The reference moment (defaults to the current time).\n * @param {string} [locale] BCP 47 tag (defaults to `boardDateLocale()`).\n * @return {string} The short date, or '' when the value is not a date.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language\n */\nexport function formatBoardDate(value, now = new Date(), locale = boardDateLocale()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst options = { day: 'numeric', month: 'short' }\n\tif (date.getFullYear() !== now.getFullYear()) {\n\t\toptions.year = 'numeric'\n\t}\n\ttry {\n\t\treturn new Intl.DateTimeFormat(locale, options).format(date)\n\t} catch {\n\t\treturn new Intl.DateTimeFormat(undefined, options).format(date)\n\t}\n}\n\n/**\n * Whole calendar days from the date until `now` (0 today, 1 yesterday), or\n * null when the value is not a date. A future date gives a negative number.\n *\n * @param {unknown} value The date.\n * @param {Date} [now] The reference moment.\n * @return {number|null} The day count.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-says-how-long-something-has-waited\n */\nexport function ageInDays(value, now = new Date()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn null\n\t}\n\t// UTC midnights of the two LOCAL days, so a daylight-saving hour never\n\t// turns one day into zero or two.\n\tconst start = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())\n\tconst today = Date.UTC(now.getFullYear(), now.getMonth(), now.getDate())\n\treturn Math.round((today - start) / MS_PER_DAY)\n}\n\n/**\n * The time of day the boards write: \"09.42\" for a Dutch reader, \"09:42\" for\n * an English one. The separator belongs to the language, so it is one\n * translatable string with two placeholders.\n *\n * @param {Date} date The moment.\n * @return {string} The time of day.\n * @spec openspec/changes/screens-relative-dates/specs/relative-dates/spec.md#requirement-a-relative-date-reads-today-yesterday-then-a-short-date\n */\nexport function formatTimeOfDay(date) {\n\tconst pad = (v) => String(v).padStart(2, '0')\n\treturn t('nextcloud-vue', '{hours}:{minutes}', { hours: pad(date.getHours()), minutes: pad(date.getMinutes()) })\n}\n\n/**\n * A moment the way a list says when something came in: \"today\" or\n * \"yesterday\" (with the time of day on request: \"vandaag, 09.42\"), and from\n * the day before that the board's short date (\"5 okt\", \"14 feb 2024\").\n * Tomorrow reads \"tomorrow\", for a due date. Word and month names follow the\n * user's language through the library's catalogue.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {object} [options] Options.\n * @param {boolean} [options.withTime] Add the time of day after the day: \"today, 09.42\".\n * @param {Date} [options.now] The reference moment (defaults to the current time).\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-relative-dates/specs/relative-dates/spec.md#requirement-a-relative-date-reads-today-yesterday-then-a-short-date\n */\nexport function formatRelativeDate(value, options = {}) {\n\tconst now = options.now || new Date()\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst days = ageInDays(date, now)\n\tlet day\n\tif (days === 0) {\n\t\tday = t('nextcloud-vue', 'today')\n\t} else if (days === 1) {\n\t\tday = t('nextcloud-vue', 'yesterday')\n\t} else if (days === -1) {\n\t\tday = t('nextcloud-vue', 'tomorrow')\n\t} else {\n\t\tday = formatBoardDate(date, now)\n\t}\n\treturn options.withTime === true ? `${day}, ${formatTimeOfDay(date)}` : day\n}\n\n/**\n * How long something has waited: \"4 uur\" under 24 hours (at least 1), else\n * \"1 dag\" / \"3 dagen\" in whole calendar days. Plural forms come from the\n * library's own catalogue (`nextcloud-vue`). A date in the future reads as\n * waiting no time: \"0 hours\".\n *\n * @param {unknown} value The moment the wait started.\n * @param {Date} [now] The reference moment.\n * @return {string} The age, or '' when the value is not a date.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-says-how-long-something-has-waited\n */\nexport function formatAge(value, now = new Date()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst elapsed = now.getTime() - date.getTime()\n\tif (elapsed < MS_PER_DAY) {\n\t\tconst hours = elapsed <= 0 ? 0 : Math.max(1, Math.floor(elapsed / MS_PER_HOUR))\n\t\treturn n('nextcloud-vue', '%n hour', '%n hours', hours)\n\t}\n\tconst days = shownAgeDays(date, now)\n\treturn n('nextcloud-vue', '%n day', '%n days', days)\n}\n\n/**\n * The number of days an age cell shows: 0 under 24 hours (the cell then reads\n * hours), else the whole calendar days, at least 1. The threshold rules\n * compare against this number, so the colour always agrees with the text.\n *\n * @param {unknown} value The moment the wait started.\n * @param {Date} [now] The reference moment.\n * @return {number|null} The day count, or null when the value is not a date.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-turns-red-past-its-threshold\n */\nexport function shownAgeDays(value, now = new Date()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn null\n\t}\n\tif (now.getTime() - date.getTime() < MS_PER_DAY) {\n\t\treturn 0\n\t}\n\treturn Math.max(1, ageInDays(date, now))\n}\n\n/**\n * The variant of the first `variantWhen` rule the age in days matches, or ''\n * when none does. Same rule shape as the `date` widget (`{ op, value,\n * variant }`, first match wins), compared against the number of days the cell\n * shows (`shownAgeDays`): `[{ op: \"gte\", value: 3, variant: \"error\" }]` turns a wait of three\n * days or more red.\n *\n * @param {unknown} value The moment the wait started.\n * @param {Array<{op: string, value: number, variant: string}>} rules The rules.\n * @param {Date} [now] The reference moment.\n * @return {string} `success | warning | error | default`, or ''.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-turns-red-past-its-threshold\n */\nexport function resolveAgeVariant(value, rules, now = new Date()) {\n\tif (!Array.isArray(rules) || rules.length === 0) {\n\t\treturn ''\n\t}\n\tconst days = shownAgeDays(value, now)\n\tif (days === null) {\n\t\treturn ''\n\t}\n\tfor (const rule of rules) {\n\t\tif (!rule || typeof rule !== 'object') {\n\t\t\tcontinue\n\t\t}\n\t\tif (compareVisibleWhen(days, rule.op || 'eq', rule.value) === true) {\n\t\t\tconst variant = rule.variant === 'danger' ? 'error' : rule.variant\n\t\t\treturn AGE_VARIANTS.includes(variant) ? variant : ''\n\t\t}\n\t}\n\treturn ''\n}\n"],"names":["t","n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"boardDate.js","sources":["../../../src/utils/boardDate.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * boardDate: the two short time readings the drawn boards put in a table cell.\n *\n * - A date (\"5 okt\", \"30 okt\", \"14 feb 2024\"): day and short month in the\n * user's language, the year only when it is not the current year.\n * - An age (\"4 uur\", \"1 dag\", \"3 dagen\"): how long something has been\n * waiting, in hours under a day and in whole calendar days after that.\n *\n * Both read the Nextcloud user LANGUAGE for the month and unit names. A\n * Nextcloud account keeps its locale (date order) apart from its language,\n * and a Dutch reader whose locale was left at the en_US default saw\n * \"Feb 14, 2024\": the language decides the words, so it decides here, and the\n * locale is only used when it is a region of that same language.\n *\n * @module utils/boardDate\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md\n */\n\nimport { getCanonicalLocale, getLanguage, translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { parseDateValue } from './dateVariant.js'\nimport { compareVisibleWhen } from './visibleWhen.js'\n\nconst MS_PER_HOUR = 3600000\nconst MS_PER_DAY = 86400000\n\n/** Variants an age rule may name. `danger` is accepted as an alias of `error`. */\nexport const AGE_VARIANTS = Object.freeze(['default', 'success', 'warning', 'error'])\n\n/**\n * Read a language or locale tag from an @nextcloud/l10n getter, which throws\n * when Nextcloud has not set one (outside a Nextcloud page, in a test).\n *\n * @param {() => string} getter `getLanguage` or `getCanonicalLocale`.\n * @return {string} The tag with `-` separators, or ''.\n */\nfunction readTag(getter) {\n\ttry {\n\t\treturn String(getter() || '').replace(/_/g, '-')\n\t} catch {\n\t\treturn ''\n\t}\n}\n\n/**\n * The BCP 47 tag the board dates are written in: the user's locale when it is\n * a region of the user's language (`nl-BE` for language `nl`), else the\n * language itself.\n *\n * @return {string|undefined} The tag, or undefined to let Intl choose.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language\n */\nexport function boardDateLocale() {\n\tconst language = readTag(getLanguage)\n\tconst locale = readTag(getCanonicalLocale)\n\tconst base = (tag) => tag.split('-')[0].toLowerCase()\n\tif (language && locale && base(language) === base(locale)) {\n\t\treturn locale\n\t}\n\treturn language || locale || undefined\n}\n\n/**\n * A date in the board's short form: \"5 okt\", or \"14 feb 2024\" outside the\n * current year.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} [now] The reference moment (defaults to the current time).\n * @param {string} [locale] BCP 47 tag (defaults to `boardDateLocale()`).\n * @return {string} The short date, or '' when the value is not a date.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language\n */\nexport function formatBoardDate(value, now = new Date(), locale = boardDateLocale()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst options = { day: 'numeric', month: 'short' }\n\tif (date.getFullYear() !== now.getFullYear()) {\n\t\toptions.year = 'numeric'\n\t}\n\ttry {\n\t\treturn new Intl.DateTimeFormat(locale, options).format(date)\n\t} catch {\n\t\treturn new Intl.DateTimeFormat(undefined, options).format(date)\n\t}\n}\n\n/**\n * A date with its short weekday, the way an agenda writes it: \"wo 7 okt\", or\n * \"wo 14 feb 2024\" outside the current year. With `today` set, today's date\n * reads \"Vandaag, wo 7 okt\".\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} [now] The reference moment (defaults to the current time).\n * @param {object} [options] Options.\n * @param {boolean} [options.today] Put \"Today, \" before today's date.\n * @param {string} [options.locale] BCP 47 tag (defaults to `boardDateLocale()`).\n * @return {string} The date, or '' when the value is not a date.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language\n */\nexport function formatBoardWeekday(value, now = new Date(), options = {}) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst parts = { weekday: 'short', day: 'numeric', month: 'short' }\n\tif (date.getFullYear() !== now.getFullYear()) {\n\t\tparts.year = 'numeric'\n\t}\n\tlet text\n\ttry {\n\t\ttext = new Intl.DateTimeFormat(options.locale || boardDateLocale(), parts).format(date)\n\t} catch {\n\t\ttext = new Intl.DateTimeFormat(undefined, parts).format(date)\n\t}\n\tif (options.today === true && ageInDays(date, now) === 0) {\n\t\treturn t('nextcloud-vue', 'Today, {date}', { date: text })\n\t}\n\treturn text\n}\n\n/**\n * Whole calendar days from the date until `now` (0 today, 1 yesterday), or\n * null when the value is not a date. A future date gives a negative number.\n *\n * @param {unknown} value The date.\n * @param {Date} [now] The reference moment.\n * @return {number|null} The day count.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-says-how-long-something-has-waited\n */\nexport function ageInDays(value, now = new Date()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn null\n\t}\n\t// UTC midnights of the two LOCAL days, so a daylight-saving hour never\n\t// turns one day into zero or two.\n\tconst start = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())\n\tconst today = Date.UTC(now.getFullYear(), now.getMonth(), now.getDate())\n\treturn Math.round((today - start) / MS_PER_DAY)\n}\n\n/**\n * The time of day the boards write: \"09.42\" for a Dutch reader, \"09:42\" for\n * an English one. The separator belongs to the language, so it is one\n * translatable string with two placeholders.\n *\n * @param {Date} date The moment.\n * @return {string} The time of day.\n * @spec openspec/changes/screens-relative-dates/specs/relative-dates/spec.md#requirement-a-relative-date-reads-today-yesterday-then-a-short-date\n */\nexport function formatTimeOfDay(date) {\n\tconst pad = (v) => String(v).padStart(2, '0')\n\treturn t('nextcloud-vue', '{hours}:{minutes}', { hours: pad(date.getHours()), minutes: pad(date.getMinutes()) })\n}\n\n/**\n * A moment the way a list says when something came in: \"today\" or\n * \"yesterday\" (with the time of day on request: \"vandaag, 09.42\"), and from\n * the day before that the board's short date (\"5 okt\", \"14 feb 2024\").\n * Tomorrow reads \"tomorrow\", for a due date. Word and month names follow the\n * user's language through the library's catalogue.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {object} [options] Options.\n * @param {boolean} [options.withTime] Add the time of day after the day: \"today, 09.42\".\n * @param {Date} [options.now] The reference moment (defaults to the current time).\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-relative-dates/specs/relative-dates/spec.md#requirement-a-relative-date-reads-today-yesterday-then-a-short-date\n */\nexport function formatRelativeDate(value, options = {}) {\n\tconst now = options.now || new Date()\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst days = ageInDays(date, now)\n\tlet day\n\tif (days === 0) {\n\t\tday = t('nextcloud-vue', 'today')\n\t} else if (days === 1) {\n\t\tday = t('nextcloud-vue', 'yesterday')\n\t} else if (days === -1) {\n\t\tday = t('nextcloud-vue', 'tomorrow')\n\t} else {\n\t\tday = formatBoardDate(date, now)\n\t}\n\treturn options.withTime === true ? `${day}, ${formatTimeOfDay(date)}` : day\n}\n\n/**\n * How long something has waited: \"4 uur\" under 24 hours (at least 1), else\n * \"1 dag\" / \"3 dagen\" in whole calendar days. Plural forms come from the\n * library's own catalogue (`nextcloud-vue`). A date in the future reads as\n * waiting no time: \"0 hours\".\n *\n * @param {unknown} value The moment the wait started.\n * @param {Date} [now] The reference moment.\n * @return {string} The age, or '' when the value is not a date.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-says-how-long-something-has-waited\n */\nexport function formatAge(value, now = new Date()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst elapsed = now.getTime() - date.getTime()\n\tif (elapsed < MS_PER_DAY) {\n\t\tconst hours = elapsed <= 0 ? 0 : Math.max(1, Math.floor(elapsed / MS_PER_HOUR))\n\t\treturn n('nextcloud-vue', '%n hour', '%n hours', hours)\n\t}\n\tconst days = shownAgeDays(date, now)\n\treturn n('nextcloud-vue', '%n day', '%n days', days)\n}\n\n/**\n * The number of days an age cell shows: 0 under 24 hours (the cell then reads\n * hours), else the whole calendar days, at least 1. The threshold rules\n * compare against this number, so the colour always agrees with the text.\n *\n * @param {unknown} value The moment the wait started.\n * @param {Date} [now] The reference moment.\n * @return {number|null} The day count, or null when the value is not a date.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-turns-red-past-its-threshold\n */\nexport function shownAgeDays(value, now = new Date()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn null\n\t}\n\tif (now.getTime() - date.getTime() < MS_PER_DAY) {\n\t\treturn 0\n\t}\n\treturn Math.max(1, ageInDays(date, now))\n}\n\n/**\n * The variant of the first `variantWhen` rule the age in days matches, or ''\n * when none does. Same rule shape as the `date` widget (`{ op, value,\n * variant }`, first match wins), compared against the number of days the cell\n * shows (`shownAgeDays`): `[{ op: \"gte\", value: 3, variant: \"error\" }]` turns a wait of three\n * days or more red.\n *\n * @param {unknown} value The moment the wait started.\n * @param {Array<{op: string, value: number, variant: string}>} rules The rules.\n * @param {Date} [now] The reference moment.\n * @return {string} `success | warning | error | default`, or ''.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-turns-red-past-its-threshold\n */\nexport function resolveAgeVariant(value, rules, now = new Date()) {\n\tif (!Array.isArray(rules) || rules.length === 0) {\n\t\treturn ''\n\t}\n\tconst days = shownAgeDays(value, now)\n\tif (days === null) {\n\t\treturn ''\n\t}\n\tfor (const rule of rules) {\n\t\tif (!rule || typeof rule !== 'object') {\n\t\t\tcontinue\n\t\t}\n\t\tif (compareVisibleWhen(days, rule.op || 'eq', rule.value) === true) {\n\t\t\tconst variant = rule.variant === 'danger' ? 'error' : rule.variant\n\t\t\treturn AGE_VARIANTS.includes(variant) ? variant : ''\n\t\t}\n\t}\n\treturn ''\n}\n"],"names":["t","n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { formatBoardDate, formatRelativeDate, boardDateLocale } from './boardDate.js';
|
|
1
|
+
import { formatBoardDate, formatBoardWeekday, formatTimeOfDay, formatRelativeDate, boardDateLocale } from './boardDate.js';
|
|
2
2
|
import { parseDateValue } from './dateVariant.js';
|
|
3
3
|
import { formatMoney } from './formatMetric.js';
|
|
4
4
|
import { readPath } from './readPath.js';
|
|
@@ -37,7 +37,7 @@ function formatBoardDateTime(value, now = new Date()) {
|
|
|
37
37
|
* Format one token value.
|
|
38
38
|
*
|
|
39
39
|
* @param {unknown} value The raw value.
|
|
40
|
-
* @param {string} format `date`, `datetime`, `relative`, `relativetime`, `days` or '' (as is).
|
|
40
|
+
* @param {string} format `date`, `datetime`, `weekday`, `time`, `relative`, `relativetime`, `days` or '' (as is).
|
|
41
41
|
* @param {Date} now The reference moment.
|
|
42
42
|
* @param {string} [arg] The format's argument (`currency:EUR`, or a row path holding the code).
|
|
43
43
|
* @param {object|null} [object] The row, for a currency read from a field.
|
|
@@ -54,6 +54,13 @@ function formatValue(value, format, now, arg = '', object = null, config = null)
|
|
|
54
54
|
if (format === 'datetime') {
|
|
55
55
|
return formatBoardDateTime(value, now)
|
|
56
56
|
}
|
|
57
|
+
if (format === 'weekday') {
|
|
58
|
+
return formatBoardWeekday(value, now, { today: arg === 'today' })
|
|
59
|
+
}
|
|
60
|
+
if (format === 'time') {
|
|
61
|
+
const date = parseDateValue(value);
|
|
62
|
+
return date === null ? '' : formatTimeOfDay(date)
|
|
63
|
+
}
|
|
57
64
|
if (format === 'currency') {
|
|
58
65
|
// `{amount|currency:EUR}` names the code, `{amount|currency:price.code}`
|
|
59
66
|
// reads it from the row, `{amount|currency}` is the app's reporting
|
|
@@ -80,7 +87,9 @@ function formatValue(value, format, now, arg = '', object = null, config = null)
|
|
|
80
87
|
* `"Termijn {deadline|date}"` from an object.
|
|
81
88
|
*
|
|
82
89
|
* Every `{path}` token is a dot path into the object; `{path|date}` and
|
|
83
|
-
* `{path|datetime}` format it as the boards write dates, `{path|
|
|
90
|
+
* `{path|datetime}` format it as the boards write dates, `{path|weekday}` adds
|
|
91
|
+
* the short weekday ("wo 7 okt"; `{path|weekday:today}` reads "Vandaag, wo 7 okt"
|
|
92
|
+
* for today), `{path|time}` is the time of day ("08:45"), `{path|currency}`,
|
|
84
93
|
* `{path|currency:EUR}` and `{path|currency:field}` write an amount in a
|
|
85
94
|
* currency (a code, or the row field that holds it; without either, or when
|
|
86
95
|
* the row field is empty, the app's reporting currency), `{path|relative}` and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fillTemplate.js","sources":["../../../src/utils/fillTemplate.js"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 Conduction B.V.\n// SPDX-License-Identifier: EUPL-1.2\n\nimport { boardDateLocale, formatBoardDate, formatRelativeDate } from './boardDate.js'\nimport { parseDateValue } from './dateVariant.js'\nimport { formatMoney } from './formatMetric.js'\nimport { readPath } from './readPath.js'\n\nconst TOKEN = /\\{([^{}\\s|]+)(?:\\|([a-z]+)(?::([^{}\\s]+))?)?\\}/g\n\n/**\n * A moment in the board's short form with the time: \"4 okt, 10:52\", or\n * \"14 feb 2024, 09:00\" outside the current year.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} [now] The reference moment.\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function formatBoardDateTime(value, now = new Date()) {\n\tconst day = formatBoardDate(value, now)\n\tconst date = parseDateValue(value)\n\tif (day === '' || date === null) {\n\t\treturn ''\n\t}\n\tlet time\n\ttry {\n\t\ttime = new Intl.DateTimeFormat(boardDateLocale(), { hour: '2-digit', minute: '2-digit' }).format(date)\n\t} catch {\n\t\ttime = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit' }).format(date)\n\t}\n\treturn `${day}, ${time}`\n}\n\n/**\n * Format one token value.\n *\n * @param {unknown} value The raw value.\n * @param {string} format `date`, `datetime`, `relative`, `relativetime`, `days` or '' (as is).\n * @param {Date} now The reference moment.\n * @param {string} [arg] The format's argument (`currency:EUR`, or a row path holding the code).\n * @param {object|null} [object] The row, for a currency read from a field.\n * @param {object|null} [config] The app config map, for the reporting currency.\n * @return {string} The text, '' when nothing can be shown.\n */\nfunction formatValue(value, format, now, arg = '', object = null, config = null) {\n\tif (value === null || value === undefined || value === '' || typeof value === 'object') {\n\t\treturn ''\n\t}\n\tif (format === 'date') {\n\t\treturn formatBoardDate(value, now)\n\t}\n\tif (format === 'datetime') {\n\t\treturn formatBoardDateTime(value, now)\n\t}\n\tif (format === 'currency') {\n\t\t// `{amount|currency:EUR}` names the code, `{amount|currency:price.code}`\n\t\t// reads it from the row, `{amount|currency}` is the app's reporting\n\t\t// currency (EUR without one).\n\t\tconst isCode = /^[A-Za-z]{3}$/.test(arg)\n\t\treturn formatMoney(value, { currency: isCode ? arg : undefined, currencyField: !isCode && arg ? arg : undefined }, object, config)\n\t}\n\tif (format === 'relative') {\n\t\treturn formatRelativeDate(value, { now })\n\t}\n\tif (format === 'relativetime') {\n\t\treturn formatRelativeDate(value, { now, withTime: true })\n\t}\n\tif (format === 'days') {\n\t\t// Whole days from the date to now (0 for today; negative for a future date).\n\t\tconst date = parseDateValue(value)\n\t\treturn date === null ? '' : String(Math.floor((now.getTime() - date.getTime()) / 86400000))\n\t}\n\treturn String(value)\n}\n\n/**\n * Fill a row template such as `\"{identifier} · {caseTypeName}\"` or\n * `\"Termijn {deadline|date}\"` from an object.\n *\n * Every `{path}` token is a dot path into the object; `{path|date}` and\n * `{path|datetime}` format it as the boards write dates, `{path|currency}`,\n * `{path|currency:EUR}` and `{path|currency:field}` write an amount in a\n * currency (a code, or the row field that holds it; without either, or when\n * the row field is empty, the app's reporting currency), `{path|relative}` and\n * `{path|relativetime}` say \"today\", \"yesterday\" and then the short date\n * (\"vandaag, 09.42\"), and `{path|days}` is the\n * whole days since that date (\"has waited {createdAt|days} days\"). A token without a\n * value empties the WHOLE template, because \"Termijn \" is worse than nothing.\n * A template without tokens is returned as written (a constant label).\n *\n * @param {string} template The template, already translated.\n * @param {object|null} object The object to read.\n * @param {Date} [now] The reference moment for dates.\n * @param {{config?: object}|null} [context] The app config map, for the reporting currency of a money token.\n * @return {string} The filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function fillTemplate(template, object, now = new Date(), context = null) {\n\tif (typeof template !== 'string' || template.trim() === '') {\n\t\treturn ''\n\t}\n\tlet missing = false\n\tconst text = template.replace(TOKEN, (_match, path, format, arg) => {\n\t\tconst value = object && typeof object === 'object' ? readPath(object, path) : undefined\n\t\tconst shown = formatValue(value, format || '', now, arg || '', object, context && context.config ? context.config : null)\n\t\tif (shown === '') {\n\t\t\tmissing = true\n\t\t}\n\t\treturn shown\n\t})\n\treturn missing ? '' : text.trim()\n}\n\n/**\n * The first of several templates that fills: `[\"Klaar {closedAt|date}\",\n * \"Termijn {deadline|date}\"]` reads \"Klaar ...\" once the case is closed and\n * \"Termijn ...\" while it is open. A single string is one candidate.\n *\n * @param {string|string[]} templates The candidates, in order.\n * @param {object|null} object The object to read.\n * @param {(key: string) => string} [translate] Applied to each template before filling.\n * @param {Date} [now] The reference moment for dates.\n * @param {{config?: object}|null} [context] Passed on to `fillTemplate`.\n * @return {string} The first filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function firstFilled(templates, object, translate = (key) => key, now = new Date(), context = null) {\n\tconst list = Array.isArray(templates) ? templates : [templates]\n\tfor (const template of list) {\n\t\tif (typeof template !== 'string') {\n\t\t\tcontinue\n\t\t}\n\t\tconst text = fillTemplate(translate(template), object, now, context)\n\t\tif (text !== '') {\n\t\t\treturn text\n\t\t}\n\t}\n\treturn ''\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fillTemplate.js","sources":["../../../src/utils/fillTemplate.js"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 Conduction B.V.\n// SPDX-License-Identifier: EUPL-1.2\n\nimport { boardDateLocale, formatBoardDate, formatBoardWeekday, formatRelativeDate, formatTimeOfDay } from './boardDate.js'\nimport { parseDateValue } from './dateVariant.js'\nimport { formatMoney } from './formatMetric.js'\nimport { readPath } from './readPath.js'\n\nconst TOKEN = /\\{([^{}\\s|]+)(?:\\|([a-z]+)(?::([^{}\\s]+))?)?\\}/g\n\n/**\n * A moment in the board's short form with the time: \"4 okt, 10:52\", or\n * \"14 feb 2024, 09:00\" outside the current year.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} [now] The reference moment.\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function formatBoardDateTime(value, now = new Date()) {\n\tconst day = formatBoardDate(value, now)\n\tconst date = parseDateValue(value)\n\tif (day === '' || date === null) {\n\t\treturn ''\n\t}\n\tlet time\n\ttry {\n\t\ttime = new Intl.DateTimeFormat(boardDateLocale(), { hour: '2-digit', minute: '2-digit' }).format(date)\n\t} catch {\n\t\ttime = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit' }).format(date)\n\t}\n\treturn `${day}, ${time}`\n}\n\n/**\n * Format one token value.\n *\n * @param {unknown} value The raw value.\n * @param {string} format `date`, `datetime`, `weekday`, `time`, `relative`, `relativetime`, `days` or '' (as is).\n * @param {Date} now The reference moment.\n * @param {string} [arg] The format's argument (`currency:EUR`, or a row path holding the code).\n * @param {object|null} [object] The row, for a currency read from a field.\n * @param {object|null} [config] The app config map, for the reporting currency.\n * @return {string} The text, '' when nothing can be shown.\n */\nfunction formatValue(value, format, now, arg = '', object = null, config = null) {\n\tif (value === null || value === undefined || value === '' || typeof value === 'object') {\n\t\treturn ''\n\t}\n\tif (format === 'date') {\n\t\treturn formatBoardDate(value, now)\n\t}\n\tif (format === 'datetime') {\n\t\treturn formatBoardDateTime(value, now)\n\t}\n\tif (format === 'weekday') {\n\t\treturn formatBoardWeekday(value, now, { today: arg === 'today' })\n\t}\n\tif (format === 'time') {\n\t\tconst date = parseDateValue(value)\n\t\treturn date === null ? '' : formatTimeOfDay(date)\n\t}\n\tif (format === 'currency') {\n\t\t// `{amount|currency:EUR}` names the code, `{amount|currency:price.code}`\n\t\t// reads it from the row, `{amount|currency}` is the app's reporting\n\t\t// currency (EUR without one).\n\t\tconst isCode = /^[A-Za-z]{3}$/.test(arg)\n\t\treturn formatMoney(value, { currency: isCode ? arg : undefined, currencyField: !isCode && arg ? arg : undefined }, object, config)\n\t}\n\tif (format === 'relative') {\n\t\treturn formatRelativeDate(value, { now })\n\t}\n\tif (format === 'relativetime') {\n\t\treturn formatRelativeDate(value, { now, withTime: true })\n\t}\n\tif (format === 'days') {\n\t\t// Whole days from the date to now (0 for today; negative for a future date).\n\t\tconst date = parseDateValue(value)\n\t\treturn date === null ? '' : String(Math.floor((now.getTime() - date.getTime()) / 86400000))\n\t}\n\treturn String(value)\n}\n\n/**\n * Fill a row template such as `\"{identifier} · {caseTypeName}\"` or\n * `\"Termijn {deadline|date}\"` from an object.\n *\n * Every `{path}` token is a dot path into the object; `{path|date}` and\n * `{path|datetime}` format it as the boards write dates, `{path|weekday}` adds\n * the short weekday (\"wo 7 okt\"; `{path|weekday:today}` reads \"Vandaag, wo 7 okt\"\n * for today), `{path|time}` is the time of day (\"08:45\"), `{path|currency}`,\n * `{path|currency:EUR}` and `{path|currency:field}` write an amount in a\n * currency (a code, or the row field that holds it; without either, or when\n * the row field is empty, the app's reporting currency), `{path|relative}` and\n * `{path|relativetime}` say \"today\", \"yesterday\" and then the short date\n * (\"vandaag, 09.42\"), and `{path|days}` is the\n * whole days since that date (\"has waited {createdAt|days} days\"). A token without a\n * value empties the WHOLE template, because \"Termijn \" is worse than nothing.\n * A template without tokens is returned as written (a constant label).\n *\n * @param {string} template The template, already translated.\n * @param {object|null} object The object to read.\n * @param {Date} [now] The reference moment for dates.\n * @param {{config?: object}|null} [context] The app config map, for the reporting currency of a money token.\n * @return {string} The filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function fillTemplate(template, object, now = new Date(), context = null) {\n\tif (typeof template !== 'string' || template.trim() === '') {\n\t\treturn ''\n\t}\n\tlet missing = false\n\tconst text = template.replace(TOKEN, (_match, path, format, arg) => {\n\t\tconst value = object && typeof object === 'object' ? readPath(object, path) : undefined\n\t\tconst shown = formatValue(value, format || '', now, arg || '', object, context && context.config ? context.config : null)\n\t\tif (shown === '') {\n\t\t\tmissing = true\n\t\t}\n\t\treturn shown\n\t})\n\treturn missing ? '' : text.trim()\n}\n\n/**\n * The first of several templates that fills: `[\"Klaar {closedAt|date}\",\n * \"Termijn {deadline|date}\"]` reads \"Klaar ...\" once the case is closed and\n * \"Termijn ...\" while it is open. A single string is one candidate.\n *\n * @param {string|string[]} templates The candidates, in order.\n * @param {object|null} object The object to read.\n * @param {(key: string) => string} [translate] Applied to each template before filling.\n * @param {Date} [now] The reference moment for dates.\n * @param {{config?: object}|null} [context] Passed on to `fillTemplate`.\n * @return {string} The first filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function firstFilled(templates, object, translate = (key) => key, now = new Date(), context = null) {\n\tconst list = Array.isArray(templates) ? templates : [templates]\n\tfor (const template of list) {\n\t\tif (typeof template !== 'string') {\n\t\t\tcontinue\n\t\t}\n\t\tconst text = fillTemplate(translate(template), object, now, context)\n\t\tif (text !== '') {\n\t\t\treturn text\n\t\t}\n\t}\n\treturn ''\n}\n"],"names":[],"mappings":"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"}
|