@conduction/nextcloud-vue 2.100.0 → 2.102.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/CnActionsBar/CnActionsBar.vue2.js +22 -0
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +8 -6
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +29 -4
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +9 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js +22 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +26 -16
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +70 -6
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +105 -74
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js +9 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js +139 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js +76 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +160 -2
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +206 -94
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesReview.js +61 -0
- package/dist/esm/components/CnFilesBrowser/filesReview.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js +9 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js +279 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js +122 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/index.js +29 -0
- package/dist/esm/components/CnGroupedCardsWidget/index.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +320 -74
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +323 -213
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useNamedSource.js +30 -3
- package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue.js +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js +15 -0
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue.js +1 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue2.js +25 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +14 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +14 -6
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +10 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +6 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +91 -4
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +63 -38
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue.js +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +106 -4
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +70 -25
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js +9 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js +175 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js +77 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +35 -2
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +4 -0
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +2 -23
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/composables/indexSources.js +176 -20
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/composables/useEndpointSource.js +28 -6
- package/dist/esm/composables/useEndpointSource.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +41 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +39 -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 +939 -376
- package/dist/esm/utils/builtInFormatters.js +24 -4
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/languageMapText.js +50 -0
- package/dist/esm/utils/languageMapText.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +3 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/mapList.js +71 -0
- package/dist/esm/utils/mapList.js.map +1 -0
- package/dist/esm/utils/rowClassRules.js +43 -0
- package/dist/esm/utils/rowClassRules.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/visibleWhen.js +19 -2
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +9681 -7136
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +939 -376
- package/l10n/en.json +41 -1
- package/l10n/nl.json +39 -1
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.md +1 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +31 -4
- package/src/components/CnChartWidget/CnChartWidget.md +3 -2
- package/src/components/CnChartWidget/CnChartWidget.vue +42 -4
- package/src/components/CnChoiceCards/CnChoiceCards.md +39 -0
- package/src/components/CnChoiceCards/CnChoiceCards.vue +93 -16
- package/src/components/CnDashboardPage/CnDashboardPage.vue +110 -21
- package/src/components/CnExportsWidget/CnExportsWidget.vue +233 -0
- package/src/components/CnExportsWidget/index.js +7 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +324 -0
- package/src/components/CnFilesBrowser/filesReview.js +57 -0
- package/src/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue +433 -0
- package/src/components/CnGroupedCardsWidget/index.js +28 -0
- package/src/components/CnIndexPage/CnIndexPage.md +5 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +313 -9
- package/src/components/CnIndexPage/useNamedSource.js +30 -3
- package/src/components/CnMapWidget/CnMapWidget.vue +37 -0
- package/src/components/CnMenuWidget/CnMenuWidget.vue +25 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +29 -1
- package/src/components/CnObjectSidebar/CnFilesTab.vue +15 -0
- package/src/components/CnOrderedListField/CnOrderedListField.vue +151 -5
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.md +1 -0
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +159 -5
- package/src/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue +272 -0
- package/src/components/CnSavedViewAlertWidget/index.js +7 -0
- package/src/components/CnSetupWizard/CnSetupWizard.vue +35 -2
- package/src/components/CnWidgetGrid/builtInWidgets.js +4 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +2 -23
- package/src/components/index.js +3 -0
- package/src/composables/indexSources.js +175 -19
- package/src/composables/useEndpointSource.js +28 -6
- package/src/css/index-page.css +117 -0
- package/src/css/look-board-index.css +10 -0
- package/src/css/look-board.css +44 -0
- package/src/index.js +1 -0
- package/src/schemas/app-manifest-v2.schema.json +85 -3
- package/src/utils/builtInFormatters.js +24 -4
- package/src/utils/languageMapText.js +46 -0
- package/src/utils/libraryWidgetKeys.js +3 -0
- package/src/utils/mapList.js +67 -0
- package/src/utils/rowClassRules.js +39 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/visibleWhen.js +19 -2
|
@@ -379,25 +379,45 @@ const CONNECTION_STATUS_LABELS = {
|
|
|
379
379
|
error: 'Error',
|
|
380
380
|
};
|
|
381
381
|
|
|
382
|
+
/**
|
|
383
|
+
* The short labels `formatterOptions.short` draws (board DqInstellingen
|
|
384
|
+
* "Aan / Uit / Fout"): a connection that works in some form is on, one that
|
|
385
|
+
* does not run is off, and a failing one is an error.
|
|
386
|
+
*/
|
|
387
|
+
const CONNECTION_STATUS_SHORT_LABELS = {
|
|
388
|
+
configured: 'On',
|
|
389
|
+
limited: 'On',
|
|
390
|
+
simulated: 'On',
|
|
391
|
+
unconfigured: 'Off',
|
|
392
|
+
disabled: 'Off',
|
|
393
|
+
unavailable: 'Off',
|
|
394
|
+
error: 'Error',
|
|
395
|
+
};
|
|
396
|
+
|
|
382
397
|
/**
|
|
383
398
|
* Connection status formatter (`connectionStatus` registry key) for the
|
|
384
399
|
* fleet connection registry. Renders one of the seven stored status values
|
|
385
400
|
* as its translated label through the library's own slug (`nextcloud-vue`).
|
|
401
|
+
* With `formatterOptions.short` it renders the board's three short labels
|
|
402
|
+
* instead: On (configured, limited, simulated), Off (not configured,
|
|
403
|
+
* switched off, not available) and Error.
|
|
386
404
|
*
|
|
387
405
|
* An unknown value passes through unchanged, so a status added later still
|
|
388
406
|
* shows as its raw value instead of an empty cell. Null and undefined render
|
|
389
407
|
* as `''`. Never throws.
|
|
390
408
|
*
|
|
391
409
|
* @param {unknown} value The stored status value.
|
|
410
|
+
* @param {object} [row] The row (unused).
|
|
411
|
+
* @param {object} [property] The column's property (unused).
|
|
412
|
+
* @param {{short?: boolean}} [options] The column's `formatterOptions`.
|
|
392
413
|
* @return {string} The translated label, or the value itself when it is not one of the seven.
|
|
393
414
|
*/
|
|
394
|
-
function formatConnectionStatus(value) {
|
|
415
|
+
function formatConnectionStatus(value, row, property, options) {
|
|
395
416
|
if (value === null || value === undefined) {
|
|
396
417
|
return ''
|
|
397
418
|
}
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
: null;
|
|
419
|
+
const labels = options && options.short === true ? CONNECTION_STATUS_SHORT_LABELS : CONNECTION_STATUS_LABELS;
|
|
420
|
+
const label = Object.hasOwn(labels, value) ? labels[value] : null;
|
|
401
421
|
return label ? translate('nextcloud-vue', label) : String(value)
|
|
402
422
|
}
|
|
403
423
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtInFormatters.js","sources":["../../../src/utils/builtInFormatters.js"],"sourcesContent":["import { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { formatRelativeDate } from './boardDate.js'\nimport { formatMoney } from './formatMetric.js'\n\n/**\n * @file Built-in formatters merged into the `cnFormatters` registry\n * `CnAppRoot` provides to descendants.\n *\n * Resolution order (in `CnCellRenderer`):\n * 1. Consumer-registered `formatters[id]` (passed to CnAppRoot).\n * 2. Built-in `BUILT_IN_FORMATTERS[id]` from this file.\n * 3. Schema-type-aware rendering.\n * 4. Plain `formatValue()` fallback.\n *\n * Consumer formatters win (CnAppRoot spreads\n * `{ ...BUILT_IN_FORMATTERS, ...props.formatters }`).\n *\n * A formatter is invoked as `fn(value, row, property, options)` — `options`\n * is the column's declarative `formatterOptions` map (undefined when the\n * column declares none), so config-driven formatters (`currency`,\n * `conditionalPhrase`) work without one registry function per configuration.\n * The fourth argument is additive: pre-existing three-argument formatters\n * are unaffected.\n *\n * Every built-in MUST be safe against null / empty / non-parseable\n * inputs — the contract is \"return the original value (or empty\n * string) rather than throw\".\n */\n\n/**\n * Parse a value that's meant to be a date — accepts a `Date` instance,\n * an ISO/parseable string, or a numeric timestamp.\n *\n * @param {Date|string|number|null|undefined} value The candidate date value.\n * @return {Date|null} A `Date` instance, or `null` if the input is null/empty/unparseable.\n */\nfunction toDate(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn null\n\t}\n\tif (value instanceof Date) {\n\t\treturn Number.isNaN(value.getTime()) ? null : value\n\t}\n\tconst d = new Date(value)\n\treturn Number.isNaN(d.getTime()) ? null : d\n}\n\n/**\n * Locale-formatted date (no time). Backed by `Intl.DateTimeFormat`\n * (`dateStyle: 'medium'`) using the user-agent locale.\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @return {string} Formatted date, or `''` for null/empty, or `String(value)` for unparseable.\n */\nexport function formatDate(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst d = toDate(value)\n\tif (!d) {\n\t\treturn String(value)\n\t}\n\treturn new Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(d)\n}\n\n/**\n * Locale-formatted date + time. Backed by `Intl.DateTimeFormat`\n * (`dateStyle: 'medium'`, `timeStyle: 'short'`).\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @return {string} Formatted date + time, or `''` for null/empty, or `String(value)` for unparseable.\n */\nexport function formatDateTime(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst d = toDate(value)\n\tif (!d) {\n\t\treturn String(value)\n\t}\n\treturn new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }).format(d)\n}\n\n/**\n * Relative time (\"3 days ago\") via `Intl.RelativeTimeFormat`.\n * Picks the coarsest unit whose absolute delta exceeds one unit,\n * down to minutes (anything sub-minute clamps to \"now\"/seconds).\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @return {string} Relative phrasing, or `''` for null/empty, or `String(value)` for unparseable.\n */\nexport function formatRelativeTime(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst d = toDate(value)\n\tif (!d) {\n\t\treturn String(value)\n\t}\n\tconst diffMs = d.getTime() - Date.now()\n\tconst rtf = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' })\n\tconst units = [\n\t\t['year', 31536000000],\n\t\t['month', 2592000000],\n\t\t['week', 604800000],\n\t\t['day', 86400000],\n\t\t['hour', 3600000],\n\t\t['minute', 60000],\n\t]\n\tfor (const [unit, ms] of units) {\n\t\tif (Math.abs(diffMs) >= ms || unit === 'minute') {\n\t\t\treturn rtf.format(Math.round(diffMs / ms), unit)\n\t\t}\n\t}\n\treturn rtf.format(0, 'second')\n}\n\n/**\n * Whole-day difference between a date value and today, comparing\n * date-only (both clamped to local midnight) so a due date later today\n * still counts as \"today\", not \"in 0.4 days\".\n *\n * @param {Date} d The parsed date.\n * @return {number} Signed day count: positive = future, negative = past, 0 = today.\n */\nfunction dayDiffFromToday(d) {\n\tconst target = new Date(d)\n\ttarget.setHours(0, 0, 0, 0)\n\tconst today = new Date()\n\ttoday.setHours(0, 0, 0, 0)\n\treturn Math.round((target.getTime() - today.getTime()) / 86400000)\n}\n\n/**\n * Future-oriented deadline formatter (`daysUntil` registry key), i18n'd\n * through the library's own translation slug (`nextcloud-vue`):\n * a future date renders \"N days remaining\", today renders \"Due today\",\n * and a past date renders \"N days overdue\". Day counts are plural-aware\n * via `translatePlural`.\n *\n * Null-safe per the built-in-formatter contract: `''` for null/empty,\n * `String(value)` for unparseable input — never throws.\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @return {string} The relative-day phrasing (or ''/original on bad input).\n */\nexport function formatDaysUntil(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst d = toDate(value)\n\tif (!d) {\n\t\treturn String(value)\n\t}\n\tconst days = dayDiffFromToday(d)\n\tif (days === 0) {\n\t\treturn t('nextcloud-vue', 'Due today')\n\t}\n\tif (days > 0) {\n\t\treturn n('nextcloud-vue', '{count} day remaining', '{count} days remaining', days, { count: days })\n\t}\n\tconst overdue = Math.abs(days)\n\treturn n('nextcloud-vue', '{count} day overdue', '{count} days overdue', overdue, { count: overdue })\n}\n\n/**\n * Elapsed-time formatter (`daysSince` registry key), i18n'd through the\n * library's own translation slug (`nextcloud-vue`) in the Nextcloud user\n * language. Whole calendar days, read as a running phrase so it sits after a\n * title or in a meta line: today renders \"today\", one day back renders\n * \"yesterday\", further back renders \"N days ago\". A (nonsensical but\n * harmless) future date renders \"tomorrow\" or \"in N days\". Day counts are\n * plural-aware via `translatePlural`.\n *\n * Null-safe per the built-in-formatter contract: `''` for null/empty,\n * `String(value)` for unparseable input, never throws.\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @return {string} The relative-day phrasing (or ''/original on bad input).\n */\nexport function formatDaysSince(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst d = toDate(value)\n\tif (!d) {\n\t\treturn String(value)\n\t}\n\tconst days = dayDiffFromToday(d)\n\tif (days === 0) {\n\t\treturn t('nextcloud-vue', 'today')\n\t}\n\tif (days === -1) {\n\t\treturn t('nextcloud-vue', 'yesterday')\n\t}\n\tif (days === 1) {\n\t\treturn t('nextcloud-vue', 'tomorrow')\n\t}\n\tif (days < 0) {\n\t\tconst ago = Math.abs(days)\n\t\treturn n('nextcloud-vue', '{count} day ago', '{count} days ago', ago, { count: ago })\n\t}\n\treturn n('nextcloud-vue', 'in {count} day', 'in {count} days', days, { count: days })\n}\n\n/**\n * Relative-date formatter (`relativeDate` registry key): \"today\", \"yesterday\",\n * then the board's short date (\"5 okt\"). The column's `formatterOptions` may set\n * `time: true` to add the time of day after the day (\"vandaag, 09.42\").\n *\n * Null-safe per the built-in-formatter contract: `''` for null/empty,\n * `String(value)` for unparseable input, never throws.\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @param {object} [_row] The full row (unused).\n * @param {object} [_property] The schema property (unused).\n * @param {{time?: boolean}} [options] The column's `formatterOptions`.\n * @return {string} The relative-day phrasing (or ''/original on bad input).\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 formatRelativeDay(value, _row, _property, options) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst text = formatRelativeDate(value, { withTime: options?.time === true })\n\treturn text === '' ? String(value) : text\n}\n\n/**\n * Currency formatter (`currency` registry key) via `Intl.NumberFormat`.\n * Defaults to EUR; the column's `formatterOptions` may set `currency`\n * (ISO-4217 code, guarded by `safeCurrencyCode` so an invalid code can\n * never throw a `RangeError`) and `decimals` (default 2).\n *\n * Null-safe per the built-in-formatter contract: `''` for null/empty,\n * `String(value)` for non-numeric input — never throws.\n *\n * @param {unknown} value A numeric value (or numeric string).\n * @param {object} [row] The full row, for `currencyField`.\n * @param {object} [_property] The schema property (unused).\n * @param {{currency?: string, currencyField?: string, decimals?: number}} [options] The column's `formatterOptions`.\n * @return {string} The locale currency string (or ''/original on bad input).\n */\nexport function formatCurrency(value, row, _property, options) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst num = Number(value)\n\tif (!Number.isFinite(num)) {\n\t\treturn String(value)\n\t}\n\treturn formatMoney(num, options || {}, row)\n}\n\n/**\n * Sign/zero-based phrase selector (`conditionalPhrase` registry key) —\n * generalizes `daysUntil` to ANY numeric field: the column's\n * `formatterOptions` supply pre-translated `{ negative, zero, positive }`\n * phrases and the matching one renders with `{n}` replaced by the ABSOLUTE\n * value (so \"{n} days overdue\" reads naturally for -3). Phrases are\n * consumer-provided strings — apps translate them before declaring the\n * column, keeping i18n in the app layer.\n *\n * Null-safe per the built-in-formatter contract: `''` for null/empty,\n * `String(value)` for non-numeric input or when the selected phrase is\n * missing — never throws.\n *\n * @param {unknown} value A numeric value (or numeric string).\n * @param {object} [_row] The full row (unused).\n * @param {object} [_property] The schema property (unused).\n * @param {{negative?: string, zero?: string, positive?: string}} [options] The column's `formatterOptions`.\n * @return {string} The selected phrase with `{n}` substituted (or ''/original on bad input).\n */\nexport function formatConditionalPhrase(value, _row, _property, options) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst num = Number(value)\n\tif (!Number.isFinite(num)) {\n\t\treturn String(value)\n\t}\n\tconst opts = options || {}\n\tconst phrase = num < 0 ? opts.negative : (num > 0 ? opts.positive : opts.zero)\n\tif (typeof phrase !== 'string' || phrase === '') {\n\t\treturn String(value)\n\t}\n\treturn phrase.replace(/\\{n\\}/g, String(Math.abs(num)))\n}\n\n/**\n * Read a value that may be a collection SERIALISED as JSON back into the\n * collection itself. OpenRegister hands a stringified array back for some\n * property types (and an app's own import path may persist one), which\n * `formatCount` would otherwise report as a single entry.\n *\n * Only `[`/`{`-leading strings are tried, so an ordinary text value never pays\n * for a parse attempt, and a string that merely looks like JSON but isn't falls\n * back to being one entry.\n *\n * @param {unknown} value The raw cell value.\n * @return {unknown} The parsed collection, or `value` unchanged.\n */\nfunction parseCollection(value) {\n\tif (typeof value !== 'string') {\n\t\treturn value\n\t}\n\tconst trimmed = value.trim()\n\tif (trimmed[0] !== '[' && trimmed[0] !== '{') {\n\t\treturn value\n\t}\n\ttry {\n\t\tconst parsed = JSON.parse(trimmed)\n\t\treturn (parsed !== null && typeof parsed === 'object') ? parsed : value\n\t} catch {\n\t\treturn value\n\t}\n}\n\n/**\n * Entry-count summariser (`count` registry key) — renders a collection-valued\n * cell as \"5 frames\" instead of the truncated JSON blob `formatValue` would\n * produce. Counts array entries or object keys; a PRESENT scalar counts as 1.\n * The column's `formatterOptions` supply pre-translated\n * `{ singular, plural, zero }` phrases with `{n}` substituted, following the\n * `conditionalPhrase` convention of keeping i18n in the app layer. Without\n * phrases it renders the bare count.\n *\n * Null-safe per the built-in-formatter contract: `zero` (or `''`) for\n * null/empty — never throws.\n *\n * @param {unknown} value An array, object, JSON-encoded collection, or scalar.\n * @param {object} [_row] The full row (unused).\n * @param {object} [_property] The schema property (unused).\n * @param {{singular?: string, plural?: string, zero?: string}} [options] The column's `formatterOptions`.\n * @return {string} The selected phrase with `{n}` substituted, or the bare count.\n */\nexport function formatCount(value, _row, _property, options) {\n\tconst opts = options || {}\n\tconst zero = typeof opts.zero === 'string' ? opts.zero : ''\n\t// Every EMPTY value takes the zero phrase, not just null/''. `0` and `false`\n\t// used to fall through to the scalar branch and count as one entry, so a\n\t// column over a `0` rendered \"1 retry\" — the singular of a thing that isn't\n\t// there.\n\tif (value === null || value === undefined || value === '' || value === 0 || value === false) {\n\t\treturn zero\n\t}\n\tconst collection = parseCollection(value)\n\tlet n\n\tif (Array.isArray(collection)) {\n\t\tn = collection.length\n\t} else if (typeof collection === 'object') {\n\t\tn = Object.keys(collection).length\n\t} else {\n\t\tn = 1\n\t}\n\tif (n === 0) {\n\t\treturn zero\n\t}\n\tconst phrase = n === 1 ? (opts.singular ?? opts.plural) : (opts.plural ?? opts.singular)\n\tif (typeof phrase !== 'string' || phrase === '') {\n\t\treturn String(n)\n\t}\n\treturn phrase.replace(/\\{n\\}/g, String(n))\n}\n\n/**\n * The seven stored connection status values and their English source labels.\n *\n * The label is translated on each call, not here. A module-level `t()` runs\n * before the catalogue is registered and would freeze every label in English.\n */\nconst CONNECTION_STATUS_LABELS = {\n\tconfigured: 'Configured',\n\tlimited: 'Limited',\n\tunconfigured: 'Not configured',\n\tsimulated: 'Simulated',\n\tdisabled: 'Switched off',\n\tunavailable: 'Not available',\n\terror: 'Error',\n}\n\n/**\n * Connection status formatter (`connectionStatus` registry key) for the\n * fleet connection registry. Renders one of the seven stored status values\n * as its translated label through the library's own slug (`nextcloud-vue`).\n *\n * An unknown value passes through unchanged, so a status added later still\n * shows as its raw value instead of an empty cell. Null and undefined render\n * as `''`. Never throws.\n *\n * @param {unknown} value The stored status value.\n * @return {string} The translated label, or the value itself when it is not one of the seven.\n */\nexport function formatConnectionStatus(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tconst label = Object.hasOwn(CONNECTION_STATUS_LABELS, value)\n\t\t? CONNECTION_STATUS_LABELS[value]\n\t\t: null\n\treturn label ? t('nextcloud-vue', label) : String(value)\n}\n\n/**\n * Connection settings label formatter (`connectionSettingsLabel` registry\n * key). Renders \"Open settings\" when the row's `settingsUrl` is a non-empty\n * string, and `''` otherwise. Paired with the built-in `link` cell widget, an\n * empty label and href fall through to plain empty text, so a connection with\n * no settings section offers nothing to click.\n *\n * @param {unknown} value The row's `settingsUrl`.\n * @return {string} The translated link text, or `''`.\n */\nexport function formatConnectionSettingsLabel(value) {\n\treturn typeof value === 'string' && value !== ''\n\t\t? t('nextcloud-vue', 'Open settings')\n\t\t: ''\n}\n\n/**\n * Show a value with all but its last characters replaced by `*`, as the facts\n * widget's `masked` format does: an IBAN or a BSN reads `*****4567`.\n *\n * DISPLAY ONLY, NOT ACCESS CONTROL. The full value is still in the row the\n * page received; a reader with the browser's developer tools can read it.\n * A value the reader must not have belongs hidden in OpenRegister (property\n * authorization), not masked here.\n *\n * The column's `formatterOptions.visible` sets how many characters stay\n * (default 4); at most five `*` are drawn, so the length does not leak.\n * Null-safe: `''` for null or empty.\n *\n * @param {unknown} value The value.\n * @param {object} [_row] The full row (unused).\n * @param {object} [_property] The schema property (unused).\n * @param {{visible?: number}} [options] The column's `formatterOptions`.\n * @return {string} The masked value.\n * @spec openspec/changes/masked-formatter-max-aggregate/specs/index-page/spec.md#requirement-a-column-can-mask-its-value-for-display\n */\nexport function formatMasked(value, _row, _property, options) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst text = String(value)\n\tconst visible = Number.isInteger(options && options.visible) && options.visible >= 0 ? options.visible : 4\n\tif (text.length <= visible) {\n\t\treturn text\n\t}\n\treturn '*'.repeat(Math.min(text.length - visible, 5)) + (visible > 0 ? text.slice(-visible) : '')\n}\n\n/**\n * Built-in formatter registry merged under any consumer-registered\n * `formatters` in `CnAppRoot`'s `cnFormatters` provide.\n */\nexport const BUILT_IN_FORMATTERS = {\n\tdate: formatDate,\n\tdatetime: formatDateTime,\n\t'relative-time': formatRelativeTime,\n\tdaysSince: formatDaysSince,\n\tdaysUntil: formatDaysUntil,\n\trelativeDate: formatRelativeDay,\n\tcurrency: formatCurrency,\n\tconditionalPhrase: formatConditionalPhrase,\n\tcount: formatCount,\n\tconnectionStatus: formatConnectionStatus,\n\tconnectionSettingsLabel: formatConnectionSettingsLabel,\n\tmasked: formatMasked,\n}\n"],"names":["t","n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"builtInFormatters.js","sources":["../../../src/utils/builtInFormatters.js"],"sourcesContent":["import { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { formatRelativeDate } from './boardDate.js'\nimport { formatMoney } from './formatMetric.js'\n\n/**\n * @file Built-in formatters merged into the `cnFormatters` registry\n * `CnAppRoot` provides to descendants.\n *\n * Resolution order (in `CnCellRenderer`):\n * 1. Consumer-registered `formatters[id]` (passed to CnAppRoot).\n * 2. Built-in `BUILT_IN_FORMATTERS[id]` from this file.\n * 3. Schema-type-aware rendering.\n * 4. Plain `formatValue()` fallback.\n *\n * Consumer formatters win (CnAppRoot spreads\n * `{ ...BUILT_IN_FORMATTERS, ...props.formatters }`).\n *\n * A formatter is invoked as `fn(value, row, property, options)` — `options`\n * is the column's declarative `formatterOptions` map (undefined when the\n * column declares none), so config-driven formatters (`currency`,\n * `conditionalPhrase`) work without one registry function per configuration.\n * The fourth argument is additive: pre-existing three-argument formatters\n * are unaffected.\n *\n * Every built-in MUST be safe against null / empty / non-parseable\n * inputs — the contract is \"return the original value (or empty\n * string) rather than throw\".\n */\n\n/**\n * Parse a value that's meant to be a date — accepts a `Date` instance,\n * an ISO/parseable string, or a numeric timestamp.\n *\n * @param {Date|string|number|null|undefined} value The candidate date value.\n * @return {Date|null} A `Date` instance, or `null` if the input is null/empty/unparseable.\n */\nfunction toDate(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn null\n\t}\n\tif (value instanceof Date) {\n\t\treturn Number.isNaN(value.getTime()) ? null : value\n\t}\n\tconst d = new Date(value)\n\treturn Number.isNaN(d.getTime()) ? null : d\n}\n\n/**\n * Locale-formatted date (no time). Backed by `Intl.DateTimeFormat`\n * (`dateStyle: 'medium'`) using the user-agent locale.\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @return {string} Formatted date, or `''` for null/empty, or `String(value)` for unparseable.\n */\nexport function formatDate(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst d = toDate(value)\n\tif (!d) {\n\t\treturn String(value)\n\t}\n\treturn new Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(d)\n}\n\n/**\n * Locale-formatted date + time. Backed by `Intl.DateTimeFormat`\n * (`dateStyle: 'medium'`, `timeStyle: 'short'`).\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @return {string} Formatted date + time, or `''` for null/empty, or `String(value)` for unparseable.\n */\nexport function formatDateTime(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst d = toDate(value)\n\tif (!d) {\n\t\treturn String(value)\n\t}\n\treturn new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }).format(d)\n}\n\n/**\n * Relative time (\"3 days ago\") via `Intl.RelativeTimeFormat`.\n * Picks the coarsest unit whose absolute delta exceeds one unit,\n * down to minutes (anything sub-minute clamps to \"now\"/seconds).\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @return {string} Relative phrasing, or `''` for null/empty, or `String(value)` for unparseable.\n */\nexport function formatRelativeTime(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst d = toDate(value)\n\tif (!d) {\n\t\treturn String(value)\n\t}\n\tconst diffMs = d.getTime() - Date.now()\n\tconst rtf = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' })\n\tconst units = [\n\t\t['year', 31536000000],\n\t\t['month', 2592000000],\n\t\t['week', 604800000],\n\t\t['day', 86400000],\n\t\t['hour', 3600000],\n\t\t['minute', 60000],\n\t]\n\tfor (const [unit, ms] of units) {\n\t\tif (Math.abs(diffMs) >= ms || unit === 'minute') {\n\t\t\treturn rtf.format(Math.round(diffMs / ms), unit)\n\t\t}\n\t}\n\treturn rtf.format(0, 'second')\n}\n\n/**\n * Whole-day difference between a date value and today, comparing\n * date-only (both clamped to local midnight) so a due date later today\n * still counts as \"today\", not \"in 0.4 days\".\n *\n * @param {Date} d The parsed date.\n * @return {number} Signed day count: positive = future, negative = past, 0 = today.\n */\nfunction dayDiffFromToday(d) {\n\tconst target = new Date(d)\n\ttarget.setHours(0, 0, 0, 0)\n\tconst today = new Date()\n\ttoday.setHours(0, 0, 0, 0)\n\treturn Math.round((target.getTime() - today.getTime()) / 86400000)\n}\n\n/**\n * Future-oriented deadline formatter (`daysUntil` registry key), i18n'd\n * through the library's own translation slug (`nextcloud-vue`):\n * a future date renders \"N days remaining\", today renders \"Due today\",\n * and a past date renders \"N days overdue\". Day counts are plural-aware\n * via `translatePlural`.\n *\n * Null-safe per the built-in-formatter contract: `''` for null/empty,\n * `String(value)` for unparseable input — never throws.\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @return {string} The relative-day phrasing (or ''/original on bad input).\n */\nexport function formatDaysUntil(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst d = toDate(value)\n\tif (!d) {\n\t\treturn String(value)\n\t}\n\tconst days = dayDiffFromToday(d)\n\tif (days === 0) {\n\t\treturn t('nextcloud-vue', 'Due today')\n\t}\n\tif (days > 0) {\n\t\treturn n('nextcloud-vue', '{count} day remaining', '{count} days remaining', days, { count: days })\n\t}\n\tconst overdue = Math.abs(days)\n\treturn n('nextcloud-vue', '{count} day overdue', '{count} days overdue', overdue, { count: overdue })\n}\n\n/**\n * Elapsed-time formatter (`daysSince` registry key), i18n'd through the\n * library's own translation slug (`nextcloud-vue`) in the Nextcloud user\n * language. Whole calendar days, read as a running phrase so it sits after a\n * title or in a meta line: today renders \"today\", one day back renders\n * \"yesterday\", further back renders \"N days ago\". A (nonsensical but\n * harmless) future date renders \"tomorrow\" or \"in N days\". Day counts are\n * plural-aware via `translatePlural`.\n *\n * Null-safe per the built-in-formatter contract: `''` for null/empty,\n * `String(value)` for unparseable input, never throws.\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @return {string} The relative-day phrasing (or ''/original on bad input).\n */\nexport function formatDaysSince(value) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst d = toDate(value)\n\tif (!d) {\n\t\treturn String(value)\n\t}\n\tconst days = dayDiffFromToday(d)\n\tif (days === 0) {\n\t\treturn t('nextcloud-vue', 'today')\n\t}\n\tif (days === -1) {\n\t\treturn t('nextcloud-vue', 'yesterday')\n\t}\n\tif (days === 1) {\n\t\treturn t('nextcloud-vue', 'tomorrow')\n\t}\n\tif (days < 0) {\n\t\tconst ago = Math.abs(days)\n\t\treturn n('nextcloud-vue', '{count} day ago', '{count} days ago', ago, { count: ago })\n\t}\n\treturn n('nextcloud-vue', 'in {count} day', 'in {count} days', days, { count: days })\n}\n\n/**\n * Relative-date formatter (`relativeDate` registry key): \"today\", \"yesterday\",\n * then the board's short date (\"5 okt\"). The column's `formatterOptions` may set\n * `time: true` to add the time of day after the day (\"vandaag, 09.42\").\n *\n * Null-safe per the built-in-formatter contract: `''` for null/empty,\n * `String(value)` for unparseable input, never throws.\n *\n * @param {unknown} value A `Date`, parseable date string, or timestamp.\n * @param {object} [_row] The full row (unused).\n * @param {object} [_property] The schema property (unused).\n * @param {{time?: boolean}} [options] The column's `formatterOptions`.\n * @return {string} The relative-day phrasing (or ''/original on bad input).\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 formatRelativeDay(value, _row, _property, options) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst text = formatRelativeDate(value, { withTime: options?.time === true })\n\treturn text === '' ? String(value) : text\n}\n\n/**\n * Currency formatter (`currency` registry key) via `Intl.NumberFormat`.\n * Defaults to EUR; the column's `formatterOptions` may set `currency`\n * (ISO-4217 code, guarded by `safeCurrencyCode` so an invalid code can\n * never throw a `RangeError`) and `decimals` (default 2).\n *\n * Null-safe per the built-in-formatter contract: `''` for null/empty,\n * `String(value)` for non-numeric input — never throws.\n *\n * @param {unknown} value A numeric value (or numeric string).\n * @param {object} [row] The full row, for `currencyField`.\n * @param {object} [_property] The schema property (unused).\n * @param {{currency?: string, currencyField?: string, decimals?: number}} [options] The column's `formatterOptions`.\n * @return {string} The locale currency string (or ''/original on bad input).\n */\nexport function formatCurrency(value, row, _property, options) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst num = Number(value)\n\tif (!Number.isFinite(num)) {\n\t\treturn String(value)\n\t}\n\treturn formatMoney(num, options || {}, row)\n}\n\n/**\n * Sign/zero-based phrase selector (`conditionalPhrase` registry key) —\n * generalizes `daysUntil` to ANY numeric field: the column's\n * `formatterOptions` supply pre-translated `{ negative, zero, positive }`\n * phrases and the matching one renders with `{n}` replaced by the ABSOLUTE\n * value (so \"{n} days overdue\" reads naturally for -3). Phrases are\n * consumer-provided strings — apps translate them before declaring the\n * column, keeping i18n in the app layer.\n *\n * Null-safe per the built-in-formatter contract: `''` for null/empty,\n * `String(value)` for non-numeric input or when the selected phrase is\n * missing — never throws.\n *\n * @param {unknown} value A numeric value (or numeric string).\n * @param {object} [_row] The full row (unused).\n * @param {object} [_property] The schema property (unused).\n * @param {{negative?: string, zero?: string, positive?: string}} [options] The column's `formatterOptions`.\n * @return {string} The selected phrase with `{n}` substituted (or ''/original on bad input).\n */\nexport function formatConditionalPhrase(value, _row, _property, options) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst num = Number(value)\n\tif (!Number.isFinite(num)) {\n\t\treturn String(value)\n\t}\n\tconst opts = options || {}\n\tconst phrase = num < 0 ? opts.negative : (num > 0 ? opts.positive : opts.zero)\n\tif (typeof phrase !== 'string' || phrase === '') {\n\t\treturn String(value)\n\t}\n\treturn phrase.replace(/\\{n\\}/g, String(Math.abs(num)))\n}\n\n/**\n * Read a value that may be a collection SERIALISED as JSON back into the\n * collection itself. OpenRegister hands a stringified array back for some\n * property types (and an app's own import path may persist one), which\n * `formatCount` would otherwise report as a single entry.\n *\n * Only `[`/`{`-leading strings are tried, so an ordinary text value never pays\n * for a parse attempt, and a string that merely looks like JSON but isn't falls\n * back to being one entry.\n *\n * @param {unknown} value The raw cell value.\n * @return {unknown} The parsed collection, or `value` unchanged.\n */\nfunction parseCollection(value) {\n\tif (typeof value !== 'string') {\n\t\treturn value\n\t}\n\tconst trimmed = value.trim()\n\tif (trimmed[0] !== '[' && trimmed[0] !== '{') {\n\t\treturn value\n\t}\n\ttry {\n\t\tconst parsed = JSON.parse(trimmed)\n\t\treturn (parsed !== null && typeof parsed === 'object') ? parsed : value\n\t} catch {\n\t\treturn value\n\t}\n}\n\n/**\n * Entry-count summariser (`count` registry key) — renders a collection-valued\n * cell as \"5 frames\" instead of the truncated JSON blob `formatValue` would\n * produce. Counts array entries or object keys; a PRESENT scalar counts as 1.\n * The column's `formatterOptions` supply pre-translated\n * `{ singular, plural, zero }` phrases with `{n}` substituted, following the\n * `conditionalPhrase` convention of keeping i18n in the app layer. Without\n * phrases it renders the bare count.\n *\n * Null-safe per the built-in-formatter contract: `zero` (or `''`) for\n * null/empty — never throws.\n *\n * @param {unknown} value An array, object, JSON-encoded collection, or scalar.\n * @param {object} [_row] The full row (unused).\n * @param {object} [_property] The schema property (unused).\n * @param {{singular?: string, plural?: string, zero?: string}} [options] The column's `formatterOptions`.\n * @return {string} The selected phrase with `{n}` substituted, or the bare count.\n */\nexport function formatCount(value, _row, _property, options) {\n\tconst opts = options || {}\n\tconst zero = typeof opts.zero === 'string' ? opts.zero : ''\n\t// Every EMPTY value takes the zero phrase, not just null/''. `0` and `false`\n\t// used to fall through to the scalar branch and count as one entry, so a\n\t// column over a `0` rendered \"1 retry\" — the singular of a thing that isn't\n\t// there.\n\tif (value === null || value === undefined || value === '' || value === 0 || value === false) {\n\t\treturn zero\n\t}\n\tconst collection = parseCollection(value)\n\tlet n\n\tif (Array.isArray(collection)) {\n\t\tn = collection.length\n\t} else if (typeof collection === 'object') {\n\t\tn = Object.keys(collection).length\n\t} else {\n\t\tn = 1\n\t}\n\tif (n === 0) {\n\t\treturn zero\n\t}\n\tconst phrase = n === 1 ? (opts.singular ?? opts.plural) : (opts.plural ?? opts.singular)\n\tif (typeof phrase !== 'string' || phrase === '') {\n\t\treturn String(n)\n\t}\n\treturn phrase.replace(/\\{n\\}/g, String(n))\n}\n\n/**\n * The seven stored connection status values and their English source labels.\n *\n * The label is translated on each call, not here. A module-level `t()` runs\n * before the catalogue is registered and would freeze every label in English.\n */\nconst CONNECTION_STATUS_LABELS = {\n\tconfigured: 'Configured',\n\tlimited: 'Limited',\n\tunconfigured: 'Not configured',\n\tsimulated: 'Simulated',\n\tdisabled: 'Switched off',\n\tunavailable: 'Not available',\n\terror: 'Error',\n}\n\n/**\n * The short labels `formatterOptions.short` draws (board DqInstellingen\n * \"Aan / Uit / Fout\"): a connection that works in some form is on, one that\n * does not run is off, and a failing one is an error.\n */\nconst CONNECTION_STATUS_SHORT_LABELS = {\n\tconfigured: 'On',\n\tlimited: 'On',\n\tsimulated: 'On',\n\tunconfigured: 'Off',\n\tdisabled: 'Off',\n\tunavailable: 'Off',\n\terror: 'Error',\n}\n\n/**\n * Connection status formatter (`connectionStatus` registry key) for the\n * fleet connection registry. Renders one of the seven stored status values\n * as its translated label through the library's own slug (`nextcloud-vue`).\n * With `formatterOptions.short` it renders the board's three short labels\n * instead: On (configured, limited, simulated), Off (not configured,\n * switched off, not available) and Error.\n *\n * An unknown value passes through unchanged, so a status added later still\n * shows as its raw value instead of an empty cell. Null and undefined render\n * as `''`. Never throws.\n *\n * @param {unknown} value The stored status value.\n * @param {object} [row] The row (unused).\n * @param {object} [property] The column's property (unused).\n * @param {{short?: boolean}} [options] The column's `formatterOptions`.\n * @return {string} The translated label, or the value itself when it is not one of the seven.\n */\nexport function formatConnectionStatus(value, row, property, options) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tconst labels = options && options.short === true ? CONNECTION_STATUS_SHORT_LABELS : CONNECTION_STATUS_LABELS\n\tconst label = Object.hasOwn(labels, value) ? labels[value] : null\n\treturn label ? t('nextcloud-vue', label) : String(value)\n}\n\n/**\n * Connection settings label formatter (`connectionSettingsLabel` registry\n * key). Renders \"Open settings\" when the row's `settingsUrl` is a non-empty\n * string, and `''` otherwise. Paired with the built-in `link` cell widget, an\n * empty label and href fall through to plain empty text, so a connection with\n * no settings section offers nothing to click.\n *\n * @param {unknown} value The row's `settingsUrl`.\n * @return {string} The translated link text, or `''`.\n */\nexport function formatConnectionSettingsLabel(value) {\n\treturn typeof value === 'string' && value !== ''\n\t\t? t('nextcloud-vue', 'Open settings')\n\t\t: ''\n}\n\n/**\n * Show a value with all but its last characters replaced by `*`, as the facts\n * widget's `masked` format does: an IBAN or a BSN reads `*****4567`.\n *\n * DISPLAY ONLY, NOT ACCESS CONTROL. The full value is still in the row the\n * page received; a reader with the browser's developer tools can read it.\n * A value the reader must not have belongs hidden in OpenRegister (property\n * authorization), not masked here.\n *\n * The column's `formatterOptions.visible` sets how many characters stay\n * (default 4); at most five `*` are drawn, so the length does not leak.\n * Null-safe: `''` for null or empty.\n *\n * @param {unknown} value The value.\n * @param {object} [_row] The full row (unused).\n * @param {object} [_property] The schema property (unused).\n * @param {{visible?: number}} [options] The column's `formatterOptions`.\n * @return {string} The masked value.\n * @spec openspec/changes/masked-formatter-max-aggregate/specs/index-page/spec.md#requirement-a-column-can-mask-its-value-for-display\n */\nexport function formatMasked(value, _row, _property, options) {\n\tif (value === null || value === undefined || value === '') {\n\t\treturn ''\n\t}\n\tconst text = String(value)\n\tconst visible = Number.isInteger(options && options.visible) && options.visible >= 0 ? options.visible : 4\n\tif (text.length <= visible) {\n\t\treturn text\n\t}\n\treturn '*'.repeat(Math.min(text.length - visible, 5)) + (visible > 0 ? text.slice(-visible) : '')\n}\n\n/**\n * Built-in formatter registry merged under any consumer-registered\n * `formatters` in `CnAppRoot`'s `cnFormatters` provide.\n */\nexport const BUILT_IN_FORMATTERS = {\n\tdate: formatDate,\n\tdatetime: formatDateTime,\n\t'relative-time': formatRelativeTime,\n\tdaysSince: formatDaysSince,\n\tdaysUntil: formatDaysUntil,\n\trelativeDate: formatRelativeDay,\n\tcurrency: formatCurrency,\n\tconditionalPhrase: formatConditionalPhrase,\n\tcount: formatCount,\n\tconnectionStatus: formatConnectionStatus,\n\tconnectionSettingsLabel: formatConnectionSettingsLabel,\n\tmasked: formatMasked,\n}\n"],"names":["t","n"],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { getLanguage } from '@nextcloud/l10n';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
5
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The user's Nextcloud language as a bare code (`nl_NL` reads `nl`), or ''.
|
|
11
|
+
*
|
|
12
|
+
* @return {string} The code.
|
|
13
|
+
*/
|
|
14
|
+
function userLanguage() {
|
|
15
|
+
try {
|
|
16
|
+
return String(getLanguage() || '').split(/[-_]/)[0].toLowerCase()
|
|
17
|
+
} catch {
|
|
18
|
+
return ''
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The text of a value that may be a language map (`{ nl: 'Ontvangst', en:
|
|
24
|
+
* 'Intake' }`), in the user's Nextcloud language, then Dutch, then English,
|
|
25
|
+
* then the first text it holds. A plain value comes back as text; anything
|
|
26
|
+
* else as ''.
|
|
27
|
+
*
|
|
28
|
+
* @param {(string|number|object|null|undefined)} value The value.
|
|
29
|
+
* @return {string} The text.
|
|
30
|
+
* @spec openspec/changes/grouped-cards-widget/specs/grouped-cards-widget/spec.md#requirement-a-widget-draws-groups-of-cards-from-an-endpoint
|
|
31
|
+
*/
|
|
32
|
+
function languageMapText(value) {
|
|
33
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
34
|
+
return String(value)
|
|
35
|
+
}
|
|
36
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
37
|
+
return ''
|
|
38
|
+
}
|
|
39
|
+
const language = userLanguage();
|
|
40
|
+
for (const key of [language, 'nl', 'en']) {
|
|
41
|
+
if (key && typeof value[key] === 'string' && value[key] !== '') {
|
|
42
|
+
return value[key]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const first = Object.values(value).find((text) => typeof text === 'string' && text !== '');
|
|
46
|
+
return first || ''
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { languageMapText };
|
|
50
|
+
//# sourceMappingURL=languageMapText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"languageMapText.js","sources":["../../../src/utils/languageMapText.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\n\nimport { getLanguage } from '@nextcloud/l10n'\n\n/**\n * The user's Nextcloud language as a bare code (`nl_NL` reads `nl`), or ''.\n *\n * @return {string} The code.\n */\nfunction userLanguage() {\n\ttry {\n\t\treturn String(getLanguage() || '').split(/[-_]/)[0].toLowerCase()\n\t} catch {\n\t\treturn ''\n\t}\n}\n\n/**\n * The text of a value that may be a language map (`{ nl: 'Ontvangst', en:\n * 'Intake' }`), in the user's Nextcloud language, then Dutch, then English,\n * then the first text it holds. A plain value comes back as text; anything\n * else as ''.\n *\n * @param {(string|number|object|null|undefined)} value The value.\n * @return {string} The text.\n * @spec openspec/changes/grouped-cards-widget/specs/grouped-cards-widget/spec.md#requirement-a-widget-draws-groups-of-cards-from-an-endpoint\n */\nexport function languageMapText(value) {\n\tif (typeof value === 'string' || typeof value === 'number') {\n\t\treturn String(value)\n\t}\n\tif (!value || typeof value !== 'object' || Array.isArray(value)) {\n\t\treturn ''\n\t}\n\tconst language = userLanguage()\n\tfor (const key of [language, 'nl', 'en']) {\n\t\tif (key && typeof value[key] === 'string' && value[key] !== '') {\n\t\t\treturn value[key]\n\t\t}\n\t}\n\tconst first = Object.values(value).find((text) => typeof text === 'string' && text !== '')\n\treturn first || ''\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,YAAY,GAAG;AACxB,CAAC,IAAI;AACL,EAAE,OAAO,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;AACnE,EAAE,CAAC,MAAM;AACT,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,KAAK,EAAE;AACvC,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC7D,EAAE,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,EAAE;AACF,CAAC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;AAClE,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,MAAM,QAAQ,GAAG,YAAY,GAAE;AAChC,CAAC,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE;AAC3C,EAAE,IAAI,GAAG,IAAI,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE;AAClE,GAAG,OAAO,KAAK,CAAC,GAAG,CAAC;AACpB,GAAG;AACH,EAAE;AACF,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE,EAAC;AAC3F,CAAC,OAAO,KAAK,IAAI,EAAE;AACnB;;;;"}
|
|
@@ -72,6 +72,8 @@ const BUILT_IN_WIDGET_KEYS = Object.freeze([
|
|
|
72
72
|
'header',
|
|
73
73
|
'text',
|
|
74
74
|
'divider',
|
|
75
|
+
'saved-view-alert',
|
|
76
|
+
'exports',
|
|
75
77
|
]);
|
|
76
78
|
|
|
77
79
|
/**
|
|
@@ -110,6 +112,7 @@ const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([
|
|
|
110
112
|
'files',
|
|
111
113
|
'flow-runs',
|
|
112
114
|
'gauge',
|
|
115
|
+
'grouped-cards',
|
|
113
116
|
'header',
|
|
114
117
|
'image',
|
|
115
118
|
'interaction-form',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libraryWidgetKeys.js","sources":["../../../src/utils/libraryWidgetKeys.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * libraryWidgetKeys — the one vocabulary of widget keys the LIBRARY itself\n * renders, and the only list `validateManifestV2` is allowed to consult.\n *\n * WHY THIS MODULE EXISTS\n * ----------------------\n * The validator used to carry its own hand-written array of \"library built-in\n * widget keys\", with a comment instructing whoever added a built-in to append\n * it \"in the same PR\". That instruction was followed for five keys and missed\n * for six, so the validator knew 11 keys while the library actually rendered\n * 45. Nothing failed when the two disagreed, because the only consumer is an\n * exemption: a key the validator does not recognise is treated as a CUSTOM\n * registry component, which makes a legitimate single-widget library dashboard\n * fail the ADR-036 Decision 1 anti-pattern rule with advice (\"declare it as\n * type:'custom'\") that is wrong for a library widget.\n *\n * A comment cannot enforce anything, so the array moved here and\n * `tests/utils/libraryWidgetKeys.spec.js` now binds it to the two registries\n * that actually resolve a key at runtime:\n *\n * 1. `BUILT_IN_WIDGETS` (components/CnWidgetGrid/builtInWidgets.js) — the v2\n * manifest widget set `CnWidgetGrid` resolves a `widgetKey` against.\n * 2. The dashboard widget catalog (`dashboardWidgetRegistry`), populated by\n * `registerDashboardWidgets.js`. `CnWidgetGrid` falls back to it (third in\n * the chain, after the consumer registry and `BUILT_IN_WIDGETS`), so a\n * catalog key renders on a v2 page exactly like a `BUILT_IN_WIDGETS` one.\n *\n * The test asserts SET EQUALITY in both directions against the live registries,\n * so adding a widget to either registry without touching this file fails CI,\n * and deleting one without removing it here fails too. The lists below are the\n * validator's view; the registries are the truth the test holds them to.\n *\n * THE CATALOG IS NOT LEGACY. It is a live half of the same vocabulary, and the\n * old array already conceded the point by exempting `chart` and `stats-block`,\n * which are catalog-only keys that `BUILT_IN_WIDGETS` has never held. What it\n * got wrong was doing that for two keys and not the other thirty-seven.\n * `widgetDispatch.js` unified the two resolution paths for the same reason: a\n * widget authored against one vocabulary was invisible to the other and said\n * nothing about why (101 of 236 detail widgets on hrmq).\n *\n * @module utils/libraryWidgetKeys\n */\n\nimport { WIDGET_TYPE_ALIASES } from './widgetTypeAliases.js'\n\n/**\n * Keys of `BUILT_IN_WIDGETS` — the v2 manifest widget set.\n *\n * Kept in the registry's own declaration order so a diff against\n * `builtInWidgets.js` reads straight down.\n *\n * @type {string[]} Frozen.\n */\nexport const BUILT_IN_WIDGET_KEYS = Object.freeze([\n\t'object-table',\n\t'form-renderer',\n\t'map-viewer',\n\t'object-geo',\n\t'card-grid',\n\t'nav-card-grid',\n\t'data',\n\t'metadata',\n\t'related',\n\t'integration',\n\t'banner',\n\t'audit-trail',\n\t'timeline',\n\t'header',\n\t'text',\n\t'divider',\n])\n\n/**\n * Widget types the LIBRARY registers into the dashboard catalog at import time\n * (`registerDashboardWidgets.js` and the widget `index.js` modules it pulls in).\n *\n * Deliberately a static list rather than a read of `dashboardWidgetRegistry`:\n * that object is mutable and consumer-extensible, so deriving the exemption\n * from it at call time would let an app register its own full-page component in\n * the catalog and walk straight through the ADR-036 Decision 1 rule the\n * exemption exists to enforce. Only keys the library itself ships are exempt.\n *\n * Sorted, because unlike `BUILT_IN_WIDGETS` these registrations are spread over\n * ~30 files and have no meaningful declaration order.\n *\n * @type {string[]} Frozen.\n */\nexport const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([\n\t'audit-trail',\n\t'banner',\n\t'calendar',\n\t'chart',\n\t'checklist-run',\n\t'chips-field',\n\t'choice-picker',\n\t'container',\n\t'conversation',\n\t'countdown',\n\t'data',\n\t'delta',\n\t'device-frames',\n\t'divider',\n\t'document-review',\n\t'facts',\n\t'field-list',\n\t'files',\n\t'flow-runs',\n\t'gauge',\n\t'header',\n\t'image',\n\t'interaction-form',\n\t'kb-search',\n\t'label',\n\t'link',\n\t'links',\n\t'map',\n\t'markdown',\n\t'menu',\n\t'nc-widget',\n\t'next-step',\n\t'notepad',\n\t// `news` was here until the widget moved to LaunchPad, which owns the feed\n\t// fetching. An app that renders one now gets it from LaunchPad's registry.\n\t'object-geo',\n\t'object-list',\n\t'object-table',\n\t'party-actions',\n\t'people',\n\t'quicklinks',\n\t'radio-cards',\n\t'record-list',\n\t'related',\n\t'saved-view',\n\t'saved-views',\n\t'sections',\n\t'settings-form',\n\t'spend-analytics',\n\t'stacked-bar',\n\t'stages',\n\t'stat',\n\t'stats-block',\n\t'switch-rows',\n\t'table',\n\t'tabs',\n\t'tasks',\n\t'text',\n\t'tile',\n\t'timeline',\n\t'toggle-table',\n\t'video',\n\t'week-strip',\n\t'working-calendar',\n\t'workspace-filter',\n])\n\n/**\n * Expand a key set over `WIDGET_TYPE_ALIASES` so both spellings of an aliased\n * widget are present whenever either one is.\n *\n * `CnWidgetGrid` resolves a key and its canonical form (`object-table` ⇄\n * `table`, `map-viewer` ⇄ `map`), so both spellings render and both must be\n * exempt. Today each registry happens to hold one side of each pair, which is\n * exactly the accident that would silently stop being true when the next alias\n * lands.\n *\n * @param {Set<string>} keys The key set to expand, mutated in place.\n * @return {Set<string>} The same set.\n */\nfunction expandAliases(keys) {\n\tfor (const [alias, canonical] of Object.entries(WIDGET_TYPE_ALIASES)) {\n\t\tif (keys.has(alias) || keys.has(canonical)) {\n\t\t\tkeys.add(alias)\n\t\t\tkeys.add(canonical)\n\t\t}\n\t}\n\treturn keys\n}\n\n/**\n * Every widget key the library renders itself: both registries, plus both\n * spellings of every alias.\n *\n * A `widgetKey` in this list is served by a library `Cn*` SFC and does NOT\n * count as a custom registry component for the single-12×12-widget dashboard\n * rule (ADR-036 Decision 1).\n *\n * @type {string[]} Frozen.\n */\nexport const LIBRARY_WIDGET_KEYS = Object.freeze([\n\t...expandAliases(new Set([\n\t\t...BUILT_IN_WIDGET_KEYS,\n\t\t...DASHBOARD_CATALOG_WIDGET_KEYS,\n\t])),\n])\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"libraryWidgetKeys.js","sources":["../../../src/utils/libraryWidgetKeys.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * libraryWidgetKeys — the one vocabulary of widget keys the LIBRARY itself\n * renders, and the only list `validateManifestV2` is allowed to consult.\n *\n * WHY THIS MODULE EXISTS\n * ----------------------\n * The validator used to carry its own hand-written array of \"library built-in\n * widget keys\", with a comment instructing whoever added a built-in to append\n * it \"in the same PR\". That instruction was followed for five keys and missed\n * for six, so the validator knew 11 keys while the library actually rendered\n * 45. Nothing failed when the two disagreed, because the only consumer is an\n * exemption: a key the validator does not recognise is treated as a CUSTOM\n * registry component, which makes a legitimate single-widget library dashboard\n * fail the ADR-036 Decision 1 anti-pattern rule with advice (\"declare it as\n * type:'custom'\") that is wrong for a library widget.\n *\n * A comment cannot enforce anything, so the array moved here and\n * `tests/utils/libraryWidgetKeys.spec.js` now binds it to the two registries\n * that actually resolve a key at runtime:\n *\n * 1. `BUILT_IN_WIDGETS` (components/CnWidgetGrid/builtInWidgets.js) — the v2\n * manifest widget set `CnWidgetGrid` resolves a `widgetKey` against.\n * 2. The dashboard widget catalog (`dashboardWidgetRegistry`), populated by\n * `registerDashboardWidgets.js`. `CnWidgetGrid` falls back to it (third in\n * the chain, after the consumer registry and `BUILT_IN_WIDGETS`), so a\n * catalog key renders on a v2 page exactly like a `BUILT_IN_WIDGETS` one.\n *\n * The test asserts SET EQUALITY in both directions against the live registries,\n * so adding a widget to either registry without touching this file fails CI,\n * and deleting one without removing it here fails too. The lists below are the\n * validator's view; the registries are the truth the test holds them to.\n *\n * THE CATALOG IS NOT LEGACY. It is a live half of the same vocabulary, and the\n * old array already conceded the point by exempting `chart` and `stats-block`,\n * which are catalog-only keys that `BUILT_IN_WIDGETS` has never held. What it\n * got wrong was doing that for two keys and not the other thirty-seven.\n * `widgetDispatch.js` unified the two resolution paths for the same reason: a\n * widget authored against one vocabulary was invisible to the other and said\n * nothing about why (101 of 236 detail widgets on hrmq).\n *\n * @module utils/libraryWidgetKeys\n */\n\nimport { WIDGET_TYPE_ALIASES } from './widgetTypeAliases.js'\n\n/**\n * Keys of `BUILT_IN_WIDGETS` — the v2 manifest widget set.\n *\n * Kept in the registry's own declaration order so a diff against\n * `builtInWidgets.js` reads straight down.\n *\n * @type {string[]} Frozen.\n */\nexport const BUILT_IN_WIDGET_KEYS = Object.freeze([\n\t'object-table',\n\t'form-renderer',\n\t'map-viewer',\n\t'object-geo',\n\t'card-grid',\n\t'nav-card-grid',\n\t'data',\n\t'metadata',\n\t'related',\n\t'integration',\n\t'banner',\n\t'audit-trail',\n\t'timeline',\n\t'header',\n\t'text',\n\t'divider',\n\t'saved-view-alert',\n\t'exports',\n])\n\n/**\n * Widget types the LIBRARY registers into the dashboard catalog at import time\n * (`registerDashboardWidgets.js` and the widget `index.js` modules it pulls in).\n *\n * Deliberately a static list rather than a read of `dashboardWidgetRegistry`:\n * that object is mutable and consumer-extensible, so deriving the exemption\n * from it at call time would let an app register its own full-page component in\n * the catalog and walk straight through the ADR-036 Decision 1 rule the\n * exemption exists to enforce. Only keys the library itself ships are exempt.\n *\n * Sorted, because unlike `BUILT_IN_WIDGETS` these registrations are spread over\n * ~30 files and have no meaningful declaration order.\n *\n * @type {string[]} Frozen.\n */\nexport const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([\n\t'audit-trail',\n\t'banner',\n\t'calendar',\n\t'chart',\n\t'checklist-run',\n\t'chips-field',\n\t'choice-picker',\n\t'container',\n\t'conversation',\n\t'countdown',\n\t'data',\n\t'delta',\n\t'device-frames',\n\t'divider',\n\t'document-review',\n\t'facts',\n\t'field-list',\n\t'files',\n\t'flow-runs',\n\t'gauge',\n\t'grouped-cards',\n\t'header',\n\t'image',\n\t'interaction-form',\n\t'kb-search',\n\t'label',\n\t'link',\n\t'links',\n\t'map',\n\t'markdown',\n\t'menu',\n\t'nc-widget',\n\t'next-step',\n\t'notepad',\n\t// `news` was here until the widget moved to LaunchPad, which owns the feed\n\t// fetching. An app that renders one now gets it from LaunchPad's registry.\n\t'object-geo',\n\t'object-list',\n\t'object-table',\n\t'party-actions',\n\t'people',\n\t'quicklinks',\n\t'radio-cards',\n\t'record-list',\n\t'related',\n\t'saved-view',\n\t'saved-views',\n\t'sections',\n\t'settings-form',\n\t'spend-analytics',\n\t'stacked-bar',\n\t'stages',\n\t'stat',\n\t'stats-block',\n\t'switch-rows',\n\t'table',\n\t'tabs',\n\t'tasks',\n\t'text',\n\t'tile',\n\t'timeline',\n\t'toggle-table',\n\t'video',\n\t'week-strip',\n\t'working-calendar',\n\t'workspace-filter',\n])\n\n/**\n * Expand a key set over `WIDGET_TYPE_ALIASES` so both spellings of an aliased\n * widget are present whenever either one is.\n *\n * `CnWidgetGrid` resolves a key and its canonical form (`object-table` ⇄\n * `table`, `map-viewer` ⇄ `map`), so both spellings render and both must be\n * exempt. Today each registry happens to hold one side of each pair, which is\n * exactly the accident that would silently stop being true when the next alias\n * lands.\n *\n * @param {Set<string>} keys The key set to expand, mutated in place.\n * @return {Set<string>} The same set.\n */\nfunction expandAliases(keys) {\n\tfor (const [alias, canonical] of Object.entries(WIDGET_TYPE_ALIASES)) {\n\t\tif (keys.has(alias) || keys.has(canonical)) {\n\t\t\tkeys.add(alias)\n\t\t\tkeys.add(canonical)\n\t\t}\n\t}\n\treturn keys\n}\n\n/**\n * Every widget key the library renders itself: both registries, plus both\n * spellings of every alias.\n *\n * A `widgetKey` in this list is served by a library `Cn*` SFC and does NOT\n * count as a custom registry component for the single-12×12-widget dashboard\n * rule (ADR-036 Decision 1).\n *\n * @type {string[]} Frozen.\n */\nexport const LIBRARY_WIDGET_KEYS = Object.freeze([\n\t...expandAliases(new Set([\n\t\t...BUILT_IN_WIDGET_KEYS,\n\t\t...DASHBOARD_CATALOG_WIDGET_KEYS,\n\t])),\n])\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;AAClD,CAAC,cAAc;AACf,CAAC,eAAe;AAChB,CAAC,YAAY;AACb,CAAC,YAAY;AACb,CAAC,WAAW;AACZ,CAAC,eAAe;AAChB,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,SAAS;AACV,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,aAAa;AACd,CAAC,UAAU;AACX,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,SAAS;AACV,CAAC,kBAAkB;AACnB,CAAC,SAAS;AACV,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,6BAA6B,GAAG,MAAM,CAAC,MAAM,CAAC;AAC3D,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,UAAU;AACX,CAAC,OAAO;AACR,CAAC,eAAe;AAChB,CAAC,aAAa;AACd,CAAC,eAAe;AAChB,CAAC,WAAW;AACZ,CAAC,cAAc;AACf,CAAC,WAAW;AACZ,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,eAAe;AAChB,CAAC,SAAS;AACV,CAAC,iBAAiB;AAClB,CAAC,OAAO;AACR,CAAC,YAAY;AACb,CAAC,OAAO;AACR,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,eAAe;AAChB,CAAC,QAAQ;AACT,CAAC,OAAO;AACR,CAAC,kBAAkB;AACnB,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,KAAK;AACN,CAAC,UAAU;AACX,CAAC,MAAM;AACP,CAAC,WAAW;AACZ,CAAC,WAAW;AACZ,CAAC,SAAS;AACV;AACA;AACA,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,cAAc;AACf,CAAC,eAAe;AAChB,CAAC,QAAQ;AACT,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,aAAa;AACd,CAAC,SAAS;AACV,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,UAAU;AACX,CAAC,eAAe;AAChB,CAAC,iBAAiB;AAClB,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,aAAa;AACd,CAAC,aAAa;AACd,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,cAAc;AACf,CAAC,OAAO;AACR,CAAC,YAAY;AACb,CAAC,kBAAkB;AACnB,CAAC,kBAAkB;AACnB,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,CAAC,IAAI,EAAE;AAC7B,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE;AACvE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;AAC9C,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAC;AAClB,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAC;AACtB,GAAG;AACH,EAAE;AACF,CAAC,OAAO,IAAI;AACZ,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC;AACjD,CAAC,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC;AAC1B,EAAE,GAAG,oBAAoB;AACzB,EAAE,GAAG,6BAA6B;AAClC,EAAE,CAAC,CAAC;AACJ,CAAC;;;;"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { isBadgeVariant } from './badgeVariants.js';
|
|
2
|
+
import { fillTemplate } from './fillTemplate.js';
|
|
3
|
+
import { readPath } from './readPath.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
7
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The numbered list beside an index map (DqZakenKaart): one entry per row
|
|
12
|
+
* that has a place on the map, numbered in list order so the number on the
|
|
13
|
+
* pin and the number in the list are the same, and the rows without one.
|
|
14
|
+
*
|
|
15
|
+
* `spec` is `mapConfig.list`: `{ title, sub?, pills?: [{ path?, text?, map?,
|
|
16
|
+
* colorMap? }], due? }`. `title`, `sub` and `due` are field templates
|
|
17
|
+
* (`"{title}"`, `"{address} · {identifier}"`, `"{deadline|date}"`), already
|
|
18
|
+
* translated by the caller; a pill reads `path` (or shows `text`), maps it
|
|
19
|
+
* through `map`, and takes its tone from `colorMap` by the raw value.
|
|
20
|
+
*
|
|
21
|
+
* @param {Array<object>} rows The rows, in display order.
|
|
22
|
+
* @param {object} spec The list declaration.
|
|
23
|
+
* @param {object} options How to read a row.
|
|
24
|
+
* @param {string} options.rowKey The row's key field.
|
|
25
|
+
* @param {Function} options.located Whether a row has a place on the map.
|
|
26
|
+
* @param {Function} [options.translate] The host's translate, for pill labels.
|
|
27
|
+
* @return {{items: Array<{key: string, n: number, row: object, title: string, sub: string, due: string, pills: Array<{label: string, variant: string}>}>, missing: Array<object>}}
|
|
28
|
+
*/
|
|
29
|
+
function mapListItems(rows, spec, { rowKey, located, translate = (text) => text }) {
|
|
30
|
+
const items = [];
|
|
31
|
+
const missing = [];
|
|
32
|
+
const list = spec && typeof spec === 'object' ? spec : {};
|
|
33
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
34
|
+
if (!located(row)) {
|
|
35
|
+
missing.push(row);
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
const pills = (Array.isArray(list.pills) ? list.pills : [])
|
|
39
|
+
.map((pill) => {
|
|
40
|
+
if (!pill || typeof pill !== 'object') {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
const raw = typeof pill.path === 'string' && pill.path !== '' ? readPath(row, pill.path) : undefined;
|
|
44
|
+
const mapped = pill.map && (typeof raw === 'string' || typeof raw === 'number') && Object.hasOwn(pill.map, raw) ? pill.map[raw] : undefined;
|
|
45
|
+
const text = mapped ?? pill.text ?? raw;
|
|
46
|
+
if (typeof text !== 'string' && typeof text !== 'number') {
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
const label = translate(String(text));
|
|
50
|
+
if (label === '') {
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
const tone = pill.colorMap && (typeof raw === 'string' || typeof raw === 'number') ? pill.colorMap[raw] : pill.variant;
|
|
54
|
+
return { label, variant: isBadgeVariant(tone) ? tone : 'default' }
|
|
55
|
+
})
|
|
56
|
+
.filter(Boolean);
|
|
57
|
+
items.push({
|
|
58
|
+
key: String(row[rowKey]),
|
|
59
|
+
n: items.length + 1,
|
|
60
|
+
row,
|
|
61
|
+
title: fillTemplate(list.title || '', row),
|
|
62
|
+
sub: fillTemplate(list.sub || '', row),
|
|
63
|
+
due: fillTemplate(list.due || '', row),
|
|
64
|
+
pills,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return { items, missing }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { mapListItems };
|
|
71
|
+
//# sourceMappingURL=mapList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapList.js","sources":["../../../src/utils/mapList.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\nimport { isBadgeVariant } from './badgeVariants.js'\nimport { fillTemplate } from './fillTemplate.js'\nimport { readPath } from './readPath.js'\n\n/**\n * The numbered list beside an index map (DqZakenKaart): one entry per row\n * that has a place on the map, numbered in list order so the number on the\n * pin and the number in the list are the same, and the rows without one.\n *\n * `spec` is `mapConfig.list`: `{ title, sub?, pills?: [{ path?, text?, map?,\n * colorMap? }], due? }`. `title`, `sub` and `due` are field templates\n * (`\"{title}\"`, `\"{address} · {identifier}\"`, `\"{deadline|date}\"`), already\n * translated by the caller; a pill reads `path` (or shows `text`), maps it\n * through `map`, and takes its tone from `colorMap` by the raw value.\n *\n * @param {Array<object>} rows The rows, in display order.\n * @param {object} spec The list declaration.\n * @param {object} options How to read a row.\n * @param {string} options.rowKey The row's key field.\n * @param {Function} options.located Whether a row has a place on the map.\n * @param {Function} [options.translate] The host's translate, for pill labels.\n * @return {{items: Array<{key: string, n: number, row: object, title: string, sub: string, due: string, pills: Array<{label: string, variant: string}>}>, missing: Array<object>}}\n */\nexport function mapListItems(rows, spec, { rowKey, located, translate = (text) => text }) {\n\tconst items = []\n\tconst missing = []\n\tconst list = spec && typeof spec === 'object' ? spec : {}\n\tfor (const row of Array.isArray(rows) ? rows : []) {\n\t\tif (!located(row)) {\n\t\t\tmissing.push(row)\n\t\t\tcontinue\n\t\t}\n\t\tconst pills = (Array.isArray(list.pills) ? list.pills : [])\n\t\t\t.map((pill) => {\n\t\t\t\tif (!pill || typeof pill !== 'object') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst raw = typeof pill.path === 'string' && pill.path !== '' ? readPath(row, pill.path) : undefined\n\t\t\t\tconst mapped = pill.map && (typeof raw === 'string' || typeof raw === 'number') && Object.hasOwn(pill.map, raw) ? pill.map[raw] : undefined\n\t\t\t\tconst text = mapped ?? pill.text ?? raw\n\t\t\t\tif (typeof text !== 'string' && typeof text !== 'number') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst label = translate(String(text))\n\t\t\t\tif (label === '') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst tone = pill.colorMap && (typeof raw === 'string' || typeof raw === 'number') ? pill.colorMap[raw] : pill.variant\n\t\t\t\treturn { label, variant: isBadgeVariant(tone) ? tone : 'default' }\n\t\t\t})\n\t\t\t.filter(Boolean)\n\t\titems.push({\n\t\t\tkey: String(row[rowKey]),\n\t\t\tn: items.length + 1,\n\t\t\trow,\n\t\t\ttitle: fillTemplate(list.title || '', row),\n\t\t\tsub: fillTemplate(list.sub || '', row),\n\t\t\tdue: fillTemplate(list.due || '', row),\n\t\t\tpills,\n\t\t})\n\t}\n\treturn { items, missing }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { readVisibleWhenPath, compareVisibleWhen } from './visibleWhen.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
5
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Compile a row-class declaration into the `(row) => string` CnDataTable
|
|
10
|
+
* takes. A function passes straight through. An array of rules
|
|
11
|
+
* `[{ when: { field, op?, value }, class }]` adds each rule's `class` to a row
|
|
12
|
+
* whose `field` (a dot-path) compares to `value` with `op` (`eq` by default;
|
|
13
|
+
* the shared `visibleWhen` operators, `@today` and the other tokens included),
|
|
14
|
+
* every matching class space-joined: an overdue row can be marked from the
|
|
15
|
+
* manifest (`{ when: { field: "dueAt", op: "lt", value: "@today" }, class:
|
|
16
|
+
* "cn-row--overdue" }`). Anything else, or an empty list, is null.
|
|
17
|
+
*
|
|
18
|
+
* @param {Function|Array<{when: {field: string, op?: string, value: unknown}, class: string}>|null} rowClass The declaration.
|
|
19
|
+
* @return {?(row: object) => string} The row-class function, or null.
|
|
20
|
+
*/
|
|
21
|
+
function compileRowClass(rowClass) {
|
|
22
|
+
if (typeof rowClass === 'function') {
|
|
23
|
+
return rowClass
|
|
24
|
+
}
|
|
25
|
+
if (!Array.isArray(rowClass) || rowClass.length === 0) {
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
const rules = rowClass.filter((rule) => rule && typeof rule.class === 'string' && rule.class !== '');
|
|
29
|
+
return (row) => {
|
|
30
|
+
const classes = [];
|
|
31
|
+
for (const rule of rules) {
|
|
32
|
+
const when = rule.when || {};
|
|
33
|
+
const actual = readVisibleWhenPath(row, when.field);
|
|
34
|
+
if (compareVisibleWhen(actual, when.op || 'eq', when.value)) {
|
|
35
|
+
classes.push(rule.class);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return classes.join(' ')
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { compileRowClass };
|
|
43
|
+
//# sourceMappingURL=rowClassRules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rowClassRules.js","sources":["../../../src/utils/rowClassRules.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\nimport { compareVisibleWhen, readVisibleWhenPath } from './visibleWhen.js'\n\n/**\n * Compile a row-class declaration into the `(row) => string` CnDataTable\n * takes. A function passes straight through. An array of rules\n * `[{ when: { field, op?, value }, class }]` adds each rule's `class` to a row\n * whose `field` (a dot-path) compares to `value` with `op` (`eq` by default;\n * the shared `visibleWhen` operators, `@today` and the other tokens included),\n * every matching class space-joined: an overdue row can be marked from the\n * manifest (`{ when: { field: \"dueAt\", op: \"lt\", value: \"@today\" }, class:\n * \"cn-row--overdue\" }`). Anything else, or an empty list, is null.\n *\n * @param {Function|Array<{when: {field: string, op?: string, value: unknown}, class: string}>|null} rowClass The declaration.\n * @return {?(row: object) => string} The row-class function, or null.\n */\nexport function compileRowClass(rowClass) {\n\tif (typeof rowClass === 'function') {\n\t\treturn rowClass\n\t}\n\tif (!Array.isArray(rowClass) || rowClass.length === 0) {\n\t\treturn null\n\t}\n\tconst rules = rowClass.filter((rule) => rule && typeof rule.class === 'string' && rule.class !== '')\n\treturn (row) => {\n\t\tconst classes = []\n\t\tfor (const rule of rules) {\n\t\t\tconst when = rule.when || {}\n\t\t\tconst actual = readVisibleWhenPath(row, when.field)\n\t\t\tif (compareVisibleWhen(actual, when.op || 'eq', when.value)) {\n\t\t\t\tclasses.push(rule.class)\n\t\t\t}\n\t\t}\n\t\treturn classes.join(' ')\n\t}\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,QAAQ,EAAE;AAC1C,CAAC,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;AACrC,EAAE,OAAO,QAAQ;AACjB,EAAE;AACF,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;AACxD,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,EAAE,EAAC;AACrG,CAAC,OAAO,CAAC,GAAG,KAAK;AACjB,EAAE,MAAM,OAAO,GAAG,GAAE;AACpB,EAAE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AAC5B,GAAG,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,GAAE;AAC/B,GAAG,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,EAAC;AACtD,GAAG,IAAI,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE;AAChE,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAC;AAC5B,IAAI;AACJ,GAAG;AACH,EAAE,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;AAC1B,EAAE;AACF;;;;"}
|