@conduction/nextcloud-vue 2.110.0 → 2.112.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/CnActionDialog/CnActionDialog.vue.js +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +3 -2
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +4 -2
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +2 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +3 -2
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js +2 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +54 -5
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +16 -2
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue.js +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +10 -0
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +2 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +22 -2
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +17 -3
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +52 -8
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +98 -63
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +48 -11
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +160 -97
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +2 -2
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +203 -14
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +19 -4
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js +1 -1
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js +21 -1
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js.map +1 -1
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue.js +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +36 -2
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +44 -5
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js +1 -0
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js.map +1 -1
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js +22 -2
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js.map +1 -1
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js +36 -16
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js.map +1 -1
- package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue.js +1 -1
- package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue2.js.map +1 -1
- package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue3.js.map +1 -1
- package/dist/esm/composables/useRefLabels.js +26 -6
- package/dist/esm/composables/useRefLabels.js.map +1 -1
- package/dist/esm/integrations/registry.js +4 -0
- package/dist/esm/integrations/registry.js.map +1 -1
- package/dist/esm/l10n/en.json.js +3 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +3 -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 +630 -303
- package/dist/esm/store/plugins/liveUpdates.js +3 -1
- package/dist/esm/store/plugins/liveUpdates.js.map +1 -1
- package/dist/esm/store/plugins/logs.js +2 -1
- package/dist/esm/store/plugins/logs.js.map +1 -1
- package/dist/esm/utils/detailActionModel.js +28 -1
- package/dist/esm/utils/detailActionModel.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +13 -5
- package/dist/esm/utils/fillTemplate.js.map +1 -1
- package/dist/esm/utils/headerFieldChips.js +21 -2
- package/dist/esm/utils/headerFieldChips.js.map +1 -1
- package/dist/esm/utils/mapList.js +26 -11
- package/dist/esm/utils/mapList.js.map +1 -1
- package/dist/esm/utils/savedViewHelpers.js +40 -3
- package/dist/esm/utils/savedViewHelpers.js.map +1 -1
- package/dist/esm/utils/savedViewMenu.js +12 -5
- package/dist/esm/utils/savedViewMenu.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 +2760 -1995
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +630 -303
- package/l10n/en.json +3 -1
- package/l10n/nl.json +3 -1
- package/package.json +1 -1
- package/src/components/CnActionDialog/CnActionDialog.vue +32 -9
- package/src/components/CnAppRoot/CnAppRoot.vue +2 -1
- package/src/components/CnAssignDialog/CnAssignDialog.vue +4 -2
- package/src/components/CnBoardView/CnBoardView.vue +58 -5
- package/src/components/CnCardGrid/CnCardGrid.md +1 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +11 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +27 -3
- package/src/components/CnChartWidget/CnChartWidget.vue +6 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +77 -20
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +100 -5
- package/src/components/CnFormDialog/CnFormDialog.vue +1 -1
- package/src/components/CnIndexPage/CnIndexPage.md +1 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +211 -16
- package/src/components/CnIndexSidebar/CnIndexSidebar.md +1 -1
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +2 -2
- package/src/components/CnNcDashboardList/CnNcDashboardList.vue +21 -1
- package/src/components/CnObjectCard/CnObjectCard.md +1 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +51 -5
- package/src/components/CnPartyPicker/CnPartyPicker.vue +62 -16
- package/src/components/CnSearchSelect/CnSearchSelect.vue +8 -0
- package/src/composables/useRefLabels.js +25 -5
- package/src/css/board.css +5 -2
- package/src/css/dialog.css +53 -3
- package/src/css/index-page.css +26 -1
- package/src/css/look-board-detail.css +55 -22
- package/src/css/look-board-index.css +82 -12
- package/src/css/look-board-reports.css +1 -1
- package/src/css/look-board.css +84 -11
- package/src/integrations/registry.js +4 -0
- package/src/schemas/app-manifest-v2.schema.json +11 -5
- package/src/store/plugins/liveUpdates.js +3 -1
- package/src/store/plugins/logs.js +2 -1
- package/src/utils/detailActionModel.js +27 -0
- package/src/utils/fillTemplate.js +13 -5
- package/src/utils/headerFieldChips.js +21 -2
- package/src/utils/mapList.js +26 -11
- package/src/utils/savedViewHelpers.js +39 -2
- package/src/utils/savedViewMenu.js +12 -5
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -390,6 +390,8 @@ function liveUpdatesPlugin(opts = {}) {
|
|
|
390
390
|
// Plain (non-reactive) Map: type → last params object
|
|
391
391
|
store.__lastCollectionParams = new Map();
|
|
392
392
|
|
|
393
|
+
// Detached: the store outlives the component whose setup first
|
|
394
|
+
// used it, and an attached observer is removed with that scope.
|
|
393
395
|
store.$onAction(({ name, args, after }) => {
|
|
394
396
|
if (name === 'fetchCollection') {
|
|
395
397
|
const [type, params = {}] = args;
|
|
@@ -397,7 +399,7 @@ function liveUpdatesPlugin(opts = {}) {
|
|
|
397
399
|
store.__lastCollectionParams.set(type, params);
|
|
398
400
|
});
|
|
399
401
|
}
|
|
400
|
-
});
|
|
402
|
+
}, true);
|
|
401
403
|
|
|
402
404
|
// -- In-flight dedup for fetchObject --
|
|
403
405
|
// Plain Map: dedupKey → Promise
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"liveUpdates.js","sources":["../../../../src/store/plugins/liveUpdates.js"],"sourcesContent":["/**\n * liveUpdatesPlugin — Pinia plugin factory for OpenRegister real-time updates.\n *\n * Adds live-update subscription support to any store created via createObjectStore().\n * Uses @nextcloud/notify_push via the transport singleton, with automatic polling\n * fallback when push is unavailable.\n *\n * Contributed state:\n * - `liveStatus` — current transport status string (`'offline'|'connecting'|'live'|'reconnecting'|'polling'`)\n * - `liveSubscriptions` — number of active subscriptions\n * - `liveLastEventAt` — Date of last received event, or null\n *\n * Contributed getters:\n * - `getLiveStatus`, `getLiveSubscriptions`, `getLiveLastEventAt`\n *\n * Contributed actions:\n * - `subscribe(type, id?, opts?)` — subscribe to live updates; returns a handle\n * - `unsubscribe(handle)` — tear down a subscription\n *\n * Usage note (Options API):\n * `tryOnScopeDispose` auto-registers cleanup when called from a Vue setup()\n * or Options API mounted() with an active effect scope. In Options API mounted()\n * without Vue 2.7 scope tracking, manual `unsubscribe(handle)` in beforeDestroy\n * is required. Document this at the call site.\n *\n * In-flight dedup:\n * Concurrent calls to fetchObject(type, id) or fetchCollection(type, params)\n * for the same key are coalesced into one HTTP request. The dedup maps live as\n * plain (non-reactive) Maps on the store instance to avoid Vue 2 overhead.\n * Dedup activates on the FIRST `subscribe()` call — before that, fetch actions\n * pass straight through to the base implementations. This keeps the plugin\n * fully inert (zero behaviour change) on stores that never subscribe, which\n * matters now that createObjectStore installs this plugin by default\n * (live-updates-default-on).\n *\n * Laziness guarantee:\n * Installing the plugin causes NO transport activity. The transport singleton\n * (and therefore the notify_push `listen()` probe / websocket connection /\n * polling timers) is only created inside the `subscribe()` action.\n *\n * Event coalescing:\n * Live events are HINTS (\"something changed\"), not payloads — each one\n * triggers a refetch. A burst of events (mass import, bulk save, another\n * user's rapid edits) would fire a refetch per event, so the dispatch is\n * coalesced per subscription: the FIRST event refetches immediately, and\n * any further events inside the `refetchDebounce` window (default 750 ms)\n * collapse into ONE trailing refetch when the window closes. Pending\n * trailing refetches are cancelled by `unsubscribe()`.\n *\n * @param {object} [opts] Plugin options\n * @param {number} [opts.pollIntervalCollection=30000] Collection poll interval (ms)\n * @param {number} [opts.pollIntervalObject=60000] Object poll interval (ms)\n * @param {number} [opts.refetchDebounce=750] Event-burst coalescing window (ms); 0 disables\n * @return {object} Plugin definition for use with createObjectStore\n *\n * @example\n * import { createObjectStore, liveUpdatesPlugin } from '@conduction/nextcloud-vue'\n *\n * const useStore = createObjectStore('myStore', {\n * plugins: [liveUpdatesPlugin({ pollIntervalCollection: 15000 })],\n * })\n *\n * // In a Vue component:\n * const store = useStore()\n * const handle = await store.subscribe('melding') // collection\n * const handle2 = await store.subscribe('melding', id) // object\n * // In beforeDestroy (Options API without scope):\n * store.unsubscribe(handle)\n */\n\n// `tryOnScopeDispose` comes from @vueuse/core, a DECLARED (non-optional) peer\n// dependency, so it is imported statically.\n//\n// It used to be pulled in through a CommonJS call inside a try/catch \"to avoid\n// a hard failure if @vueuse/core is absent\". That could not work: this module\n// ships to `dist/esm/**` where that function is not defined, and @vueuse/core's\n// own `exports` map resolves \".\" to a single ESM file with no CommonJS\n// condition. The catch therefore fired every time, `_tryOnScopeDispose` was\n// permanently null, and auto-cleanup on scope dispose silently never happened —\n// subscriptions leaked with no warning anywhere.\nimport { tryOnScopeDispose as _tryOnScopeDispose } from '@vueuse/core'\nimport { buildCollectionKey, buildObjectKey } from '../liveUpdates/eventKeys.js'\nimport { getLiveUpdates } from '../liveUpdates/transport.js'\n\n/**\n * Compute a stable cache key for dedup of fetchCollection calls.\n *\n * @param {string} type Object type slug\n * @param {object} params Query params\n * @return {string}\n */\nfunction collectionDedupKey(type, params) {\n\treturn `${type}:${JSON.stringify(params)}`\n}\n\n/**\n * Compute the cache key for dedup of fetchObject calls.\n *\n * @param {string} type Object type slug\n * @param {string} id Object ID\n * @return {string}\n */\nfunction objectDedupKey(type, id) {\n\treturn `${type}:${id}`\n}\n\n/**\n * Wrap a refetch dispatcher in a leading+trailing burst coalescer.\n *\n * Events are hints, so the exact invocation count doesn't matter — only\n * that the data ends up fresh. The first `run()` dispatches immediately\n * (snappy single-event case); further `run()`s inside the `waitMs` window\n * collapse into one trailing dispatch when the window closes (which opens\n * a new window, so a sustained burst dispatches at most once per window).\n * `cancel()` clears any pending trailing dispatch — called on\n * unsubscribe so a torn-down subscription never fires a late refetch.\n *\n * @param {() => void} fn The refetch dispatcher.\n * @param {number} waitMs Coalescing window in ms; `<= 0` disables (every run dispatches).\n * @return {{run: () => void, cancel: () => void}} The coalesced dispatcher.\n */\nfunction createHintCoalescer(fn, waitMs) {\n\tlet timer = null\n\tlet pending = false\n\n\tfunction run() {\n\t\tif (!(waitMs > 0)) {\n\t\t\tfn()\n\t\t\treturn\n\t\t}\n\t\tif (timer) {\n\t\t\tpending = true\n\t\t\treturn\n\t\t}\n\t\tfn()\n\t\ttimer = setTimeout(() => {\n\t\t\ttimer = null\n\t\t\tif (pending) {\n\t\t\t\tpending = false\n\t\t\t\trun()\n\t\t\t}\n\t\t}, waitMs)\n\t}\n\n\tfunction cancel() {\n\t\tif (timer) {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t\ttimer = null\n\t\tpending = false\n\t}\n\n\treturn { run, cancel }\n}\n\nexport function liveUpdatesPlugin(opts = {}) {\n\tconst pluginPollCollection = opts.pollIntervalCollection || 30000\n\tconst pluginPollObject = opts.pollIntervalObject || 60000\n\tconst pluginRefetchDebounce = opts.refetchDebounce === undefined ? 750 : opts.refetchDebounce\n\n\treturn {\n\t\tname: 'liveUpdates',\n\n\t\tstate: () => ({\n\t\t\t/** @type {'offline'|'connecting'|'live'|'reconnecting'|'polling'} */\n\t\t\tliveStatus: 'offline',\n\t\t\t/** @type {number} */\n\t\t\tliveSubscriptions: 0,\n\t\t\t/** @type {Date|null} */\n\t\t\tliveLastEventAt: null,\n\t\t}),\n\n\t\tgetters: {\n\t\t\t/**\n\t\t\t * Get the current transport status.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {string}\n\t\t\t */\n\t\t\tgetLiveStatus: (state) => state.liveStatus,\n\n\t\t\t/**\n\t\t\t * Get the number of active subscriptions.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {number}\n\t\t\t */\n\t\t\tgetLiveSubscriptions: (state) => state.liveSubscriptions,\n\n\t\t\t/**\n\t\t\t * Get the timestamp of the last received live event.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {Date|null}\n\t\t\t */\n\t\t\tgetLiveLastEventAt: (state) => state.liveLastEventAt,\n\t\t},\n\n\t\tactions: {\n\t\t\t/**\n\t\t\t * Subscribe to live updates for an object type.\n\t\t\t *\n\t\t\t * When `id` is provided: subscribes to `or-object-{id}` (per-object events).\n\t\t\t * When `id` is omitted: subscribes to `or-collection-{registerSlug}-{schemaSlug}`\n\t\t\t * derived from objectTypeRegistry. Slugs are lazy-fetched if not already cached.\n\t\t\t *\n\t\t\t * **Options API note**: In `mounted()` without an active Vue scope,\n\t\t\t * `tryOnScopeDispose` cannot auto-register cleanup. Call `unsubscribe(handle)`\n\t\t\t * manually in `beforeDestroy`.\n\t\t\t *\n\t\t\t * @param {string} type Registered object type slug\n\t\t\t * @param {string} [id] Object UUID (omit for collection subscription)\n\t\t\t * @param {object} [subscribeOpts] Options\n\t\t\t * @param {number} [subscribeOpts.interval] Override poll interval (ms)\n\t\t\t * @return {Promise<object>} Opaque handle for unsubscribe()\n\t\t\t * @throws {Error} If type is not registered, or if lazy slug fetch fails (collection form)\n\t\t\t */\n\t\t\tasync subscribe(type, id, subscribeOpts = {}) {\n\t\t\t\tconst config = this.objectTypeRegistry[type]\n\t\t\t\tif (!config) {\n\t\t\t\t\tthrow new Error(`\"${type}\" is not registered. Call registerObjectType('${type}', ...) first.`)\n\t\t\t\t}\n\n\t\t\t\t// First subscribe() activates the in-flight dedup wrappers set\n\t\t\t\t// up in the plugin's setup() hook. Before this point the store\n\t\t\t\t// behaves exactly as if the plugin were not installed.\n\t\t\t\tthis.__liveDedupActive = true\n\n\t\t\t\tconst liveUpdates = getLiveUpdates({\n\t\t\t\t\tpollIntervalCollection: pluginPollCollection,\n\t\t\t\t\tpollIntervalObject: pluginPollObject,\n\t\t\t\t})\n\n\t\t\t\t// Register transport status observer (idempotent: only once per store instance)\n\t\t\t\tif (!this.__liveStatusObserverRegistered) {\n\t\t\t\t\tthis.__liveStatusObserverRegistered = true\n\t\t\t\t\tliveUpdates.onStatusChange((newStatus) => {\n\t\t\t\t\t\tthis.liveStatus = { ...this.liveStatus, valueOf: undefined, toString: undefined }\n\t\t\t\t\t\t// Vue 2 spread pattern for primitive reactive update\n\t\t\t\t\t\tthis.liveStatus = newStatus\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tlet eventKey\n\t\t\t\tlet isObject = false\n\n\t\t\t\tif (id !== undefined && id !== null) {\n\t\t\t\t\t// Object subscription — no slug lookup needed\n\t\t\t\t\tisObject = true\n\t\t\t\t\teventKey = buildObjectKey(id)\n\t\t\t\t} else {\n\t\t\t\t\t// Collection subscription — need registerSlug + schemaSlug\n\t\t\t\t\tlet { registerSlug, schemaSlug } = config\n\n\t\t\t\t\tif (!registerSlug || !schemaSlug) {\n\t\t\t\t\t\t// Lazy fetch slugs via existing store actions\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst [register, schema] = await Promise.all([\n\t\t\t\t\t\t\t\tthis.fetchRegister(type),\n\t\t\t\t\t\t\t\tthis.fetchSchema(type),\n\t\t\t\t\t\t\t])\n\n\t\t\t\t\t\t\tif (!register || !register.slug) {\n\t\t\t\t\t\t\t\tthrow new Error(`Failed to resolve register slug for \"${type}\"`)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!schema || !schema.slug) {\n\t\t\t\t\t\t\t\tthrow new Error(`Failed to resolve schema slug for \"${type}\"`)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tregisterSlug = register.slug\n\t\t\t\t\t\t\tschemaSlug = schema.slug\n\n\t\t\t\t\t\t\t// Cache slugs in registry (Vue 2 spread pattern)\n\t\t\t\t\t\t\tthis.objectTypeRegistry = {\n\t\t\t\t\t\t\t\t...this.objectTypeRegistry,\n\t\t\t\t\t\t\t\t[type]: { ...config, registerSlug, schemaSlug },\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\tthrow new Error(`liveUpdatesPlugin: cannot subscribe to \"${type}\" collection — ${err.message}`, { cause: err })\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\teventKey = buildCollectionKey(registerSlug, schemaSlug)\n\t\t\t\t}\n\n\t\t\t\t// Set status to 'connecting' on first subscribe (before transport sets it)\n\t\t\t\tif (this.liveStatus === 'offline') {\n\t\t\t\t\tthis.liveStatus = 'connecting'\n\t\t\t\t}\n\n\t\t\t\tconst store = this\n\n\t\t\t\t// Events are hints — coalesce refetch bursts per subscription\n\t\t\t\t// (see \"Event coalescing\" in the module docblock). A per-call\n\t\t\t\t// `subscribeOpts.debounce` overrides the plugin default; 0 disables.\n\t\t\t\tconst debounceMs = subscribeOpts.debounce === undefined ? pluginRefetchDebounce : subscribeOpts.debounce\n\t\t\t\tconst dispatch = isObject\n\t\t\t\t\t? () => {\n\t\t\t\t\t\t// Dispatch fetchObject with dedup\n\t\t\t\t\t\t\tstore.fetchObject(type, id)\n\t\t\t\t\t\t}\n\t\t\t\t\t: () => {\n\t\t\t\t\t\t// Dispatch fetchCollection with last stashed params + dedup\n\t\t\t\t\t\t\tconst lastParams = store.__lastCollectionParams?.get(type) || {}\n\t\t\t\t\t\t\tstore.fetchCollection(type, lastParams)\n\t\t\t\t\t\t}\n\t\t\t\tconst coalesced = createHintCoalescer(dispatch, debounceMs)\n\n\t\t\t\tconst callback = () => {\n\t\t\t\t\tstore.liveLastEventAt = new Date()\n\t\t\t\t\tcoalesced.run()\n\t\t\t\t}\n\n\t\t\t\tconst transportOpts = {\n\t\t\t\t\tisObject,\n\t\t\t\t\tinterval: subscribeOpts.interval || (isObject ? pluginPollObject : pluginPollCollection),\n\t\t\t\t}\n\n\t\t\t\tconst transportHandle = liveUpdates.subscribe(eventKey, callback, transportOpts)\n\n\t\t\t\t// Update reactive subscriptions count (Vue 2 spread)\n\t\t\t\tthis.liveSubscriptions = this.liveSubscriptions + 1\n\n\t\t\t\t// Update liveStatus from transport now that we've subscribed\n\t\t\t\tconst transportStatus = liveUpdates.getStatus()\n\t\t\t\tif (transportStatus !== 'offline') {\n\t\t\t\t\tthis.liveStatus = transportStatus\n\t\t\t\t}\n\n\t\t\t\tconst handle = {\n\t\t\t\t\t_livePlugin: true,\n\t\t\t\t\ttype,\n\t\t\t\t\tid,\n\t\t\t\t\teventKey,\n\t\t\t\t\ttransportHandle,\n\t\t\t\t\t// Cancels a pending coalesced (trailing) refetch — called by\n\t\t\t\t\t// unsubscribe() so a torn-down subscription never fires late.\n\t\t\t\t\t_cancelPendingRefetch: coalesced.cancel,\n\t\t\t\t}\n\n\t\t\t\t// Auto-cleanup via tryOnScopeDispose (Vue 2.7 composition API scopes)\n\t\t\t\tif (_tryOnScopeDispose) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t_tryOnScopeDispose(() => {\n\t\t\t\t\t\t\tthis.unsubscribe(handle)\n\t\t\t\t\t\t})\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Not in a scope — manual unsubscribe required in beforeDestroy\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn handle\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Unsubscribe from live updates. Idempotent — releasing the same\n\t\t\t * handle twice (e.g. the plugin's own scope-dispose cleanup racing\n\t\t\t * a composable's explicit detach) is a no-op the second time, so\n\t\t\t * `liveSubscriptions` never double-decrements.\n\t\t\t *\n\t\t\t * @param {object} handle Handle returned by subscribe()\n\t\t\t */\n\t\t\tunsubscribe(handle) {\n\t\t\t\tif (!handle || !handle._livePlugin || handle._released) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\thandle._released = true\n\n\t\t\t\t// Cancel any pending coalesced refetch before tearing down the\n\t\t\t\t// transport subscription — a late trailing dispatch after\n\t\t\t\t// unsubscribe would refetch for a scope nobody renders anymore.\n\t\t\t\tif (typeof handle._cancelPendingRefetch === 'function') {\n\t\t\t\t\thandle._cancelPendingRefetch()\n\t\t\t\t}\n\n\t\t\t\tconst liveUpdates = getLiveUpdates()\n\t\t\t\tliveUpdates.unsubscribe(handle.transportHandle)\n\n\t\t\t\tthis.liveSubscriptions = Math.max(0, this.liveSubscriptions - 1)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * Plugin setup hook — called once per store instance.\n\t\t * Sets up:\n\t\t * 1. `$onAction` observer for fetchCollection → stash last params\n\t\t * 2. In-flight dedup Maps for fetchObject and fetchCollection\n\t\t *\n\t\t * @param {object} store The Pinia store instance\n\t\t */\n\t\tsetup(store) {\n\t\t\t// Dedup activation flag — flipped to true by the first subscribe()\n\t\t\t// call. Until then the fetch wrappers below pass straight through,\n\t\t\t// so a default-installed plugin causes zero behaviour change on\n\t\t\t// stores that never subscribe (live-updates-default-on).\n\t\t\tstore.__liveDedupActive = false\n\n\t\t\t// -- Last-params stash for collection re-fetch on events --\n\t\t\t// Plain (non-reactive) Map: type → last params object\n\t\t\tstore.__lastCollectionParams = new Map()\n\n\t\t\tstore.$onAction(({ name, args, after }) => {\n\t\t\t\tif (name === 'fetchCollection') {\n\t\t\t\t\tconst [type, params = {}] = args\n\t\t\t\t\tafter(() => {\n\t\t\t\t\t\tstore.__lastCollectionParams.set(type, params)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// -- In-flight dedup for fetchObject --\n\t\t\t// Plain Map: dedupKey → Promise\n\t\t\tconst objectInFlight = new Map()\n\n\t\t\tconst originalFetchObject = store.fetchObject.bind(store)\n\t\t\tstore.fetchObject = async function dedupedFetchObject(type, id) {\n\t\t\t\tif (!store.__liveDedupActive) {\n\t\t\t\t\treturn originalFetchObject(type, id)\n\t\t\t\t}\n\t\t\t\tconst key = objectDedupKey(type, id)\n\t\t\t\tif (objectInFlight.has(key)) {\n\t\t\t\t\treturn objectInFlight.get(key)\n\t\t\t\t}\n\t\t\t\tconst promise = originalFetchObject(type, id).finally(() => {\n\t\t\t\t\tobjectInFlight.delete(key)\n\t\t\t\t})\n\t\t\t\tobjectInFlight.set(key, promise)\n\t\t\t\treturn promise\n\t\t\t}\n\n\t\t\t// -- In-flight dedup for fetchCollection --\n\t\t\tconst collectionInFlight = new Map()\n\n\t\t\tconst originalFetchCollection = store.fetchCollection.bind(store)\n\t\t\tstore.fetchCollection = async function dedupedFetchCollection(type, params = {}, options = {}) {\n\t\t\t\tif (!store.__liveDedupActive) {\n\t\t\t\t\treturn originalFetchCollection(type, params, options)\n\t\t\t\t}\n\t\t\t\tconst key = collectionDedupKey(type, params)\n\t\t\t\tlet entry = collectionInFlight.get(key)\n\t\t\t\tif (!entry) {\n\t\t\t\t\t// The shared request reports into its own sink, copied to\n\t\t\t\t\t// each caller's `options.outcome` below, so a caller that\n\t\t\t\t\t// joined it learns that request's result too.\n\t\t\t\t\tconst outcome = {}\n\t\t\t\t\tconst promise = originalFetchCollection(type, params, { ...options, outcome }).finally(() => {\n\t\t\t\t\t\tcollectionInFlight.delete(key)\n\t\t\t\t\t})\n\t\t\t\t\tentry = { promise, outcome }\n\t\t\t\t\tcollectionInFlight.set(key, entry)\n\t\t\t\t}\n\t\t\t\tconst results = await entry.promise\n\t\t\t\tconst sink = options && options.outcome\n\t\t\t\tif (sink && typeof sink === 'object' && 'error' in entry.outcome) {\n\t\t\t\t\tsink.error = entry.outcome.error\n\t\t\t\t}\n\t\t\t\treturn results\n\t\t\t}\n\t\t},\n\t}\n}\n"],"names":["_tryOnScopeDispose"],"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;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,SAAS,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE;AAC1C,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,cAAc,CAAC,IAAI,EAAE,EAAE,EAAE;AAClC,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACvB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE;AACzC,CAAC,IAAI,KAAK,GAAG,KAAI;AACjB,CAAC,IAAI,OAAO,GAAG,MAAK;AACpB;AACA,CAAC,SAAS,GAAG,GAAG;AAChB,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE;AACrB,GAAG,EAAE,GAAE;AACP,GAAG,MAAM;AACT,GAAG;AACH,EAAE,IAAI,KAAK,EAAE;AACb,GAAG,OAAO,GAAG,KAAI;AACjB,GAAG,MAAM;AACT,GAAG;AACH,EAAE,EAAE,GAAE;AACN,EAAE,KAAK,GAAG,UAAU,CAAC,MAAM;AAC3B,GAAG,KAAK,GAAG,KAAI;AACf,GAAG,IAAI,OAAO,EAAE;AAChB,IAAI,OAAO,GAAG,MAAK;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"}
|
|
1
|
+
{"version":3,"file":"liveUpdates.js","sources":["../../../../src/store/plugins/liveUpdates.js"],"sourcesContent":["/**\n * liveUpdatesPlugin — Pinia plugin factory for OpenRegister real-time updates.\n *\n * Adds live-update subscription support to any store created via createObjectStore().\n * Uses @nextcloud/notify_push via the transport singleton, with automatic polling\n * fallback when push is unavailable.\n *\n * Contributed state:\n * - `liveStatus` — current transport status string (`'offline'|'connecting'|'live'|'reconnecting'|'polling'`)\n * - `liveSubscriptions` — number of active subscriptions\n * - `liveLastEventAt` — Date of last received event, or null\n *\n * Contributed getters:\n * - `getLiveStatus`, `getLiveSubscriptions`, `getLiveLastEventAt`\n *\n * Contributed actions:\n * - `subscribe(type, id?, opts?)` — subscribe to live updates; returns a handle\n * - `unsubscribe(handle)` — tear down a subscription\n *\n * Usage note (Options API):\n * `tryOnScopeDispose` auto-registers cleanup when called from a Vue setup()\n * or Options API mounted() with an active effect scope. In Options API mounted()\n * without Vue 2.7 scope tracking, manual `unsubscribe(handle)` in beforeDestroy\n * is required. Document this at the call site.\n *\n * In-flight dedup:\n * Concurrent calls to fetchObject(type, id) or fetchCollection(type, params)\n * for the same key are coalesced into one HTTP request. The dedup maps live as\n * plain (non-reactive) Maps on the store instance to avoid Vue 2 overhead.\n * Dedup activates on the FIRST `subscribe()` call — before that, fetch actions\n * pass straight through to the base implementations. This keeps the plugin\n * fully inert (zero behaviour change) on stores that never subscribe, which\n * matters now that createObjectStore installs this plugin by default\n * (live-updates-default-on).\n *\n * Laziness guarantee:\n * Installing the plugin causes NO transport activity. The transport singleton\n * (and therefore the notify_push `listen()` probe / websocket connection /\n * polling timers) is only created inside the `subscribe()` action.\n *\n * Event coalescing:\n * Live events are HINTS (\"something changed\"), not payloads — each one\n * triggers a refetch. A burst of events (mass import, bulk save, another\n * user's rapid edits) would fire a refetch per event, so the dispatch is\n * coalesced per subscription: the FIRST event refetches immediately, and\n * any further events inside the `refetchDebounce` window (default 750 ms)\n * collapse into ONE trailing refetch when the window closes. Pending\n * trailing refetches are cancelled by `unsubscribe()`.\n *\n * @param {object} [opts] Plugin options\n * @param {number} [opts.pollIntervalCollection=30000] Collection poll interval (ms)\n * @param {number} [opts.pollIntervalObject=60000] Object poll interval (ms)\n * @param {number} [opts.refetchDebounce=750] Event-burst coalescing window (ms); 0 disables\n * @return {object} Plugin definition for use with createObjectStore\n *\n * @example\n * import { createObjectStore, liveUpdatesPlugin } from '@conduction/nextcloud-vue'\n *\n * const useStore = createObjectStore('myStore', {\n * plugins: [liveUpdatesPlugin({ pollIntervalCollection: 15000 })],\n * })\n *\n * // In a Vue component:\n * const store = useStore()\n * const handle = await store.subscribe('melding') // collection\n * const handle2 = await store.subscribe('melding', id) // object\n * // In beforeDestroy (Options API without scope):\n * store.unsubscribe(handle)\n */\n\n// `tryOnScopeDispose` comes from @vueuse/core, a DECLARED (non-optional) peer\n// dependency, so it is imported statically.\n//\n// It used to be pulled in through a CommonJS call inside a try/catch \"to avoid\n// a hard failure if @vueuse/core is absent\". That could not work: this module\n// ships to `dist/esm/**` where that function is not defined, and @vueuse/core's\n// own `exports` map resolves \".\" to a single ESM file with no CommonJS\n// condition. The catch therefore fired every time, `_tryOnScopeDispose` was\n// permanently null, and auto-cleanup on scope dispose silently never happened —\n// subscriptions leaked with no warning anywhere.\nimport { tryOnScopeDispose as _tryOnScopeDispose } from '@vueuse/core'\nimport { buildCollectionKey, buildObjectKey } from '../liveUpdates/eventKeys.js'\nimport { getLiveUpdates } from '../liveUpdates/transport.js'\n\n/**\n * Compute a stable cache key for dedup of fetchCollection calls.\n *\n * @param {string} type Object type slug\n * @param {object} params Query params\n * @return {string}\n */\nfunction collectionDedupKey(type, params) {\n\treturn `${type}:${JSON.stringify(params)}`\n}\n\n/**\n * Compute the cache key for dedup of fetchObject calls.\n *\n * @param {string} type Object type slug\n * @param {string} id Object ID\n * @return {string}\n */\nfunction objectDedupKey(type, id) {\n\treturn `${type}:${id}`\n}\n\n/**\n * Wrap a refetch dispatcher in a leading+trailing burst coalescer.\n *\n * Events are hints, so the exact invocation count doesn't matter — only\n * that the data ends up fresh. The first `run()` dispatches immediately\n * (snappy single-event case); further `run()`s inside the `waitMs` window\n * collapse into one trailing dispatch when the window closes (which opens\n * a new window, so a sustained burst dispatches at most once per window).\n * `cancel()` clears any pending trailing dispatch — called on\n * unsubscribe so a torn-down subscription never fires a late refetch.\n *\n * @param {() => void} fn The refetch dispatcher.\n * @param {number} waitMs Coalescing window in ms; `<= 0` disables (every run dispatches).\n * @return {{run: () => void, cancel: () => void}} The coalesced dispatcher.\n */\nfunction createHintCoalescer(fn, waitMs) {\n\tlet timer = null\n\tlet pending = false\n\n\tfunction run() {\n\t\tif (!(waitMs > 0)) {\n\t\t\tfn()\n\t\t\treturn\n\t\t}\n\t\tif (timer) {\n\t\t\tpending = true\n\t\t\treturn\n\t\t}\n\t\tfn()\n\t\ttimer = setTimeout(() => {\n\t\t\ttimer = null\n\t\t\tif (pending) {\n\t\t\t\tpending = false\n\t\t\t\trun()\n\t\t\t}\n\t\t}, waitMs)\n\t}\n\n\tfunction cancel() {\n\t\tif (timer) {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t\ttimer = null\n\t\tpending = false\n\t}\n\n\treturn { run, cancel }\n}\n\nexport function liveUpdatesPlugin(opts = {}) {\n\tconst pluginPollCollection = opts.pollIntervalCollection || 30000\n\tconst pluginPollObject = opts.pollIntervalObject || 60000\n\tconst pluginRefetchDebounce = opts.refetchDebounce === undefined ? 750 : opts.refetchDebounce\n\n\treturn {\n\t\tname: 'liveUpdates',\n\n\t\tstate: () => ({\n\t\t\t/** @type {'offline'|'connecting'|'live'|'reconnecting'|'polling'} */\n\t\t\tliveStatus: 'offline',\n\t\t\t/** @type {number} */\n\t\t\tliveSubscriptions: 0,\n\t\t\t/** @type {Date|null} */\n\t\t\tliveLastEventAt: null,\n\t\t}),\n\n\t\tgetters: {\n\t\t\t/**\n\t\t\t * Get the current transport status.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {string}\n\t\t\t */\n\t\t\tgetLiveStatus: (state) => state.liveStatus,\n\n\t\t\t/**\n\t\t\t * Get the number of active subscriptions.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {number}\n\t\t\t */\n\t\t\tgetLiveSubscriptions: (state) => state.liveSubscriptions,\n\n\t\t\t/**\n\t\t\t * Get the timestamp of the last received live event.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {Date|null}\n\t\t\t */\n\t\t\tgetLiveLastEventAt: (state) => state.liveLastEventAt,\n\t\t},\n\n\t\tactions: {\n\t\t\t/**\n\t\t\t * Subscribe to live updates for an object type.\n\t\t\t *\n\t\t\t * When `id` is provided: subscribes to `or-object-{id}` (per-object events).\n\t\t\t * When `id` is omitted: subscribes to `or-collection-{registerSlug}-{schemaSlug}`\n\t\t\t * derived from objectTypeRegistry. Slugs are lazy-fetched if not already cached.\n\t\t\t *\n\t\t\t * **Options API note**: In `mounted()` without an active Vue scope,\n\t\t\t * `tryOnScopeDispose` cannot auto-register cleanup. Call `unsubscribe(handle)`\n\t\t\t * manually in `beforeDestroy`.\n\t\t\t *\n\t\t\t * @param {string} type Registered object type slug\n\t\t\t * @param {string} [id] Object UUID (omit for collection subscription)\n\t\t\t * @param {object} [subscribeOpts] Options\n\t\t\t * @param {number} [subscribeOpts.interval] Override poll interval (ms)\n\t\t\t * @return {Promise<object>} Opaque handle for unsubscribe()\n\t\t\t * @throws {Error} If type is not registered, or if lazy slug fetch fails (collection form)\n\t\t\t */\n\t\t\tasync subscribe(type, id, subscribeOpts = {}) {\n\t\t\t\tconst config = this.objectTypeRegistry[type]\n\t\t\t\tif (!config) {\n\t\t\t\t\tthrow new Error(`\"${type}\" is not registered. Call registerObjectType('${type}', ...) first.`)\n\t\t\t\t}\n\n\t\t\t\t// First subscribe() activates the in-flight dedup wrappers set\n\t\t\t\t// up in the plugin's setup() hook. Before this point the store\n\t\t\t\t// behaves exactly as if the plugin were not installed.\n\t\t\t\tthis.__liveDedupActive = true\n\n\t\t\t\tconst liveUpdates = getLiveUpdates({\n\t\t\t\t\tpollIntervalCollection: pluginPollCollection,\n\t\t\t\t\tpollIntervalObject: pluginPollObject,\n\t\t\t\t})\n\n\t\t\t\t// Register transport status observer (idempotent: only once per store instance)\n\t\t\t\tif (!this.__liveStatusObserverRegistered) {\n\t\t\t\t\tthis.__liveStatusObserverRegistered = true\n\t\t\t\t\tliveUpdates.onStatusChange((newStatus) => {\n\t\t\t\t\t\tthis.liveStatus = { ...this.liveStatus, valueOf: undefined, toString: undefined }\n\t\t\t\t\t\t// Vue 2 spread pattern for primitive reactive update\n\t\t\t\t\t\tthis.liveStatus = newStatus\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tlet eventKey\n\t\t\t\tlet isObject = false\n\n\t\t\t\tif (id !== undefined && id !== null) {\n\t\t\t\t\t// Object subscription — no slug lookup needed\n\t\t\t\t\tisObject = true\n\t\t\t\t\teventKey = buildObjectKey(id)\n\t\t\t\t} else {\n\t\t\t\t\t// Collection subscription — need registerSlug + schemaSlug\n\t\t\t\t\tlet { registerSlug, schemaSlug } = config\n\n\t\t\t\t\tif (!registerSlug || !schemaSlug) {\n\t\t\t\t\t\t// Lazy fetch slugs via existing store actions\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst [register, schema] = await Promise.all([\n\t\t\t\t\t\t\t\tthis.fetchRegister(type),\n\t\t\t\t\t\t\t\tthis.fetchSchema(type),\n\t\t\t\t\t\t\t])\n\n\t\t\t\t\t\t\tif (!register || !register.slug) {\n\t\t\t\t\t\t\t\tthrow new Error(`Failed to resolve register slug for \"${type}\"`)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!schema || !schema.slug) {\n\t\t\t\t\t\t\t\tthrow new Error(`Failed to resolve schema slug for \"${type}\"`)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tregisterSlug = register.slug\n\t\t\t\t\t\t\tschemaSlug = schema.slug\n\n\t\t\t\t\t\t\t// Cache slugs in registry (Vue 2 spread pattern)\n\t\t\t\t\t\t\tthis.objectTypeRegistry = {\n\t\t\t\t\t\t\t\t...this.objectTypeRegistry,\n\t\t\t\t\t\t\t\t[type]: { ...config, registerSlug, schemaSlug },\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\tthrow new Error(`liveUpdatesPlugin: cannot subscribe to \"${type}\" collection — ${err.message}`, { cause: err })\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\teventKey = buildCollectionKey(registerSlug, schemaSlug)\n\t\t\t\t}\n\n\t\t\t\t// Set status to 'connecting' on first subscribe (before transport sets it)\n\t\t\t\tif (this.liveStatus === 'offline') {\n\t\t\t\t\tthis.liveStatus = 'connecting'\n\t\t\t\t}\n\n\t\t\t\tconst store = this\n\n\t\t\t\t// Events are hints — coalesce refetch bursts per subscription\n\t\t\t\t// (see \"Event coalescing\" in the module docblock). A per-call\n\t\t\t\t// `subscribeOpts.debounce` overrides the plugin default; 0 disables.\n\t\t\t\tconst debounceMs = subscribeOpts.debounce === undefined ? pluginRefetchDebounce : subscribeOpts.debounce\n\t\t\t\tconst dispatch = isObject\n\t\t\t\t\t? () => {\n\t\t\t\t\t\t// Dispatch fetchObject with dedup\n\t\t\t\t\t\t\tstore.fetchObject(type, id)\n\t\t\t\t\t\t}\n\t\t\t\t\t: () => {\n\t\t\t\t\t\t// Dispatch fetchCollection with last stashed params + dedup\n\t\t\t\t\t\t\tconst lastParams = store.__lastCollectionParams?.get(type) || {}\n\t\t\t\t\t\t\tstore.fetchCollection(type, lastParams)\n\t\t\t\t\t\t}\n\t\t\t\tconst coalesced = createHintCoalescer(dispatch, debounceMs)\n\n\t\t\t\tconst callback = () => {\n\t\t\t\t\tstore.liveLastEventAt = new Date()\n\t\t\t\t\tcoalesced.run()\n\t\t\t\t}\n\n\t\t\t\tconst transportOpts = {\n\t\t\t\t\tisObject,\n\t\t\t\t\tinterval: subscribeOpts.interval || (isObject ? pluginPollObject : pluginPollCollection),\n\t\t\t\t}\n\n\t\t\t\tconst transportHandle = liveUpdates.subscribe(eventKey, callback, transportOpts)\n\n\t\t\t\t// Update reactive subscriptions count (Vue 2 spread)\n\t\t\t\tthis.liveSubscriptions = this.liveSubscriptions + 1\n\n\t\t\t\t// Update liveStatus from transport now that we've subscribed\n\t\t\t\tconst transportStatus = liveUpdates.getStatus()\n\t\t\t\tif (transportStatus !== 'offline') {\n\t\t\t\t\tthis.liveStatus = transportStatus\n\t\t\t\t}\n\n\t\t\t\tconst handle = {\n\t\t\t\t\t_livePlugin: true,\n\t\t\t\t\ttype,\n\t\t\t\t\tid,\n\t\t\t\t\teventKey,\n\t\t\t\t\ttransportHandle,\n\t\t\t\t\t// Cancels a pending coalesced (trailing) refetch — called by\n\t\t\t\t\t// unsubscribe() so a torn-down subscription never fires late.\n\t\t\t\t\t_cancelPendingRefetch: coalesced.cancel,\n\t\t\t\t}\n\n\t\t\t\t// Auto-cleanup via tryOnScopeDispose (Vue 2.7 composition API scopes)\n\t\t\t\tif (_tryOnScopeDispose) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t_tryOnScopeDispose(() => {\n\t\t\t\t\t\t\tthis.unsubscribe(handle)\n\t\t\t\t\t\t})\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Not in a scope — manual unsubscribe required in beforeDestroy\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn handle\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Unsubscribe from live updates. Idempotent — releasing the same\n\t\t\t * handle twice (e.g. the plugin's own scope-dispose cleanup racing\n\t\t\t * a composable's explicit detach) is a no-op the second time, so\n\t\t\t * `liveSubscriptions` never double-decrements.\n\t\t\t *\n\t\t\t * @param {object} handle Handle returned by subscribe()\n\t\t\t */\n\t\t\tunsubscribe(handle) {\n\t\t\t\tif (!handle || !handle._livePlugin || handle._released) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\thandle._released = true\n\n\t\t\t\t// Cancel any pending coalesced refetch before tearing down the\n\t\t\t\t// transport subscription — a late trailing dispatch after\n\t\t\t\t// unsubscribe would refetch for a scope nobody renders anymore.\n\t\t\t\tif (typeof handle._cancelPendingRefetch === 'function') {\n\t\t\t\t\thandle._cancelPendingRefetch()\n\t\t\t\t}\n\n\t\t\t\tconst liveUpdates = getLiveUpdates()\n\t\t\t\tliveUpdates.unsubscribe(handle.transportHandle)\n\n\t\t\t\tthis.liveSubscriptions = Math.max(0, this.liveSubscriptions - 1)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * Plugin setup hook — called once per store instance.\n\t\t * Sets up:\n\t\t * 1. `$onAction` observer for fetchCollection → stash last params\n\t\t * 2. In-flight dedup Maps for fetchObject and fetchCollection\n\t\t *\n\t\t * @param {object} store The Pinia store instance\n\t\t */\n\t\tsetup(store) {\n\t\t\t// Dedup activation flag — flipped to true by the first subscribe()\n\t\t\t// call. Until then the fetch wrappers below pass straight through,\n\t\t\t// so a default-installed plugin causes zero behaviour change on\n\t\t\t// stores that never subscribe (live-updates-default-on).\n\t\t\tstore.__liveDedupActive = false\n\n\t\t\t// -- Last-params stash for collection re-fetch on events --\n\t\t\t// Plain (non-reactive) Map: type → last params object\n\t\t\tstore.__lastCollectionParams = new Map()\n\n\t\t\t// Detached: the store outlives the component whose setup first\n\t\t\t// used it, and an attached observer is removed with that scope.\n\t\t\tstore.$onAction(({ name, args, after }) => {\n\t\t\t\tif (name === 'fetchCollection') {\n\t\t\t\t\tconst [type, params = {}] = args\n\t\t\t\t\tafter(() => {\n\t\t\t\t\t\tstore.__lastCollectionParams.set(type, params)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}, true)\n\n\t\t\t// -- In-flight dedup for fetchObject --\n\t\t\t// Plain Map: dedupKey → Promise\n\t\t\tconst objectInFlight = new Map()\n\n\t\t\tconst originalFetchObject = store.fetchObject.bind(store)\n\t\t\tstore.fetchObject = async function dedupedFetchObject(type, id) {\n\t\t\t\tif (!store.__liveDedupActive) {\n\t\t\t\t\treturn originalFetchObject(type, id)\n\t\t\t\t}\n\t\t\t\tconst key = objectDedupKey(type, id)\n\t\t\t\tif (objectInFlight.has(key)) {\n\t\t\t\t\treturn objectInFlight.get(key)\n\t\t\t\t}\n\t\t\t\tconst promise = originalFetchObject(type, id).finally(() => {\n\t\t\t\t\tobjectInFlight.delete(key)\n\t\t\t\t})\n\t\t\t\tobjectInFlight.set(key, promise)\n\t\t\t\treturn promise\n\t\t\t}\n\n\t\t\t// -- In-flight dedup for fetchCollection --\n\t\t\tconst collectionInFlight = new Map()\n\n\t\t\tconst originalFetchCollection = store.fetchCollection.bind(store)\n\t\t\tstore.fetchCollection = async function dedupedFetchCollection(type, params = {}, options = {}) {\n\t\t\t\tif (!store.__liveDedupActive) {\n\t\t\t\t\treturn originalFetchCollection(type, params, options)\n\t\t\t\t}\n\t\t\t\tconst key = collectionDedupKey(type, params)\n\t\t\t\tlet entry = collectionInFlight.get(key)\n\t\t\t\tif (!entry) {\n\t\t\t\t\t// The shared request reports into its own sink, copied to\n\t\t\t\t\t// each caller's `options.outcome` below, so a caller that\n\t\t\t\t\t// joined it learns that request's result too.\n\t\t\t\t\tconst outcome = {}\n\t\t\t\t\tconst promise = originalFetchCollection(type, params, { ...options, outcome }).finally(() => {\n\t\t\t\t\t\tcollectionInFlight.delete(key)\n\t\t\t\t\t})\n\t\t\t\t\tentry = { promise, outcome }\n\t\t\t\t\tcollectionInFlight.set(key, entry)\n\t\t\t\t}\n\t\t\t\tconst results = await entry.promise\n\t\t\t\tconst sink = options && options.outcome\n\t\t\t\tif (sink && typeof sink === 'object' && 'error' in entry.outcome) {\n\t\t\t\t\tsink.error = entry.outcome.error\n\t\t\t\t}\n\t\t\t\treturn results\n\t\t\t}\n\t\t},\n\t}\n}\n"],"names":["_tryOnScopeDispose"],"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;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,SAAS,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE;AAC1C,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,cAAc,CAAC,IAAI,EAAE,EAAE,EAAE;AAClC,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACvB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE;AACzC,CAAC,IAAI,KAAK,GAAG,KAAI;AACjB,CAAC,IAAI,OAAO,GAAG,MAAK;AACpB;AACA,CAAC,SAAS,GAAG,GAAG;AAChB,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE;AACrB,GAAG,EAAE,GAAE;AACP,GAAG,MAAM;AACT,GAAG;AACH,EAAE,IAAI,KAAK,EAAE;AACb,GAAG,OAAO,GAAG,KAAI;AACjB,GAAG,MAAM;AACT,GAAG;AACH,EAAE,EAAE,GAAE;AACN,EAAE,KAAK,GAAG,UAAU,CAAC,MAAM;AAC3B,GAAG,KAAK,GAAG,KAAI;AACf,GAAG,IAAI,OAAO,EAAE;AAChB,IAAI,OAAO,GAAG,MAAK;AACnB,IAAI,GAAG,GAAE;AACT,IAAI;AACJ,GAAG,EAAE,MAAM,EAAC;AACZ,EAAE;AACF;AACA,CAAC,SAAS,MAAM,GAAG;AACnB,EAAE,IAAI,KAAK,EAAE;AACb,GAAG,YAAY,CAAC,KAAK,EAAC;AACtB,GAAG;AACH,EAAE,KAAK,GAAG,KAAI;AACd,EAAE,OAAO,GAAG,MAAK;AACjB,EAAE;AACF;AACA,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE;AACvB,CAAC;AACD;AACO,SAAS,iBAAiB,CAAC,IAAI,GAAG,EAAE,EAAE;AAC7C,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC,sBAAsB,IAAI,MAAK;AAClE,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,kBAAkB,IAAI,MAAK;AAC1D,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC,eAAe,KAAK,SAAS,GAAG,GAAG,GAAG,IAAI,CAAC,gBAAe;AAC9F;AACA,CAAC,OAAO;AACR,EAAE,IAAI,EAAE,aAAa;AACrB;AACA,EAAE,KAAK,EAAE,OAAO;AAChB;AACA,GAAG,UAAU,EAAE,SAAS;AACxB;AACA,GAAG,iBAAiB,EAAE,CAAC;AACvB;AACA,GAAG,eAAe,EAAE,IAAI;AACxB,GAAG,CAAC;AACJ;AACA,EAAE,OAAO,EAAE;AACX;AACA;AACA;AACA;AACA;AACA;AACA,GAAG,aAAa,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,UAAU;AAC7C;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG,oBAAoB,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,iBAAiB;AAC3D;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG,kBAAkB,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,eAAe;AACvD,GAAG;AACH;AACA,EAAE,OAAO,EAAE;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,aAAa,GAAG,EAAE,EAAE;AACjD,IAAI,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAC;AAChD,IAAI,IAAI,CAAC,MAAM,EAAE;AACjB,KAAK,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,8CAA8C,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;AACnG,KAAK;AACL;AACA;AACA;AACA;AACA,IAAI,IAAI,CAAC,iBAAiB,GAAG,KAAI;AACjC;AACA,IAAI,MAAM,WAAW,GAAG,cAAc,CAAC;AACvC,KAAK,sBAAsB,EAAE,oBAAoB;AACjD,KAAK,kBAAkB,EAAE,gBAAgB;AACzC,KAAK,EAAC;AACN;AACA;AACA,IAAI,IAAI,CAAC,IAAI,CAAC,8BAA8B,EAAE;AAC9C,KAAK,IAAI,CAAC,8BAA8B,GAAG,KAAI;AAC/C,KAAK,WAAW,CAAC,cAAc,CAAC,CAAC,SAAS,KAAK;AAC/C,MAAM,IAAI,CAAC,UAAU,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,GAAE;AACvF;AACA,MAAM,IAAI,CAAC,UAAU,GAAG,UAAS;AACjC,MAAM,EAAC;AACP,KAAK;AACL;AACA,IAAI,IAAI,SAAQ;AAChB,IAAI,IAAI,QAAQ,GAAG,MAAK;AACxB;AACA,IAAI,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,EAAE;AACzC;AACA,KAAK,QAAQ,GAAG,KAAI;AACpB,KAAK,QAAQ,GAAG,cAAc,CAAC,EAAE,EAAC;AAClC,KAAK,MAAM;AACX;AACA,KAAK,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,OAAM;AAC9C;AACA,KAAK,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,EAAE;AACvC;AACA,MAAM,IAAI;AACV,OAAO,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;AACpD,QAAQ,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;AAChC,QAAQ,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AAC9B,QAAQ,EAAC;AACT;AACA,OAAO,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;AACxC,QAAQ,MAAM,IAAI,KAAK,CAAC,CAAC,qCAAqC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AACxE,QAAQ;AACR,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;AACpC,QAAQ,MAAM,IAAI,KAAK,CAAC,CAAC,mCAAmC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AACtE,QAAQ;AACR;AACA,OAAO,YAAY,GAAG,QAAQ,CAAC,KAAI;AACnC,OAAO,UAAU,GAAG,MAAM,CAAC,KAAI;AAC/B;AACA;AACA,OAAO,IAAI,CAAC,kBAAkB,GAAG;AACjC,QAAQ,GAAG,IAAI,CAAC,kBAAkB;AAClC,QAAQ,CAAC,IAAI,GAAG,EAAE,GAAG,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE;AACvD,SAAQ;AACR,OAAO,CAAC,OAAO,GAAG,EAAE;AACpB,OAAO,MAAM,IAAI,KAAK,CAAC,CAAC,wCAAwC,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACtH,OAAO;AACP,MAAM;AACN;AACA,KAAK,QAAQ,GAAG,kBAAkB,CAAC,YAAY,EAAE,UAAU,EAAC;AAC5D,KAAK;AACL;AACA;AACA,IAAI,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE;AACvC,KAAK,IAAI,CAAC,UAAU,GAAG,aAAY;AACnC,KAAK;AACL;AACA,IAAI,MAAM,KAAK,GAAG,KAAI;AACtB;AACA;AACA;AACA;AACA,IAAI,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,KAAK,SAAS,GAAG,qBAAqB,GAAG,aAAa,CAAC,SAAQ;AAC5G,IAAI,MAAM,QAAQ,GAAG,QAAQ;AAC7B,OAAO,MAAM;AACb;AACA,OAAO,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,EAAC;AAClC,OAAO;AACP,OAAO,MAAM;AACb;AACA,OAAO,MAAM,UAAU,GAAG,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,GAAE;AACvE,OAAO,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,UAAU,EAAC;AAC9C,QAAO;AACP,IAAI,MAAM,SAAS,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAC;AAC/D;AACA,IAAI,MAAM,QAAQ,GAAG,MAAM;AAC3B,KAAK,KAAK,CAAC,eAAe,GAAG,IAAI,IAAI,GAAE;AACvC,KAAK,SAAS,CAAC,GAAG,GAAE;AACpB,MAAK;AACL;AACA,IAAI,MAAM,aAAa,GAAG;AAC1B,KAAK,QAAQ;AACb,KAAK,QAAQ,EAAE,aAAa,CAAC,QAAQ,KAAK,QAAQ,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;AAC7F,MAAK;AACL;AACA,IAAI,MAAM,eAAe,GAAG,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAC;AACpF;AACA;AACA,IAAI,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,GAAG,EAAC;AACvD;AACA;AACA,IAAI,MAAM,eAAe,GAAG,WAAW,CAAC,SAAS,GAAE;AACnD,IAAI,IAAI,eAAe,KAAK,SAAS,EAAE;AACvC,KAAK,IAAI,CAAC,UAAU,GAAG,gBAAe;AACtC,KAAK;AACL;AACA,IAAI,MAAM,MAAM,GAAG;AACnB,KAAK,WAAW,EAAE,IAAI;AACtB,KAAK,IAAI;AACT,KAAK,EAAE;AACP,KAAK,QAAQ;AACb,KAAK,eAAe;AACpB;AACA;AACA,KAAK,qBAAqB,EAAE,SAAS,CAAC,MAAM;AAC5C,MAAK;AACL;AACA;AACA,IAAI,IAAIA,iBAAkB,EAAE;AAC5B,KAAK,IAAI;AACT,MAAMA,iBAAkB,CAAC,MAAM;AAC/B,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,EAAC;AAC/B,OAAO,EAAC;AACR,MAAM,CAAC,MAAM;AACb;AACA,MAAM;AACN,KAAK;AACL;AACA,IAAI,OAAO,MAAM;AACjB,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG,WAAW,CAAC,MAAM,EAAE;AACvB,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,SAAS,EAAE;AAC5D,KAAK,MAAM;AACX,KAAK;AACL,IAAI,MAAM,CAAC,SAAS,GAAG,KAAI;AAC3B;AACA;AACA;AACA;AACA,IAAI,IAAI,OAAO,MAAM,CAAC,qBAAqB,KAAK,UAAU,EAAE;AAC5D,KAAK,MAAM,CAAC,qBAAqB,GAAE;AACnC,KAAK;AACL;AACA,IAAI,MAAM,WAAW,GAAG,cAAc,GAAE;AACxC,IAAI,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC,eAAe,EAAC;AACnD;AACA,IAAI,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,iBAAiB,GAAG,CAAC,EAAC;AACpE,IAAI;AACJ,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,KAAK,CAAC,KAAK,EAAE;AACf;AACA;AACA;AACA;AACA,GAAG,KAAK,CAAC,iBAAiB,GAAG,MAAK;AAClC;AACA;AACA;AACA,GAAG,KAAK,CAAC,sBAAsB,GAAG,IAAI,GAAG,GAAE;AAC3C;AACA;AACA;AACA,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK;AAC9C,IAAI,IAAI,IAAI,KAAK,iBAAiB,EAAE;AACpC,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,KAAI;AACrC,KAAK,KAAK,CAAC,MAAM;AACjB,MAAM,KAAK,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAC;AACpD,MAAM,EAAC;AACP,KAAK;AACL,IAAI,EAAE,IAAI,EAAC;AACX;AACA;AACA;AACA,GAAG,MAAM,cAAc,GAAG,IAAI,GAAG,GAAE;AACnC;AACA,GAAG,MAAM,mBAAmB,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,EAAC;AAC5D,GAAG,KAAK,CAAC,WAAW,GAAG,eAAe,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE;AACnE,IAAI,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE;AAClC,KAAK,OAAO,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC;AACzC,KAAK;AACL,IAAI,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,EAAE,EAAE,EAAC;AACxC,IAAI,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AACjC,KAAK,OAAO,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;AACnC,KAAK;AACL,IAAI,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM;AAChE,KAAK,cAAc,CAAC,MAAM,CAAC,GAAG,EAAC;AAC/B,KAAK,EAAC;AACN,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,EAAC;AACpC,IAAI,OAAO,OAAO;AAClB,KAAI;AACJ;AACA;AACA,GAAG,MAAM,kBAAkB,GAAG,IAAI,GAAG,GAAE;AACvC;AACA,GAAG,MAAM,uBAAuB,GAAG,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,EAAC;AACpE,GAAG,KAAK,CAAC,eAAe,GAAG,eAAe,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,EAAE,OAAO,GAAG,EAAE,EAAE;AAClG,IAAI,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE;AAClC,KAAK,OAAO,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;AAC1D,KAAK;AACL,IAAI,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAC;AAChD,IAAI,IAAI,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,GAAG,EAAC;AAC3C,IAAI,IAAI,CAAC,KAAK,EAAE;AAChB;AACA;AACA;AACA,KAAK,MAAM,OAAO,GAAG,GAAE;AACvB,KAAK,MAAM,OAAO,GAAG,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM;AAClG,MAAM,kBAAkB,CAAC,MAAM,CAAC,GAAG,EAAC;AACpC,MAAM,EAAC;AACP,KAAK,KAAK,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE;AACjC,KAAK,kBAAkB,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAC;AACvC,KAAK;AACL,IAAI,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,QAAO;AACvC,IAAI,MAAM,IAAI,GAAG,OAAO,IAAI,OAAO,CAAC,QAAO;AAC3C,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE;AACtE,KAAK,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAK;AACrC,KAAK;AACL,IAAI,OAAO,OAAO;AAClB,KAAI;AACJ,GAAG;AACH,EAAE;AACF;;;;"}
|
|
@@ -154,6 +154,7 @@ function logsPlugin(options = {}) {
|
|
|
154
154
|
|
|
155
155
|
if (autoRefreshOnItemChange) {
|
|
156
156
|
plugin.setup = function setup(store) {
|
|
157
|
+
// Detached: the store outlives the component whose setup first used it.
|
|
157
158
|
store.$onAction(({ name, after }) => {
|
|
158
159
|
if (name !== 'setItem') {
|
|
159
160
|
return
|
|
@@ -168,7 +169,7 @@ function logsPlugin(options = {}) {
|
|
|
168
169
|
store.clearLogs();
|
|
169
170
|
}
|
|
170
171
|
});
|
|
171
|
-
});
|
|
172
|
+
}, true);
|
|
172
173
|
};
|
|
173
174
|
}
|
|
174
175
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logs.js","sources":["../../../../src/store/plugins/logs.js"],"sourcesContent":["import { trackedFetch } from '../../utils/diagnostics.js'\n// `buildHeaders` is reached via `this._buildHeaders()` so logs fetches\n// inherit the active tenant UUID (multi-tenancy-context).\nimport { genericError, networkError, parseResponseError } from '../../utils/errors.js'\nimport { buildQueryString } from '../../utils/headers.js'\n\n/**\n * Logs sub-resource plugin for createCrudStore.\n *\n * Many apps expose a logs collection keyed off the parent resource ID, via a\n * flat endpoint like `/sources/logs?source_id=<id>`. This plugin generates the\n * standard state, getters, and actions for that pattern so consumer stores\n * don't have to re-implement the same fetch/refresh logic.\n *\n * **Contributed state**\n * - `logs` — last fetched response (raw array or `{ results, ... }` shape)\n * - `logsLoading` — per-store loading flag\n * - `logsError` — last error (`ApiError` or null)\n *\n * **Contributed getters**\n * - `getLogs`, `isLogsLoading`, `getLogsError`\n *\n * **Contributed actions**\n * - `refreshLogs(filters?)` — fetch and store; returns `{ response, data }`\n * - `setLogs(data)` — replace state directly\n * - `clearLogs()` — reset to empty\n *\n * When `autoRefreshOnItemChange: true`, the plugin registers a\n * `store.$onAction` subscriber in its `setup` hook that observes `setItem`\n * and triggers `refreshLogs()` / `clearLogs()` after the action resolves.\n * This composes cleanly with other plugins that also want to observe\n * `setItem` — none of them have to override the action.\n *\n * **URL construction**\n * - `this._options.baseApiUrl + '/' + path` → e.g. `/sources/logs`\n * - Query params: `{ [parentIdParam]: item.id, ...defaultSort, ...filters }` —\n * caller-supplied filters always win.\n *\n * @param {object} options Plugin options\n * @param {string} options.parentIdParam Required. Query param name carrying the\n * parent resource's id — e.g. `'source_id'` for a sources store.\n * @param {string} [options.path] Path segment appended to the store's base API\n * URL. Default: `'logs'`.\n * @param {object} [options.defaultSort] Default query params merged before\n * user filters. Default: `{ '_sort[created]': 'desc' }`.\n * @param {boolean} [options.autoRefreshOnItemChange] When true, overrides\n * `setItem` so selecting a new item automatically refreshes logs. Default:\n * `false`.\n * @return {object} Plugin definition consumed by `createCrudStore`.\n *\n * @example\n * import { createCrudStore, logsPlugin } from '@conduction/nextcloud-vue'\n *\n * export const useSourceStore = createCrudStore('source', {\n * endpoint: 'sources',\n * entity: Source,\n * plugins: [logsPlugin({ parentIdParam: 'source_id', autoRefreshOnItemChange: true })],\n * })\n *\n * // Later\n * const store = useSourceStore()\n * await store.refreshLogs({ '_limit': 50 })\n * console.log(store.logs, store.logsLoading)\n */\nexport function logsPlugin(options = {}) {\n\tconst {\n\t\tparentIdParam,\n\t\tpath = 'logs',\n\t\tdefaultSort = { '_sort[created]': 'desc' },\n\t\tautoRefreshOnItemChange = false,\n\t} = options\n\n\tif (!parentIdParam) {\n\t\tthrow new Error('logsPlugin: options.parentIdParam is required')\n\t}\n\n\tconst actions = {\n\t\t/**\n\t\t * Fetch logs for the current item from the logs endpoint.\n\t\t *\n\t\t * @param {object} [filters] Extra query params (override defaults).\n\t\t * @return {Promise<{ response: Response, data: unknown }>}\n\t\t */\n\t\tasync refreshLogs(filters = {}) {\n\t\t\tthis.logsLoading = true\n\t\t\tthis.logsError = null\n\n\t\t\ttry {\n\t\t\t\tconst params = { ...defaultSort }\n\t\t\t\tif (!(parentIdParam in filters) && this.item?.id !== null && this.item?.id !== undefined) {\n\t\t\t\t\tparams[parentIdParam] = String(this.item.id)\n\t\t\t\t}\n\t\t\t\tObject.assign(params, filters)\n\n\t\t\t\tconst url = this._options.baseApiUrl + '/' + path + buildQueryString(params)\n\t\t\t\tconst response = await trackedFetch(url, {\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: this._buildHeaders(),\n\t\t\t\t})\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthis.logsError = await parseResponseError(response, 'logs')\n\t\t\t\t\treturn { response, data: null }\n\t\t\t\t}\n\n\t\t\t\tconst data = await response.json()\n\t\t\t\tthis.logs = data\n\t\t\t\treturn { response, data }\n\t\t\t} catch (error) {\n\t\t\t\tthis.logsError = error?.name === 'TypeError'\n\t\t\t\t\t? networkError(error)\n\t\t\t\t\t: genericError(error)\n\t\t\t\tthrow error\n\t\t\t} finally {\n\t\t\t\tthis.logsLoading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Replace the logs state directly (e.g. when a parent action returned\n\t\t * logs as a side effect).\n\t\t *\n\t\t * @param {unknown} data Response data (array or `{ results, ... }`).\n\t\t */\n\t\tsetLogs(data) {\n\t\t\tthis.logs = data\n\t\t},\n\n\t\t/**\n\t\t * Reset logs state back to empty defaults.\n\t\t */\n\t\tclearLogs() {\n\t\t\tthis.logs = []\n\t\t\tthis.logsLoading = false\n\t\t\tthis.logsError = null\n\t\t},\n\t}\n\n\tconst plugin = {\n\t\tname: 'logs',\n\n\t\tstate: () => ({\n\t\t\tlogs: [],\n\t\t\tlogsLoading: false,\n\t\t\tlogsError: null,\n\t\t}),\n\n\t\tgetters: {\n\t\t\tgetLogs: (state) => state.logs,\n\t\t\tisLogsLoading: (state) => state.logsLoading,\n\t\t\tgetLogsError: (state) => state.logsError,\n\t\t},\n\n\t\tactions,\n\t}\n\n\tif (autoRefreshOnItemChange) {\n\t\tplugin.setup = function setup(store) {\n\t\t\tstore.$onAction(({ name, after }) => {\n\t\t\t\tif (name !== 'setItem') {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tafter(() => {\n\t\t\t\t\tif (store.item?.id !== null && store.item?.id !== undefined) {\n\t\t\t\t\t\tstore.refreshLogs().catch((error) => {\n\t\t\t\t\t\t\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\t\t\t\tconsole.error('logsPlugin: auto-refresh failed:', error)\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstore.clearLogs()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t}\n\n\treturn plugin\n}\n"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"logs.js","sources":["../../../../src/store/plugins/logs.js"],"sourcesContent":["import { trackedFetch } from '../../utils/diagnostics.js'\n// `buildHeaders` is reached via `this._buildHeaders()` so logs fetches\n// inherit the active tenant UUID (multi-tenancy-context).\nimport { genericError, networkError, parseResponseError } from '../../utils/errors.js'\nimport { buildQueryString } from '../../utils/headers.js'\n\n/**\n * Logs sub-resource plugin for createCrudStore.\n *\n * Many apps expose a logs collection keyed off the parent resource ID, via a\n * flat endpoint like `/sources/logs?source_id=<id>`. This plugin generates the\n * standard state, getters, and actions for that pattern so consumer stores\n * don't have to re-implement the same fetch/refresh logic.\n *\n * **Contributed state**\n * - `logs` — last fetched response (raw array or `{ results, ... }` shape)\n * - `logsLoading` — per-store loading flag\n * - `logsError` — last error (`ApiError` or null)\n *\n * **Contributed getters**\n * - `getLogs`, `isLogsLoading`, `getLogsError`\n *\n * **Contributed actions**\n * - `refreshLogs(filters?)` — fetch and store; returns `{ response, data }`\n * - `setLogs(data)` — replace state directly\n * - `clearLogs()` — reset to empty\n *\n * When `autoRefreshOnItemChange: true`, the plugin registers a\n * `store.$onAction` subscriber in its `setup` hook that observes `setItem`\n * and triggers `refreshLogs()` / `clearLogs()` after the action resolves.\n * This composes cleanly with other plugins that also want to observe\n * `setItem` — none of them have to override the action.\n *\n * **URL construction**\n * - `this._options.baseApiUrl + '/' + path` → e.g. `/sources/logs`\n * - Query params: `{ [parentIdParam]: item.id, ...defaultSort, ...filters }` —\n * caller-supplied filters always win.\n *\n * @param {object} options Plugin options\n * @param {string} options.parentIdParam Required. Query param name carrying the\n * parent resource's id — e.g. `'source_id'` for a sources store.\n * @param {string} [options.path] Path segment appended to the store's base API\n * URL. Default: `'logs'`.\n * @param {object} [options.defaultSort] Default query params merged before\n * user filters. Default: `{ '_sort[created]': 'desc' }`.\n * @param {boolean} [options.autoRefreshOnItemChange] When true, overrides\n * `setItem` so selecting a new item automatically refreshes logs. Default:\n * `false`.\n * @return {object} Plugin definition consumed by `createCrudStore`.\n *\n * @example\n * import { createCrudStore, logsPlugin } from '@conduction/nextcloud-vue'\n *\n * export const useSourceStore = createCrudStore('source', {\n * endpoint: 'sources',\n * entity: Source,\n * plugins: [logsPlugin({ parentIdParam: 'source_id', autoRefreshOnItemChange: true })],\n * })\n *\n * // Later\n * const store = useSourceStore()\n * await store.refreshLogs({ '_limit': 50 })\n * console.log(store.logs, store.logsLoading)\n */\nexport function logsPlugin(options = {}) {\n\tconst {\n\t\tparentIdParam,\n\t\tpath = 'logs',\n\t\tdefaultSort = { '_sort[created]': 'desc' },\n\t\tautoRefreshOnItemChange = false,\n\t} = options\n\n\tif (!parentIdParam) {\n\t\tthrow new Error('logsPlugin: options.parentIdParam is required')\n\t}\n\n\tconst actions = {\n\t\t/**\n\t\t * Fetch logs for the current item from the logs endpoint.\n\t\t *\n\t\t * @param {object} [filters] Extra query params (override defaults).\n\t\t * @return {Promise<{ response: Response, data: unknown }>}\n\t\t */\n\t\tasync refreshLogs(filters = {}) {\n\t\t\tthis.logsLoading = true\n\t\t\tthis.logsError = null\n\n\t\t\ttry {\n\t\t\t\tconst params = { ...defaultSort }\n\t\t\t\tif (!(parentIdParam in filters) && this.item?.id !== null && this.item?.id !== undefined) {\n\t\t\t\t\tparams[parentIdParam] = String(this.item.id)\n\t\t\t\t}\n\t\t\t\tObject.assign(params, filters)\n\n\t\t\t\tconst url = this._options.baseApiUrl + '/' + path + buildQueryString(params)\n\t\t\t\tconst response = await trackedFetch(url, {\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: this._buildHeaders(),\n\t\t\t\t})\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthis.logsError = await parseResponseError(response, 'logs')\n\t\t\t\t\treturn { response, data: null }\n\t\t\t\t}\n\n\t\t\t\tconst data = await response.json()\n\t\t\t\tthis.logs = data\n\t\t\t\treturn { response, data }\n\t\t\t} catch (error) {\n\t\t\t\tthis.logsError = error?.name === 'TypeError'\n\t\t\t\t\t? networkError(error)\n\t\t\t\t\t: genericError(error)\n\t\t\t\tthrow error\n\t\t\t} finally {\n\t\t\t\tthis.logsLoading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Replace the logs state directly (e.g. when a parent action returned\n\t\t * logs as a side effect).\n\t\t *\n\t\t * @param {unknown} data Response data (array or `{ results, ... }`).\n\t\t */\n\t\tsetLogs(data) {\n\t\t\tthis.logs = data\n\t\t},\n\n\t\t/**\n\t\t * Reset logs state back to empty defaults.\n\t\t */\n\t\tclearLogs() {\n\t\t\tthis.logs = []\n\t\t\tthis.logsLoading = false\n\t\t\tthis.logsError = null\n\t\t},\n\t}\n\n\tconst plugin = {\n\t\tname: 'logs',\n\n\t\tstate: () => ({\n\t\t\tlogs: [],\n\t\t\tlogsLoading: false,\n\t\t\tlogsError: null,\n\t\t}),\n\n\t\tgetters: {\n\t\t\tgetLogs: (state) => state.logs,\n\t\t\tisLogsLoading: (state) => state.logsLoading,\n\t\t\tgetLogsError: (state) => state.logsError,\n\t\t},\n\n\t\tactions,\n\t}\n\n\tif (autoRefreshOnItemChange) {\n\t\tplugin.setup = function setup(store) {\n\t\t\t// Detached: the store outlives the component whose setup first used it.\n\t\t\tstore.$onAction(({ name, after }) => {\n\t\t\t\tif (name !== 'setItem') {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tafter(() => {\n\t\t\t\t\tif (store.item?.id !== null && store.item?.id !== undefined) {\n\t\t\t\t\t\tstore.refreshLogs().catch((error) => {\n\t\t\t\t\t\t\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\t\t\t\tconsole.error('logsPlugin: auto-refresh failed:', error)\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstore.clearLogs()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}, true)\n\t\t}\n\t}\n\n\treturn plugin\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { variantForColor } from './headerFieldChips.js';
|
|
1
2
|
import { readPath } from './readPath.js';
|
|
2
3
|
import { evaluateVisibleWhenLocal } from './visibleWhen.js';
|
|
3
4
|
|
|
@@ -240,6 +241,32 @@ function resolvePill(config, object) {
|
|
|
240
241
|
}
|
|
241
242
|
}
|
|
242
243
|
|
|
244
|
+
/**
|
|
245
|
+
* What a pill takes from the record its `resolve` points at
|
|
246
|
+
* (`resolve.colorField`, `resolve.iconField`): the tone its colour name maps
|
|
247
|
+
* onto (an NL Design System hue or a badge role, see `variantForColor`), which
|
|
248
|
+
* replaces the pill's own `variant` and `colorMap`, and an icon name. An empty
|
|
249
|
+
* colour or icon leaves the pill as declared.
|
|
250
|
+
*
|
|
251
|
+
* @param {{color?: string|null, icon?: string|null}|null|undefined} look The names read off the record.
|
|
252
|
+
* @return {{variant?: string, colorMap?: null, icon?: string}} The keys to lay over the pill.
|
|
253
|
+
* @spec openspec/changes/pill-color-field/specs/pill-color-field/spec.md#requirement-a-pill-takes-its-tone-and-icon-from-the-referenced-record
|
|
254
|
+
*/
|
|
255
|
+
function pillLook(look) {
|
|
256
|
+
const out = {};
|
|
257
|
+
if (!look || typeof look !== 'object') {
|
|
258
|
+
return out
|
|
259
|
+
}
|
|
260
|
+
if (typeof look.color === 'string' && look.color !== '') {
|
|
261
|
+
out.variant = variantForColor(look.color);
|
|
262
|
+
out.colorMap = null;
|
|
263
|
+
}
|
|
264
|
+
if (typeof look.icon === 'string' && look.icon !== '') {
|
|
265
|
+
out.icon = look.icon;
|
|
266
|
+
}
|
|
267
|
+
return out
|
|
268
|
+
}
|
|
269
|
+
|
|
243
270
|
/**
|
|
244
271
|
* Resolve a tab's count.
|
|
245
272
|
*
|
|
@@ -319,5 +346,5 @@ function resolveCountPill(config, count, translate = (text) => text) {
|
|
|
319
346
|
}
|
|
320
347
|
}
|
|
321
348
|
|
|
322
|
-
export { MAX_QUICK_ACTIONS, groupMenuEntries, isChecklistItemDone, normalisePinnedAction, pillCountSource, resolveCountPill, resolveNextStep, resolvePill, resolveTabCount, stageEntry, stageOf };
|
|
349
|
+
export { MAX_QUICK_ACTIONS, groupMenuEntries, isChecklistItemDone, normalisePinnedAction, pillCountSource, pillLook, resolveCountPill, resolveNextStep, resolvePill, resolveTabCount, stageEntry, stageOf };
|
|
323
350
|
//# sourceMappingURL=detailActionModel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detailActionModel.js","sources":["../../../src/utils/detailActionModel.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * detailActionModel: the rules behind a detail page's four action levels and\n * the case surfaces that read the record's stage.\n *\n * 1. the next step: one primary button, chosen by the record's stage,\n * 2. quick actions: at most three always visible buttons,\n * 3. the overflow menu: the rest, in named groups,\n * 4. admin actions: the last group, for admins only.\n *\n * Pure functions, no Vue and no fetch, so CnDetailPage, the widgets and the\n * tests all read one implementation.\n *\n * @module utils/detailActionModel\n */\n\nimport { readPath } from './readPath.js'\nimport { evaluateVisibleWhenLocal } from './visibleWhen.js'\n\n/** The most quick actions a page shows. Later entries are dropped. */\nexport const MAX_QUICK_ACTIONS = 3\n\n/**\n * The record's stage, as the string a stage map is keyed on.\n *\n * @param {object|null} object The record.\n * @param {string} [stageField] Dot-path to the stage. Defaults to `status`.\n * @return {string} The stage, or '' when the record has none.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage\n */\nexport function stageOf(object, stageField = 'status') {\n\tif (!object || typeof object !== 'object') {\n\t\treturn ''\n\t}\n\tconst value = readPath(object, stageField || 'status')\n\tif (value === null || value === undefined || typeof value === 'object') {\n\t\treturn ''\n\t}\n\treturn String(value)\n}\n\n/**\n * Look a stage up in a stage-keyed map. An exact key wins; otherwise the\n * match ignores case, because a status is stored as `in_behandeling` in one\n * register and `In_Behandeling` in the next.\n *\n * @param {object|null} map The stage-keyed map.\n * @param {string} stage The stage.\n * @return {unknown} The entry, or undefined.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage\n */\nexport function stageEntry(map, stage) {\n\tif (!map || typeof map !== 'object' || Array.isArray(map) || stage === '') {\n\t\treturn undefined\n\t}\n\tif (Object.hasOwn(map, stage)) {\n\t\treturn map[stage]\n\t}\n\tconst wanted = stage.toLowerCase()\n\tconst key = Object.keys(map).find((candidate) => candidate.toLowerCase() === wanted)\n\treturn key === undefined ? undefined : map[key]\n}\n\n/**\n * Turn a pinned action entry into a descriptor the page can dispatch.\n *\n * An entry is the id of a declared header action, or an action object of its\n * own. An object without an id gets `fallbackId`, since every dispatched\n * action is addressed by id.\n *\n * @param {string|object|null} entry The entry.\n * @param {string} fallbackId Id for an inline action that names none.\n * @return {{id: string, inline: object|null}|null} The id, plus the inline definition when the entry carried one.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-quick-actions\n */\nexport function normalisePinnedAction(entry, fallbackId) {\n\tif (typeof entry === 'string') {\n\t\treturn entry === '' ? null : { id: entry, inline: null }\n\t}\n\tif (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n\t\treturn null\n\t}\n\tif (typeof entry.label !== 'string' || entry.label === '') {\n\t\treturn null\n\t}\n\tconst id = typeof entry.id === 'string' && entry.id !== '' ? entry.id : fallbackId\n\treturn { id, inline: { ...entry, id } }\n}\n\n/**\n * Split menu entries into the groups the overflow menu draws.\n *\n * Ungrouped entries come first, under no caption. Named groups follow in the\n * order they first appear. Admin only entries are dropped for everyone else\n * and form the last group for admins, whatever `group` they also name.\n *\n * `adminOnly` hides a control. It is never an authorization decision: the\n * endpoint behind the action decides who may call it.\n *\n * @param {Array<object>} entries Menu-ready entries, each with an `id`.\n * @param {object} actionsById The declared actions by id, carrying `group` / `adminOnly`.\n * @param {{isAdmin?: boolean, adminLabel?: string}} [options] Who is looking, and what the admin group is called.\n * @return {Array<{key: string, label: string, entries: Array<object>}>} The non-empty groups, in render order.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-grouped-and-admin-only-menu-actions\n */\nexport function groupMenuEntries(entries, actionsById = {}, options = {}) {\n\tconst isAdmin = options.isAdmin === true\n\tconst plain = { key: '', label: '', entries: [] }\n\tconst named = []\n\tconst admin = { key: 'cn-admin', label: options.adminLabel || '', entries: [] }\n\tfor (const entry of Array.isArray(entries) ? entries : []) {\n\t\tconst declared = (entry && actionsById[entry.id]) || {}\n\t\tif (declared.adminOnly === true) {\n\t\t\tif (isAdmin) {\n\t\t\t\tadmin.entries.push(entry)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tconst label = typeof declared.group === 'string' ? declared.group.trim() : ''\n\t\tif (label === '') {\n\t\t\tplain.entries.push(entry)\n\t\t\tcontinue\n\t\t}\n\t\tlet group = named.find((candidate) => candidate.label === label)\n\t\tif (!group) {\n\t\t\tgroup = { key: `cn-group-${named.length}`, label, entries: [] }\n\t\t\tnamed.push(group)\n\t\t}\n\t\tgroup.entries.push(entry)\n\t}\n\treturn [plain, ...named, admin].filter((group) => group.entries.length > 0)\n}\n\n/**\n * Whether one checklist item is done for this record.\n *\n * `done` (a literal) wins, then `doneWhen` (a local visibleWhen condition),\n * then `doneField` (truthy, and for a list: not empty).\n *\n * @param {object} item The checklist item.\n * @param {object|null} object The record.\n * @return {boolean} True when the item is done.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card\n */\nexport function isChecklistItemDone(item, object) {\n\tif (!item || typeof item !== 'object') {\n\t\treturn false\n\t}\n\tif (typeof item.done === 'boolean') {\n\t\treturn item.done\n\t}\n\tconst data = object && typeof object === 'object' ? object : {}\n\tif (item.doneWhen && typeof item.doneWhen === 'object') {\n\t\treturn evaluateVisibleWhenLocal(item.doneWhen, data)\n\t}\n\tif (typeof item.doneField === 'string' && item.doneField !== '') {\n\t\tconst value = readPath(data, item.doneField)\n\t\treturn Array.isArray(value) ? value.length > 0 : Boolean(value)\n\t}\n\treturn false\n}\n\n/**\n * Resolve the \"what now\" card for the record's stage.\n *\n * @param {{stages?: object, title?: string}|null} config The `nextStep` config.\n * @param {object|null} object The record.\n * @param {string} [stageField] Dot-path to the stage.\n * @return {{stage: string, title: string, items: Array<{label: string, done: boolean, hint: string}>, after: string}|null} The card, or null when this stage declares no checklist.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card\n */\nexport function resolveNextStep(config, object, stageField = 'status') {\n\tif (!config || typeof config !== 'object') {\n\t\treturn null\n\t}\n\tconst stage = stageOf(object, config.field || stageField)\n\tconst declared = stageEntry(config.stages, stage)\n\tif (!declared || typeof declared !== 'object' || !Array.isArray(declared.checklist)) {\n\t\treturn null\n\t}\n\tconst items = declared.checklist\n\t\t.filter((item) => item && typeof item === 'object' && typeof item.label === 'string' && item.label !== '')\n\t\t.map((item) => ({\n\t\t\tlabel: item.label,\n\t\t\tdone: isChecklistItemDone(item, object),\n\t\t\thint: typeof item.hint === 'string' ? item.hint : '',\n\t\t}))\n\tif (items.length === 0) {\n\t\treturn null\n\t}\n\treturn {\n\t\tstage,\n\t\ttitle: typeof declared.title === 'string' ? declared.title : (typeof config.title === 'string' ? config.title : ''),\n\t\titems,\n\t\tafter: typeof declared.after === 'string' ? declared.after : '',\n\t}\n}\n\n/**\n * Resolve a header pill from the record.\n *\n * A pill with `field` reads it off the record; one without `field` and with\n * `text` is a constant. A count pill (`countFrom`) is resolved by\n * {@link resolveCountPill} once its count is known.\n *\n * @param {{field?: string, text?: string, colorMap?: object, labels?: object, variant?: string}|null} config The pill config.\n * @param {object|null} object The record.\n * @return {{label: string, colorKey: string, colorMap: object|null, variant: string}|null} What CnStatusBadge needs, or null when the field is empty.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-header-pills\n */\nexport function resolvePill(config, object) {\n\tif (!config || typeof config !== 'object') {\n\t\treturn null\n\t}\n\t// A constant pill (`text`): \"Inwoner\", \"Klant\" (screens-relation-pages).\n\tif ((typeof config.field !== 'string' || config.field === '') && typeof config.text === 'string' && config.text !== '') {\n\t\treturn {\n\t\t\tlabel: config.text,\n\t\t\tcolorKey: config.text,\n\t\t\tcolorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,\n\t\t\tvariant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',\n\t\t}\n\t}\n\tif (typeof config.field !== 'string' || config.field === '') {\n\t\treturn null\n\t}\n\tconst raw = stageOf(object, config.field)\n\tif (raw === '') {\n\t\treturn null\n\t}\n\tconst mapped = stageEntry(config.labels, raw)\n\treturn {\n\t\tlabel: typeof mapped === 'string' && mapped !== '' ? mapped : raw,\n\t\tcolorKey: raw,\n\t\tcolorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,\n\t\tvariant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',\n\t}\n}\n\n/**\n * Resolve a tab's count.\n *\n * `count` (a number) wins. Otherwise `countField` is read off the record: a\n * list counts its items, a number is taken as is. A tab that declares neither\n * takes what is new on it from `@self.unreadCounts[<unreadKey>]` (the key\n * defaults to the tab's `id`, else its `widgetId`) when that is above 0;\n * absent or 0 is no count.\n *\n * @param {{count?: number, countField?: string, unreadKey?: string, id?: string, widgetId?: string}|null} tab The tab declaration.\n * @param {object|null} object The record.\n * @return {number|null} The count, or null when the tab declares none.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-tab-counts-and-overflow\n */\nexport function resolveTabCount(tab, object) {\n\tif (!tab || typeof tab !== 'object') {\n\t\treturn null\n\t}\n\tif (typeof tab.count === 'number' && Number.isFinite(tab.count)) {\n\t\treturn tab.count\n\t}\n\tif (typeof tab.countField !== 'string' || tab.countField === '') {\n\t\tconst counts = object && typeof object === 'object' && object['@self'] && object['@self'].unreadCounts\n\t\tconst key = tab.unreadKey || tab.id || tab.widgetId\n\t\tconst unread = counts && typeof counts === 'object' && typeof key === 'string' ? Number(counts[key]) : 0\n\t\treturn Number.isFinite(unread) && unread > 0 ? unread : null\n\t}\n\tconst value = readPath(object && typeof object === 'object' ? object : {}, tab.countField)\n\tif (Array.isArray(value)) {\n\t\treturn value.length\n\t}\n\tif (value === null || value === undefined || value === '') {\n\t\treturn 0\n\t}\n\tconst number = Number(value)\n\treturn Number.isFinite(number) ? number : null\n}\n\n/**\n * The list a count pill counts (`countFrom`), or null for any other pill.\n *\n * @param {object|null} config The pill config.\n * @return {{register: string, schema: string, filter: object}|null} The list.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-header-pill-can-be-a-constant-or-a-count\n */\nexport function pillCountSource(config) {\n\tconst from = config && typeof config === 'object' && !config.field ? config.countFrom : null\n\tif (!from || typeof from !== 'object' || typeof from.register !== 'string' || from.register === '' || typeof from.schema !== 'string' || from.schema === '') {\n\t\treturn null\n\t}\n\tconst filter = from.filter && typeof from.filter === 'object' && !Array.isArray(from.filter) ? from.filter : {}\n\treturn { register: from.register, schema: from.schema, filter }\n}\n\n/**\n * A count pill once its count is known: \"1 lopende zaak\", \"2 lopende zaken\".\n * Nothing for a count of 0, an unknown count or a failed one.\n *\n * @param {{countLabel?: {one?: string, other?: string}, colorMap?: object, variant?: string}|null} config The pill config.\n * @param {number|null|undefined} count The count.\n * @param {(text: string) => string} [translate] Applied to the label before `{count}` is filled.\n * @return {{label: string, colorKey: string, colorMap: object|null, variant: string}|null} What CnStatusBadge needs, or null.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-header-pill-can-be-a-constant-or-a-count\n */\nexport function resolveCountPill(config, count, translate = (text) => text) {\n\tif (!pillCountSource(config) || typeof count !== 'number' || !Number.isFinite(count) || count <= 0) {\n\t\treturn null\n\t}\n\tconst labels = config.countLabel && typeof config.countLabel === 'object' ? config.countLabel : {}\n\tconst template = (count === 1 ? labels.one : labels.other) || labels.other || labels.one || '{count}'\n\tconst label = translate(template).replace(/\\{count\\}/g, String(count))\n\treturn {\n\t\tlabel,\n\t\tcolorKey: '',\n\t\tcolorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,\n\t\tvariant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"detailActionModel.js","sources":["../../../src/utils/detailActionModel.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * detailActionModel: the rules behind a detail page's four action levels and\n * the case surfaces that read the record's stage.\n *\n * 1. the next step: one primary button, chosen by the record's stage,\n * 2. quick actions: at most three always visible buttons,\n * 3. the overflow menu: the rest, in named groups,\n * 4. admin actions: the last group, for admins only.\n *\n * Pure functions, no Vue and no fetch, so CnDetailPage, the widgets and the\n * tests all read one implementation.\n *\n * @module utils/detailActionModel\n */\n\nimport { variantForColor } from './headerFieldChips.js'\nimport { readPath } from './readPath.js'\nimport { evaluateVisibleWhenLocal } from './visibleWhen.js'\n\n/** The most quick actions a page shows. Later entries are dropped. */\nexport const MAX_QUICK_ACTIONS = 3\n\n/**\n * The record's stage, as the string a stage map is keyed on.\n *\n * @param {object|null} object The record.\n * @param {string} [stageField] Dot-path to the stage. Defaults to `status`.\n * @return {string} The stage, or '' when the record has none.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage\n */\nexport function stageOf(object, stageField = 'status') {\n\tif (!object || typeof object !== 'object') {\n\t\treturn ''\n\t}\n\tconst value = readPath(object, stageField || 'status')\n\tif (value === null || value === undefined || typeof value === 'object') {\n\t\treturn ''\n\t}\n\treturn String(value)\n}\n\n/**\n * Look a stage up in a stage-keyed map. An exact key wins; otherwise the\n * match ignores case, because a status is stored as `in_behandeling` in one\n * register and `In_Behandeling` in the next.\n *\n * @param {object|null} map The stage-keyed map.\n * @param {string} stage The stage.\n * @return {unknown} The entry, or undefined.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage\n */\nexport function stageEntry(map, stage) {\n\tif (!map || typeof map !== 'object' || Array.isArray(map) || stage === '') {\n\t\treturn undefined\n\t}\n\tif (Object.hasOwn(map, stage)) {\n\t\treturn map[stage]\n\t}\n\tconst wanted = stage.toLowerCase()\n\tconst key = Object.keys(map).find((candidate) => candidate.toLowerCase() === wanted)\n\treturn key === undefined ? undefined : map[key]\n}\n\n/**\n * Turn a pinned action entry into a descriptor the page can dispatch.\n *\n * An entry is the id of a declared header action, or an action object of its\n * own. An object without an id gets `fallbackId`, since every dispatched\n * action is addressed by id.\n *\n * @param {string|object|null} entry The entry.\n * @param {string} fallbackId Id for an inline action that names none.\n * @return {{id: string, inline: object|null}|null} The id, plus the inline definition when the entry carried one.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-quick-actions\n */\nexport function normalisePinnedAction(entry, fallbackId) {\n\tif (typeof entry === 'string') {\n\t\treturn entry === '' ? null : { id: entry, inline: null }\n\t}\n\tif (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n\t\treturn null\n\t}\n\tif (typeof entry.label !== 'string' || entry.label === '') {\n\t\treturn null\n\t}\n\tconst id = typeof entry.id === 'string' && entry.id !== '' ? entry.id : fallbackId\n\treturn { id, inline: { ...entry, id } }\n}\n\n/**\n * Split menu entries into the groups the overflow menu draws.\n *\n * Ungrouped entries come first, under no caption. Named groups follow in the\n * order they first appear. Admin only entries are dropped for everyone else\n * and form the last group for admins, whatever `group` they also name.\n *\n * `adminOnly` hides a control. It is never an authorization decision: the\n * endpoint behind the action decides who may call it.\n *\n * @param {Array<object>} entries Menu-ready entries, each with an `id`.\n * @param {object} actionsById The declared actions by id, carrying `group` / `adminOnly`.\n * @param {{isAdmin?: boolean, adminLabel?: string}} [options] Who is looking, and what the admin group is called.\n * @return {Array<{key: string, label: string, entries: Array<object>}>} The non-empty groups, in render order.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-grouped-and-admin-only-menu-actions\n */\nexport function groupMenuEntries(entries, actionsById = {}, options = {}) {\n\tconst isAdmin = options.isAdmin === true\n\tconst plain = { key: '', label: '', entries: [] }\n\tconst named = []\n\tconst admin = { key: 'cn-admin', label: options.adminLabel || '', entries: [] }\n\tfor (const entry of Array.isArray(entries) ? entries : []) {\n\t\tconst declared = (entry && actionsById[entry.id]) || {}\n\t\tif (declared.adminOnly === true) {\n\t\t\tif (isAdmin) {\n\t\t\t\tadmin.entries.push(entry)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tconst label = typeof declared.group === 'string' ? declared.group.trim() : ''\n\t\tif (label === '') {\n\t\t\tplain.entries.push(entry)\n\t\t\tcontinue\n\t\t}\n\t\tlet group = named.find((candidate) => candidate.label === label)\n\t\tif (!group) {\n\t\t\tgroup = { key: `cn-group-${named.length}`, label, entries: [] }\n\t\t\tnamed.push(group)\n\t\t}\n\t\tgroup.entries.push(entry)\n\t}\n\treturn [plain, ...named, admin].filter((group) => group.entries.length > 0)\n}\n\n/**\n * Whether one checklist item is done for this record.\n *\n * `done` (a literal) wins, then `doneWhen` (a local visibleWhen condition),\n * then `doneField` (truthy, and for a list: not empty).\n *\n * @param {object} item The checklist item.\n * @param {object|null} object The record.\n * @return {boolean} True when the item is done.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card\n */\nexport function isChecklistItemDone(item, object) {\n\tif (!item || typeof item !== 'object') {\n\t\treturn false\n\t}\n\tif (typeof item.done === 'boolean') {\n\t\treturn item.done\n\t}\n\tconst data = object && typeof object === 'object' ? object : {}\n\tif (item.doneWhen && typeof item.doneWhen === 'object') {\n\t\treturn evaluateVisibleWhenLocal(item.doneWhen, data)\n\t}\n\tif (typeof item.doneField === 'string' && item.doneField !== '') {\n\t\tconst value = readPath(data, item.doneField)\n\t\treturn Array.isArray(value) ? value.length > 0 : Boolean(value)\n\t}\n\treturn false\n}\n\n/**\n * Resolve the \"what now\" card for the record's stage.\n *\n * @param {{stages?: object, title?: string}|null} config The `nextStep` config.\n * @param {object|null} object The record.\n * @param {string} [stageField] Dot-path to the stage.\n * @return {{stage: string, title: string, items: Array<{label: string, done: boolean, hint: string}>, after: string}|null} The card, or null when this stage declares no checklist.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card\n */\nexport function resolveNextStep(config, object, stageField = 'status') {\n\tif (!config || typeof config !== 'object') {\n\t\treturn null\n\t}\n\tconst stage = stageOf(object, config.field || stageField)\n\tconst declared = stageEntry(config.stages, stage)\n\tif (!declared || typeof declared !== 'object' || !Array.isArray(declared.checklist)) {\n\t\treturn null\n\t}\n\tconst items = declared.checklist\n\t\t.filter((item) => item && typeof item === 'object' && typeof item.label === 'string' && item.label !== '')\n\t\t.map((item) => ({\n\t\t\tlabel: item.label,\n\t\t\tdone: isChecklistItemDone(item, object),\n\t\t\thint: typeof item.hint === 'string' ? item.hint : '',\n\t\t}))\n\tif (items.length === 0) {\n\t\treturn null\n\t}\n\treturn {\n\t\tstage,\n\t\ttitle: typeof declared.title === 'string' ? declared.title : (typeof config.title === 'string' ? config.title : ''),\n\t\titems,\n\t\tafter: typeof declared.after === 'string' ? declared.after : '',\n\t}\n}\n\n/**\n * Resolve a header pill from the record.\n *\n * A pill with `field` reads it off the record; one without `field` and with\n * `text` is a constant. A count pill (`countFrom`) is resolved by\n * {@link resolveCountPill} once its count is known.\n *\n * @param {{field?: string, text?: string, colorMap?: object, labels?: object, variant?: string}|null} config The pill config.\n * @param {object|null} object The record.\n * @return {{label: string, colorKey: string, colorMap: object|null, variant: string}|null} What CnStatusBadge needs, or null when the field is empty.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-header-pills\n */\nexport function resolvePill(config, object) {\n\tif (!config || typeof config !== 'object') {\n\t\treturn null\n\t}\n\t// A constant pill (`text`): \"Inwoner\", \"Klant\" (screens-relation-pages).\n\tif ((typeof config.field !== 'string' || config.field === '') && typeof config.text === 'string' && config.text !== '') {\n\t\treturn {\n\t\t\tlabel: config.text,\n\t\t\tcolorKey: config.text,\n\t\t\tcolorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,\n\t\t\tvariant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',\n\t\t}\n\t}\n\tif (typeof config.field !== 'string' || config.field === '') {\n\t\treturn null\n\t}\n\tconst raw = stageOf(object, config.field)\n\tif (raw === '') {\n\t\treturn null\n\t}\n\tconst mapped = stageEntry(config.labels, raw)\n\treturn {\n\t\tlabel: typeof mapped === 'string' && mapped !== '' ? mapped : raw,\n\t\tcolorKey: raw,\n\t\tcolorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,\n\t\tvariant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',\n\t}\n}\n\n/**\n * What a pill takes from the record its `resolve` points at\n * (`resolve.colorField`, `resolve.iconField`): the tone its colour name maps\n * onto (an NL Design System hue or a badge role, see `variantForColor`), which\n * replaces the pill's own `variant` and `colorMap`, and an icon name. An empty\n * colour or icon leaves the pill as declared.\n *\n * @param {{color?: string|null, icon?: string|null}|null|undefined} look The names read off the record.\n * @return {{variant?: string, colorMap?: null, icon?: string}} The keys to lay over the pill.\n * @spec openspec/changes/pill-color-field/specs/pill-color-field/spec.md#requirement-a-pill-takes-its-tone-and-icon-from-the-referenced-record\n */\nexport function pillLook(look) {\n\tconst out = {}\n\tif (!look || typeof look !== 'object') {\n\t\treturn out\n\t}\n\tif (typeof look.color === 'string' && look.color !== '') {\n\t\tout.variant = variantForColor(look.color)\n\t\tout.colorMap = null\n\t}\n\tif (typeof look.icon === 'string' && look.icon !== '') {\n\t\tout.icon = look.icon\n\t}\n\treturn out\n}\n\n/**\n * Resolve a tab's count.\n *\n * `count` (a number) wins. Otherwise `countField` is read off the record: a\n * list counts its items, a number is taken as is. A tab that declares neither\n * takes what is new on it from `@self.unreadCounts[<unreadKey>]` (the key\n * defaults to the tab's `id`, else its `widgetId`) when that is above 0;\n * absent or 0 is no count.\n *\n * @param {{count?: number, countField?: string, unreadKey?: string, id?: string, widgetId?: string}|null} tab The tab declaration.\n * @param {object|null} object The record.\n * @return {number|null} The count, or null when the tab declares none.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-tab-counts-and-overflow\n */\nexport function resolveTabCount(tab, object) {\n\tif (!tab || typeof tab !== 'object') {\n\t\treturn null\n\t}\n\tif (typeof tab.count === 'number' && Number.isFinite(tab.count)) {\n\t\treturn tab.count\n\t}\n\tif (typeof tab.countField !== 'string' || tab.countField === '') {\n\t\tconst counts = object && typeof object === 'object' && object['@self'] && object['@self'].unreadCounts\n\t\tconst key = tab.unreadKey || tab.id || tab.widgetId\n\t\tconst unread = counts && typeof counts === 'object' && typeof key === 'string' ? Number(counts[key]) : 0\n\t\treturn Number.isFinite(unread) && unread > 0 ? unread : null\n\t}\n\tconst value = readPath(object && typeof object === 'object' ? object : {}, tab.countField)\n\tif (Array.isArray(value)) {\n\t\treturn value.length\n\t}\n\tif (value === null || value === undefined || value === '') {\n\t\treturn 0\n\t}\n\tconst number = Number(value)\n\treturn Number.isFinite(number) ? number : null\n}\n\n/**\n * The list a count pill counts (`countFrom`), or null for any other pill.\n *\n * @param {object|null} config The pill config.\n * @return {{register: string, schema: string, filter: object}|null} The list.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-header-pill-can-be-a-constant-or-a-count\n */\nexport function pillCountSource(config) {\n\tconst from = config && typeof config === 'object' && !config.field ? config.countFrom : null\n\tif (!from || typeof from !== 'object' || typeof from.register !== 'string' || from.register === '' || typeof from.schema !== 'string' || from.schema === '') {\n\t\treturn null\n\t}\n\tconst filter = from.filter && typeof from.filter === 'object' && !Array.isArray(from.filter) ? from.filter : {}\n\treturn { register: from.register, schema: from.schema, filter }\n}\n\n/**\n * A count pill once its count is known: \"1 lopende zaak\", \"2 lopende zaken\".\n * Nothing for a count of 0, an unknown count or a failed one.\n *\n * @param {{countLabel?: {one?: string, other?: string}, colorMap?: object, variant?: string}|null} config The pill config.\n * @param {number|null|undefined} count The count.\n * @param {(text: string) => string} [translate] Applied to the label before `{count}` is filled.\n * @return {{label: string, colorKey: string, colorMap: object|null, variant: string}|null} What CnStatusBadge needs, or null.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-header-pill-can-be-a-constant-or-a-count\n */\nexport function resolveCountPill(config, count, translate = (text) => text) {\n\tif (!pillCountSource(config) || typeof count !== 'number' || !Number.isFinite(count) || count <= 0) {\n\t\treturn null\n\t}\n\tconst labels = config.countLabel && typeof config.countLabel === 'object' ? config.countLabel : {}\n\tconst template = (count === 1 ? labels.one : labels.other) || labels.other || labels.one || '{count}'\n\tconst label = translate(template).replace(/\\{count\\}/g, String(count))\n\treturn {\n\t\tlabel,\n\t\tcolorKey: '',\n\t\tcolorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,\n\t\tvariant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ import { userDisplayName } from './userDisplayName.js';
|
|
|
8
8
|
// SPDX-License-Identifier: EUPL-1.2
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
const TOKEN = /\{([^{}\s|]+)(?:\|([
|
|
11
|
+
const TOKEN = /\{([^{}\s|]+)(?:\|([A-Za-z]+)(?::([^{}\s]+))?)?\}/g;
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* A moment in the board's short form with the time: "4 okt, 10:52", or
|
|
@@ -69,12 +69,18 @@ function formatLongDate(value, now = new Date(), withYear = false) {
|
|
|
69
69
|
* @param {string} [arg] The format's argument (`currency:EUR`, or a row path holding the code).
|
|
70
70
|
* @param {object|null} [object] The row, for a currency read from a field.
|
|
71
71
|
* @param {object|null} [config] The app config map, for the reporting currency.
|
|
72
|
+
* @param {object|null} [formatters] The app's named formatters (`cnFormatters`), for any other format name.
|
|
72
73
|
* @return {string} The text, '' when nothing can be shown.
|
|
74
|
+
* @spec openspec/changes/map-list-formatters/specs/map-list-formatters/spec.md#requirement-a-map-list-reads-display-names-through-the-apps-formatters
|
|
73
75
|
*/
|
|
74
|
-
function formatValue(value, format, now, arg = '', object = null, config = null) {
|
|
76
|
+
function formatValue(value, format, now, arg = '', object = null, config = null, formatters = null) {
|
|
75
77
|
if (value === null || value === undefined || value === '' || typeof value === 'object') {
|
|
76
78
|
return ''
|
|
77
79
|
}
|
|
80
|
+
if (format !== '' && formatters && typeof formatters[format] === 'function') {
|
|
81
|
+
const shown = formatters[format](value, object);
|
|
82
|
+
return shown === null || shown === undefined ? '' : String(shown)
|
|
83
|
+
}
|
|
78
84
|
if (format === 'date') {
|
|
79
85
|
return formatBoardDate(value, now)
|
|
80
86
|
}
|
|
@@ -131,14 +137,16 @@ function formatValue(value, format, now, arg = '', object = null, config = null)
|
|
|
131
137
|
* the row field is empty, the app's reporting currency), `{path|relative}` and
|
|
132
138
|
* `{path|relativetime}` say "today", "yesterday" and then the short date
|
|
133
139
|
* ("vandaag, 09.42"), and `{path|days}` is the
|
|
134
|
-
* whole days since that date ("has waited {createdAt|days} days").
|
|
140
|
+
* whole days since that date ("has waited {createdAt|days} days"). Any other
|
|
141
|
+
* name is one of the app's formatters, passed as `context.formatters`
|
|
142
|
+
* (`{id|caseAddress}`). A token without a
|
|
135
143
|
* value empties the WHOLE template, because "Termijn " is worse than nothing.
|
|
136
144
|
* A template without tokens is returned as written (a constant label).
|
|
137
145
|
*
|
|
138
146
|
* @param {string} template The template, already translated.
|
|
139
147
|
* @param {object|null} object The object to read.
|
|
140
148
|
* @param {Date} [now] The reference moment for dates.
|
|
141
|
-
* @param {{config?: object}|null} [context] The app config map, for the reporting currency of a money token.
|
|
149
|
+
* @param {{config?: object, formatters?: object}|null} [context] The app config map, for the reporting currency of a money token, and the app's named formatters.
|
|
142
150
|
* @return {string} The filled text, or ''.
|
|
143
151
|
* @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record
|
|
144
152
|
*/
|
|
@@ -149,7 +157,7 @@ function fillTemplate(template, object, now = new Date(), context = null) {
|
|
|
149
157
|
let missing = false;
|
|
150
158
|
const text = template.replace(TOKEN, (_match, path, format, arg) => {
|
|
151
159
|
const value = object && typeof object === 'object' ? readPath(object, path) : undefined;
|
|
152
|
-
const shown = formatValue(value, format || '', now, arg || '', object, context && context.config ? context.config : null);
|
|
160
|
+
const shown = formatValue(value, format || '', now, arg || '', object, context && context.config ? context.config : null, context && context.formatters ? context.formatters : null);
|
|
153
161
|
if (shown === '') {
|
|
154
162
|
missing = true;
|
|
155
163
|
}
|
|
@@ -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, formatBoardWeekday, formatRelativeDate, formatTimeOfDay } from './boardDate.js'\nimport { parseDateValue } from './dateVariant.js'\nimport { formatMoney } from './formatMetric.js'\nimport { readPath } from './readPath.js'\nimport { userDisplayName } from './userDisplayName.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 * A date with the month written out: \"7 oktober\", or \"6 oktober 2026\"\n * outside the current year or when `withYear` asks for it.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} now The reference moment.\n * @param {boolean} [withYear] Always write the year.\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-header-meta-tokens/specs/detail-page-board-look/spec.md#requirement-the-header-meta-line-formats-users-long-dates-and-enum-labels\n */\nexport function formatLongDate(value, now = new Date(), withYear = false) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst options = { day: 'numeric', month: 'long' }\n\tif (withYear || date.getFullYear() !== now.getFullYear()) {\n\t\toptions.year = 'numeric'\n\t}\n\ttry {\n\t\treturn new Intl.DateTimeFormat(boardDateLocale(), options).format(date)\n\t} catch {\n\t\treturn new Intl.DateTimeFormat(undefined, options).format(date)\n\t}\n}\n\n/**\n * Format one token value.\n *\n * @param {unknown} value The raw value.\n * @param {string} format `date`, `datetime`, `weekday`, `longdate`, `time`, `user`, `currency`, `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 === 'longdate') {\n\t\treturn formatLongDate(value, now, arg === 'year')\n\t}\n\tif (format === 'user') {\n\t\treturn userDisplayName(value)\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|longdate}` writes the month out (\"7 oktober\";\n * `{path|longdate:year}` always adds the year), `{path|time}` is the time\n * of day (\"08:45\"), `{path|user}` is a user id's display name (looked up\n * once, '' until it arrives), `{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\n/**\n * Fill a template such as `\"{identifier} · {requester}\"` field by field\n * (a table column's `secondary` line, a detail page's meta line).\n *\n * A field without a value takes its separator with it: the text between two\n * fields belongs to the field after it, so an empty requester leaves\n * \"2026-0002\", not \"2026-0002 ·\", and an empty first field does not leave a\n * leading separator either. Text before the first field and after the last\n * one is kept as written. A template whose fields all have values fills\n * exactly as before.\n *\n * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-empty-field-takes-its-separator-with-it\n * @param {string} template The template, with `{field}` placeholders.\n * @param {(key: string) => string} valueOf The text of a field ('' when empty).\n * @return {string} The filled template.\n */\nexport function joinTemplateParts(template, valueOf) {\n\t// split() with a capture group alternates literal, field, literal, …\n\tconst parts = template.split(/\\{([^}]+)\\}/)\n\tconst lead = parts[0]\n\tconst tail = parts[parts.length - 1]\n\tlet body = ''\n\tfor (let i = 1; i < parts.length; i += 2) {\n\t\tconst value = valueOf(parts[i].trim())\n\t\tif (value === '') {\n\t\t\tcontinue\n\t\t}\n\t\t// The literal before this field: the separator, unless nothing has\n\t\t// been written yet (then there is nothing to separate from).\n\t\tconst separator = i === 1 ? '' : parts[i - 1]\n\t\tbody += (body === '' ? '' : separator) + value\n\t}\n\treturn lead + body + tail\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'\nimport { userDisplayName } from './userDisplayName.js'\n\nconst TOKEN = /\\{([^{}\\s|]+)(?:\\|([A-Za-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 * A date with the month written out: \"7 oktober\", or \"6 oktober 2026\"\n * outside the current year or when `withYear` asks for it.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} now The reference moment.\n * @param {boolean} [withYear] Always write the year.\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-header-meta-tokens/specs/detail-page-board-look/spec.md#requirement-the-header-meta-line-formats-users-long-dates-and-enum-labels\n */\nexport function formatLongDate(value, now = new Date(), withYear = false) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst options = { day: 'numeric', month: 'long' }\n\tif (withYear || date.getFullYear() !== now.getFullYear()) {\n\t\toptions.year = 'numeric'\n\t}\n\ttry {\n\t\treturn new Intl.DateTimeFormat(boardDateLocale(), options).format(date)\n\t} catch {\n\t\treturn new Intl.DateTimeFormat(undefined, options).format(date)\n\t}\n}\n\n/**\n * Format one token value.\n *\n * @param {unknown} value The raw value.\n * @param {string} format `date`, `datetime`, `weekday`, `longdate`, `time`, `user`, `currency`, `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 * @param {object|null} [formatters] The app's named formatters (`cnFormatters`), for any other format name.\n * @return {string} The text, '' when nothing can be shown.\n * @spec openspec/changes/map-list-formatters/specs/map-list-formatters/spec.md#requirement-a-map-list-reads-display-names-through-the-apps-formatters\n */\nfunction formatValue(value, format, now, arg = '', object = null, config = null, formatters = null) {\n\tif (value === null || value === undefined || value === '' || typeof value === 'object') {\n\t\treturn ''\n\t}\n\tif (format !== '' && formatters && typeof formatters[format] === 'function') {\n\t\tconst shown = formatters[format](value, object)\n\t\treturn shown === null || shown === undefined ? '' : String(shown)\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 === 'longdate') {\n\t\treturn formatLongDate(value, now, arg === 'year')\n\t}\n\tif (format === 'user') {\n\t\treturn userDisplayName(value)\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|longdate}` writes the month out (\"7 oktober\";\n * `{path|longdate:year}` always adds the year), `{path|time}` is the time\n * of day (\"08:45\"), `{path|user}` is a user id's display name (looked up\n * once, '' until it arrives), `{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\"). Any other\n * name is one of the app's formatters, passed as `context.formatters`\n * (`{id|caseAddress}`). 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, formatters?: object}|null} [context] The app config map, for the reporting currency of a money token, and the app's named formatters.\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, context && context.formatters ? context.formatters : 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\n/**\n * Fill a template such as `\"{identifier} · {requester}\"` field by field\n * (a table column's `secondary` line, a detail page's meta line).\n *\n * A field without a value takes its separator with it: the text between two\n * fields belongs to the field after it, so an empty requester leaves\n * \"2026-0002\", not \"2026-0002 ·\", and an empty first field does not leave a\n * leading separator either. Text before the first field and after the last\n * one is kept as written. A template whose fields all have values fills\n * exactly as before.\n *\n * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-empty-field-takes-its-separator-with-it\n * @param {string} template The template, with `{field}` placeholders.\n * @param {(key: string) => string} valueOf The text of a field ('' when empty).\n * @return {string} The filled template.\n */\nexport function joinTemplateParts(template, valueOf) {\n\t// split() with a capture group alternates literal, field, literal, …\n\tconst parts = template.split(/\\{([^}]+)\\}/)\n\tconst lead = parts[0]\n\tconst tail = parts[parts.length - 1]\n\tlet body = ''\n\tfor (let i = 1; i < parts.length; i += 2) {\n\t\tconst value = valueOf(parts[i].trim())\n\t\tif (value === '') {\n\t\t\tcontinue\n\t\t}\n\t\t// The literal before this field: the separator, unless nothing has\n\t\t// been written yet (then there is nothing to separate from).\n\t\tconst separator = i === 1 ? '' : parts[i - 1]\n\t\tbody += (body === '' ? '' : separator) + value\n\t}\n\treturn lead + body + tail\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
/** The renderers a header chip can use. */
|
|
12
12
|
const HEADER_CHIP_FORMATS = Object.freeze(['text', 'mono', 'badge', 'user', 'date']);
|
|
13
13
|
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* The variants a colour name maps onto; anything else is neutral. A name is a
|
|
16
|
+
* badge role (`success`, `warning`, ...) or an NL Design System hue as a
|
|
17
|
+
* register stores it (`red`, `blue-light`, ...): a hue and its light tint
|
|
18
|
+
* take the same tone.
|
|
19
|
+
*/
|
|
15
20
|
const COLOR_VARIANTS = Object.freeze({
|
|
16
21
|
success: 'success',
|
|
17
22
|
warning: 'warning',
|
|
@@ -19,6 +24,20 @@ const COLOR_VARIANTS = Object.freeze({
|
|
|
19
24
|
info: 'info',
|
|
20
25
|
primary: 'primary',
|
|
21
26
|
neutral: 'default',
|
|
27
|
+
blue: 'info',
|
|
28
|
+
'blue-light': 'info',
|
|
29
|
+
green: 'success',
|
|
30
|
+
'green-light': 'success',
|
|
31
|
+
orange: 'warning',
|
|
32
|
+
'orange-light': 'warning',
|
|
33
|
+
red: 'error',
|
|
34
|
+
'red-light': 'error',
|
|
35
|
+
purple: 'purple',
|
|
36
|
+
'purple-light': 'purple',
|
|
37
|
+
teal: 'teal',
|
|
38
|
+
'teal-light': 'teal',
|
|
39
|
+
grey: 'default',
|
|
40
|
+
'grey-light': 'default',
|
|
22
41
|
});
|
|
23
42
|
|
|
24
43
|
/**
|
|
@@ -69,7 +88,7 @@ function isEmptyChipValue(value) {
|
|
|
69
88
|
/**
|
|
70
89
|
* Map a colour name read off a referenced object onto a badge variant.
|
|
71
90
|
*
|
|
72
|
-
* @param {*} name The colour name (`success`, `warning`, `error`, `info`, `neutral`).
|
|
91
|
+
* @param {*} name The colour name: a badge role (`success`, `warning`, `error`, `info`, `neutral`) or an NL Design System hue (`red`, `blue-light`, ...).
|
|
73
92
|
* @return {string} A `CnStatusBadge` variant; `default` (neutral) when unknown.
|
|
74
93
|
*/
|
|
75
94
|
function variantForColor(name) {
|