@conduction/nextcloud-vue 2.83.0 → 2.85.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/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +131 -3
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +54 -18
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +232 -4
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +95 -51
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue.js +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue2.js +22 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue2.js.map +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue3.js.map +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +47 -4
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue.js +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +239 -3
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +5 -5
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +50 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +22 -12
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue.js +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +226 -8
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +3 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +18 -5
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +38 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +45 -5
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue.js +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +548 -77
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +191 -82
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js +11 -2
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js +29 -6
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +126 -2
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +24 -2
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +106 -3
- 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/CnRegisterMapping/CnRegisterMapping.vue.js +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js +67 -5
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js.map +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js +181 -124
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue.js +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js +135 -2
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js +95 -43
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +5 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue.js +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +72 -5
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +33 -8
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue.js +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +135 -11
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +6 -2
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue.js +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +17 -2
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +39 -6
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +140 -7
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js +37 -9
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +6 -5
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue.js +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +16 -0
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +2 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue2.js +53 -1
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue3.js +3 -2
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +135 -0
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue.js +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +19 -0
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +37 -19
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
- package/dist/esm/composables/useListView.js +21 -0
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue.js +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js +68 -6
- package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js.map +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js +133 -105
- package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +38 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +78 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +1030 -533
- package/dist/esm/store/useObjectStore.js +16 -2
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/boardColumns.js +8 -1
- package/dist/esm/utils/boardColumns.js.map +1 -1
- package/dist/esm/utils/boardReferenceColumns.js +278 -0
- package/dist/esm/utils/boardReferenceColumns.js.map +1 -0
- package/dist/esm/utils/buildManifest.js +41 -4
- package/dist/esm/utils/buildManifest.js.map +1 -1
- package/dist/esm/utils/detailWidgetVisibility.js +149 -0
- package/dist/esm/utils/detailWidgetVisibility.js.map +1 -0
- package/dist/esm/utils/formatMetric.js +39 -3
- package/dist/esm/utils/formatMetric.js.map +1 -1
- package/dist/esm/utils/gridStack.js +40 -1
- package/dist/esm/utils/gridStack.js.map +1 -1
- package/dist/esm/utils/humaniseKey.js +41 -0
- package/dist/esm/utils/humaniseKey.js.map +1 -0
- package/dist/esm/utils/notificationRuleLabel.js +6 -23
- package/dist/esm/utils/notificationRuleLabel.js.map +1 -1
- package/dist/esm/utils/resolveManifestTokens.js +1 -0
- package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +9 -0
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/widgetDispatch.js +8 -2
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +17159 -13422
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1030 -533
- package/l10n/en.json +38 -1
- package/l10n/nl.json +78 -1
- package/package.json +1 -1
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +160 -8
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +1 -1
- package/src/components/CnBoardView/CnBoardView.vue +262 -6
- package/src/components/CnBodySections/CnBodySections.vue +22 -1
- package/src/components/CnCountdownWidget/CnCountdownWidget.vue +47 -4
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +244 -8
- package/src/components/CnDashboardPage/CnDashboardPage.vue +62 -2
- package/src/components/CnDeltaWidget/CnDeltaWidget.vue +1 -1
- package/src/components/CnDetailPage/CnDetailPage.md +3 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +226 -8
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +3 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +18 -5
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +63 -1
- package/src/components/CnGaugeWidget/CnGaugeWidget.vue +1 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +586 -25
- package/src/components/CnIndexPage/useSelfFetchList.js +10 -1
- package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +29 -6
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.md +1 -1
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +154 -5
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +106 -3
- package/src/components/CnRegisterMapping/CnRegisterMapping.md +5 -0
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +231 -96
- package/src/components/CnReportsPage/CnReportsPage.vue +179 -1
- package/src/components/CnSettingsPage/CnSettingsPage.vue +5 -1
- package/src/components/CnStagesWidget/CnStagesWidget.vue +103 -6
- package/src/components/CnStatWidget/CnStatWidget.vue +137 -12
- package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +34 -3
- package/src/components/CnTabsWidget/CnTabsWidget.vue +39 -6
- package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +140 -7
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.md +2 -1
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +42 -13
- package/src/components/CnWalkthrough/CnWalkthrough.vue +16 -0
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +2 -1
- package/src/components/CnWidgetCardGrid/CnWidgetCardGrid.vue +55 -2
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +135 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +10 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +44 -1
- package/src/composables/useListView.js +21 -0
- package/src/css/board.css +65 -4
- package/src/css/dialog.css +4 -2
- package/src/css/form-field.css +14 -0
- package/src/css/form-widget.css +11 -0
- package/src/css/index-page.css +49 -0
- package/src/css/kpi-card.css +13 -0
- package/src/css/look-board-reports.css +58 -0
- package/src/css/look-board.css +108 -7
- package/src/dialogs/CnTransitionInputDialog.vue +87 -7
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +137 -6
- package/src/store/useObjectStore.js +16 -2
- package/src/utils/boardColumns.js +8 -1
- package/src/utils/boardReferenceColumns.js +274 -0
- package/src/utils/buildManifest.js +40 -3
- package/src/utils/detailWidgetVisibility.js +145 -0
- package/src/utils/formatMetric.js +38 -2
- package/src/utils/gridStack.js +39 -0
- package/src/utils/humaniseKey.js +38 -0
- package/src/utils/notificationRuleLabel.js +5 -23
- package/src/utils/resolveManifestTokens.js +1 -0
- package/src/utils/sentinelTokens.js +9 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/widgetDispatch.js +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnWalkthrough.vue3.js","sources":["../../../../src/components/CnWalkthrough/CnWalkthrough.vue?vue&type=template&id=165ee49b&lang.js"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<div v-if=\"active\"\n\t\tclass=\"cn-walkthrough\"\n\t\t:style=\"{ zIndex }\"\n\t\trole=\"dialog\"\n\t\taria-modal=\"true\"\n\t\t:aria-label=\"dialogLabel\">\n\t\t<!-- aria-live announcement of the current step -->\n\t\t<div class=\"cn-walkthrough__live\" aria-live=\"polite\">\n\t\t\t{{ liveText }}\n\t\t</div>\n\n\t\t<!-- Dimmer: a centered step uses one full overlay; an anchored step uses\n\t\t four strips that frame the target rect, leaving a real interactive hole.\n\t\t An ANCHORED step that lost its anchor (target present but not laid\n\t\t out, e.g. inside a closed settings foldout) falls back to the full\n\t\t overlay too — but there it must let clicks THROUGH: the step's own\n\t\t task tells the user to click something in the app underneath, and an\n\t\t interactive backdrop would swallow exactly that click, leaving only\n\t\t Back/Finish usable. The trade-off is deliberate: a passthrough dim\n\t\t cannot be clicked to dismiss the tour, so on such a step the exits\n\t\t are ESC, the coachmark's close button and Back/Finish — a click on\n\t\t the greyed-out area reaches the app, which is the point. A centered\n\t\t step spotlights nothing, so its backdrop stays interactive and\n\t\t dismisses the tour. -->\n\t\t<!-- Quiet: an app dialog is open, or the step's target belongs to\n\t\t another page. No dim and no cutout, so the overlay never sits on\n\t\t top of the dialog and never invites a click on the wrong page.\n\t\t Only the docked coachmark stays. -->\n\t\t<template v-if=\"quiet\" />\n\t\t<template v-else-if=\"isCentered || !rect\">\n\t\t\t<div v-if=\"isCentered\"\n\t\t\t\tclass=\"cn-walkthrough__dim cn-walkthrough__dim--full\"\n\t\t\t\t@click.self=\"onBackdrop\" />\n\t\t\t<!-- No dismiss handler on a passthrough dim: `pointer-events: none`\n\t\t\t means a browser never delivers the click here anyway, and\n\t\t\t leaving the listener attached would make the SAME gesture\n\t\t\t dismiss the tour in any environment that ignores\n\t\t\t pointer-events (jsdom, a forced-colors mode) while reaching the\n\t\t\t app in a browser. One behaviour, not two. -->\n\t\t\t<div v-else\n\t\t\t\tclass=\"cn-walkthrough__dim cn-walkthrough__dim--full cn-walkthrough__dim--passthrough\"\n\t\t\t\taria-hidden=\"true\" />\n\t\t</template>\n\t\t<template v-else>\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.top\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.bottom\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.left\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.right\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__ring\" :style=\"ringStyle\" aria-hidden=\"true\" />\n\t\t</template>\n\n\t\t<!-- Coachmark card -->\n\t\t<div ref=\"card\"\n\t\t\tclass=\"cn-walkthrough__card\"\n\t\t\t:class=\"[`cn-walkthrough__card--${cardPlacement}`, { 'cn-walkthrough__card--docked': quiet }]\"\n\t\t\t:style=\"cardStyle\">\n\t\t\t<NcButton class=\"cn-walkthrough__close\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:aria-label=\"closeLabel\"\n\t\t\t\t:title=\"closeLabel\"\n\t\t\t\t@click=\"close\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Close :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t</NcButton>\n\t\t\t<!-- @slot coachmark Override the whole coachmark body. Scope: { step, index, total, next, back, skip, skipLabel }.\n\t\t\t The default body has no Skip control (ADR-062 gives that job to\n\t\t\t the corner close button), so `skipLabel` reaches the only place\n\t\t\t a Skip control can live: a host-supplied coachmark. -->\n\t\t\t<slot name=\"coachmark\"\n\t\t\t\t:step=\"step\"\n\t\t\t\t:index=\"index\"\n\t\t\t\t:total=\"total\"\n\t\t\t\t:next=\"advance\"\n\t\t\t\t:back=\"back\"\n\t\t\t\t:skip=\"skip\"\n\t\t\t\t:skipLabel=\"skipLabel\">\n\t\t\t\t<div class=\"cn-walkthrough__counter\">\n\t\t\t\t\t{{ index + 1 }} / {{ total }}\n\t\t\t\t</div>\n\t\t\t\t<h3 v-if=\"stepTitle\" class=\"cn-walkthrough__title\">\n\t\t\t\t\t{{ stepTitle }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"stepBody\" class=\"cn-walkthrough__body\">\n\t\t\t\t\t{{ stepBody }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"stepTask\" class=\"cn-walkthrough__task\">\n\t\t\t\t\t<span class=\"cn-walkthrough__task-icon\" aria-hidden=\"true\">👉</span> {{ stepTask }}\n\t\t\t\t</p>\n\t\t\t\t<div class=\"cn-walkthrough__actions\">\n\t\t\t\t\t<NcButton v-if=\"!isFirst\" variant=\"secondary\" @click=\"back\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronLeft :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ backLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<span class=\"cn-walkthrough__spacer\" />\n\t\t\t\t\t<NcButton v-if=\"isHandoff\"\n\t\t\t\t\t\tref=\"firstBtn\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\talignment=\"center-reverse\"\n\t\t\t\t\t\t@click=\"doHandoff\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ handoffLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton v-else-if=\"showNext\"\n\t\t\t\t\t\tref=\"firstBtn\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\talignment=\"center-reverse\"\n\t\t\t\t\t\t@click=\"advance\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ isLast ? finishLabel : nextLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</slot>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport ChevronLeft from 'vue-material-design-icons/ChevronLeft.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport { useWalkthrough } from '../../composables/useWalkthrough.js'\n\n/**\n * CnWalkthrough — abstract, manifest-driven product walkthrough (ADR-043).\n *\n * Renders a `manifest.walkthrough` tour as a gray dimmer with a spotlight cutout\n * around one real, interactive element, plus an auto-positioned coachmark card.\n * It never promotes the target's `z-index` — the dimmer is four strips framing\n * the target rect, so the element stays clickable in place regardless of its\n * stacking context. When a target is hidden inside a collapsed `NcAppNavigation`\n * group (children rendered but `display:none`, so unmeasurable), the engine\n * best-effort expands the group to reveal the item before spotlighting it, rather\n * than falling back to a centered coachmark. Step advancement is declarative: the component sources\n * signals (route change via `$router`, a `cn-walkthrough:object-created` window\n * event, an appearing element via `MutationObserver`, a click on the target, or a\n * delay) and feeds them to {@link useWalkthrough}, which captures route params /\n * object ids into a context bag interpolated into later steps.\n *\n * Mount it gated by `CnAppRoot` over the live shell, or standalone with an\n * explicit `tour-id`.\n *\n * ```vue\n * <CnWalkthrough :app-id=\"'pipelinq'\" :manifest=\"manifest\" :seen-version=\"seen\"\n * @complete=\"onComplete\" />\n * ```\n */\nexport default {\n\tname: 'CnWalkthrough',\n\n\tcomponents: { NcButton, Close, ChevronLeft, ChevronRight },\n\n\tprops: {\n\t\t/** The Nextcloud app id (walkthrough machine cache key). */\n\t\tappId: { type: String, required: true },\n\t\t/** The app manifest; `manifest.walkthrough` + `manifest.version` are read. */\n\t\tmanifest: { type: Object, default: null },\n\t\t/** The user's last-seen app version (drives \"what's new\" composition). */\n\t\tseenVersion: { type: String, default: '' },\n\t\t/** Force a specific tour id; when empty the auto-start tour is used. */\n\t\ttourId: { type: String, default: '' },\n\t\t/** Optional resume token `{ tourId, stepId }` (refresh / cross-app hand-off). */\n\t\tresume: { type: Object, default: null },\n\t\t/**\n\t\t * Whether a first-visit tour may start on its own. The host turns it\n\t\t * off while the user's tour is paused, so a paused tour stays hidden\n\t\t * across page loads until the user picks \"Continue\".\n\t\t */\n\t\tautoStart: { type: Boolean, default: true },\n\t\t/** Stacking order of the overlay. */\n\t\tzIndex: { type: Number, default: 10000 },\n\t\t/** Next button label. */\n\t\tnextLabel: { type: String, default: () => t('nextcloud-vue', 'Next') },\n\t\t/** Back button label. */\n\t\tbackLabel: { type: String, default: () => t('nextcloud-vue', 'Back') },\n\t\t/** Skip button label. */\n\t\tskipLabel: { type: String, default: () => t('nextcloud-vue', 'Skip') },\n\t\t/** Final-step button label. */\n\t\tfinishLabel: { type: String, default: () => t('nextcloud-vue', 'Finish') },\n\t\t/** Accessible label for the corner close button that ends the tour. */\n\t\tcloseLabel: { type: String, default: () => t('nextcloud-vue', 'Close tour') },\n\t\t/** Optional translate function applied to step title/body/task i18n keys. */\n\t\ttranslate: { type: Function, default: null },\n\t},\n\n\temits: ['complete', 'dismiss', 'pause', 'progress', 'step-change', 'advance', 'handoff'],\n\n\tsetup(props) {\n\t\tconst wt = useWalkthrough(props.appId, props.manifest, {\n\t\t\tseenVersion: props.seenVersion,\n\t\t\tresume: props.resume,\n\t\t\tonComplete: undefined,\n\t\t})\n\t\treturn { wt }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trect: null, // target bounding rect (viewport coords)\n\t\t\tcardPos: { top: 0, left: 0 },\n\t\t\tcardPlacement: 'bottom',\n\t\t\ttargetEl: null,\n\t\t\t// An app dialog (NcModal / NcDialog) is open over the page.\n\t\t\tappModalOpen: false,\n\t\t\t// The step's target was found on another page than the current one.\n\t\t\toffPage: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tactive() {\n\t\t\treturn this.wt.running.value && !!this.step\n\t\t},\n\n\t\tstep() {\n\t\t\treturn this.wt.currentStep.value\n\t\t},\n\n\t\tindex() {\n\t\t\treturn this.wt.activeTour.value ? this.wt.activeTour.value.steps.findIndex((s) => s.id === (this.step && this.step.id)) : 0\n\t\t},\n\n\t\ttotal() {\n\t\t\treturn this.wt.totalSteps.value\n\t\t},\n\n\t\tisFirst() {\n\t\t\treturn this.wt.isFirst.value\n\t\t},\n\n\t\tisLast() {\n\t\t\treturn this.wt.isLast.value\n\t\t},\n\n\t\t/**\n\t\t * Whether the overlay steps back: no dim, no cutout, a docked card.\n\t\t * True while an app dialog is open or the step's target is on\n\t\t * another page.\n\t\t *\n\t\t * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md\n\t\t * @return {boolean}\n\t\t */\n\t\tquiet() {\n\t\t\treturn this.appModalOpen || this.offPage\n\t\t},\n\n\t\tisCentered() {\n\t\t\treturn this.step && this.step.placement === 'center'\n\t\t},\n\n\t\t/**\n\t\t * Whether the Next control is shown. Hidden on an enforced action step\n\t\t * (a task + a non-manual advanceOn) unless `allowManualNext` is set.\n\t\t *\n\t\t * @return {boolean} True when Next should render.\n\t\t */\n\t\tshowNext() {\n\t\t\tif (!this.step) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst enforced = !!this.step.task && this.step.advanceOn && this.step.advanceOn.type !== 'manual'\n\t\t\treturn !enforced || this.step.allowManualNext === true\n\t\t},\n\n\t\t/**\n\t\t * Whether the active step is a cross-app hand-off (declares `handoff.url`).\n\t\t *\n\t\t * @return {boolean} True when the coachmark offers \"Continue in {app}\".\n\t\t */\n\t\tisHandoff() {\n\t\t\treturn !!(this.step && this.step.handoff && this.step.handoff.url)\n\t\t},\n\n\t\thandoffLabel() {\n\t\t\tconst app = this.step && this.step.handoff && this.step.handoff.app\n\t\t\treturn app ? t('nextcloud-vue', 'Continue in {app}', { app }) : t('nextcloud-vue', 'Continue')\n\t\t},\n\n\t\tstepTitle() {\n\t\t\treturn this.tr(this.step && this.step.title)\n\t\t},\n\n\t\tstepBody() {\n\t\t\treturn this.tr(this.step && this.step.body)\n\t\t},\n\n\t\t/**\n\t\t * The task line, through the same `translate` prop as title/body — a\n\t\t * raw `step.task` shipped English even in fully translated locales.\n\t\t *\n\t\t * @return {string} The translated task, or `''` when the step has none.\n\t\t */\n\t\tstepTask() {\n\t\t\treturn this.tr(this.step && this.step.task)\n\t\t},\n\n\t\tdialogLabel() {\n\t\t\treturn this.stepTitle || t('nextcloud-vue', 'Walkthrough')\n\t\t},\n\n\t\tliveText() {\n\t\t\tif (!this.step) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Step {n} of {total}', { n: this.index + 1, total: this.total }) + ': ' + (this.stepTitle || '')\n\t\t},\n\n\t\tringStyle() {\n\t\t\tif (!this.rect) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst pad = 6\n\t\t\treturn {\n\t\t\t\ttop: (this.rect.top - pad) + 'px',\n\t\t\t\tleft: (this.rect.left - pad) + 'px',\n\t\t\t\twidth: (this.rect.width + pad * 2) + 'px',\n\t\t\t\theight: (this.rect.height + pad * 2) + 'px',\n\t\t\t}\n\t\t},\n\n\t\tstrip() {\n\t\t\tconst r = this.rect\n\t\t\tif (!r) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst pad = 6\n\t\t\tconst top = Math.max(0, r.top - pad)\n\t\t\tconst bottom = r.top + r.height + pad\n\t\t\tconst left = Math.max(0, r.left - pad)\n\t\t\tconst right = r.left + r.width + pad\n\t\t\treturn {\n\t\t\t\ttop: { top: 0, left: 0, width: '100%', height: top + 'px' },\n\t\t\t\tbottom: { top: bottom + 'px', left: 0, width: '100%', bottom: 0 },\n\t\t\t\tleft: { top: top + 'px', left: 0, width: left + 'px', height: (bottom - top) + 'px' },\n\t\t\t\tright: { top: top + 'px', left: right + 'px', right: 0, height: (bottom - top) + 'px' },\n\t\t\t}\n\t\t},\n\n\t\tcardStyle() {\n\t\t\tif (this.quiet) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tif (this.isCentered || !this.rect) {\n\t\t\t\treturn { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }\n\t\t\t}\n\t\t\treturn { top: this.cardPos.top + 'px', left: this.cardPos.left + 'px' }\n\t\t},\n\t},\n\n\twatch: {\n\t\tstep(newStep, oldStep) {\n\t\t\tif (!newStep) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!oldStep || newStep.id !== oldStep.id) {\n\t\t\t\t/**\n\t\t\t\t * @event step-change Emitted when the active step changes.\n\t\t\t\t * @type {{ stepId: string, index: number }}\n\t\t\t\t */\n\t\t\t\tthis.$emit('step-change', { stepId: newStep.id, index: this.index })\n\t\t\t\t/**\n\t\t\t\t * @event progress Emitted on every step change so the host can remember where the user is.\n\t\t\t\t * @type {{ tourId: string, stepId: string, index: number }}\n\t\t\t\t */\n\t\t\t\tthis.$emit('progress', {\n\t\t\t\t\ttourId: (this.wt.activeTour.value && this.wt.activeTour.value.id) || '',\n\t\t\t\t\tstepId: newStep.id,\n\t\t\t\t\tindex: this.index,\n\t\t\t\t})\n\t\t\t\t// A new step finds its own page again.\n\t\t\t\tthis._homeRoute = null\n\t\t\t\tthis.offPage = false\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t}\n\t\t},\n\n\t\tactive(now) {\n\t\t\tif (now) {\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t} else {\n\t\t\t\tthis.teardownStep()\n\t\t\t}\n\t\t},\n\t},\n\n\t/**\n\t * Initialise non-reactive instance state (event handler refs, DOM observer\n\t * handles). These use the `_` prefix convention and are set here rather than\n\t * in `data()` to avoid Vue's reserved-key warning for `_`-prefixed fields.\n\t *\n\t * @spec openspec/changes/cn-walkthrough-engine/specs/cn-walkthrough/spec.md\n\t */\n\tcreated() {\n\t\t// Non-reactive instance state: event handlers and DOM watchers.\n\t\t// Not in data() to avoid triggering Vue's reserved-key warning (_prefix).\n\t\tthis._revealAttempted = false\n\t\tthis._observer = null\n\t\tthis._resizeObs = null\n\t\tthis._delayTimer = null\n\t\tthis._onScroll = null\n\t\tthis._onKey = null\n\t\tthis._onObjectCreated = null\n\t\tthis._routeUnhook = null\n\t\t// Layout watchers for the ACTIVE step's target (see trackTargetLayout).\n\t\tthis._layoutObs = null\n\t\tthis._bodyResizeObs = null\n\t\tthis._remeasureRaf = null\n\t\t// A qualifying auto-start tour whose first-step page is NOT the current\n\t\t// route — held here until the user navigates to that page (see hookRouter).\n\t\tthis._pendingAutoTour = null\n\t\t// The route the active step's target was first found on (see locateTarget).\n\t\tthis._homeRoute = null\n\t\tthis._modalObs = null\n\t},\n\n\tmounted() {\n\t\t// Auto-start a qualifying tour unless one is already running.\n\t\tthis.maybeAutoStart()\n\t\tthis._onScroll = () => this.computeRect()\n\t\twindow.addEventListener('scroll', this._onScroll, true)\n\t\twindow.addEventListener('resize', this._onScroll)\n\t\tthis._onKey = (e) => {\n\t\t\tif (e.key !== 'Escape' || !this.active) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// ESC while an app dialog is open belongs to that dialog. It used\n\t\t\t// to dismiss the tour too, so closing a create form ended it.\n\t\t\tif (this.isAppModalOpen()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onBackdrop()\n\t\t}\n\t\twindow.addEventListener('keydown', this._onKey)\n\t\tthis._onObjectCreated = (e) => {\n\t\t\tconst d = (e && e.detail) || {}\n\t\t\tthis.wt.notify({\n\t\t\t\tkind: 'object-created',\n\t\t\t\tregister: d.register,\n\t\t\t\tschema: d.schema,\n\t\t\t\tobject: d.object && typeof d.object === 'object' ? d.object : d,\n\t\t\t})\n\t\t}\n\t\tthis.watchAppModals()\n\t\twindow.addEventListener('cn-walkthrough:object-created', this._onObjectCreated)\n\t\tthis.hookRouter()\n\t\tif (this.active) {\n\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\twindow.removeEventListener('scroll', this._onScroll, true)\n\t\twindow.removeEventListener('resize', this._onScroll)\n\t\twindow.removeEventListener('keydown', this._onKey)\n\t\twindow.removeEventListener('cn-walkthrough:object-created', this._onObjectCreated)\n\t\tif (this._modalObs) {\n\t\t\tthis._modalObs.disconnect()\n\t\t\tthis._modalObs = null\n\t\t}\n\t\tif (typeof this._routeUnhook === 'function') {\n\t\t\tthis._routeUnhook()\n\t\t}\n\t\tthis.teardownStep()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether an app dialog (NcModal / NcDialog, or any other modal\n\t\t * dialog that is not this overlay) is open.\n\t\t *\n\t\t * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md\n\t\t * @return {boolean}\n\t\t */\n\t\tisAppModalOpen() {\n\t\t\tif (typeof document === 'undefined') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst own = this.$el\n\t\t\tconst nodes = document.querySelectorAll('.modal-mask, [role=\"dialog\"][aria-modal=\"true\"]')\n\t\t\tfor (const node of nodes) {\n\t\t\t\tif (own && typeof own.contains === 'function' && (own === node || own.contains(node))) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\n\t\t/**\n\t\t * Follow app dialogs opening and closing, so the overlay steps back\n\t\t * while one is open and comes back after.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\twatchAppModals() {\n\t\t\tconst update = () => {\n\t\t\t\tconst open = this.isAppModalOpen()\n\t\t\t\tif (open !== this.appModalOpen) {\n\t\t\t\t\tthis.appModalOpen = open\n\t\t\t\t\tif (!open && this.active) {\n\t\t\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tupdate()\n\t\t\tif (typeof window !== 'undefined' && window.MutationObserver) {\n\t\t\t\tthis._modalObs = new MutationObserver(update)\n\t\t\t\tthis._modalObs.observe(document.body, { childList: true, subtree: true })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Translate an i18n key/text via the `translate` prop when given.\n\t\t *\n\t\t * @param {string} value The key or literal.\n\t\t * @return {string} The resolved string.\n\t\t */\n\t\ttr(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.translate === 'function' ? this.translate(value) : value\n\t\t},\n\n\t\t/**\n\t\t * Hook `$router.afterEach` (when a router is present) to feed route signals.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\thookRouter() {\n\t\t\tconst router = this.$router\n\t\t\tif (!router || typeof router.afterEach !== 'function') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis._routeUnhook = router.afterEach((to) => {\n\t\t\t\tthis.wt.notify({ kind: 'route', route: to.name, params: to.params || {} })\n\t\t\t\t// A first-visit tour deferred because the user deep-linked onto a\n\t\t\t\t// different page starts once they reach its first-step page.\n\t\t\t\tif (this._pendingAutoTour && !this.wt.running.value\n\t\t\t\t\t&& this.routeMatchesTour(this._pendingAutoTour, to.name)) {\n\t\t\t\t\tconst tour = this._pendingAutoTour\n\t\t\t\t\tthis._pendingAutoTour = null\n\t\t\t\t\tthis.wt.start(tour.id)\n\t\t\t\t}\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Start a qualifying auto-start tour — but a `first-visit` tour whose\n\t\t * first step SPOTLIGHTS something on a specific page only opens when the\n\t\t * user is ON that page. A centered welcome step spotlights nothing and\n\t\t * therefore starts anywhere. Deep-linking onto an unrelated route (e.g. a shared detail-page\n\t\t * URL) defers the tour via `_pendingAutoTour` instead of popping it over\n\t\t * the wrong screen (ADR-062). A forced `tourId` always starts; a tour\n\t\t * whose first step is not page-anchored keeps the prior any-route\n\t\t * behaviour; without a router there is nothing to match, so it starts.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tmaybeAutoStart() {\n\t\t\tif (this.wt.running.value) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.tourId) {\n\t\t\t\tthis.wt.start(this.tourId)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.autoStart) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst auto = this.wt.autoStartTour.value\n\t\t\tif (!auto) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.$router) {\n\t\t\t\tthis.wt.start(auto.id)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst routeName = this.$route && this.$route.name\n\t\t\tif (this.routeMatchesTour(auto, routeName)) {\n\t\t\t\tthis.wt.start(auto.id)\n\t\t\t} else {\n\t\t\t\tthis._pendingAutoTour = auto\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The route name a tour's FIRST step is anchored to, or null when the\n\t\t * first step is not page-anchored (a centered welcome step, or a\n\t\t * non-page target). Used to gate auto-start to the right screen.\n\t\t *\n\t\t * A `placement: \"center\"` first step returns NULL however it is\n\t\t * anchored. This is the whole point: a centered step spotlights\n\t\t * nothing — it is a welcome card floating in the middle of the screen —\n\t\t * so there is no element for the user to be \"on the right page\" for,\n\t\t * and gating its auto-open on a route only decides whether the tour\n\t\t * opens at all.\n\t\t *\n\t\t * That is what it did. This docblock already CLAIMED a centered welcome\n\t\t * step returned null; the code never checked `placement`, so any app\n\t\t * whose welcome step happened to carry a page/nav anchor other than its\n\t\t * landing route parked the tour in `_pendingAutoTour` and never opened\n\t\t * it — no error, no log, just no walkthrough. Measured on one instance\n\t\t * with an empty seen-state: opencatalogi (welcome anchored to\n\t\t * `Catalogs`) and pipelinq (anchored to `Products`) both showed nothing\n\t\t * at `#/` and the full tour at the anchored route, while dossiq\n\t\t * (anchored to its landing `Dashboard`) worked — the control that shows\n\t\t * the anchor, not the tour, was the variable. 19 of 20 fleet manifests\n\t\t * declare a walkthrough, so this is a class, not two instances.\n\t\t *\n\t\t * A step that genuinely spotlights an element keeps the ADR-062\n\t\t * behaviour, which is what it was built for: it still waits for its\n\t\t * page rather than popping over a deep-linked detail screen.\n\t\t *\n\t\t * @param {object} tour The tour definition.\n\t\t * @return {string|null} The first-step page/nav route name, or null.\n\t\t */\n\t\tfirstStepPage(tour) {\n\t\t\tconst steps = (tour && tour.steps) || []\n\t\t\tconst first = steps[0]\n\t\t\t// A CENTERED first step is not page-anchored, whatever it targets.\n\t\t\t//\n\t\t\t// The doc comment above has always said so; the code did not check.\n\t\t\t// `placement: 'center'` renders a card in the middle of the screen\n\t\t\t// and spotlights NOTHING — `locateTarget()` returns early on\n\t\t\t// `isCentered` and never resolves the target at all. So gating\n\t\t\t// auto-start on being on that step's route asks the user to be\n\t\t\t// somewhere the step will not point at.\n\t\t\t//\n\t\t\t// Consequence measured 2026-08-27 on one running instance, same\n\t\t\t// user, empty seen-state:\n\t\t\t//\n\t\t\t// opencatalogi welcome anchored to `Catalogs` -> `/` shows nothing,\n\t\t\t// `#/catalogi` shows \"1 / 8 Welcome to OpenCatalogi\"\n\t\t\t// pipelinq welcome anchored to `Products` -> `/` shows nothing,\n\t\t\t// `#/products` shows \"1 / 11 Welcome to Pipelinq\"\n\t\t\t// dossiq welcome anchored to `Dashboard` -> works (control)\n\t\t\t//\n\t\t\t// The tour was parked in `_pendingAutoTour` and opened only if the\n\t\t\t// user happened to navigate to that page — so the getting-started\n\t\t\t// tour never appeared on the landing page, silently, with nothing\n\t\t\t// logged. 19 of 20 fleet manifests declare a walkthrough, so this\n\t\t\t// is not two apps; it is however many anchored their welcome step\n\t\t\t// somewhere other than their landing route.\n\t\t\t//\n\t\t\t// ADR-062's intent is preserved exactly: a first step that really\n\t\t\t// does spotlight an element still gates on that element's route, so\n\t\t\t// a tour never pops over a deep-linked detail page.\n\t\t\tif (!first) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (first.placement === 'center') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst tgt = first.target\n\t\t\tif (tgt && (tgt.kind === 'page' || tgt.kind === 'nav-item') && tgt.ref) {\n\t\t\t\treturn String(tgt.ref)\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * Whether a tour may auto-open on the given route. True when the tour's\n\t\t * first step is not page-anchored — which includes every `center`-placed\n\t\t * welcome step, since it spotlights nothing (see firstStepPage) — so the\n\t\t * tour starts wherever the user lands. Otherwise the route name must\n\t\t * equal the first-step page.\n\t\t *\n\t\t * This is the ONLY consumer of firstStepPage, and both auto-start paths\n\t\t * (maybeAutoStart on mount, and the router afterEach that resumes a\n\t\t * deferred tour) go through here — so a centered step cannot be\n\t\t * swallowed by either.\n\t\t *\n\t\t * @param {object} tour The tour definition.\n\t\t * @param {string} [routeName] The current route name.\n\t\t * @return {boolean} True when the tour may open on this route.\n\t\t */\n\t\trouteMatchesTour(tour, routeName) {\n\t\t\tconst page = this.firstStepPage(tour)\n\t\t\tif (!page) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn !!routeName && String(routeName) === page\n\t\t},\n\n\t\t/**\n\t\t * Resolve, scroll to, and measure the active step's target, then install\n\t\t * the step's advance watcher.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tlocateTarget() {\n\t\t\tthis.teardownStep()\n\t\t\tif (!this.step || this.isCentered) {\n\t\t\t\tthis.rect = null\n\t\t\t\tthis.focusCard()\n\t\t\t\tthis.armDelay()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst el = this.resolveTarget(this.step)\n\t\t\t// A step's target belongs to the page it was first found on. The\n\t\t\t// same `index-add` button exists on every list page, and a step\n\t\t\t// left half-done on Products used to spotlight the Clients page's\n\t\t\t// button too, inviting a click that belonged to another step.\n\t\t\tconst kind = (this.step.target && this.step.target.kind) || ''\n\t\t\tconst routeName = this.$route ? this.$route.name : null\n\t\t\tif (el && kind !== 'page' && kind !== 'nav-item' && routeName) {\n\t\t\t\tif (this._homeRoute === null) {\n\t\t\t\t\tthis._homeRoute = routeName\n\t\t\t\t} else if (this._homeRoute !== routeName) {\n\t\t\t\t\tthis.offPage = true\n\t\t\t\t\tthis.targetEl = null\n\t\t\t\t\tthis.rect = null\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.offPage = false\n\t\t\tthis.targetEl = el\n\t\t\tif (!el) {\n\t\t\t\t// Optional step whose target is absent → skip; else wait for it.\n\t\t\t\tif (this.step.optional) {\n\t\t\t\t\tthis.wt.skip()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// Genuinely not in the DOM (nothing to reveal) — wait for it to\n\t\t\t\t// appear. A target hidden by a collapsed nav group is handled by\n\t\t\t\t// computeRect()'s zero-size check below instead, since `el` is\n\t\t\t\t// resolvable there and revealTarget() can walk up from it.\n\t\t\t\tthis.observeForTarget()\n\t\t\t\tthis.rect = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tel.scrollIntoView({ block: 'center', inline: 'center' })\n\t\t\t} catch { /* jsdom */ }\n\t\t\tthis.computeRect()\n\t\t\tthis.armStep(el)\n\t\t\t// The ResizeObserver only fires when the target itself resizes. A nav\n\t\t\t// item can MOVE (siblings render/settle after mount) without resizing,\n\t\t\t// leaving a stale cutout one row off. Re-measure across the next few\n\t\t\t// frames so the spotlight tracks the settled position, and keep\n\t\t\t// tracking it afterwards for content that arrives later still.\n\t\t\tthis.scheduleSettleRemeasure()\n\t\t\tthis.trackTargetLayout()\n\t\t},\n\n\t\t/**\n\t\t * Resolve a step's `target` to a DOM element, preferring stable manifest\n\t\t * identities, then `data-walkthrough-id`/`data-testid`, then raw CSS.\n\t\t *\n\t\t * @param {object} step The active step.\n\t\t * @return {Element|null} The resolved element.\n\t\t */\n\t\tresolveTarget(step) {\n\t\t\tconst tgt = step.target || {}\n\t\t\tconst ref = tgt.ref\n\t\t\tconst q = (sel) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn document.querySelector(sel)\n\t\t\t\t} catch {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst esc = (v) => (window.CSS && CSS.escape ? CSS.escape(v) : String(v).replace(/\"/g, '\\\\\"'))\n\t\t\tif (tgt.kind === 'selector' && tgt.selector) {\n\t\t\t\treturn q(tgt.selector)\n\t\t\t}\n\t\t\tif (!ref) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst byId = q(`[data-walkthrough-id=\"${esc(ref)}\"]`) || q(`[data-testid=\"${esc(ref)}\"]`)\n\t\t\tif (byId) {\n\t\t\t\treturn byId\n\t\t\t}\n\t\t\tif (tgt.kind === 'nav-item' || tgt.kind === 'page') {\n\t\t\t\treturn q(`[data-cn-route=\"${esc(ref)}\"]`) || q(`a[href$=\"#/${esc(ref)}\"]`) || q(`[data-route=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\tif (tgt.kind === 'widget') {\n\t\t\t\treturn q(`[data-widget-key=\"${esc(ref)}\"]`) || q(`[data-widget-id=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\tif (tgt.kind === 'action') {\n\t\t\t\treturn q(`[data-action-id=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * Expand only the collapsed nav groups that are actual ancestors of\n\t\t * `this.targetEl` (already resolved, just `display:none`'d by a\n\t\t * collapsed parent), instead of scanning the whole navigation.\n\t\t * Attempted at most once per step (`_revealAttempted`, reset in\n\t\t * teardownStep).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\trevealTarget() {\n\t\t\tif (this._revealAttempted || !this.targetEl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis._revealAttempted = true\n\t\t\tconst click = (btn) => {\n\t\t\t\tif (btn && typeof btn.click === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbtn.click()\n\t\t\t\t\t} catch { /* jsdom / detached */ }\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Walk every ancestor (not just .closest() jumps) since a nav group\n\t\t\t// and the Settings foldout use different container markup and can\n\t\t\t// nest either way.\n\t\t\tlet node = this.targetEl.parentElement\n\t\t\twhile (node) {\n\t\t\t\tif (node.classList.contains('app-navigation-entry--collapsible')) {\n\t\t\t\t\tclick(node.querySelector('button.icon-collapse, .app-navigation-entry__children-toggle, .app-navigation-entry__collapse'))\n\t\t\t\t} else if (node.matches?.('[data-testid=\"cn-nav-settings\"]')) {\n\t\t\t\t\t// NcAppNavigationSettings: CSS-module classes are hashed at\n\t\t\t\t\t// build time, so aria-expanded is the only stable handle on\n\t\t\t\t\t// its own toggle button.\n\t\t\t\t\tclick(node.querySelector('button[aria-expanded]'))\n\t\t\t\t}\n\t\t\t\tnode = node.parentElement\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Re-measure the target a few times after arming, to catch post-mount\n\t\t * layout settling (sibling nav items rendering, fonts, etc.) that moves\n\t\t * the target without resizing it. Cleared in teardownStep.\n\t\t *\n\t\t * Fast path only; later movement is caught by `trackTargetLayout()`.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tscheduleSettleRemeasure() {\n\t\t\tconst again = () => {\n\t\t\t\tif (this.targetEl && !this.isCentered) {\n\t\t\t\t\tthis.computeRect()\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._settleTimers = (this._settleTimers || [])\n\t\t\tthis._settleTimers.push(setTimeout(again, 100), setTimeout(again, 300), setTimeout(again, 600))\n\t\t},\n\n\t\t/**\n\t\t * Re-measure on any layout change for as long as the step is active.\n\t\t *\n\t\t * The settle timers above stop at 600ms; a target on a page whose content\n\t\t * arrives from the network keeps moving after that, leaving the cutout on\n\t\t * empty space. The MutationObserver catches inserted content, the\n\t\t * ResizeObserver catches reflows that insert no nodes.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttrackTargetLayout() {\n\t\t\tconst remeasure = () => {\n\t\t\t\tif (this._remeasureRaf) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst raf = typeof window.requestAnimationFrame === 'function'\n\t\t\t\t\t? window.requestAnimationFrame\n\t\t\t\t\t: (fn) => setTimeout(fn, 16)\n\t\t\t\tthis._remeasureRaf = raf(() => {\n\t\t\t\t\tthis._remeasureRaf = null\n\t\t\t\t\tif (this.targetEl && !this.isCentered) {\n\t\t\t\t\t\tthis.computeRect()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (window.MutationObserver) {\n\t\t\t\tthis._layoutObs = new MutationObserver(remeasure)\n\t\t\t\tthis._layoutObs.observe(document.body, { childList: true, subtree: true })\n\t\t\t}\n\t\t\tif (window.ResizeObserver) {\n\t\t\t\tthis._bodyResizeObs = new ResizeObserver(remeasure)\n\t\t\t\tthis._bodyResizeObs.observe(document.body)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Measure the target into a viewport rect.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcomputeRect() {\n\t\t\tif (!this.targetEl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst r = this.targetEl.getBoundingClientRect()\n\t\t\t// Target present but not laid out (e.g. a nav item inside a collapsed\n\t\t\t// group, or a hidden element): fall back to an anchorless centered\n\t\t\t// coachmark — no point-sized cutout — until it becomes visible. The\n\t\t\t// ResizeObserver armed in armStep() recomputes when it gains size.\n\t\t\tif (r.width === 0 || r.height === 0) {\n\t\t\t\t// Resolved but not laid out — most often a nav item inside a\n\t\t\t\t// collapsed group. Expand the group so the ResizeObserver armed in\n\t\t\t\t// armStep() recomputes once it gains size; until then, anchorless.\n\t\t\t\tthis.revealTarget()\n\t\t\t\tthis.rect = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// The overlay (.cn-walkthrough) is position:fixed, but a transformed\n\t\t\t// ancestor (NC content area) can become its containing block, so its\n\t\t\t// top-left is not the viewport origin. Convert the viewport rect into\n\t\t\t// the overlay's coordinate space so the dim/ring/card line up with the\n\t\t\t// real element instead of sitting a header's height too low.\n\t\t\tconst host = this.$el && this.$el.getBoundingClientRect ? this.$el.getBoundingClientRect() : { top: 0, left: 0 }\n\t\t\t// Remember the overlay's own viewport offset so placeCard can clamp\n\t\t\t// the coachmark against the REAL viewport even when a transformed\n\t\t\t// ancestor makes the position:fixed origin non-zero.\n\t\t\tthis._hostOffset = { top: host.top, left: host.left }\n\t\t\tconst next = { top: r.top - host.top, left: r.left - host.left, width: r.width, height: r.height }\n\t\t\t// Bail when nothing moved: trackTargetLayout() calls this on every\n\t\t\t// layout change, and placeCard()'s focusCard() would otherwise steal\n\t\t\t// focus back from whatever the user is typing in.\n\t\t\tconst p = this.rect\n\t\t\tif (p && p.top === next.top && p.left === next.left && p.width === next.width && p.height === next.height) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rect = next\n\t\t\tthis.$nextTick(() => this.placeCard())\n\t\t},\n\n\t\t/**\n\t\t * Position the coachmark near the target with a simple flip so it stays\n\t\t * on-screen.\n\t\t *\n\t\t * @spec openspec/changes/cn-walkthrough-engine/specs/cn-walkthrough/spec.md\n\t\t * @return {void}\n\t\t */\n\t\tplaceCard() {\n\t\t\tconst card = this.$refs.card\n\t\t\tif (!card || !this.rect) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst cw = card.offsetWidth || 320\n\t\t\tconst ch = card.offsetHeight || 160\n\t\t\tconst gap = 12\n\t\t\tconst vw = window.innerWidth\n\t\t\tconst vh = window.innerHeight\n\t\t\tconst r = this.rect\n\t\t\tlet placement = this.step.placement && this.step.placement !== 'auto' ? this.step.placement : 'bottom'\n\t\t\tlet top\n\t\t\tlet left\n\t\t\tif (placement === 'bottom' && r.top + r.height + gap + ch > vh) {\n\t\t\t\tplacement = 'top'\n\t\t\t}\n\t\t\tif (placement === 'top' && r.top - gap - ch < 0) {\n\t\t\t\tplacement = 'bottom'\n\t\t\t}\n\t\t\tif (placement === 'bottom') {\n\t\t\t\ttop = r.top + r.height + gap\n\t\t\t\tleft = r.left\n\t\t\t} else if (placement === 'top') {\n\t\t\t\ttop = r.top - gap - ch\n\t\t\t\tleft = r.left\n\t\t\t} else if (placement === 'left') {\n\t\t\t\ttop = r.top\n\t\t\t\tleft = r.left - gap - cw\n\t\t\t} else {\n\t\t\t\ttop = r.top\n\t\t\t\tleft = r.left + r.width + gap\n\t\t\t}\n\t\t\t// Clamp to the viewport. `left`/`top` live in overlay-relative space\n\t\t\t// (this.rect was host-subtracted), so the viewport bounds must be\n\t\t\t// host-subtracted too — otherwise a scrolled/transformed host would\n\t\t\t// let the card render off-screen (ADR-062: the coachmark is always\n\t\t\t// fully on-screen).\n\t\t\tconst ho = this._hostOffset || { top: 0, left: 0 }\n\t\t\tleft = Math.max(gap - ho.left, Math.min(left, vw - cw - gap - ho.left))\n\t\t\ttop = Math.max(gap - ho.top, Math.min(top, vh - ch - gap - ho.top))\n\t\t\tthis.cardPlacement = placement\n\t\t\tthis.cardPos = { top, left }\n\t\t\tthis.focusCard()\n\t\t},\n\n\t\t/**\n\t\t * Move focus to the coachmark's first control (focus management / a11y).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tfocusCard() {\n\t\t\t// Never take focus from an open app dialog.\n\t\t\tif (this.appModalOpen) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tconst btn = this.$refs.firstBtn && this.$refs.firstBtn.$el\n\t\t\t\tif (btn && typeof btn.focus === 'function') {\n\t\t\t\t\tbtn.focus()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Install the advance watcher for the active step (click / element / delay;\n\t\t * route + object-created are global listeners installed at mount).\n\t\t *\n\t\t * @param {Element} el The resolved target.\n\t\t * @return {void}\n\t\t */\n\t\tarmStep(el) {\n\t\t\tconst a = this.step.advanceOn || {}\n\t\t\tif (a.type === 'click-target' && el) {\n\t\t\t\tthis._clickHandler = () => this.wt.notify({ kind: 'click' })\n\t\t\t\tel.addEventListener('click', this._clickHandler, { once: true })\n\t\t\t}\n\t\t\t// Recompute the cutout when the target's size changes — covers a target\n\t\t\t// that starts hidden/zero-size (e.g. inside a collapsed nav group) and\n\t\t\t// becomes visible while the step is active.\n\t\t\tif (el && window.ResizeObserver) {\n\t\t\t\tthis._resizeObs = new ResizeObserver(() => this.computeRect())\n\t\t\t\tthis._resizeObs.observe(el)\n\t\t\t}\n\t\t\tthis.armDelay()\n\t\t},\n\n\t\t/**\n\t\t * Arm the delay timer for a `delay` advance.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tarmDelay() {\n\t\t\tconst a = (this.step && this.step.advanceOn) || {}\n\t\t\tif (a.type === 'delay') {\n\t\t\t\tthis._delayTimer = setTimeout(() => this.wt.notify({ kind: 'delay' }), a.ms || 1500)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Watch the DOM until an `element-appears` (or a not-yet-mounted) target\n\t\t * resolves, then re-locate.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tobserveForTarget() {\n\t\t\tthis._observer = new MutationObserver(() => {\n\t\t\t\tconst el = this.resolveTarget(this.step)\n\t\t\t\tif (el) {\n\t\t\t\t\tthis._observer.disconnect()\n\t\t\t\t\tthis._observer = null\n\t\t\t\t\tif (this.step.advanceOn && this.step.advanceOn.type === 'element-appears') {\n\t\t\t\t\t\tthis.wt.notify({ kind: 'element' })\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.locateTarget()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t\tthis._observer.observe(document.body, { childList: true, subtree: true })\n\t\t},\n\n\t\t/**\n\t\t * Tear down the current step's watchers/timers/listeners.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tteardownStep() {\n\t\t\tthis._revealAttempted = false\n\t\t\t// computeRect()'s bail-when-unchanged optimisation must not survive\n\t\t\t// into the next step, or a same-geometry target never re-runs\n\t\t\t// placeCard()/focusCard() on step change.\n\t\t\tthis.rect = null\n\t\t\tif (this._observer) {\n\t\t\t\tthis._observer.disconnect()\n\t\t\t\tthis._observer = null\n\t\t\t}\n\t\t\tif (this._resizeObs) {\n\t\t\t\tthis._resizeObs.disconnect()\n\t\t\t\tthis._resizeObs = null\n\t\t\t}\n\t\t\tif (this._layoutObs) {\n\t\t\t\tthis._layoutObs.disconnect()\n\t\t\t\tthis._layoutObs = null\n\t\t\t}\n\t\t\tif (this._bodyResizeObs) {\n\t\t\t\tthis._bodyResizeObs.disconnect()\n\t\t\t\tthis._bodyResizeObs = null\n\t\t\t}\n\t\t\tif (this._remeasureRaf) {\n\t\t\t\tconst cancel = typeof window.cancelAnimationFrame === 'function' ? window.cancelAnimationFrame : clearTimeout\n\t\t\t\tcancel(this._remeasureRaf)\n\t\t\t\tthis._remeasureRaf = null\n\t\t\t}\n\t\t\tif (this._delayTimer) {\n\t\t\t\tclearTimeout(this._delayTimer)\n\t\t\t\tthis._delayTimer = null\n\t\t\t}\n\t\t\tif (this._settleTimers) {\n\t\t\t\tthis._settleTimers.forEach(clearTimeout)\n\t\t\t\tthis._settleTimers = null\n\t\t\t}\n\t\t\tif (this.targetEl && this._clickHandler) {\n\t\t\t\tthis.targetEl.removeEventListener('click', this._clickHandler)\n\t\t\t\tthis._clickHandler = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Advance the tour one step, completing on the last step.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tadvance() {\n\t\t\t/**\n\t\t\t * @event advance Emitted when the user advances the tour.\n\t\t\t * @type {{ stepId: string }}\n\t\t\t */\n\t\t\tthis.$emit('advance', { stepId: this.step && this.step.id })\n\t\t\tconst wasLast = this.isLast\n\t\t\tthis.wt.next()\n\t\t\tif (wasLast) {\n\t\t\t\t/**\n\t\t\t\t * @event complete Emitted when the last step is passed.\n\t\t\t\t */\n\t\t\t\tthis.$emit('complete')\n\t\t\t}\n\t\t},\n\n\t\tback() {\n\t\t\tthis.wt.back()\n\t\t},\n\n\t\t/**\n\t\t * The coachmark's \"Skip\" control — ENDS the tour (it does not advance a\n\t\t * step). Marks the tour complete so the seen-version is persisted and it\n\t\t * won't auto-reopen, then emits `dismiss` + `complete` (ADR-062: Skip\n\t\t * dismisses the whole tour; Next is the only control that advances).\n\t\t * The internal per-step skip (an optional step whose target is absent)\n\t\t * still uses `wt.skip()` directly in `locateTarget`.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tskip() {\n\t\t\tthis.wt.complete()\n\t\t\t/**\n\t\t\t * @event dismiss Emitted when the user skips (ends) the tour.\n\t\t\t */\n\t\t\tthis.$emit('dismiss')\n\t\t\tthis.$emit('complete')\n\t\t},\n\n\t\t/**\n\t\t * Execute a cross-app hand-off: complete this tour locally, then navigate\n\t\t * the browser to the destination URL carrying a `cn_resume_tour` /\n\t\t * `cn_resume_step` token so the destination app's useWalkthrough resumes.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tdoHandoff() {\n\t\t\tconst h = (this.step && this.step.handoff) || {}\n\t\t\tif (!h.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst tour = h.tour || (this.wt.activeTour.value && this.wt.activeTour.value.id) || ''\n\t\t\tlet url = h.url + (h.url.indexOf('?') === -1 ? '?' : '&') + 'cn_resume_tour=' + encodeURIComponent(tour)\n\t\t\tif (h.step) {\n\t\t\t\turl += '&cn_resume_step=' + encodeURIComponent(h.step)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event handoff Emitted just before navigating to a cross-app destination.\n\t\t\t * @type {{ app: string, url: string }}\n\t\t\t */\n\t\t\tthis.$emit('handoff', { app: h.app, url })\n\t\t\tthis.$emit('complete')\n\t\t\tthis.wt.complete()\n\t\t\ttry {\n\t\t\t\twindow.location.href = url\n\t\t\t} catch { /* jsdom */ }\n\t\t},\n\n\t\t/**\n\t\t * The corner close button PAUSES the tour, like ESC and the dim. It\n\t\t * used to complete it, and the host then wiped the saved step, so a\n\t\t * restart began at step 1 again. Only Finish on the last step (and a\n\t\t * host-supplied Skip) completes the tour; \"Start over\" in the user\n\t\t * settings stays the way back to step 1.\n\t\t *\n\t\t * @spec openspec/changes/audit-round-lib-fixes/specs/cn-walkthrough/spec.md\n\t\t * @return {void}\n\t\t */\n\t\tclose() {\n\t\t\tthis.onBackdrop()\n\t\t},\n\n\t\t/**\n\t\t * Pause the tour from the corner X, a backdrop click or ESC. The tour\n\t\t * hides and keeps its step; only Finish (or a host Skip) ends it.\n\t\t * Pressing ESC used to dismiss it, and the host recorded that as\n\t\t * \"seen\", so the tour never came back. The `pause` event carries\n\t\t * where the user was, so the host can keep a paused tour hidden on\n\t\t * the next page load.\n\t\t *\n\t\t * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md\n\t\t * @spec openspec/changes/audit-round-lib-fixes/specs/cn-walkthrough/spec.md\n\t\t * @return {void}\n\t\t */\n\t\tonBackdrop() {\n\t\t\tconst where = {\n\t\t\t\ttourId: (this.wt.activeTour.value && this.wt.activeTour.value.id) || '',\n\t\t\t\tstepId: (this.step && this.step.id) || '',\n\t\t\t\tindex: this.index,\n\t\t\t}\n\t\t\tthis.wt.pause()\n\t\t\t/**\n\t\t\t * @event pause Emitted when the user hides the tour (X, backdrop, ESC). The tour can continue later.\n\t\t\t * @type {{ tourId: string, stepId: string, index: number }}\n\t\t\t */\n\t\t\tthis.$emit('pause', where)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-walkthrough {\n\tposition: fixed;\n\tinset: 0;\n\tpointer-events: none;\n}\n\n.cn-walkthrough__live {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n}\n\n.cn-walkthrough__dim {\n\tposition: fixed;\n\tbackground: var(--color-modal-background, rgba(0, 0, 0, 0.5));\n\tpointer-events: auto;\n}\n\n.cn-walkthrough__dim--full {\n\tinset: 0;\n}\n\n/* Anchorless fallback for an ANCHORED step: dim everything, but do not\n intercept pointer events — the step's task requires clicking the app\n underneath (see the template comment). */\n/* Lets the step's own task through — and therefore cannot be clicked to\n dismiss; ESC, the close button and Back/Finish remain (see the template). */\n.cn-walkthrough__dim--passthrough {\n\tpointer-events: none;\n}\n\n.cn-walkthrough__ring {\n\tposition: fixed;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 0 0 2px var(--color-primary-element, #0082c9);\n\tpointer-events: none;\n}\n\n.cn-walkthrough__card {\n\tposition: fixed;\n\twidth: 320px;\n\tmax-width: calc(100vw - 24px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 2px 12px var(--color-box-shadow, rgba(0, 0, 0, 0.3));\n\tpadding: 16px;\n\tpointer-events: auto;\n}\n\n.cn-walkthrough__card--docked {\n\tinset-block-end: 16px;\n\tinset-inline-start: 16px;\n}\n\n.cn-walkthrough__close {\n\t/* !important beats NcButton's own `position: relative`, which would otherwise\n\t leave the close button in-flow at the card's top-left. */\n\tposition: absolute !important;\n\ttop: 8px;\n\tinset-inline-end: 8px;\n\tinset-inline-start: auto;\n\tz-index: 1;\n}\n\n.cn-walkthrough__counter {\n\tfont-size: 0.8125rem;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-bottom: 4px;\n\t/* Keep the counter/title clear of the absolutely-positioned close button. */\n\tpadding-inline-end: 32px;\n}\n\n.cn-walkthrough__title {\n\tmargin: 0 0 6px;\n\tfont-size: 1.1rem;\n}\n\n.cn-walkthrough__body {\n\tmargin: 0 0 8px;\n}\n\n.cn-walkthrough__task {\n\tmargin: 0 0 8px;\n\tpadding: 8px;\n\tbackground: var(--color-primary-element-light, var(--color-background-hover));\n\tborder-radius: var(--border-radius, 4px);\n\tfont-weight: 600;\n}\n\n.cn-walkthrough__task-icon {\n\tmargin-inline-end: 4px;\n}\n\n.cn-walkthrough__actions {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin-top: 8px;\n}\n\n.cn-walkthrough__spacer {\n\tflex: 1 1 auto;\n}\n</style>\n"],"names":["_createElementBlock","_createElementVNode","_toDisplayString","_Fragment","_openBlock","_normalizeStyle","_normalizeClass","_createVNode","_createTextVNode","_createBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnWalkthrough.vue3.js","sources":["../../../../src/components/CnWalkthrough/CnWalkthrough.vue?vue&type=template&id=34991bbf&lang.js"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<div v-if=\"active\"\n\t\tclass=\"cn-walkthrough\"\n\t\t:style=\"{ zIndex }\"\n\t\trole=\"dialog\"\n\t\taria-modal=\"true\"\n\t\t:aria-label=\"dialogLabel\">\n\t\t<!-- aria-live announcement of the current step -->\n\t\t<div class=\"cn-walkthrough__live\" aria-live=\"polite\">\n\t\t\t{{ liveText }}\n\t\t</div>\n\n\t\t<!-- Dimmer: a centered step uses one full overlay; an anchored step uses\n\t\t four strips that frame the target rect, leaving a real interactive hole.\n\t\t An ANCHORED step that lost its anchor (target present but not laid\n\t\t out, e.g. inside a closed settings foldout) falls back to the full\n\t\t overlay too — but there it must let clicks THROUGH: the step's own\n\t\t task tells the user to click something in the app underneath, and an\n\t\t interactive backdrop would swallow exactly that click, leaving only\n\t\t Back/Finish usable. The trade-off is deliberate: a passthrough dim\n\t\t cannot be clicked to dismiss the tour, so on such a step the exits\n\t\t are ESC, the coachmark's close button and Back/Finish — a click on\n\t\t the greyed-out area reaches the app, which is the point. A centered\n\t\t step spotlights nothing, so its backdrop stays interactive and\n\t\t dismisses the tour. -->\n\t\t<!-- Quiet: an app dialog is open, or the step's target belongs to\n\t\t another page. No dim and no cutout, so the overlay never sits on\n\t\t top of the dialog and never invites a click on the wrong page.\n\t\t Only the docked coachmark stays. -->\n\t\t<template v-if=\"quiet\" />\n\t\t<template v-else-if=\"isCentered || !rect\">\n\t\t\t<div v-if=\"isCentered\"\n\t\t\t\tclass=\"cn-walkthrough__dim cn-walkthrough__dim--full\"\n\t\t\t\t@click.self=\"onBackdrop\" />\n\t\t\t<!-- No dismiss handler on a passthrough dim: `pointer-events: none`\n\t\t\t means a browser never delivers the click here anyway, and\n\t\t\t leaving the listener attached would make the SAME gesture\n\t\t\t dismiss the tour in any environment that ignores\n\t\t\t pointer-events (jsdom, a forced-colors mode) while reaching the\n\t\t\t app in a browser. One behaviour, not two. -->\n\t\t\t<div v-else\n\t\t\t\tclass=\"cn-walkthrough__dim cn-walkthrough__dim--full cn-walkthrough__dim--passthrough\"\n\t\t\t\taria-hidden=\"true\" />\n\t\t</template>\n\t\t<template v-else>\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.top\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.bottom\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.left\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.right\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__ring\" :style=\"ringStyle\" aria-hidden=\"true\" />\n\t\t</template>\n\n\t\t<!-- Coachmark card -->\n\t\t<div ref=\"card\"\n\t\t\tclass=\"cn-walkthrough__card\"\n\t\t\t:class=\"[`cn-walkthrough__card--${cardPlacement}`, { 'cn-walkthrough__card--docked': quiet }]\"\n\t\t\t:style=\"cardStyle\">\n\t\t\t<NcButton class=\"cn-walkthrough__close\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:aria-label=\"closeLabel\"\n\t\t\t\t:title=\"closeLabel\"\n\t\t\t\t@click=\"close\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Close :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t</NcButton>\n\t\t\t<!-- @slot coachmark Override the whole coachmark body. Scope: { step, index, total, next, back, skip, skipLabel }.\n\t\t\t The default body has no Skip control (ADR-062 gives that job to\n\t\t\t the corner close button), so `skipLabel` reaches the only place\n\t\t\t a Skip control can live: a host-supplied coachmark. -->\n\t\t\t<slot name=\"coachmark\"\n\t\t\t\t:step=\"step\"\n\t\t\t\t:index=\"index\"\n\t\t\t\t:total=\"total\"\n\t\t\t\t:next=\"advance\"\n\t\t\t\t:back=\"back\"\n\t\t\t\t:skip=\"skip\"\n\t\t\t\t:skipLabel=\"skipLabel\">\n\t\t\t\t<div class=\"cn-walkthrough__counter\">\n\t\t\t\t\t{{ index + 1 }} / {{ total }}\n\t\t\t\t</div>\n\t\t\t\t<h3 v-if=\"stepTitle\" class=\"cn-walkthrough__title\">\n\t\t\t\t\t{{ stepTitle }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"stepBody\" class=\"cn-walkthrough__body\">\n\t\t\t\t\t{{ stepBody }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"stepTask\" class=\"cn-walkthrough__task\">\n\t\t\t\t\t<span class=\"cn-walkthrough__task-icon\" aria-hidden=\"true\">👉</span> {{ stepTask }}\n\t\t\t\t</p>\n\t\t\t\t<div class=\"cn-walkthrough__actions\">\n\t\t\t\t\t<NcButton v-if=\"!isFirst\" variant=\"secondary\" @click=\"back\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronLeft :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ backLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<span class=\"cn-walkthrough__spacer\" />\n\t\t\t\t\t<NcButton v-if=\"isHandoff\"\n\t\t\t\t\t\tref=\"firstBtn\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\talignment=\"center-reverse\"\n\t\t\t\t\t\t@click=\"doHandoff\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ handoffLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton v-else-if=\"showNext\"\n\t\t\t\t\t\tref=\"firstBtn\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\talignment=\"center-reverse\"\n\t\t\t\t\t\t@click=\"advance\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ isLast ? finishLabel : nextLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</slot>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport ChevronLeft from 'vue-material-design-icons/ChevronLeft.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport { useWalkthrough } from '../../composables/useWalkthrough.js'\n\n/**\n * CnWalkthrough — abstract, manifest-driven product walkthrough (ADR-043).\n *\n * Renders a `manifest.walkthrough` tour as a gray dimmer with a spotlight cutout\n * around one real, interactive element, plus an auto-positioned coachmark card.\n * It never promotes the target's `z-index` — the dimmer is four strips framing\n * the target rect, so the element stays clickable in place regardless of its\n * stacking context. When a target is hidden inside a collapsed `NcAppNavigation`\n * group (children rendered but `display:none`, so unmeasurable), the engine\n * best-effort expands the group to reveal the item before spotlighting it, rather\n * than falling back to a centered coachmark. Step advancement is declarative: the component sources\n * signals (route change via `$router`, a `cn-walkthrough:object-created` window\n * event, an appearing element via `MutationObserver`, a click on the target, or a\n * delay) and feeds them to {@link useWalkthrough}, which captures route params /\n * object ids into a context bag interpolated into later steps.\n *\n * Mount it gated by `CnAppRoot` over the live shell, or standalone with an\n * explicit `tour-id`.\n *\n * ```vue\n * <CnWalkthrough :app-id=\"'pipelinq'\" :manifest=\"manifest\" :seen-version=\"seen\"\n * @complete=\"onComplete\" />\n * ```\n */\nexport default {\n\tname: 'CnWalkthrough',\n\n\tcomponents: { NcButton, Close, ChevronLeft, ChevronRight },\n\n\tprops: {\n\t\t/** The Nextcloud app id (walkthrough machine cache key). */\n\t\tappId: { type: String, required: true },\n\t\t/** The app manifest; `manifest.walkthrough` + `manifest.version` are read. */\n\t\tmanifest: { type: Object, default: null },\n\t\t/** The user's last-seen app version (drives \"what's new\" composition). */\n\t\tseenVersion: { type: String, default: '' },\n\t\t/** Force a specific tour id; when empty the auto-start tour is used. */\n\t\ttourId: { type: String, default: '' },\n\t\t/** Optional resume token `{ tourId, stepId }` (refresh / cross-app hand-off). */\n\t\tresume: { type: Object, default: null },\n\t\t/**\n\t\t * Whether a first-visit tour may start on its own. The host turns it\n\t\t * off while the user's tour is paused, so a paused tour stays hidden\n\t\t * across page loads until the user picks \"Continue\".\n\t\t */\n\t\tautoStart: { type: Boolean, default: true },\n\t\t/** Stacking order of the overlay. */\n\t\tzIndex: { type: Number, default: 10000 },\n\t\t/** Next button label. */\n\t\tnextLabel: { type: String, default: () => t('nextcloud-vue', 'Next') },\n\t\t/** Back button label. */\n\t\tbackLabel: { type: String, default: () => t('nextcloud-vue', 'Back') },\n\t\t/** Skip button label. */\n\t\tskipLabel: { type: String, default: () => t('nextcloud-vue', 'Skip') },\n\t\t/** Final-step button label. */\n\t\tfinishLabel: { type: String, default: () => t('nextcloud-vue', 'Finish') },\n\t\t/** Accessible label for the corner close button that ends the tour. */\n\t\tcloseLabel: { type: String, default: () => t('nextcloud-vue', 'Close tour') },\n\t\t/** Optional translate function applied to step title/body/task i18n keys. */\n\t\ttranslate: { type: Function, default: null },\n\t},\n\n\temits: ['complete', 'dismiss', 'pause', 'progress', 'step-change', 'advance', 'handoff'],\n\n\tsetup(props) {\n\t\tconst wt = useWalkthrough(props.appId, props.manifest, {\n\t\t\tseenVersion: props.seenVersion,\n\t\t\tresume: props.resume,\n\t\t\tonComplete: undefined,\n\t\t})\n\t\treturn { wt }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trect: null, // target bounding rect (viewport coords)\n\t\t\tcardPos: { top: 0, left: 0 },\n\t\t\tcardPlacement: 'bottom',\n\t\t\ttargetEl: null,\n\t\t\t// An app dialog (NcModal / NcDialog) is open over the page.\n\t\t\tappModalOpen: false,\n\t\t\t// The step's target was found on another page than the current one.\n\t\t\toffPage: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tactive() {\n\t\t\treturn this.wt.running.value && !!this.step\n\t\t},\n\n\t\tstep() {\n\t\t\treturn this.wt.currentStep.value\n\t\t},\n\n\t\tindex() {\n\t\t\treturn this.wt.activeTour.value ? this.wt.activeTour.value.steps.findIndex((s) => s.id === (this.step && this.step.id)) : 0\n\t\t},\n\n\t\ttotal() {\n\t\t\treturn this.wt.totalSteps.value\n\t\t},\n\n\t\tisFirst() {\n\t\t\treturn this.wt.isFirst.value\n\t\t},\n\n\t\tisLast() {\n\t\t\treturn this.wt.isLast.value\n\t\t},\n\n\t\t/**\n\t\t * Whether the overlay steps back: no dim, no cutout, a docked card.\n\t\t * True while an app dialog is open or the step's target is on\n\t\t * another page.\n\t\t *\n\t\t * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md\n\t\t * @return {boolean}\n\t\t */\n\t\tquiet() {\n\t\t\treturn this.appModalOpen || this.offPage\n\t\t},\n\n\t\tisCentered() {\n\t\t\treturn this.step && this.step.placement === 'center'\n\t\t},\n\n\t\t/**\n\t\t * Whether the Next control is shown. Hidden on an enforced action step\n\t\t * (a task + a non-manual advanceOn) unless `allowManualNext` is set.\n\t\t *\n\t\t * @return {boolean} True when Next should render.\n\t\t */\n\t\tshowNext() {\n\t\t\tif (!this.step) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst enforced = !!this.step.task && this.step.advanceOn && this.step.advanceOn.type !== 'manual'\n\t\t\treturn !enforced || this.step.allowManualNext === true\n\t\t},\n\n\t\t/**\n\t\t * Whether the active step is a cross-app hand-off (declares `handoff.url`).\n\t\t *\n\t\t * @return {boolean} True when the coachmark offers \"Continue in {app}\".\n\t\t */\n\t\tisHandoff() {\n\t\t\treturn !!(this.step && this.step.handoff && this.step.handoff.url)\n\t\t},\n\n\t\thandoffLabel() {\n\t\t\tconst app = this.step && this.step.handoff && this.step.handoff.app\n\t\t\treturn app ? t('nextcloud-vue', 'Continue in {app}', { app }) : t('nextcloud-vue', 'Continue')\n\t\t},\n\n\t\tstepTitle() {\n\t\t\treturn this.tr(this.step && this.step.title)\n\t\t},\n\n\t\tstepBody() {\n\t\t\treturn this.tr(this.step && this.step.body)\n\t\t},\n\n\t\t/**\n\t\t * The task line, through the same `translate` prop as title/body — a\n\t\t * raw `step.task` shipped English even in fully translated locales.\n\t\t *\n\t\t * @return {string} The translated task, or `''` when the step has none.\n\t\t */\n\t\tstepTask() {\n\t\t\treturn this.tr(this.step && this.step.task)\n\t\t},\n\n\t\tdialogLabel() {\n\t\t\treturn this.stepTitle || t('nextcloud-vue', 'Walkthrough')\n\t\t},\n\n\t\tliveText() {\n\t\t\tif (!this.step) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Step {n} of {total}', { n: this.index + 1, total: this.total }) + ': ' + (this.stepTitle || '')\n\t\t},\n\n\t\tringStyle() {\n\t\t\tif (!this.rect) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst pad = 6\n\t\t\treturn {\n\t\t\t\ttop: (this.rect.top - pad) + 'px',\n\t\t\t\tleft: (this.rect.left - pad) + 'px',\n\t\t\t\twidth: (this.rect.width + pad * 2) + 'px',\n\t\t\t\theight: (this.rect.height + pad * 2) + 'px',\n\t\t\t}\n\t\t},\n\n\t\tstrip() {\n\t\t\tconst r = this.rect\n\t\t\tif (!r) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst pad = 6\n\t\t\tconst top = Math.max(0, r.top - pad)\n\t\t\tconst bottom = r.top + r.height + pad\n\t\t\tconst left = Math.max(0, r.left - pad)\n\t\t\tconst right = r.left + r.width + pad\n\t\t\treturn {\n\t\t\t\ttop: { top: 0, left: 0, width: '100%', height: top + 'px' },\n\t\t\t\tbottom: { top: bottom + 'px', left: 0, width: '100%', bottom: 0 },\n\t\t\t\tleft: { top: top + 'px', left: 0, width: left + 'px', height: (bottom - top) + 'px' },\n\t\t\t\tright: { top: top + 'px', left: right + 'px', right: 0, height: (bottom - top) + 'px' },\n\t\t\t}\n\t\t},\n\n\t\tcardStyle() {\n\t\t\tif (this.quiet) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tif (this.isCentered || !this.rect) {\n\t\t\t\treturn { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }\n\t\t\t}\n\t\t\treturn { top: this.cardPos.top + 'px', left: this.cardPos.left + 'px' }\n\t\t},\n\t},\n\n\twatch: {\n\t\tstep(newStep, oldStep) {\n\t\t\tif (!newStep) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!oldStep || newStep.id !== oldStep.id) {\n\t\t\t\t/**\n\t\t\t\t * @event step-change Emitted when the active step changes.\n\t\t\t\t * @type {{ stepId: string, index: number }}\n\t\t\t\t */\n\t\t\t\tthis.$emit('step-change', { stepId: newStep.id, index: this.index })\n\t\t\t\t/**\n\t\t\t\t * @event progress Emitted on every step change so the host can remember where the user is.\n\t\t\t\t * @type {{ tourId: string, stepId: string, index: number }}\n\t\t\t\t */\n\t\t\t\tthis.$emit('progress', {\n\t\t\t\t\ttourId: (this.wt.activeTour.value && this.wt.activeTour.value.id) || '',\n\t\t\t\t\tstepId: newStep.id,\n\t\t\t\t\tindex: this.index,\n\t\t\t\t})\n\t\t\t\t// A new step finds its own page again.\n\t\t\t\tthis._homeRoute = null\n\t\t\t\tthis.offPage = false\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t}\n\t\t},\n\n\t\tactive(now) {\n\t\t\tif (now) {\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t} else {\n\t\t\t\tthis.teardownStep()\n\t\t\t}\n\t\t},\n\t},\n\n\t/**\n\t * Initialise non-reactive instance state (event handler refs, DOM observer\n\t * handles). These use the `_` prefix convention and are set here rather than\n\t * in `data()` to avoid Vue's reserved-key warning for `_`-prefixed fields.\n\t *\n\t * @spec openspec/changes/cn-walkthrough-engine/specs/cn-walkthrough/spec.md\n\t */\n\tcreated() {\n\t\t// Non-reactive instance state: event handlers and DOM watchers.\n\t\t// Not in data() to avoid triggering Vue's reserved-key warning (_prefix).\n\t\tthis._revealAttempted = false\n\t\tthis._observer = null\n\t\tthis._resizeObs = null\n\t\tthis._delayTimer = null\n\t\tthis._onScroll = null\n\t\tthis._onKey = null\n\t\tthis._onObjectCreated = null\n\t\tthis._routeUnhook = null\n\t\t// Layout watchers for the ACTIVE step's target (see trackTargetLayout).\n\t\tthis._layoutObs = null\n\t\tthis._bodyResizeObs = null\n\t\tthis._remeasureRaf = null\n\t\t// A qualifying auto-start tour whose first-step page is NOT the current\n\t\t// route — held here until the user navigates to that page (see hookRouter).\n\t\tthis._pendingAutoTour = null\n\t\t// The route the active step's target was first found on (see locateTarget).\n\t\tthis._homeRoute = null\n\t\tthis._modalObs = null\n\t},\n\n\tmounted() {\n\t\t// Auto-start a qualifying tour unless one is already running.\n\t\tthis.maybeAutoStart()\n\t\tthis._onScroll = () => this.computeRect()\n\t\twindow.addEventListener('scroll', this._onScroll, true)\n\t\twindow.addEventListener('resize', this._onScroll)\n\t\tthis._onKey = (e) => {\n\t\t\tif (e.key !== 'Escape' || !this.active) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// ESC while an app dialog is open belongs to that dialog. It used\n\t\t\t// to dismiss the tour too, so closing a create form ended it.\n\t\t\tif (this.isAppModalOpen()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onBackdrop()\n\t\t}\n\t\twindow.addEventListener('keydown', this._onKey)\n\t\tthis._onObjectCreated = (e) => {\n\t\t\tconst d = (e && e.detail) || {}\n\t\t\tthis.wt.notify({\n\t\t\t\tkind: 'object-created',\n\t\t\t\tregister: d.register,\n\t\t\t\tschema: d.schema,\n\t\t\t\tobject: d.object && typeof d.object === 'object' ? d.object : d,\n\t\t\t})\n\t\t}\n\t\tthis.watchAppModals()\n\t\twindow.addEventListener('cn-walkthrough:object-created', this._onObjectCreated)\n\t\tthis.hookRouter()\n\t\tif (this.active) {\n\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\twindow.removeEventListener('scroll', this._onScroll, true)\n\t\twindow.removeEventListener('resize', this._onScroll)\n\t\twindow.removeEventListener('keydown', this._onKey)\n\t\twindow.removeEventListener('cn-walkthrough:object-created', this._onObjectCreated)\n\t\tif (this._modalObs) {\n\t\t\tthis._modalObs.disconnect()\n\t\t\tthis._modalObs = null\n\t\t}\n\t\tif (typeof this._routeUnhook === 'function') {\n\t\t\tthis._routeUnhook()\n\t\t}\n\t\tthis.teardownStep()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether an app dialog (NcModal / NcDialog, or any other modal\n\t\t * dialog that is not this overlay) is open.\n\t\t *\n\t\t * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md\n\t\t * @return {boolean}\n\t\t */\n\t\tisAppModalOpen() {\n\t\t\tif (typeof document === 'undefined') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst own = this.$el\n\t\t\tconst nodes = document.querySelectorAll('.modal-mask, [role=\"dialog\"][aria-modal=\"true\"]')\n\t\t\tfor (const node of nodes) {\n\t\t\t\tif (own && typeof own.contains === 'function' && (own === node || own.contains(node))) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\n\t\t/**\n\t\t * Follow app dialogs opening and closing, so the overlay steps back\n\t\t * while one is open and comes back after.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\twatchAppModals() {\n\t\t\tconst update = () => {\n\t\t\t\tconst open = this.isAppModalOpen()\n\t\t\t\tif (open !== this.appModalOpen) {\n\t\t\t\t\tthis.appModalOpen = open\n\t\t\t\t\tif (!open && this.active) {\n\t\t\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tupdate()\n\t\t\tif (typeof window !== 'undefined' && window.MutationObserver) {\n\t\t\t\tthis._modalObs = new MutationObserver(update)\n\t\t\t\tthis._modalObs.observe(document.body, { childList: true, subtree: true })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Translate an i18n key/text via the `translate` prop when given.\n\t\t *\n\t\t * @param {string} value The key or literal.\n\t\t * @return {string} The resolved string.\n\t\t */\n\t\ttr(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.translate === 'function' ? this.translate(value) : value\n\t\t},\n\n\t\t/**\n\t\t * Hook `$router.afterEach` (when a router is present) to feed route signals.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\thookRouter() {\n\t\t\tconst router = this.$router\n\t\t\tif (!router || typeof router.afterEach !== 'function') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis._routeUnhook = router.afterEach((to) => {\n\t\t\t\tthis.wt.notify({ kind: 'route', route: to.name, params: to.params || {} })\n\t\t\t\t// A first-visit tour deferred because the user deep-linked onto a\n\t\t\t\t// different page starts once they reach its first-step page.\n\t\t\t\tif (this._pendingAutoTour && !this.wt.running.value\n\t\t\t\t\t&& this.routeMatchesTour(this._pendingAutoTour, to.name)) {\n\t\t\t\t\tconst tour = this._pendingAutoTour\n\t\t\t\t\tthis._pendingAutoTour = null\n\t\t\t\t\tthis.wt.start(tour.id)\n\t\t\t\t}\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Start a qualifying auto-start tour — but a `first-visit` tour whose\n\t\t * first step SPOTLIGHTS something on a specific page only opens when the\n\t\t * user is ON that page. A centered welcome step spotlights nothing and\n\t\t * therefore starts anywhere. Deep-linking onto an unrelated route (e.g. a shared detail-page\n\t\t * URL) defers the tour via `_pendingAutoTour` instead of popping it over\n\t\t * the wrong screen (ADR-062). A forced `tourId` always starts; a tour\n\t\t * whose first step is not page-anchored keeps the prior any-route\n\t\t * behaviour; without a router there is nothing to match, so it starts.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tmaybeAutoStart() {\n\t\t\tif (this.wt.running.value) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.tourId) {\n\t\t\t\tthis.wt.start(this.tourId)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.autoStart) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst auto = this.wt.autoStartTour.value\n\t\t\tif (!auto) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.$router) {\n\t\t\t\tthis.wt.start(auto.id)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst routeName = this.$route && this.$route.name\n\t\t\tif (this.routeMatchesTour(auto, routeName)) {\n\t\t\t\tthis.wt.start(auto.id)\n\t\t\t} else {\n\t\t\t\tthis._pendingAutoTour = auto\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The route name a tour's FIRST step is anchored to, or null when the\n\t\t * first step is not page-anchored (a centered welcome step, or a\n\t\t * non-page target). Used to gate auto-start to the right screen.\n\t\t *\n\t\t * A `placement: \"center\"` first step returns NULL however it is\n\t\t * anchored. This is the whole point: a centered step spotlights\n\t\t * nothing — it is a welcome card floating in the middle of the screen —\n\t\t * so there is no element for the user to be \"on the right page\" for,\n\t\t * and gating its auto-open on a route only decides whether the tour\n\t\t * opens at all.\n\t\t *\n\t\t * That is what it did. This docblock already CLAIMED a centered welcome\n\t\t * step returned null; the code never checked `placement`, so any app\n\t\t * whose welcome step happened to carry a page/nav anchor other than its\n\t\t * landing route parked the tour in `_pendingAutoTour` and never opened\n\t\t * it — no error, no log, just no walkthrough. Measured on one instance\n\t\t * with an empty seen-state: opencatalogi (welcome anchored to\n\t\t * `Catalogs`) and pipelinq (anchored to `Products`) both showed nothing\n\t\t * at `#/` and the full tour at the anchored route, while dossiq\n\t\t * (anchored to its landing `Dashboard`) worked — the control that shows\n\t\t * the anchor, not the tour, was the variable. 19 of 20 fleet manifests\n\t\t * declare a walkthrough, so this is a class, not two instances.\n\t\t *\n\t\t * A step that genuinely spotlights an element keeps the ADR-062\n\t\t * behaviour, which is what it was built for: it still waits for its\n\t\t * page rather than popping over a deep-linked detail screen.\n\t\t *\n\t\t * @param {object} tour The tour definition.\n\t\t * @return {string|null} The first-step page/nav route name, or null.\n\t\t */\n\t\tfirstStepPage(tour) {\n\t\t\tconst steps = (tour && tour.steps) || []\n\t\t\tconst first = steps[0]\n\t\t\t// A CENTERED first step is not page-anchored, whatever it targets.\n\t\t\t//\n\t\t\t// The doc comment above has always said so; the code did not check.\n\t\t\t// `placement: 'center'` renders a card in the middle of the screen\n\t\t\t// and spotlights NOTHING — `locateTarget()` returns early on\n\t\t\t// `isCentered` and never resolves the target at all. So gating\n\t\t\t// auto-start on being on that step's route asks the user to be\n\t\t\t// somewhere the step will not point at.\n\t\t\t//\n\t\t\t// Consequence measured 2026-08-27 on one running instance, same\n\t\t\t// user, empty seen-state:\n\t\t\t//\n\t\t\t// opencatalogi welcome anchored to `Catalogs` -> `/` shows nothing,\n\t\t\t// `#/catalogi` shows \"1 / 8 Welcome to OpenCatalogi\"\n\t\t\t// pipelinq welcome anchored to `Products` -> `/` shows nothing,\n\t\t\t// `#/products` shows \"1 / 11 Welcome to Pipelinq\"\n\t\t\t// dossiq welcome anchored to `Dashboard` -> works (control)\n\t\t\t//\n\t\t\t// The tour was parked in `_pendingAutoTour` and opened only if the\n\t\t\t// user happened to navigate to that page — so the getting-started\n\t\t\t// tour never appeared on the landing page, silently, with nothing\n\t\t\t// logged. 19 of 20 fleet manifests declare a walkthrough, so this\n\t\t\t// is not two apps; it is however many anchored their welcome step\n\t\t\t// somewhere other than their landing route.\n\t\t\t//\n\t\t\t// ADR-062's intent is preserved exactly: a first step that really\n\t\t\t// does spotlight an element still gates on that element's route, so\n\t\t\t// a tour never pops over a deep-linked detail page.\n\t\t\tif (!first) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (first.placement === 'center') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst tgt = first.target\n\t\t\tif (tgt && (tgt.kind === 'page' || tgt.kind === 'nav-item') && tgt.ref) {\n\t\t\t\treturn String(tgt.ref)\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * Whether a tour may auto-open on the given route. True when the tour's\n\t\t * first step is not page-anchored — which includes every `center`-placed\n\t\t * welcome step, since it spotlights nothing (see firstStepPage) — so the\n\t\t * tour starts wherever the user lands. Otherwise the route name must\n\t\t * equal the first-step page.\n\t\t *\n\t\t * This is the ONLY consumer of firstStepPage, and both auto-start paths\n\t\t * (maybeAutoStart on mount, and the router afterEach that resumes a\n\t\t * deferred tour) go through here — so a centered step cannot be\n\t\t * swallowed by either.\n\t\t *\n\t\t * A tour that declares `startOn` opens only on one of those route\n\t\t * names, whatever its first step looks like.\n\t\t *\n\t\t * @param {object} tour The tour definition.\n\t\t * @param {string} [routeName] The current route name.\n\t\t * @return {boolean} True when the tour may open on this route.\n\t\t * @spec openspec/changes/a-tour-starts-on-the-pages-it-names/specs/walkthrough/spec.md\n\t\t */\n\t\trouteMatchesTour(tour, routeName) {\n\t\t\t// A tour that names where it starts (`startOn`) opens only there.\n\t\t\t// It wins over the centered-step rule below: a welcome card floats\n\t\t\t// in the middle of the screen, but a user who opens a case from a\n\t\t\t// mail link lands on that case, and the card then blocks the page\n\t\t\t// they came for. Measured 13 October on dossiq: a staff user's first\n\t\t\t// visit was a deep link to a Woo request, and \"Welcome to Dossiq\"\n\t\t\t// opened over it. The tour waits in `_pendingAutoTour` and opens when\n\t\t\t// the user reaches a named route.\n\t\t\tconst startOn = Array.isArray(tour && tour.startOn) ? tour.startOn.map(String) : []\n\t\t\tif (startOn.length > 0) {\n\t\t\t\treturn !!routeName && startOn.includes(String(routeName))\n\t\t\t}\n\t\t\tconst page = this.firstStepPage(tour)\n\t\t\tif (!page) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn !!routeName && String(routeName) === page\n\t\t},\n\n\t\t/**\n\t\t * Resolve, scroll to, and measure the active step's target, then install\n\t\t * the step's advance watcher.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tlocateTarget() {\n\t\t\tthis.teardownStep()\n\t\t\tif (!this.step || this.isCentered) {\n\t\t\t\tthis.rect = null\n\t\t\t\tthis.focusCard()\n\t\t\t\tthis.armDelay()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst el = this.resolveTarget(this.step)\n\t\t\t// A step's target belongs to the page it was first found on. The\n\t\t\t// same `index-add` button exists on every list page, and a step\n\t\t\t// left half-done on Products used to spotlight the Clients page's\n\t\t\t// button too, inviting a click that belonged to another step.\n\t\t\tconst kind = (this.step.target && this.step.target.kind) || ''\n\t\t\tconst routeName = this.$route ? this.$route.name : null\n\t\t\tif (el && kind !== 'page' && kind !== 'nav-item' && routeName) {\n\t\t\t\tif (this._homeRoute === null) {\n\t\t\t\t\tthis._homeRoute = routeName\n\t\t\t\t} else if (this._homeRoute !== routeName) {\n\t\t\t\t\tthis.offPage = true\n\t\t\t\t\tthis.targetEl = null\n\t\t\t\t\tthis.rect = null\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.offPage = false\n\t\t\tthis.targetEl = el\n\t\t\tif (!el) {\n\t\t\t\t// Optional step whose target is absent → skip; else wait for it.\n\t\t\t\tif (this.step.optional) {\n\t\t\t\t\tthis.wt.skip()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// Genuinely not in the DOM (nothing to reveal) — wait for it to\n\t\t\t\t// appear. A target hidden by a collapsed nav group is handled by\n\t\t\t\t// computeRect()'s zero-size check below instead, since `el` is\n\t\t\t\t// resolvable there and revealTarget() can walk up from it.\n\t\t\t\tthis.observeForTarget()\n\t\t\t\tthis.rect = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tel.scrollIntoView({ block: 'center', inline: 'center' })\n\t\t\t} catch { /* jsdom */ }\n\t\t\tthis.computeRect()\n\t\t\tthis.armStep(el)\n\t\t\t// The ResizeObserver only fires when the target itself resizes. A nav\n\t\t\t// item can MOVE (siblings render/settle after mount) without resizing,\n\t\t\t// leaving a stale cutout one row off. Re-measure across the next few\n\t\t\t// frames so the spotlight tracks the settled position, and keep\n\t\t\t// tracking it afterwards for content that arrives later still.\n\t\t\tthis.scheduleSettleRemeasure()\n\t\t\tthis.trackTargetLayout()\n\t\t},\n\n\t\t/**\n\t\t * Resolve a step's `target` to a DOM element, preferring stable manifest\n\t\t * identities, then `data-walkthrough-id`/`data-testid`, then raw CSS.\n\t\t *\n\t\t * @param {object} step The active step.\n\t\t * @return {Element|null} The resolved element.\n\t\t */\n\t\tresolveTarget(step) {\n\t\t\tconst tgt = step.target || {}\n\t\t\tconst ref = tgt.ref\n\t\t\tconst q = (sel) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn document.querySelector(sel)\n\t\t\t\t} catch {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst esc = (v) => (window.CSS && CSS.escape ? CSS.escape(v) : String(v).replace(/\"/g, '\\\\\"'))\n\t\t\tif (tgt.kind === 'selector' && tgt.selector) {\n\t\t\t\treturn q(tgt.selector)\n\t\t\t}\n\t\t\tif (!ref) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst byId = q(`[data-walkthrough-id=\"${esc(ref)}\"]`) || q(`[data-testid=\"${esc(ref)}\"]`)\n\t\t\tif (byId) {\n\t\t\t\treturn byId\n\t\t\t}\n\t\t\tif (tgt.kind === 'nav-item' || tgt.kind === 'page') {\n\t\t\t\treturn q(`[data-cn-route=\"${esc(ref)}\"]`) || q(`a[href$=\"#/${esc(ref)}\"]`) || q(`[data-route=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\tif (tgt.kind === 'widget') {\n\t\t\t\treturn q(`[data-widget-key=\"${esc(ref)}\"]`) || q(`[data-widget-id=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\tif (tgt.kind === 'action') {\n\t\t\t\treturn q(`[data-action-id=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * Expand only the collapsed nav groups that are actual ancestors of\n\t\t * `this.targetEl` (already resolved, just `display:none`'d by a\n\t\t * collapsed parent), instead of scanning the whole navigation.\n\t\t * Attempted at most once per step (`_revealAttempted`, reset in\n\t\t * teardownStep).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\trevealTarget() {\n\t\t\tif (this._revealAttempted || !this.targetEl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis._revealAttempted = true\n\t\t\tconst click = (btn) => {\n\t\t\t\tif (btn && typeof btn.click === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbtn.click()\n\t\t\t\t\t} catch { /* jsdom / detached */ }\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Walk every ancestor (not just .closest() jumps) since a nav group\n\t\t\t// and the Settings foldout use different container markup and can\n\t\t\t// nest either way.\n\t\t\tlet node = this.targetEl.parentElement\n\t\t\twhile (node) {\n\t\t\t\tif (node.classList.contains('app-navigation-entry--collapsible')) {\n\t\t\t\t\tclick(node.querySelector('button.icon-collapse, .app-navigation-entry__children-toggle, .app-navigation-entry__collapse'))\n\t\t\t\t} else if (node.matches?.('[data-testid=\"cn-nav-settings\"]')) {\n\t\t\t\t\t// NcAppNavigationSettings: CSS-module classes are hashed at\n\t\t\t\t\t// build time, so aria-expanded is the only stable handle on\n\t\t\t\t\t// its own toggle button.\n\t\t\t\t\tclick(node.querySelector('button[aria-expanded]'))\n\t\t\t\t}\n\t\t\t\tnode = node.parentElement\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Re-measure the target a few times after arming, to catch post-mount\n\t\t * layout settling (sibling nav items rendering, fonts, etc.) that moves\n\t\t * the target without resizing it. Cleared in teardownStep.\n\t\t *\n\t\t * Fast path only; later movement is caught by `trackTargetLayout()`.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tscheduleSettleRemeasure() {\n\t\t\tconst again = () => {\n\t\t\t\tif (this.targetEl && !this.isCentered) {\n\t\t\t\t\tthis.computeRect()\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._settleTimers = (this._settleTimers || [])\n\t\t\tthis._settleTimers.push(setTimeout(again, 100), setTimeout(again, 300), setTimeout(again, 600))\n\t\t},\n\n\t\t/**\n\t\t * Re-measure on any layout change for as long as the step is active.\n\t\t *\n\t\t * The settle timers above stop at 600ms; a target on a page whose content\n\t\t * arrives from the network keeps moving after that, leaving the cutout on\n\t\t * empty space. The MutationObserver catches inserted content, the\n\t\t * ResizeObserver catches reflows that insert no nodes.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttrackTargetLayout() {\n\t\t\tconst remeasure = () => {\n\t\t\t\tif (this._remeasureRaf) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst raf = typeof window.requestAnimationFrame === 'function'\n\t\t\t\t\t? window.requestAnimationFrame\n\t\t\t\t\t: (fn) => setTimeout(fn, 16)\n\t\t\t\tthis._remeasureRaf = raf(() => {\n\t\t\t\t\tthis._remeasureRaf = null\n\t\t\t\t\tif (this.targetEl && !this.isCentered) {\n\t\t\t\t\t\tthis.computeRect()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (window.MutationObserver) {\n\t\t\t\tthis._layoutObs = new MutationObserver(remeasure)\n\t\t\t\tthis._layoutObs.observe(document.body, { childList: true, subtree: true })\n\t\t\t}\n\t\t\tif (window.ResizeObserver) {\n\t\t\t\tthis._bodyResizeObs = new ResizeObserver(remeasure)\n\t\t\t\tthis._bodyResizeObs.observe(document.body)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Measure the target into a viewport rect.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcomputeRect() {\n\t\t\tif (!this.targetEl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst r = this.targetEl.getBoundingClientRect()\n\t\t\t// Target present but not laid out (e.g. a nav item inside a collapsed\n\t\t\t// group, or a hidden element): fall back to an anchorless centered\n\t\t\t// coachmark — no point-sized cutout — until it becomes visible. The\n\t\t\t// ResizeObserver armed in armStep() recomputes when it gains size.\n\t\t\tif (r.width === 0 || r.height === 0) {\n\t\t\t\t// Resolved but not laid out — most often a nav item inside a\n\t\t\t\t// collapsed group. Expand the group so the ResizeObserver armed in\n\t\t\t\t// armStep() recomputes once it gains size; until then, anchorless.\n\t\t\t\tthis.revealTarget()\n\t\t\t\tthis.rect = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// The overlay (.cn-walkthrough) is position:fixed, but a transformed\n\t\t\t// ancestor (NC content area) can become its containing block, so its\n\t\t\t// top-left is not the viewport origin. Convert the viewport rect into\n\t\t\t// the overlay's coordinate space so the dim/ring/card line up with the\n\t\t\t// real element instead of sitting a header's height too low.\n\t\t\tconst host = this.$el && this.$el.getBoundingClientRect ? this.$el.getBoundingClientRect() : { top: 0, left: 0 }\n\t\t\t// Remember the overlay's own viewport offset so placeCard can clamp\n\t\t\t// the coachmark against the REAL viewport even when a transformed\n\t\t\t// ancestor makes the position:fixed origin non-zero.\n\t\t\tthis._hostOffset = { top: host.top, left: host.left }\n\t\t\tconst next = { top: r.top - host.top, left: r.left - host.left, width: r.width, height: r.height }\n\t\t\t// Bail when nothing moved: trackTargetLayout() calls this on every\n\t\t\t// layout change, and placeCard()'s focusCard() would otherwise steal\n\t\t\t// focus back from whatever the user is typing in.\n\t\t\tconst p = this.rect\n\t\t\tif (p && p.top === next.top && p.left === next.left && p.width === next.width && p.height === next.height) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rect = next\n\t\t\tthis.$nextTick(() => this.placeCard())\n\t\t},\n\n\t\t/**\n\t\t * Position the coachmark near the target with a simple flip so it stays\n\t\t * on-screen.\n\t\t *\n\t\t * @spec openspec/changes/cn-walkthrough-engine/specs/cn-walkthrough/spec.md\n\t\t * @return {void}\n\t\t */\n\t\tplaceCard() {\n\t\t\tconst card = this.$refs.card\n\t\t\tif (!card || !this.rect) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst cw = card.offsetWidth || 320\n\t\t\tconst ch = card.offsetHeight || 160\n\t\t\tconst gap = 12\n\t\t\tconst vw = window.innerWidth\n\t\t\tconst vh = window.innerHeight\n\t\t\tconst r = this.rect\n\t\t\tlet placement = this.step.placement && this.step.placement !== 'auto' ? this.step.placement : 'bottom'\n\t\t\tlet top\n\t\t\tlet left\n\t\t\tif (placement === 'bottom' && r.top + r.height + gap + ch > vh) {\n\t\t\t\tplacement = 'top'\n\t\t\t}\n\t\t\tif (placement === 'top' && r.top - gap - ch < 0) {\n\t\t\t\tplacement = 'bottom'\n\t\t\t}\n\t\t\tif (placement === 'bottom') {\n\t\t\t\ttop = r.top + r.height + gap\n\t\t\t\tleft = r.left\n\t\t\t} else if (placement === 'top') {\n\t\t\t\ttop = r.top - gap - ch\n\t\t\t\tleft = r.left\n\t\t\t} else if (placement === 'left') {\n\t\t\t\ttop = r.top\n\t\t\t\tleft = r.left - gap - cw\n\t\t\t} else {\n\t\t\t\ttop = r.top\n\t\t\t\tleft = r.left + r.width + gap\n\t\t\t}\n\t\t\t// Clamp to the viewport. `left`/`top` live in overlay-relative space\n\t\t\t// (this.rect was host-subtracted), so the viewport bounds must be\n\t\t\t// host-subtracted too — otherwise a scrolled/transformed host would\n\t\t\t// let the card render off-screen (ADR-062: the coachmark is always\n\t\t\t// fully on-screen).\n\t\t\tconst ho = this._hostOffset || { top: 0, left: 0 }\n\t\t\tleft = Math.max(gap - ho.left, Math.min(left, vw - cw - gap - ho.left))\n\t\t\ttop = Math.max(gap - ho.top, Math.min(top, vh - ch - gap - ho.top))\n\t\t\tthis.cardPlacement = placement\n\t\t\tthis.cardPos = { top, left }\n\t\t\tthis.focusCard()\n\t\t},\n\n\t\t/**\n\t\t * Move focus to the coachmark's first control (focus management / a11y).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tfocusCard() {\n\t\t\t// Never take focus from an open app dialog.\n\t\t\tif (this.appModalOpen) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tconst btn = this.$refs.firstBtn && this.$refs.firstBtn.$el\n\t\t\t\tif (btn && typeof btn.focus === 'function') {\n\t\t\t\t\tbtn.focus()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Install the advance watcher for the active step (click / element / delay;\n\t\t * route + object-created are global listeners installed at mount).\n\t\t *\n\t\t * @param {Element} el The resolved target.\n\t\t * @return {void}\n\t\t */\n\t\tarmStep(el) {\n\t\t\tconst a = this.step.advanceOn || {}\n\t\t\tif (a.type === 'click-target' && el) {\n\t\t\t\tthis._clickHandler = () => this.wt.notify({ kind: 'click' })\n\t\t\t\tel.addEventListener('click', this._clickHandler, { once: true })\n\t\t\t}\n\t\t\t// Recompute the cutout when the target's size changes — covers a target\n\t\t\t// that starts hidden/zero-size (e.g. inside a collapsed nav group) and\n\t\t\t// becomes visible while the step is active.\n\t\t\tif (el && window.ResizeObserver) {\n\t\t\t\tthis._resizeObs = new ResizeObserver(() => this.computeRect())\n\t\t\t\tthis._resizeObs.observe(el)\n\t\t\t}\n\t\t\tthis.armDelay()\n\t\t},\n\n\t\t/**\n\t\t * Arm the delay timer for a `delay` advance.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tarmDelay() {\n\t\t\tconst a = (this.step && this.step.advanceOn) || {}\n\t\t\tif (a.type === 'delay') {\n\t\t\t\tthis._delayTimer = setTimeout(() => this.wt.notify({ kind: 'delay' }), a.ms || 1500)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Watch the DOM until an `element-appears` (or a not-yet-mounted) target\n\t\t * resolves, then re-locate.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tobserveForTarget() {\n\t\t\tthis._observer = new MutationObserver(() => {\n\t\t\t\tconst el = this.resolveTarget(this.step)\n\t\t\t\tif (el) {\n\t\t\t\t\tthis._observer.disconnect()\n\t\t\t\t\tthis._observer = null\n\t\t\t\t\tif (this.step.advanceOn && this.step.advanceOn.type === 'element-appears') {\n\t\t\t\t\t\tthis.wt.notify({ kind: 'element' })\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.locateTarget()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t\tthis._observer.observe(document.body, { childList: true, subtree: true })\n\t\t},\n\n\t\t/**\n\t\t * Tear down the current step's watchers/timers/listeners.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tteardownStep() {\n\t\t\tthis._revealAttempted = false\n\t\t\t// computeRect()'s bail-when-unchanged optimisation must not survive\n\t\t\t// into the next step, or a same-geometry target never re-runs\n\t\t\t// placeCard()/focusCard() on step change.\n\t\t\tthis.rect = null\n\t\t\tif (this._observer) {\n\t\t\t\tthis._observer.disconnect()\n\t\t\t\tthis._observer = null\n\t\t\t}\n\t\t\tif (this._resizeObs) {\n\t\t\t\tthis._resizeObs.disconnect()\n\t\t\t\tthis._resizeObs = null\n\t\t\t}\n\t\t\tif (this._layoutObs) {\n\t\t\t\tthis._layoutObs.disconnect()\n\t\t\t\tthis._layoutObs = null\n\t\t\t}\n\t\t\tif (this._bodyResizeObs) {\n\t\t\t\tthis._bodyResizeObs.disconnect()\n\t\t\t\tthis._bodyResizeObs = null\n\t\t\t}\n\t\t\tif (this._remeasureRaf) {\n\t\t\t\tconst cancel = typeof window.cancelAnimationFrame === 'function' ? window.cancelAnimationFrame : clearTimeout\n\t\t\t\tcancel(this._remeasureRaf)\n\t\t\t\tthis._remeasureRaf = null\n\t\t\t}\n\t\t\tif (this._delayTimer) {\n\t\t\t\tclearTimeout(this._delayTimer)\n\t\t\t\tthis._delayTimer = null\n\t\t\t}\n\t\t\tif (this._settleTimers) {\n\t\t\t\tthis._settleTimers.forEach(clearTimeout)\n\t\t\t\tthis._settleTimers = null\n\t\t\t}\n\t\t\tif (this.targetEl && this._clickHandler) {\n\t\t\t\tthis.targetEl.removeEventListener('click', this._clickHandler)\n\t\t\t\tthis._clickHandler = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Advance the tour one step, completing on the last step.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tadvance() {\n\t\t\t/**\n\t\t\t * @event advance Emitted when the user advances the tour.\n\t\t\t * @type {{ stepId: string }}\n\t\t\t */\n\t\t\tthis.$emit('advance', { stepId: this.step && this.step.id })\n\t\t\tconst wasLast = this.isLast\n\t\t\tthis.wt.next()\n\t\t\tif (wasLast) {\n\t\t\t\t/**\n\t\t\t\t * @event complete Emitted when the last step is passed.\n\t\t\t\t */\n\t\t\t\tthis.$emit('complete')\n\t\t\t}\n\t\t},\n\n\t\tback() {\n\t\t\tthis.wt.back()\n\t\t},\n\n\t\t/**\n\t\t * The coachmark's \"Skip\" control — ENDS the tour (it does not advance a\n\t\t * step). Marks the tour complete so the seen-version is persisted and it\n\t\t * won't auto-reopen, then emits `dismiss` + `complete` (ADR-062: Skip\n\t\t * dismisses the whole tour; Next is the only control that advances).\n\t\t * The internal per-step skip (an optional step whose target is absent)\n\t\t * still uses `wt.skip()` directly in `locateTarget`.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tskip() {\n\t\t\tthis.wt.complete()\n\t\t\t/**\n\t\t\t * @event dismiss Emitted when the user skips (ends) the tour.\n\t\t\t */\n\t\t\tthis.$emit('dismiss')\n\t\t\tthis.$emit('complete')\n\t\t},\n\n\t\t/**\n\t\t * Execute a cross-app hand-off: complete this tour locally, then navigate\n\t\t * the browser to the destination URL carrying a `cn_resume_tour` /\n\t\t * `cn_resume_step` token so the destination app's useWalkthrough resumes.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tdoHandoff() {\n\t\t\tconst h = (this.step && this.step.handoff) || {}\n\t\t\tif (!h.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst tour = h.tour || (this.wt.activeTour.value && this.wt.activeTour.value.id) || ''\n\t\t\tlet url = h.url + (h.url.indexOf('?') === -1 ? '?' : '&') + 'cn_resume_tour=' + encodeURIComponent(tour)\n\t\t\tif (h.step) {\n\t\t\t\turl += '&cn_resume_step=' + encodeURIComponent(h.step)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event handoff Emitted just before navigating to a cross-app destination.\n\t\t\t * @type {{ app: string, url: string }}\n\t\t\t */\n\t\t\tthis.$emit('handoff', { app: h.app, url })\n\t\t\tthis.$emit('complete')\n\t\t\tthis.wt.complete()\n\t\t\ttry {\n\t\t\t\twindow.location.href = url\n\t\t\t} catch { /* jsdom */ }\n\t\t},\n\n\t\t/**\n\t\t * The corner close button PAUSES the tour, like ESC and the dim. It\n\t\t * used to complete it, and the host then wiped the saved step, so a\n\t\t * restart began at step 1 again. Only Finish on the last step (and a\n\t\t * host-supplied Skip) completes the tour; \"Start over\" in the user\n\t\t * settings stays the way back to step 1.\n\t\t *\n\t\t * @spec openspec/changes/audit-round-lib-fixes/specs/cn-walkthrough/spec.md\n\t\t * @return {void}\n\t\t */\n\t\tclose() {\n\t\t\tthis.onBackdrop()\n\t\t},\n\n\t\t/**\n\t\t * Pause the tour from the corner X, a backdrop click or ESC. The tour\n\t\t * hides and keeps its step; only Finish (or a host Skip) ends it.\n\t\t * Pressing ESC used to dismiss it, and the host recorded that as\n\t\t * \"seen\", so the tour never came back. The `pause` event carries\n\t\t * where the user was, so the host can keep a paused tour hidden on\n\t\t * the next page load.\n\t\t *\n\t\t * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md\n\t\t * @spec openspec/changes/audit-round-lib-fixes/specs/cn-walkthrough/spec.md\n\t\t * @return {void}\n\t\t */\n\t\tonBackdrop() {\n\t\t\tconst where = {\n\t\t\t\ttourId: (this.wt.activeTour.value && this.wt.activeTour.value.id) || '',\n\t\t\t\tstepId: (this.step && this.step.id) || '',\n\t\t\t\tindex: this.index,\n\t\t\t}\n\t\t\tthis.wt.pause()\n\t\t\t/**\n\t\t\t * @event pause Emitted when the user hides the tour (X, backdrop, ESC). The tour can continue later.\n\t\t\t * @type {{ tourId: string, stepId: string, index: number }}\n\t\t\t */\n\t\t\tthis.$emit('pause', where)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-walkthrough {\n\tposition: fixed;\n\tinset: 0;\n\tpointer-events: none;\n}\n\n.cn-walkthrough__live {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n}\n\n.cn-walkthrough__dim {\n\tposition: fixed;\n\tbackground: var(--color-modal-background, rgba(0, 0, 0, 0.5));\n\tpointer-events: auto;\n}\n\n.cn-walkthrough__dim--full {\n\tinset: 0;\n}\n\n/* Anchorless fallback for an ANCHORED step: dim everything, but do not\n intercept pointer events — the step's task requires clicking the app\n underneath (see the template comment). */\n/* Lets the step's own task through — and therefore cannot be clicked to\n dismiss; ESC, the close button and Back/Finish remain (see the template). */\n.cn-walkthrough__dim--passthrough {\n\tpointer-events: none;\n}\n\n.cn-walkthrough__ring {\n\tposition: fixed;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 0 0 2px var(--color-primary-element, #0082c9);\n\tpointer-events: none;\n}\n\n.cn-walkthrough__card {\n\tposition: fixed;\n\twidth: 320px;\n\tmax-width: calc(100vw - 24px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 2px 12px var(--color-box-shadow, rgba(0, 0, 0, 0.3));\n\tpadding: 16px;\n\tpointer-events: auto;\n}\n\n.cn-walkthrough__card--docked {\n\tinset-block-end: 16px;\n\tinset-inline-start: 16px;\n}\n\n.cn-walkthrough__close {\n\t/* !important beats NcButton's own `position: relative`, which would otherwise\n\t leave the close button in-flow at the card's top-left. */\n\tposition: absolute !important;\n\ttop: 8px;\n\tinset-inline-end: 8px;\n\tinset-inline-start: auto;\n\tz-index: 1;\n}\n\n.cn-walkthrough__counter {\n\tfont-size: 0.8125rem;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-bottom: 4px;\n\t/* Keep the counter/title clear of the absolutely-positioned close button. */\n\tpadding-inline-end: 32px;\n}\n\n.cn-walkthrough__title {\n\tmargin: 0 0 6px;\n\tfont-size: 1.1rem;\n}\n\n.cn-walkthrough__body {\n\tmargin: 0 0 8px;\n}\n\n.cn-walkthrough__task {\n\tmargin: 0 0 8px;\n\tpadding: 8px;\n\tbackground: var(--color-primary-element-light, var(--color-background-hover));\n\tborder-radius: var(--border-radius, 4px);\n\tfont-weight: 600;\n}\n\n.cn-walkthrough__task-icon {\n\tmargin-inline-end: 4px;\n}\n\n.cn-walkthrough__actions {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin-top: 8px;\n}\n\n.cn-walkthrough__spacer {\n\tflex: 1 1 auto;\n}\n</style>\n"],"names":["_createElementBlock","_createElementVNode","_toDisplayString","_Fragment","_openBlock","_normalizeStyle","_normalizeClass","_createVNode","_createTextVNode","_createBlock"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnWeekStripWidget.vue2.js';
|
|
|
2
2
|
import { render } from './CnWeekStripWidget.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-27a717bd";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -16,7 +16,8 @@ const DEFAULT_LIMIT = 100;
|
|
|
16
16
|
*
|
|
17
17
|
* It renders the five working days by default (`days: 7` adds the weekend),
|
|
18
18
|
* highlights today, marks items that are late, and says so when a day has
|
|
19
|
-
* nothing. On a narrow screen the strip scrolls sideways
|
|
19
|
+
* nothing. On a narrow screen the strip scrolls sideways; under the board
|
|
20
|
+
* look (`.cn-look-board`) the days wrap into rows instead.
|
|
20
21
|
*
|
|
21
22
|
* Items come from an OpenRegister source (one request for the whole week) or
|
|
22
23
|
* from static `items`. Resolved by its registry type key `week-strip`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnWeekStripWidget.vue2.js","sources":["../../../../src/components/CnWeekStripWidget/CnWeekStripWidget.vue"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-week-strip\" :class=\"{ 'cn-week-strip--inset': inset }\" data-testid=\"cn-week-strip\">\n\t\t<div v-if=\"loading\" class=\"cn-week-strip__state\" role=\"status\">\n\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t<span class=\"cn-week-strip__state-text\">{{ loadingLabel }}</span>\n\t\t</div>\n\t\t<p v-else-if=\"error\" class=\"cn-week-strip__state\" role=\"status\">\n\t\t\t{{ errorLabel }}\n\t\t</p>\n\t\t<!-- A scroll container a keyboard user cannot focus cannot be scrolled,\n\t\t so the region is focusable and named. -->\n\t\t<div\n\t\t\tv-else\n\t\t\tclass=\"cn-week-strip__scroll\"\n\t\t\trole=\"region\"\n\t\t\ttabindex=\"0\"\n\t\t\t:aria-label=\"regionLabel\">\n\t\t\t<ol class=\"cn-week-strip__days\" :style=\"{ '--cn-week-strip-columns': dayColumns.length }\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"day in dayColumns\"\n\t\t\t\t\t:key=\"day.key\"\n\t\t\t\t\tclass=\"cn-week-strip__day\"\n\t\t\t\t\t:class=\"{ 'cn-week-strip__day--today': day.today }\"\n\t\t\t\t\t:aria-current=\"day.today ? 'date' : null\"\n\t\t\t\t\t:data-day=\"day.key\">\n\t\t\t\t\t<span class=\"cn-week-strip__head\">\n\t\t\t\t\t\t<span class=\"cn-week-strip__label\">{{ day.label }}</span>\n\t\t\t\t\t\t<span v-if=\"day.today\" class=\"cn-week-strip__today\">{{ todayLabel }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ul v-if=\"day.items.length > 0\" class=\"cn-week-strip__items\">\n\t\t\t\t\t\t<li v-for=\"item in day.shown\" :key=\"item.key\" class=\"cn-week-strip__item-row\">\n\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t:is=\"item.href ? 'a' : 'div'\"\n\t\t\t\t\t\t\t\tclass=\"cn-week-strip__item\"\n\t\t\t\t\t\t\t\t:class=\"{ 'cn-week-strip__item--late': item.late, 'cn-week-strip__item--link': !!item.href }\"\n\t\t\t\t\t\t\t\t:href=\"item.href || null\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-week-strip-item\"\n\t\t\t\t\t\t\t\t@click=\"onItemClick($event, item)\">\n\t\t\t\t\t\t\t\t<strong class=\"cn-week-strip__title\">{{ item.title }}</strong>\n\t\t\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-week-strip__meta\">{{ item.meta }}</span>\n\t\t\t\t\t\t\t\t<span v-if=\"item.late\" class=\"cn-week-strip__late\">{{ lateLabel }}</span>\n\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<!-- `maxPerDay`: the rest of a full day sits behind \"+N more\": a link\n\t\t\t\t\t to `moreRoute`, or a button that opens the day in place. -->\n\t\t\t\t\t<a\n\t\t\t\t\t\tv-if=\"day.hidden > 0 && moreHref\"\n\t\t\t\t\t\tclass=\"cn-week-strip__more\"\n\t\t\t\t\t\t:href=\"moreHref\"\n\t\t\t\t\t\tdata-testid=\"cn-week-strip-more\"\n\t\t\t\t\t\t@click=\"onMoreClick($event)\">{{ moreLabel(day.hidden) }}</a>\n\t\t\t\t\t<button\n\t\t\t\t\t\tv-else-if=\"day.hidden > 0 || day.expanded\"\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"cn-week-strip__more\"\n\t\t\t\t\t\t:aria-expanded=\"day.expanded ? 'true' : 'false'\"\n\t\t\t\t\t\tdata-testid=\"cn-week-strip-more\"\n\t\t\t\t\t\t@click=\"toggleDay(day.key)\">\n\t\t\t\t\t\t{{ day.expanded ? lessLabel : moreLabel(day.hidden) }}\n\t\t\t\t\t</button>\n\t\t\t\t\t<span v-if=\"day.items.length === 0\" class=\"cn-week-strip__empty\">{{ emptyLabel }}</span>\n\t\t\t\t</li>\n\t\t\t</ol>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { getCanonicalLocale, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { dayKey, daysUntil, parseDateValue } from '../../utils/dateVariant.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { objectDisplayName, objectFieldValue } from '../../utils/objectName.js'\nimport { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { compareVisibleWhen } from '../../utils/visibleWhen.js'\n\nconst DEFAULT_LIMIT = 100\n\n/**\n * CnWeekStripWidget shows the current week as day columns, with the items\n * that fall on each day listed under it. Use it for deadlines, appointments\n * or anything else that has a date and that somebody should see coming.\n *\n * It renders the five working days by default (`days: 7` adds the weekend),\n * highlights today, marks items that are late, and says so when a day has\n * nothing. On a narrow screen the strip scrolls sideways.\n *\n * Items come from an OpenRegister source (one request for the whole week) or\n * from static `items`. Resolved by its registry type key `week-strip`.\n *\n * ```json\n * {\n * \"widgetKey\": \"week-strip\",\n * \"props\": {\n * \"content\": {\n * \"source\": { \"register\": \"dossiq\", \"schema\": \"case\", \"filter\": { \"assignee\": \"@me\" } },\n * \"dateField\": \"deadline\",\n * \"titleField\": \"title\",\n * \"metaFields\": [\"identifier\", \"caseType\"],\n * \"itemRoute\": \"CaseDetail\",\n * \"emptyText\": \"No deadlines\"\n * }\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnWeekStripWidget',\n\n\tcomponents: { NcLoadingIcon },\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot. The manifest-authored\n\t\t * `emptyText` and static item texts run through it. Identity by default.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's configuration. `days` is `5` (working days, the\n\t\t * default) or `7`. `source` names the OpenRegister register and schema,\n\t\t * with an optional `filter` (the shared @-token grammar) and `limit`.\n\t\t * `dateField` is the field the items are bucketed by. `titleField` and\n\t\t * `metaFields` pick what each item shows. `itemRoute` is the route name\n\t\t * an item links to (with the record id as `params.id`). `lateWhen` is a\n\t\t * `{ op, value }` rule on the days until the date (default: before\n\t\t * today), `lateField` a boolean field that marks a record late.\n\t\t * `items` are static entries `{ title, meta?, date, route?, href?, late? }`.\n\t\t * `emptyText` is the text of a day without items. `weekOffset` moves\n\t\t * the strip whole weeks from the current one. `inset: true` draws the\n\t\t * strip inside the board's inset (16px above, 24px at the sides, 22px\n\t\t * below) instead of from card edge to card edge. `maxPerDay` caps the\n\t\t * items a day shows; the rest sit behind \"+N more\", a link to\n\t\t * `moreRoute` (a route name or object) or, without one, a button that\n\t\t * opens the day in place. `moreLabel` replaces the text (`{count}` is\n\t\t * the number hidden).\n\t\t *\n\t\t * @type {{maxPerDay?: number, moreRoute?: (string|object), moreLabel?: string, days?: (5|7), inset?: boolean, source?: {register?: string, schema?: string, filter?: object, limit?: number}, dateField?: string, titleField?: string, metaFields?: Array<string>, itemRoute?: string, lateWhen?: {op?: string, value?: number}, lateField?: string, items?: Array<{title: string, meta?: string, date: string, route?: (string|object), href?: string, late?: boolean}>, emptyText?: string, weekOffset?: number}}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Translate function. Falls back to the injected `cnTranslate`. Pass it\n\t\t * when mounting the widget outside a CnAppRoot.\n\t\t *\n\t\t * @type {((key: string) => string)|null}\n\t\t */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The moment the strip treats as \"now\". Leave empty for the current\n\t\t * time; set it to render another week or to test.\n\t\t *\n\t\t * @type {Date|null}\n\t\t */\n\t\tnow: {\n\t\t\ttype: Date,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\t/** The days opened past `maxPerDay`, by day key. */\n\t\t\texpandedDays: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the widget draws the board's inset (`content.inset`). Off\n\t\t * by default, which keeps it edge to edge as before.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-strip-and-a-stacked-bar-can-take-the-board-inset\n\t\t * @return {boolean}\n\t\t */\n\t\tinset() {\n\t\t\treturn Boolean(this.content && this.content.inset === true)\n\t\t},\n\n\t\t/**\n\t\t * The translate function in use.\n\t\t *\n\t\t * @return {(key: string) => string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\teffectiveTranslate() {\n\t\t\treturn this.translate ?? this.cnTranslate\n\t\t},\n\n\t\t/**\n\t\t * The reference moment.\n\t\t *\n\t\t * @return {Date}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\treference() {\n\t\t\treturn this.now instanceof Date ? this.now : new Date()\n\t\t},\n\n\t\t/**\n\t\t * How many day columns render: 7, or 5 for anything else.\n\t\t *\n\t\t * @return {number}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tdayCount() {\n\t\t\treturn Number(this.content.days) === 7 ? 7 : 5\n\t\t},\n\n\t\t/**\n\t\t * The days of the strip, Monday first.\n\t\t *\n\t\t * @return {Array<Date>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tweekDays() {\n\t\t\tconst ref = this.reference\n\t\t\tconst offset = Number.isInteger(this.content.weekOffset) ? this.content.weekOffset : 0\n\t\t\tconst mondayDelta = (ref.getDay() + 6) % 7\n\t\t\tconst days = []\n\t\t\tfor (let i = 0; i < this.dayCount; i++) {\n\t\t\t\tdays.push(new Date(ref.getFullYear(), ref.getMonth(), ref.getDate() - mondayDelta + (offset * 7) + i))\n\t\t\t}\n\t\t\treturn days\n\t\t},\n\n\t\t/**\n\t\t * The field the items are bucketed by.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tdateField() {\n\t\t\treturn typeof this.content.dateField === 'string' ? this.content.dateField : ''\n\t\t},\n\n\t\t/**\n\t\t * Whether the items come from OpenRegister.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\thasSource() {\n\t\t\tconst s = this.content.source || {}\n\t\t\treturn Boolean(s.register && s.schema && this.dateField)\n\t\t},\n\n\t\t/**\n\t\t * Every item, from the source rows or the static list, in one shape.\n\t\t *\n\t\t * @return {Array<{key: string, title: string, meta: string, date: (Date|null), late: boolean, href: string, target: (object|string|null)}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tnormalizedItems() {\n\t\t\tif (this.hasSource) {\n\t\t\t\treturn this.rows.map((row, index) => this.itemFromRow(row, index))\n\t\t\t}\n\t\t\tconst items = Array.isArray(this.content.items) ? this.content.items : []\n\t\t\treturn items\n\t\t\t\t.filter((item) => item && typeof item === 'object')\n\t\t\t\t.map((item, index) => this.itemFromStatic(item, index))\n\t\t},\n\n\t\t/**\n\t\t * The columns: one per day, each with its items.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, today: boolean, items: Array<object>}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tdayColumns() {\n\t\t\tconst todayKey = dayKey(this.reference)\n\t\t\tconst buckets = {}\n\t\t\tfor (const item of this.normalizedItems) {\n\t\t\t\tif (item.date === null) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst key = dayKey(item.date)\n\t\t\t\t;(buckets[key] = buckets[key] || []).push(item)\n\t\t\t}\n\t\t\tconst formatter = this.dayFormatter\n\t\t\treturn this.weekDays.map((day) => {\n\t\t\t\tconst key = dayKey(day)\n\t\t\t\tconst items = buckets[key] || []\n\t\t\t\tconst expanded = this.expandedDays[key] === true\n\t\t\t\tconst cap = this.maxPerDay > 0 && !expanded ? this.maxPerDay : items.length\n\t\t\t\treturn {\n\t\t\t\t\tkey,\n\t\t\t\t\tlabel: formatter.format(day),\n\t\t\t\t\ttoday: key === todayKey,\n\t\t\t\t\titems,\n\t\t\t\t\tshown: items.slice(0, cap),\n\t\t\t\t\thidden: Math.max(0, items.length - cap),\n\t\t\t\t\texpanded: expanded && this.maxPerDay > 0 && items.length > this.maxPerDay,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The most items a day shows (`content.maxPerDay`), or 0 for no cap.\n\t\t *\n\t\t * @return {number}\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tmaxPerDay() {\n\t\t\tconst value = Number(this.content.maxPerDay)\n\t\t\treturn Number.isInteger(value) && value > 0 ? value : 0\n\t\t},\n\n\t\t/**\n\t\t * Where \"+N more\" leads (`content.moreRoute`), or null to open the day in place.\n\t\t *\n\t\t * @return {object|null}\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tmoreTarget() {\n\t\t\tconst route = this.content.moreRoute\n\t\t\tif (typeof route === 'string' && route !== '') {\n\t\t\t\treturn { name: route }\n\t\t\t}\n\t\t\treturn route && typeof route === 'object' ? route : null\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The href of the \"+N more\" link, '' without a target.\n\t\t *\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tmoreHref() {\n\t\t\treturn this.moreTarget ? (resolveHref(this.moreTarget, this.$router) || '') : ''\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The text that closes an opened day.\n\t\t *\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tlessLabel() {\n\t\t\treturn t('nextcloud-vue', 'Show less')\n\t\t},\n\n\t\t/**\n\t\t * Formats a day as a short weekday and a day number (\"Mon 5\").\n\t\t *\n\t\t * @return {Intl.DateTimeFormat}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tdayFormatter() {\n\t\t\tlet locale\n\t\t\ttry {\n\t\t\t\tlocale = getCanonicalLocale()\n\t\t\t} catch {\n\t\t\t\tlocale = undefined\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Intl.DateTimeFormat(locale || undefined, { weekday: 'short', day: 'numeric' })\n\t\t\t} catch {\n\t\t\t\treturn new Intl.DateTimeFormat(undefined, { weekday: 'short', day: 'numeric' })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The text of a day without items.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\temptyLabel() {\n\t\t\tconst text = this.content.emptyText\n\t\t\treturn (typeof text === 'string' && text !== '')\n\t\t\t\t? this.effectiveTranslate(text)\n\t\t\t\t: t('nextcloud-vue', 'Nothing planned')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The badge on today's column.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\ttodayLabel() {\n\t\t\treturn t('nextcloud-vue', 'today')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The marker on a late item.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tlateLabel() {\n\t\t\treturn t('nextcloud-vue', 'Late')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The accessible name of the scrolling region.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tregionLabel() {\n\t\t\treturn t('nextcloud-vue', 'This week, day by day')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The loading text.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading…')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The text shown when the items could not be loaded.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\terrorLabel() {\n\t\t\treturn t('nextcloud-vue', 'The items for this week could not be loaded.')\n\t\t},\n\n\t\t/**\n\t\t * Changes whenever a new fetch is needed.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tsourceKey() {\n\t\t\treturn JSON.stringify({\n\t\t\t\ts: this.content.source || {},\n\t\t\t\tf: this.dateField,\n\t\t\t\td: this.weekDays.length > 0 ? [dayKey(this.weekDays[0]), this.weekDays.length] : [],\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\t/** @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget */\n\t\tsourceKey() {\n\t\t\tthis.fetchItems()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.fetchItems()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether a date counts as late under `content.lateWhen` (default:\n\t\t * before today).\n\t\t *\n\t\t * @param {Date|null} date The item's date.\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tisLateDate(date) {\n\t\t\tif (date === null) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst rule = (this.content.lateWhen && typeof this.content.lateWhen === 'object')\n\t\t\t\t? this.content.lateWhen\n\t\t\t\t: { op: 'lt', value: 0 }\n\t\t\treturn compareVisibleWhen(daysUntil(date, this.reference), rule.op || 'lt', rule.value ?? 0)\n\t\t},\n\n\t\t/**\n\t\t * One OpenRegister record as a strip item.\n\t\t *\n\t\t * @param {object} row The record.\n\t\t * @param {number} index Its position, used as a key of last resort.\n\t\t * @return {object} The item.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\titemFromRow(row, index) {\n\t\t\tconst c = this.content\n\t\t\tconst id = row && (row.id || (row['@self'] && row['@self'].id))\n\t\t\tconst date = parseDateValue(objectFieldValue(row, this.dateField))\n\t\t\tconst titleValue = c.titleField ? objectFieldValue(row, c.titleField) : undefined\n\t\t\tconst title = (titleValue !== undefined && titleValue !== null && titleValue !== '')\n\t\t\t\t? String(titleValue)\n\t\t\t\t: objectDisplayName(row)\n\t\t\tconst metaFields = Array.isArray(c.metaFields) ? c.metaFields : []\n\t\t\tconst meta = metaFields\n\t\t\t\t.map((field) => objectFieldValue(row, field))\n\t\t\t\t.filter((value) => value !== undefined && value !== null && value !== '' && typeof value !== 'object')\n\t\t\t\t.map(String)\n\t\t\t\t.join(' · ')\n\t\t\tconst flagged = c.lateField ? objectFieldValue(row, c.lateField) === true : false\n\t\t\tconst target = (c.itemRoute && id) ? { name: c.itemRoute, params: { id } } : null\n\t\t\treturn {\n\t\t\t\tkey: String(id || `row-${index}`),\n\t\t\t\ttitle,\n\t\t\t\tmeta,\n\t\t\t\tdate,\n\t\t\t\tlate: flagged || this.isLateDate(date),\n\t\t\t\ttarget,\n\t\t\t\thref: target ? resolveHref(target, this.$router) : '',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * One static manifest entry as a strip item.\n\t\t *\n\t\t * @param {object} item The entry.\n\t\t * @param {number} index Its position.\n\t\t * @return {object} The item.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\titemFromStatic(item, index) {\n\t\t\tconst date = parseDateValue(item.date)\n\t\t\tlet target = null\n\t\t\tlet href = ''\n\t\t\tif (item.route) {\n\t\t\t\ttarget = typeof item.route === 'string' ? { name: item.route } : item.route\n\t\t\t\thref = resolveHref(target, this.$router)\n\t\t\t} else if (typeof item.href === 'string' && item.href !== '') {\n\t\t\t\tconst safe = safeHref(item.href)\n\t\t\t\thref = safe === '#' ? '' : safe\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkey: `item-${index}`,\n\t\t\t\ttitle: typeof item.title === 'string' ? this.effectiveTranslate(item.title) : '',\n\t\t\t\tmeta: typeof item.meta === 'string' ? this.effectiveTranslate(item.meta) : '',\n\t\t\t\tdate,\n\t\t\t\tlate: item.late === true || (item.late !== false && this.isLateDate(date)),\n\t\t\t\ttarget,\n\t\t\t\thref,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Load the week's records in one request.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tasync fetchItems() {\n\t\t\tif (!this.hasSource) {\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst s = this.content.source\n\t\t\tconst requestKey = this.sourceKey\n\t\t\tconst first = this.weekDays[0]\n\t\t\tconst last = this.weekDays[this.weekDays.length - 1]\n\t\t\tconst dayAfter = new Date(last.getFullYear(), last.getMonth(), last.getDate() + 1)\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl(\n\t\t\t\t\t'/apps/openregister/api/objects/{register}/{schema}',\n\t\t\t\t\t{ register: s.register, schema: s.schema },\n\t\t\t\t)\n\t\t\t\tconst params = {\n\t\t\t\t\t_limit: Number.isInteger(s.limit) && s.limit > 0 ? s.limit : DEFAULT_LIMIT,\n\t\t\t\t\t[`_order[${this.dateField}]`]: 'asc',\n\t\t\t\t}\n\t\t\t\tconst filter = resolveFilterTokens(s.filter || {}, {})\n\t\t\t\tfor (const [k, v] of Object.entries(filter || {})) {\n\t\t\t\t\tif (v && typeof v === 'object') {\n\t\t\t\t\t\tfor (const [op, ov] of Object.entries(v)) {\n\t\t\t\t\t\t\tparams[`${k}[${op}]`] = ov\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (v !== '' && v !== null && v !== undefined) {\n\t\t\t\t\t\tparams[k] = v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// The week window goes last, so a manifest filter on the date\n\t\t\t\t// field cannot widen the strip beyond the days it shows.\n\t\t\t\tparams[`${this.dateField}[gte]`] = dayKey(first)\n\t\t\t\tparams[`${this.dateField}[lt]`] = dayKey(dayAfter)\n\t\t\t\tconst res = await axios.get(url, { params })\n\t\t\t\tif (requestKey !== this.sourceKey) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.rows = (res && res.data && Array.isArray(res.data.results)) ? res.data.results : []\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnWeekStripWidget] failed to load items:', e)\n\t\t\t\tthis.error = (e && e.message) || 'error'\n\t\t\t\tthis.rows = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The text of the \"+N more\" control: `content.moreLabel` with `{count}`\n\t\t * filled, else \"+N more\" from the library's catalogue.\n\t\t *\n\t\t * @param {number} count The number of hidden items.\n\t\t * @return {string}\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tmoreLabel(count) {\n\t\t\tconst own = this.content.moreLabel\n\t\t\tif (typeof own === 'string' && own !== '') {\n\t\t\t\treturn this.effectiveTranslate(own).replace(/\\{count\\}/g, String(count))\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '+{count} more', { count })\n\t\t},\n\n\t\t/**\n\t\t * Open or close a day past its cap.\n\t\t *\n\t\t * @param {string} key The day key.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\ttoggleDay(key) {\n\t\t\tthis.expandedDays = { ...this.expandedDays, [key]: this.expandedDays[key] !== true }\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on the \"+N more\" link.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tonMoreClick(event) {\n\t\t\tif (this.moreTarget) {\n\t\t\t\tfollowLinkClick(event, this.moreTarget, this.$router)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on an in-app item; the browser handles the rest.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @param {object} item The clicked item.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tonItemClick(event, item) {\n\t\t\tif (item.target) {\n\t\t\t\tfollowLinkClick(event, item.target, this.$router)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-week-strip {\n\twidth: 100%;\n}\n\n/* `content.inset: true`: the board's inset inside the card (zuiddrecht-pixel-\n gaps-3). A dashboard renders its widgets flush, so without it the strip ran\n from card edge to card edge. Theme hook: --cn-widget-board-inset. */\n.cn-week-strip--inset {\n\tbox-sizing: border-box;\n\tpadding: var(--cn-widget-board-inset, 16px 24px 22px);\n}\n\n.cn-week-strip__state {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin: 0;\n\tpadding: 12px 4px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-week-strip__scroll {\n\toverflow-x: auto;\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-week-strip__scroll:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-week-strip__days {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(var(--cn-week-strip-columns, 5), minmax(128px, 1fr));\n\tgap: 10px;\n\t/* 5 columns of 128px plus gaps: below this the region scrolls sideways. */\n\tmin-width: calc(var(--cn-week-strip-columns, 5) * 136px);\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-week-strip__day {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmin-width: 0;\n\tpadding: 10px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-week-strip__day--today {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-week-strip__head {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 6px;\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-week-strip__today {\n\tpadding: 1px 8px;\n\tborder-radius: var(--border-radius-pill, 10px);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n\tfont-size: 0.85em;\n\tfont-weight: 700;\n}\n\n.cn-week-strip__items {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-week-strip__item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 10px 12px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-week-strip__item--link:hover {\n\tborder-color: var(--color-primary-element);\n}\n\n.cn-week-strip__item--link:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n/* The edge is drawn inside the box, so a late item is as wide as the others. */\n.cn-week-strip__item--late {\n\tborder-color: var(--color-element-error, var(--color-error));\n\tbox-shadow: inset 3px 0 0 0 var(--color-element-error, var(--color-error));\n}\n\n.cn-week-strip__title {\n\tfont-size: 0.95em;\n\tline-height: 1.3;\n\toverflow-wrap: anywhere;\n}\n\n.cn-week-strip__meta {\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow-wrap: anywhere;\n}\n\n.cn-week-strip__late {\n\tfont-size: 0.8em;\n\tfont-weight: 600;\n\tcolor: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-week-strip__empty {\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* \"+N more\" under a capped day: a quiet text control, 24px high to click. */\n.cn-week-strip__more {\n\talign-self: flex-start;\n\tmin-height: 24px;\n\tmargin: 0;\n\tpadding: 2px 4px;\n\tborder: 0;\n\tborder-radius: var(--border-radius, 4px);\n\tbackground: transparent;\n\tcolor: var(--color-primary-element);\n\tfont: inherit;\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.cn-week-strip__more:hover {\n\ttext-decoration: underline;\n}\n\n.cn-week-strip__more:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnWeekStripWidget.vue2.js","sources":["../../../../src/components/CnWeekStripWidget/CnWeekStripWidget.vue"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-week-strip\" :class=\"{ 'cn-week-strip--inset': inset }\" data-testid=\"cn-week-strip\">\n\t\t<div v-if=\"loading\" class=\"cn-week-strip__state\" role=\"status\">\n\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t<span class=\"cn-week-strip__state-text\">{{ loadingLabel }}</span>\n\t\t</div>\n\t\t<p v-else-if=\"error\" class=\"cn-week-strip__state\" role=\"status\">\n\t\t\t{{ errorLabel }}\n\t\t</p>\n\t\t<!-- A scroll container a keyboard user cannot focus cannot be scrolled,\n\t\t so the region is focusable and named. -->\n\t\t<div\n\t\t\tv-else\n\t\t\tclass=\"cn-week-strip__scroll\"\n\t\t\trole=\"region\"\n\t\t\ttabindex=\"0\"\n\t\t\t:aria-label=\"regionLabel\">\n\t\t\t<ol class=\"cn-week-strip__days\" :style=\"{ '--cn-week-strip-columns': dayColumns.length }\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"day in dayColumns\"\n\t\t\t\t\t:key=\"day.key\"\n\t\t\t\t\tclass=\"cn-week-strip__day\"\n\t\t\t\t\t:class=\"{ 'cn-week-strip__day--today': day.today }\"\n\t\t\t\t\t:aria-current=\"day.today ? 'date' : null\"\n\t\t\t\t\t:data-day=\"day.key\">\n\t\t\t\t\t<span class=\"cn-week-strip__head\">\n\t\t\t\t\t\t<span class=\"cn-week-strip__label\">{{ day.label }}</span>\n\t\t\t\t\t\t<span v-if=\"day.today\" class=\"cn-week-strip__today\">{{ todayLabel }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ul v-if=\"day.items.length > 0\" class=\"cn-week-strip__items\">\n\t\t\t\t\t\t<li v-for=\"item in day.shown\" :key=\"item.key\" class=\"cn-week-strip__item-row\">\n\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t:is=\"item.href ? 'a' : 'div'\"\n\t\t\t\t\t\t\t\tclass=\"cn-week-strip__item\"\n\t\t\t\t\t\t\t\t:class=\"{ 'cn-week-strip__item--late': item.late, 'cn-week-strip__item--link': !!item.href }\"\n\t\t\t\t\t\t\t\t:href=\"item.href || null\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-week-strip-item\"\n\t\t\t\t\t\t\t\t@click=\"onItemClick($event, item)\">\n\t\t\t\t\t\t\t\t<strong class=\"cn-week-strip__title\">{{ item.title }}</strong>\n\t\t\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-week-strip__meta\">{{ item.meta }}</span>\n\t\t\t\t\t\t\t\t<span v-if=\"item.late\" class=\"cn-week-strip__late\">{{ lateLabel }}</span>\n\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<!-- `maxPerDay`: the rest of a full day sits behind \"+N more\": a link\n\t\t\t\t\t to `moreRoute`, or a button that opens the day in place. -->\n\t\t\t\t\t<a\n\t\t\t\t\t\tv-if=\"day.hidden > 0 && moreHref\"\n\t\t\t\t\t\tclass=\"cn-week-strip__more\"\n\t\t\t\t\t\t:href=\"moreHref\"\n\t\t\t\t\t\tdata-testid=\"cn-week-strip-more\"\n\t\t\t\t\t\t@click=\"onMoreClick($event)\">{{ moreLabel(day.hidden) }}</a>\n\t\t\t\t\t<button\n\t\t\t\t\t\tv-else-if=\"day.hidden > 0 || day.expanded\"\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"cn-week-strip__more\"\n\t\t\t\t\t\t:aria-expanded=\"day.expanded ? 'true' : 'false'\"\n\t\t\t\t\t\tdata-testid=\"cn-week-strip-more\"\n\t\t\t\t\t\t@click=\"toggleDay(day.key)\">\n\t\t\t\t\t\t{{ day.expanded ? lessLabel : moreLabel(day.hidden) }}\n\t\t\t\t\t</button>\n\t\t\t\t\t<span v-if=\"day.items.length === 0\" class=\"cn-week-strip__empty\">{{ emptyLabel }}</span>\n\t\t\t\t</li>\n\t\t\t</ol>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { getCanonicalLocale, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { dayKey, daysUntil, parseDateValue } from '../../utils/dateVariant.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { objectDisplayName, objectFieldValue } from '../../utils/objectName.js'\nimport { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { compareVisibleWhen } from '../../utils/visibleWhen.js'\n\nconst DEFAULT_LIMIT = 100\n\n/**\n * CnWeekStripWidget shows the current week as day columns, with the items\n * that fall on each day listed under it. Use it for deadlines, appointments\n * or anything else that has a date and that somebody should see coming.\n *\n * It renders the five working days by default (`days: 7` adds the weekend),\n * highlights today, marks items that are late, and says so when a day has\n * nothing. On a narrow screen the strip scrolls sideways; under the board\n * look (`.cn-look-board`) the days wrap into rows instead.\n *\n * Items come from an OpenRegister source (one request for the whole week) or\n * from static `items`. Resolved by its registry type key `week-strip`.\n *\n * ```json\n * {\n * \"widgetKey\": \"week-strip\",\n * \"props\": {\n * \"content\": {\n * \"source\": { \"register\": \"dossiq\", \"schema\": \"case\", \"filter\": { \"assignee\": \"@me\" } },\n * \"dateField\": \"deadline\",\n * \"titleField\": \"title\",\n * \"metaFields\": [\"identifier\", \"caseType\"],\n * \"itemRoute\": \"CaseDetail\",\n * \"emptyText\": \"No deadlines\"\n * }\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnWeekStripWidget',\n\n\tcomponents: { NcLoadingIcon },\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot. The manifest-authored\n\t\t * `emptyText` and static item texts run through it. Identity by default.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's configuration. `days` is `5` (working days, the\n\t\t * default) or `7`. `source` names the OpenRegister register and schema,\n\t\t * with an optional `filter` (the shared @-token grammar) and `limit`.\n\t\t * `dateField` is the field the items are bucketed by. `titleField` and\n\t\t * `metaFields` pick what each item shows. `itemRoute` is the route name\n\t\t * an item links to (with the record id as `params.id`). `lateWhen` is a\n\t\t * `{ op, value }` rule on the days until the date (default: before\n\t\t * today), `lateField` a boolean field that marks a record late.\n\t\t * `items` are static entries `{ title, meta?, date, route?, href?, late? }`.\n\t\t * `emptyText` is the text of a day without items. `weekOffset` moves\n\t\t * the strip whole weeks from the current one. `inset: true` draws the\n\t\t * strip inside the board's inset (16px above, 24px at the sides, 22px\n\t\t * below) instead of from card edge to card edge. `maxPerDay` caps the\n\t\t * items a day shows; the rest sit behind \"+N more\", a link to\n\t\t * `moreRoute` (a route name or object) or, without one, a button that\n\t\t * opens the day in place. `moreLabel` replaces the text (`{count}` is\n\t\t * the number hidden).\n\t\t *\n\t\t * @type {{maxPerDay?: number, moreRoute?: (string|object), moreLabel?: string, days?: (5|7), inset?: boolean, source?: {register?: string, schema?: string, filter?: object, limit?: number}, dateField?: string, titleField?: string, metaFields?: Array<string>, itemRoute?: string, lateWhen?: {op?: string, value?: number}, lateField?: string, items?: Array<{title: string, meta?: string, date: string, route?: (string|object), href?: string, late?: boolean}>, emptyText?: string, weekOffset?: number}}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Translate function. Falls back to the injected `cnTranslate`. Pass it\n\t\t * when mounting the widget outside a CnAppRoot.\n\t\t *\n\t\t * @type {((key: string) => string)|null}\n\t\t */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The moment the strip treats as \"now\". Leave empty for the current\n\t\t * time; set it to render another week or to test.\n\t\t *\n\t\t * @type {Date|null}\n\t\t */\n\t\tnow: {\n\t\t\ttype: Date,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\t/** The days opened past `maxPerDay`, by day key. */\n\t\t\texpandedDays: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the widget draws the board's inset (`content.inset`). Off\n\t\t * by default, which keeps it edge to edge as before.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-strip-and-a-stacked-bar-can-take-the-board-inset\n\t\t * @return {boolean}\n\t\t */\n\t\tinset() {\n\t\t\treturn Boolean(this.content && this.content.inset === true)\n\t\t},\n\n\t\t/**\n\t\t * The translate function in use.\n\t\t *\n\t\t * @return {(key: string) => string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\teffectiveTranslate() {\n\t\t\treturn this.translate ?? this.cnTranslate\n\t\t},\n\n\t\t/**\n\t\t * The reference moment.\n\t\t *\n\t\t * @return {Date}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\treference() {\n\t\t\treturn this.now instanceof Date ? this.now : new Date()\n\t\t},\n\n\t\t/**\n\t\t * How many day columns render: 7, or 5 for anything else.\n\t\t *\n\t\t * @return {number}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tdayCount() {\n\t\t\treturn Number(this.content.days) === 7 ? 7 : 5\n\t\t},\n\n\t\t/**\n\t\t * The days of the strip, Monday first.\n\t\t *\n\t\t * @return {Array<Date>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tweekDays() {\n\t\t\tconst ref = this.reference\n\t\t\tconst offset = Number.isInteger(this.content.weekOffset) ? this.content.weekOffset : 0\n\t\t\tconst mondayDelta = (ref.getDay() + 6) % 7\n\t\t\tconst days = []\n\t\t\tfor (let i = 0; i < this.dayCount; i++) {\n\t\t\t\tdays.push(new Date(ref.getFullYear(), ref.getMonth(), ref.getDate() - mondayDelta + (offset * 7) + i))\n\t\t\t}\n\t\t\treturn days\n\t\t},\n\n\t\t/**\n\t\t * The field the items are bucketed by.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tdateField() {\n\t\t\treturn typeof this.content.dateField === 'string' ? this.content.dateField : ''\n\t\t},\n\n\t\t/**\n\t\t * Whether the items come from OpenRegister.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\thasSource() {\n\t\t\tconst s = this.content.source || {}\n\t\t\treturn Boolean(s.register && s.schema && this.dateField)\n\t\t},\n\n\t\t/**\n\t\t * Every item, from the source rows or the static list, in one shape.\n\t\t *\n\t\t * @return {Array<{key: string, title: string, meta: string, date: (Date|null), late: boolean, href: string, target: (object|string|null)}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tnormalizedItems() {\n\t\t\tif (this.hasSource) {\n\t\t\t\treturn this.rows.map((row, index) => this.itemFromRow(row, index))\n\t\t\t}\n\t\t\tconst items = Array.isArray(this.content.items) ? this.content.items : []\n\t\t\treturn items\n\t\t\t\t.filter((item) => item && typeof item === 'object')\n\t\t\t\t.map((item, index) => this.itemFromStatic(item, index))\n\t\t},\n\n\t\t/**\n\t\t * The columns: one per day, each with its items.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, today: boolean, items: Array<object>}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tdayColumns() {\n\t\t\tconst todayKey = dayKey(this.reference)\n\t\t\tconst buckets = {}\n\t\t\tfor (const item of this.normalizedItems) {\n\t\t\t\tif (item.date === null) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst key = dayKey(item.date)\n\t\t\t\t;(buckets[key] = buckets[key] || []).push(item)\n\t\t\t}\n\t\t\tconst formatter = this.dayFormatter\n\t\t\treturn this.weekDays.map((day) => {\n\t\t\t\tconst key = dayKey(day)\n\t\t\t\tconst items = buckets[key] || []\n\t\t\t\tconst expanded = this.expandedDays[key] === true\n\t\t\t\tconst cap = this.maxPerDay > 0 && !expanded ? this.maxPerDay : items.length\n\t\t\t\treturn {\n\t\t\t\t\tkey,\n\t\t\t\t\tlabel: formatter.format(day),\n\t\t\t\t\ttoday: key === todayKey,\n\t\t\t\t\titems,\n\t\t\t\t\tshown: items.slice(0, cap),\n\t\t\t\t\thidden: Math.max(0, items.length - cap),\n\t\t\t\t\texpanded: expanded && this.maxPerDay > 0 && items.length > this.maxPerDay,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The most items a day shows (`content.maxPerDay`), or 0 for no cap.\n\t\t *\n\t\t * @return {number}\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tmaxPerDay() {\n\t\t\tconst value = Number(this.content.maxPerDay)\n\t\t\treturn Number.isInteger(value) && value > 0 ? value : 0\n\t\t},\n\n\t\t/**\n\t\t * Where \"+N more\" leads (`content.moreRoute`), or null to open the day in place.\n\t\t *\n\t\t * @return {object|null}\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tmoreTarget() {\n\t\t\tconst route = this.content.moreRoute\n\t\t\tif (typeof route === 'string' && route !== '') {\n\t\t\t\treturn { name: route }\n\t\t\t}\n\t\t\treturn route && typeof route === 'object' ? route : null\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The href of the \"+N more\" link, '' without a target.\n\t\t *\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tmoreHref() {\n\t\t\treturn this.moreTarget ? (resolveHref(this.moreTarget, this.$router) || '') : ''\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The text that closes an opened day.\n\t\t *\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tlessLabel() {\n\t\t\treturn t('nextcloud-vue', 'Show less')\n\t\t},\n\n\t\t/**\n\t\t * Formats a day as a short weekday and a day number (\"Mon 5\").\n\t\t *\n\t\t * @return {Intl.DateTimeFormat}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tdayFormatter() {\n\t\t\tlet locale\n\t\t\ttry {\n\t\t\t\tlocale = getCanonicalLocale()\n\t\t\t} catch {\n\t\t\t\tlocale = undefined\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Intl.DateTimeFormat(locale || undefined, { weekday: 'short', day: 'numeric' })\n\t\t\t} catch {\n\t\t\t\treturn new Intl.DateTimeFormat(undefined, { weekday: 'short', day: 'numeric' })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The text of a day without items.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\temptyLabel() {\n\t\t\tconst text = this.content.emptyText\n\t\t\treturn (typeof text === 'string' && text !== '')\n\t\t\t\t? this.effectiveTranslate(text)\n\t\t\t\t: t('nextcloud-vue', 'Nothing planned')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The badge on today's column.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\ttodayLabel() {\n\t\t\treturn t('nextcloud-vue', 'today')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The marker on a late item.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tlateLabel() {\n\t\t\treturn t('nextcloud-vue', 'Late')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The accessible name of the scrolling region.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tregionLabel() {\n\t\t\treturn t('nextcloud-vue', 'This week, day by day')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The loading text.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading…')\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The text shown when the items could not be loaded.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\terrorLabel() {\n\t\t\treturn t('nextcloud-vue', 'The items for this week could not be loaded.')\n\t\t},\n\n\t\t/**\n\t\t * Changes whenever a new fetch is needed.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tsourceKey() {\n\t\t\treturn JSON.stringify({\n\t\t\t\ts: this.content.source || {},\n\t\t\t\tf: this.dateField,\n\t\t\t\td: this.weekDays.length > 0 ? [dayKey(this.weekDays[0]), this.weekDays.length] : [],\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\t/** @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget */\n\t\tsourceKey() {\n\t\t\tthis.fetchItems()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.fetchItems()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether a date counts as late under `content.lateWhen` (default:\n\t\t * before today).\n\t\t *\n\t\t * @param {Date|null} date The item's date.\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tisLateDate(date) {\n\t\t\tif (date === null) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst rule = (this.content.lateWhen && typeof this.content.lateWhen === 'object')\n\t\t\t\t? this.content.lateWhen\n\t\t\t\t: { op: 'lt', value: 0 }\n\t\t\treturn compareVisibleWhen(daysUntil(date, this.reference), rule.op || 'lt', rule.value ?? 0)\n\t\t},\n\n\t\t/**\n\t\t * One OpenRegister record as a strip item.\n\t\t *\n\t\t * @param {object} row The record.\n\t\t * @param {number} index Its position, used as a key of last resort.\n\t\t * @return {object} The item.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\titemFromRow(row, index) {\n\t\t\tconst c = this.content\n\t\t\tconst id = row && (row.id || (row['@self'] && row['@self'].id))\n\t\t\tconst date = parseDateValue(objectFieldValue(row, this.dateField))\n\t\t\tconst titleValue = c.titleField ? objectFieldValue(row, c.titleField) : undefined\n\t\t\tconst title = (titleValue !== undefined && titleValue !== null && titleValue !== '')\n\t\t\t\t? String(titleValue)\n\t\t\t\t: objectDisplayName(row)\n\t\t\tconst metaFields = Array.isArray(c.metaFields) ? c.metaFields : []\n\t\t\tconst meta = metaFields\n\t\t\t\t.map((field) => objectFieldValue(row, field))\n\t\t\t\t.filter((value) => value !== undefined && value !== null && value !== '' && typeof value !== 'object')\n\t\t\t\t.map(String)\n\t\t\t\t.join(' · ')\n\t\t\tconst flagged = c.lateField ? objectFieldValue(row, c.lateField) === true : false\n\t\t\tconst target = (c.itemRoute && id) ? { name: c.itemRoute, params: { id } } : null\n\t\t\treturn {\n\t\t\t\tkey: String(id || `row-${index}`),\n\t\t\t\ttitle,\n\t\t\t\tmeta,\n\t\t\t\tdate,\n\t\t\t\tlate: flagged || this.isLateDate(date),\n\t\t\t\ttarget,\n\t\t\t\thref: target ? resolveHref(target, this.$router) : '',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * One static manifest entry as a strip item.\n\t\t *\n\t\t * @param {object} item The entry.\n\t\t * @param {number} index Its position.\n\t\t * @return {object} The item.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\titemFromStatic(item, index) {\n\t\t\tconst date = parseDateValue(item.date)\n\t\t\tlet target = null\n\t\t\tlet href = ''\n\t\t\tif (item.route) {\n\t\t\t\ttarget = typeof item.route === 'string' ? { name: item.route } : item.route\n\t\t\t\thref = resolveHref(target, this.$router)\n\t\t\t} else if (typeof item.href === 'string' && item.href !== '') {\n\t\t\t\tconst safe = safeHref(item.href)\n\t\t\t\thref = safe === '#' ? '' : safe\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkey: `item-${index}`,\n\t\t\t\ttitle: typeof item.title === 'string' ? this.effectiveTranslate(item.title) : '',\n\t\t\t\tmeta: typeof item.meta === 'string' ? this.effectiveTranslate(item.meta) : '',\n\t\t\t\tdate,\n\t\t\t\tlate: item.late === true || (item.late !== false && this.isLateDate(date)),\n\t\t\t\ttarget,\n\t\t\t\thref,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Load the week's records in one request.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tasync fetchItems() {\n\t\t\tif (!this.hasSource) {\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst s = this.content.source\n\t\t\tconst requestKey = this.sourceKey\n\t\t\tconst first = this.weekDays[0]\n\t\t\tconst last = this.weekDays[this.weekDays.length - 1]\n\t\t\tconst dayAfter = new Date(last.getFullYear(), last.getMonth(), last.getDate() + 1)\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl(\n\t\t\t\t\t'/apps/openregister/api/objects/{register}/{schema}',\n\t\t\t\t\t{ register: s.register, schema: s.schema },\n\t\t\t\t)\n\t\t\t\tconst params = {\n\t\t\t\t\t_limit: Number.isInteger(s.limit) && s.limit > 0 ? s.limit : DEFAULT_LIMIT,\n\t\t\t\t\t[`_order[${this.dateField}]`]: 'asc',\n\t\t\t\t}\n\t\t\t\tconst filter = resolveFilterTokens(s.filter || {}, {})\n\t\t\t\tfor (const [k, v] of Object.entries(filter || {})) {\n\t\t\t\t\tif (v && typeof v === 'object') {\n\t\t\t\t\t\tfor (const [op, ov] of Object.entries(v)) {\n\t\t\t\t\t\t\tparams[`${k}[${op}]`] = ov\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (v !== '' && v !== null && v !== undefined) {\n\t\t\t\t\t\tparams[k] = v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// The week window goes last, so a manifest filter on the date\n\t\t\t\t// field cannot widen the strip beyond the days it shows.\n\t\t\t\tparams[`${this.dateField}[gte]`] = dayKey(first)\n\t\t\t\tparams[`${this.dateField}[lt]`] = dayKey(dayAfter)\n\t\t\t\tconst res = await axios.get(url, { params })\n\t\t\t\tif (requestKey !== this.sourceKey) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.rows = (res && res.data && Array.isArray(res.data.results)) ? res.data.results : []\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnWeekStripWidget] failed to load items:', e)\n\t\t\t\tthis.error = (e && e.message) || 'error'\n\t\t\t\tthis.rows = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The text of the \"+N more\" control: `content.moreLabel` with `{count}`\n\t\t * filled, else \"+N more\" from the library's catalogue.\n\t\t *\n\t\t * @param {number} count The number of hidden items.\n\t\t * @return {string}\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tmoreLabel(count) {\n\t\t\tconst own = this.content.moreLabel\n\t\t\tif (typeof own === 'string' && own !== '') {\n\t\t\t\treturn this.effectiveTranslate(own).replace(/\\{count\\}/g, String(count))\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '+{count} more', { count })\n\t\t},\n\n\t\t/**\n\t\t * Open or close a day past its cap.\n\t\t *\n\t\t * @param {string} key The day key.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\ttoggleDay(key) {\n\t\t\tthis.expandedDays = { ...this.expandedDays, [key]: this.expandedDays[key] !== true }\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on the \"+N more\" link.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/screens-week-strip-max-per-day/specs/week-strip-max-per-day/spec.md#requirement-a-day-shows-at-most-maxperday-items\n\t\t */\n\t\tonMoreClick(event) {\n\t\t\tif (this.moreTarget) {\n\t\t\t\tfollowLinkClick(event, this.moreTarget, this.$router)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on an in-app item; the browser handles the rest.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @param {object} item The clicked item.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget\n\t\t */\n\t\tonItemClick(event, item) {\n\t\t\tif (item.target) {\n\t\t\t\tfollowLinkClick(event, item.target, this.$router)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-week-strip {\n\twidth: 100%;\n}\n\n/* `content.inset: true`: the board's inset inside the card (zuiddrecht-pixel-\n gaps-3). A dashboard renders its widgets flush, so without it the strip ran\n from card edge to card edge. Theme hook: --cn-widget-board-inset. */\n.cn-week-strip--inset {\n\tbox-sizing: border-box;\n\tpadding: var(--cn-widget-board-inset, 16px 24px 22px);\n}\n\n.cn-week-strip__state {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin: 0;\n\tpadding: 12px 4px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-week-strip__scroll {\n\toverflow-x: auto;\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-week-strip__scroll:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-week-strip__days {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(var(--cn-week-strip-columns, 5), minmax(128px, 1fr));\n\tgap: 10px;\n\t/* 5 columns of 128px plus gaps: below this the region scrolls sideways. */\n\tmin-width: calc(var(--cn-week-strip-columns, 5) * 136px);\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-week-strip__day {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmin-width: 0;\n\tpadding: 10px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-week-strip__day--today {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-week-strip__head {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 6px;\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-week-strip__today {\n\tpadding: 1px 8px;\n\tborder-radius: var(--border-radius-pill, 10px);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n\tfont-size: 0.85em;\n\tfont-weight: 700;\n}\n\n.cn-week-strip__items {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-week-strip__item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 10px 12px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-week-strip__item--link:hover {\n\tborder-color: var(--color-primary-element);\n}\n\n.cn-week-strip__item--link:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n/* The edge is drawn inside the box, so a late item is as wide as the others. */\n.cn-week-strip__item--late {\n\tborder-color: var(--color-element-error, var(--color-error));\n\tbox-shadow: inset 3px 0 0 0 var(--color-element-error, var(--color-error));\n}\n\n.cn-week-strip__title {\n\tfont-size: 0.95em;\n\tline-height: 1.3;\n\toverflow-wrap: anywhere;\n}\n\n.cn-week-strip__meta {\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow-wrap: anywhere;\n}\n\n.cn-week-strip__late {\n\tfont-size: 0.8em;\n\tfont-weight: 600;\n\tcolor: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-week-strip__empty {\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* \"+N more\" under a capped day: a quiet text control, 24px high to click. */\n.cn-week-strip__more {\n\talign-self: flex-start;\n\tmin-height: 24px;\n\tmargin: 0;\n\tpadding: 2px 4px;\n\tborder: 0;\n\tborder-radius: var(--border-radius, 4px);\n\tbackground: transparent;\n\tcolor: var(--color-primary-element);\n\tfont: inherit;\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.cn-week-strip__more:hover {\n\ttext-decoration: underline;\n}\n\n.cn-week-strip__more:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|