@conduction/nextcloud-vue 2.62.0 → 2.64.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/CnAdminActionCard/CnAdminActionCard.vue.js +10 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js.map +1 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js +189 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js.map +1 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js +72 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js.map +1 -0
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +425 -39
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +119 -42
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +186 -9
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +65 -42
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +61 -16
- 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/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js +20 -0
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +3 -2
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +33 -2
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +53 -37
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +29 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +152 -135
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js +10 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +293 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +189 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +253 -9
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +126 -50
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +66 -14
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +23 -14
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js +3 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +234 -9
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +96 -57
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +81 -4
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +22 -5
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +81 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +7 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/selfModeActions.js +3 -3
- package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +41 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +8 -3
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js +121 -12
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js.map +1 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js +3 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +326 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +112 -26
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +63 -2
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +96 -37
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/stagesModel.js +5 -2
- package/dist/esm/components/CnStagesWidget/stagesModel.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +52 -2
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +6 -6
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +14 -2
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +2 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +10 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +337 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +102 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js +34 -0
- package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js.map +1 -0
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +131 -8
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js +40 -38
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +8 -6
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +73 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +14 -2
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +19 -0
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
- package/dist/esm/composables/useDependencyCheck.js +65 -0
- package/dist/esm/composables/useDependencyCheck.js.map +1 -0
- package/dist/esm/composables/useWalkthrough.js +218 -6
- package/dist/esm/composables/useWalkthrough.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +45 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +45 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +780 -81
- package/dist/esm/store/useObjectStore.js +8 -0
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/columnFilters.js +311 -0
- package/dist/esm/utils/columnFilters.js.map +1 -0
- package/dist/esm/utils/groupAutocomplete.js +123 -0
- package/dist/esm/utils/groupAutocomplete.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +2 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/pickerOptions.js +198 -0
- package/dist/esm/utils/pickerOptions.js.map +1 -0
- package/dist/esm/utils/schema.js +83 -7
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/schemaRefSlug.js +19 -3
- package/dist/esm/utils/schemaRefSlug.js.map +1 -1
- package/dist/esm/utils/timelineEvents.js +174 -0
- package/dist/esm/utils/timelineEvents.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/walkthroughSignals.js +88 -0
- package/dist/esm/utils/walkthroughSignals.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +13088 -7937
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +780 -81
- package/l10n/en.json +45 -0
- package/l10n/nl.json +45 -0
- package/package.json +1 -1
- package/src/components/CnAdminActionCard/CnAdminActionCard.vue +234 -0
- package/src/components/CnAdminActionCard/index.js +4 -0
- package/src/components/CnAppNav/CnAppNav.vue +568 -43
- package/src/components/CnAppRoot/CnAppRoot.md +4 -0
- package/src/components/CnAppRoot/CnAppRoot.vue +223 -16
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +61 -16
- package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +22 -1
- package/src/components/CnCellRenderer/CnCellRenderer.vue +33 -2
- package/src/components/CnChoiceCards/CnChoiceCards.vue +56 -31
- package/src/components/CnDashboardPage/CnDashboardPage.vue +45 -4
- package/src/components/CnDataTable/CnColumnFilterPopover.vue +524 -0
- package/src/components/CnDataTable/CnDataTable.md +3 -0
- package/src/components/CnDataTable/CnDataTable.vue +305 -5
- package/src/components/CnDetailPage/CnDetailPage.md +4 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +60 -1
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
- package/src/components/CnFieldHelper/CnFieldHelper.vue +4 -2
- package/src/components/CnFormDialog/CnFormDialog.vue +290 -31
- package/src/components/CnHeaderWidget/CnHeaderWidget.vue +116 -6
- package/src/components/CnIndexPage/CnIndexPage.vue +87 -4
- package/src/components/CnIndexPage/selfModeActions.js +3 -3
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +46 -2
- package/src/components/CnResourceSelect/CnResourceSelect.vue +123 -12
- package/src/components/CnSetupWizard/CnSetupWizard.vue +392 -3
- package/src/components/CnStagesWidget/CnStagesWidget.vue +180 -2
- package/src/components/CnStagesWidget/stagesModel.js +5 -2
- package/src/components/CnStatWidget/CnStatWidget.vue +57 -3
- package/src/components/CnTabsWidget/CnTabsWidget.vue +15 -2
- package/src/components/CnTimelineWidget/CnTimelineWidget.vue +477 -0
- package/src/components/CnTimelineWidget/dashboardRegistration.js +32 -0
- package/src/components/CnTimelineWidget/index.js +4 -0
- package/src/components/CnWalkthrough/CnWalkthrough.vue +143 -10
- package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +4 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +107 -1
- package/src/components/CnWizardDialog/CnWizardDialog.vue +20 -1
- package/src/components/index.js +2 -0
- package/src/composables/useDependencyCheck.js +61 -0
- package/src/composables/useWalkthrough.js +217 -5
- package/src/css/dashboard.css +49 -0
- package/src/css/detail-page.css +29 -4
- package/src/css/kpi-card.css +24 -1
- package/src/css/table.css +99 -6
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +135 -3
- package/src/store/useObjectStore.js +8 -0
- package/src/utils/columnFilters.js +324 -0
- package/src/utils/groupAutocomplete.js +119 -0
- package/src/utils/libraryWidgetKeys.js +2 -0
- package/src/utils/pickerOptions.js +194 -0
- package/src/utils/schema.js +83 -7
- package/src/utils/schemaRefSlug.js +19 -3
- package/src/utils/timelineEvents.js +171 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
- package/src/utils/walkthroughSignals.js +94 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, toDisplayString, createCommentVNode, createVNode, createElementVNode, Fragment, createBlock, withCtx, createTextVNode, renderList, normalizeClass } from 'vue';
|
|
2
|
+
|
|
3
|
+
const _hoisted_1 = ["aria-busy"];
|
|
4
|
+
const _hoisted_2 = {
|
|
5
|
+
key: 0,
|
|
6
|
+
class: "cn-timeline-widget__title"
|
|
7
|
+
};
|
|
8
|
+
const _hoisted_3 = {
|
|
9
|
+
key: 1,
|
|
10
|
+
class: "cn-timeline-widget__loading"
|
|
11
|
+
};
|
|
12
|
+
const _hoisted_4 = {
|
|
13
|
+
key: 1,
|
|
14
|
+
class: "cn-timeline-widget__empty",
|
|
15
|
+
"data-testid": "cn-timeline-widget-empty"
|
|
16
|
+
};
|
|
17
|
+
const _hoisted_5 = {
|
|
18
|
+
key: 2,
|
|
19
|
+
class: "cn-timeline-widget__list"
|
|
20
|
+
};
|
|
21
|
+
const _hoisted_6 = ["data-source"];
|
|
22
|
+
const _hoisted_7 = { class: "cn-timeline-widget__body" };
|
|
23
|
+
const _hoisted_8 = ["datetime"];
|
|
24
|
+
const _hoisted_9 = {
|
|
25
|
+
key: 0,
|
|
26
|
+
class: "cn-timeline-widget__badge"
|
|
27
|
+
};
|
|
28
|
+
const _hoisted_10 = { class: "cn-timeline-widget__label" };
|
|
29
|
+
const _hoisted_11 = {
|
|
30
|
+
key: 1,
|
|
31
|
+
class: "cn-timeline-widget__detail"
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
35
|
+
const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
|
|
36
|
+
const _component_NcNoteCard = resolveComponent("NcNoteCard");
|
|
37
|
+
|
|
38
|
+
return (openBlock(), createElementBlock("section", {
|
|
39
|
+
class: "cn-timeline-widget",
|
|
40
|
+
"data-testid": "cn-timeline-widget",
|
|
41
|
+
"aria-busy": $data.loading ? 'true' : 'false'
|
|
42
|
+
}, [
|
|
43
|
+
($options.resolvedTitle)
|
|
44
|
+
? (openBlock(), createElementBlock("h3", _hoisted_2, toDisplayString($options.resolvedTitle), 1 /* TEXT */))
|
|
45
|
+
: createCommentVNode("v-if", true),
|
|
46
|
+
($data.loading && $options.events.length === 0)
|
|
47
|
+
? (openBlock(), createElementBlock("div", _hoisted_3, [
|
|
48
|
+
createVNode(_component_NcLoadingIcon, {
|
|
49
|
+
size: 24,
|
|
50
|
+
"aria-hidden": "true"
|
|
51
|
+
}),
|
|
52
|
+
createElementVNode("span", null, toDisplayString($options.loadingLabel), 1 /* TEXT */)
|
|
53
|
+
]))
|
|
54
|
+
: (openBlock(), createElementBlock(Fragment, { key: 2 }, [
|
|
55
|
+
($data.failedSources.length > 0)
|
|
56
|
+
? (openBlock(), createBlock(_component_NcNoteCard, {
|
|
57
|
+
key: 0,
|
|
58
|
+
type: "warning",
|
|
59
|
+
"data-testid": "cn-timeline-widget-error"
|
|
60
|
+
}, {
|
|
61
|
+
default: withCtx(() => [
|
|
62
|
+
createTextVNode(toDisplayString($options.failedText), 1 /* TEXT */)
|
|
63
|
+
]),
|
|
64
|
+
_: 1 /* STABLE */
|
|
65
|
+
}))
|
|
66
|
+
: createCommentVNode("v-if", true),
|
|
67
|
+
($options.events.length === 0)
|
|
68
|
+
? (openBlock(), createElementBlock("p", _hoisted_4, toDisplayString($options.emptyLabel), 1 /* TEXT */))
|
|
69
|
+
: (openBlock(), createElementBlock("ol", _hoisted_5, [
|
|
70
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($options.events, (event) => {
|
|
71
|
+
return (openBlock(), createElementBlock("li", {
|
|
72
|
+
key: event.id,
|
|
73
|
+
class: normalizeClass(["cn-timeline-widget__event", { 'cn-timeline-widget__event--upcoming': event.upcoming }]),
|
|
74
|
+
"data-source": event.source,
|
|
75
|
+
"data-testid": "cn-timeline-widget-event"
|
|
76
|
+
}, [
|
|
77
|
+
_cache[0] || (_cache[0] = createElementVNode("span", {
|
|
78
|
+
class: "cn-timeline-widget__dot",
|
|
79
|
+
"aria-hidden": "true"
|
|
80
|
+
}, null, -1 /* CACHED */)),
|
|
81
|
+
createElementVNode("div", _hoisted_7, [
|
|
82
|
+
createElementVNode("time", {
|
|
83
|
+
class: "cn-timeline-widget__when",
|
|
84
|
+
datetime: event.at.toISOString()
|
|
85
|
+
}, toDisplayString($options.formatMoment(event)), 9 /* TEXT, PROPS */, _hoisted_8),
|
|
86
|
+
(event.upcoming)
|
|
87
|
+
? (openBlock(), createElementBlock("span", _hoisted_9, toDisplayString($options.upcomingLabel), 1 /* TEXT */))
|
|
88
|
+
: createCommentVNode("v-if", true),
|
|
89
|
+
createElementVNode("p", _hoisted_10, toDisplayString($options.translate(event.label)), 1 /* TEXT */),
|
|
90
|
+
(event.detail)
|
|
91
|
+
? (openBlock(), createElementBlock("p", _hoisted_11, toDisplayString(event.detail), 1 /* TEXT */))
|
|
92
|
+
: createCommentVNode("v-if", true)
|
|
93
|
+
])
|
|
94
|
+
], 10 /* CLASS, PROPS */, _hoisted_6))
|
|
95
|
+
}), 128 /* KEYED_FRAGMENT */))
|
|
96
|
+
]))
|
|
97
|
+
], 64 /* STABLE_FRAGMENT */))
|
|
98
|
+
], 8 /* PROPS */, _hoisted_1))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export { render };
|
|
102
|
+
//# sourceMappingURL=CnTimelineWidget.vue3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnTimelineWidget.vue3.js","sources":["../../../../src/components/CnTimelineWidget/CnTimelineWidget.vue?vue&type=template&id=621fcccf&lang.js"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<section class=\"cn-timeline-widget\" data-testid=\"cn-timeline-widget\" :aria-busy=\"loading ? 'true' : 'false'\">\n\t\t<h3 v-if=\"resolvedTitle\" class=\"cn-timeline-widget__title\">\n\t\t\t{{ resolvedTitle }}\n\t\t</h3>\n\n\t\t<div v-if=\"loading && events.length === 0\" class=\"cn-timeline-widget__loading\">\n\t\t\t<NcLoadingIcon :size=\"24\" aria-hidden=\"true\" />\n\t\t\t<span>{{ loadingLabel }}</span>\n\t\t</div>\n\n\t\t<template v-else>\n\t\t\t<!-- A failed read is not an empty history: say which part is missing. -->\n\t\t\t<NcNoteCard v-if=\"failedSources.length > 0\" type=\"warning\" data-testid=\"cn-timeline-widget-error\">\n\t\t\t\t{{ failedText }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"events.length === 0\" class=\"cn-timeline-widget__empty\" data-testid=\"cn-timeline-widget-empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</p>\n\n\t\t\t<ol v-else class=\"cn-timeline-widget__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"event in events\"\n\t\t\t\t\t:key=\"event.id\"\n\t\t\t\t\tclass=\"cn-timeline-widget__event\"\n\t\t\t\t\t:class=\"{ 'cn-timeline-widget__event--upcoming': event.upcoming }\"\n\t\t\t\t\t:data-source=\"event.source\"\n\t\t\t\t\tdata-testid=\"cn-timeline-widget-event\">\n\t\t\t\t\t<span class=\"cn-timeline-widget__dot\" aria-hidden=\"true\" />\n\t\t\t\t\t<div class=\"cn-timeline-widget__body\">\n\t\t\t\t\t\t<time class=\"cn-timeline-widget__when\" :datetime=\"event.at.toISOString()\">\n\t\t\t\t\t\t\t{{ formatMoment(event) }}\n\t\t\t\t\t\t</time>\n\t\t\t\t\t\t<span v-if=\"event.upcoming\" class=\"cn-timeline-widget__badge\">\n\t\t\t\t\t\t\t{{ upcomingLabel }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<p class=\"cn-timeline-widget__label\">\n\t\t\t\t\t\t\t{{ translate(event.label) }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p v-if=\"event.detail\" class=\"cn-timeline-widget__detail\">\n\t\t\t\t\t\t\t{{ event.detail }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ol>\n\t\t</template>\n\t</section>\n</template>\n\n<script>\nimport { getLanguage, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { auditEvents, fieldEvents, relatedEvents, sortEvents, timelineEntryEvents } from '../../utils/timelineEvents.js'\n\n/**\n * CnTimelineWidget — an object's dated events in time order (`timeline`\n * widget type for detail pages).\n *\n * Generalises dossiq's case timeline. It merges up to four sources, all\n * optional, into one list:\n *\n * - `fields`: the object's own date properties, `[{ field, label }]`. A\n * dotted path reaches metadata, such as `@self.created`.\n * - `related`: related objects, `[{ register?, schema, field, dateField?,\n * label, titleField? }]`. Rows of `schema` whose `field` holds this object's\n * id become events at their `dateField` (default `@self.created`).\n * - `auditTrail: true`: the object's audit trail, one event per change.\n * - `timeline: true`: OpenRegister's timeline of notes, calls and messages.\n *\n * Oldest first by default (`order: 'desc'` flips it). A moment after now is\n * marked upcoming, in words as well as style.\n *\n * ```json\n * { \"type\": \"timeline\", \"content\": {\n * \"title\": \"Timeline\",\n * \"fields\": [\n * { \"field\": \"@self.created\", \"label\": \"Booking created\" },\n * { \"field\": \"depositClearedAt\", \"label\": \"Deposit cleared\" },\n * { \"field\": \"confirmationSentAt\", \"label\": \"Confirmation mail sent\" },\n * { \"field\": \"startsAt\", \"label\": \"Starts\" },\n * { \"field\": \"endsAt\", \"label\": \"Ends\" }\n * ] } }\n * ```\n *\n * The object comes from the detail page's context, like `audit-trail`:\n * explicit props win, then the injects, then `content`.\n *\n * @spec openspec/changes/timeline-widget/specs/timeline-widget/spec.md#requirement-a-timeline-widget-shows-an-objects-dated-events-in-order\n */\nexport default {\n\tname: 'CnTimelineWidget',\n\n\tcomponents: {\n\t\tNcLoadingIcon,\n\t\tNcNoteCard,\n\t},\n\n\tinject: {\n\t\t/** Detail-page object context from CnDetailPage (a ref or a raw bag). */\n\t\tcnObjectContext: { default: null },\n\t\t/** Detail object context holder from CnPageRenderer (`{ value: { objectData, ... } }`). */\n\t\tcnDetailObjectContext: { default: null },\n\t\t/** Consumer translation function; labels are authored in English. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** Register slug; object context spread as props by CnWidgetGrid. */\n\t\tregister: { type: String, default: '' },\n\t\t/**\n\t\t * Schema slug, or the schema object the detail context supplies.\n\t\t *\n\t\t * @type {string|object}\n\t\t */\n\t\tschema: { type: [String, Object], default: '' },\n\t\t/** The object's id. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** The object itself, when the host already has it. */\n\t\tobject: { type: Object, default: null },\n\t\t/** Card title override. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Stored widget content: `{ title, fields, related, auditTrail, timeline, order }`. */\n\t\tcontent: { type: Object, default: () => ({}) },\n\t\t/** OpenRegister API base. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: false,\n\t\t\trelatedRows: {},\n\t\t\tauditEntries: [],\n\t\t\ttimelineEntries: [],\n\t\t\tfailedSources: [],\n\t\t\tfetchedObject: null,\n\t\t\tloadSeq: 0,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tctx() {\n\t\t\tconst raw = this.cnObjectContext\n\t\t\tconst inj = raw && (raw.value !== undefined ? raw.value : raw)\n\t\t\tconst holder = this.cnDetailObjectContext && this.cnDetailObjectContext.value\n\t\t\treturn inj || holder || {}\n\t\t},\n\n\t\tresolvedObjectId() {\n\t\t\treturn this.objectId || this.ctx.objectId || this.content.objectId || ''\n\t\t},\n\n\t\tresolvedRegister() {\n\t\t\treturn this.register || this.ctx.register || this.content.register || ''\n\t\t},\n\n\t\tresolvedSchema() {\n\t\t\tconst s = this.schema || this.ctx.schema || this.content.schema || ''\n\t\t\treturn typeof s === 'string' ? s : (s && (s.slug || s.name || s.id)) || ''\n\t\t},\n\n\t\t/**\n\t\t * The object: explicit prop, then the page context, then a fetch of\n\t\t * our own when only the id is known.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tresolvedObject() {\n\t\t\treturn this.object || this.ctx.object || this.ctx.objectData || this.fetchedObject || null\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\tconst raw = this.title || this.content.title || ''\n\t\t\treturn raw ? this.translate(raw) : ''\n\t\t},\n\n\t\tfieldsConfig() {\n\t\t\treturn Array.isArray(this.content.fields) ? this.content.fields : []\n\t\t},\n\n\t\trelatedConfig() {\n\t\t\treturn Array.isArray(this.content.related) ? this.content.related.filter((r) => r && r.schema && r.field) : []\n\t\t},\n\n\t\t/**\n\t\t * Every event from every source, in time order.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tevents() {\n\t\t\tconst now = new Date()\n\t\t\tconst all = [\n\t\t\t\t...fieldEvents(this.resolvedObject || {}, this.fieldsConfig, now),\n\t\t\t\t...this.relatedConfig.flatMap((cfg, i) => relatedEvents(this.relatedRows[i] || [], cfg, now)),\n\t\t\t\t...auditEvents(this.auditEntries, this.describeAudit, now),\n\t\t\t\t...timelineEntryEvents(this.timelineEntries, t('nextcloud-vue', 'Note'), now),\n\t\t\t]\n\t\t\treturn sortEvents(all, this.content.order === 'desc' ? 'desc' : 'asc')\n\t\t},\n\n\t\t/**\n\t\t * A signature of what to fetch, so a change refetches once.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tloadKey() {\n\t\t\treturn JSON.stringify({\n\t\t\t\tid: this.resolvedObjectId,\n\t\t\t\tregister: this.resolvedRegister,\n\t\t\t\tschema: this.resolvedSchema,\n\t\t\t\trelated: this.relatedConfig,\n\t\t\t\taudit: this.content.auditTrail === true,\n\t\t\t\ttimeline: this.content.timeline === true,\n\t\t\t\tneedObject: this.fieldsConfig.length > 0 && !(this.object || this.ctx.object || this.ctx.objectData),\n\t\t\t})\n\t\t},\n\n\t\tfailedText() {\n\t\t\treturn t('nextcloud-vue', 'Part of the timeline could not load: {parts}.', {\n\t\t\t\tparts: this.failedSources.map((s) => this.sourceName(s)).join(', '),\n\t\t\t})\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading…')\n\t\t},\n\n\t\temptyLabel() {\n\t\t\treturn t('nextcloud-vue', 'Nothing has happened yet.')\n\t\t},\n\n\t\tupcomingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Upcoming')\n\t\t},\n\t},\n\n\twatch: {\n\t\tloadKey: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.load()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\ttranslate(text) {\n\t\t\treturn text ? this.cnTranslate(text) : ''\n\t\t},\n\n\t\tsourceName(source) {\n\t\t\tif (source === 'audit') {\n\t\t\t\treturn t('nextcloud-vue', 'changes')\n\t\t\t}\n\t\t\tif (source === 'timeline') {\n\t\t\t\treturn t('nextcloud-vue', 'notes and messages')\n\t\t\t}\n\t\t\tif (source === 'object') {\n\t\t\t\treturn t('nextcloud-vue', 'dates')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'related items')\n\t\t},\n\n\t\t/**\n\t\t * The label of an audit event, from its action and who did it.\n\t\t *\n\t\t * @param {string} action The audit action (create, update, delete).\n\t\t * @param {string} actor Who did it.\n\t\t * @return {string}\n\t\t */\n\t\tdescribeAudit(action, actor) {\n\t\t\tconst known = {\n\t\t\t\tcreate: t('nextcloud-vue', 'Created'),\n\t\t\t\tupdate: t('nextcloud-vue', 'Updated'),\n\t\t\t\tdelete: t('nextcloud-vue', 'Deleted'),\n\t\t\t}\n\t\t\tconst what = known[action] || action || t('nextcloud-vue', 'Changed')\n\t\t\treturn actor ? t('nextcloud-vue', '{action} by {actor}', { action: what, actor }) : what\n\t\t},\n\n\t\t/**\n\t\t * Show a moment in the reader's language: a date for a calendar day,\n\t\t * date and time otherwise.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {string}\n\t\t */\n\t\tformatMoment(event) {\n\t\t\tconst locale = (typeof getLanguage === 'function' && getLanguage()) || undefined\n\t\t\tconst options = event.dateOnly\n\t\t\t\t? { dateStyle: 'medium' }\n\t\t\t\t: { dateStyle: 'medium', timeStyle: 'short' }\n\t\t\ttry {\n\t\t\t\treturn new Intl.DateTimeFormat(locale, options).format(event.at)\n\t\t\t} catch {\n\t\t\t\treturn event.at.toLocaleString()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fetch every configured source. Each one fails on its own, so one\n\t\t * unreachable source never hides the others.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/timeline-widget/specs/timeline-widget/spec.md#requirement-a-timeline-widget-shows-an-objects-dated-events-in-order\n\t\t */\n\t\tasync load() {\n\t\t\tconst id = this.resolvedObjectId\n\t\t\tconst register = this.resolvedRegister\n\t\t\tconst schema = this.resolvedSchema\n\t\t\tconst seq = ++this.loadSeq\n\t\t\tif (!id || !register || !schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\timport('@nextcloud/router'),\n\t\t\t])\n\t\t\tconst base = `${this.apiBase}/objects/${register}/${schema}/${id}`\n\t\t\tconst failed = []\n\t\t\tconst jobs = []\n\n\t\t\tif (this.fieldsConfig.length > 0 && !(this.object || this.ctx.object || this.ctx.objectData)) {\n\t\t\t\tjobs.push(axios.get(generateUrl(base))\n\t\t\t\t\t.then((r) => {\n\t\t\t\t\t\tthis.fetchedObject = (r && r.data) || null\n\t\t\t\t\t})\n\t\t\t\t\t.catch(() => failed.push('object')))\n\t\t\t}\n\n\t\t\tconst rows = {}\n\t\t\tthis.relatedConfig.forEach((cfg, i) => {\n\t\t\t\tconst url = generateUrl(`${this.apiBase}/objects/${cfg.register || register}/${cfg.schema}`)\n\t\t\t\tjobs.push(axios.get(url, { params: { [cfg.field]: id, _limit: cfg.limit || 50 } })\n\t\t\t\t\t.then((r) => {\n\t\t\t\t\t\tconst data = r && r.data\n\t\t\t\t\t\trows[i] = (data && (data.results || data)) || []\n\t\t\t\t\t})\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\trows[i] = []\n\t\t\t\t\t\tif (!failed.includes('related')) {\n\t\t\t\t\t\t\tfailed.push('related')\n\t\t\t\t\t\t}\n\t\t\t\t\t}))\n\t\t\t})\n\n\t\t\tif (this.content.auditTrail === true) {\n\t\t\t\tjobs.push(axios.get(generateUrl(`${base}/audit-trail`), { params: { limit: this.content.auditLimit || 50 } })\n\t\t\t\t\t.then((r) => {\n\t\t\t\t\t\tconst data = r && r.data\n\t\t\t\t\t\tthis.auditEntries = (data && (data.results || data)) || []\n\t\t\t\t\t})\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\tthis.auditEntries = []\n\t\t\t\t\t\tfailed.push('audit')\n\t\t\t\t\t}))\n\t\t\t}\n\n\t\t\tif (this.content.timeline === true) {\n\t\t\t\tjobs.push(axios.get(generateUrl(`${base}/timeline`))\n\t\t\t\t\t.then((r) => {\n\t\t\t\t\t\tconst data = r && r.data\n\t\t\t\t\t\tthis.timelineEntries = (data && (data.results || data)) || []\n\t\t\t\t\t})\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\tthis.timelineEntries = []\n\t\t\t\t\t\tfailed.push('timeline')\n\t\t\t\t\t}))\n\t\t\t}\n\n\t\t\tif (jobs.length === 0) {\n\t\t\t\tthis.failedSources = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tawait Promise.all(jobs)\n\t\t\tif (seq !== this.loadSeq) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.relatedRows = rows\n\t\t\tthis.failedSources = failed\n\t\t\tthis.loading = false\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-timeline-widget {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n}\n\n.cn-timeline-widget__title {\n\tmargin: 0;\n\tfont-size: var(--font-size-large, 1.1em);\n}\n\n.cn-timeline-widget__loading {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-timeline-widget__empty {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-timeline-widget__list {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-timeline-widget__event {\n\tposition: relative;\n\tdisplay: flex;\n\tgap: 12px;\n\tpadding-block-end: 12px;\n}\n\n/* The line between dots: drawn from each dot down to the next event. */\n.cn-timeline-widget__event:not(:last-child)::before {\n\tcontent: '';\n\tposition: absolute;\n\tinset-block: 14px 0;\n\tinset-inline-start: 5px;\n\tborder-inline-start: 2px solid var(--color-border-dark);\n}\n\n.cn-timeline-widget__dot {\n\tflex: 0 0 auto;\n\twidth: 12px;\n\theight: 12px;\n\tmargin-block-start: 4px;\n\tborder-radius: 50%;\n\tbackground-color: var(--color-primary-element);\n}\n\n/* Upcoming: a hollow dot, plus the word \"Upcoming\" beside the date. */\n.cn-timeline-widget__event--upcoming .cn-timeline-widget__dot {\n\tbox-sizing: border-box;\n\tborder: 2px solid var(--color-primary-element);\n\tbackground-color: var(--color-main-background);\n}\n\n.cn-timeline-widget__body {\n\tmin-width: 0;\n}\n\n.cn-timeline-widget__when {\n\tfont-size: var(--font-size-small, 13px);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-timeline-widget__badge {\n\tmargin-inline-start: 8px;\n\tfont-size: var(--font-size-small, 13px);\n\tfont-weight: bold;\n\tcolor: var(--color-main-text);\n}\n\n.cn-timeline-widget__label {\n\tmargin: 0;\n\tfont-weight: bold;\n}\n\n.cn-timeline-widget__detail {\n\tmargin: 0;\n\toverflow-wrap: anywhere;\n}\n</style>\n"],"names":["_createElementBlock","_toDisplayString","_createVNode","_createBlock","_openBlock","_Fragment","_renderList","_normalizeClass","_createElementVNode"],"mappings":";;;;;AAMM,EAAA,KAAA,EAAA,2BAAA;;;;AAI2B,EAAA,KAAA,EAAA,6BAAA;;;;AAW3B,EAAA,KAAA,EAAA,2BAAA;AAAA,EAAA,aAAA,EAAA,0BAAA;;;;AAIQ,EAAA,KAAK,EAAE,0BAAM;;;AASjB,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,0BAAe,GAAA;;;;AAInB,EAAA,KAAA,EAAA,2BAAA;;AAGH,MAAA,WAAA,GAAA,EAAA,KAAA,EAAA,2BAAA,GAAA;;;AAGA,EAAA,KAAA,EAAA,4BAAA;;;;;;;uBAvCNA;AAAO,IAAA,KAAK,EAAC,oBAAK;AAAA,IAAA,aAAA,EAAA,oBAAA;AAAA,IAAA,WAAA,EAAA,KAAA,CAAA,OAAA,GAAA,MAAA,GAAA,OAAA;;KACf,QAAA,CAAA,aAAA;sBAAJA,kBAEK,CAAA,IAAA,EAFL,UAEK,EAAAC,eAAA;;KAEM,KAAO,CAAA,OAAA,IAAK,QAAK,CAAA,MAAA,CAAC,MAAE,KAAA,CAAA;yCAGzB,iBAAA,EAAA;UAFLC,WAAI,CAAA,wBAAA,EAAA;AAAA,YAAA,IAAA,EAAA,EAAA;AAAA,YAAA,aAAA,EAAA,MAAA;;;;sBAIJF;AAEY,UAAA,CAAA,KAAA,CAAA,aAAA,CAAA,MAAA,GAAA,CAAA;4BAAZG,WAEa,CAAA,qBAAA,EAAA;;AAFD,gBAAA,IAAA,EAAA,SAAA;AAAA,gBAAA,aAAA,EAAA,0BAAA;;;;;;;;AAIT,UAAA,CAAA,QAAA,CAAA,MAAA,CAAA,MAAA,KAAA,CAAA;4BAAHH,kBAEI,CAAA,GAAA,EAFJ,UAEI,EAAAC,eAAA;AAEF,eAAAG,SAAA,EAAA,EAAAJ,kBAAA,CAwBG,MAxBH,UAwBG,EAAA;kCAvBHA,kBAsBI,CAAAK,QAAA,EAAA,IAAA,EAAAC,UAAA,CArBY,QAAM,CAAA,MAAA,EAAA,CAAf,KAAK,KAAA;uCADZN,kBAsBI,CAAA,IAAA,EAAA;oBApBH,GAAG,EAAE,KAAK,CAAC,EAAE;AACd,oBAAA,KAAK,EAACO,cAAA,CAAA,CAAA,2BAAoB,EACY,EAAA,qCAAA,EAAA,KAAA,CAAA,QAAA,EAAA,CAAA,CAAA;oBACrC,aAAW,EAAE,KAAK,CAAC,MAAM;AAC1B,oBAAA,aAAW,EAAC,0BAAyB;;8CACpCC,kBAA0D,CAAA,MAAA,EAAA;AAAtD,sBAAA,KAAM,EAAC,yBAAyB;AAAC,sBAAA,aAAW,EAAC,MAAM;;uCActD,iBAAA,EAAA;sBAZDA,kBAEO,CAAA,MAAA,EAAA;AAFD,wBAAA,KAAA,EAAA,0BAAA;AAAA,wBAAA,QAAA,EAAA,KAAA,CAAA,EAAA,CAAA,WAAA,EAAA;AACF,uBAAA,EAAAP,eAAA,CAAA,QAAA,CAAA,YAAY,CAAC,KAAK,CAAA,CAAA,EAAA,CAAA,oBAAA,UAAA,CAAA;AAEhB,sBAAA,CAAA,KAAA,CAAA,QAAA;wCAAND,kBAEO,CAAA,MAAA,EAFP,UAEO,EAAAC,eAAA,CADH,QAAa,CAAA,aAAA,CAAA,EAAA,CAAA,YAAA;;AAEjB,sBAAAO,kBAAA,CAEI,KAFJ,WAEI,EAAAP,eAAA,CADA,kBAAS,CAAC,KAAK,CAAC,KAAK,CAAA,CAAA,EAAA,CAAA,YAAA;AAEtB,sBAAA,CAAA,KAAA,CAAA,MAAA;AAAH,2BAAAG,SAAA,EAAA,EAAAJ,kBAAA,CAEE,GAFF,EAAA,WAEE,EADEC,eAAA,CAAA,KAAA,CAAA,MAAA,CAAA,EAAA,CAAA,YAAA;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import __sfc_main from './CnTimelineWidget.vue.js';
|
|
2
|
+
import { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
6
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
7
|
+
*
|
|
8
|
+
* Registers the `timeline` widget type (CnTimelineWidget) in the shared
|
|
9
|
+
* dashboardWidgetRegistry. Like `audit-trail` it is a DETAIL-PAGE surface:
|
|
10
|
+
* the object comes from the page's object context. It has no config form
|
|
11
|
+
* yet, so it is placed from the manifest and never offered in the
|
|
12
|
+
* Add-widget picker.
|
|
13
|
+
*
|
|
14
|
+
* @spec openspec/changes/timeline-widget/specs/timeline-widget/spec.md#requirement-a-timeline-widget-shows-an-objects-dated-events-in-order
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
registerDashboardWidget('timeline', {
|
|
19
|
+
renderer: __sfc_main,
|
|
20
|
+
form: null,
|
|
21
|
+
defaultContent: {
|
|
22
|
+
title: '',
|
|
23
|
+
fields: [],
|
|
24
|
+
related: [],
|
|
25
|
+
auditTrail: false,
|
|
26
|
+
timeline: false,
|
|
27
|
+
order: 'asc',
|
|
28
|
+
},
|
|
29
|
+
displayName: 'Timeline',
|
|
30
|
+
icon: 'TimelineClockOutline',
|
|
31
|
+
surfaces: ['detail-page'],
|
|
32
|
+
ownsTitle: true,
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=dashboardRegistration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboardRegistration.js","sources":["../../../../src/components/CnTimelineWidget/dashboardRegistration.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Registers the `timeline` widget type (CnTimelineWidget) in the shared\n * dashboardWidgetRegistry. Like `audit-trail` it is a DETAIL-PAGE surface:\n * the object comes from the page's object context. It has no config form\n * yet, so it is placed from the manifest and never offered in the\n * Add-widget picker.\n *\n * @spec openspec/changes/timeline-widget/specs/timeline-widget/spec.md#requirement-a-timeline-widget-shows-an-objects-dated-events-in-order\n */\n\nimport CnTimelineWidget from './CnTimelineWidget.vue'\nimport { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js'\n\nregisterDashboardWidget('timeline', {\n\trenderer: CnTimelineWidget,\n\tform: null,\n\tdefaultContent: {\n\t\ttitle: '',\n\t\tfields: [],\n\t\trelated: [],\n\t\tauditTrail: false,\n\t\ttimeline: false,\n\t\torder: 'asc',\n\t},\n\tdisplayName: 'Timeline',\n\ticon: 'TimelineClockOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n})\n"],"names":["CnTimelineWidget"],"mappings":";;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA,uBAAuB,CAAC,UAAU,EAAE;AACpC,CAAC,QAAQ,EAAEA,UAAgB;AAC3B,CAAC,IAAI,EAAE,IAAI;AACX,CAAC,cAAc,EAAE;AACjB,EAAE,KAAK,EAAE,EAAE;AACX,EAAE,MAAM,EAAE,EAAE;AACZ,EAAE,OAAO,EAAE,EAAE;AACb,EAAE,UAAU,EAAE,KAAK;AACnB,EAAE,QAAQ,EAAE,KAAK;AACjB,EAAE,KAAK,EAAE,KAAK;AACd,EAAE;AACF,CAAC,WAAW,EAAE,UAAU;AACxB,CAAC,IAAI,EAAE,sBAAsB;AAC7B,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC;AAC1B,CAAC,SAAS,EAAE,IAAI;AAChB,CAAC"}
|
|
@@ -61,7 +61,7 @@ var script = {
|
|
|
61
61
|
translate: { type: Function, default: null },
|
|
62
62
|
},
|
|
63
63
|
|
|
64
|
-
emits: ['complete', 'dismiss', 'step-change', 'advance', 'handoff'],
|
|
64
|
+
emits: ['complete', 'dismiss', 'pause', 'progress', 'step-change', 'advance', 'handoff'],
|
|
65
65
|
|
|
66
66
|
setup(props) {
|
|
67
67
|
const wt = useWalkthrough(props.appId, props.manifest, {
|
|
@@ -78,6 +78,10 @@ var script = {
|
|
|
78
78
|
cardPos: { top: 0, left: 0 },
|
|
79
79
|
cardPlacement: 'bottom',
|
|
80
80
|
targetEl: null,
|
|
81
|
+
// An app dialog (NcModal / NcDialog) is open over the page.
|
|
82
|
+
appModalOpen: false,
|
|
83
|
+
// The step's target was found on another page than the current one.
|
|
84
|
+
offPage: false,
|
|
81
85
|
}
|
|
82
86
|
},
|
|
83
87
|
|
|
@@ -106,6 +110,18 @@ var script = {
|
|
|
106
110
|
return this.wt.isLast.value
|
|
107
111
|
},
|
|
108
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Whether the overlay steps back: no dim, no cutout, a docked card.
|
|
115
|
+
* True while an app dialog is open or the step's target is on
|
|
116
|
+
* another page.
|
|
117
|
+
*
|
|
118
|
+
* @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md
|
|
119
|
+
* @return {boolean}
|
|
120
|
+
*/
|
|
121
|
+
quiet() {
|
|
122
|
+
return this.appModalOpen || this.offPage
|
|
123
|
+
},
|
|
124
|
+
|
|
109
125
|
isCentered() {
|
|
110
126
|
return this.step && this.step.placement === 'center'
|
|
111
127
|
},
|
|
@@ -199,6 +215,9 @@ var script = {
|
|
|
199
215
|
},
|
|
200
216
|
|
|
201
217
|
cardStyle() {
|
|
218
|
+
if (this.quiet) {
|
|
219
|
+
return {}
|
|
220
|
+
}
|
|
202
221
|
if (this.isCentered || !this.rect) {
|
|
203
222
|
return { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }
|
|
204
223
|
}
|
|
@@ -217,6 +236,18 @@ var script = {
|
|
|
217
236
|
* @type {{ stepId: string, index: number }}
|
|
218
237
|
*/
|
|
219
238
|
this.$emit('step-change', { stepId: newStep.id, index: this.index });
|
|
239
|
+
/**
|
|
240
|
+
* @event progress Emitted on every step change so the host can remember where the user is.
|
|
241
|
+
* @type {{ tourId: string, stepId: string, index: number }}
|
|
242
|
+
*/
|
|
243
|
+
this.$emit('progress', {
|
|
244
|
+
tourId: (this.wt.activeTour.value && this.wt.activeTour.value.id) || '',
|
|
245
|
+
stepId: newStep.id,
|
|
246
|
+
index: this.index,
|
|
247
|
+
});
|
|
248
|
+
// A new step finds its own page again.
|
|
249
|
+
this._homeRoute = null;
|
|
250
|
+
this.offPage = false;
|
|
220
251
|
this.$nextTick(() => this.locateTarget());
|
|
221
252
|
}
|
|
222
253
|
},
|
|
@@ -255,6 +286,9 @@ var script = {
|
|
|
255
286
|
// A qualifying auto-start tour whose first-step page is NOT the current
|
|
256
287
|
// route — held here until the user navigates to that page (see hookRouter).
|
|
257
288
|
this._pendingAutoTour = null;
|
|
289
|
+
// The route the active step's target was first found on (see locateTarget).
|
|
290
|
+
this._homeRoute = null;
|
|
291
|
+
this._modalObs = null;
|
|
258
292
|
},
|
|
259
293
|
|
|
260
294
|
mounted() {
|
|
@@ -264,12 +298,27 @@ var script = {
|
|
|
264
298
|
window.addEventListener('scroll', this._onScroll, true);
|
|
265
299
|
window.addEventListener('resize', this._onScroll);
|
|
266
300
|
this._onKey = (e) => {
|
|
267
|
-
if (e.key
|
|
268
|
-
|
|
301
|
+
if (e.key !== 'Escape' || !this.active) {
|
|
302
|
+
return
|
|
269
303
|
}
|
|
304
|
+
// ESC while an app dialog is open belongs to that dialog. It used
|
|
305
|
+
// to dismiss the tour too, so closing a create form ended it.
|
|
306
|
+
if (this.isAppModalOpen()) {
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
this.onBackdrop();
|
|
270
310
|
};
|
|
271
311
|
window.addEventListener('keydown', this._onKey);
|
|
272
|
-
this._onObjectCreated = (e) =>
|
|
312
|
+
this._onObjectCreated = (e) => {
|
|
313
|
+
const d = (e && e.detail) || {};
|
|
314
|
+
this.wt.notify({
|
|
315
|
+
kind: 'object-created',
|
|
316
|
+
register: d.register,
|
|
317
|
+
schema: d.schema,
|
|
318
|
+
object: d.object && typeof d.object === 'object' ? d.object : d,
|
|
319
|
+
});
|
|
320
|
+
};
|
|
321
|
+
this.watchAppModals();
|
|
273
322
|
window.addEventListener('cn-walkthrough:object-created', this._onObjectCreated);
|
|
274
323
|
this.hookRouter();
|
|
275
324
|
if (this.active) {
|
|
@@ -282,6 +331,10 @@ var script = {
|
|
|
282
331
|
window.removeEventListener('resize', this._onScroll);
|
|
283
332
|
window.removeEventListener('keydown', this._onKey);
|
|
284
333
|
window.removeEventListener('cn-walkthrough:object-created', this._onObjectCreated);
|
|
334
|
+
if (this._modalObs) {
|
|
335
|
+
this._modalObs.disconnect();
|
|
336
|
+
this._modalObs = null;
|
|
337
|
+
}
|
|
285
338
|
if (typeof this._routeUnhook === 'function') {
|
|
286
339
|
this._routeUnhook();
|
|
287
340
|
}
|
|
@@ -289,6 +342,51 @@ var script = {
|
|
|
289
342
|
},
|
|
290
343
|
|
|
291
344
|
methods: {
|
|
345
|
+
/**
|
|
346
|
+
* Whether an app dialog (NcModal / NcDialog, or any other modal
|
|
347
|
+
* dialog that is not this overlay) is open.
|
|
348
|
+
*
|
|
349
|
+
* @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md
|
|
350
|
+
* @return {boolean}
|
|
351
|
+
*/
|
|
352
|
+
isAppModalOpen() {
|
|
353
|
+
if (typeof document === 'undefined') {
|
|
354
|
+
return false
|
|
355
|
+
}
|
|
356
|
+
const own = this.$el;
|
|
357
|
+
const nodes = document.querySelectorAll('.modal-mask, [role="dialog"][aria-modal="true"]');
|
|
358
|
+
for (const node of nodes) {
|
|
359
|
+
if (own && typeof own.contains === 'function' && (own === node || own.contains(node))) {
|
|
360
|
+
continue
|
|
361
|
+
}
|
|
362
|
+
return true
|
|
363
|
+
}
|
|
364
|
+
return false
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Follow app dialogs opening and closing, so the overlay steps back
|
|
369
|
+
* while one is open and comes back after.
|
|
370
|
+
*
|
|
371
|
+
* @return {void}
|
|
372
|
+
*/
|
|
373
|
+
watchAppModals() {
|
|
374
|
+
const update = () => {
|
|
375
|
+
const open = this.isAppModalOpen();
|
|
376
|
+
if (open !== this.appModalOpen) {
|
|
377
|
+
this.appModalOpen = open;
|
|
378
|
+
if (!open && this.active) {
|
|
379
|
+
this.$nextTick(() => this.locateTarget());
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
update();
|
|
384
|
+
if (typeof window !== 'undefined' && window.MutationObserver) {
|
|
385
|
+
this._modalObs = new MutationObserver(update);
|
|
386
|
+
this._modalObs.observe(document.body, { childList: true, subtree: true });
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
|
|
292
390
|
/**
|
|
293
391
|
* Translate an i18n key/text via the `translate` prop when given.
|
|
294
392
|
*
|
|
@@ -476,6 +574,23 @@ var script = {
|
|
|
476
574
|
return
|
|
477
575
|
}
|
|
478
576
|
const el = this.resolveTarget(this.step);
|
|
577
|
+
// A step's target belongs to the page it was first found on. The
|
|
578
|
+
// same `index-add` button exists on every list page, and a step
|
|
579
|
+
// left half-done on Products used to spotlight the Clients page's
|
|
580
|
+
// button too, inviting a click that belonged to another step.
|
|
581
|
+
const kind = (this.step.target && this.step.target.kind) || '';
|
|
582
|
+
const routeName = this.$route ? this.$route.name : null;
|
|
583
|
+
if (el && kind !== 'page' && kind !== 'nav-item' && routeName) {
|
|
584
|
+
if (this._homeRoute === null) {
|
|
585
|
+
this._homeRoute = routeName;
|
|
586
|
+
} else if (this._homeRoute !== routeName) {
|
|
587
|
+
this.offPage = true;
|
|
588
|
+
this.targetEl = null;
|
|
589
|
+
this.rect = null;
|
|
590
|
+
return
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
this.offPage = false;
|
|
479
594
|
this.targetEl = el;
|
|
480
595
|
if (!el) {
|
|
481
596
|
// Optional step whose target is absent → skip; else wait for it.
|
|
@@ -740,6 +855,10 @@ var script = {
|
|
|
740
855
|
* @return {void}
|
|
741
856
|
*/
|
|
742
857
|
focusCard() {
|
|
858
|
+
// Never take focus from an open app dialog.
|
|
859
|
+
if (this.appModalOpen) {
|
|
860
|
+
return
|
|
861
|
+
}
|
|
743
862
|
this.$nextTick(() => {
|
|
744
863
|
const btn = this.$refs.firstBtn && this.$refs.firstBtn.$el;
|
|
745
864
|
if (btn && typeof btn.focus === 'function') {
|
|
@@ -936,16 +1055,20 @@ var script = {
|
|
|
936
1055
|
},
|
|
937
1056
|
|
|
938
1057
|
/**
|
|
939
|
-
*
|
|
1058
|
+
* Pause the tour from a backdrop click or ESC. The tour hides and
|
|
1059
|
+
* keeps its step; only Skip (the close button) and Finish end it.
|
|
1060
|
+
* Pressing ESC used to dismiss it, and the host recorded that as
|
|
1061
|
+
* "seen", so the tour never came back.
|
|
940
1062
|
*
|
|
1063
|
+
* @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md
|
|
941
1064
|
* @return {void}
|
|
942
1065
|
*/
|
|
943
1066
|
onBackdrop() {
|
|
944
|
-
this.wt.
|
|
1067
|
+
this.wt.pause();
|
|
945
1068
|
/**
|
|
946
|
-
* @event
|
|
1069
|
+
* @event pause Emitted when the user hides the tour (backdrop / ESC). The tour can continue later.
|
|
947
1070
|
*/
|
|
948
|
-
this.$emit('
|
|
1071
|
+
this.$emit('pause');
|
|
949
1072
|
},
|
|
950
1073
|
},
|
|
951
1074
|
};
|