@conduction/nextcloud-vue 2.79.0 → 2.81.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/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 +13 -11
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +179 -13
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +52 -16
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +96 -4
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +158 -110
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js +10 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js +252 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js +140 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js +26 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js +10 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js +354 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js +172 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js +27 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +65 -5
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +19 -11
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +4 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +134 -4
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +21 -4
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +24 -0
- 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/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.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 +867 -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 +626 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +396 -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 +302 -4
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +65 -17
- 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 +78 -3
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +333 -73
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +79 -17
- 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/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +65 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.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/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +10 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +174 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +75 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js +28 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js.map +1 -0
- 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 +411 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +178 -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/CnSavedViewsWidget/CnSavedViewsWidget.vue.js +10 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js +222 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js +64 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js +30 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js.map +1 -0
- 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/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +10 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +347 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +184 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/index.js +27 -0
- package/dist/esm/components/CnSettingsFormWidget/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 +87 -6
- 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/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +10 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +159 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js +60 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js +26 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js.map +1 -0
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +168 -7
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +86 -23
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +80 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +75 -32
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +10 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +269 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js +106 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/index.js +27 -0
- package/dist/esm/components/CnToggleTableWidget/index.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +11 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
- 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/CnWidgetObjectTable/dashboardRegistration.js +5 -0
- package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +301 -6
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +153 -55
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useFormDraft.js +140 -9
- package/dist/esm/composables/useFormDraft.js.map +1 -1
- package/dist/esm/composables/useListView.js +4 -4
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/index.js +21 -1
- 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 +20 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +70 -2
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +2192 -222
- package/dist/esm/utils/contentHeaderLink.js +51 -0
- package/dist/esm/utils/contentHeaderLink.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/facets.js +75 -3
- package/dist/esm/utils/facets.js.map +1 -1
- package/dist/esm/utils/fetchFilterCounts.js +40 -5
- package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +120 -0
- package/dist/esm/utils/fillTemplate.js.map +1 -0
- package/dist/esm/utils/initials.js +32 -0
- package/dist/esm/utils/initials.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +11 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/objectWrite.js +59 -0
- package/dist/esm/utils/objectWrite.js.map +1 -0
- package/dist/esm/utils/pageControls.js +81 -0
- package/dist/esm/utils/pageControls.js.map +1 -0
- package/dist/esm/utils/personalLenses.js +40 -3
- package/dist/esm/utils/personalLenses.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 +12 -1
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/resolveManifestTokens.js +2 -0
- package/dist/esm/utils/resolveManifestTokens.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 +22 -4
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/userGroupsPeek.js +91 -0
- package/dist/esm/utils/userGroupsPeek.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/visibleWhen.js +57 -5
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/esm/utils/widgetDispatch.js +7 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/esm/utils/widgetVisibility.js +4 -57
- package/dist/esm/utils/widgetVisibility.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +19708 -9301
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +2192 -222
- package/l10n/en.json +20 -1
- package/l10n/nl.json +70 -2
- package/package.json +2 -1
- package/scripts/check-docs-mdx.mjs +108 -0
- package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
- package/src/components/CnActionsBar/CnActionsBar.md +4 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +48 -11
- package/src/components/CnAppRoot/CnAppRoot.md +4 -2
- package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
- package/src/components/CnBannerWidget/CnBannerWidget.vue +244 -23
- package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +235 -5
- package/src/components/CnChipsFieldWidget/CnChipsFieldWidget.vue +433 -0
- package/src/components/CnChipsFieldWidget/index.js +25 -0
- package/src/components/CnChoicePickerWidget/CnChoicePickerWidget.vue +556 -0
- package/src/components/CnChoicePickerWidget/index.js +26 -0
- package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +78 -5
- package/src/components/CnDataTable/CnDataTable.vue +22 -12
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +152 -6
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +66 -0
- package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
- package/src/components/CnFactsWidget/CnFactsWidget.vue +1190 -0
- package/src/components/CnFactsWidget/index.js +29 -0
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +1008 -0
- package/src/components/CnFieldListWidget/index.js +28 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
- package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
- package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
- package/src/components/CnFormDialog/CnFormDialog.md +9 -3
- package/src/components/CnFormDialog/CnFormDialog.vue +69 -21
- package/src/components/CnFormPage/CnFormPage.vue +99 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +309 -9
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
- package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +69 -2
- package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
- package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
- package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
- package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
- package/src/components/CnRadioCardsWidget/index.js +27 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +652 -0
- package/src/components/CnRecordListWidget/index.js +30 -0
- package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
- package/src/components/CnSavedViewsWidget/index.js +29 -0
- package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
- package/src/components/CnSectionsWidget/index.js +34 -0
- package/src/components/CnSegmentedControl/CnSegmentedControl.vue +13 -0
- package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +555 -0
- package/src/components/CnSettingsFormWidget/index.js +26 -0
- package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
- package/src/components/CnStatWidget/CnStatWidget.vue +88 -7
- package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
- package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +226 -0
- package/src/components/CnSwitchRowsWidget/index.js +25 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +263 -8
- package/src/components/CnTasksWidget/CnTasksWidget.vue +175 -15
- package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +386 -0
- package/src/components/CnToggleTableWidget/index.js +26 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +11 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
- package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +493 -11
- package/src/components/index.js +19 -0
- package/src/composables/__tests__/useFormDraft.spec.js +69 -0
- package/src/composables/useFormDraft.js +139 -8
- package/src/composables/useListView.js +4 -4
- 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 +113 -2
- package/src/css/look-board-index.css +90 -8
- package/src/css/look-board.css +109 -2
- package/src/css/table.css +19 -0
- package/src/index.js +19 -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 +196 -13
- package/src/utils/contentHeaderLink.js +48 -0
- package/src/utils/detailActionModel.js +59 -2
- package/src/utils/facets.js +74 -2
- package/src/utils/fetchFilterCounts.js +39 -4
- package/src/utils/fillTemplate.js +116 -0
- package/src/utils/initials.js +29 -0
- package/src/utils/libraryWidgetKeys.js +11 -0
- package/src/utils/objectWrite.js +55 -0
- package/src/utils/pageControls.js +78 -0
- package/src/utils/personalLenses.js +39 -2
- package/src/utils/queryPresets.js +93 -0
- package/src/utils/resolveFilterTokens.js +14 -1
- package/src/utils/resolveManifestTokens.js +2 -0
- package/src/utils/schema.js +21 -0
- package/src/utils/sentinelTokens.js +22 -4
- package/src/utils/userGroupsPeek.js +87 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/visibleWhen.js +56 -4
- package/src/utils/widgetDispatch.js +7 -1
- package/src/utils/widgetVisibility.js +7 -56
|
@@ -50,7 +50,7 @@ import { resolveFilterValue, resolveFilterTokens } from './resolveFilterTokens.j
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
/** Supported visibleWhen comparison operators. */
|
|
53
|
-
const VISIBLE_WHEN_OPS = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'empty', 'notEmpty'];
|
|
53
|
+
const VISIBLE_WHEN_OPS = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'empty', 'notEmpty', 'in'];
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Read a dot-path off an object (`'a.b.c'`); the object itself when no
|
|
@@ -94,6 +94,12 @@ function compareVisibleWhen(actual, op, expected, ctx) {
|
|
|
94
94
|
// The shared @-token grammar, so a condition can name the READER (`@me`)
|
|
95
95
|
// rather than a literal only the server knows.
|
|
96
96
|
const right = resolveFilterValue(expected, ctx || {});
|
|
97
|
+
// `in` holds when the left-hand value is one of the listed values
|
|
98
|
+
// (screens-page-lenses: a widget shown under several chips).
|
|
99
|
+
if (operator === 'in') {
|
|
100
|
+
const list = Array.isArray(right) ? right : [right];
|
|
101
|
+
return list.some((entry) => entry === actual || String(entry) === String(actual))
|
|
102
|
+
}
|
|
97
103
|
if (operator === 'eq' || operator === 'neq') {
|
|
98
104
|
const equal = actual === right || String(actual) === String(right);
|
|
99
105
|
return operator === 'eq' ? equal : !equal
|
|
@@ -115,17 +121,53 @@ function compareVisibleWhen(actual, op, expected, ctx) {
|
|
|
115
121
|
return a <= b
|
|
116
122
|
}
|
|
117
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Whether a condition reads the page workspace context (`workspace: "<key>"`).
|
|
126
|
+
* Such a condition is answered synchronously from the reactive bag, so a
|
|
127
|
+
* widget appears and disappears the moment a page control writes its key
|
|
128
|
+
* (screens-page-lenses). A condition that also names an endpoint or a source
|
|
129
|
+
* is not a workspace condition.
|
|
130
|
+
*
|
|
131
|
+
* @param {object|null} cond The visibleWhen condition.
|
|
132
|
+
* @return {boolean} True for a workspace-mode condition.
|
|
133
|
+
* @spec openspec/changes/screens-page-lenses/specs/page-lenses/spec.md#requirement-a-widget-can-be-shown-by-a-workspace-value
|
|
134
|
+
*/
|
|
135
|
+
function isWorkspaceCondition(cond) {
|
|
136
|
+
return Boolean(cond) && typeof cond === 'object' && typeof cond.workspace === 'string' && cond.workspace !== ''
|
|
137
|
+
&& !cond.endpoint && !cond.source
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Evaluate a workspace-mode condition against the workspace bag. No fetch.
|
|
142
|
+
* A key nobody wrote is unset, so only `empty` and `neq` hold for it.
|
|
143
|
+
*
|
|
144
|
+
* @param {{workspace: string, op?: string, value?: unknown}} cond The condition.
|
|
145
|
+
* @param {object} [workspace] The plain workspace bag.
|
|
146
|
+
* @return {boolean} Whether the guarded element shows.
|
|
147
|
+
* @spec openspec/changes/screens-page-lenses/specs/page-lenses/spec.md#requirement-a-widget-can-be-shown-by-a-workspace-value
|
|
148
|
+
*/
|
|
149
|
+
function evaluateWorkspaceCondition(cond, workspace) {
|
|
150
|
+
const bag = workspace && typeof workspace === 'object' ? workspace : {};
|
|
151
|
+
return compareVisibleWhen(bag[cond.workspace], cond.op || 'eq', cond.value, { workspace: bag })
|
|
152
|
+
}
|
|
153
|
+
|
|
118
154
|
/**
|
|
119
155
|
* Resolve the condition's left-hand value from its endpoint / OpenRegister
|
|
120
156
|
* source / local object context (see the module docblock for the three
|
|
121
157
|
* modes). Throws on unusable conditions and failed fetches — callers that
|
|
122
158
|
* want the fail-safe boolean use {@link evaluateVisibleWhen}.
|
|
123
159
|
*
|
|
124
|
-
*
|
|
160
|
+
* A `source` may carry `order` (`{ field: "asc" | "desc" }`), so "the first
|
|
161
|
+
* row" is the first of an ordered query. A caller that wants that row, not
|
|
162
|
+
* just the value, passes `sink`: the first result is written to `sink.row`.
|
|
163
|
+
*
|
|
164
|
+
* @param {{endpoint?: string, source?: {register: string, schema: string, filter?: object, order?: object}, field?: string}} cond The visibleWhen condition.
|
|
125
165
|
* @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} [ctx] The caller's token / object context.
|
|
166
|
+
* @param {{row?: object}} [sink] Receives the first result of a `source` query as `row`.
|
|
126
167
|
* @return {Promise<unknown>} The value `field` points at.
|
|
168
|
+
* @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-banner-can-name-the-first-row-of-a-query
|
|
127
169
|
*/
|
|
128
|
-
async function readVisibleWhenValue(cond, ctx) {
|
|
170
|
+
async function readVisibleWhenValue(cond, ctx, sink) {
|
|
129
171
|
if (cond.endpoint) {
|
|
130
172
|
const response = await fetch(prefixUrl(cond.endpoint), { headers: buildHeaders() });
|
|
131
173
|
if (!response.ok) {
|
|
@@ -137,7 +179,11 @@ async function readVisibleWhenValue(cond, ctx) {
|
|
|
137
179
|
const src = cond.source;
|
|
138
180
|
if (src && src.register && src.schema) {
|
|
139
181
|
const filter = resolveFilterTokens(src.filter || {}, ctx);
|
|
140
|
-
const
|
|
182
|
+
const order = {};
|
|
183
|
+
for (const [field, dir] of Object.entries(src.order && typeof src.order === 'object' ? src.order : {})) {
|
|
184
|
+
order[`_order[${field}]`] = dir === 'desc' ? 'desc' : 'asc';
|
|
185
|
+
}
|
|
186
|
+
const qs = buildQueryString({ ...filter, ...order, _limit: 1 });
|
|
141
187
|
const url = prefixUrl(`/apps/openregister/api/objects/${src.register}/${src.schema}${qs}`);
|
|
142
188
|
const response = await fetch(url, { headers: buildHeaders() });
|
|
143
189
|
if (!response.ok) {
|
|
@@ -148,6 +194,9 @@ async function readVisibleWhenValue(cond, ctx) {
|
|
|
148
194
|
return data.total ?? (Array.isArray(data.results) ? data.results.length : 0)
|
|
149
195
|
}
|
|
150
196
|
const first = Array.isArray(data.results) ? data.results[0] : data;
|
|
197
|
+
if (sink && typeof sink === 'object') {
|
|
198
|
+
sink.row = first && typeof first === 'object' ? first : undefined;
|
|
199
|
+
}
|
|
151
200
|
return readVisibleWhenPath(first, cond.field)
|
|
152
201
|
}
|
|
153
202
|
// Local mode (Wave 3): evaluate against the caller's object context.
|
|
@@ -186,6 +235,9 @@ async function evaluateVisibleWhen(cond, ctx) {
|
|
|
186
235
|
return false
|
|
187
236
|
}
|
|
188
237
|
}
|
|
238
|
+
if (isWorkspaceCondition(cond)) {
|
|
239
|
+
return evaluateWorkspaceCondition(cond, ctx && ctx.workspace)
|
|
240
|
+
}
|
|
189
241
|
try {
|
|
190
242
|
// `appInstalled` is checked FIRST and on its own: it answers "is the app
|
|
191
243
|
// that backs this action even here", which has to settle before any of
|
|
@@ -302,5 +354,5 @@ function isLocallyDecidableVisibleWhen(cond) {
|
|
|
302
354
|
return !cond.endpoint && !cond.source
|
|
303
355
|
}
|
|
304
356
|
|
|
305
|
-
export { VISIBLE_WHEN_OPS, compareVisibleWhen, evaluateVisibleWhen, evaluateVisibleWhenLocal, isLocallyDecidableVisibleWhen, readVisibleWhenPath, readVisibleWhenValue };
|
|
357
|
+
export { VISIBLE_WHEN_OPS, compareVisibleWhen, evaluateVisibleWhen, evaluateVisibleWhenLocal, evaluateWorkspaceCondition, isLocallyDecidableVisibleWhen, isWorkspaceCondition, readVisibleWhenPath, readVisibleWhenValue };
|
|
306
358
|
//# sourceMappingURL=visibleWhen.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visibleWhen.js","sources":["../../../src/utils/visibleWhen.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * visibleWhen — the shared declarative visibility predicate (Wave 1 banner\n * shape, generalised in Wave 3 of nextcloud-vue#91 for manifest actions).\n *\n * A `visibleWhen` condition is\n * `{ appInstalled?, endpoint?, source?, field?, op?, value }`.\n *\n * `appInstalled` is a precondition rather than a mode: it names the Nextcloud\n * app that backs the guarded element, and it is checked before anything else.\n * On its own it IS the whole condition; combined with one of the modes below\n * it gates that mode. It exists because the menu's `visibleIf` already spoke\n * this word and `visibleWhen` did not, so the same manifest vocabulary meant\n * two different things in two places.\n *\n * The value itself comes from one of THREE evaluation modes (first match wins):\n *\n * 1. `endpoint` — GET a same-origin JSON endpoint; `field` is a dot-path\n * into the response body.\n * 2. `source` — `{ register, schema, filter? }`, an OpenRegister object\n * query (`filter` supports the shared @-token grammar). `field` is a\n * dot-path into the FIRST result; omit it — or use `@total` — to\n * compare the collection total.\n * 3. Neither — the LOCAL mode (Wave 3): `field` is a dot-path into the\n * caller's object context (`ctx.object`, e.g. the loaded detail-page\n * record) — no request is made. This is how detail header actions gate\n * on the current record's state (`{ field: \"lifecycleState\", op: \"eq\",\n * value: \"approved\" }`).\n *\n * `op` is `eq | neq | gt | gte | lt | lte` (default `eq`); `value` is the\n * literal right-hand side. Evaluation is FAIL-SAFE: any fetch / shape error\n * resolves `false` (hidden) — a broken predicate never breaks a page.\n *\n * manifest-form-logic adds `evaluateVisibleWhenLocal(cond, data)`, a SYNC\n * counterpart to `evaluateVisibleWhen()` for the LOCAL mode only, composed\n * from `readVisibleWhenPath` + `compareVisibleWhen` with no fetch / promise.\n * `CnFormPage` calls it on every form-data change so conditions gating one\n * field on another's live value resolve without flicker. `endpoint` /\n * `source` conditions on form fields still go through the async\n * `evaluateVisibleWhen()`, evaluated once at mount and cached.\n *\n * @module utils/visibleWhen\n */\n\nimport { isAppInstalled } from './appInstalled.js'\nimport { buildHeaders, buildQueryString, prefixUrl } from './headers.js'\nimport { resolveFilterTokens, resolveFilterValue } from './resolveFilterTokens.js'\n\n/** Supported visibleWhen comparison operators. */\nexport const VISIBLE_WHEN_OPS = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'empty', 'notEmpty']\n\n/**\n * Read a dot-path off an object (`'a.b.c'`); the object itself when no\n * field is given.\n *\n * @param {object} data The source object.\n * @param {string} [field] Dot-path into the object.\n * @return {unknown} The value at the path (undefined on a missing segment).\n */\nexport function readVisibleWhenPath(data, field) {\n\tif (!field) {\n\t\treturn data\n\t}\n\treturn String(field).split('.').reduce((obj, key) => (obj === null || obj === undefined ? obj : obj[key]), data)\n}\n\n/**\n * Apply a visibleWhen comparison operator. Ordering operators coerce both\n * sides to Number; equality compares loosely-normalized primitives\n * (`String(a) === String(b)` when types differ) so `\"3\" eq 3` holds for\n * JSON round-trips.\n *\n * @param {unknown} actual The resolved left-hand value.\n * @param {string} op The operator (`eq` when unknown).\n * @param {unknown} expected The declared right-hand value.\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} [ctx] Token-resolution\n * context for the right-hand side, so `@object.<field>` and `@workspace.<key>` resolve\n * as well as `@me`. Omitted, only the context-free tokens resolve.\n * @return {boolean} Whether the comparison holds.\n */\nexport function compareVisibleWhen(actual, op, expected, ctx) {\n\tconst operator = VISIBLE_WHEN_OPS.includes(op) ? op : 'eq'\n\t// `empty` takes no `value`. It is the one question eq cannot ask: an unset\n\t// field arrives as undefined, null or '' depending on the store and the\n\t// serialiser, and `String(null)` is the word \"null\" rather than blank.\n\tif (operator === 'empty' || operator === 'notEmpty') {\n\t\tconst blank = actual === undefined || actual === null || actual === ''\n\t\t\t|| (Array.isArray(actual) === true && actual.length === 0)\n\t\treturn operator === 'empty' ? blank : !blank\n\t}\n\t// The shared @-token grammar, so a condition can name the READER (`@me`)\n\t// rather than a literal only the server knows.\n\tconst right = resolveFilterValue(expected, ctx || {})\n\tif (operator === 'eq' || operator === 'neq') {\n\t\tconst equal = actual === right || String(actual) === String(right)\n\t\treturn operator === 'eq' ? equal : !equal\n\t}\n\tconst a = Number(actual)\n\tconst b = Number(right)\n\tif (!Number.isFinite(a) || !Number.isFinite(b)) {\n\t\treturn false\n\t}\n\tif (operator === 'gt') {\n\t\treturn a > b\n\t}\n\tif (operator === 'gte') {\n\t\treturn a >= b\n\t}\n\tif (operator === 'lt') {\n\t\treturn a < b\n\t}\n\treturn a <= b\n}\n\n/**\n * Resolve the condition's left-hand value from its endpoint / OpenRegister\n * source / local object context (see the module docblock for the three\n * modes). Throws on unusable conditions and failed fetches — callers that\n * want the fail-safe boolean use {@link evaluateVisibleWhen}.\n *\n * @param {{endpoint?: string, source?: {register: string, schema: string, filter?: object}, field?: string}} cond The visibleWhen condition.\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} [ctx] The caller's token / object context.\n * @return {Promise<unknown>} The value `field` points at.\n */\nexport async function readVisibleWhenValue(cond, ctx) {\n\tif (cond.endpoint) {\n\t\tconst response = await fetch(prefixUrl(cond.endpoint), { headers: buildHeaders() })\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`endpoint returned ${response.status}`)\n\t\t}\n\t\tconst data = await response.json()\n\t\treturn readVisibleWhenPath(data, cond.field)\n\t}\n\tconst src = cond.source\n\tif (src && src.register && src.schema) {\n\t\tconst filter = resolveFilterTokens(src.filter || {}, ctx)\n\t\tconst qs = buildQueryString({ ...filter, _limit: 1 })\n\t\tconst url = prefixUrl(`/apps/openregister/api/objects/${src.register}/${src.schema}${qs}`)\n\t\tconst response = await fetch(url, { headers: buildHeaders() })\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`source returned ${response.status}`)\n\t\t}\n\t\tconst data = await response.json()\n\t\tif (!cond.field || cond.field === '@total') {\n\t\t\treturn data.total ?? (Array.isArray(data.results) ? data.results.length : 0)\n\t\t}\n\t\tconst first = Array.isArray(data.results) ? data.results[0] : data\n\t\treturn readVisibleWhenPath(first, cond.field)\n\t}\n\t// Local mode (Wave 3): evaluate against the caller's object context.\n\tif (cond.field && ctx && ctx.object && typeof ctx.object === 'object') {\n\t\treturn readVisibleWhenPath(ctx.object, cond.field)\n\t}\n\tthrow new Error('visibleWhen needs an endpoint, a source, or a field on the object context')\n}\n\n/**\n * Evaluate a visibleWhen condition to a boolean, FAIL-SAFE: any error\n * (fetch failure, unusable condition, missing context) resolves `false`.\n * A `null` / `undefined` condition resolves `true` (no condition = always\n * visible) so callers can pass the raw config value straight through.\n *\n * @param {{endpoint?: string, source?: object, field?: string, op?: string, value?: unknown}|null} cond The condition (or null).\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} [ctx] The caller's token / object context.\n * @return {Promise<boolean>} Whether the guarded element should show.\n */\nexport async function evaluateVisibleWhen(cond, ctx) {\n\tif (!cond) {\n\t\treturn true\n\t}\n\t// `all` / `any` compose conditions, so a gate can be more than one fact.\n\t// Without it a claim button could say \"not closed\" OR \"nobody holds it\",\n\t// never both, and the weaker of the two is the one that ships.\n\tif (Array.isArray(cond.all) === true || Array.isArray(cond.any) === true) {\n\t\ttry {\n\t\t\t// Read the key that IS an array: a malformed `{ all: {}, any: [] }`\n\t\t\t// enters this branch on `any`, and `cond.all ?? cond.any` would hand\n\t\t\t// the object to `.map`.\n\t\t\tconst parts = Array.isArray(cond.all) === true ? cond.all : cond.any\n\t\t\tconst results = await Promise.all(parts.map((part) => evaluateVisibleWhen(part, ctx)))\n\t\t\treturn Array.isArray(cond.all) === true ? results.every(Boolean) : results.some(Boolean)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\ttry {\n\t\t// `appInstalled` is checked FIRST and on its own: it answers \"is the app\n\t\t// that backs this action even here\", which has to settle before any of\n\t\t// the other modes are worth evaluating. The menu's `visibleIf` already\n\t\t// spoke this word, and `visibleWhen` did not, so a manifest author who\n\t\t// wrote `{ appInstalled: \"humaniq\" }` here got a condition with no\n\t\t// `field` — which the local mode rejects as malformed and hides. The\n\t\t// button then never appeared, and nothing said why.\n\t\tif (typeof cond.appInstalled === 'string' && cond.appInstalled !== '') {\n\t\t\tif (!isAppInstalled(cond.appInstalled)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// `appInstalled` on its own IS the whole condition. Combined with a\n\t\t\t// field/endpoint/source it acts as a precondition, and evaluation\n\t\t\t// falls through to that.\n\t\t\tif (!cond.field && !cond.endpoint && !cond.source) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\tconst actual = await readVisibleWhenValue(cond, ctx)\n\t\treturn compareVisibleWhen(actual, cond.op || 'eq', cond.value, ctx)\n\t} catch (error) {\n\t\t// Still false; `ctx.onError` lets a caller (a journey run) record why.\n\t\tif (ctx && typeof ctx.onError === 'function') {\n\t\t\tctx.onError(error, cond)\n\t\t}\n\t\treturn false\n\t}\n}\n\n/**\n * Evaluate a LOCAL-mode visibleWhen condition synchronously against a\n * plain data object (`CnFormPage`'s live `formData`). No fetch, no\n * promise — pure composition of {@link readVisibleWhenPath} and\n * {@link compareVisibleWhen}, so it is safe to call on every keystroke.\n *\n * A nullish condition resolves `true` (no condition = always visible),\n * mirroring {@link evaluateVisibleWhen}'s fail-safe posture. A malformed\n * condition — not a plain object, missing `field`, or declaring\n * `endpoint` / `source` (those are NOT local-mode conditions) — resolves\n * `false` (hidden).\n *\n * @param {{field?: string, op?: string, value?: unknown}|null} cond The LOCAL visibleWhen condition (or null).\n * @param {object} data The live data object `field` dot-paths into (e.g. formData).\n * @return {boolean} Whether the guarded element should show.\n */\nexport function evaluateVisibleWhenLocal(cond, data) {\n\tif (cond === null || cond === undefined) {\n\t\treturn true\n\t}\n\tif (typeof cond !== 'object' || Array.isArray(cond)) {\n\t\treturn false\n\t}\n\tif (Array.isArray(cond.all) === true || Array.isArray(cond.any) === true) {\n\t\ttry {\n\t\t\tconst parts = Array.isArray(cond.all) === true ? cond.all : cond.any\n\t\t\tconst results = parts.map((part) => evaluateVisibleWhenLocal(part, data))\n\t\t\treturn Array.isArray(cond.all) === true ? results.every(Boolean) : results.some(Boolean)\n\t\t} catch {\n\t\t\t// Same posture as the async twin: a malformed composition hides the\n\t\t\t// one element, rather than throwing out of the computed that calls it\n\t\t\t// and taking the render with it.\n\t\t\treturn false\n\t\t}\n\t}\n\tif (cond.endpoint || cond.source) {\n\t\treturn false\n\t}\n\tif (typeof cond.appInstalled === 'string' && cond.appInstalled !== '') {\n\t\tif (!isAppInstalled(cond.appInstalled)) {\n\t\t\treturn false\n\t\t}\n\t\tif (!cond.field) {\n\t\t\treturn true\n\t\t}\n\t}\n\tif (typeof cond.field !== 'string' || cond.field.length === 0) {\n\t\treturn false\n\t}\n\ttry {\n\t\tconst actual = readVisibleWhenPath(data, cond.field)\n\t\t// The row / form record IS the object context here, so `@object.<field>`\n\t\t// on the right-hand side compares one field against another instead of\n\t\t// against the literal token string.\n\t\treturn compareVisibleWhen(actual, cond.op || 'eq', cond.value, { object: data })\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Whether {@link evaluateVisibleWhenLocal} can actually DECIDE a condition.\n *\n * It answers `false` to anything naming an `endpoint` or a `source`, because it\n * cannot fetch. That is right for a form field, where an undecidable gate should\n * hide — and wrong for a surface that had no gate at all before, which would\n * simply lose the element. Callers in the second group check this first and skip\n * the gate when it returns `false`.\n *\n * @param {object|null} cond The condition (or null).\n * @return {boolean} True when every leaf is a local `field` / `appInstalled` check.\n */\nexport function isLocallyDecidableVisibleWhen(cond) {\n\tif (cond === null || cond === undefined) {\n\t\treturn true\n\t}\n\tif (typeof cond !== 'object' || Array.isArray(cond)) {\n\t\treturn false\n\t}\n\tif (Array.isArray(cond.all) === true || Array.isArray(cond.any) === true) {\n\t\tconst parts = Array.isArray(cond.all) === true ? cond.all : cond.any\n\t\treturn parts.every((part) => isLocallyDecidableVisibleWhen(part))\n\t}\n\treturn !cond.endpoint && !cond.source\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"visibleWhen.js","sources":["../../../src/utils/visibleWhen.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * visibleWhen — the shared declarative visibility predicate (Wave 1 banner\n * shape, generalised in Wave 3 of nextcloud-vue#91 for manifest actions).\n *\n * A `visibleWhen` condition is\n * `{ appInstalled?, endpoint?, source?, field?, op?, value }`.\n *\n * `appInstalled` is a precondition rather than a mode: it names the Nextcloud\n * app that backs the guarded element, and it is checked before anything else.\n * On its own it IS the whole condition; combined with one of the modes below\n * it gates that mode. It exists because the menu's `visibleIf` already spoke\n * this word and `visibleWhen` did not, so the same manifest vocabulary meant\n * two different things in two places.\n *\n * The value itself comes from one of THREE evaluation modes (first match wins):\n *\n * 1. `endpoint` — GET a same-origin JSON endpoint; `field` is a dot-path\n * into the response body.\n * 2. `source` — `{ register, schema, filter? }`, an OpenRegister object\n * query (`filter` supports the shared @-token grammar). `field` is a\n * dot-path into the FIRST result; omit it — or use `@total` — to\n * compare the collection total.\n * 3. Neither — the LOCAL mode (Wave 3): `field` is a dot-path into the\n * caller's object context (`ctx.object`, e.g. the loaded detail-page\n * record) — no request is made. This is how detail header actions gate\n * on the current record's state (`{ field: \"lifecycleState\", op: \"eq\",\n * value: \"approved\" }`).\n *\n * `op` is `eq | neq | gt | gte | lt | lte` (default `eq`); `value` is the\n * literal right-hand side. Evaluation is FAIL-SAFE: any fetch / shape error\n * resolves `false` (hidden) — a broken predicate never breaks a page.\n *\n * manifest-form-logic adds `evaluateVisibleWhenLocal(cond, data)`, a SYNC\n * counterpart to `evaluateVisibleWhen()` for the LOCAL mode only, composed\n * from `readVisibleWhenPath` + `compareVisibleWhen` with no fetch / promise.\n * `CnFormPage` calls it on every form-data change so conditions gating one\n * field on another's live value resolve without flicker. `endpoint` /\n * `source` conditions on form fields still go through the async\n * `evaluateVisibleWhen()`, evaluated once at mount and cached.\n *\n * @module utils/visibleWhen\n */\n\nimport { isAppInstalled } from './appInstalled.js'\nimport { buildHeaders, buildQueryString, prefixUrl } from './headers.js'\nimport { resolveFilterTokens, resolveFilterValue } from './resolveFilterTokens.js'\n\n/** Supported visibleWhen comparison operators. */\nexport const VISIBLE_WHEN_OPS = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'empty', 'notEmpty', 'in']\n\n/**\n * Read a dot-path off an object (`'a.b.c'`); the object itself when no\n * field is given.\n *\n * @param {object} data The source object.\n * @param {string} [field] Dot-path into the object.\n * @return {unknown} The value at the path (undefined on a missing segment).\n */\nexport function readVisibleWhenPath(data, field) {\n\tif (!field) {\n\t\treturn data\n\t}\n\treturn String(field).split('.').reduce((obj, key) => (obj === null || obj === undefined ? obj : obj[key]), data)\n}\n\n/**\n * Apply a visibleWhen comparison operator. Ordering operators coerce both\n * sides to Number; equality compares loosely-normalized primitives\n * (`String(a) === String(b)` when types differ) so `\"3\" eq 3` holds for\n * JSON round-trips.\n *\n * @param {unknown} actual The resolved left-hand value.\n * @param {string} op The operator (`eq` when unknown).\n * @param {unknown} expected The declared right-hand value.\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} [ctx] Token-resolution\n * context for the right-hand side, so `@object.<field>` and `@workspace.<key>` resolve\n * as well as `@me`. Omitted, only the context-free tokens resolve.\n * @return {boolean} Whether the comparison holds.\n */\nexport function compareVisibleWhen(actual, op, expected, ctx) {\n\tconst operator = VISIBLE_WHEN_OPS.includes(op) ? op : 'eq'\n\t// `empty` takes no `value`. It is the one question eq cannot ask: an unset\n\t// field arrives as undefined, null or '' depending on the store and the\n\t// serialiser, and `String(null)` is the word \"null\" rather than blank.\n\tif (operator === 'empty' || operator === 'notEmpty') {\n\t\tconst blank = actual === undefined || actual === null || actual === ''\n\t\t\t|| (Array.isArray(actual) === true && actual.length === 0)\n\t\treturn operator === 'empty' ? blank : !blank\n\t}\n\t// The shared @-token grammar, so a condition can name the READER (`@me`)\n\t// rather than a literal only the server knows.\n\tconst right = resolveFilterValue(expected, ctx || {})\n\t// `in` holds when the left-hand value is one of the listed values\n\t// (screens-page-lenses: a widget shown under several chips).\n\tif (operator === 'in') {\n\t\tconst list = Array.isArray(right) ? right : [right]\n\t\treturn list.some((entry) => entry === actual || String(entry) === String(actual))\n\t}\n\tif (operator === 'eq' || operator === 'neq') {\n\t\tconst equal = actual === right || String(actual) === String(right)\n\t\treturn operator === 'eq' ? equal : !equal\n\t}\n\tconst a = Number(actual)\n\tconst b = Number(right)\n\tif (!Number.isFinite(a) || !Number.isFinite(b)) {\n\t\treturn false\n\t}\n\tif (operator === 'gt') {\n\t\treturn a > b\n\t}\n\tif (operator === 'gte') {\n\t\treturn a >= b\n\t}\n\tif (operator === 'lt') {\n\t\treturn a < b\n\t}\n\treturn a <= b\n}\n\n/**\n * Whether a condition reads the page workspace context (`workspace: \"<key>\"`).\n * Such a condition is answered synchronously from the reactive bag, so a\n * widget appears and disappears the moment a page control writes its key\n * (screens-page-lenses). A condition that also names an endpoint or a source\n * is not a workspace condition.\n *\n * @param {object|null} cond The visibleWhen condition.\n * @return {boolean} True for a workspace-mode condition.\n * @spec openspec/changes/screens-page-lenses/specs/page-lenses/spec.md#requirement-a-widget-can-be-shown-by-a-workspace-value\n */\nexport function isWorkspaceCondition(cond) {\n\treturn Boolean(cond) && typeof cond === 'object' && typeof cond.workspace === 'string' && cond.workspace !== ''\n\t\t&& !cond.endpoint && !cond.source\n}\n\n/**\n * Evaluate a workspace-mode condition against the workspace bag. No fetch.\n * A key nobody wrote is unset, so only `empty` and `neq` hold for it.\n *\n * @param {{workspace: string, op?: string, value?: unknown}} cond The condition.\n * @param {object} [workspace] The plain workspace bag.\n * @return {boolean} Whether the guarded element shows.\n * @spec openspec/changes/screens-page-lenses/specs/page-lenses/spec.md#requirement-a-widget-can-be-shown-by-a-workspace-value\n */\nexport function evaluateWorkspaceCondition(cond, workspace) {\n\tconst bag = workspace && typeof workspace === 'object' ? workspace : {}\n\treturn compareVisibleWhen(bag[cond.workspace], cond.op || 'eq', cond.value, { workspace: bag })\n}\n\n/**\n * Resolve the condition's left-hand value from its endpoint / OpenRegister\n * source / local object context (see the module docblock for the three\n * modes). Throws on unusable conditions and failed fetches — callers that\n * want the fail-safe boolean use {@link evaluateVisibleWhen}.\n *\n * A `source` may carry `order` (`{ field: \"asc\" | \"desc\" }`), so \"the first\n * row\" is the first of an ordered query. A caller that wants that row, not\n * just the value, passes `sink`: the first result is written to `sink.row`.\n *\n * @param {{endpoint?: string, source?: {register: string, schema: string, filter?: object, order?: object}, field?: string}} cond The visibleWhen condition.\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} [ctx] The caller's token / object context.\n * @param {{row?: object}} [sink] Receives the first result of a `source` query as `row`.\n * @return {Promise<unknown>} The value `field` points at.\n * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-banner-can-name-the-first-row-of-a-query\n */\nexport async function readVisibleWhenValue(cond, ctx, sink) {\n\tif (cond.endpoint) {\n\t\tconst response = await fetch(prefixUrl(cond.endpoint), { headers: buildHeaders() })\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`endpoint returned ${response.status}`)\n\t\t}\n\t\tconst data = await response.json()\n\t\treturn readVisibleWhenPath(data, cond.field)\n\t}\n\tconst src = cond.source\n\tif (src && src.register && src.schema) {\n\t\tconst filter = resolveFilterTokens(src.filter || {}, ctx)\n\t\tconst order = {}\n\t\tfor (const [field, dir] of Object.entries(src.order && typeof src.order === 'object' ? src.order : {})) {\n\t\t\torder[`_order[${field}]`] = dir === 'desc' ? 'desc' : 'asc'\n\t\t}\n\t\tconst qs = buildQueryString({ ...filter, ...order, _limit: 1 })\n\t\tconst url = prefixUrl(`/apps/openregister/api/objects/${src.register}/${src.schema}${qs}`)\n\t\tconst response = await fetch(url, { headers: buildHeaders() })\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`source returned ${response.status}`)\n\t\t}\n\t\tconst data = await response.json()\n\t\tif (!cond.field || cond.field === '@total') {\n\t\t\treturn data.total ?? (Array.isArray(data.results) ? data.results.length : 0)\n\t\t}\n\t\tconst first = Array.isArray(data.results) ? data.results[0] : data\n\t\tif (sink && typeof sink === 'object') {\n\t\t\tsink.row = first && typeof first === 'object' ? first : undefined\n\t\t}\n\t\treturn readVisibleWhenPath(first, cond.field)\n\t}\n\t// Local mode (Wave 3): evaluate against the caller's object context.\n\tif (cond.field && ctx && ctx.object && typeof ctx.object === 'object') {\n\t\treturn readVisibleWhenPath(ctx.object, cond.field)\n\t}\n\tthrow new Error('visibleWhen needs an endpoint, a source, or a field on the object context')\n}\n\n/**\n * Evaluate a visibleWhen condition to a boolean, FAIL-SAFE: any error\n * (fetch failure, unusable condition, missing context) resolves `false`.\n * A `null` / `undefined` condition resolves `true` (no condition = always\n * visible) so callers can pass the raw config value straight through.\n *\n * @param {{endpoint?: string, source?: object, field?: string, op?: string, value?: unknown}|null} cond The condition (or null).\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} [ctx] The caller's token / object context.\n * @return {Promise<boolean>} Whether the guarded element should show.\n */\nexport async function evaluateVisibleWhen(cond, ctx) {\n\tif (!cond) {\n\t\treturn true\n\t}\n\t// `all` / `any` compose conditions, so a gate can be more than one fact.\n\t// Without it a claim button could say \"not closed\" OR \"nobody holds it\",\n\t// never both, and the weaker of the two is the one that ships.\n\tif (Array.isArray(cond.all) === true || Array.isArray(cond.any) === true) {\n\t\ttry {\n\t\t\t// Read the key that IS an array: a malformed `{ all: {}, any: [] }`\n\t\t\t// enters this branch on `any`, and `cond.all ?? cond.any` would hand\n\t\t\t// the object to `.map`.\n\t\t\tconst parts = Array.isArray(cond.all) === true ? cond.all : cond.any\n\t\t\tconst results = await Promise.all(parts.map((part) => evaluateVisibleWhen(part, ctx)))\n\t\t\treturn Array.isArray(cond.all) === true ? results.every(Boolean) : results.some(Boolean)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\tif (isWorkspaceCondition(cond)) {\n\t\treturn evaluateWorkspaceCondition(cond, ctx && ctx.workspace)\n\t}\n\ttry {\n\t\t// `appInstalled` is checked FIRST and on its own: it answers \"is the app\n\t\t// that backs this action even here\", which has to settle before any of\n\t\t// the other modes are worth evaluating. The menu's `visibleIf` already\n\t\t// spoke this word, and `visibleWhen` did not, so a manifest author who\n\t\t// wrote `{ appInstalled: \"humaniq\" }` here got a condition with no\n\t\t// `field` — which the local mode rejects as malformed and hides. The\n\t\t// button then never appeared, and nothing said why.\n\t\tif (typeof cond.appInstalled === 'string' && cond.appInstalled !== '') {\n\t\t\tif (!isAppInstalled(cond.appInstalled)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// `appInstalled` on its own IS the whole condition. Combined with a\n\t\t\t// field/endpoint/source it acts as a precondition, and evaluation\n\t\t\t// falls through to that.\n\t\t\tif (!cond.field && !cond.endpoint && !cond.source) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\tconst actual = await readVisibleWhenValue(cond, ctx)\n\t\treturn compareVisibleWhen(actual, cond.op || 'eq', cond.value, ctx)\n\t} catch (error) {\n\t\t// Still false; `ctx.onError` lets a caller (a journey run) record why.\n\t\tif (ctx && typeof ctx.onError === 'function') {\n\t\t\tctx.onError(error, cond)\n\t\t}\n\t\treturn false\n\t}\n}\n\n/**\n * Evaluate a LOCAL-mode visibleWhen condition synchronously against a\n * plain data object (`CnFormPage`'s live `formData`). No fetch, no\n * promise — pure composition of {@link readVisibleWhenPath} and\n * {@link compareVisibleWhen}, so it is safe to call on every keystroke.\n *\n * A nullish condition resolves `true` (no condition = always visible),\n * mirroring {@link evaluateVisibleWhen}'s fail-safe posture. A malformed\n * condition — not a plain object, missing `field`, or declaring\n * `endpoint` / `source` (those are NOT local-mode conditions) — resolves\n * `false` (hidden).\n *\n * @param {{field?: string, op?: string, value?: unknown}|null} cond The LOCAL visibleWhen condition (or null).\n * @param {object} data The live data object `field` dot-paths into (e.g. formData).\n * @return {boolean} Whether the guarded element should show.\n */\nexport function evaluateVisibleWhenLocal(cond, data) {\n\tif (cond === null || cond === undefined) {\n\t\treturn true\n\t}\n\tif (typeof cond !== 'object' || Array.isArray(cond)) {\n\t\treturn false\n\t}\n\tif (Array.isArray(cond.all) === true || Array.isArray(cond.any) === true) {\n\t\ttry {\n\t\t\tconst parts = Array.isArray(cond.all) === true ? cond.all : cond.any\n\t\t\tconst results = parts.map((part) => evaluateVisibleWhenLocal(part, data))\n\t\t\treturn Array.isArray(cond.all) === true ? results.every(Boolean) : results.some(Boolean)\n\t\t} catch {\n\t\t\t// Same posture as the async twin: a malformed composition hides the\n\t\t\t// one element, rather than throwing out of the computed that calls it\n\t\t\t// and taking the render with it.\n\t\t\treturn false\n\t\t}\n\t}\n\tif (cond.endpoint || cond.source) {\n\t\treturn false\n\t}\n\tif (typeof cond.appInstalled === 'string' && cond.appInstalled !== '') {\n\t\tif (!isAppInstalled(cond.appInstalled)) {\n\t\t\treturn false\n\t\t}\n\t\tif (!cond.field) {\n\t\t\treturn true\n\t\t}\n\t}\n\tif (typeof cond.field !== 'string' || cond.field.length === 0) {\n\t\treturn false\n\t}\n\ttry {\n\t\tconst actual = readVisibleWhenPath(data, cond.field)\n\t\t// The row / form record IS the object context here, so `@object.<field>`\n\t\t// on the right-hand side compares one field against another instead of\n\t\t// against the literal token string.\n\t\treturn compareVisibleWhen(actual, cond.op || 'eq', cond.value, { object: data })\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Whether {@link evaluateVisibleWhenLocal} can actually DECIDE a condition.\n *\n * It answers `false` to anything naming an `endpoint` or a `source`, because it\n * cannot fetch. That is right for a form field, where an undecidable gate should\n * hide — and wrong for a surface that had no gate at all before, which would\n * simply lose the element. Callers in the second group check this first and skip\n * the gate when it returns `false`.\n *\n * @param {object|null} cond The condition (or null).\n * @return {boolean} True when every leaf is a local `field` / `appInstalled` check.\n */\nexport function isLocallyDecidableVisibleWhen(cond) {\n\tif (cond === null || cond === undefined) {\n\t\treturn true\n\t}\n\tif (typeof cond !== 'object' || Array.isArray(cond)) {\n\t\treturn false\n\t}\n\tif (Array.isArray(cond.all) === true || Array.isArray(cond.any) === true) {\n\t\tconst parts = Array.isArray(cond.all) === true ? cond.all : cond.any\n\t\treturn parts.every((part) => isLocallyDecidableVisibleWhen(part))\n\t}\n\treturn !cond.endpoint && !cond.source\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -40,10 +40,16 @@ import { canonicalWidgetType } from './widgetTypeAliases.js';
|
|
|
40
40
|
* because a run is started by a flow, never by a person clicking Add.
|
|
41
41
|
* `tasks` follows for the same two reasons: a bare row list, and a task is
|
|
42
42
|
* created by a flow, never by a person clicking Add.
|
|
43
|
+
* `facts` is a bare list of label/value rows about one object: on a side
|
|
44
|
+
* card it needs the same titled wrapper, and there is nothing to Add.
|
|
45
|
+
* `field-list` draws rows of one object field and carries its own add form,
|
|
46
|
+
* so it takes the titled wrapper and not the catalog Add action.
|
|
47
|
+
* `record-list` draws bare rows of another schema's objects; the card and its
|
|
48
|
+
* header (link, note) come from the host, and it has no Add.
|
|
43
49
|
*
|
|
44
50
|
* @type {string[]}
|
|
45
51
|
*/
|
|
46
|
-
const CONTENT_ONLY_TYPES = Object.freeze(['object-list', 'table', 'files', 'flow-runs', 'tasks']);
|
|
52
|
+
const CONTENT_ONLY_TYPES = Object.freeze(['object-list', 'table', 'files', 'flow-runs', 'tasks', 'facts', 'field-list', 'record-list', 'settings-form', 'switch-rows', 'radio-cards', 'chips-field', 'toggle-table', 'choice-picker']);
|
|
47
53
|
|
|
48
54
|
/**
|
|
49
55
|
* The stored content/config blob for a widget definition.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widgetDispatch.js","sources":["../../../src/utils/widgetDispatch.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * widgetDispatch — how a detail-page widget definition picks its renderer.\n *\n * These predicates used to live as methods on `CnDetailPage`, each one calling\n * `findWidget(item)` first. That was fine while the detail grid was the only\n * surface rendering a widget definition. `CnTabsWidget` is a second one: it\n * renders the SAME definitions with no card chrome, because the tab strip owns\n * the title and the Actions menu instead.\n *\n * Two copies of \"is this a card widget?\" is exactly the kind of thing that\n * drifts. One copy grows a type, the other does not, and the symptom is a\n * widget that renders correctly on the page and blank inside a tab, with\n * nothing in the console either way. So the decisions live here as pure\n * functions over a widget DEFINITION, and both surfaces import them.\n *\n * The functions take the resolved definition, not a layout item. Resolving an\n * item to a definition needs the surface's own widget list, which is the one\n * part that genuinely differs between the two.\n *\n * @module utils/widgetDispatch\n */\n\nimport { BUILT_IN_WIDGETS } from '../components/CnWidgetGrid/builtInWidgets.js'\nimport { getWidgetTypeEntry } from '../components/CnWidgetGrid/dashboardWidgetRegistry.js'\nimport { canonicalWidgetType } from './widgetTypeAliases.js'\n\n/**\n * Content-only catalog widgets: bare renderers with no chrome of their own.\n * On a card surface they need the titled wrapper; inside a tab panel the strip\n * already supplies the title, so they render bare.\n *\n * `flow-runs` belongs here for the same reason the lists do: it renders a bare\n * row list, so without the wrapper it appeared on the detail page as loose text\n * with no title and no card, which ADR-062 rule 5 forbids (\"every body widget\n * has card chrome and its manifest title\"). It is NOT in the Add-action list,\n * because a run is started by a flow, never by a person clicking Add.\n * `tasks` follows for the same two reasons: a bare row list, and a task is\n * created by a flow, never by a person clicking Add.\n *\n * @type {string[]}\n */\nexport const CONTENT_ONLY_TYPES = Object.freeze(['object-list', 'table', 'files', 'flow-runs', 'tasks'])\n\n/**\n * The stored content/config blob for a widget definition.\n *\n * @param {object} def The widget definition.\n * @return {object} The definition's `content`, or an empty object.\n */\nexport function widgetContentOf(def) {\n\treturn (def && def.content && typeof def.content === 'object') ? def.content : {}\n}\n\n/**\n * Whether the definition is the schema-driven `data` widget.\n *\n * @param {object} def The widget definition.\n * @return {boolean} true for `type: 'data'`.\n */\nexport function isDataWidgetDef(def) {\n\treturn Boolean(def) && def.type === 'data'\n}\n\n/**\n * Whether the definition is the schema-driven `related` widget.\n *\n * @param {object} def The widget definition.\n * @return {boolean} true for `type: 'related'`.\n */\nexport function isRelatedWidgetDef(def) {\n\treturn Boolean(def) && def.type === 'related'\n}\n\n/**\n * Whether the definition is the `object-geo` widget.\n *\n * @param {object} def The widget definition.\n * @return {boolean} true for `type: 'object-geo'`.\n */\nexport function isGeoWidgetDef(def) {\n\treturn Boolean(def) && def.type === 'object-geo'\n}\n\n/**\n * Whether the definition names a registered integration leaf.\n *\n * @param {object} def The widget definition.\n * @return {boolean} true for `type: 'integration'` with an `integrationId`.\n */\nexport function isIntegrationWidgetDef(def) {\n\treturn Boolean(def) && def.type === 'integration' && typeof def.integrationId === 'string'\n}\n\n/**\n * Whether the definition is a content-only catalog widget.\n *\n * @param {object} def The widget definition.\n * @return {boolean} true when the type is in {@link CONTENT_ONLY_TYPES}.\n */\nexport function isContentOnlyWidgetDef(def) {\n\treturn Boolean(def) && CONTENT_ONLY_TYPES.includes(def.type)\n}\n\n/**\n * Whether the definition is a registry \"card\" widget: a self-contained KPI,\n * gauge or delta tile that headlines itself (registry entry `card: true`).\n *\n * @param {object} def The widget definition.\n * @return {boolean} true when the registry entry declares `card`.\n */\nexport function isCardWidgetDef(def) {\n\tif (!def || !def.type) {\n\t\treturn false\n\t}\n\tconst entry = getWidgetTypeEntry(def.type)\n\treturn Boolean(entry && entry.card === true)\n}\n\n/**\n * Resolve the registered RENDERER for a content-driven catalog widget type.\n *\n * Integration widgets resolve separately, and `data` needs object context the\n * caller supplies, so both are excluded from this generic fallback.\n *\n * Resolution order is consumer registry, then the dashboard catalog, then\n * `BUILT_IN_WIDGETS`. Those last two were near-disjoint vocabularies until they\n * were unified here: the dashboard catalog held chart / map / object-list /\n * related / stats-block / table, `BUILT_IN_WIDGETS` held audit-trail / banner /\n * card-grid / data / divider / form-renderer / header / integration /\n * map-viewer / metadata / nav-card-grid / object-geo / related / text, and only\n * `related` was in both. A widget authored against one was invisible to the\n * other, and the surface rendered nothing while saying nothing about why. On\n * hrmq that was 101 of 236 detail widgets.\n *\n * @param {object} def The widget definition.\n * @param {object} [cnRegistry] The consumer's component registry, if any.\n * @return {object|null} The renderer component, or null.\n */\nexport function resolveRegistryRenderer(def, cnRegistry = {}) {\n\tif (!def || !def.type || def.type === 'integration' || def.type === 'data') {\n\t\treturn null\n\t}\n\t// Consumer registry FIRST — the order REQ-MVR-005 mandates (\"Custom widget\n\t// overrides built-in\"), and the order CnWidgetGrid already uses.\n\tconst consumer = (cnRegistry || {})[def.type]\n\tif (consumer) {\n\t\treturn consumer.component ?? consumer\n\t}\n\tconst entry = getWidgetTypeEntry(canonicalWidgetType(def.type))\n\tif (entry && entry.renderer) {\n\t\treturn entry.renderer\n\t}\n\treturn BUILT_IN_WIDGETS[canonicalWidgetType(def.type)] || BUILT_IN_WIDGETS[def.type] || null\n}\n\n/**\n * The effective title for a widget definition.\n *\n * A title-owning type (registry `ownsTitle`, e.g. `related`) keeps its editable\n * title in `content.title`, and that MUST win over the chrome `def.title`.\n * Otherwise a non-empty seed title permanently shadows the user's edit and the\n * title can never be changed. Without a `content.title` it falls back to\n * `def.title`. Every other type reads `def.title` first.\n *\n * @param {object} def The widget definition.\n * @return {string|undefined} The title, or undefined to let the widget default.\n */\nexport function widgetTitleOf(def) {\n\tif (!def) {\n\t\treturn undefined\n\t}\n\tconst content = widgetContentOf(def)\n\tconst entry = getWidgetTypeEntry(def.type)\n\tif (entry && entry.ownsTitle) {\n\t\t// A manifest that never set content.title keeps its def.title.\n\t\tif (typeof content.title !== 'string') {\n\t\t\treturn def.title || undefined\n\t\t}\n\t\treturn content.title || undefined\n\t}\n\treturn def.title || content.title || undefined\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"widgetDispatch.js","sources":["../../../src/utils/widgetDispatch.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * widgetDispatch — how a detail-page widget definition picks its renderer.\n *\n * These predicates used to live as methods on `CnDetailPage`, each one calling\n * `findWidget(item)` first. That was fine while the detail grid was the only\n * surface rendering a widget definition. `CnTabsWidget` is a second one: it\n * renders the SAME definitions with no card chrome, because the tab strip owns\n * the title and the Actions menu instead.\n *\n * Two copies of \"is this a card widget?\" is exactly the kind of thing that\n * drifts. One copy grows a type, the other does not, and the symptom is a\n * widget that renders correctly on the page and blank inside a tab, with\n * nothing in the console either way. So the decisions live here as pure\n * functions over a widget DEFINITION, and both surfaces import them.\n *\n * The functions take the resolved definition, not a layout item. Resolving an\n * item to a definition needs the surface's own widget list, which is the one\n * part that genuinely differs between the two.\n *\n * @module utils/widgetDispatch\n */\n\nimport { BUILT_IN_WIDGETS } from '../components/CnWidgetGrid/builtInWidgets.js'\nimport { getWidgetTypeEntry } from '../components/CnWidgetGrid/dashboardWidgetRegistry.js'\nimport { canonicalWidgetType } from './widgetTypeAliases.js'\n\n/**\n * Content-only catalog widgets: bare renderers with no chrome of their own.\n * On a card surface they need the titled wrapper; inside a tab panel the strip\n * already supplies the title, so they render bare.\n *\n * `flow-runs` belongs here for the same reason the lists do: it renders a bare\n * row list, so without the wrapper it appeared on the detail page as loose text\n * with no title and no card, which ADR-062 rule 5 forbids (\"every body widget\n * has card chrome and its manifest title\"). It is NOT in the Add-action list,\n * because a run is started by a flow, never by a person clicking Add.\n * `tasks` follows for the same two reasons: a bare row list, and a task is\n * created by a flow, never by a person clicking Add.\n * `facts` is a bare list of label/value rows about one object: on a side\n * card it needs the same titled wrapper, and there is nothing to Add.\n * `field-list` draws rows of one object field and carries its own add form,\n * so it takes the titled wrapper and not the catalog Add action.\n * `record-list` draws bare rows of another schema's objects; the card and its\n * header (link, note) come from the host, and it has no Add.\n *\n * @type {string[]}\n */\nexport const CONTENT_ONLY_TYPES = Object.freeze(['object-list', 'table', 'files', 'flow-runs', 'tasks', 'facts', 'field-list', 'record-list', 'settings-form', 'switch-rows', 'radio-cards', 'chips-field', 'toggle-table', 'choice-picker'])\n\n/**\n * The stored content/config blob for a widget definition.\n *\n * @param {object} def The widget definition.\n * @return {object} The definition's `content`, or an empty object.\n */\nexport function widgetContentOf(def) {\n\treturn (def && def.content && typeof def.content === 'object') ? def.content : {}\n}\n\n/**\n * Whether the definition is the schema-driven `data` widget.\n *\n * @param {object} def The widget definition.\n * @return {boolean} true for `type: 'data'`.\n */\nexport function isDataWidgetDef(def) {\n\treturn Boolean(def) && def.type === 'data'\n}\n\n/**\n * Whether the definition is the schema-driven `related` widget.\n *\n * @param {object} def The widget definition.\n * @return {boolean} true for `type: 'related'`.\n */\nexport function isRelatedWidgetDef(def) {\n\treturn Boolean(def) && def.type === 'related'\n}\n\n/**\n * Whether the definition is the `object-geo` widget.\n *\n * @param {object} def The widget definition.\n * @return {boolean} true for `type: 'object-geo'`.\n */\nexport function isGeoWidgetDef(def) {\n\treturn Boolean(def) && def.type === 'object-geo'\n}\n\n/**\n * Whether the definition names a registered integration leaf.\n *\n * @param {object} def The widget definition.\n * @return {boolean} true for `type: 'integration'` with an `integrationId`.\n */\nexport function isIntegrationWidgetDef(def) {\n\treturn Boolean(def) && def.type === 'integration' && typeof def.integrationId === 'string'\n}\n\n/**\n * Whether the definition is a content-only catalog widget.\n *\n * @param {object} def The widget definition.\n * @return {boolean} true when the type is in {@link CONTENT_ONLY_TYPES}.\n */\nexport function isContentOnlyWidgetDef(def) {\n\treturn Boolean(def) && CONTENT_ONLY_TYPES.includes(def.type)\n}\n\n/**\n * Whether the definition is a registry \"card\" widget: a self-contained KPI,\n * gauge or delta tile that headlines itself (registry entry `card: true`).\n *\n * @param {object} def The widget definition.\n * @return {boolean} true when the registry entry declares `card`.\n */\nexport function isCardWidgetDef(def) {\n\tif (!def || !def.type) {\n\t\treturn false\n\t}\n\tconst entry = getWidgetTypeEntry(def.type)\n\treturn Boolean(entry && entry.card === true)\n}\n\n/**\n * Resolve the registered RENDERER for a content-driven catalog widget type.\n *\n * Integration widgets resolve separately, and `data` needs object context the\n * caller supplies, so both are excluded from this generic fallback.\n *\n * Resolution order is consumer registry, then the dashboard catalog, then\n * `BUILT_IN_WIDGETS`. Those last two were near-disjoint vocabularies until they\n * were unified here: the dashboard catalog held chart / map / object-list /\n * related / stats-block / table, `BUILT_IN_WIDGETS` held audit-trail / banner /\n * card-grid / data / divider / form-renderer / header / integration /\n * map-viewer / metadata / nav-card-grid / object-geo / related / text, and only\n * `related` was in both. A widget authored against one was invisible to the\n * other, and the surface rendered nothing while saying nothing about why. On\n * hrmq that was 101 of 236 detail widgets.\n *\n * @param {object} def The widget definition.\n * @param {object} [cnRegistry] The consumer's component registry, if any.\n * @return {object|null} The renderer component, or null.\n */\nexport function resolveRegistryRenderer(def, cnRegistry = {}) {\n\tif (!def || !def.type || def.type === 'integration' || def.type === 'data') {\n\t\treturn null\n\t}\n\t// Consumer registry FIRST — the order REQ-MVR-005 mandates (\"Custom widget\n\t// overrides built-in\"), and the order CnWidgetGrid already uses.\n\tconst consumer = (cnRegistry || {})[def.type]\n\tif (consumer) {\n\t\treturn consumer.component ?? consumer\n\t}\n\tconst entry = getWidgetTypeEntry(canonicalWidgetType(def.type))\n\tif (entry && entry.renderer) {\n\t\treturn entry.renderer\n\t}\n\treturn BUILT_IN_WIDGETS[canonicalWidgetType(def.type)] || BUILT_IN_WIDGETS[def.type] || null\n}\n\n/**\n * The effective title for a widget definition.\n *\n * A title-owning type (registry `ownsTitle`, e.g. `related`) keeps its editable\n * title in `content.title`, and that MUST win over the chrome `def.title`.\n * Otherwise a non-empty seed title permanently shadows the user's edit and the\n * title can never be changed. Without a `content.title` it falls back to\n * `def.title`. Every other type reads `def.title` first.\n *\n * @param {object} def The widget definition.\n * @return {string|undefined} The title, or undefined to let the widget default.\n */\nexport function widgetTitleOf(def) {\n\tif (!def) {\n\t\treturn undefined\n\t}\n\tconst content = widgetContentOf(def)\n\tconst entry = getWidgetTypeEntry(def.type)\n\tif (entry && entry.ownsTitle) {\n\t\t// A manifest that never set content.title keeps its def.title.\n\t\tif (typeof content.title !== 'string') {\n\t\t\treturn def.title || undefined\n\t\t}\n\t\treturn content.title || undefined\n\t}\n\treturn def.title || content.title || undefined\n}\n"],"names":[],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,CAAC,EAAC;AAC7O;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,GAAG,EAAE;AACrC,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,GAAG,EAAE;AAClF,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,GAAG,EAAE;AACrC,CAAC,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM;AAC3C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,GAAG,EAAE;AACxC,CAAC,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS;AAC9C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,cAAc,CAAC,GAAG,EAAE;AACpC,CAAC,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY;AACjD,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,sBAAsB,CAAC,GAAG,EAAE;AAC5C,CAAC,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,aAAa,IAAI,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ;AAC3F,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,sBAAsB,CAAC,GAAG,EAAE;AAC5C,CAAC,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;AAC7D,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,GAAG,EAAE;AACrC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;AACxB,EAAE,OAAO,KAAK;AACd,EAAE;AACF,CAAC,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAC;AAC3C,CAAC,OAAO,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AAC7C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,uBAAuB,CAAC,GAAG,EAAE,UAAU,GAAG,EAAE,EAAE;AAC9D,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,KAAK,aAAa,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE;AAC7E,EAAE,OAAO,IAAI;AACb,EAAE;AACF;AACA;AACA,CAAC,MAAM,QAAQ,GAAG,CAAC,UAAU,IAAI,EAAE,EAAE,GAAG,CAAC,IAAI,EAAC;AAC9C,CAAC,IAAI,QAAQ,EAAE;AACf,EAAE,OAAO,QAAQ,CAAC,SAAS,IAAI,QAAQ;AACvC,EAAE;AACF,CAAC,MAAM,KAAK,GAAG,kBAAkB,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAC;AAChE,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC,QAAQ,EAAE;AAC9B,EAAE,OAAO,KAAK,CAAC,QAAQ;AACvB,EAAE;AACF,CAAC,OAAO,gBAAgB,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI;AAC7F,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAC,GAAG,EAAE;AACnC,CAAC,IAAI,CAAC,GAAG,EAAE;AACX,EAAE,OAAO,SAAS;AAClB,EAAE;AACF,CAAC,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,EAAC;AACrC,CAAC,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAC;AAC3C,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC,SAAS,EAAE;AAC/B;AACA,EAAE,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE;AACzC,GAAG,OAAO,GAAG,CAAC,KAAK,IAAI,SAAS;AAChC,GAAG;AACH,EAAE,OAAO,OAAO,CAAC,KAAK,IAAI,SAAS;AACnC,EAAE;AACF,CAAC,OAAO,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI,SAAS;AAC/C;;;;"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import axios__default from '@nextcloud/axios';
|
|
2
2
|
import { generateOcsUrl } from '@nextcloud/router';
|
|
3
|
-
import {
|
|
3
|
+
import { getCurrentUserId, resetGroupsPeek } from './userGroupsPeek.js';
|
|
4
|
+
export { peekCurrentUserGroups } from './userGroupsPeek.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Cached user groups — fetched once per session and reused.
|
|
@@ -13,29 +14,6 @@ const _cache = {
|
|
|
13
14
|
promise: null,
|
|
14
15
|
};
|
|
15
16
|
|
|
16
|
-
/**
|
|
17
|
-
* Reactive mirror of the group cache, keyed by user id, for synchronous
|
|
18
|
-
* readers ({@link peekCurrentUserGroups}). A computed that reads a missing key
|
|
19
|
-
* tracks it, so it re-runs when the groups arrive.
|
|
20
|
-
*
|
|
21
|
-
* @type {{ byUser: Record<string, string[]> }}
|
|
22
|
-
*/
|
|
23
|
-
const _peek = reactive({ byUser: {} });
|
|
24
|
-
|
|
25
|
-
/** User ids whose groups a peek already asked for (not reactive on purpose). */
|
|
26
|
-
const _peekRequested = new Set();
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Get the current Nextcloud user ID from OC.currentUser.
|
|
30
|
-
*
|
|
31
|
-
* @return {string|null} The current user ID, or null if not logged in
|
|
32
|
-
*/
|
|
33
|
-
function getCurrentUserId() {
|
|
34
|
-
return window.OC?.currentUser?.uid
|
|
35
|
-
|| window.OC?.currentUser
|
|
36
|
-
|| null
|
|
37
|
-
}
|
|
38
|
-
|
|
39
17
|
/**
|
|
40
18
|
* Fetch the current user's Nextcloud groups. Results are cached so the
|
|
41
19
|
* OCS API is only called once per page load.
|
|
@@ -72,34 +50,6 @@ async function getCurrentUserGroups() {
|
|
|
72
50
|
return _cache.groups
|
|
73
51
|
}
|
|
74
52
|
|
|
75
|
-
/**
|
|
76
|
-
* Synchronous, reactive read of the current user's group ids, for code that
|
|
77
|
-
* cannot await (the `@myGroups` filter token). Returns the ids once they are
|
|
78
|
-
* loaded, `null` while the first request is in flight (and starts that
|
|
79
|
-
* request on first use), and `[]` when nobody is signed in. Read inside a
|
|
80
|
-
* computed, the computed re-runs when the groups arrive.
|
|
81
|
-
*
|
|
82
|
-
* @spec openspec/changes/nextcloud-group-surfaces/specs/schema-utilities/spec.md#requirement-mygroups-resolves-to-the-current-users-group-ids
|
|
83
|
-
* @return {string[]|null} The group ids, or null while loading.
|
|
84
|
-
*/
|
|
85
|
-
function peekCurrentUserGroups() {
|
|
86
|
-
const userId = getCurrentUserId();
|
|
87
|
-
if (!userId) {
|
|
88
|
-
return []
|
|
89
|
-
}
|
|
90
|
-
const known = _peek.byUser[userId];
|
|
91
|
-
if (Array.isArray(known)) {
|
|
92
|
-
return known
|
|
93
|
-
}
|
|
94
|
-
if (!_peekRequested.has(userId)) {
|
|
95
|
-
_peekRequested.add(userId);
|
|
96
|
-
getCurrentUserGroups().then((groups) => {
|
|
97
|
-
_peek.byUser[userId] = Array.isArray(groups) ? [...groups] : [];
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
return null
|
|
101
|
-
}
|
|
102
|
-
|
|
103
53
|
/**
|
|
104
54
|
* Internal: fetch groups from OCS API.
|
|
105
55
|
*
|
|
@@ -202,11 +152,8 @@ function resetVisibilityCache() {
|
|
|
202
152
|
_cache.userId = null;
|
|
203
153
|
_cache.groups = null;
|
|
204
154
|
_cache.promise = null;
|
|
205
|
-
|
|
206
|
-
for (const key of Object.keys(_peek.byUser)) {
|
|
207
|
-
delete _peek.byUser[key];
|
|
208
|
-
}
|
|
155
|
+
resetGroupsPeek();
|
|
209
156
|
}
|
|
210
157
|
|
|
211
|
-
export { filterWidgetsByVisibility, getCurrentUserGroups, getCurrentUserId, isWidgetVisible,
|
|
158
|
+
export { filterWidgetsByVisibility, getCurrentUserGroups, getCurrentUserId, isWidgetVisible, resetVisibilityCache };
|
|
212
159
|
//# sourceMappingURL=widgetVisibility.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widgetVisibility.js","sources":["../../../src/utils/widgetVisibility.js"],"sourcesContent":["import axios from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\nimport { reactive } from 'vue'\n\n/**\n * Cached user groups — fetched once per session and reused.\n *\n * @type {{ userId: string|null, groups: string[]|null, promise: Promise|null }}\n */\nconst _cache = {\n\tuserId: null,\n\tgroups: null,\n\tpromise: null,\n}\n\n/**\n * Reactive mirror of the group cache, keyed by user id, for synchronous\n * readers ({@link peekCurrentUserGroups}). A computed that reads a missing key\n * tracks it, so it re-runs when the groups arrive.\n *\n * @type {{ byUser: Record<string, string[]> }}\n */\nconst _peek = reactive({ byUser: {} })\n\n/** User ids whose groups a peek already asked for (not reactive on purpose). */\nconst _peekRequested = new Set()\n\n/**\n * Get the current Nextcloud user ID from OC.currentUser.\n *\n * @return {string|null} The current user ID, or null if not logged in\n */\nexport function getCurrentUserId() {\n\treturn window.OC?.currentUser?.uid\n\t\t|| window.OC?.currentUser\n\t\t|| null\n}\n\n/**\n * Fetch the current user's Nextcloud groups. Results are cached so the\n * OCS API is only called once per page load.\n *\n * @return {Promise<string[]>} Array of group IDs the current user belongs to\n */\nexport async function getCurrentUserGroups() {\n\tconst userId = getCurrentUserId()\n\tif (!userId) {\n\t\treturn []\n\t}\n\n\t// Return cached result if we already fetched for this user\n\tif (_cache.userId === userId && _cache.groups !== null) {\n\t\treturn _cache.groups\n\t}\n\n\t// If a fetch is already in progress for this user, await it\n\tif (_cache.userId === userId && _cache.promise) {\n\t\treturn _cache.promise\n\t}\n\n\t_cache.userId = userId\n\t_cache.promise = _fetchGroups(userId)\n\n\ttry {\n\t\t_cache.groups = await _cache.promise\n\t} catch {\n\t\t_cache.groups = []\n\t} finally {\n\t\t_cache.promise = null\n\t}\n\n\treturn _cache.groups\n}\n\n/**\n * Synchronous, reactive read of the current user's group ids, for code that\n * cannot await (the `@myGroups` filter token). Returns the ids once they are\n * loaded, `null` while the first request is in flight (and starts that\n * request on first use), and `[]` when nobody is signed in. Read inside a\n * computed, the computed re-runs when the groups arrive.\n *\n * @spec openspec/changes/nextcloud-group-surfaces/specs/schema-utilities/spec.md#requirement-mygroups-resolves-to-the-current-users-group-ids\n * @return {string[]|null} The group ids, or null while loading.\n */\nexport function peekCurrentUserGroups() {\n\tconst userId = getCurrentUserId()\n\tif (!userId) {\n\t\treturn []\n\t}\n\tconst known = _peek.byUser[userId]\n\tif (Array.isArray(known)) {\n\t\treturn known\n\t}\n\tif (!_peekRequested.has(userId)) {\n\t\t_peekRequested.add(userId)\n\t\tgetCurrentUserGroups().then((groups) => {\n\t\t\t_peek.byUser[userId] = Array.isArray(groups) ? [...groups] : []\n\t\t})\n\t}\n\treturn null\n}\n\n/**\n * Internal: fetch groups from OCS API.\n *\n * @param {string} userId The user ID to look up\n * @return {Promise<string[]>} Array of group IDs\n */\nasync function _fetchGroups(userId) {\n\ttry {\n\t\tconst url = generateOcsUrl('/cloud/users/{userId}/groups', { userId })\n\t\tconst response = await axios.get(url)\n\t\tconst groups = response.data?.ocs?.data?.groups || []\n\t\treturn groups\n\t} catch (error) {\n\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\tconsole.error('[widgetVisibility] Failed to fetch user groups:', error)\n\t\treturn []\n\t}\n}\n\n/**\n * Check whether a single widget is visible to the current user based on\n * its `visibility` configuration.\n *\n * Visibility rules:\n * - No `visibility` property -> visible to everyone\n * - `visibility.users` contains the current user ID -> visible\n * - `visibility.groups` overlaps with the user's groups -> visible\n * - Both `users` and `groups` are specified -> either match grants access (OR logic)\n * - Both are empty arrays -> visible to everyone\n *\n * @param {object} widget Widget definition object\n * @param {object} [widget.visibility] Optional visibility configuration\n * @param {string[]} [widget.visibility.users] User IDs who can see this widget\n * @param {string[]} [widget.visibility.groups] Group names who can see this widget\n * @param {string|null} userId Current user ID\n * @param {string[]} userGroups Current user's group memberships\n * @return {boolean} Whether the widget should be visible\n */\nexport function isWidgetVisible(widget, userId, userGroups) {\n\tconst visibility = widget?.visibility\n\tif (!visibility) {\n\t\treturn true\n\t}\n\n\tconst allowedUsers = visibility.users || []\n\tconst allowedGroups = visibility.groups || []\n\n\t// If both are empty, visible to everyone\n\tif (allowedUsers.length === 0 && allowedGroups.length === 0) {\n\t\treturn true\n\t}\n\n\t// Check user match\n\tif (allowedUsers.length > 0 && userId && allowedUsers.includes(userId)) {\n\t\treturn true\n\t}\n\n\t// Check group match\n\tif (allowedGroups.length > 0 && userGroups.length > 0) {\n\t\tconst hasGroupMatch = allowedGroups.some((group) => userGroups.includes(group))\n\t\tif (hasGroupMatch) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * Filter an array of widget definitions by visibility for the current user.\n *\n * This is an async function because it may need to fetch the user's groups\n * from the OCS API (cached after first call).\n *\n * @param {Array} widgets Array of widget definition objects\n * @return {Promise<Array>} Filtered array of visible widgets\n */\nexport async function filterWidgetsByVisibility(widgets) {\n\tif (!widgets || widgets.length === 0) {\n\t\treturn []\n\t}\n\n\t// Quick path: if no widgets have visibility config, return all\n\tconst hasVisibilityConfig = widgets.some((w) => w.visibility)\n\tif (!hasVisibilityConfig) {\n\t\treturn widgets\n\t}\n\n\tconst userId = getCurrentUserId()\n\tconst userGroups = await getCurrentUserGroups()\n\n\treturn widgets.filter((widget) => isWidgetVisible(widget, userId, userGroups))\n}\n\n/**\n * Reset the internal groups cache. Useful for testing or when user\n * context changes.\n */\nexport function resetVisibilityCache() {\n\t_cache.userId = null\n\t_cache.groups = null\n\t_cache.promise = null\n\t_peekRequested.clear()\n\tfor (const key of Object.keys(_peek.byUser)) {\n\t\tdelete _peek.byUser[key]\n\t}\n}\n"],"names":["axios"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"widgetVisibility.js","sources":["../../../src/utils/widgetVisibility.js"],"sourcesContent":["import axios from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\nimport { getCurrentUserId, peekCurrentUserGroups, resetGroupsPeek } from './userGroupsPeek.js'\n\n// The pure peek and user-id read live in userGroupsPeek.js so the filter-token\n// resolver can use them without importing axios; re-exported here so the public\n// API is unchanged.\nexport { getCurrentUserId, peekCurrentUserGroups }\n\n/**\n * Cached user groups — fetched once per session and reused.\n *\n * @type {{ userId: string|null, groups: string[]|null, promise: Promise|null }}\n */\nconst _cache = {\n\tuserId: null,\n\tgroups: null,\n\tpromise: null,\n}\n\n/**\n * Fetch the current user's Nextcloud groups. Results are cached so the\n * OCS API is only called once per page load.\n *\n * @return {Promise<string[]>} Array of group IDs the current user belongs to\n */\nexport async function getCurrentUserGroups() {\n\tconst userId = getCurrentUserId()\n\tif (!userId) {\n\t\treturn []\n\t}\n\n\t// Return cached result if we already fetched for this user\n\tif (_cache.userId === userId && _cache.groups !== null) {\n\t\treturn _cache.groups\n\t}\n\n\t// If a fetch is already in progress for this user, await it\n\tif (_cache.userId === userId && _cache.promise) {\n\t\treturn _cache.promise\n\t}\n\n\t_cache.userId = userId\n\t_cache.promise = _fetchGroups(userId)\n\n\ttry {\n\t\t_cache.groups = await _cache.promise\n\t} catch {\n\t\t_cache.groups = []\n\t} finally {\n\t\t_cache.promise = null\n\t}\n\n\treturn _cache.groups\n}\n\n/**\n * Internal: fetch groups from OCS API.\n *\n * @param {string} userId The user ID to look up\n * @return {Promise<string[]>} Array of group IDs\n */\nasync function _fetchGroups(userId) {\n\ttry {\n\t\tconst url = generateOcsUrl('/cloud/users/{userId}/groups', { userId })\n\t\tconst response = await axios.get(url)\n\t\tconst groups = response.data?.ocs?.data?.groups || []\n\t\treturn groups\n\t} catch (error) {\n\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\tconsole.error('[widgetVisibility] Failed to fetch user groups:', error)\n\t\treturn []\n\t}\n}\n\n/**\n * Check whether a single widget is visible to the current user based on\n * its `visibility` configuration.\n *\n * Visibility rules:\n * - No `visibility` property -> visible to everyone\n * - `visibility.users` contains the current user ID -> visible\n * - `visibility.groups` overlaps with the user's groups -> visible\n * - Both `users` and `groups` are specified -> either match grants access (OR logic)\n * - Both are empty arrays -> visible to everyone\n *\n * @param {object} widget Widget definition object\n * @param {object} [widget.visibility] Optional visibility configuration\n * @param {string[]} [widget.visibility.users] User IDs who can see this widget\n * @param {string[]} [widget.visibility.groups] Group names who can see this widget\n * @param {string|null} userId Current user ID\n * @param {string[]} userGroups Current user's group memberships\n * @return {boolean} Whether the widget should be visible\n */\nexport function isWidgetVisible(widget, userId, userGroups) {\n\tconst visibility = widget?.visibility\n\tif (!visibility) {\n\t\treturn true\n\t}\n\n\tconst allowedUsers = visibility.users || []\n\tconst allowedGroups = visibility.groups || []\n\n\t// If both are empty, visible to everyone\n\tif (allowedUsers.length === 0 && allowedGroups.length === 0) {\n\t\treturn true\n\t}\n\n\t// Check user match\n\tif (allowedUsers.length > 0 && userId && allowedUsers.includes(userId)) {\n\t\treturn true\n\t}\n\n\t// Check group match\n\tif (allowedGroups.length > 0 && userGroups.length > 0) {\n\t\tconst hasGroupMatch = allowedGroups.some((group) => userGroups.includes(group))\n\t\tif (hasGroupMatch) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * Filter an array of widget definitions by visibility for the current user.\n *\n * This is an async function because it may need to fetch the user's groups\n * from the OCS API (cached after first call).\n *\n * @param {Array} widgets Array of widget definition objects\n * @return {Promise<Array>} Filtered array of visible widgets\n */\nexport async function filterWidgetsByVisibility(widgets) {\n\tif (!widgets || widgets.length === 0) {\n\t\treturn []\n\t}\n\n\t// Quick path: if no widgets have visibility config, return all\n\tconst hasVisibilityConfig = widgets.some((w) => w.visibility)\n\tif (!hasVisibilityConfig) {\n\t\treturn widgets\n\t}\n\n\tconst userId = getCurrentUserId()\n\tconst userGroups = await getCurrentUserGroups()\n\n\treturn widgets.filter((widget) => isWidgetVisible(widget, userId, userGroups))\n}\n\n/**\n * Reset the internal groups cache. Useful for testing or when user\n * context changes.\n */\nexport function resetVisibilityCache() {\n\t_cache.userId = null\n\t_cache.groups = null\n\t_cache.promise = null\n\tresetGroupsPeek()\n}\n"],"names":["axios"],"mappings":"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"}
|