@conduction/nextcloud-vue 2.78.0 → 2.80.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/bundled-packages.json +0 -2
- package/dist/esm/bundled-packages.json +0 -2
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +3 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +32 -3
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +115 -104
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +12 -2
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +39 -3
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +158 -0
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +157 -96
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +139 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +140 -92
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +77 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +5 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +62 -4
- 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.vue2.js +85 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +43 -4
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +11 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +736 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +238 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/index.js +30 -0
- package/dist/esm/components/CnFactsWidget/index.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +10 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +551 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +183 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/index.js +29 -0
- package/dist/esm/components/CnFieldListWidget/index.js.map +1 -0
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +173 -5
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +181 -99
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js +29 -11
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/openRegisterSource.js +252 -0
- package/dist/esm/components/CnFilesBrowser/openRegisterSource.js.map +1 -0
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +66 -18
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +4 -4
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +18 -3
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +330 -74
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +34 -15
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +10 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +99 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +48 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +100 -3
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -2
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js +29 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +3 -2
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +10 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +387 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +169 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/index.js +31 -0
- package/dist/esm/components/CnRecordListWidget/index.js.map +1 -0
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js +10 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js +351 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js +71 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/index.js +35 -0
- package/dist/esm/components/CnSectionsWidget/index.js.map +1 -0
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +81 -2
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +1 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +152 -8
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +84 -23
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -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/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +106 -2
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +105 -53
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useAppManifest.js +8 -3
- package/dist/esm/composables/useAppManifest.js.map +1 -1
- package/dist/esm/composables/useFormDraft.js +140 -9
- package/dist/esm/composables/useFormDraft.js.map +1 -1
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +28 -20
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js +9 -0
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection.js +21 -0
- package/dist/esm/integrations/builtin/field-inspection.js.map +1 -1
- package/dist/esm/integrations/offline/checklistSubmission.js +141 -0
- package/dist/esm/integrations/offline/checklistSubmission.js.map +1 -0
- package/dist/esm/integrations/offline/fieldCollectionHelpers.js +70 -5
- package/dist/esm/integrations/offline/fieldCollectionHelpers.js.map +1 -1
- package/dist/esm/integrations/offline/index.js +2 -1
- package/dist/esm/integrations/offline/index.js.map +1 -1
- package/dist/esm/integrations/offline/offlineDb.js +6 -1
- package/dist/esm/integrations/offline/offlineDb.js.map +1 -1
- package/dist/esm/integrations/offline/syncReplayService.js +8 -2
- package/dist/esm/integrations/offline/syncReplayService.js.map +1 -1
- package/dist/esm/l10n/en.json.js +72 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +120 -2
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +1314 -74
- package/dist/esm/utils/boardDate.js +193 -0
- package/dist/esm/utils/boardDate.js.map +1 -0
- package/dist/esm/utils/detailActionModel.js +60 -3
- package/dist/esm/utils/detailActionModel.js.map +1 -1
- package/dist/esm/utils/dynamicProperties.js +5 -1
- package/dist/esm/utils/dynamicProperties.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +114 -0
- package/dist/esm/utils/fillTemplate.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +4 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/queryPresets.js +96 -0
- package/dist/esm/utils/queryPresets.js.map +1 -0
- package/dist/esm/utils/resolveFilterTokens.js +11 -0
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/schema.js +21 -0
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +3 -3
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +57 -6
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/widgetDispatch.js +7 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +14278 -8169
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1314 -74
- package/l10n/en.json +72 -1
- package/l10n/nl.json +120 -2
- package/package.json +1 -1
- package/scripts/build-validators.js +26 -0
- package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
- package/src/components/CnActionsBar/CnActionsBar.md +4 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +83 -42
- package/src/components/CnAppRoot/CnAppRoot.vue +12 -2
- package/src/components/CnBannerWidget/CnBannerWidget.vue +39 -3
- package/src/components/CnCellRenderer/CnCellRenderer.vue +306 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +219 -4
- package/src/components/CnDataTable/CnDataTable.md +2 -0
- package/src/components/CnDataTable/CnDataTable.vue +82 -2
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +63 -3
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +127 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +1050 -0
- package/src/components/CnFactsWidget/index.js +29 -0
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +746 -0
- package/src/components/CnFieldListWidget/index.js +28 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +232 -11
- package/src/components/CnFilesBrowser/filesBrowser.js +43 -2
- package/src/components/CnFilesBrowser/index.js +1 -0
- package/src/components/CnFilesBrowser/openRegisterSource.js +248 -0
- package/src/components/CnFormDialog/CnFormDialog.md +9 -3
- package/src/components/CnFormDialog/CnFormDialog.vue +70 -22
- package/src/components/CnFormPage/CnFormPage.vue +18 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +281 -9
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
- package/src/components/CnObjectSidebar/CnFilesTab.vue +103 -4
- package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +3 -2
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +603 -0
- package/src/components/CnRecordListWidget/index.js +30 -0
- package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +1 -1
- package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
- package/src/components/CnSectionsWidget/index.js +34 -0
- package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
- package/src/components/CnStatWidget/CnStatWidget.vue +1 -1
- package/src/components/CnTabsWidget/CnTabsWidget.vue +214 -9
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +188 -4
- package/src/components/index.js +5 -1
- package/src/composables/__tests__/useFormDraft.spec.js +69 -0
- package/src/composables/useAppManifest.js +8 -3
- package/src/composables/useFormDraft.js +139 -8
- package/src/css/form-field.css +14 -0
- package/src/css/index-page.css +71 -0
- package/src/css/kpi-card.css +32 -0
- package/src/css/look-board-detail.css +87 -0
- package/src/css/look-board-index.css +143 -14
- package/src/css/look-board.css +114 -2
- package/src/css/table.css +56 -0
- package/src/index.js +8 -0
- package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +35 -20
- package/src/integrations/builtin/field-inspection.js +21 -0
- package/src/integrations/offline/checklistSubmission.js +137 -0
- package/src/integrations/offline/fieldCollectionHelpers.js +69 -4
- package/src/integrations/offline/index.js +3 -0
- package/src/integrations/offline/offlineDb.js +6 -1
- package/src/integrations/offline/syncReplayService.js +8 -2
- package/src/schemas/app-manifest-v2.schema.json +149 -5
- package/src/utils/boardDate.js +189 -0
- package/src/utils/detailActionModel.js +59 -2
- package/src/utils/dynamicProperties.js +5 -1
- package/src/utils/fillTemplate.js +110 -0
- package/src/utils/libraryWidgetKeys.js +4 -0
- package/src/utils/queryPresets.js +93 -0
- package/src/utils/resolveFilterTokens.js +11 -0
- package/src/utils/schema.js +21 -0
- package/src/utils/sentinelTokens.js +3 -3
- package/src/utils/validateManifestV2.compiled.js +57 -2
- package/src/utils/widgetDispatch.js +7 -1
- package/dist/esm/_virtual/equal.js +0 -4
- package/dist/esm/_virtual/equal.js.map +0 -1
- package/dist/esm/_virtual/ucs2length.js +0 -4
- package/dist/esm/_virtual/ucs2length.js.map +0 -1
- package/dist/esm/node_modules/ajv/dist/runtime/equal.js +0 -9
- package/dist/esm/node_modules/ajv/dist/runtime/equal.js.map +0 -1
- package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js +0 -25
- package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js.map +0 -1
- package/dist/esm/node_modules/fast-deep-equal/index.js +0 -47
- package/dist/esm/node_modules/fast-deep-equal/index.js.map +0 -1
|
@@ -80,10 +80,64 @@
|
|
|
80
80
|
</span>
|
|
81
81
|
</template>
|
|
82
82
|
|
|
83
|
+
<!-- A stack (`widgetIds`): every widget is a titled card; the
|
|
84
|
+
first joins the open tab, the next ones stand below it. -->
|
|
85
|
+
<div
|
|
86
|
+
v-if="entry.widget && entry.stack.length"
|
|
87
|
+
class="cn-tabs-widget__stack"
|
|
88
|
+
data-testid="cn-tabs-widget-stack">
|
|
89
|
+
<div class="cn-tabs-widget__stack-first">
|
|
90
|
+
<CnDetailWidgetHost
|
|
91
|
+
:widget="entry.widget"
|
|
92
|
+
chrome="card"
|
|
93
|
+
:showCardTitle="true"
|
|
94
|
+
:showActions="false"
|
|
95
|
+
:availableWidgets="availableWidgets"
|
|
96
|
+
:objectId="objectId"
|
|
97
|
+
:object="objectData"
|
|
98
|
+
:objectType="objectType"
|
|
99
|
+
:schemaObject="schemaObject"
|
|
100
|
+
:register="register"
|
|
101
|
+
:schema="schema"
|
|
102
|
+
:store="store"
|
|
103
|
+
:surface="surface"
|
|
104
|
+
:integrationContext="integrationContext"
|
|
105
|
+
:cnRegistry="cnRegistry"
|
|
106
|
+
@geoSaved="onGeoSaved"
|
|
107
|
+
@openIntegration="onOpenIntegration"
|
|
108
|
+
@selectObject="onSelectObject" />
|
|
109
|
+
</div>
|
|
110
|
+
<div
|
|
111
|
+
v-for="child in entry.stack"
|
|
112
|
+
:key="`${entry.key}-${child.id}`"
|
|
113
|
+
class="cn-tabs-widget__stack-item"
|
|
114
|
+
:data-testid="`cn-tabs-widget-stack-${child.id}`">
|
|
115
|
+
<CnDetailWidgetHost
|
|
116
|
+
:widget="child"
|
|
117
|
+
chrome="card"
|
|
118
|
+
:showCardTitle="true"
|
|
119
|
+
:showActions="false"
|
|
120
|
+
:availableWidgets="availableWidgets"
|
|
121
|
+
:objectId="objectId"
|
|
122
|
+
:object="objectData"
|
|
123
|
+
:objectType="objectType"
|
|
124
|
+
:schemaObject="schemaObject"
|
|
125
|
+
:register="register"
|
|
126
|
+
:schema="schema"
|
|
127
|
+
:store="store"
|
|
128
|
+
:surface="surface"
|
|
129
|
+
:integrationContext="integrationContext"
|
|
130
|
+
:cnRegistry="cnRegistry"
|
|
131
|
+
@geoSaved="onGeoSaved"
|
|
132
|
+
@openIntegration="onOpenIntegration"
|
|
133
|
+
@selectObject="onSelectObject" />
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
83
136
|
<CnDetailWidgetHost
|
|
84
|
-
v-if="entry.widget"
|
|
137
|
+
v-else-if="entry.widget"
|
|
85
138
|
:widget="entry.widget"
|
|
86
139
|
chrome="bare"
|
|
140
|
+
:availableWidgets="availableWidgets"
|
|
87
141
|
:objectId="objectId"
|
|
88
142
|
:object="objectData"
|
|
89
143
|
:objectType="objectType"
|
|
@@ -114,6 +168,7 @@ import CnTab from '../CnTabs/CnTab.vue'
|
|
|
114
168
|
import CnTabs from '../CnTabs/CnTabs.vue'
|
|
115
169
|
import { normalizeLook } from '../../composables/useLook.js'
|
|
116
170
|
import { resolveTabCount } from '../../utils/detailActionModel.js'
|
|
171
|
+
import { fetchListTotal } from '../../utils/fetchFilterCounts.js'
|
|
117
172
|
import { isActivityWidget } from '../../utils/headerMeta.js'
|
|
118
173
|
import { PANEL_ACTION_SINK } from '../../utils/panelActions.js'
|
|
119
174
|
import { evaluateVisibleWhen, evaluateVisibleWhenLocal, isLocallyDecidableVisibleWhen } from '../../utils/visibleWhen.js'
|
|
@@ -254,6 +309,11 @@ export default {
|
|
|
254
309
|
cnLook: { default: 'nextcloud' },
|
|
255
310
|
/** The tab list's name under the board look, provided by CnDetailPage (`config.tabsLabel`). */
|
|
256
311
|
cnDetailTabsLabel: { default: null },
|
|
312
|
+
/**
|
|
313
|
+
* The host's translate (CnAppRoot provides `cnTranslate`), so a tab
|
|
314
|
+
* label written in the manifest reads in the user's language.
|
|
315
|
+
*/
|
|
316
|
+
cnTranslate: { default: () => (key) => key },
|
|
257
317
|
},
|
|
258
318
|
|
|
259
319
|
props: {
|
|
@@ -263,7 +323,11 @@ export default {
|
|
|
263
323
|
* `tabs[]` entries are `{ widgetId, label?, icon?, count?, countField?, overflow? }`.
|
|
264
324
|
* `label` and `icon` fall back to the referenced widget's own title and
|
|
265
325
|
* icon. `count` is a number shown after the label; `countField` reads
|
|
266
|
-
* it off the record instead (a list counts its items). `
|
|
326
|
+
* it off the record instead (a list counts its items). `countFrom`
|
|
327
|
+
* counts a list on the server: `"widget"` counts the child widget's
|
|
328
|
+
* own list (`content.register`, `schema`, `filter`, tokens such as
|
|
329
|
+
* `@objectId` resolved), an object `{ register, schema, filter? }`
|
|
330
|
+
* names the list; `count` and `countField` win over it. `overflow: true`
|
|
267
331
|
* lists the tab under "More".
|
|
268
332
|
*
|
|
269
333
|
* `maxVisibleTabs` caps the strip: later tabs go under "More".
|
|
@@ -271,7 +335,7 @@ export default {
|
|
|
271
335
|
* `moreLabel` names that menu. `variant: "segmented"` draws the strip
|
|
272
336
|
* as a pill switch (CnTabs' segmented variant); `line` is the default.
|
|
273
337
|
*
|
|
274
|
-
* @type {{ tabs?: Array<{widgetId
|
|
338
|
+
* @type {{ tabs?: Array<{widgetId?: string, widgetIds?: string[], label?: string, icon?: string, count?: number, countField?: string, countFrom?: ('widget'|{register: string, schema: string, filter?: object}), overflow?: boolean}>, ariaLabel?: string, maxVisibleTabs?: number, hideEmpty?: boolean, moreLabel?: string, variant?: ('line'|'segmented') }}
|
|
275
339
|
*/
|
|
276
340
|
content: {
|
|
277
341
|
type: Object,
|
|
@@ -391,6 +455,9 @@ export default {
|
|
|
391
455
|
sourceVisibility: {},
|
|
392
456
|
// Bumped per evaluation round, so a slow answer from an earlier round is dropped.
|
|
393
457
|
visibilitySeq: 0,
|
|
458
|
+
// Totals counted for tabs with `countFrom`, by position in `content.tabs`.
|
|
459
|
+
widgetCounts: {},
|
|
460
|
+
countSeq: 0,
|
|
394
461
|
// Tab id named by the route hash that has not appeared yet (still pending).
|
|
395
462
|
hashTargetId: '',
|
|
396
463
|
// Items published by the panels, keyed by widget id. Keyed rather
|
|
@@ -433,7 +500,7 @@ export default {
|
|
|
433
500
|
// whatever its place in the manifest. `index` stays the position in
|
|
434
501
|
// `content.tabs`, which the source-mode conditions are keyed on.
|
|
435
502
|
const isActivityTab = (tab) => {
|
|
436
|
-
const id =
|
|
503
|
+
const id = this.tabWidgetIds(tab)[0]
|
|
437
504
|
return isActivityWidget(this.availableWidgets.find((w) => w && w.id === id))
|
|
438
505
|
}
|
|
439
506
|
const ordered = authored.map((tab, index) => ({ tab, index }))
|
|
@@ -455,9 +522,19 @@ export default {
|
|
|
455
522
|
if (visibility === 'hidden') {
|
|
456
523
|
return
|
|
457
524
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const
|
|
525
|
+
// A stack (`widgetIds`) resolves every id; one that names no
|
|
526
|
+
// widget is skipped. The first that resolves is the panel's.
|
|
527
|
+
const ids = this.tabWidgetIds(tab)
|
|
528
|
+
const widgets = ids.length > 1
|
|
529
|
+
? ids.map((id) => this.availableWidgets.find((w) => w && w.id === id) || null).filter(Boolean)
|
|
530
|
+
: []
|
|
531
|
+
const widget = ids.length > 1
|
|
532
|
+
? (widgets[0] || null)
|
|
533
|
+
: (this.availableWidgets.find((w) => w && w.id === ids[0]) || null)
|
|
534
|
+
const widgetId = widget ? widget.id : ids[0]
|
|
535
|
+
const declared = resolveTabCount(typeof tab === 'object' ? tab : null, this.objectData)
|
|
536
|
+
const counted = this.widgetCounts[index]
|
|
537
|
+
const count = declared === null && typeof counted === 'number' ? counted : declared
|
|
461
538
|
// The board strip has no "More": every tab is in the strip.
|
|
462
539
|
let overflow = !this.isBoard && ((tab && tab.overflow === true) || (hideEmpty && count === 0))
|
|
463
540
|
if (!overflow && !this.isBoard) {
|
|
@@ -471,9 +548,10 @@ export default {
|
|
|
471
548
|
key: `${widgetId || 'tab'}-${index}`,
|
|
472
549
|
id: (tab && typeof tab === 'object' && tab.id) || widgetId || '',
|
|
473
550
|
widgetId,
|
|
474
|
-
label:
|
|
551
|
+
label: this.tabLabel(tab, widget, widgetId),
|
|
475
552
|
icon: (tab && tab.icon) || widget?.icon || '',
|
|
476
553
|
widget,
|
|
554
|
+
stack: widgets.slice(1),
|
|
477
555
|
count,
|
|
478
556
|
overflow,
|
|
479
557
|
pending: visibility === 'pending',
|
|
@@ -492,6 +570,17 @@ export default {
|
|
|
492
570
|
return t('nextcloud-vue', 'Loading …')
|
|
493
571
|
},
|
|
494
572
|
|
|
573
|
+
/**
|
|
574
|
+
* The lists the tabs count (`countFrom`), serialised, to count again
|
|
575
|
+
* when they change.
|
|
576
|
+
*
|
|
577
|
+
* @return {string} The key.
|
|
578
|
+
*/
|
|
579
|
+
countSourcesKey() {
|
|
580
|
+
const tabs = Array.isArray(this.content?.tabs) ? this.content.tabs : []
|
|
581
|
+
return JSON.stringify(tabs.map((tab) => this.countSourceOf(tab)))
|
|
582
|
+
},
|
|
583
|
+
|
|
495
584
|
/** @return {string} The source-mode conditions, serialised, to re-evaluate when they change. */
|
|
496
585
|
sourceConditionsKey() {
|
|
497
586
|
const tabs = Array.isArray(this.content?.tabs) ? this.content.tabs : []
|
|
@@ -604,6 +693,12 @@ export default {
|
|
|
604
693
|
objectId() {
|
|
605
694
|
this.sourceVisibility = {}
|
|
606
695
|
this.refreshSourceVisibility()
|
|
696
|
+
this.widgetCounts = {}
|
|
697
|
+
this.refreshWidgetCounts()
|
|
698
|
+
},
|
|
699
|
+
|
|
700
|
+
countSourcesKey() {
|
|
701
|
+
this.refreshWidgetCounts()
|
|
607
702
|
},
|
|
608
703
|
|
|
609
704
|
sourceConditionsKey() {
|
|
@@ -638,10 +733,14 @@ export default {
|
|
|
638
733
|
}
|
|
639
734
|
this.activeKey = this.resolvedTabs[this.activeIndex]?.key || ''
|
|
640
735
|
this.refreshSourceVisibility()
|
|
736
|
+
this.refreshWidgetCounts()
|
|
641
737
|
},
|
|
642
738
|
|
|
643
739
|
mounted() {
|
|
644
|
-
this._onRefresh = () =>
|
|
740
|
+
this._onRefresh = () => {
|
|
741
|
+
this.refreshSourceVisibility()
|
|
742
|
+
this.refreshWidgetCounts()
|
|
743
|
+
}
|
|
645
744
|
subscribe('cn:page:refresh', this._onRefresh)
|
|
646
745
|
subscribe('cn:widget:refresh', this._onRefresh)
|
|
647
746
|
},
|
|
@@ -652,6 +751,51 @@ export default {
|
|
|
652
751
|
},
|
|
653
752
|
|
|
654
753
|
methods: {
|
|
754
|
+
/**
|
|
755
|
+
* The widget ids a tab names: `widgetIds` (a stack) when it holds at
|
|
756
|
+
* least one id, else `widgetId`, or the bare string.
|
|
757
|
+
*
|
|
758
|
+
* @param {object|string} tab The `content.tabs[]` entry.
|
|
759
|
+
* @return {string[]} The ids, at least one entry (possibly undefined).
|
|
760
|
+
* @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-tab-can-hold-a-stack-of-widgets
|
|
761
|
+
*/
|
|
762
|
+
tabWidgetIds(tab) {
|
|
763
|
+
if (typeof tab === 'string') {
|
|
764
|
+
return [tab]
|
|
765
|
+
}
|
|
766
|
+
if (tab && Array.isArray(tab.widgetIds)) {
|
|
767
|
+
const ids = tab.widgetIds.filter((id) => typeof id === 'string' && id !== '')
|
|
768
|
+
if (ids.length) {
|
|
769
|
+
return ids
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return [tab?.widgetId]
|
|
773
|
+
},
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* The label a tab shows: the authored `label`, else (board look) History
|
|
777
|
+
* for the activity widget, else the child widget's title, else its id.
|
|
778
|
+
* A manifest label or title goes through the host's translate; the
|
|
779
|
+
* library's own History is translated by the library catalogue.
|
|
780
|
+
*
|
|
781
|
+
* @param {object|string} tab The `content.tabs[]` entry.
|
|
782
|
+
* @param {object|null} widget The widget definition it names.
|
|
783
|
+
* @param {string} widgetId The widget id.
|
|
784
|
+
* @return {string} The label.
|
|
785
|
+
* @spec openspec/changes/screens-detail-labels-parity/specs/detail-labels-board-look/spec.md#requirement-detail-labels-read-in-the-users-language
|
|
786
|
+
*/
|
|
787
|
+
tabLabel(tab, widget, widgetId) {
|
|
788
|
+
const translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key
|
|
789
|
+
if (tab && typeof tab.label === 'string' && tab.label !== '') {
|
|
790
|
+
return translate(tab.label)
|
|
791
|
+
}
|
|
792
|
+
if (this.isBoard && isActivityWidget(widget)) {
|
|
793
|
+
return t('nextcloud-vue', 'History')
|
|
794
|
+
}
|
|
795
|
+
const title = widgetTitleOf(widget)
|
|
796
|
+
return title ? translate(title) : (widgetId || '')
|
|
797
|
+
},
|
|
798
|
+
|
|
655
799
|
/**
|
|
656
800
|
* Whether a tab shows: `visible`, `hidden`, or `pending` while its
|
|
657
801
|
* condition cannot be decided yet (object not loaded, count not back).
|
|
@@ -704,6 +848,60 @@ export default {
|
|
|
704
848
|
}))
|
|
705
849
|
},
|
|
706
850
|
|
|
851
|
+
/**
|
|
852
|
+
* The list a tab counts (`countFrom`): `"widget"` counts the child
|
|
853
|
+
* widget's own list (its `content.register`, `content.schema` and
|
|
854
|
+
* `content.filter`); an object `{ register, schema, filter? }` names
|
|
855
|
+
* the list itself. Null for a tab that does not ask, or a child with
|
|
856
|
+
* no list.
|
|
857
|
+
*
|
|
858
|
+
* @param {object|string} tab The `content.tabs[]` entry.
|
|
859
|
+
* @return {{register: string, schema: string, filter: object}|null} The list.
|
|
860
|
+
* @spec openspec/changes/screens-tab-counts-parity/specs/tab-counts/spec.md#requirement-a-tab-can-count-its-list
|
|
861
|
+
*/
|
|
862
|
+
countSourceOf(tab) {
|
|
863
|
+
const from = tab && typeof tab === 'object' ? tab.countFrom : null
|
|
864
|
+
let source = null
|
|
865
|
+
if (from === 'widget') {
|
|
866
|
+
// A stack counts its first widget that has a list.
|
|
867
|
+
const lists = this.tabWidgetIds(tab)
|
|
868
|
+
.map((id) => this.availableWidgets.find((w) => w && w.id === id))
|
|
869
|
+
.filter((w) => w && w.content && typeof w.content === 'object' && w.content.register && w.content.schema)
|
|
870
|
+
source = lists.length ? lists[0].content : null
|
|
871
|
+
} else if (from && typeof from === 'object') {
|
|
872
|
+
source = from
|
|
873
|
+
}
|
|
874
|
+
if (!source || typeof source.register !== 'string' || source.register === '' || typeof source.schema !== 'string' || source.schema === '') {
|
|
875
|
+
return null
|
|
876
|
+
}
|
|
877
|
+
const filter = source.filter && typeof source.filter === 'object' && !Array.isArray(source.filter) ? source.filter : {}
|
|
878
|
+
return { register: source.register, schema: source.schema, filter }
|
|
879
|
+
},
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Count the list behind every tab with `countFrom`: once on mount,
|
|
883
|
+
* when the record changes and after a refresh. A tab keeps its last
|
|
884
|
+
* total while the new one is on its way; a failed count shows none.
|
|
885
|
+
*
|
|
886
|
+
* @return {Promise<void>}
|
|
887
|
+
* @spec openspec/changes/screens-tab-counts-parity/specs/tab-counts/spec.md#requirement-a-tab-can-count-its-list
|
|
888
|
+
*/
|
|
889
|
+
async refreshWidgetCounts() {
|
|
890
|
+
const tabs = Array.isArray(this.content?.tabs) ? this.content.tabs : []
|
|
891
|
+
const seq = ++this.countSeq
|
|
892
|
+
const ctx = { objectId: this.objectId, object: this.objectData || undefined }
|
|
893
|
+
await Promise.all(tabs.map(async (tab, index) => {
|
|
894
|
+
const source = this.countSourceOf(tab)
|
|
895
|
+
if (!source) {
|
|
896
|
+
return
|
|
897
|
+
}
|
|
898
|
+
const total = await fetchListTotal(source.register, source.schema, source.filter, ctx).catch(() => null)
|
|
899
|
+
if (seq === this.countSeq) {
|
|
900
|
+
this.widgetCounts = { ...this.widgetCounts, [index]: total }
|
|
901
|
+
}
|
|
902
|
+
}))
|
|
903
|
+
},
|
|
904
|
+
|
|
707
905
|
/** @return {string} The route hash without its `#`, or ''. */
|
|
708
906
|
currentHash() {
|
|
709
907
|
const fromRoute = this.$route && typeof this.$route.hash === 'string' ? this.$route.hash : ''
|
|
@@ -794,6 +992,13 @@ export default {
|
|
|
794
992
|
</script>
|
|
795
993
|
|
|
796
994
|
<style scoped>
|
|
995
|
+
/* A stack (`widgetIds`): the panel's widget, then cards 20px apart. */
|
|
996
|
+
.cn-tabs-widget__stack {
|
|
997
|
+
display: flex;
|
|
998
|
+
flex-direction: column;
|
|
999
|
+
gap: 20px;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
797
1002
|
/* The panel is the card; the strip is not inside it. There is no title bar
|
|
798
1003
|
above the tabs either: the open tab names the panel, so a title row would
|
|
799
1004
|
say the same thing twice and cost a row of height on a card that is mostly
|
|
@@ -72,6 +72,10 @@ import '../CnDeltaWidget/index.js'
|
|
|
72
72
|
import '../CnGaugeWidget/index.js'
|
|
73
73
|
import '../CnFlowRunsWidget/index.js'
|
|
74
74
|
import '../CnTasksWidget/index.js'
|
|
75
|
+
import '../CnSectionsWidget/index.js'
|
|
76
|
+
import '../CnFactsWidget/index.js'
|
|
77
|
+
import '../CnFieldListWidget/index.js'
|
|
78
|
+
import '../CnRecordListWidget/index.js'
|
|
75
79
|
import '../CnNotepadWidget/index.js'
|
|
76
80
|
import '../CnObjectDataWidget/dashboardRegistration.js'
|
|
77
81
|
import '../CnObjectGeoWidget/dashboardRegistration.js'
|
|
@@ -323,6 +323,19 @@ export default {
|
|
|
323
323
|
default: false,
|
|
324
324
|
},
|
|
325
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Fit the table to the tile: the title column (a column's `grow: true`,
|
|
328
|
+
* else `title` or `name`, else the first) shrinks and ends in an
|
|
329
|
+
* ellipsis so the trailing columns stay visible. On by default; pass
|
|
330
|
+
* `false` for the table's own auto layout. Forwarded to CnDataTable.
|
|
331
|
+
*
|
|
332
|
+
* @spec openspec/changes/narrow-object-table-widgets/specs/narrow-object-table-widgets/spec.md#requirement-a-narrow-object-table-keeps-its-trailing-columns-in-view
|
|
333
|
+
*/
|
|
334
|
+
fitWidth: {
|
|
335
|
+
type: Boolean,
|
|
336
|
+
default: true,
|
|
337
|
+
},
|
|
338
|
+
|
|
326
339
|
/**
|
|
327
340
|
* Route NAME for row-click navigation. Mapped onto CnDataTable's
|
|
328
341
|
* `rowClickRoute` as `{ name: rowRoute, params: { id } }` with the
|
|
@@ -4,12 +4,43 @@
|
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
6
|
<template>
|
|
7
|
-
<div
|
|
7
|
+
<div
|
|
8
|
+
class="cn-workspace-filter-widget"
|
|
9
|
+
:class="{ 'cn-workspace-filter-widget--start': isStart }"
|
|
10
|
+
data-testid="cn-workspace-filter-widget">
|
|
11
|
+
<!-- The start card (`layout: "start"`): a title and one line of
|
|
12
|
+
guidance beside the control, as the "Contact starten" card. -->
|
|
13
|
+
<div v-if="isStart && (title || description)" class="cn-workspace-filter-widget__text">
|
|
14
|
+
<h3 v-if="title" class="cn-workspace-filter-widget__title">
|
|
15
|
+
{{ title }}
|
|
16
|
+
</h3>
|
|
17
|
+
<p v-if="description" class="cn-workspace-filter-widget__description">
|
|
18
|
+
{{ description }}
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
8
22
|
<p v-if="label" class="cn-workspace-filter-widget__label">
|
|
9
23
|
{{ label }}
|
|
10
24
|
</p>
|
|
11
25
|
|
|
12
|
-
|
|
26
|
+
<!-- An object picker (`objectSource`): searches OpenRegister on the
|
|
27
|
+
server, so it reaches every object, not a fetched page. -->
|
|
28
|
+
<CnResourceSelect
|
|
29
|
+
v-if="objectSource"
|
|
30
|
+
class="cn-workspace-filter-widget__control"
|
|
31
|
+
:register="objectSource.register"
|
|
32
|
+
:schema="objectSource.schema"
|
|
33
|
+
:labelField="objectSource.labelField || 'title'"
|
|
34
|
+
:modelValue="selected === undefined || selected === null ? '' : String(selected)"
|
|
35
|
+
:placeholder="tr(objectSource.placeholder || '')"
|
|
36
|
+
:ariaLabelCombobox="tr(objectSource.ariaLabel || '') || title || label || writeKey"
|
|
37
|
+
:preload="objectSource.preload !== false"
|
|
38
|
+
:clearable="Boolean(content.allLabel)"
|
|
39
|
+
:allowCreate="false"
|
|
40
|
+
data-testid="cn-workspace-filter-object"
|
|
41
|
+
@update:modelValue="applySelection($event || '')" />
|
|
42
|
+
|
|
43
|
+
<div v-else-if="loading" class="cn-workspace-filter-widget__status">
|
|
13
44
|
{{ loadingLabel }}
|
|
14
45
|
</div>
|
|
15
46
|
|
|
@@ -47,12 +78,23 @@
|
|
|
47
78
|
</NcCheckboxRadioSwitch>
|
|
48
79
|
</li>
|
|
49
80
|
</ul>
|
|
81
|
+
|
|
82
|
+
<component
|
|
83
|
+
:is="startLink.to ? 'router-link' : 'a'"
|
|
84
|
+
v-if="startLink"
|
|
85
|
+
:to="startLink.to || undefined"
|
|
86
|
+
:href="startLink.href || undefined"
|
|
87
|
+
class="cn-workspace-filter-widget__link"
|
|
88
|
+
data-testid="cn-workspace-filter-link">
|
|
89
|
+
{{ startLink.label }}
|
|
90
|
+
</component>
|
|
50
91
|
</div>
|
|
51
92
|
</template>
|
|
52
93
|
|
|
53
94
|
<script>
|
|
54
95
|
import { translate as t } from '@nextcloud/l10n'
|
|
55
96
|
import { NcCheckboxRadioSwitch, NcSelect } from '@nextcloud/vue'
|
|
97
|
+
import CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'
|
|
56
98
|
import { fetchEndpointSource } from '../../composables/useEndpointSource.js'
|
|
57
99
|
import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
58
100
|
|
|
@@ -94,7 +136,7 @@ import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
|
94
136
|
export default {
|
|
95
137
|
name: 'CnWorkspaceFilterWidget',
|
|
96
138
|
|
|
97
|
-
components: { NcSelect, NcCheckboxRadioSwitch },
|
|
139
|
+
components: { CnResourceSelect, NcSelect, NcCheckboxRadioSwitch },
|
|
98
140
|
|
|
99
141
|
inject: {
|
|
100
142
|
/**
|
|
@@ -104,13 +146,23 @@ export default {
|
|
|
104
146
|
* Null on pages without one (the widget then just emits `@change`).
|
|
105
147
|
*/
|
|
106
148
|
cnWorkspaceContext: { default: null },
|
|
149
|
+
/** Host translate function, for the start card's texts. */
|
|
150
|
+
cnTranslate: { default: () => (key) => key },
|
|
107
151
|
},
|
|
108
152
|
|
|
109
153
|
props: {
|
|
110
154
|
/**
|
|
111
155
|
* Persisted configuration blob.
|
|
112
156
|
*
|
|
113
|
-
*
|
|
157
|
+
* Start card keys (abstract-start-card): `layout: "start"` draws
|
|
158
|
+
* `title` and `description` beside the control, and `link`
|
|
159
|
+
* (`{ label, route? | href? }`) after it. `objectSource`
|
|
160
|
+
* (`{ register, schema, labelField?, placeholder?, ariaLabel?, preload? }`)
|
|
161
|
+
* makes the control an OpenRegister object picker that searches on the
|
|
162
|
+
* server and writes the picked object's id; without a selection and
|
|
163
|
+
* without `allLabel` it selects the first object.
|
|
164
|
+
*
|
|
165
|
+
* @type {{label?: string, writes?: string, style?: string, allLabel?: string, showCounts?: boolean, options?: Array, source?: object, endpointSource?: object, objectSource?: object, default?: (string|number), layout?: string, title?: string, description?: string, link?: object}}
|
|
114
166
|
*/
|
|
115
167
|
content: {
|
|
116
168
|
type: Object,
|
|
@@ -137,6 +189,50 @@ export default {
|
|
|
137
189
|
return this.content.label || ''
|
|
138
190
|
},
|
|
139
191
|
|
|
192
|
+
/** Whether the widget draws the start card. */
|
|
193
|
+
isStart() {
|
|
194
|
+
return this.content.layout === 'start'
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
/** The start card's title, translated. */
|
|
198
|
+
title() {
|
|
199
|
+
return this.tr(this.content.title || '')
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
/** The start card's guidance line, translated. */
|
|
203
|
+
description() {
|
|
204
|
+
return this.tr(this.content.description || '')
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The link after the control, or null.
|
|
209
|
+
*
|
|
210
|
+
* @return {{label: string, to?: object, href?: string}|null}
|
|
211
|
+
*/
|
|
212
|
+
startLink() {
|
|
213
|
+
const link = this.content.link
|
|
214
|
+
if (!link || typeof link !== 'object' || !link.label) {
|
|
215
|
+
return null
|
|
216
|
+
}
|
|
217
|
+
if (link.route && typeof link.route === 'object' && link.route.name) {
|
|
218
|
+
return { label: this.tr(link.label), to: link.route }
|
|
219
|
+
}
|
|
220
|
+
if (typeof link.href === 'string' && link.href !== '') {
|
|
221
|
+
return { label: this.tr(link.label), href: link.href }
|
|
222
|
+
}
|
|
223
|
+
return null
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The object picker declaration, or null.
|
|
228
|
+
*
|
|
229
|
+
* @return {{register: string, schema: string, labelField?: string}|null}
|
|
230
|
+
*/
|
|
231
|
+
objectSource() {
|
|
232
|
+
const src = this.content.objectSource
|
|
233
|
+
return src && typeof src === 'object' && src.register && src.schema ? src : null
|
|
234
|
+
},
|
|
235
|
+
|
|
140
236
|
/** Choice style: 'select' or 'radio' (default). */
|
|
141
237
|
style() {
|
|
142
238
|
return this.content.style === 'select' ? 'select' : 'radio'
|
|
@@ -263,6 +359,10 @@ export default {
|
|
|
263
359
|
* @return {Promise<void>}
|
|
264
360
|
*/
|
|
265
361
|
async loadOptions() {
|
|
362
|
+
if (this.objectSource) {
|
|
363
|
+
await this.seedFirstObject()
|
|
364
|
+
return
|
|
365
|
+
}
|
|
266
366
|
if (this.content.endpointSource && this.content.endpointSource.url) {
|
|
267
367
|
await this.loadFromEndpoint()
|
|
268
368
|
} else if (this.content.source && this.content.source.schema) {
|
|
@@ -281,6 +381,51 @@ export default {
|
|
|
281
381
|
}
|
|
282
382
|
},
|
|
283
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Translate a manifest string through the host app.
|
|
386
|
+
*
|
|
387
|
+
* @param {string} text The text.
|
|
388
|
+
* @return {string}
|
|
389
|
+
*/
|
|
390
|
+
tr(text) {
|
|
391
|
+
if (typeof text !== 'string' || text === '') {
|
|
392
|
+
return ''
|
|
393
|
+
}
|
|
394
|
+
return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* The object picker's default: with nothing selected, no `default` and
|
|
399
|
+
* no `allLabel`, select the first object the source lists, so the page
|
|
400
|
+
* always has one in focus. Fail-safe: a failed read selects nothing.
|
|
401
|
+
*
|
|
402
|
+
* @return {Promise<void>}
|
|
403
|
+
*/
|
|
404
|
+
async seedFirstObject() {
|
|
405
|
+
if ((this.selected !== undefined && this.selected !== '') || this.content.allLabel || this.content.default !== undefined) {
|
|
406
|
+
return
|
|
407
|
+
}
|
|
408
|
+
const src = this.objectSource
|
|
409
|
+
try {
|
|
410
|
+
const [{ default: axios }, { generateUrl }] = await Promise.all([
|
|
411
|
+
import('@nextcloud/axios'),
|
|
412
|
+
import('@nextcloud/router'),
|
|
413
|
+
])
|
|
414
|
+
const url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: src.register, schema: src.schema })
|
|
415
|
+
const res = await axios.get(url, { params: { _limit: 1 } })
|
|
416
|
+
const data = res && res.data
|
|
417
|
+
const rows = Array.isArray(data) ? data : ((data && data.results) || [])
|
|
418
|
+
const first = rows[0]
|
|
419
|
+
const id = first && (first.id || (first['@self'] && first['@self'].id))
|
|
420
|
+
if (id && (this.selected === undefined || this.selected === '') && this.readWorkspace() === undefined) {
|
|
421
|
+
this.selected = String(id)
|
|
422
|
+
this.writeWorkspace(String(id))
|
|
423
|
+
}
|
|
424
|
+
} catch {
|
|
425
|
+
// Nothing selected: the siblings wait for a pick.
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
|
|
284
429
|
/**
|
|
285
430
|
* Fetch options from an `endpointSource` (array of options at
|
|
286
431
|
* responsePath). Fail-safe: a failed fetch leaves an empty list.
|
|
@@ -465,6 +610,45 @@ export default {
|
|
|
465
610
|
gap: 2px;
|
|
466
611
|
}
|
|
467
612
|
|
|
613
|
+
/* The start card: text left, control (and link) right, as PqDashboard's "Contact starten". */
|
|
614
|
+
.cn-workspace-filter-widget--start {
|
|
615
|
+
flex-direction: row;
|
|
616
|
+
flex-wrap: wrap;
|
|
617
|
+
align-items: center;
|
|
618
|
+
gap: 16px 24px;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.cn-workspace-filter-widget__text {
|
|
622
|
+
flex: 1 1 320px;
|
|
623
|
+
min-width: 0;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.cn-workspace-filter-widget__title {
|
|
627
|
+
margin: 0 0 4px;
|
|
628
|
+
font-size: 17px;
|
|
629
|
+
font-weight: 700;
|
|
630
|
+
color: var(--color-main-text);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.cn-workspace-filter-widget__description {
|
|
634
|
+
margin: 0;
|
|
635
|
+
max-width: 420px;
|
|
636
|
+
color: var(--color-text-maxcontrast);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.cn-workspace-filter-widget--start .cn-workspace-filter-widget__control,
|
|
640
|
+
.cn-workspace-filter-widget--start > .v-select {
|
|
641
|
+
flex: 0 1 480px;
|
|
642
|
+
min-width: 240px;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
.cn-workspace-filter-widget__link {
|
|
646
|
+
font-weight: 600;
|
|
647
|
+
color: var(--color-primary-element);
|
|
648
|
+
text-decoration: underline;
|
|
649
|
+
white-space: nowrap;
|
|
650
|
+
}
|
|
651
|
+
|
|
468
652
|
.cn-workspace-filter-widget__count {
|
|
469
653
|
margin-inline-start: 6px;
|
|
470
654
|
padding: 0 6px;
|
package/src/components/index.js
CHANGED
|
@@ -109,7 +109,7 @@ export { CnTreeView } from './CnTreeView/index.js'
|
|
|
109
109
|
export { CnUserActionMenu } from './CnUserActionMenu/index.js'
|
|
110
110
|
export { CnNotesCard } from './CnNotesCard/index.js'
|
|
111
111
|
export { CnTasksCard } from './CnTasksCard/index.js'
|
|
112
|
-
export { CnFilesBrowser } from './CnFilesBrowser/index.js'
|
|
112
|
+
export { CnFilesBrowser, createOpenRegisterSource } from './CnFilesBrowser/index.js'
|
|
113
113
|
export { CnFilesCard } from './CnFilesCard/index.js'
|
|
114
114
|
export { CnFileManager } from './CnFileManager/index.js'
|
|
115
115
|
export { CnRelatedFiles } from './CnRelatedFiles/index.js'
|
|
@@ -216,6 +216,10 @@ export { CnFlowStepOutcomes } from './CnFlowStepOutcomes/index.js'
|
|
|
216
216
|
export { CnFlowRunsWidgetForm } from './CnFlowRunsWidgetForm/index.js'
|
|
217
217
|
export { CnTasksWidget } from './CnTasksWidget/index.js'
|
|
218
218
|
export { CnTasksWidgetForm } from './CnTasksWidgetForm/index.js'
|
|
219
|
+
export { CnSectionsWidget } from './CnSectionsWidget/index.js'
|
|
220
|
+
export { CnFactsWidget } from './CnFactsWidget/index.js'
|
|
221
|
+
export { CnFieldListWidget } from './CnFieldListWidget/index.js'
|
|
222
|
+
export { CnRecordListWidget } from './CnRecordListWidget/index.js'
|
|
219
223
|
export { CnFilePreview } from './CnFilePreview/index.js'
|
|
220
224
|
export { CnTaskFormDialog } from './CnTaskFormDialog/index.js'
|
|
221
225
|
export { CnChildRecordsField } from './CnChildRecordsField/index.js'
|