@conduction/nextcloud-vue 2.145.0 → 2.147.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/CnAiCompanion/CnAiChatPanel.vue2.js.map +1 -1
- package/dist/esm/components/CnAiCompanion/CnAiChatPanel.vue3.js.map +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +12 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +42 -30
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +22 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +2 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +112 -15
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +40 -16
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js +1 -0
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue.js +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js +21 -2
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js.map +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js +2 -2
- package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +634 -301
- package/dist/esm/utils/detailObjectContext.js +7 -2
- package/dist/esm/utils/detailObjectContext.js.map +1 -1
- package/dist/esm/utils/objectParties.js +30 -1
- package/dist/esm/utils/objectParties.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/visibleWhen.js +6 -0
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +362 -138
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +634 -301
- package/package.json +1 -1
- package/src/components/CnAiCompanion/CnAiChatPanel.vue +3 -0
- package/src/components/CnChecklistRunWidget/CnChecklistRunWidget.vue +4 -2
- package/src/components/CnDataTable/CnDataTable.vue +19 -2
- package/src/components/CnDetailPage/CnDetailPage.md +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +23 -0
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +1 -0
- package/src/components/CnFactsWidget/CnFactsWidget.vue +168 -16
- package/src/components/CnNotesCard/CnNotesCard.vue +7 -2
- package/src/components/CnNotificationMatrix/CnNotificationMatrix.vue +21 -2
- package/src/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue +2 -1
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +1 -0
- package/src/components/CnRetentionWidget/CnRetentionWidget.vue +2 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +6 -1
- package/src/css/dialog.css +7 -0
- package/src/css/look-board-detail.css +152 -8
- package/src/css/look-board-index.css +78 -0
- package/src/css/look-board.css +33 -3
- package/src/css/table.css +9 -0
- package/src/schemas/app-manifest-v2.schema.json +2 -2
- package/src/utils/detailObjectContext.js +7 -2
- package/src/utils/objectParties.js +29 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
- package/src/utils/visibleWhen.js +6 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnNotificationMatrix.vue2.js","sources":["../../../../src/components/CnNotificationMatrix/CnNotificationMatrix.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-notification-matrix\" :class=\"{ 'cn-notification-matrix--board': isBoardLook }\" data-testid=\"cn-notification-matrix\">\n\t\t<p v-if=\"adminMode\" class=\"cn-notification-matrix__note\" data-testid=\"cn-nm-admin-note\">\n\t\t\t{{ adminNote }}\n\t\t</p>\n\n\t\t<table class=\"cn-notification-matrix__table\" data-testid=\"cn-nm-table\">\n\t\t\t<caption class=\"cn-notification-matrix__caption\">\n\t\t\t\t{{ tableCaption }}\n\t\t\t</caption>\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ eventHeader }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`ch-${channel.id}`\"\n\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-channel-header\"\n\t\t\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t\t\t{{ channel.label }}\n\t\t\t\t\t\t<template v-if=\"!isBoardLook\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\n\t\t\t<tbody v-for=\"group in groups\" :key=\"`grp-${group.key}`\">\n\t\t\t\t<tr>\n\t\t\t\t\t<th :colspan=\"channels.length + 1\" scope=\"colgroup\">\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__group\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-group\"\n\t\t\t\t\t\t\t:data-group=\"group.key\"\n\t\t\t\t\t\t\t:aria-expanded=\"String(!isCollapsed(group.key))\"\n\t\t\t\t\t\t\t@click=\"toggleGroup(group.key)\">\n\t\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\n\t\t\t\t<tr\n\t\t\t\t\tv-for=\"row in rowsFor(group)\"\n\t\t\t\t\tv-show=\"!isCollapsed(group.key)\"\n\t\t\t\t\t:key=\"`ev-${row.event.id}-${row.scope}`\"\n\t\t\t\t\tdata-testid=\"cn-nm-row\"\n\t\t\t\t\t:class=\"{ 'cn-notification-matrix__row--scoped': !row.isGlobal }\"\n\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t:data-scope=\"row.scope\">\n\t\t\t\t\t<th scope=\"row\" :class=\"{ 'cn-notification-matrix__scope': !row.isGlobal }\">\n\t\t\t\t\t\t<template v-if=\"row.isGlobal\">\n\t\t\t\t\t\t\t{{ row.event.label }}\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"row.event.immediate\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-immediate\">\n\t\t\t\t\t\t\t\t{{ immediateText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tv-if=\"addableScopes(row.event).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-add-scope\"\n\t\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t\t:aria-label=\"addScopeLabel(row.event)\"\n\t\t\t\t\t\t\t\tvalue=\"\"\n\t\t\t\t\t\t\t\t@change=\"onAddScope(row.event, $event)\">\n\t\t\t\t\t\t\t\t<option value=\"\">\n\t\t\t\t\t\t\t\t\t{{ addScopeText }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\tv-for=\"choice in addableScopes(row.event)\"\n\t\t\t\t\t\t\t\t\t:key=\"`sc-${row.event.id}-${choice.id}`\"\n\t\t\t\t\t\t\t\t\t:value=\"choice.id\">\n\t\t\t\t\t\t\t\t\t{{ choice.label }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t<span data-testid=\"cn-nm-scope-label\">{{ scopeLabel(row.scope) }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\n\t\t\t\t\t<td\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`c-${row.event.id}-${row.scope}-${channel.id}`\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-cell\"\n\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-level=\"cellFor(row.event, channel, row.scope).level\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-toggle\"\n\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:checked=\"cellFor(row.event, channel, row.scope).value\"\n\t\t\t\t\t\t\t:disabled=\"isLockedFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-label=\"cellLabelFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-describedby=\"isBoardLook ? sourceIdFor(row.event, channel, row.scope) : null\"\n\t\t\t\t\t\t\t@change=\"onToggle(row.event, channel, $event, row.scope)\">\n\t\t\t\t\t\t<!-- The board look draws only the checkbox; why it is set stays said, and stays visible when the cell is locked. -->\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t:id=\"sourceIdFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t:class=\"{ 'hidden-visually': isBoardLook && !isLockedFor(row.event, channel, row.scope) }\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-source\">\n\t\t\t\t\t\t\t{{ sourceTextFor(row.event, channel, row.scope) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<!-- The board look keeps the grid to its checkboxes; each channel's bundling, its time and the test send live here. -->\n\t\t<details\n\t\t\tv-if=\"isBoardLook\"\n\t\t\tclass=\"cn-notification-matrix__channels\"\n\t\t\tdata-testid=\"cn-nm-channel-settings\">\n\t\t\t<summary class=\"cn-notification-matrix__channels-summary\">\n\t\t\t\t{{ channelSettingsTitle }}\n\t\t\t</summary>\n\t\t\t<div\n\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t:key=\"`cs-${channel.id}`\"\n\t\t\t\tclass=\"cn-notification-matrix__channel\"\n\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t<h4 class=\"cn-notification-matrix__channel-name\">\n\t\t\t\t\t{{ channel.label }}\n\t\t\t\t</h4>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t</span>\n\n\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<label\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t</span>\n\n\t\t\t\t\t<button\n\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t</button>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</details>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport {\n\tchannelAvailability,\n\tPREFERENCE_LEVELS,\n\tresolvePreference,\n} from '../../utils/notificationPreference.js'\nimport {\n\tGLOBAL_SCOPE,\n\tscopeRowsFor,\n\tvalueForScope,\n} from '../../utils/preferenceScopes.js'\n\n/**\n * CnNotificationMatrix — one screen where a person chooses what notifies them,\n * and finds out why they are getting what they are getting.\n *\n * 🔴 IT DOES NOT REPLACE `CnNotificationPreferences`, IT SITS BESIDE IT. That\n * component is a self-contained settings pane: it takes no props, fetches for\n * itself and scopes to the app whose settings modal is open, and `CnAppRoot`\n * mounts it as the default of its `#user-settings` slot. This one takes a\n * catalogue from its host, knows four levels rather than two and has a channel\n * axis. Replacing the older one in place, which an earlier revision of this\n * change did, left every app that mounts it through `CnAppRoot` rendering an\n * empty table with no error: a silent regression, not a breakage anybody would\n * see in a test.\n *\n * Events down, channels across. The catalogue is the host's, so this component\n * never knows what a \"term expires\" event is.\n *\n * 🔴 IT NEVER SHOWS A SETTING THAT DOES NOT APPLY. That is the whole point of\n * the screen and it is the one way it can fail while looking perfect. Three\n * cases, each handled rather than hidden:\n *\n * - A FORCED cell is locked, says who forced it and why. An administrator\n * has taken the choice away; a toggle the person can move which then does\n * nothing is worse than a locked one, because they believe they acted.\n * - A channel the instance has not configured is disabled with the reason.\n * - A channel REFUSED for this kind and recipient says the rule. An internal\n * notice to an outside recipient is a rule working, not a gap to fix, and\n * \"no channels available\" would send somebody to the wrong place.\n *\n * 🔴 IT IS A REAL TABLE. A grid of checkboxes with no row and column headers\n * is unreadable to a screen reader: every cell is \"checkbox, checked\" with no\n * way to know which event or which channel. The header cells are `th` with\n * scopes, and each toggle carries a name that says both.\n *\n * 🔴 A PREFERENCE IS SET GLOBALLY OR FOR ONE CASE DOMAIN. \"Tell me when a term\n * expires\" is usually too much and \"never\" is too little, so the event row is\n * the global one and scoped rows sit indented under it. The narrower row wins\n * only where it applies: everywhere else the global row still stands, because\n * a scope that replaced it would silence every other domain the moment\n * somebody narrowed one.\n *\n * 🔴 ONLY EVENTS IN THE CATALOGUE ARE LISTED. A stored preference for an event\n * that no longer exists is not rendered, because a switch for something that\n * can no longer happen is a lie; the store drops it on the next write.\n *\n * 🔴 A TEST SEND REPORTS WHAT HAPPENED, INCLUDING A REFUSAL. Nobody should have\n * to wait for a real event to find out a channel is broken, and a button that\n * silently did nothing would read as proof the channel works.\n *\n * @event {object} change — A cell was set. Payload: `{ eventId, channelId, scope, value }`.\n * @event {object} add-scope — A scope was added to a row. Payload: `{ eventId, scope }`.\n * @event {object} digest-change — A channel's digest was set. Payload: `{ channelId, mode, timeOfDay }`.\n * @event {object} test-send — A test was asked for. Payload: `{ channelId }`.\n */\nexport default {\n\tname: 'CnNotificationMatrix',\n\n\tinject: {\n\t\t/** The look CnAppRoot (or the page) draws in. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The events, each `{ id, label, group, groupLabel, immediate,\n\t\t * appDefault }`. From the host's catalogue.\n\t\t */\n\t\tevents: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The channels, each `{ id, label, configured, unconfiguredReason }`. */\n\t\tchannels: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The group's values, as `{ [eventId]: { [channelId]: boolean } }`. */\n\t\tgroupValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** This person's values, same shape. */\n\t\tpersonalValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What an administrator has forced, as\n\t\t * `{ [eventId]: { [channelId]: { value, by, reason } } }`.\n\t\t *\n\t\t * A level ABOVE the person's own rather than another default: it is\n\t\t * the administrator removing the choice, not setting it.\n\t\t */\n\t\tforcedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What the platform refuses for this recipient, same shape, each\n\t\t * `{ reason }`. A refusal is not an absence: it is a rule, and the\n\t\t * screen says which.\n\t\t */\n\t\trefusals: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Whether this is the admin screen writing the group's defaults. The\n\t\t * screen says a person's own value wins, so an administrator is not\n\t\t * surprised when somebody does not get what they set.\n\t\t */\n\t\tadminMode: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * This person's narrower values, as\n\t\t * `{ [eventId]: { [channelId]: { [scope]: boolean } } }`.\n\t\t *\n\t\t * Separate from `personalValues` rather than replacing it, so the\n\t\t * global row keeps the flat shape every existing caller passes. A row\n\t\t * left unset here falls through to the global value, which is what\n\t\t * makes an absent scoped row an unanswered question rather than a no.\n\t\t */\n\t\tscopedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The scopes a row can be narrowed to, each `{ id, label }`. Case\n\t\t * domains or record types, named by whoever configured the app.\n\t\t */\n\t\tscopeChoices: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The digest per channel, as `{ [channelId]: { mode, timeOfDay } }`\n\t\t * where mode is `off`, `daily` or `weekly`.\n\t\t */\n\t\tdigest: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The last test send per channel, as\n\t\t * `{ [channelId]: { ok, message } }`. A refusal belongs here as much\n\t\t * as a success: both are results.\n\t\t */\n\t\ttestResults: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['change', 'add-scope', 'digest-change', 'test-send'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Groups the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the matrix draws the board's grid.\n\t\t *\n\t\t * @return {boolean} True in the board look.\n\t\t * @spec openspec/changes/notification-matrix-board-grid/specs/notification-matrix/spec.md\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/** @return {string} The disclosure under the board grid. */\n\t\tchannelSettingsTitle() {\n\t\t\treturn t('nextcloud-vue', 'Bundling and test message')\n\t\t},\n\n\t\t/** @return {Array<object>} The events, in their groups. */\n\t\tgroups() {\n\t\t\tconst byKey = new Map()\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst key = String(event?.group ?? '')\n\t\t\t\tif (byKey.has(key) === false) {\n\t\t\t\t\tbyKey.set(key, {\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: String(event?.groupLabel ?? key) || this.ungroupedText,\n\t\t\t\t\t\tevents: [],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tbyKey.get(key).events.push(event)\n\t\t\t}\n\t\t\treturn [...byKey.values()]\n\t\t},\n\n\t\t/** @return {string} The table's caption. */\n\t\ttableCaption() {\n\t\t\treturn t('nextcloud-vue', 'What notifies you, and over which channel')\n\t\t},\n\n\t\t/** @return {string} The events column header. */\n\t\teventHeader() {\n\t\t\treturn t('nextcloud-vue', 'Event')\n\t\t},\n\n\t\t/** @return {string} What an ungrouped event group is called. */\n\t\tungroupedText() {\n\t\t\treturn t('nextcloud-vue', 'Other')\n\t\t},\n\n\t\t/** @return {string} What an unusable channel says with no reason given. */\n\t\tunavailableText() {\n\t\t\treturn t('nextcloud-vue', 'Not available on this instance')\n\t\t},\n\n\t\t/** @return {string} What an immediate event says. */\n\t\timmediateText() {\n\t\t\treturn t('nextcloud-vue', 'Always sent straight away, never held for a digest')\n\t\t},\n\n\t\t/** @return {Array<object>} The digest choices, in the order they escalate. */\n\t\tdigestModes() {\n\t\t\treturn [\n\t\t\t\t{ id: 'off', label: t('nextcloud-vue', 'Send each one as it happens') },\n\t\t\t\t{ id: 'daily', label: t('nextcloud-vue', 'Once a day') },\n\t\t\t\t{ id: 'weekly', label: t('nextcloud-vue', 'Once a week') },\n\t\t\t]\n\t\t},\n\n\t\t/** @return {string} What the digest control is called. */\n\t\tdigestLabel() {\n\t\t\treturn t('nextcloud-vue', 'Bundle these')\n\t\t},\n\n\t\t/** @return {string} What the time control is called. */\n\t\tdigestTimeLabel() {\n\t\t\treturn t('nextcloud-vue', 'Time of day')\n\t\t},\n\n\t\t/** @return {string} What a digest says about the events it cannot hold. */\n\t\tdigestImmediateNote() {\n\t\t\treturn t('nextcloud-vue', 'Events marked as urgent are still sent straight away.')\n\t\t},\n\n\t\t/** @return {string} What the test button says. */\n\t\ttestSendText() {\n\t\t\treturn t('nextcloud-vue', 'Send a test')\n\t\t},\n\n\t\t/** @return {string} What the add-a-scope control says when nothing is chosen. */\n\t\taddScopeText() {\n\t\t\treturn t('nextcloud-vue', 'Set this for one kind of case')\n\t\t},\n\n\t\t/** @return {string} What the admin screen says about a person's own value. */\n\t\tadminNote() {\n\t\t\treturn t('nextcloud-vue', \"These are the group's defaults. Somebody who has set their own value keeps it, unless a channel is forced.\")\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The id of a cell's source line, which describes its checkbox.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The row's scope.\n\t\t * @return {string} The id.\n\t\t */\n\t\tsourceIdFor(event, channel, scope) {\n\t\t\treturn `cn-nm-src-${this.$?.uid ?? 0}-${event.id}-${scope}-${channel.id}`.replace(/[^A-Za-z0-9_-]/g, '_')\n\t\t},\n\n\t\tt,\n\n\t\t/**\n\t\t * Whether a channel can be used at all, for any event.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} Its availability.\n\t\t */\n\t\tchannelState(channel) {\n\t\t\treturn channelAvailability({ channel })\n\t\t},\n\n\t\t/**\n\t\t * One cell, resolved.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcell(event, channel) {\n\t\t\treturn this.cellFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * One cell on one row, resolved.\n\t\t *\n\t\t * The scoped value is a NARROWER ANSWER TO THE SAME QUESTION, not a\n\t\t * separate preference, so an unset scoped cell falls back to the\n\t\t * person's global value and from there to the group and the default.\n\t\t * Reading an unset scoped cell as false would override both.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcellFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\n\t\t\treturn resolvePreference({\n\t\t\t\tappDefault: event?.appDefault === true,\n\t\t\t\tgroupValue: this.at(this.groupValues, event, channel),\n\t\t\t\tpersonalValue: narrow.value === null\n\t\t\t\t\t? this.at(this.personalValues, event, channel)\n\t\t\t\t\t: narrow.value,\n\t\t\t\tforced: this.at(this.forcedValues, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The rows one group renders: each event, then its scopes.\n\t\t *\n\t\t * Only events in the catalogue. A stored preference for an event the\n\t\t * catalogue no longer has is not listed, because a switch for\n\t\t * something that can no longer happen is a lie.\n\t\t *\n\t\t * @param {object} group The group.\n\t\t * @return {Array<object>} `{ event, scope, isGlobal }`.\n\t\t */\n\t\trowsFor(group) {\n\t\t\tconst rows = []\n\t\t\tfor (const event of group.events) {\n\t\t\t\tfor (const row of scopeRowsFor({ values: this.scopedValues, eventId: event.id })) {\n\t\t\t\t\trows.push({ event, scope: row.scope, isGlobal: row.isGlobal })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn rows\n\t\t},\n\n\t\t/**\n\t\t * The scopes this row can still be narrowed to.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {Array<object>} The remaining choices.\n\t\t */\n\t\taddableScopes(event) {\n\t\t\tconst taken = new Set(scopeRowsFor({ values: this.scopedValues, eventId: event?.id })\n\t\t\t\t.map((row) => row.scope))\n\t\t\treturn this.scopeChoices.filter((choice) => taken.has(String(choice?.id)) === false)\n\t\t},\n\n\t\t/**\n\t\t * What a scope is called on screen.\n\t\t *\n\t\t * @param {string} scope The scope.\n\t\t * @return {string} Its label, or the id when nobody named it.\n\t\t */\n\t\tscopeLabel(scope) {\n\t\t\tconst choice = this.scopeChoices.find((entry) => String(entry?.id) === scope)\n\t\t\treturn String(choice?.label ?? scope)\n\t\t},\n\n\t\t/**\n\t\t * Whether the platform refuses this channel for this event.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The availability.\n\t\t */\n\t\tcellAvailability(event, channel) {\n\t\t\treturn channelAvailability({\n\t\t\t\tchannel,\n\t\t\t\trefusal: this.at(this.refusals, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A value out of one of the nested maps.\n\t\t *\n\t\t * @param {object} map The map.\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {(boolean|object|null)} The value, or null.\n\t\t */\n\t\tat(map, event, channel) {\n\t\t\tconst row = map?.[event?.id]\n\t\t\tconst value = row?.[channel?.id]\n\t\t\treturn value === undefined ? null : value\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLocked(event, channel) {\n\t\t\treturn this.isLockedFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell on one row cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLockedFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\treturn this.cellFor(event, channel, scope).editable === false\n\t\t\t\t|| this.cellAvailability(event, channel).usable === false\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called to somebody who cannot see the table.\n\t\t *\n\t\t * Says the event, the channel and why it is locked when it is. A\n\t\t * disabled checkbox with no name is a dead end for a screen reader.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabel(event, channel) {\n\t\t\treturn this.cellLabelFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called, saying which row it is on.\n\t\t *\n\t\t * A scoped row is indented, and indentation is invisible to a screen\n\t\t * reader: without the scope in the name, two checkboxes for the same\n\t\t * event and channel are indistinguishable.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabelFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst base = scope === GLOBAL_SCOPE\n\t\t\t\t? t('nextcloud-vue', '{event} over {channel}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t})\n\t\t\t\t: t('nextcloud-vue', '{event} over {channel}, for {scope}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t\tscope: this.scopeLabel(scope),\n\t\t\t\t\t})\n\t\t\tconst reason = this.lockReasonFor(event, channel, scope)\n\t\t\treturn reason === '' ? base : `${base}. ${reason}`\n\t\t},\n\n\t\t/**\n\t\t * Why a cell is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReason(event, channel) {\n\t\t\treturn this.lockReasonFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Why a cell on one row is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReasonFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst availability = this.cellAvailability(event, channel)\n\t\t\tif (availability.usable === false) {\n\t\t\t\treturn availability.reason || this.unavailableText\n\t\t\t}\n\n\t\t\tconst resolved = this.cellFor(event, channel, scope)\n\t\t\tif (resolved.editable === true) {\n\t\t\t\treturn ''\n\t\t\t}\n\n\t\t\tif (resolved.forcedBy !== '' && resolved.reason !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}: {reason}', {\n\t\t\t\t\tby: resolved.forcedBy,\n\t\t\t\t\treason: resolved.reason,\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (resolved.forcedBy !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}, and cannot be changed here', { by: resolved.forcedBy })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Set by an administrator, and cannot be changed here')\n\t\t},\n\n\t\t/**\n\t\t * What a cell says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceText(event, channel) {\n\t\t\treturn this.sourceTextFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell on one row says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceTextFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst locked = this.lockReasonFor(event, channel, scope)\n\t\t\tif (locked !== '') {\n\t\t\t\treturn locked\n\t\t\t}\n\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\t\t\tif (scope !== GLOBAL_SCOPE && narrow.scope === GLOBAL_SCOPE) {\n\t\t\t\t// Said out loud, because an unanswered narrower question looks\n\t\t\t\t// exactly like an answered one: the checkbox shows the same\n\t\t\t\t// state either way.\n\t\t\t\treturn t('nextcloud-vue', 'Follows the row above')\n\t\t\t}\n\n\t\t\tconst level = this.cellFor(event, channel, scope).level\n\t\t\tif (level === PREFERENCE_LEVELS.PERSONAL) {\n\t\t\t\treturn t('nextcloud-vue', 'Your choice')\n\t\t\t}\n\t\t\tif (level === PREFERENCE_LEVELS.GROUP) {\n\t\t\t\treturn t('nextcloud-vue', 'Follows your group')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Follows the app default')\n\t\t},\n\n\t\t/**\n\t\t * Whether a group is collapsed.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a group, or open it again.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t */\n\t\ttoggleGroup(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * A cell was set.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t * @param {string} scope The scope of the row it is on.\n\t\t */\n\t\tonToggle(event, channel, domEvent, scope = GLOBAL_SCOPE) {\n\t\t\t// Refuse from here too, not only in the template. A disabled\n\t\t\t// attribute is a rendering; this is the rule.\n\t\t\tif (this.isLockedFor(event, channel, scope)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event change A cell was set.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('change', {\n\t\t\t\teventId: event.id,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tscope,\n\t\t\t\tvalue: domEvent?.target?.checked === true,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A scope was added to a row, from the row itself.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonAddScope(event, domEvent) {\n\t\t\tconst scope = String(domEvent?.target?.value ?? '')\n\t\t\tif (scope === '') {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Back to the placeholder, so the control reads as an action\n\t\t\t// rather than as a setting that now says something.\n\t\t\tif (domEvent?.target) {\n\t\t\t\tdomEvent.target.value = ''\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event add-scope A row was narrowed.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('add-scope', { eventId: event.id, scope })\n\t\t},\n\n\t\t/**\n\t\t * What a channel's digest is set to.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} `{ mode, timeOfDay }`.\n\t\t */\n\t\tdigestFor(channel) {\n\t\t\tconst set = this.digest?.[channel?.id]\n\t\t\treturn {\n\t\t\t\tmode: String(set?.mode ?? 'off'),\n\t\t\t\ttimeOfDay: String(set?.timeOfDay ?? ''),\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The digest mode was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestMode(channel, domEvent) {\n\t\t\t/**\n\t\t\t * @event digest-change Emitted when a reader changes a channel's digest mode or its time of day. Payload: `{ channelId, mode, timeOfDay }`.\n\t\t\t */\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: String(domEvent?.target?.value ?? 'off'),\n\t\t\t\ttimeOfDay: this.digestFor(channel).timeOfDay,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The digest time was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestTime(channel, domEvent) {\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: this.digestFor(channel).mode,\n\t\t\t\ttimeOfDay: String(domEvent?.target?.value ?? ''),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The last test send on a channel, or null when there has been none.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {?object} `{ ok, message }`.\n\t\t */\n\t\ttestResultFor(channel) {\n\t\t\tconst result = this.testResults?.[channel?.id]\n\t\t\treturn result === undefined || result === null ? null : result\n\t\t},\n\n\t\t/**\n\t\t * What a test send reported.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\ttestResultText(channel) {\n\t\t\tconst result = this.testResultFor(channel)\n\t\t\tif (result === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (String(result.message ?? '') !== '') {\n\t\t\t\treturn String(result.message)\n\t\t\t}\n\t\t\t// Only when the server sent no sentence of its own. A refusal\n\t\t\t// without a reason still has to say it was a refusal.\n\t\t\treturn result.ok === true\n\t\t\t\t? t('nextcloud-vue', 'The test arrived.')\n\t\t\t\t: t('nextcloud-vue', 'The test did not arrive.')\n\t\t},\n\n\t\t/**\n\t\t * What the test button is called.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\ttestSendLabel(channel) {\n\t\t\treturn t('nextcloud-vue', 'Send a test over {channel}', { channel: channel.label })\n\t\t},\n\n\t\t/**\n\t\t * What the add-a-scope control is called.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\taddScopeLabel(event) {\n\t\t\treturn t('nextcloud-vue', 'Set {event} for one kind of case', { event: event.label })\n\t\t},\n\n\t\t/**\n\t\t * A test send was asked for.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t */\n\t\tonTestSend(channel) {\n\t\t\t/**\n\t\t\t * @event test-send Emitted when a reader asks for a test notification on one channel. Payload: `{ channelId }`.\n\t\t\t */\n\t\t\tthis.$emit('test-send', { channelId: channel.id })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-notification-matrix__table {\n\tinline-size: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-notification-matrix__caption {\n\ttext-align: start;\n\tpadding-block-end: 8px;\n}\n\n.cn-notification-matrix__table th,\n.cn-notification-matrix__table td {\n\ttext-align: start;\n\tpadding: 6px 8px;\n\tborder-block-end: 1px solid var(--color-border);\n\tvertical-align: top;\n}\n\n.cn-notification-matrix__muted {\n\tdisplay: block;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85rem;\n}\n\n.cn-notification-matrix__group {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-notification-matrix__scope {\n\tpadding-inline-start: 24px !important;\n\tfont-weight: normal;\n}\n\n.cn-notification-matrix__row--scoped th,\n.cn-notification-matrix__row--scoped td {\n\tborder-block-end-style: dotted;\n}\n\n.cn-notification-matrix__test {\n\tmargin-block-start: 4px;\n}\n\n.cn-notification-matrix__note {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The board look (DqPersoonlijkeInstellingen \"Meldingen\"): muted 13px/600\n headers over a hairline, the channel columns 96px with a centred 20px\n checkbox, the event rows 10px high with the soft rule. The caption stays for\n assistive technology. */\n.cn-notification-matrix--board {\n\t.cn-notification-matrix__caption {\n\t\tposition: absolute;\n\t\tinline-size: 1px;\n\t\tblock-size: 1px;\n\t\toverflow: hidden;\n\t\tclip-path: inset(50%);\n\t\twhite-space: nowrap;\n\t}\n\n\t/* A theme that colours every th (thematiq's table header token) leaves\n\t the board grid's head white. */\n\t.cn-notification-matrix__table {\n\t\t--nldesign-component-table-header-background-color: transparent;\n\t\tfont-size: 14px;\n\t}\n\n\t.cn-notification-matrix__table thead th {\n\t\tpadding: 8px 10px 8px 0;\n\t\tline-height: 1.2;\n\t\tborder-block-end: 1px solid var(--color-border);\n\t\tcolor: var(--color-text-maxcontrast);\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tvertical-align: bottom;\n\t}\n\n\t.cn-notification-matrix__table thead th[data-channel] {\n\t\tinline-size: 96px;\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody th,\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding: 10px 10px 10px 0;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t\tfont-weight: 400;\n\t\tline-height: 1.4;\n\t\tvertical-align: middle;\n\t}\n\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody td input[type='checkbox'] {\n\t\tinline-size: 20px;\n\t\tmin-inline-size: 20px;\n\t\tblock-size: 20px;\n\t\tmin-block-size: 20px;\n\t\t/* A block box, so the line's baseline adds no height: the row is\n\t\t 42px as on the board (10px, the 1px-low checkbox, 10px, the rule). */\n\t\tdisplay: block;\n\t\tmargin: 1px auto 0;\n\t}\n\n\t.cn-notification-matrix__group {\n\t\tpadding: 4px 0;\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tcolor: var(--color-text-maxcontrast);\n\t}\n\n\t.cn-notification-matrix__channels {\n\t\tmargin-block-start: 16px;\n\t}\n\n\t.cn-notification-matrix__channels-summary {\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t\tcursor: pointer;\n\t}\n\n\t.cn-notification-matrix__channel {\n\t\tdisplay: flex;\n\t\tflex-wrap: wrap;\n\t\talign-items: flex-end;\n\t\tgap: 8px 16px;\n\t\tpadding-block: 10px;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t}\n\n\t.cn-notification-matrix__channel-name {\n\t\tflex: 1 0 100%;\n\t\tmargin: 0;\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t}\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnNotificationMatrix.vue2.js","sources":["../../../../src/components/CnNotificationMatrix/CnNotificationMatrix.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-notification-matrix\" :class=\"{ 'cn-notification-matrix--board': isBoardLook }\" data-testid=\"cn-notification-matrix\">\n\t\t<p v-if=\"adminMode\" class=\"cn-notification-matrix__note\" data-testid=\"cn-nm-admin-note\">\n\t\t\t{{ adminNote }}\n\t\t</p>\n\n\t\t<table class=\"cn-notification-matrix__table\" data-testid=\"cn-nm-table\">\n\t\t\t<caption class=\"cn-notification-matrix__caption\">\n\t\t\t\t{{ tableCaption }}\n\t\t\t</caption>\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ eventHeader }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`ch-${channel.id}`\"\n\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-channel-header\"\n\t\t\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t\t\t{{ channel.label }}\n\t\t\t\t\t\t<template v-if=\"!isBoardLook\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\n\t\t\t<tbody v-for=\"group in groups\" :key=\"`grp-${group.key}`\">\n\t\t\t\t<tr>\n\t\t\t\t\t<th :colspan=\"channels.length + 1\" scope=\"colgroup\">\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__group\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-group\"\n\t\t\t\t\t\t\t:data-group=\"group.key\"\n\t\t\t\t\t\t\t:aria-expanded=\"String(!isCollapsed(group.key))\"\n\t\t\t\t\t\t\t@click=\"toggleGroup(group.key)\">\n\t\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\n\t\t\t\t<tr\n\t\t\t\t\tv-for=\"row in rowsFor(group)\"\n\t\t\t\t\tv-show=\"!isCollapsed(group.key)\"\n\t\t\t\t\t:key=\"`ev-${row.event.id}-${row.scope}`\"\n\t\t\t\t\tdata-testid=\"cn-nm-row\"\n\t\t\t\t\t:class=\"{ 'cn-notification-matrix__row--scoped': !row.isGlobal }\"\n\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t:data-scope=\"row.scope\">\n\t\t\t\t\t<th scope=\"row\" :class=\"{ 'cn-notification-matrix__scope': !row.isGlobal }\">\n\t\t\t\t\t\t<template v-if=\"row.isGlobal\">\n\t\t\t\t\t\t\t{{ row.event.label }}\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"row.event.immediate\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-immediate\">\n\t\t\t\t\t\t\t\t{{ immediateText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tv-if=\"addableScopes(row.event).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-add-scope\"\n\t\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t\t:aria-label=\"addScopeLabel(row.event)\"\n\t\t\t\t\t\t\t\tvalue=\"\"\n\t\t\t\t\t\t\t\t@change=\"onAddScope(row.event, $event)\">\n\t\t\t\t\t\t\t\t<option value=\"\">\n\t\t\t\t\t\t\t\t\t{{ addScopeText }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\tv-for=\"choice in addableScopes(row.event)\"\n\t\t\t\t\t\t\t\t\t:key=\"`sc-${row.event.id}-${choice.id}`\"\n\t\t\t\t\t\t\t\t\t:value=\"choice.id\">\n\t\t\t\t\t\t\t\t\t{{ choice.label }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t<span data-testid=\"cn-nm-scope-label\">{{ scopeLabel(row.scope) }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\n\t\t\t\t\t<td\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`c-${row.event.id}-${row.scope}-${channel.id}`\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-cell\"\n\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-level=\"cellFor(row.event, channel, row.scope).level\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-toggle\"\n\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:checked=\"cellFor(row.event, channel, row.scope).value\"\n\t\t\t\t\t\t\t:disabled=\"isLockedFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-label=\"cellLabelFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-describedby=\"isBoardLook ? sourceIdFor(row.event, channel, row.scope) : null\"\n\t\t\t\t\t\t\t@change=\"onToggle(row.event, channel, $event, row.scope)\">\n\t\t\t\t\t\t<!-- The board look draws only the checkbox; why it is set stays said, and stays visible when the cell is locked. -->\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t:id=\"sourceIdFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t:class=\"{ 'hidden-visually': isBoardLook && !isLockedFor(row.event, channel, row.scope) }\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-source\">\n\t\t\t\t\t\t\t{{ sourceTextFor(row.event, channel, row.scope) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<!-- The board look keeps the grid to its checkboxes; each channel's bundling, its time and the test send live here. -->\n\t\t<details\n\t\t\tv-if=\"isBoardLook\"\n\t\t\tclass=\"cn-notification-matrix__channels\"\n\t\t\tdata-testid=\"cn-nm-channel-settings\">\n\t\t\t<summary class=\"cn-notification-matrix__channels-summary\">\n\t\t\t\t{{ channelSettingsTitle }}\n\t\t\t</summary>\n\t\t\t<div\n\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t:key=\"`cs-${channel.id}`\"\n\t\t\t\tclass=\"cn-notification-matrix__channel\"\n\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t<h4 class=\"cn-notification-matrix__channel-name\">\n\t\t\t\t\t{{ channel.label }}\n\t\t\t\t</h4>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t</span>\n\n\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<label\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t</span>\n\n\t\t\t\t\t<button\n\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t</button>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</details>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport {\n\tchannelAvailability,\n\tPREFERENCE_LEVELS,\n\tresolvePreference,\n} from '../../utils/notificationPreference.js'\nimport {\n\tGLOBAL_SCOPE,\n\tscopeRowsFor,\n\tvalueForScope,\n} from '../../utils/preferenceScopes.js'\n\n/**\n * CnNotificationMatrix — one screen where a person chooses what notifies them,\n * and finds out why they are getting what they are getting.\n *\n * 🔴 IT DOES NOT REPLACE `CnNotificationPreferences`, IT SITS BESIDE IT. That\n * component is a self-contained settings pane: it takes no props, fetches for\n * itself and scopes to the app whose settings modal is open, and `CnAppRoot`\n * mounts it as the default of its `#user-settings` slot. This one takes a\n * catalogue from its host, knows four levels rather than two and has a channel\n * axis. Replacing the older one in place, which an earlier revision of this\n * change did, left every app that mounts it through `CnAppRoot` rendering an\n * empty table with no error: a silent regression, not a breakage anybody would\n * see in a test.\n *\n * Events down, channels across. The catalogue is the host's, so this component\n * never knows what a \"term expires\" event is.\n *\n * 🔴 IT NEVER SHOWS A SETTING THAT DOES NOT APPLY. That is the whole point of\n * the screen and it is the one way it can fail while looking perfect. Three\n * cases, each handled rather than hidden:\n *\n * - A FORCED cell is locked, says who forced it and why. An administrator\n * has taken the choice away; a toggle the person can move which then does\n * nothing is worse than a locked one, because they believe they acted.\n * - A channel the instance has not configured is disabled with the reason.\n * - A channel REFUSED for this kind and recipient says the rule. An internal\n * notice to an outside recipient is a rule working, not a gap to fix, and\n * \"no channels available\" would send somebody to the wrong place.\n *\n * 🔴 IT IS A REAL TABLE. A grid of checkboxes with no row and column headers\n * is unreadable to a screen reader: every cell is \"checkbox, checked\" with no\n * way to know which event or which channel. The header cells are `th` with\n * scopes, and each toggle carries a name that says both.\n *\n * 🔴 A PREFERENCE IS SET GLOBALLY OR FOR ONE CASE DOMAIN. \"Tell me when a term\n * expires\" is usually too much and \"never\" is too little, so the event row is\n * the global one and scoped rows sit indented under it. The narrower row wins\n * only where it applies: everywhere else the global row still stands, because\n * a scope that replaced it would silence every other domain the moment\n * somebody narrowed one.\n *\n * 🔴 ONLY EVENTS IN THE CATALOGUE ARE LISTED. A stored preference for an event\n * that no longer exists is not rendered, because a switch for something that\n * can no longer happen is a lie; the store drops it on the next write.\n *\n * 🔴 A TEST SEND REPORTS WHAT HAPPENED, INCLUDING A REFUSAL. Nobody should have\n * to wait for a real event to find out a channel is broken, and a button that\n * silently did nothing would read as proof the channel works.\n *\n * @event {object} change — A cell was set. Payload: `{ eventId, channelId, scope, value }`.\n * @event {object} add-scope — A scope was added to a row. Payload: `{ eventId, scope }`.\n * @event {object} digest-change — A channel's digest was set. Payload: `{ channelId, mode, timeOfDay }`.\n * @event {object} test-send — A test was asked for. Payload: `{ channelId }`.\n */\n/**\n * An event's shipped default in one channel: one boolean for every channel,\n * or the channel's own entry when the default differs per channel.\n *\n * @param {object} event The event.\n * @param {object} channel The channel.\n * @return {boolean} The default.\n * @spec openspec/changes/notification-matrix-default-per-channel/specs/notification-matrix/spec.md\n */\nfunction appDefaultFor(event, channel) {\n\tconst value = event?.appDefault\n\tif (value !== null && typeof value === 'object') {\n\t\treturn value[channel?.id] === true\n\t}\n\treturn value === true\n}\n\nexport default {\n\tname: 'CnNotificationMatrix',\n\n\tinject: {\n\t\t/** The look CnAppRoot (or the page) draws in. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The events, each `{ id, label, group, groupLabel, immediate,\n\t\t * appDefault }`. From the host's catalogue. `appDefault` is one\n\t\t * boolean for every channel, or `{ [channelId]: boolean }` when the\n\t\t * default differs per channel (an opt-in e-mail is off by default).\n\t\t */\n\t\tevents: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The channels, each `{ id, label, configured, unconfiguredReason }`. */\n\t\tchannels: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The group's values, as `{ [eventId]: { [channelId]: boolean } }`. */\n\t\tgroupValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** This person's values, same shape. */\n\t\tpersonalValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What an administrator has forced, as\n\t\t * `{ [eventId]: { [channelId]: { value, by, reason } } }`.\n\t\t *\n\t\t * A level ABOVE the person's own rather than another default: it is\n\t\t * the administrator removing the choice, not setting it.\n\t\t */\n\t\tforcedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What the platform refuses for this recipient, same shape, each\n\t\t * `{ reason }`. A refusal is not an absence: it is a rule, and the\n\t\t * screen says which.\n\t\t */\n\t\trefusals: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Whether this is the admin screen writing the group's defaults. The\n\t\t * screen says a person's own value wins, so an administrator is not\n\t\t * surprised when somebody does not get what they set.\n\t\t */\n\t\tadminMode: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * This person's narrower values, as\n\t\t * `{ [eventId]: { [channelId]: { [scope]: boolean } } }`.\n\t\t *\n\t\t * Separate from `personalValues` rather than replacing it, so the\n\t\t * global row keeps the flat shape every existing caller passes. A row\n\t\t * left unset here falls through to the global value, which is what\n\t\t * makes an absent scoped row an unanswered question rather than a no.\n\t\t */\n\t\tscopedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The scopes a row can be narrowed to, each `{ id, label }`. Case\n\t\t * domains or record types, named by whoever configured the app.\n\t\t */\n\t\tscopeChoices: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The digest per channel, as `{ [channelId]: { mode, timeOfDay } }`\n\t\t * where mode is `off`, `daily` or `weekly`.\n\t\t */\n\t\tdigest: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The last test send per channel, as\n\t\t * `{ [channelId]: { ok, message } }`. A refusal belongs here as much\n\t\t * as a success: both are results.\n\t\t */\n\t\ttestResults: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['change', 'add-scope', 'digest-change', 'test-send'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Groups the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the matrix draws the board's grid.\n\t\t *\n\t\t * @return {boolean} True in the board look.\n\t\t * @spec openspec/changes/notification-matrix-board-grid/specs/notification-matrix/spec.md\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/** @return {string} The disclosure under the board grid. */\n\t\tchannelSettingsTitle() {\n\t\t\treturn t('nextcloud-vue', 'Bundling and test message')\n\t\t},\n\n\t\t/** @return {Array<object>} The events, in their groups. */\n\t\tgroups() {\n\t\t\tconst byKey = new Map()\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst key = String(event?.group ?? '')\n\t\t\t\tif (byKey.has(key) === false) {\n\t\t\t\t\tbyKey.set(key, {\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: String(event?.groupLabel ?? key) || this.ungroupedText,\n\t\t\t\t\t\tevents: [],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tbyKey.get(key).events.push(event)\n\t\t\t}\n\t\t\treturn [...byKey.values()]\n\t\t},\n\n\t\t/** @return {string} The table's caption. */\n\t\ttableCaption() {\n\t\t\treturn t('nextcloud-vue', 'What notifies you, and over which channel')\n\t\t},\n\n\t\t/** @return {string} The events column header. */\n\t\teventHeader() {\n\t\t\treturn t('nextcloud-vue', 'Event')\n\t\t},\n\n\t\t/** @return {string} What an ungrouped event group is called. */\n\t\tungroupedText() {\n\t\t\treturn t('nextcloud-vue', 'Other')\n\t\t},\n\n\t\t/** @return {string} What an unusable channel says with no reason given. */\n\t\tunavailableText() {\n\t\t\treturn t('nextcloud-vue', 'Not available on this instance')\n\t\t},\n\n\t\t/** @return {string} What an immediate event says. */\n\t\timmediateText() {\n\t\t\treturn t('nextcloud-vue', 'Always sent straight away, never held for a digest')\n\t\t},\n\n\t\t/** @return {Array<object>} The digest choices, in the order they escalate. */\n\t\tdigestModes() {\n\t\t\treturn [\n\t\t\t\t{ id: 'off', label: t('nextcloud-vue', 'Send each one as it happens') },\n\t\t\t\t{ id: 'daily', label: t('nextcloud-vue', 'Once a day') },\n\t\t\t\t{ id: 'weekly', label: t('nextcloud-vue', 'Once a week') },\n\t\t\t]\n\t\t},\n\n\t\t/** @return {string} What the digest control is called. */\n\t\tdigestLabel() {\n\t\t\treturn t('nextcloud-vue', 'Bundle these')\n\t\t},\n\n\t\t/** @return {string} What the time control is called. */\n\t\tdigestTimeLabel() {\n\t\t\treturn t('nextcloud-vue', 'Time of day')\n\t\t},\n\n\t\t/** @return {string} What a digest says about the events it cannot hold. */\n\t\tdigestImmediateNote() {\n\t\t\treturn t('nextcloud-vue', 'Events marked as urgent are still sent straight away.')\n\t\t},\n\n\t\t/** @return {string} What the test button says. */\n\t\ttestSendText() {\n\t\t\treturn t('nextcloud-vue', 'Send a test')\n\t\t},\n\n\t\t/** @return {string} What the add-a-scope control says when nothing is chosen. */\n\t\taddScopeText() {\n\t\t\treturn t('nextcloud-vue', 'Set this for one kind of case')\n\t\t},\n\n\t\t/** @return {string} What the admin screen says about a person's own value. */\n\t\tadminNote() {\n\t\t\treturn t('nextcloud-vue', \"These are the group's defaults. Somebody who has set their own value keeps it, unless a channel is forced.\")\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The id of a cell's source line, which describes its checkbox.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The row's scope.\n\t\t * @return {string} The id.\n\t\t */\n\t\tsourceIdFor(event, channel, scope) {\n\t\t\treturn `cn-nm-src-${this.$?.uid ?? 0}-${event.id}-${scope}-${channel.id}`.replace(/[^A-Za-z0-9_-]/g, '_')\n\t\t},\n\n\t\tt,\n\n\t\t/**\n\t\t * Whether a channel can be used at all, for any event.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} Its availability.\n\t\t */\n\t\tchannelState(channel) {\n\t\t\treturn channelAvailability({ channel })\n\t\t},\n\n\t\t/**\n\t\t * One cell, resolved.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcell(event, channel) {\n\t\t\treturn this.cellFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * One cell on one row, resolved.\n\t\t *\n\t\t * The scoped value is a NARROWER ANSWER TO THE SAME QUESTION, not a\n\t\t * separate preference, so an unset scoped cell falls back to the\n\t\t * person's global value and from there to the group and the default.\n\t\t * Reading an unset scoped cell as false would override both.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcellFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\n\t\t\treturn resolvePreference({\n\t\t\t\tappDefault: appDefaultFor(event, channel),\n\t\t\t\tgroupValue: this.at(this.groupValues, event, channel),\n\t\t\t\tpersonalValue: narrow.value === null\n\t\t\t\t\t? this.at(this.personalValues, event, channel)\n\t\t\t\t\t: narrow.value,\n\t\t\t\tforced: this.at(this.forcedValues, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The rows one group renders: each event, then its scopes.\n\t\t *\n\t\t * Only events in the catalogue. A stored preference for an event the\n\t\t * catalogue no longer has is not listed, because a switch for\n\t\t * something that can no longer happen is a lie.\n\t\t *\n\t\t * @param {object} group The group.\n\t\t * @return {Array<object>} `{ event, scope, isGlobal }`.\n\t\t */\n\t\trowsFor(group) {\n\t\t\tconst rows = []\n\t\t\tfor (const event of group.events) {\n\t\t\t\tfor (const row of scopeRowsFor({ values: this.scopedValues, eventId: event.id })) {\n\t\t\t\t\trows.push({ event, scope: row.scope, isGlobal: row.isGlobal })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn rows\n\t\t},\n\n\t\t/**\n\t\t * The scopes this row can still be narrowed to.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {Array<object>} The remaining choices.\n\t\t */\n\t\taddableScopes(event) {\n\t\t\tconst taken = new Set(scopeRowsFor({ values: this.scopedValues, eventId: event?.id })\n\t\t\t\t.map((row) => row.scope))\n\t\t\treturn this.scopeChoices.filter((choice) => taken.has(String(choice?.id)) === false)\n\t\t},\n\n\t\t/**\n\t\t * What a scope is called on screen.\n\t\t *\n\t\t * @param {string} scope The scope.\n\t\t * @return {string} Its label, or the id when nobody named it.\n\t\t */\n\t\tscopeLabel(scope) {\n\t\t\tconst choice = this.scopeChoices.find((entry) => String(entry?.id) === scope)\n\t\t\treturn String(choice?.label ?? scope)\n\t\t},\n\n\t\t/**\n\t\t * Whether the platform refuses this channel for this event.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The availability.\n\t\t */\n\t\tcellAvailability(event, channel) {\n\t\t\treturn channelAvailability({\n\t\t\t\tchannel,\n\t\t\t\trefusal: this.at(this.refusals, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A value out of one of the nested maps.\n\t\t *\n\t\t * @param {object} map The map.\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {(boolean|object|null)} The value, or null.\n\t\t */\n\t\tat(map, event, channel) {\n\t\t\tconst row = map?.[event?.id]\n\t\t\tconst value = row?.[channel?.id]\n\t\t\treturn value === undefined ? null : value\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLocked(event, channel) {\n\t\t\treturn this.isLockedFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell on one row cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLockedFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\treturn this.cellFor(event, channel, scope).editable === false\n\t\t\t\t|| this.cellAvailability(event, channel).usable === false\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called to somebody who cannot see the table.\n\t\t *\n\t\t * Says the event, the channel and why it is locked when it is. A\n\t\t * disabled checkbox with no name is a dead end for a screen reader.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabel(event, channel) {\n\t\t\treturn this.cellLabelFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called, saying which row it is on.\n\t\t *\n\t\t * A scoped row is indented, and indentation is invisible to a screen\n\t\t * reader: without the scope in the name, two checkboxes for the same\n\t\t * event and channel are indistinguishable.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabelFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst base = scope === GLOBAL_SCOPE\n\t\t\t\t? t('nextcloud-vue', '{event} over {channel}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t})\n\t\t\t\t: t('nextcloud-vue', '{event} over {channel}, for {scope}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t\tscope: this.scopeLabel(scope),\n\t\t\t\t\t})\n\t\t\tconst reason = this.lockReasonFor(event, channel, scope)\n\t\t\treturn reason === '' ? base : `${base}. ${reason}`\n\t\t},\n\n\t\t/**\n\t\t * Why a cell is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReason(event, channel) {\n\t\t\treturn this.lockReasonFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Why a cell on one row is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReasonFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst availability = this.cellAvailability(event, channel)\n\t\t\tif (availability.usable === false) {\n\t\t\t\treturn availability.reason || this.unavailableText\n\t\t\t}\n\n\t\t\tconst resolved = this.cellFor(event, channel, scope)\n\t\t\tif (resolved.editable === true) {\n\t\t\t\treturn ''\n\t\t\t}\n\n\t\t\tif (resolved.forcedBy !== '' && resolved.reason !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}: {reason}', {\n\t\t\t\t\tby: resolved.forcedBy,\n\t\t\t\t\treason: resolved.reason,\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (resolved.forcedBy !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}, and cannot be changed here', { by: resolved.forcedBy })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Set by an administrator, and cannot be changed here')\n\t\t},\n\n\t\t/**\n\t\t * What a cell says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceText(event, channel) {\n\t\t\treturn this.sourceTextFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell on one row says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceTextFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst locked = this.lockReasonFor(event, channel, scope)\n\t\t\tif (locked !== '') {\n\t\t\t\treturn locked\n\t\t\t}\n\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\t\t\tif (scope !== GLOBAL_SCOPE && narrow.scope === GLOBAL_SCOPE) {\n\t\t\t\t// Said out loud, because an unanswered narrower question looks\n\t\t\t\t// exactly like an answered one: the checkbox shows the same\n\t\t\t\t// state either way.\n\t\t\t\treturn t('nextcloud-vue', 'Follows the row above')\n\t\t\t}\n\n\t\t\tconst level = this.cellFor(event, channel, scope).level\n\t\t\tif (level === PREFERENCE_LEVELS.PERSONAL) {\n\t\t\t\treturn t('nextcloud-vue', 'Your choice')\n\t\t\t}\n\t\t\tif (level === PREFERENCE_LEVELS.GROUP) {\n\t\t\t\treturn t('nextcloud-vue', 'Follows your group')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Follows the app default')\n\t\t},\n\n\t\t/**\n\t\t * Whether a group is collapsed.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a group, or open it again.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t */\n\t\ttoggleGroup(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * A cell was set.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t * @param {string} scope The scope of the row it is on.\n\t\t */\n\t\tonToggle(event, channel, domEvent, scope = GLOBAL_SCOPE) {\n\t\t\t// Refuse from here too, not only in the template. A disabled\n\t\t\t// attribute is a rendering; this is the rule.\n\t\t\tif (this.isLockedFor(event, channel, scope)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event change A cell was set.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('change', {\n\t\t\t\teventId: event.id,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tscope,\n\t\t\t\tvalue: domEvent?.target?.checked === true,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A scope was added to a row, from the row itself.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonAddScope(event, domEvent) {\n\t\t\tconst scope = String(domEvent?.target?.value ?? '')\n\t\t\tif (scope === '') {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Back to the placeholder, so the control reads as an action\n\t\t\t// rather than as a setting that now says something.\n\t\t\tif (domEvent?.target) {\n\t\t\t\tdomEvent.target.value = ''\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event add-scope A row was narrowed.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('add-scope', { eventId: event.id, scope })\n\t\t},\n\n\t\t/**\n\t\t * What a channel's digest is set to.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} `{ mode, timeOfDay }`.\n\t\t */\n\t\tdigestFor(channel) {\n\t\t\tconst set = this.digest?.[channel?.id]\n\t\t\treturn {\n\t\t\t\tmode: String(set?.mode ?? 'off'),\n\t\t\t\ttimeOfDay: String(set?.timeOfDay ?? ''),\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The digest mode was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestMode(channel, domEvent) {\n\t\t\t/**\n\t\t\t * @event digest-change Emitted when a reader changes a channel's digest mode or its time of day. Payload: `{ channelId, mode, timeOfDay }`.\n\t\t\t */\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: String(domEvent?.target?.value ?? 'off'),\n\t\t\t\ttimeOfDay: this.digestFor(channel).timeOfDay,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The digest time was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestTime(channel, domEvent) {\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: this.digestFor(channel).mode,\n\t\t\t\ttimeOfDay: String(domEvent?.target?.value ?? ''),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The last test send on a channel, or null when there has been none.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {?object} `{ ok, message }`.\n\t\t */\n\t\ttestResultFor(channel) {\n\t\t\tconst result = this.testResults?.[channel?.id]\n\t\t\treturn result === undefined || result === null ? null : result\n\t\t},\n\n\t\t/**\n\t\t * What a test send reported.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\ttestResultText(channel) {\n\t\t\tconst result = this.testResultFor(channel)\n\t\t\tif (result === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (String(result.message ?? '') !== '') {\n\t\t\t\treturn String(result.message)\n\t\t\t}\n\t\t\t// Only when the server sent no sentence of its own. A refusal\n\t\t\t// without a reason still has to say it was a refusal.\n\t\t\treturn result.ok === true\n\t\t\t\t? t('nextcloud-vue', 'The test arrived.')\n\t\t\t\t: t('nextcloud-vue', 'The test did not arrive.')\n\t\t},\n\n\t\t/**\n\t\t * What the test button is called.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\ttestSendLabel(channel) {\n\t\t\treturn t('nextcloud-vue', 'Send a test over {channel}', { channel: channel.label })\n\t\t},\n\n\t\t/**\n\t\t * What the add-a-scope control is called.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\taddScopeLabel(event) {\n\t\t\treturn t('nextcloud-vue', 'Set {event} for one kind of case', { event: event.label })\n\t\t},\n\n\t\t/**\n\t\t * A test send was asked for.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t */\n\t\tonTestSend(channel) {\n\t\t\t/**\n\t\t\t * @event test-send Emitted when a reader asks for a test notification on one channel. Payload: `{ channelId }`.\n\t\t\t */\n\t\t\tthis.$emit('test-send', { channelId: channel.id })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-notification-matrix__table {\n\tinline-size: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-notification-matrix__caption {\n\ttext-align: start;\n\tpadding-block-end: 8px;\n}\n\n.cn-notification-matrix__table th,\n.cn-notification-matrix__table td {\n\ttext-align: start;\n\tpadding: 6px 8px;\n\tborder-block-end: 1px solid var(--color-border);\n\tvertical-align: top;\n}\n\n.cn-notification-matrix__muted {\n\tdisplay: block;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85rem;\n}\n\n.cn-notification-matrix__group {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-notification-matrix__scope {\n\tpadding-inline-start: 24px !important;\n\tfont-weight: normal;\n}\n\n.cn-notification-matrix__row--scoped th,\n.cn-notification-matrix__row--scoped td {\n\tborder-block-end-style: dotted;\n}\n\n.cn-notification-matrix__test {\n\tmargin-block-start: 4px;\n}\n\n.cn-notification-matrix__note {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The board look (DqPersoonlijkeInstellingen \"Meldingen\"): muted 13px/600\n headers over a hairline, the channel columns 96px with a centred 20px\n checkbox, the event rows 10px high with the soft rule. The caption stays for\n assistive technology. */\n.cn-notification-matrix--board {\n\t.cn-notification-matrix__caption {\n\t\tposition: absolute;\n\t\tinline-size: 1px;\n\t\tblock-size: 1px;\n\t\toverflow: hidden;\n\t\tclip-path: inset(50%);\n\t\twhite-space: nowrap;\n\t}\n\n\t/* A theme that colours every th (thematiq's table header token) leaves\n\t the board grid's head white. */\n\t.cn-notification-matrix__table {\n\t\t--nldesign-component-table-header-background-color: transparent;\n\t\tfont-size: 14px;\n\t}\n\n\t.cn-notification-matrix__table thead th {\n\t\tpadding: 8px 10px 8px 0;\n\t\tline-height: 1.2;\n\t\tborder-block-end: 1px solid var(--color-border);\n\t\tcolor: var(--color-text-maxcontrast);\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tvertical-align: bottom;\n\t}\n\n\t.cn-notification-matrix__table thead th[data-channel] {\n\t\tinline-size: 96px;\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody th,\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding: 10px 10px 10px 0;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t\tfont-weight: 400;\n\t\tline-height: 1.4;\n\t\tvertical-align: middle;\n\t}\n\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody td input[type='checkbox'] {\n\t\tinline-size: 20px;\n\t\tmin-inline-size: 20px;\n\t\tblock-size: 20px;\n\t\tmin-block-size: 20px;\n\t\t/* A block box, so the line's baseline adds no height: the row is\n\t\t 42px as on the board (10px, the 1px-low checkbox, 10px, the rule). */\n\t\tdisplay: block;\n\t\tmargin: 1px auto 0;\n\t}\n\n\t.cn-notification-matrix__group {\n\t\tpadding: 4px 0;\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tcolor: var(--color-text-maxcontrast);\n\t}\n\n\t.cn-notification-matrix__channels {\n\t\tmargin-block-start: 16px;\n\t}\n\n\t.cn-notification-matrix__channels-summary {\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t\tcursor: pointer;\n\t}\n\n\t.cn-notification-matrix__channel {\n\t\tdisplay: flex;\n\t\tflex-wrap: wrap;\n\t\talign-items: flex-end;\n\t\tgap: 8px 16px;\n\t\tpadding-block: 10px;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t}\n\n\t.cn-notification-matrix__channel-name {\n\t\tflex: 1 0 100%;\n\t\tmargin: 0;\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t}\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnNotificationMatrix.vue3.js","sources":["../../../../src/components/CnNotificationMatrix/CnNotificationMatrix.vue?vue&type=template&id=93dc585a&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-notification-matrix\" :class=\"{ 'cn-notification-matrix--board': isBoardLook }\" data-testid=\"cn-notification-matrix\">\n\t\t<p v-if=\"adminMode\" class=\"cn-notification-matrix__note\" data-testid=\"cn-nm-admin-note\">\n\t\t\t{{ adminNote }}\n\t\t</p>\n\n\t\t<table class=\"cn-notification-matrix__table\" data-testid=\"cn-nm-table\">\n\t\t\t<caption class=\"cn-notification-matrix__caption\">\n\t\t\t\t{{ tableCaption }}\n\t\t\t</caption>\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ eventHeader }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`ch-${channel.id}`\"\n\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-channel-header\"\n\t\t\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t\t\t{{ channel.label }}\n\t\t\t\t\t\t<template v-if=\"!isBoardLook\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\n\t\t\t<tbody v-for=\"group in groups\" :key=\"`grp-${group.key}`\">\n\t\t\t\t<tr>\n\t\t\t\t\t<th :colspan=\"channels.length + 1\" scope=\"colgroup\">\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__group\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-group\"\n\t\t\t\t\t\t\t:data-group=\"group.key\"\n\t\t\t\t\t\t\t:aria-expanded=\"String(!isCollapsed(group.key))\"\n\t\t\t\t\t\t\t@click=\"toggleGroup(group.key)\">\n\t\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\n\t\t\t\t<tr\n\t\t\t\t\tv-for=\"row in rowsFor(group)\"\n\t\t\t\t\tv-show=\"!isCollapsed(group.key)\"\n\t\t\t\t\t:key=\"`ev-${row.event.id}-${row.scope}`\"\n\t\t\t\t\tdata-testid=\"cn-nm-row\"\n\t\t\t\t\t:class=\"{ 'cn-notification-matrix__row--scoped': !row.isGlobal }\"\n\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t:data-scope=\"row.scope\">\n\t\t\t\t\t<th scope=\"row\" :class=\"{ 'cn-notification-matrix__scope': !row.isGlobal }\">\n\t\t\t\t\t\t<template v-if=\"row.isGlobal\">\n\t\t\t\t\t\t\t{{ row.event.label }}\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"row.event.immediate\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-immediate\">\n\t\t\t\t\t\t\t\t{{ immediateText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tv-if=\"addableScopes(row.event).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-add-scope\"\n\t\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t\t:aria-label=\"addScopeLabel(row.event)\"\n\t\t\t\t\t\t\t\tvalue=\"\"\n\t\t\t\t\t\t\t\t@change=\"onAddScope(row.event, $event)\">\n\t\t\t\t\t\t\t\t<option value=\"\">\n\t\t\t\t\t\t\t\t\t{{ addScopeText }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\tv-for=\"choice in addableScopes(row.event)\"\n\t\t\t\t\t\t\t\t\t:key=\"`sc-${row.event.id}-${choice.id}`\"\n\t\t\t\t\t\t\t\t\t:value=\"choice.id\">\n\t\t\t\t\t\t\t\t\t{{ choice.label }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t<span data-testid=\"cn-nm-scope-label\">{{ scopeLabel(row.scope) }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\n\t\t\t\t\t<td\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`c-${row.event.id}-${row.scope}-${channel.id}`\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-cell\"\n\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-level=\"cellFor(row.event, channel, row.scope).level\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-toggle\"\n\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:checked=\"cellFor(row.event, channel, row.scope).value\"\n\t\t\t\t\t\t\t:disabled=\"isLockedFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-label=\"cellLabelFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-describedby=\"isBoardLook ? sourceIdFor(row.event, channel, row.scope) : null\"\n\t\t\t\t\t\t\t@change=\"onToggle(row.event, channel, $event, row.scope)\">\n\t\t\t\t\t\t<!-- The board look draws only the checkbox; why it is set stays said, and stays visible when the cell is locked. -->\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t:id=\"sourceIdFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t:class=\"{ 'hidden-visually': isBoardLook && !isLockedFor(row.event, channel, row.scope) }\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-source\">\n\t\t\t\t\t\t\t{{ sourceTextFor(row.event, channel, row.scope) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<!-- The board look keeps the grid to its checkboxes; each channel's bundling, its time and the test send live here. -->\n\t\t<details\n\t\t\tv-if=\"isBoardLook\"\n\t\t\tclass=\"cn-notification-matrix__channels\"\n\t\t\tdata-testid=\"cn-nm-channel-settings\">\n\t\t\t<summary class=\"cn-notification-matrix__channels-summary\">\n\t\t\t\t{{ channelSettingsTitle }}\n\t\t\t</summary>\n\t\t\t<div\n\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t:key=\"`cs-${channel.id}`\"\n\t\t\t\tclass=\"cn-notification-matrix__channel\"\n\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t<h4 class=\"cn-notification-matrix__channel-name\">\n\t\t\t\t\t{{ channel.label }}\n\t\t\t\t</h4>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t</span>\n\n\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<label\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t</span>\n\n\t\t\t\t\t<button\n\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t</button>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</details>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport {\n\tchannelAvailability,\n\tPREFERENCE_LEVELS,\n\tresolvePreference,\n} from '../../utils/notificationPreference.js'\nimport {\n\tGLOBAL_SCOPE,\n\tscopeRowsFor,\n\tvalueForScope,\n} from '../../utils/preferenceScopes.js'\n\n/**\n * CnNotificationMatrix — one screen where a person chooses what notifies them,\n * and finds out why they are getting what they are getting.\n *\n * 🔴 IT DOES NOT REPLACE `CnNotificationPreferences`, IT SITS BESIDE IT. That\n * component is a self-contained settings pane: it takes no props, fetches for\n * itself and scopes to the app whose settings modal is open, and `CnAppRoot`\n * mounts it as the default of its `#user-settings` slot. This one takes a\n * catalogue from its host, knows four levels rather than two and has a channel\n * axis. Replacing the older one in place, which an earlier revision of this\n * change did, left every app that mounts it through `CnAppRoot` rendering an\n * empty table with no error: a silent regression, not a breakage anybody would\n * see in a test.\n *\n * Events down, channels across. The catalogue is the host's, so this component\n * never knows what a \"term expires\" event is.\n *\n * 🔴 IT NEVER SHOWS A SETTING THAT DOES NOT APPLY. That is the whole point of\n * the screen and it is the one way it can fail while looking perfect. Three\n * cases, each handled rather than hidden:\n *\n * - A FORCED cell is locked, says who forced it and why. An administrator\n * has taken the choice away; a toggle the person can move which then does\n * nothing is worse than a locked one, because they believe they acted.\n * - A channel the instance has not configured is disabled with the reason.\n * - A channel REFUSED for this kind and recipient says the rule. An internal\n * notice to an outside recipient is a rule working, not a gap to fix, and\n * \"no channels available\" would send somebody to the wrong place.\n *\n * 🔴 IT IS A REAL TABLE. A grid of checkboxes with no row and column headers\n * is unreadable to a screen reader: every cell is \"checkbox, checked\" with no\n * way to know which event or which channel. The header cells are `th` with\n * scopes, and each toggle carries a name that says both.\n *\n * 🔴 A PREFERENCE IS SET GLOBALLY OR FOR ONE CASE DOMAIN. \"Tell me when a term\n * expires\" is usually too much and \"never\" is too little, so the event row is\n * the global one and scoped rows sit indented under it. The narrower row wins\n * only where it applies: everywhere else the global row still stands, because\n * a scope that replaced it would silence every other domain the moment\n * somebody narrowed one.\n *\n * 🔴 ONLY EVENTS IN THE CATALOGUE ARE LISTED. A stored preference for an event\n * that no longer exists is not rendered, because a switch for something that\n * can no longer happen is a lie; the store drops it on the next write.\n *\n * 🔴 A TEST SEND REPORTS WHAT HAPPENED, INCLUDING A REFUSAL. Nobody should have\n * to wait for a real event to find out a channel is broken, and a button that\n * silently did nothing would read as proof the channel works.\n *\n * @event {object} change — A cell was set. Payload: `{ eventId, channelId, scope, value }`.\n * @event {object} add-scope — A scope was added to a row. Payload: `{ eventId, scope }`.\n * @event {object} digest-change — A channel's digest was set. Payload: `{ channelId, mode, timeOfDay }`.\n * @event {object} test-send — A test was asked for. Payload: `{ channelId }`.\n */\nexport default {\n\tname: 'CnNotificationMatrix',\n\n\tinject: {\n\t\t/** The look CnAppRoot (or the page) draws in. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The events, each `{ id, label, group, groupLabel, immediate,\n\t\t * appDefault }`. From the host's catalogue.\n\t\t */\n\t\tevents: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The channels, each `{ id, label, configured, unconfiguredReason }`. */\n\t\tchannels: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The group's values, as `{ [eventId]: { [channelId]: boolean } }`. */\n\t\tgroupValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** This person's values, same shape. */\n\t\tpersonalValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What an administrator has forced, as\n\t\t * `{ [eventId]: { [channelId]: { value, by, reason } } }`.\n\t\t *\n\t\t * A level ABOVE the person's own rather than another default: it is\n\t\t * the administrator removing the choice, not setting it.\n\t\t */\n\t\tforcedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What the platform refuses for this recipient, same shape, each\n\t\t * `{ reason }`. A refusal is not an absence: it is a rule, and the\n\t\t * screen says which.\n\t\t */\n\t\trefusals: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Whether this is the admin screen writing the group's defaults. The\n\t\t * screen says a person's own value wins, so an administrator is not\n\t\t * surprised when somebody does not get what they set.\n\t\t */\n\t\tadminMode: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * This person's narrower values, as\n\t\t * `{ [eventId]: { [channelId]: { [scope]: boolean } } }`.\n\t\t *\n\t\t * Separate from `personalValues` rather than replacing it, so the\n\t\t * global row keeps the flat shape every existing caller passes. A row\n\t\t * left unset here falls through to the global value, which is what\n\t\t * makes an absent scoped row an unanswered question rather than a no.\n\t\t */\n\t\tscopedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The scopes a row can be narrowed to, each `{ id, label }`. Case\n\t\t * domains or record types, named by whoever configured the app.\n\t\t */\n\t\tscopeChoices: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The digest per channel, as `{ [channelId]: { mode, timeOfDay } }`\n\t\t * where mode is `off`, `daily` or `weekly`.\n\t\t */\n\t\tdigest: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The last test send per channel, as\n\t\t * `{ [channelId]: { ok, message } }`. A refusal belongs here as much\n\t\t * as a success: both are results.\n\t\t */\n\t\ttestResults: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['change', 'add-scope', 'digest-change', 'test-send'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Groups the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the matrix draws the board's grid.\n\t\t *\n\t\t * @return {boolean} True in the board look.\n\t\t * @spec openspec/changes/notification-matrix-board-grid/specs/notification-matrix/spec.md\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/** @return {string} The disclosure under the board grid. */\n\t\tchannelSettingsTitle() {\n\t\t\treturn t('nextcloud-vue', 'Bundling and test message')\n\t\t},\n\n\t\t/** @return {Array<object>} The events, in their groups. */\n\t\tgroups() {\n\t\t\tconst byKey = new Map()\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst key = String(event?.group ?? '')\n\t\t\t\tif (byKey.has(key) === false) {\n\t\t\t\t\tbyKey.set(key, {\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: String(event?.groupLabel ?? key) || this.ungroupedText,\n\t\t\t\t\t\tevents: [],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tbyKey.get(key).events.push(event)\n\t\t\t}\n\t\t\treturn [...byKey.values()]\n\t\t},\n\n\t\t/** @return {string} The table's caption. */\n\t\ttableCaption() {\n\t\t\treturn t('nextcloud-vue', 'What notifies you, and over which channel')\n\t\t},\n\n\t\t/** @return {string} The events column header. */\n\t\teventHeader() {\n\t\t\treturn t('nextcloud-vue', 'Event')\n\t\t},\n\n\t\t/** @return {string} What an ungrouped event group is called. */\n\t\tungroupedText() {\n\t\t\treturn t('nextcloud-vue', 'Other')\n\t\t},\n\n\t\t/** @return {string} What an unusable channel says with no reason given. */\n\t\tunavailableText() {\n\t\t\treturn t('nextcloud-vue', 'Not available on this instance')\n\t\t},\n\n\t\t/** @return {string} What an immediate event says. */\n\t\timmediateText() {\n\t\t\treturn t('nextcloud-vue', 'Always sent straight away, never held for a digest')\n\t\t},\n\n\t\t/** @return {Array<object>} The digest choices, in the order they escalate. */\n\t\tdigestModes() {\n\t\t\treturn [\n\t\t\t\t{ id: 'off', label: t('nextcloud-vue', 'Send each one as it happens') },\n\t\t\t\t{ id: 'daily', label: t('nextcloud-vue', 'Once a day') },\n\t\t\t\t{ id: 'weekly', label: t('nextcloud-vue', 'Once a week') },\n\t\t\t]\n\t\t},\n\n\t\t/** @return {string} What the digest control is called. */\n\t\tdigestLabel() {\n\t\t\treturn t('nextcloud-vue', 'Bundle these')\n\t\t},\n\n\t\t/** @return {string} What the time control is called. */\n\t\tdigestTimeLabel() {\n\t\t\treturn t('nextcloud-vue', 'Time of day')\n\t\t},\n\n\t\t/** @return {string} What a digest says about the events it cannot hold. */\n\t\tdigestImmediateNote() {\n\t\t\treturn t('nextcloud-vue', 'Events marked as urgent are still sent straight away.')\n\t\t},\n\n\t\t/** @return {string} What the test button says. */\n\t\ttestSendText() {\n\t\t\treturn t('nextcloud-vue', 'Send a test')\n\t\t},\n\n\t\t/** @return {string} What the add-a-scope control says when nothing is chosen. */\n\t\taddScopeText() {\n\t\t\treturn t('nextcloud-vue', 'Set this for one kind of case')\n\t\t},\n\n\t\t/** @return {string} What the admin screen says about a person's own value. */\n\t\tadminNote() {\n\t\t\treturn t('nextcloud-vue', \"These are the group's defaults. Somebody who has set their own value keeps it, unless a channel is forced.\")\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The id of a cell's source line, which describes its checkbox.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The row's scope.\n\t\t * @return {string} The id.\n\t\t */\n\t\tsourceIdFor(event, channel, scope) {\n\t\t\treturn `cn-nm-src-${this.$?.uid ?? 0}-${event.id}-${scope}-${channel.id}`.replace(/[^A-Za-z0-9_-]/g, '_')\n\t\t},\n\n\t\tt,\n\n\t\t/**\n\t\t * Whether a channel can be used at all, for any event.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} Its availability.\n\t\t */\n\t\tchannelState(channel) {\n\t\t\treturn channelAvailability({ channel })\n\t\t},\n\n\t\t/**\n\t\t * One cell, resolved.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcell(event, channel) {\n\t\t\treturn this.cellFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * One cell on one row, resolved.\n\t\t *\n\t\t * The scoped value is a NARROWER ANSWER TO THE SAME QUESTION, not a\n\t\t * separate preference, so an unset scoped cell falls back to the\n\t\t * person's global value and from there to the group and the default.\n\t\t * Reading an unset scoped cell as false would override both.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcellFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\n\t\t\treturn resolvePreference({\n\t\t\t\tappDefault: event?.appDefault === true,\n\t\t\t\tgroupValue: this.at(this.groupValues, event, channel),\n\t\t\t\tpersonalValue: narrow.value === null\n\t\t\t\t\t? this.at(this.personalValues, event, channel)\n\t\t\t\t\t: narrow.value,\n\t\t\t\tforced: this.at(this.forcedValues, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The rows one group renders: each event, then its scopes.\n\t\t *\n\t\t * Only events in the catalogue. A stored preference for an event the\n\t\t * catalogue no longer has is not listed, because a switch for\n\t\t * something that can no longer happen is a lie.\n\t\t *\n\t\t * @param {object} group The group.\n\t\t * @return {Array<object>} `{ event, scope, isGlobal }`.\n\t\t */\n\t\trowsFor(group) {\n\t\t\tconst rows = []\n\t\t\tfor (const event of group.events) {\n\t\t\t\tfor (const row of scopeRowsFor({ values: this.scopedValues, eventId: event.id })) {\n\t\t\t\t\trows.push({ event, scope: row.scope, isGlobal: row.isGlobal })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn rows\n\t\t},\n\n\t\t/**\n\t\t * The scopes this row can still be narrowed to.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {Array<object>} The remaining choices.\n\t\t */\n\t\taddableScopes(event) {\n\t\t\tconst taken = new Set(scopeRowsFor({ values: this.scopedValues, eventId: event?.id })\n\t\t\t\t.map((row) => row.scope))\n\t\t\treturn this.scopeChoices.filter((choice) => taken.has(String(choice?.id)) === false)\n\t\t},\n\n\t\t/**\n\t\t * What a scope is called on screen.\n\t\t *\n\t\t * @param {string} scope The scope.\n\t\t * @return {string} Its label, or the id when nobody named it.\n\t\t */\n\t\tscopeLabel(scope) {\n\t\t\tconst choice = this.scopeChoices.find((entry) => String(entry?.id) === scope)\n\t\t\treturn String(choice?.label ?? scope)\n\t\t},\n\n\t\t/**\n\t\t * Whether the platform refuses this channel for this event.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The availability.\n\t\t */\n\t\tcellAvailability(event, channel) {\n\t\t\treturn channelAvailability({\n\t\t\t\tchannel,\n\t\t\t\trefusal: this.at(this.refusals, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A value out of one of the nested maps.\n\t\t *\n\t\t * @param {object} map The map.\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {(boolean|object|null)} The value, or null.\n\t\t */\n\t\tat(map, event, channel) {\n\t\t\tconst row = map?.[event?.id]\n\t\t\tconst value = row?.[channel?.id]\n\t\t\treturn value === undefined ? null : value\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLocked(event, channel) {\n\t\t\treturn this.isLockedFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell on one row cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLockedFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\treturn this.cellFor(event, channel, scope).editable === false\n\t\t\t\t|| this.cellAvailability(event, channel).usable === false\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called to somebody who cannot see the table.\n\t\t *\n\t\t * Says the event, the channel and why it is locked when it is. A\n\t\t * disabled checkbox with no name is a dead end for a screen reader.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabel(event, channel) {\n\t\t\treturn this.cellLabelFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called, saying which row it is on.\n\t\t *\n\t\t * A scoped row is indented, and indentation is invisible to a screen\n\t\t * reader: without the scope in the name, two checkboxes for the same\n\t\t * event and channel are indistinguishable.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabelFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst base = scope === GLOBAL_SCOPE\n\t\t\t\t? t('nextcloud-vue', '{event} over {channel}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t})\n\t\t\t\t: t('nextcloud-vue', '{event} over {channel}, for {scope}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t\tscope: this.scopeLabel(scope),\n\t\t\t\t\t})\n\t\t\tconst reason = this.lockReasonFor(event, channel, scope)\n\t\t\treturn reason === '' ? base : `${base}. ${reason}`\n\t\t},\n\n\t\t/**\n\t\t * Why a cell is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReason(event, channel) {\n\t\t\treturn this.lockReasonFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Why a cell on one row is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReasonFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst availability = this.cellAvailability(event, channel)\n\t\t\tif (availability.usable === false) {\n\t\t\t\treturn availability.reason || this.unavailableText\n\t\t\t}\n\n\t\t\tconst resolved = this.cellFor(event, channel, scope)\n\t\t\tif (resolved.editable === true) {\n\t\t\t\treturn ''\n\t\t\t}\n\n\t\t\tif (resolved.forcedBy !== '' && resolved.reason !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}: {reason}', {\n\t\t\t\t\tby: resolved.forcedBy,\n\t\t\t\t\treason: resolved.reason,\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (resolved.forcedBy !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}, and cannot be changed here', { by: resolved.forcedBy })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Set by an administrator, and cannot be changed here')\n\t\t},\n\n\t\t/**\n\t\t * What a cell says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceText(event, channel) {\n\t\t\treturn this.sourceTextFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell on one row says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceTextFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst locked = this.lockReasonFor(event, channel, scope)\n\t\t\tif (locked !== '') {\n\t\t\t\treturn locked\n\t\t\t}\n\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\t\t\tif (scope !== GLOBAL_SCOPE && narrow.scope === GLOBAL_SCOPE) {\n\t\t\t\t// Said out loud, because an unanswered narrower question looks\n\t\t\t\t// exactly like an answered one: the checkbox shows the same\n\t\t\t\t// state either way.\n\t\t\t\treturn t('nextcloud-vue', 'Follows the row above')\n\t\t\t}\n\n\t\t\tconst level = this.cellFor(event, channel, scope).level\n\t\t\tif (level === PREFERENCE_LEVELS.PERSONAL) {\n\t\t\t\treturn t('nextcloud-vue', 'Your choice')\n\t\t\t}\n\t\t\tif (level === PREFERENCE_LEVELS.GROUP) {\n\t\t\t\treturn t('nextcloud-vue', 'Follows your group')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Follows the app default')\n\t\t},\n\n\t\t/**\n\t\t * Whether a group is collapsed.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a group, or open it again.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t */\n\t\ttoggleGroup(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * A cell was set.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t * @param {string} scope The scope of the row it is on.\n\t\t */\n\t\tonToggle(event, channel, domEvent, scope = GLOBAL_SCOPE) {\n\t\t\t// Refuse from here too, not only in the template. A disabled\n\t\t\t// attribute is a rendering; this is the rule.\n\t\t\tif (this.isLockedFor(event, channel, scope)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event change A cell was set.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('change', {\n\t\t\t\teventId: event.id,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tscope,\n\t\t\t\tvalue: domEvent?.target?.checked === true,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A scope was added to a row, from the row itself.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonAddScope(event, domEvent) {\n\t\t\tconst scope = String(domEvent?.target?.value ?? '')\n\t\t\tif (scope === '') {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Back to the placeholder, so the control reads as an action\n\t\t\t// rather than as a setting that now says something.\n\t\t\tif (domEvent?.target) {\n\t\t\t\tdomEvent.target.value = ''\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event add-scope A row was narrowed.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('add-scope', { eventId: event.id, scope })\n\t\t},\n\n\t\t/**\n\t\t * What a channel's digest is set to.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} `{ mode, timeOfDay }`.\n\t\t */\n\t\tdigestFor(channel) {\n\t\t\tconst set = this.digest?.[channel?.id]\n\t\t\treturn {\n\t\t\t\tmode: String(set?.mode ?? 'off'),\n\t\t\t\ttimeOfDay: String(set?.timeOfDay ?? ''),\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The digest mode was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestMode(channel, domEvent) {\n\t\t\t/**\n\t\t\t * @event digest-change Emitted when a reader changes a channel's digest mode or its time of day. Payload: `{ channelId, mode, timeOfDay }`.\n\t\t\t */\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: String(domEvent?.target?.value ?? 'off'),\n\t\t\t\ttimeOfDay: this.digestFor(channel).timeOfDay,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The digest time was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestTime(channel, domEvent) {\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: this.digestFor(channel).mode,\n\t\t\t\ttimeOfDay: String(domEvent?.target?.value ?? ''),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The last test send on a channel, or null when there has been none.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {?object} `{ ok, message }`.\n\t\t */\n\t\ttestResultFor(channel) {\n\t\t\tconst result = this.testResults?.[channel?.id]\n\t\t\treturn result === undefined || result === null ? null : result\n\t\t},\n\n\t\t/**\n\t\t * What a test send reported.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\ttestResultText(channel) {\n\t\t\tconst result = this.testResultFor(channel)\n\t\t\tif (result === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (String(result.message ?? '') !== '') {\n\t\t\t\treturn String(result.message)\n\t\t\t}\n\t\t\t// Only when the server sent no sentence of its own. A refusal\n\t\t\t// without a reason still has to say it was a refusal.\n\t\t\treturn result.ok === true\n\t\t\t\t? t('nextcloud-vue', 'The test arrived.')\n\t\t\t\t: t('nextcloud-vue', 'The test did not arrive.')\n\t\t},\n\n\t\t/**\n\t\t * What the test button is called.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\ttestSendLabel(channel) {\n\t\t\treturn t('nextcloud-vue', 'Send a test over {channel}', { channel: channel.label })\n\t\t},\n\n\t\t/**\n\t\t * What the add-a-scope control is called.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\taddScopeLabel(event) {\n\t\t\treturn t('nextcloud-vue', 'Set {event} for one kind of case', { event: event.label })\n\t\t},\n\n\t\t/**\n\t\t * A test send was asked for.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t */\n\t\tonTestSend(channel) {\n\t\t\t/**\n\t\t\t * @event test-send Emitted when a reader asks for a test notification on one channel. Payload: `{ channelId }`.\n\t\t\t */\n\t\t\tthis.$emit('test-send', { channelId: channel.id })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-notification-matrix__table {\n\tinline-size: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-notification-matrix__caption {\n\ttext-align: start;\n\tpadding-block-end: 8px;\n}\n\n.cn-notification-matrix__table th,\n.cn-notification-matrix__table td {\n\ttext-align: start;\n\tpadding: 6px 8px;\n\tborder-block-end: 1px solid var(--color-border);\n\tvertical-align: top;\n}\n\n.cn-notification-matrix__muted {\n\tdisplay: block;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85rem;\n}\n\n.cn-notification-matrix__group {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-notification-matrix__scope {\n\tpadding-inline-start: 24px !important;\n\tfont-weight: normal;\n}\n\n.cn-notification-matrix__row--scoped th,\n.cn-notification-matrix__row--scoped td {\n\tborder-block-end-style: dotted;\n}\n\n.cn-notification-matrix__test {\n\tmargin-block-start: 4px;\n}\n\n.cn-notification-matrix__note {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The board look (DqPersoonlijkeInstellingen \"Meldingen\"): muted 13px/600\n headers over a hairline, the channel columns 96px with a centred 20px\n checkbox, the event rows 10px high with the soft rule. The caption stays for\n assistive technology. */\n.cn-notification-matrix--board {\n\t.cn-notification-matrix__caption {\n\t\tposition: absolute;\n\t\tinline-size: 1px;\n\t\tblock-size: 1px;\n\t\toverflow: hidden;\n\t\tclip-path: inset(50%);\n\t\twhite-space: nowrap;\n\t}\n\n\t/* A theme that colours every th (thematiq's table header token) leaves\n\t the board grid's head white. */\n\t.cn-notification-matrix__table {\n\t\t--nldesign-component-table-header-background-color: transparent;\n\t\tfont-size: 14px;\n\t}\n\n\t.cn-notification-matrix__table thead th {\n\t\tpadding: 8px 10px 8px 0;\n\t\tline-height: 1.2;\n\t\tborder-block-end: 1px solid var(--color-border);\n\t\tcolor: var(--color-text-maxcontrast);\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tvertical-align: bottom;\n\t}\n\n\t.cn-notification-matrix__table thead th[data-channel] {\n\t\tinline-size: 96px;\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody th,\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding: 10px 10px 10px 0;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t\tfont-weight: 400;\n\t\tline-height: 1.4;\n\t\tvertical-align: middle;\n\t}\n\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody td input[type='checkbox'] {\n\t\tinline-size: 20px;\n\t\tmin-inline-size: 20px;\n\t\tblock-size: 20px;\n\t\tmin-block-size: 20px;\n\t\t/* A block box, so the line's baseline adds no height: the row is\n\t\t 42px as on the board (10px, the 1px-low checkbox, 10px, the rule). */\n\t\tdisplay: block;\n\t\tmargin: 1px auto 0;\n\t}\n\n\t.cn-notification-matrix__group {\n\t\tpadding: 4px 0;\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tcolor: var(--color-text-maxcontrast);\n\t}\n\n\t.cn-notification-matrix__channels {\n\t\tmargin-block-start: 16px;\n\t}\n\n\t.cn-notification-matrix__channels-summary {\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t\tcursor: pointer;\n\t}\n\n\t.cn-notification-matrix__channel {\n\t\tdisplay: flex;\n\t\tflex-wrap: wrap;\n\t\talign-items: flex-end;\n\t\tgap: 8px 16px;\n\t\tpadding-block: 10px;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t}\n\n\t.cn-notification-matrix__channel-name {\n\t\tflex: 1 0 100%;\n\t\tmargin: 0;\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t}\n}\n</style>\n"],"names":["_createElementBlock","_toDisplayString","_createElementVNode","_Fragment","_renderList","_createTextVNode","_normalizeClass","_openBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnNotificationMatrix.vue3.js","sources":["../../../../src/components/CnNotificationMatrix/CnNotificationMatrix.vue?vue&type=template&id=22022a49&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-notification-matrix\" :class=\"{ 'cn-notification-matrix--board': isBoardLook }\" data-testid=\"cn-notification-matrix\">\n\t\t<p v-if=\"adminMode\" class=\"cn-notification-matrix__note\" data-testid=\"cn-nm-admin-note\">\n\t\t\t{{ adminNote }}\n\t\t</p>\n\n\t\t<table class=\"cn-notification-matrix__table\" data-testid=\"cn-nm-table\">\n\t\t\t<caption class=\"cn-notification-matrix__caption\">\n\t\t\t\t{{ tableCaption }}\n\t\t\t</caption>\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ eventHeader }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`ch-${channel.id}`\"\n\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-channel-header\"\n\t\t\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t\t\t{{ channel.label }}\n\t\t\t\t\t\t<template v-if=\"!isBoardLook\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\n\t\t\t<tbody v-for=\"group in groups\" :key=\"`grp-${group.key}`\">\n\t\t\t\t<tr>\n\t\t\t\t\t<th :colspan=\"channels.length + 1\" scope=\"colgroup\">\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__group\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-group\"\n\t\t\t\t\t\t\t:data-group=\"group.key\"\n\t\t\t\t\t\t\t:aria-expanded=\"String(!isCollapsed(group.key))\"\n\t\t\t\t\t\t\t@click=\"toggleGroup(group.key)\">\n\t\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\n\t\t\t\t<tr\n\t\t\t\t\tv-for=\"row in rowsFor(group)\"\n\t\t\t\t\tv-show=\"!isCollapsed(group.key)\"\n\t\t\t\t\t:key=\"`ev-${row.event.id}-${row.scope}`\"\n\t\t\t\t\tdata-testid=\"cn-nm-row\"\n\t\t\t\t\t:class=\"{ 'cn-notification-matrix__row--scoped': !row.isGlobal }\"\n\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t:data-scope=\"row.scope\">\n\t\t\t\t\t<th scope=\"row\" :class=\"{ 'cn-notification-matrix__scope': !row.isGlobal }\">\n\t\t\t\t\t\t<template v-if=\"row.isGlobal\">\n\t\t\t\t\t\t\t{{ row.event.label }}\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"row.event.immediate\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-immediate\">\n\t\t\t\t\t\t\t\t{{ immediateText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tv-if=\"addableScopes(row.event).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-add-scope\"\n\t\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t\t:aria-label=\"addScopeLabel(row.event)\"\n\t\t\t\t\t\t\t\tvalue=\"\"\n\t\t\t\t\t\t\t\t@change=\"onAddScope(row.event, $event)\">\n\t\t\t\t\t\t\t\t<option value=\"\">\n\t\t\t\t\t\t\t\t\t{{ addScopeText }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\tv-for=\"choice in addableScopes(row.event)\"\n\t\t\t\t\t\t\t\t\t:key=\"`sc-${row.event.id}-${choice.id}`\"\n\t\t\t\t\t\t\t\t\t:value=\"choice.id\">\n\t\t\t\t\t\t\t\t\t{{ choice.label }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t<span data-testid=\"cn-nm-scope-label\">{{ scopeLabel(row.scope) }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\n\t\t\t\t\t<td\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`c-${row.event.id}-${row.scope}-${channel.id}`\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-cell\"\n\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-level=\"cellFor(row.event, channel, row.scope).level\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-toggle\"\n\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:checked=\"cellFor(row.event, channel, row.scope).value\"\n\t\t\t\t\t\t\t:disabled=\"isLockedFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-label=\"cellLabelFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-describedby=\"isBoardLook ? sourceIdFor(row.event, channel, row.scope) : null\"\n\t\t\t\t\t\t\t@change=\"onToggle(row.event, channel, $event, row.scope)\">\n\t\t\t\t\t\t<!-- The board look draws only the checkbox; why it is set stays said, and stays visible when the cell is locked. -->\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t:id=\"sourceIdFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t:class=\"{ 'hidden-visually': isBoardLook && !isLockedFor(row.event, channel, row.scope) }\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-source\">\n\t\t\t\t\t\t\t{{ sourceTextFor(row.event, channel, row.scope) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<!-- The board look keeps the grid to its checkboxes; each channel's bundling, its time and the test send live here. -->\n\t\t<details\n\t\t\tv-if=\"isBoardLook\"\n\t\t\tclass=\"cn-notification-matrix__channels\"\n\t\t\tdata-testid=\"cn-nm-channel-settings\">\n\t\t\t<summary class=\"cn-notification-matrix__channels-summary\">\n\t\t\t\t{{ channelSettingsTitle }}\n\t\t\t</summary>\n\t\t\t<div\n\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t:key=\"`cs-${channel.id}`\"\n\t\t\t\tclass=\"cn-notification-matrix__channel\"\n\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t<h4 class=\"cn-notification-matrix__channel-name\">\n\t\t\t\t\t{{ channel.label }}\n\t\t\t\t</h4>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t</span>\n\n\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<label\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t</span>\n\n\t\t\t\t\t<button\n\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t</button>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</details>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport {\n\tchannelAvailability,\n\tPREFERENCE_LEVELS,\n\tresolvePreference,\n} from '../../utils/notificationPreference.js'\nimport {\n\tGLOBAL_SCOPE,\n\tscopeRowsFor,\n\tvalueForScope,\n} from '../../utils/preferenceScopes.js'\n\n/**\n * CnNotificationMatrix — one screen where a person chooses what notifies them,\n * and finds out why they are getting what they are getting.\n *\n * 🔴 IT DOES NOT REPLACE `CnNotificationPreferences`, IT SITS BESIDE IT. That\n * component is a self-contained settings pane: it takes no props, fetches for\n * itself and scopes to the app whose settings modal is open, and `CnAppRoot`\n * mounts it as the default of its `#user-settings` slot. This one takes a\n * catalogue from its host, knows four levels rather than two and has a channel\n * axis. Replacing the older one in place, which an earlier revision of this\n * change did, left every app that mounts it through `CnAppRoot` rendering an\n * empty table with no error: a silent regression, not a breakage anybody would\n * see in a test.\n *\n * Events down, channels across. The catalogue is the host's, so this component\n * never knows what a \"term expires\" event is.\n *\n * 🔴 IT NEVER SHOWS A SETTING THAT DOES NOT APPLY. That is the whole point of\n * the screen and it is the one way it can fail while looking perfect. Three\n * cases, each handled rather than hidden:\n *\n * - A FORCED cell is locked, says who forced it and why. An administrator\n * has taken the choice away; a toggle the person can move which then does\n * nothing is worse than a locked one, because they believe they acted.\n * - A channel the instance has not configured is disabled with the reason.\n * - A channel REFUSED for this kind and recipient says the rule. An internal\n * notice to an outside recipient is a rule working, not a gap to fix, and\n * \"no channels available\" would send somebody to the wrong place.\n *\n * 🔴 IT IS A REAL TABLE. A grid of checkboxes with no row and column headers\n * is unreadable to a screen reader: every cell is \"checkbox, checked\" with no\n * way to know which event or which channel. The header cells are `th` with\n * scopes, and each toggle carries a name that says both.\n *\n * 🔴 A PREFERENCE IS SET GLOBALLY OR FOR ONE CASE DOMAIN. \"Tell me when a term\n * expires\" is usually too much and \"never\" is too little, so the event row is\n * the global one and scoped rows sit indented under it. The narrower row wins\n * only where it applies: everywhere else the global row still stands, because\n * a scope that replaced it would silence every other domain the moment\n * somebody narrowed one.\n *\n * 🔴 ONLY EVENTS IN THE CATALOGUE ARE LISTED. A stored preference for an event\n * that no longer exists is not rendered, because a switch for something that\n * can no longer happen is a lie; the store drops it on the next write.\n *\n * 🔴 A TEST SEND REPORTS WHAT HAPPENED, INCLUDING A REFUSAL. Nobody should have\n * to wait for a real event to find out a channel is broken, and a button that\n * silently did nothing would read as proof the channel works.\n *\n * @event {object} change — A cell was set. Payload: `{ eventId, channelId, scope, value }`.\n * @event {object} add-scope — A scope was added to a row. Payload: `{ eventId, scope }`.\n * @event {object} digest-change — A channel's digest was set. Payload: `{ channelId, mode, timeOfDay }`.\n * @event {object} test-send — A test was asked for. Payload: `{ channelId }`.\n */\n/**\n * An event's shipped default in one channel: one boolean for every channel,\n * or the channel's own entry when the default differs per channel.\n *\n * @param {object} event The event.\n * @param {object} channel The channel.\n * @return {boolean} The default.\n * @spec openspec/changes/notification-matrix-default-per-channel/specs/notification-matrix/spec.md\n */\nfunction appDefaultFor(event, channel) {\n\tconst value = event?.appDefault\n\tif (value !== null && typeof value === 'object') {\n\t\treturn value[channel?.id] === true\n\t}\n\treturn value === true\n}\n\nexport default {\n\tname: 'CnNotificationMatrix',\n\n\tinject: {\n\t\t/** The look CnAppRoot (or the page) draws in. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The events, each `{ id, label, group, groupLabel, immediate,\n\t\t * appDefault }`. From the host's catalogue. `appDefault` is one\n\t\t * boolean for every channel, or `{ [channelId]: boolean }` when the\n\t\t * default differs per channel (an opt-in e-mail is off by default).\n\t\t */\n\t\tevents: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The channels, each `{ id, label, configured, unconfiguredReason }`. */\n\t\tchannels: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The group's values, as `{ [eventId]: { [channelId]: boolean } }`. */\n\t\tgroupValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** This person's values, same shape. */\n\t\tpersonalValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What an administrator has forced, as\n\t\t * `{ [eventId]: { [channelId]: { value, by, reason } } }`.\n\t\t *\n\t\t * A level ABOVE the person's own rather than another default: it is\n\t\t * the administrator removing the choice, not setting it.\n\t\t */\n\t\tforcedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What the platform refuses for this recipient, same shape, each\n\t\t * `{ reason }`. A refusal is not an absence: it is a rule, and the\n\t\t * screen says which.\n\t\t */\n\t\trefusals: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Whether this is the admin screen writing the group's defaults. The\n\t\t * screen says a person's own value wins, so an administrator is not\n\t\t * surprised when somebody does not get what they set.\n\t\t */\n\t\tadminMode: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * This person's narrower values, as\n\t\t * `{ [eventId]: { [channelId]: { [scope]: boolean } } }`.\n\t\t *\n\t\t * Separate from `personalValues` rather than replacing it, so the\n\t\t * global row keeps the flat shape every existing caller passes. A row\n\t\t * left unset here falls through to the global value, which is what\n\t\t * makes an absent scoped row an unanswered question rather than a no.\n\t\t */\n\t\tscopedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The scopes a row can be narrowed to, each `{ id, label }`. Case\n\t\t * domains or record types, named by whoever configured the app.\n\t\t */\n\t\tscopeChoices: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The digest per channel, as `{ [channelId]: { mode, timeOfDay } }`\n\t\t * where mode is `off`, `daily` or `weekly`.\n\t\t */\n\t\tdigest: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The last test send per channel, as\n\t\t * `{ [channelId]: { ok, message } }`. A refusal belongs here as much\n\t\t * as a success: both are results.\n\t\t */\n\t\ttestResults: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['change', 'add-scope', 'digest-change', 'test-send'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Groups the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the matrix draws the board's grid.\n\t\t *\n\t\t * @return {boolean} True in the board look.\n\t\t * @spec openspec/changes/notification-matrix-board-grid/specs/notification-matrix/spec.md\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/** @return {string} The disclosure under the board grid. */\n\t\tchannelSettingsTitle() {\n\t\t\treturn t('nextcloud-vue', 'Bundling and test message')\n\t\t},\n\n\t\t/** @return {Array<object>} The events, in their groups. */\n\t\tgroups() {\n\t\t\tconst byKey = new Map()\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst key = String(event?.group ?? '')\n\t\t\t\tif (byKey.has(key) === false) {\n\t\t\t\t\tbyKey.set(key, {\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: String(event?.groupLabel ?? key) || this.ungroupedText,\n\t\t\t\t\t\tevents: [],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tbyKey.get(key).events.push(event)\n\t\t\t}\n\t\t\treturn [...byKey.values()]\n\t\t},\n\n\t\t/** @return {string} The table's caption. */\n\t\ttableCaption() {\n\t\t\treturn t('nextcloud-vue', 'What notifies you, and over which channel')\n\t\t},\n\n\t\t/** @return {string} The events column header. */\n\t\teventHeader() {\n\t\t\treturn t('nextcloud-vue', 'Event')\n\t\t},\n\n\t\t/** @return {string} What an ungrouped event group is called. */\n\t\tungroupedText() {\n\t\t\treturn t('nextcloud-vue', 'Other')\n\t\t},\n\n\t\t/** @return {string} What an unusable channel says with no reason given. */\n\t\tunavailableText() {\n\t\t\treturn t('nextcloud-vue', 'Not available on this instance')\n\t\t},\n\n\t\t/** @return {string} What an immediate event says. */\n\t\timmediateText() {\n\t\t\treturn t('nextcloud-vue', 'Always sent straight away, never held for a digest')\n\t\t},\n\n\t\t/** @return {Array<object>} The digest choices, in the order they escalate. */\n\t\tdigestModes() {\n\t\t\treturn [\n\t\t\t\t{ id: 'off', label: t('nextcloud-vue', 'Send each one as it happens') },\n\t\t\t\t{ id: 'daily', label: t('nextcloud-vue', 'Once a day') },\n\t\t\t\t{ id: 'weekly', label: t('nextcloud-vue', 'Once a week') },\n\t\t\t]\n\t\t},\n\n\t\t/** @return {string} What the digest control is called. */\n\t\tdigestLabel() {\n\t\t\treturn t('nextcloud-vue', 'Bundle these')\n\t\t},\n\n\t\t/** @return {string} What the time control is called. */\n\t\tdigestTimeLabel() {\n\t\t\treturn t('nextcloud-vue', 'Time of day')\n\t\t},\n\n\t\t/** @return {string} What a digest says about the events it cannot hold. */\n\t\tdigestImmediateNote() {\n\t\t\treturn t('nextcloud-vue', 'Events marked as urgent are still sent straight away.')\n\t\t},\n\n\t\t/** @return {string} What the test button says. */\n\t\ttestSendText() {\n\t\t\treturn t('nextcloud-vue', 'Send a test')\n\t\t},\n\n\t\t/** @return {string} What the add-a-scope control says when nothing is chosen. */\n\t\taddScopeText() {\n\t\t\treturn t('nextcloud-vue', 'Set this for one kind of case')\n\t\t},\n\n\t\t/** @return {string} What the admin screen says about a person's own value. */\n\t\tadminNote() {\n\t\t\treturn t('nextcloud-vue', \"These are the group's defaults. Somebody who has set their own value keeps it, unless a channel is forced.\")\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The id of a cell's source line, which describes its checkbox.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The row's scope.\n\t\t * @return {string} The id.\n\t\t */\n\t\tsourceIdFor(event, channel, scope) {\n\t\t\treturn `cn-nm-src-${this.$?.uid ?? 0}-${event.id}-${scope}-${channel.id}`.replace(/[^A-Za-z0-9_-]/g, '_')\n\t\t},\n\n\t\tt,\n\n\t\t/**\n\t\t * Whether a channel can be used at all, for any event.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} Its availability.\n\t\t */\n\t\tchannelState(channel) {\n\t\t\treturn channelAvailability({ channel })\n\t\t},\n\n\t\t/**\n\t\t * One cell, resolved.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcell(event, channel) {\n\t\t\treturn this.cellFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * One cell on one row, resolved.\n\t\t *\n\t\t * The scoped value is a NARROWER ANSWER TO THE SAME QUESTION, not a\n\t\t * separate preference, so an unset scoped cell falls back to the\n\t\t * person's global value and from there to the group and the default.\n\t\t * Reading an unset scoped cell as false would override both.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcellFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\n\t\t\treturn resolvePreference({\n\t\t\t\tappDefault: appDefaultFor(event, channel),\n\t\t\t\tgroupValue: this.at(this.groupValues, event, channel),\n\t\t\t\tpersonalValue: narrow.value === null\n\t\t\t\t\t? this.at(this.personalValues, event, channel)\n\t\t\t\t\t: narrow.value,\n\t\t\t\tforced: this.at(this.forcedValues, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The rows one group renders: each event, then its scopes.\n\t\t *\n\t\t * Only events in the catalogue. A stored preference for an event the\n\t\t * catalogue no longer has is not listed, because a switch for\n\t\t * something that can no longer happen is a lie.\n\t\t *\n\t\t * @param {object} group The group.\n\t\t * @return {Array<object>} `{ event, scope, isGlobal }`.\n\t\t */\n\t\trowsFor(group) {\n\t\t\tconst rows = []\n\t\t\tfor (const event of group.events) {\n\t\t\t\tfor (const row of scopeRowsFor({ values: this.scopedValues, eventId: event.id })) {\n\t\t\t\t\trows.push({ event, scope: row.scope, isGlobal: row.isGlobal })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn rows\n\t\t},\n\n\t\t/**\n\t\t * The scopes this row can still be narrowed to.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {Array<object>} The remaining choices.\n\t\t */\n\t\taddableScopes(event) {\n\t\t\tconst taken = new Set(scopeRowsFor({ values: this.scopedValues, eventId: event?.id })\n\t\t\t\t.map((row) => row.scope))\n\t\t\treturn this.scopeChoices.filter((choice) => taken.has(String(choice?.id)) === false)\n\t\t},\n\n\t\t/**\n\t\t * What a scope is called on screen.\n\t\t *\n\t\t * @param {string} scope The scope.\n\t\t * @return {string} Its label, or the id when nobody named it.\n\t\t */\n\t\tscopeLabel(scope) {\n\t\t\tconst choice = this.scopeChoices.find((entry) => String(entry?.id) === scope)\n\t\t\treturn String(choice?.label ?? scope)\n\t\t},\n\n\t\t/**\n\t\t * Whether the platform refuses this channel for this event.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The availability.\n\t\t */\n\t\tcellAvailability(event, channel) {\n\t\t\treturn channelAvailability({\n\t\t\t\tchannel,\n\t\t\t\trefusal: this.at(this.refusals, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A value out of one of the nested maps.\n\t\t *\n\t\t * @param {object} map The map.\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {(boolean|object|null)} The value, or null.\n\t\t */\n\t\tat(map, event, channel) {\n\t\t\tconst row = map?.[event?.id]\n\t\t\tconst value = row?.[channel?.id]\n\t\t\treturn value === undefined ? null : value\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLocked(event, channel) {\n\t\t\treturn this.isLockedFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell on one row cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLockedFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\treturn this.cellFor(event, channel, scope).editable === false\n\t\t\t\t|| this.cellAvailability(event, channel).usable === false\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called to somebody who cannot see the table.\n\t\t *\n\t\t * Says the event, the channel and why it is locked when it is. A\n\t\t * disabled checkbox with no name is a dead end for a screen reader.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabel(event, channel) {\n\t\t\treturn this.cellLabelFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called, saying which row it is on.\n\t\t *\n\t\t * A scoped row is indented, and indentation is invisible to a screen\n\t\t * reader: without the scope in the name, two checkboxes for the same\n\t\t * event and channel are indistinguishable.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabelFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst base = scope === GLOBAL_SCOPE\n\t\t\t\t? t('nextcloud-vue', '{event} over {channel}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t})\n\t\t\t\t: t('nextcloud-vue', '{event} over {channel}, for {scope}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t\tscope: this.scopeLabel(scope),\n\t\t\t\t\t})\n\t\t\tconst reason = this.lockReasonFor(event, channel, scope)\n\t\t\treturn reason === '' ? base : `${base}. ${reason}`\n\t\t},\n\n\t\t/**\n\t\t * Why a cell is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReason(event, channel) {\n\t\t\treturn this.lockReasonFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Why a cell on one row is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReasonFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst availability = this.cellAvailability(event, channel)\n\t\t\tif (availability.usable === false) {\n\t\t\t\treturn availability.reason || this.unavailableText\n\t\t\t}\n\n\t\t\tconst resolved = this.cellFor(event, channel, scope)\n\t\t\tif (resolved.editable === true) {\n\t\t\t\treturn ''\n\t\t\t}\n\n\t\t\tif (resolved.forcedBy !== '' && resolved.reason !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}: {reason}', {\n\t\t\t\t\tby: resolved.forcedBy,\n\t\t\t\t\treason: resolved.reason,\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (resolved.forcedBy !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}, and cannot be changed here', { by: resolved.forcedBy })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Set by an administrator, and cannot be changed here')\n\t\t},\n\n\t\t/**\n\t\t * What a cell says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceText(event, channel) {\n\t\t\treturn this.sourceTextFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell on one row says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceTextFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst locked = this.lockReasonFor(event, channel, scope)\n\t\t\tif (locked !== '') {\n\t\t\t\treturn locked\n\t\t\t}\n\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\t\t\tif (scope !== GLOBAL_SCOPE && narrow.scope === GLOBAL_SCOPE) {\n\t\t\t\t// Said out loud, because an unanswered narrower question looks\n\t\t\t\t// exactly like an answered one: the checkbox shows the same\n\t\t\t\t// state either way.\n\t\t\t\treturn t('nextcloud-vue', 'Follows the row above')\n\t\t\t}\n\n\t\t\tconst level = this.cellFor(event, channel, scope).level\n\t\t\tif (level === PREFERENCE_LEVELS.PERSONAL) {\n\t\t\t\treturn t('nextcloud-vue', 'Your choice')\n\t\t\t}\n\t\t\tif (level === PREFERENCE_LEVELS.GROUP) {\n\t\t\t\treturn t('nextcloud-vue', 'Follows your group')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Follows the app default')\n\t\t},\n\n\t\t/**\n\t\t * Whether a group is collapsed.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a group, or open it again.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t */\n\t\ttoggleGroup(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * A cell was set.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t * @param {string} scope The scope of the row it is on.\n\t\t */\n\t\tonToggle(event, channel, domEvent, scope = GLOBAL_SCOPE) {\n\t\t\t// Refuse from here too, not only in the template. A disabled\n\t\t\t// attribute is a rendering; this is the rule.\n\t\t\tif (this.isLockedFor(event, channel, scope)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event change A cell was set.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('change', {\n\t\t\t\teventId: event.id,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tscope,\n\t\t\t\tvalue: domEvent?.target?.checked === true,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A scope was added to a row, from the row itself.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonAddScope(event, domEvent) {\n\t\t\tconst scope = String(domEvent?.target?.value ?? '')\n\t\t\tif (scope === '') {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Back to the placeholder, so the control reads as an action\n\t\t\t// rather than as a setting that now says something.\n\t\t\tif (domEvent?.target) {\n\t\t\t\tdomEvent.target.value = ''\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event add-scope A row was narrowed.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('add-scope', { eventId: event.id, scope })\n\t\t},\n\n\t\t/**\n\t\t * What a channel's digest is set to.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} `{ mode, timeOfDay }`.\n\t\t */\n\t\tdigestFor(channel) {\n\t\t\tconst set = this.digest?.[channel?.id]\n\t\t\treturn {\n\t\t\t\tmode: String(set?.mode ?? 'off'),\n\t\t\t\ttimeOfDay: String(set?.timeOfDay ?? ''),\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The digest mode was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestMode(channel, domEvent) {\n\t\t\t/**\n\t\t\t * @event digest-change Emitted when a reader changes a channel's digest mode or its time of day. Payload: `{ channelId, mode, timeOfDay }`.\n\t\t\t */\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: String(domEvent?.target?.value ?? 'off'),\n\t\t\t\ttimeOfDay: this.digestFor(channel).timeOfDay,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The digest time was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestTime(channel, domEvent) {\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: this.digestFor(channel).mode,\n\t\t\t\ttimeOfDay: String(domEvent?.target?.value ?? ''),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The last test send on a channel, or null when there has been none.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {?object} `{ ok, message }`.\n\t\t */\n\t\ttestResultFor(channel) {\n\t\t\tconst result = this.testResults?.[channel?.id]\n\t\t\treturn result === undefined || result === null ? null : result\n\t\t},\n\n\t\t/**\n\t\t * What a test send reported.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\ttestResultText(channel) {\n\t\t\tconst result = this.testResultFor(channel)\n\t\t\tif (result === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (String(result.message ?? '') !== '') {\n\t\t\t\treturn String(result.message)\n\t\t\t}\n\t\t\t// Only when the server sent no sentence of its own. A refusal\n\t\t\t// without a reason still has to say it was a refusal.\n\t\t\treturn result.ok === true\n\t\t\t\t? t('nextcloud-vue', 'The test arrived.')\n\t\t\t\t: t('nextcloud-vue', 'The test did not arrive.')\n\t\t},\n\n\t\t/**\n\t\t * What the test button is called.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\ttestSendLabel(channel) {\n\t\t\treturn t('nextcloud-vue', 'Send a test over {channel}', { channel: channel.label })\n\t\t},\n\n\t\t/**\n\t\t * What the add-a-scope control is called.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\taddScopeLabel(event) {\n\t\t\treturn t('nextcloud-vue', 'Set {event} for one kind of case', { event: event.label })\n\t\t},\n\n\t\t/**\n\t\t * A test send was asked for.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t */\n\t\tonTestSend(channel) {\n\t\t\t/**\n\t\t\t * @event test-send Emitted when a reader asks for a test notification on one channel. Payload: `{ channelId }`.\n\t\t\t */\n\t\t\tthis.$emit('test-send', { channelId: channel.id })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-notification-matrix__table {\n\tinline-size: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-notification-matrix__caption {\n\ttext-align: start;\n\tpadding-block-end: 8px;\n}\n\n.cn-notification-matrix__table th,\n.cn-notification-matrix__table td {\n\ttext-align: start;\n\tpadding: 6px 8px;\n\tborder-block-end: 1px solid var(--color-border);\n\tvertical-align: top;\n}\n\n.cn-notification-matrix__muted {\n\tdisplay: block;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85rem;\n}\n\n.cn-notification-matrix__group {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-notification-matrix__scope {\n\tpadding-inline-start: 24px !important;\n\tfont-weight: normal;\n}\n\n.cn-notification-matrix__row--scoped th,\n.cn-notification-matrix__row--scoped td {\n\tborder-block-end-style: dotted;\n}\n\n.cn-notification-matrix__test {\n\tmargin-block-start: 4px;\n}\n\n.cn-notification-matrix__note {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The board look (DqPersoonlijkeInstellingen \"Meldingen\"): muted 13px/600\n headers over a hairline, the channel columns 96px with a centred 20px\n checkbox, the event rows 10px high with the soft rule. The caption stays for\n assistive technology. */\n.cn-notification-matrix--board {\n\t.cn-notification-matrix__caption {\n\t\tposition: absolute;\n\t\tinline-size: 1px;\n\t\tblock-size: 1px;\n\t\toverflow: hidden;\n\t\tclip-path: inset(50%);\n\t\twhite-space: nowrap;\n\t}\n\n\t/* A theme that colours every th (thematiq's table header token) leaves\n\t the board grid's head white. */\n\t.cn-notification-matrix__table {\n\t\t--nldesign-component-table-header-background-color: transparent;\n\t\tfont-size: 14px;\n\t}\n\n\t.cn-notification-matrix__table thead th {\n\t\tpadding: 8px 10px 8px 0;\n\t\tline-height: 1.2;\n\t\tborder-block-end: 1px solid var(--color-border);\n\t\tcolor: var(--color-text-maxcontrast);\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tvertical-align: bottom;\n\t}\n\n\t.cn-notification-matrix__table thead th[data-channel] {\n\t\tinline-size: 96px;\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody th,\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding: 10px 10px 10px 0;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t\tfont-weight: 400;\n\t\tline-height: 1.4;\n\t\tvertical-align: middle;\n\t}\n\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody td input[type='checkbox'] {\n\t\tinline-size: 20px;\n\t\tmin-inline-size: 20px;\n\t\tblock-size: 20px;\n\t\tmin-block-size: 20px;\n\t\t/* A block box, so the line's baseline adds no height: the row is\n\t\t 42px as on the board (10px, the 1px-low checkbox, 10px, the rule). */\n\t\tdisplay: block;\n\t\tmargin: 1px auto 0;\n\t}\n\n\t.cn-notification-matrix__group {\n\t\tpadding: 4px 0;\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tcolor: var(--color-text-maxcontrast);\n\t}\n\n\t.cn-notification-matrix__channels {\n\t\tmargin-block-start: 16px;\n\t}\n\n\t.cn-notification-matrix__channels-summary {\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t\tcursor: pointer;\n\t}\n\n\t.cn-notification-matrix__channel {\n\t\tdisplay: flex;\n\t\tflex-wrap: wrap;\n\t\talign-items: flex-end;\n\t\tgap: 8px 16px;\n\t\tpadding-block: 10px;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t}\n\n\t.cn-notification-matrix__channel-name {\n\t\tflex: 1 0 100%;\n\t\tmargin: 0;\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t}\n}\n</style>\n"],"names":["_createElementBlock","_toDisplayString","_createElementVNode","_Fragment","_renderList","_createTextVNode","_normalizeClass","_openBlock"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnObjectPartiesWidget.vue2.js';
|
|
|
2
2
|
import { render } from './CnObjectPartiesWidget.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-24a3d77a";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -14,7 +14,7 @@ import { normalizeLook } from '../../composables/useLook.js';
|
|
|
14
14
|
import innerHeading from '../../mixins/innerHeading.js';
|
|
15
15
|
import { conditionalLine } from '../../utils/conditionalLine.js';
|
|
16
16
|
import { initialsOf } from '../../utils/initials.js';
|
|
17
|
-
import { rolesInOrder, kindCode, roleLabel, indicatorsOf, indicatorVerdict, newPartyRecord, savedUuid, representedByMap, documentsOfParty } from '../../utils/objectParties.js';
|
|
17
|
+
import { rolesInOrder, kindCode, roleLabel, indicatorsOf, indicatorVerdict, newPartyRecord, savedUuid, representedByMap, partyKindAccepted, documentsOfParty } from '../../utils/objectParties.js';
|
|
18
18
|
import { fetchCallerScope } from '../../utils/objectPermissions.js';
|
|
19
19
|
import { readPath } from '../../utils/readPath.js';
|
|
20
20
|
|
|
@@ -654,7 +654,7 @@ var script = {
|
|
|
654
654
|
if (!party?.partyKind || !this.kinds || (enforce && this.kinds.source !== enforce)) {
|
|
655
655
|
return false
|
|
656
656
|
}
|
|
657
|
-
return !this.kinds.kinds
|
|
657
|
+
return !partyKindAccepted(party.partyKind, this.kinds.kinds, this.listing?.kinds)
|
|
658
658
|
},
|
|
659
659
|
|
|
660
660
|
/**
|