@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
|
@@ -25,7 +25,12 @@
|
|
|
25
25
|
the greyed-out area reaches the app, which is the point. A centered
|
|
26
26
|
step spotlights nothing, so its backdrop stays interactive and
|
|
27
27
|
dismisses the tour. -->
|
|
28
|
-
|
|
28
|
+
<!-- Quiet: an app dialog is open, or the step's target belongs to
|
|
29
|
+
another page. No dim and no cutout, so the overlay never sits on
|
|
30
|
+
top of the dialog and never invites a click on the wrong page.
|
|
31
|
+
Only the docked coachmark stays. -->
|
|
32
|
+
<template v-if="quiet" />
|
|
33
|
+
<template v-else-if="isCentered || !rect">
|
|
29
34
|
<div v-if="isCentered"
|
|
30
35
|
class="cn-walkthrough__dim cn-walkthrough__dim--full"
|
|
31
36
|
@click.self="onBackdrop" />
|
|
@@ -50,7 +55,7 @@
|
|
|
50
55
|
<!-- Coachmark card -->
|
|
51
56
|
<div ref="card"
|
|
52
57
|
class="cn-walkthrough__card"
|
|
53
|
-
:class="`cn-walkthrough__card--${cardPlacement}
|
|
58
|
+
:class="[`cn-walkthrough__card--${cardPlacement}`, { 'cn-walkthrough__card--docked': quiet }]"
|
|
54
59
|
:style="cardStyle">
|
|
55
60
|
<NcButton class="cn-walkthrough__close"
|
|
56
61
|
variant="tertiary"
|
|
@@ -183,7 +188,7 @@ export default {
|
|
|
183
188
|
translate: { type: Function, default: null },
|
|
184
189
|
},
|
|
185
190
|
|
|
186
|
-
emits: ['complete', 'dismiss', 'step-change', 'advance', 'handoff'],
|
|
191
|
+
emits: ['complete', 'dismiss', 'pause', 'progress', 'step-change', 'advance', 'handoff'],
|
|
187
192
|
|
|
188
193
|
setup(props) {
|
|
189
194
|
const wt = useWalkthrough(props.appId, props.manifest, {
|
|
@@ -200,6 +205,10 @@ export default {
|
|
|
200
205
|
cardPos: { top: 0, left: 0 },
|
|
201
206
|
cardPlacement: 'bottom',
|
|
202
207
|
targetEl: null,
|
|
208
|
+
// An app dialog (NcModal / NcDialog) is open over the page.
|
|
209
|
+
appModalOpen: false,
|
|
210
|
+
// The step's target was found on another page than the current one.
|
|
211
|
+
offPage: false,
|
|
203
212
|
}
|
|
204
213
|
},
|
|
205
214
|
|
|
@@ -228,6 +237,18 @@ export default {
|
|
|
228
237
|
return this.wt.isLast.value
|
|
229
238
|
},
|
|
230
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Whether the overlay steps back: no dim, no cutout, a docked card.
|
|
242
|
+
* True while an app dialog is open or the step's target is on
|
|
243
|
+
* another page.
|
|
244
|
+
*
|
|
245
|
+
* @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md
|
|
246
|
+
* @return {boolean}
|
|
247
|
+
*/
|
|
248
|
+
quiet() {
|
|
249
|
+
return this.appModalOpen || this.offPage
|
|
250
|
+
},
|
|
251
|
+
|
|
231
252
|
isCentered() {
|
|
232
253
|
return this.step && this.step.placement === 'center'
|
|
233
254
|
},
|
|
@@ -321,6 +342,9 @@ export default {
|
|
|
321
342
|
},
|
|
322
343
|
|
|
323
344
|
cardStyle() {
|
|
345
|
+
if (this.quiet) {
|
|
346
|
+
return {}
|
|
347
|
+
}
|
|
324
348
|
if (this.isCentered || !this.rect) {
|
|
325
349
|
return { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }
|
|
326
350
|
}
|
|
@@ -339,6 +363,18 @@ export default {
|
|
|
339
363
|
* @type {{ stepId: string, index: number }}
|
|
340
364
|
*/
|
|
341
365
|
this.$emit('step-change', { stepId: newStep.id, index: this.index })
|
|
366
|
+
/**
|
|
367
|
+
* @event progress Emitted on every step change so the host can remember where the user is.
|
|
368
|
+
* @type {{ tourId: string, stepId: string, index: number }}
|
|
369
|
+
*/
|
|
370
|
+
this.$emit('progress', {
|
|
371
|
+
tourId: (this.wt.activeTour.value && this.wt.activeTour.value.id) || '',
|
|
372
|
+
stepId: newStep.id,
|
|
373
|
+
index: this.index,
|
|
374
|
+
})
|
|
375
|
+
// A new step finds its own page again.
|
|
376
|
+
this._homeRoute = null
|
|
377
|
+
this.offPage = false
|
|
342
378
|
this.$nextTick(() => this.locateTarget())
|
|
343
379
|
}
|
|
344
380
|
},
|
|
@@ -377,6 +413,9 @@ export default {
|
|
|
377
413
|
// A qualifying auto-start tour whose first-step page is NOT the current
|
|
378
414
|
// route — held here until the user navigates to that page (see hookRouter).
|
|
379
415
|
this._pendingAutoTour = null
|
|
416
|
+
// The route the active step's target was first found on (see locateTarget).
|
|
417
|
+
this._homeRoute = null
|
|
418
|
+
this._modalObs = null
|
|
380
419
|
},
|
|
381
420
|
|
|
382
421
|
mounted() {
|
|
@@ -386,12 +425,27 @@ export default {
|
|
|
386
425
|
window.addEventListener('scroll', this._onScroll, true)
|
|
387
426
|
window.addEventListener('resize', this._onScroll)
|
|
388
427
|
this._onKey = (e) => {
|
|
389
|
-
if (e.key
|
|
390
|
-
|
|
428
|
+
if (e.key !== 'Escape' || !this.active) {
|
|
429
|
+
return
|
|
391
430
|
}
|
|
431
|
+
// ESC while an app dialog is open belongs to that dialog. It used
|
|
432
|
+
// to dismiss the tour too, so closing a create form ended it.
|
|
433
|
+
if (this.isAppModalOpen()) {
|
|
434
|
+
return
|
|
435
|
+
}
|
|
436
|
+
this.onBackdrop()
|
|
392
437
|
}
|
|
393
438
|
window.addEventListener('keydown', this._onKey)
|
|
394
|
-
this._onObjectCreated = (e) =>
|
|
439
|
+
this._onObjectCreated = (e) => {
|
|
440
|
+
const d = (e && e.detail) || {}
|
|
441
|
+
this.wt.notify({
|
|
442
|
+
kind: 'object-created',
|
|
443
|
+
register: d.register,
|
|
444
|
+
schema: d.schema,
|
|
445
|
+
object: d.object && typeof d.object === 'object' ? d.object : d,
|
|
446
|
+
})
|
|
447
|
+
}
|
|
448
|
+
this.watchAppModals()
|
|
395
449
|
window.addEventListener('cn-walkthrough:object-created', this._onObjectCreated)
|
|
396
450
|
this.hookRouter()
|
|
397
451
|
if (this.active) {
|
|
@@ -404,6 +458,10 @@ export default {
|
|
|
404
458
|
window.removeEventListener('resize', this._onScroll)
|
|
405
459
|
window.removeEventListener('keydown', this._onKey)
|
|
406
460
|
window.removeEventListener('cn-walkthrough:object-created', this._onObjectCreated)
|
|
461
|
+
if (this._modalObs) {
|
|
462
|
+
this._modalObs.disconnect()
|
|
463
|
+
this._modalObs = null
|
|
464
|
+
}
|
|
407
465
|
if (typeof this._routeUnhook === 'function') {
|
|
408
466
|
this._routeUnhook()
|
|
409
467
|
}
|
|
@@ -411,6 +469,51 @@ export default {
|
|
|
411
469
|
},
|
|
412
470
|
|
|
413
471
|
methods: {
|
|
472
|
+
/**
|
|
473
|
+
* Whether an app dialog (NcModal / NcDialog, or any other modal
|
|
474
|
+
* dialog that is not this overlay) is open.
|
|
475
|
+
*
|
|
476
|
+
* @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md
|
|
477
|
+
* @return {boolean}
|
|
478
|
+
*/
|
|
479
|
+
isAppModalOpen() {
|
|
480
|
+
if (typeof document === 'undefined') {
|
|
481
|
+
return false
|
|
482
|
+
}
|
|
483
|
+
const own = this.$el
|
|
484
|
+
const nodes = document.querySelectorAll('.modal-mask, [role="dialog"][aria-modal="true"]')
|
|
485
|
+
for (const node of nodes) {
|
|
486
|
+
if (own && typeof own.contains === 'function' && (own === node || own.contains(node))) {
|
|
487
|
+
continue
|
|
488
|
+
}
|
|
489
|
+
return true
|
|
490
|
+
}
|
|
491
|
+
return false
|
|
492
|
+
},
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Follow app dialogs opening and closing, so the overlay steps back
|
|
496
|
+
* while one is open and comes back after.
|
|
497
|
+
*
|
|
498
|
+
* @return {void}
|
|
499
|
+
*/
|
|
500
|
+
watchAppModals() {
|
|
501
|
+
const update = () => {
|
|
502
|
+
const open = this.isAppModalOpen()
|
|
503
|
+
if (open !== this.appModalOpen) {
|
|
504
|
+
this.appModalOpen = open
|
|
505
|
+
if (!open && this.active) {
|
|
506
|
+
this.$nextTick(() => this.locateTarget())
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
update()
|
|
511
|
+
if (typeof window !== 'undefined' && window.MutationObserver) {
|
|
512
|
+
this._modalObs = new MutationObserver(update)
|
|
513
|
+
this._modalObs.observe(document.body, { childList: true, subtree: true })
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
|
|
414
517
|
/**
|
|
415
518
|
* Translate an i18n key/text via the `translate` prop when given.
|
|
416
519
|
*
|
|
@@ -598,6 +701,23 @@ export default {
|
|
|
598
701
|
return
|
|
599
702
|
}
|
|
600
703
|
const el = this.resolveTarget(this.step)
|
|
704
|
+
// A step's target belongs to the page it was first found on. The
|
|
705
|
+
// same `index-add` button exists on every list page, and a step
|
|
706
|
+
// left half-done on Products used to spotlight the Clients page's
|
|
707
|
+
// button too, inviting a click that belonged to another step.
|
|
708
|
+
const kind = (this.step.target && this.step.target.kind) || ''
|
|
709
|
+
const routeName = this.$route ? this.$route.name : null
|
|
710
|
+
if (el && kind !== 'page' && kind !== 'nav-item' && routeName) {
|
|
711
|
+
if (this._homeRoute === null) {
|
|
712
|
+
this._homeRoute = routeName
|
|
713
|
+
} else if (this._homeRoute !== routeName) {
|
|
714
|
+
this.offPage = true
|
|
715
|
+
this.targetEl = null
|
|
716
|
+
this.rect = null
|
|
717
|
+
return
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
this.offPage = false
|
|
601
721
|
this.targetEl = el
|
|
602
722
|
if (!el) {
|
|
603
723
|
// Optional step whose target is absent → skip; else wait for it.
|
|
@@ -862,6 +982,10 @@ export default {
|
|
|
862
982
|
* @return {void}
|
|
863
983
|
*/
|
|
864
984
|
focusCard() {
|
|
985
|
+
// Never take focus from an open app dialog.
|
|
986
|
+
if (this.appModalOpen) {
|
|
987
|
+
return
|
|
988
|
+
}
|
|
865
989
|
this.$nextTick(() => {
|
|
866
990
|
const btn = this.$refs.firstBtn && this.$refs.firstBtn.$el
|
|
867
991
|
if (btn && typeof btn.focus === 'function') {
|
|
@@ -1058,16 +1182,20 @@ export default {
|
|
|
1058
1182
|
},
|
|
1059
1183
|
|
|
1060
1184
|
/**
|
|
1061
|
-
*
|
|
1185
|
+
* Pause the tour from a backdrop click or ESC. The tour hides and
|
|
1186
|
+
* keeps its step; only Skip (the close button) and Finish end it.
|
|
1187
|
+
* Pressing ESC used to dismiss it, and the host recorded that as
|
|
1188
|
+
* "seen", so the tour never came back.
|
|
1062
1189
|
*
|
|
1190
|
+
* @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md
|
|
1063
1191
|
* @return {void}
|
|
1064
1192
|
*/
|
|
1065
1193
|
onBackdrop() {
|
|
1066
|
-
this.wt.
|
|
1194
|
+
this.wt.pause()
|
|
1067
1195
|
/**
|
|
1068
|
-
* @event
|
|
1196
|
+
* @event pause Emitted when the user hides the tour (backdrop / ESC). The tour can continue later.
|
|
1069
1197
|
*/
|
|
1070
|
-
this.$emit('
|
|
1198
|
+
this.$emit('pause')
|
|
1071
1199
|
},
|
|
1072
1200
|
},
|
|
1073
1201
|
}
|
|
@@ -1126,6 +1254,11 @@ export default {
|
|
|
1126
1254
|
pointer-events: auto;
|
|
1127
1255
|
}
|
|
1128
1256
|
|
|
1257
|
+
.cn-walkthrough__card--docked {
|
|
1258
|
+
inset-block-end: 16px;
|
|
1259
|
+
inset-inline-start: 16px;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1129
1262
|
.cn-walkthrough__close {
|
|
1130
1263
|
/* !important beats NcButton's own `position: relative`, which would otherwise
|
|
1131
1264
|
leave the close button in-flow at the card's top-left. */
|
|
@@ -25,6 +25,7 @@ import CnObjectGeoWidget from '../CnObjectGeoWidget/CnObjectGeoWidget.vue'
|
|
|
25
25
|
import CnObjectMetadataWidget from '../CnObjectMetadataWidget/CnObjectMetadataWidget.vue'
|
|
26
26
|
import CnRelatedObjectsWidget from '../CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue'
|
|
27
27
|
import CnTextWidget from '../CnTextWidget/CnTextWidget.vue'
|
|
28
|
+
import CnTimelineWidget from '../CnTimelineWidget/CnTimelineWidget.vue'
|
|
28
29
|
import CnWidgetCardGrid from '../CnWidgetCardGrid/CnWidgetCardGrid.vue'
|
|
29
30
|
import CnWidgetFormRenderer from '../CnWidgetFormRenderer/CnWidgetFormRenderer.vue'
|
|
30
31
|
import CnWidgetMapViewer from '../CnWidgetMapViewer/CnWidgetMapViewer.vue'
|
|
@@ -84,6 +85,7 @@ export const BUILT_IN_WIDGETS = {
|
|
|
84
85
|
integration: CnIntegrationWidget,
|
|
85
86
|
banner: CnBannerWidget,
|
|
86
87
|
'audit-trail': CnAuditTrailWidget,
|
|
88
|
+
timeline: CnTimelineWidget,
|
|
87
89
|
header: CnHeaderWidget,
|
|
88
90
|
text: CnTextWidget,
|
|
89
91
|
divider: CnDividerWidget,
|
|
@@ -72,6 +72,7 @@ import '../CnObjectDataWidget/dashboardRegistration.js'
|
|
|
72
72
|
import '../CnObjectGeoWidget/dashboardRegistration.js'
|
|
73
73
|
import '../CnWidgetObjectTable/dashboardRegistration.js'
|
|
74
74
|
import '../CnAuditTrailWidget/dashboardRegistration.js'
|
|
75
|
+
import '../CnTimelineWidget/dashboardRegistration.js'
|
|
75
76
|
import '../CnKbSearchWidget/index.js'
|
|
76
77
|
import '../CnInteractionFormWidget/index.js'
|
|
77
78
|
import '../CnBannerWidget/index.js'
|
|
@@ -163,3 +163,7 @@ Request a feature / Report a bug / Documentation render on every widget; the sha
|
|
|
163
163
|
## High contrast
|
|
164
164
|
|
|
165
165
|
`high-contrast` declares that this widget wants a high contrast treatment. The flag reaches the theme; the component picks no colour. See [the reference page](../../../docs/components/cn-widget-wrapper.md#high-contrast).
|
|
166
|
+
|
|
167
|
+
### A text link in the header (`headerLink`)
|
|
168
|
+
|
|
169
|
+
`headerLink` (`{ label, route?, params?, query?, href? }`) draws one text link in the header before the overflow menu, such as "All deadlines".
|
|
@@ -69,6 +69,17 @@
|
|
|
69
69
|
It used to gate both, so a caller who switched the menu off
|
|
70
70
|
also lost the Save button for an inline edit, silently. -->
|
|
71
71
|
<div v-if="actionsAreVisible()" class="cn-widget-wrapper__actions">
|
|
72
|
+
<!-- One text link in the header (`headerLink`): "All deadlines",
|
|
73
|
+
"To the board". A route is a router link, an href a plain
|
|
74
|
+
anchor. Sits before the slot and the overflow menu. -->
|
|
75
|
+
<component
|
|
76
|
+
:is="headerLinkTag"
|
|
77
|
+
v-if="headerLinkTag"
|
|
78
|
+
class="cn-widget-wrapper__header-link"
|
|
79
|
+
data-testid="cn-widget-wrapper-header-link"
|
|
80
|
+
v-bind="headerLinkAttrs">
|
|
81
|
+
{{ headerLinkLabel }}
|
|
82
|
+
</component>
|
|
72
83
|
<!-- @slot actions Custom action buttons rendered before the
|
|
73
84
|
built-in overflow menu. -->
|
|
74
85
|
<slot name="actions" />
|
|
@@ -351,6 +362,22 @@ export default {
|
|
|
351
362
|
default: () => [],
|
|
352
363
|
},
|
|
353
364
|
|
|
365
|
+
/**
|
|
366
|
+
* One text link in the header's actions group, before the overflow
|
|
367
|
+
* menu: `{ label, route?, params?, query?, href? }`. A `route` is a
|
|
368
|
+
* named router link (needs a router), an `href` a plain anchor. The
|
|
369
|
+
* label goes through the host translate function. Null (the
|
|
370
|
+
* default) draws nothing. Manifest: `headerLink` on the widget
|
|
371
|
+
* definition or its layout entry.
|
|
372
|
+
*
|
|
373
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-widget-header-carries-a-text-link
|
|
374
|
+
* @type {{label: string, route?: string, params?: object, query?: object, href?: string}|null}
|
|
375
|
+
*/
|
|
376
|
+
headerLink: {
|
|
377
|
+
type: Object,
|
|
378
|
+
default: null,
|
|
379
|
+
},
|
|
380
|
+
|
|
354
381
|
/**
|
|
355
382
|
* Whether the header's overflow action menu (Refresh / Documentation /
|
|
356
383
|
* Request-a-feature + any `#action-items`) renders. Shown by default;
|
|
@@ -634,6 +661,62 @@ export default {
|
|
|
634
661
|
return this.translate ?? this.cnTranslate
|
|
635
662
|
},
|
|
636
663
|
|
|
664
|
+
/**
|
|
665
|
+
* The header link's tag: `router-link` for a route (when a router is
|
|
666
|
+
* mounted), `a` for an href, '' when there is no usable link.
|
|
667
|
+
*
|
|
668
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-widget-header-carries-a-text-link
|
|
669
|
+
* @return {string}
|
|
670
|
+
*/
|
|
671
|
+
headerLinkTag() {
|
|
672
|
+
const link = this.headerLink
|
|
673
|
+
if (!link || typeof link.label !== 'string' || link.label === '') {
|
|
674
|
+
return ''
|
|
675
|
+
}
|
|
676
|
+
if (link.route && this.$router) {
|
|
677
|
+
return 'router-link'
|
|
678
|
+
}
|
|
679
|
+
if (typeof link.href === 'string' && link.href !== '') {
|
|
680
|
+
return 'a'
|
|
681
|
+
}
|
|
682
|
+
return ''
|
|
683
|
+
},
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* The header link's attributes for its tag.
|
|
687
|
+
*
|
|
688
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-widget-header-carries-a-text-link
|
|
689
|
+
* @return {object}
|
|
690
|
+
*/
|
|
691
|
+
headerLinkAttrs() {
|
|
692
|
+
const link = this.headerLink || {}
|
|
693
|
+
if (this.headerLinkTag === 'router-link') {
|
|
694
|
+
const to = { name: link.route }
|
|
695
|
+
if (link.params && typeof link.params === 'object') {
|
|
696
|
+
to.params = link.params
|
|
697
|
+
}
|
|
698
|
+
if (link.query && typeof link.query === 'object') {
|
|
699
|
+
to.query = link.query
|
|
700
|
+
}
|
|
701
|
+
return { to }
|
|
702
|
+
}
|
|
703
|
+
if (this.headerLinkTag === 'a') {
|
|
704
|
+
return { href: link.href }
|
|
705
|
+
}
|
|
706
|
+
return {}
|
|
707
|
+
},
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* The header link's text, through the host translate function.
|
|
711
|
+
*
|
|
712
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-widget-header-carries-a-text-link
|
|
713
|
+
* @return {string}
|
|
714
|
+
*/
|
|
715
|
+
headerLinkLabel() {
|
|
716
|
+
const label = this.headerLink && this.headerLink.label
|
|
717
|
+
return label ? this.effectiveTranslate(label) : ''
|
|
718
|
+
},
|
|
719
|
+
|
|
637
720
|
/**
|
|
638
721
|
* The RENDERED widget title — `displayTitle` run through the host
|
|
639
722
|
* translate function. Kept separate from `displayTitle` on purpose:
|
|
@@ -877,7 +960,7 @@ export default {
|
|
|
877
960
|
* @return {boolean} True when the group would hold a visible control.
|
|
878
961
|
*/
|
|
879
962
|
actionsAreVisible() {
|
|
880
|
-
return this.showActions || this.actionsSlotRenders()
|
|
963
|
+
return this.showActions || this.actionsSlotRenders() || Boolean(this.headerLinkTag)
|
|
881
964
|
},
|
|
882
965
|
|
|
883
966
|
/**
|
|
@@ -1127,6 +1210,29 @@ export default {
|
|
|
1127
1210
|
flex-shrink: 0;
|
|
1128
1211
|
}
|
|
1129
1212
|
|
|
1213
|
+
/* The header's text link (`headerLink`). Theme hooks: --cn-widget-header-link-size
|
|
1214
|
+
and --cn-widget-header-link-weight. */
|
|
1215
|
+
.cn-widget-wrapper__header-link {
|
|
1216
|
+
align-self: center;
|
|
1217
|
+
padding: 0 calc(1 * var(--default-grid-baseline));
|
|
1218
|
+
color: var(--color-primary-element);
|
|
1219
|
+
font-size: var(--cn-widget-header-link-size, 14px);
|
|
1220
|
+
font-weight: var(--cn-widget-header-link-weight, 600);
|
|
1221
|
+
text-decoration: none;
|
|
1222
|
+
white-space: nowrap;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.cn-widget-wrapper__header-link:hover,
|
|
1226
|
+
.cn-widget-wrapper__header-link:focus-visible {
|
|
1227
|
+
text-decoration: underline;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
.cn-widget-wrapper__header-link:focus-visible {
|
|
1231
|
+
outline: 2px solid var(--color-primary-element);
|
|
1232
|
+
outline-offset: 2px;
|
|
1233
|
+
border-radius: var(--border-radius-small, 4px);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1130
1236
|
/* Every widget's header icon is coloured — `--cn-widget-icon-color` is set on
|
|
1131
1237
|
the header from titleIconStyle, and defaults to the theme colour if a
|
|
1132
1238
|
wrapper is ever rendered without it. Material-design-icon components paint
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
</NcButton>
|
|
106
106
|
<NcButton variant="primary"
|
|
107
107
|
data-testid="cn-wizard-next"
|
|
108
|
-
:disabled="loading"
|
|
108
|
+
:disabled="loading || nextDisabled"
|
|
109
109
|
@click="isLast ? submit() : next()">
|
|
110
110
|
<template #icon>
|
|
111
111
|
<NcLoadingIcon v-if="loading" :size="20" />
|
|
@@ -280,6 +280,19 @@ export default {
|
|
|
280
280
|
type: Boolean,
|
|
281
281
|
default: true,
|
|
282
282
|
},
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Disable the Next / Submit button, whatever the step's `validate`
|
|
286
|
+
* would say. For a host that knows the wizard cannot go on yet, such as
|
|
287
|
+
* `CnSetupWizard` while a required app is missing.
|
|
288
|
+
*
|
|
289
|
+
* @type {boolean}
|
|
290
|
+
* @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step
|
|
291
|
+
*/
|
|
292
|
+
nextDisabled: {
|
|
293
|
+
type: Boolean,
|
|
294
|
+
default: false,
|
|
295
|
+
},
|
|
283
296
|
},
|
|
284
297
|
|
|
285
298
|
emits: ['close', 'step-change', 'submit'],
|
|
@@ -396,6 +409,9 @@ export default {
|
|
|
396
409
|
* @return {Promise<void>}
|
|
397
410
|
*/
|
|
398
411
|
async next() {
|
|
412
|
+
if (this.nextDisabled) {
|
|
413
|
+
return
|
|
414
|
+
}
|
|
399
415
|
if (this.isLast) {
|
|
400
416
|
return this.submit()
|
|
401
417
|
}
|
|
@@ -467,6 +483,9 @@ export default {
|
|
|
467
483
|
* @return {Promise<void>}
|
|
468
484
|
*/
|
|
469
485
|
async submit() {
|
|
486
|
+
if (this.nextDisabled) {
|
|
487
|
+
return
|
|
488
|
+
}
|
|
470
489
|
const ok = await this.runValidation()
|
|
471
490
|
if (!ok) {
|
|
472
491
|
return
|
package/src/components/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export { CnLeafDependencySettings } from './CnLeafDependencySettings/index.js'
|
|
|
17
17
|
export { CnStatsBlock } from './CnStatsBlock/index.js'
|
|
18
18
|
export { CnStructuredDocReview } from './CnStructuredDocReview/index.js'
|
|
19
19
|
export { CnConfigurationCard } from './CnConfigurationCard/index.js'
|
|
20
|
+
export { CnAdminActionCard } from './CnAdminActionCard/index.js'
|
|
20
21
|
export { CnVersionInfoCard } from './CnVersionInfoCard/index.js'
|
|
21
22
|
export { CnAdminSettingsShell } from './CnAdminSettingsShell/index.js'
|
|
22
23
|
export { CnCellRenderer } from './CnCellRenderer/index.js'
|
|
@@ -103,6 +104,7 @@ export { CnSearchPage } from './CnSearchPage/index.js'
|
|
|
103
104
|
export { CnCommandPalette } from './CnCommandPalette/index.js'
|
|
104
105
|
export { CnTabbedFormDialog } from './CnTabbedFormDialog/index.js'
|
|
105
106
|
export { CnTimelineStages } from './CnTimelineStages/index.js'
|
|
107
|
+
export { CnTimelineWidget } from './CnTimelineWidget/index.js'
|
|
106
108
|
export { CnTreeView } from './CnTreeView/index.js'
|
|
107
109
|
export { CnUserActionMenu } from './CnUserActionMenu/index.js'
|
|
108
110
|
export { CnNotesCard } from './CnNotesCard/index.js'
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useAppStatus } from './useAppStatus.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolve `manifest.dependencies` entries to their live status.
|
|
10
|
+
*
|
|
11
|
+
* Accepts the manifest's own shape: an app-id string (a REQUIRED dependency)
|
|
12
|
+
* or `{ id, name?, required? }` where `required` defaults to true. Each entry
|
|
13
|
+
* becomes `{ id, name, required, installed, enabled }`.
|
|
14
|
+
*
|
|
15
|
+
* `serverStatuses` is the optional `dependency_statuses` initial state an app
|
|
16
|
+
* injects from PHP, keyed by app id: `{ installed, enabled }`. It wins over
|
|
17
|
+
* the browser heuristic, because only the server can tell "installed but
|
|
18
|
+
* disabled" from "not installed". Without it, `useAppStatus` answers, which
|
|
19
|
+
* is the same resolution `CnAppRoot` uses for its dependency-missing phase.
|
|
20
|
+
*
|
|
21
|
+
* @param {Array<string|{id: string, name?: string, required?: boolean}>} entries The declared dependencies.
|
|
22
|
+
* @param {object} [serverStatuses] Server-reported statuses keyed by app id.
|
|
23
|
+
* @return {Array<{id: string, name: string, required: boolean, installed: boolean, enabled: boolean}>} One row per valid entry.
|
|
24
|
+
*
|
|
25
|
+
* @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const missing = checkDependencies(manifest.dependencies)
|
|
29
|
+
* .filter((d) => d.required && !(d.installed && d.enabled))
|
|
30
|
+
*/
|
|
31
|
+
export function checkDependencies(entries, serverStatuses = {}) {
|
|
32
|
+
const list = Array.isArray(entries) ? entries : []
|
|
33
|
+
const server = (serverStatuses && typeof serverStatuses === 'object') ? serverStatuses : {}
|
|
34
|
+
return list
|
|
35
|
+
.map((entry) => {
|
|
36
|
+
const isObject = entry !== null && typeof entry === 'object'
|
|
37
|
+
const id = isObject ? entry.id : entry
|
|
38
|
+
if (typeof id !== 'string' || id === '') {
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
const required = isObject ? entry.required !== false : true
|
|
42
|
+
const name = (isObject && entry.name) || id
|
|
43
|
+
const reported = server[id]
|
|
44
|
+
if (reported !== undefined && reported !== null) {
|
|
45
|
+
return { id, name, required, installed: !!reported.installed, enabled: !!reported.enabled }
|
|
46
|
+
}
|
|
47
|
+
const status = useAppStatus(id)
|
|
48
|
+
return { id, name, required, installed: !!status.installed.value, enabled: !!status.enabled.value }
|
|
49
|
+
})
|
|
50
|
+
.filter((row) => row !== null)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether a resolved dependency row is usable: installed AND enabled.
|
|
55
|
+
*
|
|
56
|
+
* @param {{installed: boolean, enabled: boolean}} row A row from `checkDependencies`.
|
|
57
|
+
* @return {boolean} True when the app can be used.
|
|
58
|
+
*/
|
|
59
|
+
export function isDependencyResolved(row) {
|
|
60
|
+
return !!row && row.installed === true && row.enabled === true
|
|
61
|
+
}
|