@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getCurrentUser } from '@nextcloud/auth';
|
|
1
2
|
import { translate } from '@nextcloud/l10n';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -35,6 +36,9 @@ const DRAFT_DEBOUNCE_MS = 500;
|
|
|
35
36
|
/** The prefix every draft key carries, so a sweep can find them. */
|
|
36
37
|
const DRAFT_KEY_PREFIX = 'cn-form-draft';
|
|
37
38
|
|
|
39
|
+
/** The user part of a draft key when nobody is signed in. */
|
|
40
|
+
const DRAFT_ANONYMOUS_USER = 'anonymous';
|
|
41
|
+
|
|
38
42
|
/**
|
|
39
43
|
* The storage key one form's draft lives under.
|
|
40
44
|
*
|
|
@@ -55,10 +59,124 @@ function draftKey({ appId = '', schema = '', objectId = '', userId = '' } = {})
|
|
|
55
59
|
String(appId || 'app'),
|
|
56
60
|
String(schema || 'schema'),
|
|
57
61
|
String(objectId || 'new'),
|
|
58
|
-
String(userId ||
|
|
62
|
+
String(userId || DRAFT_ANONYMOUS_USER),
|
|
59
63
|
].join(':')
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Who a draft belongs to: the id the host passed, else the signed-in
|
|
68
|
+
* Nextcloud user, else '' (which {@link draftKey} stores as `anonymous`).
|
|
69
|
+
*
|
|
70
|
+
* 🔴 A HOST THAT PASSES NOTHING STILL GETS PER-USER DRAFTS. Leaving the user
|
|
71
|
+
* to every host meant no host passed it, and every draft on a shared device
|
|
72
|
+
* was keyed `anonymous`: the next person at the counter was offered what the
|
|
73
|
+
* last one typed.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} [explicit] The id the host passed (`draftUserId`).
|
|
76
|
+
* @return {string} The user id, or '' when nobody is signed in.
|
|
77
|
+
* @spec openspec/changes/form-draft-on-change-and-help-text-spacing/specs/form-draft-on-change-and-help-text-spacing/spec.md#requirement-a-draft-belongs-to-the-signed-in-user
|
|
78
|
+
*/
|
|
79
|
+
function resolveDraftUserId(explicit = '') {
|
|
80
|
+
if (explicit) {
|
|
81
|
+
return String(explicit)
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const uid = getCurrentUser()?.uid;
|
|
85
|
+
return uid ? String(uid) : ''
|
|
86
|
+
} catch {
|
|
87
|
+
// No Nextcloud page around us (a test, an embed): nobody is signed in.
|
|
88
|
+
return ''
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Remove every draft stored under the anonymous user.
|
|
94
|
+
*
|
|
95
|
+
* Called once a signed-in user is known. Such a draft was written by a page
|
|
96
|
+
* that did not know who was typing, so it may be somebody else's, and it is
|
|
97
|
+
* deleted rather than moved: moving it would hand it to whoever opened the
|
|
98
|
+
* form next.
|
|
99
|
+
*
|
|
100
|
+
* @return {number} How many drafts were removed.
|
|
101
|
+
* @spec openspec/changes/form-draft-on-change-and-help-text-spacing/specs/form-draft-on-change-and-help-text-spacing/spec.md#requirement-a-draft-belongs-to-the-signed-in-user
|
|
102
|
+
*/
|
|
103
|
+
function clearAnonymousDrafts() {
|
|
104
|
+
let removed = 0;
|
|
105
|
+
try {
|
|
106
|
+
const storage = window.localStorage;
|
|
107
|
+
const suffix = ':' + DRAFT_ANONYMOUS_USER;
|
|
108
|
+
const doomed = [];
|
|
109
|
+
for (let i = 0; i < storage.length; i++) {
|
|
110
|
+
const key = storage.key(i);
|
|
111
|
+
if (key && key.startsWith(DRAFT_KEY_PREFIX + ':') && key.endsWith(suffix)) {
|
|
112
|
+
doomed.push(key);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const key of doomed) {
|
|
116
|
+
storage.removeItem(key);
|
|
117
|
+
removed++;
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
// Blocked storage holds no drafts to leak.
|
|
121
|
+
}
|
|
122
|
+
return removed
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Whether a value counts as empty for a draft: null, undefined, '' and [].
|
|
127
|
+
*
|
|
128
|
+
* @param {unknown} value The value.
|
|
129
|
+
* @return {boolean} True when empty.
|
|
130
|
+
*/
|
|
131
|
+
function isEmptyDraftValue(value) {
|
|
132
|
+
return value === null || value === undefined || value === '' || (Array.isArray(value) && value.length === 0)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Whether two field values are the same for the purpose of a draft.
|
|
137
|
+
*
|
|
138
|
+
* Empty values are equal to each other: an input reports a missing value back
|
|
139
|
+
* as '', and that is the control normalising, not a person typing.
|
|
140
|
+
*
|
|
141
|
+
* @param {unknown} a One value.
|
|
142
|
+
* @param {unknown} b The other.
|
|
143
|
+
* @return {boolean} True when they are the same.
|
|
144
|
+
*/
|
|
145
|
+
function sameDraftValue(a, b) {
|
|
146
|
+
if (isEmptyDraftValue(a) && isEmptyDraftValue(b)) {
|
|
147
|
+
return true
|
|
148
|
+
}
|
|
149
|
+
if (a === b) {
|
|
150
|
+
return true
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
return JSON.stringify(a) === JSON.stringify(b)
|
|
154
|
+
} catch {
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Whether a person changed the form: one of the fields they edited holds a
|
|
161
|
+
* value different from the one the form opened with.
|
|
162
|
+
*
|
|
163
|
+
* 🔴 ONLY EDITED FIELDS COUNT. A form fills in values by itself after it
|
|
164
|
+
* opens (defaults, normalised dates, the active organisation, options that
|
|
165
|
+
* arrive later). Counting those wrote a draft and said "Saved just now" on a
|
|
166
|
+
* dialog nobody had touched.
|
|
167
|
+
*
|
|
168
|
+
* @param {object} values The current values.
|
|
169
|
+
* @param {object} baseline The values the form opened with.
|
|
170
|
+
* @param {string[]} editedKeys The fields a person edited.
|
|
171
|
+
* @return {boolean} True when there is something worth keeping.
|
|
172
|
+
* @spec openspec/changes/form-draft-on-change-and-help-text-spacing/specs/form-draft-on-change-and-help-text-spacing/spec.md#requirement-a-draft-is-written-only-after-a-person-changes-a-field
|
|
173
|
+
*/
|
|
174
|
+
function hasDraftChanges(values, baseline, editedKeys) {
|
|
175
|
+
const now = values || {};
|
|
176
|
+
const base = baseline || {};
|
|
177
|
+
return (editedKeys || []).some((key) => !sameDraftValue(now[key], base[key]))
|
|
178
|
+
}
|
|
179
|
+
|
|
62
180
|
/**
|
|
63
181
|
* Read one draft back, or null when there is nothing usable.
|
|
64
182
|
*
|
|
@@ -219,13 +337,6 @@ function formDraftMixin() {
|
|
|
219
337
|
draftSavedAt: 0,
|
|
220
338
|
/** Non-reactive handle for the debounce. */
|
|
221
339
|
draftTimeout: null,
|
|
222
|
-
/**
|
|
223
|
-
* Whether the form's own initial population has happened.
|
|
224
|
-
*
|
|
225
|
-
* The first change to the values is the component building
|
|
226
|
-
* them, not a person typing. See the watcher that reads this.
|
|
227
|
-
*/
|
|
228
|
-
draftSeeded: false,
|
|
229
340
|
}
|
|
230
341
|
},
|
|
231
342
|
|
|
@@ -266,6 +377,26 @@ function formDraftMixin() {
|
|
|
266
377
|
}, DRAFT_DEBOUNCE_MS);
|
|
267
378
|
},
|
|
268
379
|
|
|
380
|
+
/**
|
|
381
|
+
* The form is back to what it opened with: cancel a pending write,
|
|
382
|
+
* remove a draft written earlier and stop announcing anything.
|
|
383
|
+
*
|
|
384
|
+
* @param {string} key The storage key.
|
|
385
|
+
* @return {void}
|
|
386
|
+
* @spec openspec/changes/form-draft-on-change-and-help-text-spacing/specs/form-draft-on-change-and-help-text-spacing/spec.md#requirement-a-draft-is-written-only-after-a-person-changes-a-field
|
|
387
|
+
*/
|
|
388
|
+
settleUnchangedDraft(key) {
|
|
389
|
+
if (this.draftTimeout) {
|
|
390
|
+
clearTimeout(this.draftTimeout);
|
|
391
|
+
this.draftTimeout = null;
|
|
392
|
+
}
|
|
393
|
+
if (this.draftState !== 'idle') {
|
|
394
|
+
clearDraft(key);
|
|
395
|
+
this.draftState = 'idle';
|
|
396
|
+
this.draftSavedAt = 0;
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
|
|
269
400
|
/**
|
|
270
401
|
* Drop the draft and stop announcing anything about it.
|
|
271
402
|
*
|
|
@@ -286,5 +417,5 @@ function formDraftMixin() {
|
|
|
286
417
|
}
|
|
287
418
|
}
|
|
288
419
|
|
|
289
|
-
export { DRAFT_DEBOUNCE_MS, DRAFT_KEY_PREFIX, DRAFT_MAX_AGE_MS, clearDraft, draftIndicatorText, draftKey, formDraftMixin, readDraft, writeDraft };
|
|
420
|
+
export { DRAFT_ANONYMOUS_USER, DRAFT_DEBOUNCE_MS, DRAFT_KEY_PREFIX, DRAFT_MAX_AGE_MS, clearAnonymousDrafts, clearDraft, draftIndicatorText, draftKey, formDraftMixin, hasDraftChanges, readDraft, resolveDraftUserId, sameDraftValue, writeDraft };
|
|
290
421
|
//# sourceMappingURL=useFormDraft.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFormDraft.js","sources":["../../../src/composables/useFormDraft.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n * SPDX-License-Identifier: AGPL-3.0-or-later\n *\n * useFormDraft — keep what somebody typed when the dialog closes on them.\n *\n * A handler filling a 27-field case type form and losing the tab starts over.\n * This writes the values to `localStorage` as they type and offers them back\n * the next time the same form opens.\n *\n * 🔴 IT NEVER DECIDES TO RESTORE. The draft is offered and the person chooses.\n * Silently refilling a form is how somebody submits last week's answers without\n * noticing they were there, and a form that fills itself is indistinguishable\n * from a form the server prefilled.\n *\n * 🔴 EVERY READ AND WRITE IS WRAPPED. `localStorage` throws in a private\n * window, with site data blocked, and inside some embedded webviews. A draft\n * that cannot be saved is a small loss; a dialog that will not open because\n * saving the draft threw is a large one.\n *\n * WHAT IT DELIBERATELY DOES NOT DO. It does not touch the server. A keystroke\n * autosave against the object API writes an audit row for every half-typed\n * value, and the audit trail is read by people answering \"who changed this\".\n */\n\nimport { translate as t } from '@nextcloud/l10n'\n\n/** How long a draft stays interesting, in milliseconds (7 days). */\nexport const DRAFT_MAX_AGE_MS = (7 * 24 * 60 * 60 * 1000)\n\n/** How long to wait after the last keystroke before writing, in milliseconds. */\nexport const DRAFT_DEBOUNCE_MS = 500\n\n/** The prefix every draft key carries, so a sweep can find them. */\nexport const DRAFT_KEY_PREFIX = 'cn-form-draft'\n\n/**\n * The storage key one form's draft lives under.\n *\n * 🔴 THE USER ID IS PART OF THE KEY. A shared browser profile at a service\n * desk is the ordinary case in a municipality, and a draft keyed without the\n * user hands the next person at the counter what the last one typed.\n *\n * @param {object} parts The identifying parts.\n * @param {string} [parts.appId] The app the form belongs to.\n * @param {string} [parts.schema] The schema being filled in.\n * @param {string} [parts.objectId] The object being edited, or '' when new.\n * @param {string} [parts.userId] Who is typing.\n * @return {string} The storage key.\n */\nexport function draftKey({ appId = '', schema = '', objectId = '', userId = '' } = {}) {\n\treturn [\n\t\tDRAFT_KEY_PREFIX,\n\t\tString(appId || 'app'),\n\t\tString(schema || 'schema'),\n\t\tString(objectId || 'new'),\n\t\tString(userId || 'anonymous'),\n\t].join(':')\n}\n\n/**\n * Read one draft back, or null when there is nothing usable.\n *\n * Returns null for an absent key, unreadable storage, unparseable JSON, a\n * draft older than {@link DRAFT_MAX_AGE_MS}, and a draft older than the object\n * it belongs to. That last one matters: a colleague who saved the record after\n * you started typing has said something more recent than your draft, and\n * offering yours on top would invite you to overwrite them without being told.\n *\n * @param {string} key The storage key.\n * @param {object} [options] Reading options.\n * @param {string} [options.objectUpdated] The object's `updated` timestamp, when it has one.\n * @param {number} [options.now] The current epoch ms, for testing.\n * @return {{values: object, savedAt: number}|null} The draft, or null.\n */\nexport function readDraft(key, { objectUpdated = '', now = Date.now() } = {}) {\n\tlet raw\n\ttry {\n\t\traw = window.localStorage.getItem(key)\n\t} catch {\n\t\t// Blocked or unavailable storage reads as \"no draft\", never as a throw.\n\t\treturn null\n\t}\n\n\tif (!raw) {\n\t\treturn null\n\t}\n\n\tlet parsed\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\t// A corrupted entry is dropped rather than offered. Nothing here can\n\t\t// repair it, and offering half a form is worse than offering none.\n\t\tclearDraft(key)\n\t\treturn null\n\t}\n\n\tif (!parsed || typeof parsed !== 'object' || typeof parsed.savedAt !== 'number') {\n\t\tclearDraft(key)\n\t\treturn null\n\t}\n\n\tif ((now - parsed.savedAt) > DRAFT_MAX_AGE_MS) {\n\t\tclearDraft(key)\n\t\treturn null\n\t}\n\n\tif (objectUpdated) {\n\t\tconst updatedAt = Date.parse(String(objectUpdated))\n\t\tif (!Number.isNaN(updatedAt) && updatedAt > parsed.savedAt) {\n\t\t\t// The record moved on after this draft was written.\n\t\t\tclearDraft(key)\n\t\t\treturn null\n\t\t}\n\t}\n\n\tif (!parsed.values || typeof parsed.values !== 'object') {\n\t\tclearDraft(key)\n\t\treturn null\n\t}\n\n\treturn { values: parsed.values, savedAt: parsed.savedAt }\n}\n\n/**\n * What the draft indicator says: \"Saving\" while a local write is pending,\n * \"Saved just now\" / \"Saved 3 minutes ago\" once it landed, nothing before that.\n *\n * After a failed save (`options.saveFailed`) it never says Saving or Saved:\n * next to a server error those words read as if the record had been saved.\n * It then says \"Draft kept on this device\" when a local copy is stored, and\n * nothing otherwise.\n *\n * @param {string} state `idle`, `saving` or `saved`.\n * @param {number} [savedAt] When the last write landed, in epoch ms.\n * @param {number} [now] The current epoch ms, for testing.\n * @param {{saveFailed?: boolean}} [options] `saveFailed`: the form shows a failed save.\n * @return {string} The text, or '' when it says nothing.\n * @spec openspec/changes/cell-labels-and-draft-indicator/specs/cell-labels-and-draft-indicator/spec.md#requirement-the-draft-indicator-never-reads-as-a-server-save-after-a-failed-save\n */\nexport function draftIndicatorText(state, savedAt = 0, now = Date.now(), options = {}) {\n\tif (options && options.saveFailed === true) {\n\t\treturn state === 'saved' ? t('nextcloud-vue', 'Draft kept on this device') : ''\n\t}\n\tif (state === 'saving') {\n\t\treturn t('nextcloud-vue', 'Saving')\n\t}\n\tif (state !== 'saved') {\n\t\treturn ''\n\t}\n\tconst minutes = Math.floor(Math.max(0, now - savedAt) / 60000)\n\tif (!savedAt || minutes < 1) {\n\t\treturn t('nextcloud-vue', 'Saved just now')\n\t}\n\ttry {\n\t\tconst relative = new Intl.RelativeTimeFormat(document.documentElement.lang || 'en', { numeric: 'auto' }).format(-minutes, 'minute')\n\t\treturn t('nextcloud-vue', 'Saved {time}', { time: relative })\n\t} catch {\n\t\treturn t('nextcloud-vue', 'Saved')\n\t}\n}\n\n/**\n * Write one draft.\n *\n * @param {string} key The storage key.\n * @param {object} values The form values.\n * @param {number} [now] The current epoch ms, for testing.\n * @return {boolean} True when it was stored.\n */\nexport function writeDraft(key, values, now = Date.now()) {\n\ttry {\n\t\twindow.localStorage.setItem(\n\t\t\tkey,\n\t\t\tJSON.stringify({ savedAt: now, values: (values || {}) }),\n\t\t)\n\t\treturn true\n\t} catch {\n\t\t// Quota exceeded, or storage blocked. The form keeps working; the\n\t\t// recovery does not exist for this session. Reported to the caller so\n\t\t// the indicator can stay quiet rather than claim \"Saved\".\n\t\treturn false\n\t}\n}\n\n/**\n * Forget one draft.\n *\n * @param {string} key The storage key.\n * @return {void}\n */\nexport function clearDraft(key) {\n\ttry {\n\t\twindow.localStorage.removeItem(key)\n\t} catch {\n\t\t// Nothing to do and nothing worth failing a dialog over.\n\t}\n}\n\n/**\n * The Options API mixin the library's components use.\n *\n * A mixin rather than a Vue 3 composable, because these components are Options\n * API and the library builds for both Vue 2.7 and Vue 3.\n *\n * @return {object} The mixin.\n */\nexport function formDraftMixin() {\n\treturn {\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\t/** The draft offered on open, or null when there is none. */\n\t\t\t\tdraftOffer: null,\n\t\t\t\t/** 'idle' | 'saving' | 'saved' — what the indicator announces. */\n\t\t\t\tdraftState: 'idle',\n\t\t\t\t/** When the last local write landed, in epoch ms. */\n\t\t\t\tdraftSavedAt: 0,\n\t\t\t\t/** Non-reactive handle for the debounce. */\n\t\t\t\tdraftTimeout: null,\n\t\t\t\t/**\n\t\t\t\t * Whether the form's own initial population has happened.\n\t\t\t\t *\n\t\t\t\t * The first change to the values is the component building\n\t\t\t\t * them, not a person typing. See the watcher that reads this.\n\t\t\t\t */\n\t\t\t\tdraftSeeded: false,\n\t\t\t}\n\t\t},\n\n\t\tbeforeUnmount() {\n\t\t\tif (this.draftTimeout) {\n\t\t\t\tclearTimeout(this.draftTimeout)\n\t\t\t\tthis.draftTimeout = null\n\t\t\t}\n\t\t},\n\n\t\tmethods: {\n\t\t\t/**\n\t\t\t * Schedule a draft write, debounced.\n\t\t\t *\n\t\t\t * @param {string} key The storage key.\n\t\t\t * @param {object} values The current form values.\n\t\t\t * @return {void}\n\t\t\t */\n\t\t\tscheduleDraftWrite(key, values) {\n\t\t\t\tif (!key) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tthis.draftState = 'saving'\n\t\t\t\tif (this.draftTimeout) {\n\t\t\t\t\tclearTimeout(this.draftTimeout)\n\t\t\t\t}\n\n\t\t\t\tthis.draftTimeout = setTimeout(() => {\n\t\t\t\t\tthis.draftTimeout = null\n\t\t\t\t\t// 'idle' and not 'saved' when the write failed: an\n\t\t\t\t\t// indicator that says Saved about a draft that is not\n\t\t\t\t\t// stored is the whole class of defect this library has\n\t\t\t\t\t// been paying down, in the one place a user is trusting it.\n\t\t\t\t\tconst stored = writeDraft(key, values)\n\t\t\t\t\tthis.draftState = stored ? 'saved' : 'idle'\n\t\t\t\t\tthis.draftSavedAt = stored ? Date.now() : 0\n\t\t\t\t}, DRAFT_DEBOUNCE_MS)\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Drop the draft and stop announcing anything about it.\n\t\t\t *\n\t\t\t * @param {string} key The storage key.\n\t\t\t * @return {void}\n\t\t\t */\n\t\t\tforgetDraft(key) {\n\t\t\t\tif (this.draftTimeout) {\n\t\t\t\t\tclearTimeout(this.draftTimeout)\n\t\t\t\t\tthis.draftTimeout = null\n\t\t\t\t}\n\n\t\t\t\tclearDraft(key)\n\t\t\t\tthis.draftOffer = null\n\t\t\t\tthis.draftState = 'idle'\n\t\t\t},\n\t\t},\n\t}\n}\n"],"names":["t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useFormDraft.js","sources":["../../../src/composables/useFormDraft.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n * SPDX-License-Identifier: AGPL-3.0-or-later\n *\n * useFormDraft — keep what somebody typed when the dialog closes on them.\n *\n * A handler filling a 27-field case type form and losing the tab starts over.\n * This writes the values to `localStorage` as they type and offers them back\n * the next time the same form opens.\n *\n * 🔴 IT NEVER DECIDES TO RESTORE. The draft is offered and the person chooses.\n * Silently refilling a form is how somebody submits last week's answers without\n * noticing they were there, and a form that fills itself is indistinguishable\n * from a form the server prefilled.\n *\n * 🔴 EVERY READ AND WRITE IS WRAPPED. `localStorage` throws in a private\n * window, with site data blocked, and inside some embedded webviews. A draft\n * that cannot be saved is a small loss; a dialog that will not open because\n * saving the draft threw is a large one.\n *\n * WHAT IT DELIBERATELY DOES NOT DO. It does not touch the server. A keystroke\n * autosave against the object API writes an audit row for every half-typed\n * value, and the audit trail is read by people answering \"who changed this\".\n */\n\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { translate as t } from '@nextcloud/l10n'\n\n/** How long a draft stays interesting, in milliseconds (7 days). */\nexport const DRAFT_MAX_AGE_MS = (7 * 24 * 60 * 60 * 1000)\n\n/** How long to wait after the last keystroke before writing, in milliseconds. */\nexport const DRAFT_DEBOUNCE_MS = 500\n\n/** The prefix every draft key carries, so a sweep can find them. */\nexport const DRAFT_KEY_PREFIX = 'cn-form-draft'\n\n/** The user part of a draft key when nobody is signed in. */\nexport const DRAFT_ANONYMOUS_USER = 'anonymous'\n\n/**\n * The storage key one form's draft lives under.\n *\n * 🔴 THE USER ID IS PART OF THE KEY. A shared browser profile at a service\n * desk is the ordinary case in a municipality, and a draft keyed without the\n * user hands the next person at the counter what the last one typed.\n *\n * @param {object} parts The identifying parts.\n * @param {string} [parts.appId] The app the form belongs to.\n * @param {string} [parts.schema] The schema being filled in.\n * @param {string} [parts.objectId] The object being edited, or '' when new.\n * @param {string} [parts.userId] Who is typing.\n * @return {string} The storage key.\n */\nexport function draftKey({ appId = '', schema = '', objectId = '', userId = '' } = {}) {\n\treturn [\n\t\tDRAFT_KEY_PREFIX,\n\t\tString(appId || 'app'),\n\t\tString(schema || 'schema'),\n\t\tString(objectId || 'new'),\n\t\tString(userId || DRAFT_ANONYMOUS_USER),\n\t].join(':')\n}\n\n/**\n * Who a draft belongs to: the id the host passed, else the signed-in\n * Nextcloud user, else '' (which {@link draftKey} stores as `anonymous`).\n *\n * 🔴 A HOST THAT PASSES NOTHING STILL GETS PER-USER DRAFTS. Leaving the user\n * to every host meant no host passed it, and every draft on a shared device\n * was keyed `anonymous`: the next person at the counter was offered what the\n * last one typed.\n *\n * @param {string} [explicit] The id the host passed (`draftUserId`).\n * @return {string} The user id, or '' when nobody is signed in.\n * @spec openspec/changes/form-draft-on-change-and-help-text-spacing/specs/form-draft-on-change-and-help-text-spacing/spec.md#requirement-a-draft-belongs-to-the-signed-in-user\n */\nexport function resolveDraftUserId(explicit = '') {\n\tif (explicit) {\n\t\treturn String(explicit)\n\t}\n\ttry {\n\t\tconst uid = getCurrentUser()?.uid\n\t\treturn uid ? String(uid) : ''\n\t} catch {\n\t\t// No Nextcloud page around us (a test, an embed): nobody is signed in.\n\t\treturn ''\n\t}\n}\n\n/**\n * Remove every draft stored under the anonymous user.\n *\n * Called once a signed-in user is known. Such a draft was written by a page\n * that did not know who was typing, so it may be somebody else's, and it is\n * deleted rather than moved: moving it would hand it to whoever opened the\n * form next.\n *\n * @return {number} How many drafts were removed.\n * @spec openspec/changes/form-draft-on-change-and-help-text-spacing/specs/form-draft-on-change-and-help-text-spacing/spec.md#requirement-a-draft-belongs-to-the-signed-in-user\n */\nexport function clearAnonymousDrafts() {\n\tlet removed = 0\n\ttry {\n\t\tconst storage = window.localStorage\n\t\tconst suffix = ':' + DRAFT_ANONYMOUS_USER\n\t\tconst doomed = []\n\t\tfor (let i = 0; i < storage.length; i++) {\n\t\t\tconst key = storage.key(i)\n\t\t\tif (key && key.startsWith(DRAFT_KEY_PREFIX + ':') && key.endsWith(suffix)) {\n\t\t\t\tdoomed.push(key)\n\t\t\t}\n\t\t}\n\t\tfor (const key of doomed) {\n\t\t\tstorage.removeItem(key)\n\t\t\tremoved++\n\t\t}\n\t} catch {\n\t\t// Blocked storage holds no drafts to leak.\n\t}\n\treturn removed\n}\n\n/**\n * Whether a value counts as empty for a draft: null, undefined, '' and [].\n *\n * @param {unknown} value The value.\n * @return {boolean} True when empty.\n */\nfunction isEmptyDraftValue(value) {\n\treturn value === null || value === undefined || value === '' || (Array.isArray(value) && value.length === 0)\n}\n\n/**\n * Whether two field values are the same for the purpose of a draft.\n *\n * Empty values are equal to each other: an input reports a missing value back\n * as '', and that is the control normalising, not a person typing.\n *\n * @param {unknown} a One value.\n * @param {unknown} b The other.\n * @return {boolean} True when they are the same.\n */\nexport function sameDraftValue(a, b) {\n\tif (isEmptyDraftValue(a) && isEmptyDraftValue(b)) {\n\t\treturn true\n\t}\n\tif (a === b) {\n\t\treturn true\n\t}\n\ttry {\n\t\treturn JSON.stringify(a) === JSON.stringify(b)\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Whether a person changed the form: one of the fields they edited holds a\n * value different from the one the form opened with.\n *\n * 🔴 ONLY EDITED FIELDS COUNT. A form fills in values by itself after it\n * opens (defaults, normalised dates, the active organisation, options that\n * arrive later). Counting those wrote a draft and said \"Saved just now\" on a\n * dialog nobody had touched.\n *\n * @param {object} values The current values.\n * @param {object} baseline The values the form opened with.\n * @param {string[]} editedKeys The fields a person edited.\n * @return {boolean} True when there is something worth keeping.\n * @spec openspec/changes/form-draft-on-change-and-help-text-spacing/specs/form-draft-on-change-and-help-text-spacing/spec.md#requirement-a-draft-is-written-only-after-a-person-changes-a-field\n */\nexport function hasDraftChanges(values, baseline, editedKeys) {\n\tconst now = values || {}\n\tconst base = baseline || {}\n\treturn (editedKeys || []).some((key) => !sameDraftValue(now[key], base[key]))\n}\n\n/**\n * Read one draft back, or null when there is nothing usable.\n *\n * Returns null for an absent key, unreadable storage, unparseable JSON, a\n * draft older than {@link DRAFT_MAX_AGE_MS}, and a draft older than the object\n * it belongs to. That last one matters: a colleague who saved the record after\n * you started typing has said something more recent than your draft, and\n * offering yours on top would invite you to overwrite them without being told.\n *\n * @param {string} key The storage key.\n * @param {object} [options] Reading options.\n * @param {string} [options.objectUpdated] The object's `updated` timestamp, when it has one.\n * @param {number} [options.now] The current epoch ms, for testing.\n * @return {{values: object, savedAt: number}|null} The draft, or null.\n */\nexport function readDraft(key, { objectUpdated = '', now = Date.now() } = {}) {\n\tlet raw\n\ttry {\n\t\traw = window.localStorage.getItem(key)\n\t} catch {\n\t\t// Blocked or unavailable storage reads as \"no draft\", never as a throw.\n\t\treturn null\n\t}\n\n\tif (!raw) {\n\t\treturn null\n\t}\n\n\tlet parsed\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\t// A corrupted entry is dropped rather than offered. Nothing here can\n\t\t// repair it, and offering half a form is worse than offering none.\n\t\tclearDraft(key)\n\t\treturn null\n\t}\n\n\tif (!parsed || typeof parsed !== 'object' || typeof parsed.savedAt !== 'number') {\n\t\tclearDraft(key)\n\t\treturn null\n\t}\n\n\tif ((now - parsed.savedAt) > DRAFT_MAX_AGE_MS) {\n\t\tclearDraft(key)\n\t\treturn null\n\t}\n\n\tif (objectUpdated) {\n\t\tconst updatedAt = Date.parse(String(objectUpdated))\n\t\tif (!Number.isNaN(updatedAt) && updatedAt > parsed.savedAt) {\n\t\t\t// The record moved on after this draft was written.\n\t\t\tclearDraft(key)\n\t\t\treturn null\n\t\t}\n\t}\n\n\tif (!parsed.values || typeof parsed.values !== 'object') {\n\t\tclearDraft(key)\n\t\treturn null\n\t}\n\n\treturn { values: parsed.values, savedAt: parsed.savedAt }\n}\n\n/**\n * What the draft indicator says: \"Saving\" while a local write is pending,\n * \"Saved just now\" / \"Saved 3 minutes ago\" once it landed, nothing before that.\n *\n * After a failed save (`options.saveFailed`) it never says Saving or Saved:\n * next to a server error those words read as if the record had been saved.\n * It then says \"Draft kept on this device\" when a local copy is stored, and\n * nothing otherwise.\n *\n * @param {string} state `idle`, `saving` or `saved`.\n * @param {number} [savedAt] When the last write landed, in epoch ms.\n * @param {number} [now] The current epoch ms, for testing.\n * @param {{saveFailed?: boolean}} [options] `saveFailed`: the form shows a failed save.\n * @return {string} The text, or '' when it says nothing.\n * @spec openspec/changes/cell-labels-and-draft-indicator/specs/cell-labels-and-draft-indicator/spec.md#requirement-the-draft-indicator-never-reads-as-a-server-save-after-a-failed-save\n */\nexport function draftIndicatorText(state, savedAt = 0, now = Date.now(), options = {}) {\n\tif (options && options.saveFailed === true) {\n\t\treturn state === 'saved' ? t('nextcloud-vue', 'Draft kept on this device') : ''\n\t}\n\tif (state === 'saving') {\n\t\treturn t('nextcloud-vue', 'Saving')\n\t}\n\tif (state !== 'saved') {\n\t\treturn ''\n\t}\n\tconst minutes = Math.floor(Math.max(0, now - savedAt) / 60000)\n\tif (!savedAt || minutes < 1) {\n\t\treturn t('nextcloud-vue', 'Saved just now')\n\t}\n\ttry {\n\t\tconst relative = new Intl.RelativeTimeFormat(document.documentElement.lang || 'en', { numeric: 'auto' }).format(-minutes, 'minute')\n\t\treturn t('nextcloud-vue', 'Saved {time}', { time: relative })\n\t} catch {\n\t\treturn t('nextcloud-vue', 'Saved')\n\t}\n}\n\n/**\n * Write one draft.\n *\n * @param {string} key The storage key.\n * @param {object} values The form values.\n * @param {number} [now] The current epoch ms, for testing.\n * @return {boolean} True when it was stored.\n */\nexport function writeDraft(key, values, now = Date.now()) {\n\ttry {\n\t\twindow.localStorage.setItem(\n\t\t\tkey,\n\t\t\tJSON.stringify({ savedAt: now, values: (values || {}) }),\n\t\t)\n\t\treturn true\n\t} catch {\n\t\t// Quota exceeded, or storage blocked. The form keeps working; the\n\t\t// recovery does not exist for this session. Reported to the caller so\n\t\t// the indicator can stay quiet rather than claim \"Saved\".\n\t\treturn false\n\t}\n}\n\n/**\n * Forget one draft.\n *\n * @param {string} key The storage key.\n * @return {void}\n */\nexport function clearDraft(key) {\n\ttry {\n\t\twindow.localStorage.removeItem(key)\n\t} catch {\n\t\t// Nothing to do and nothing worth failing a dialog over.\n\t}\n}\n\n/**\n * The Options API mixin the library's components use.\n *\n * A mixin rather than a Vue 3 composable, because these components are Options\n * API and the library builds for both Vue 2.7 and Vue 3.\n *\n * @return {object} The mixin.\n */\nexport function formDraftMixin() {\n\treturn {\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\t/** The draft offered on open, or null when there is none. */\n\t\t\t\tdraftOffer: null,\n\t\t\t\t/** 'idle' | 'saving' | 'saved' — what the indicator announces. */\n\t\t\t\tdraftState: 'idle',\n\t\t\t\t/** When the last local write landed, in epoch ms. */\n\t\t\t\tdraftSavedAt: 0,\n\t\t\t\t/** Non-reactive handle for the debounce. */\n\t\t\t\tdraftTimeout: null,\n\t\t\t}\n\t\t},\n\n\t\tbeforeUnmount() {\n\t\t\tif (this.draftTimeout) {\n\t\t\t\tclearTimeout(this.draftTimeout)\n\t\t\t\tthis.draftTimeout = null\n\t\t\t}\n\t\t},\n\n\t\tmethods: {\n\t\t\t/**\n\t\t\t * Schedule a draft write, debounced.\n\t\t\t *\n\t\t\t * @param {string} key The storage key.\n\t\t\t * @param {object} values The current form values.\n\t\t\t * @return {void}\n\t\t\t */\n\t\t\tscheduleDraftWrite(key, values) {\n\t\t\t\tif (!key) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tthis.draftState = 'saving'\n\t\t\t\tif (this.draftTimeout) {\n\t\t\t\t\tclearTimeout(this.draftTimeout)\n\t\t\t\t}\n\n\t\t\t\tthis.draftTimeout = setTimeout(() => {\n\t\t\t\t\tthis.draftTimeout = null\n\t\t\t\t\t// 'idle' and not 'saved' when the write failed: an\n\t\t\t\t\t// indicator that says Saved about a draft that is not\n\t\t\t\t\t// stored is the whole class of defect this library has\n\t\t\t\t\t// been paying down, in the one place a user is trusting it.\n\t\t\t\t\tconst stored = writeDraft(key, values)\n\t\t\t\t\tthis.draftState = stored ? 'saved' : 'idle'\n\t\t\t\t\tthis.draftSavedAt = stored ? Date.now() : 0\n\t\t\t\t}, DRAFT_DEBOUNCE_MS)\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * The form is back to what it opened with: cancel a pending write,\n\t\t\t * remove a draft written earlier and stop announcing anything.\n\t\t\t *\n\t\t\t * @param {string} key The storage key.\n\t\t\t * @return {void}\n\t\t\t * @spec openspec/changes/form-draft-on-change-and-help-text-spacing/specs/form-draft-on-change-and-help-text-spacing/spec.md#requirement-a-draft-is-written-only-after-a-person-changes-a-field\n\t\t\t */\n\t\t\tsettleUnchangedDraft(key) {\n\t\t\t\tif (this.draftTimeout) {\n\t\t\t\t\tclearTimeout(this.draftTimeout)\n\t\t\t\t\tthis.draftTimeout = null\n\t\t\t\t}\n\t\t\t\tif (this.draftState !== 'idle') {\n\t\t\t\t\tclearDraft(key)\n\t\t\t\t\tthis.draftState = 'idle'\n\t\t\t\t\tthis.draftSavedAt = 0\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Drop the draft and stop announcing anything about it.\n\t\t\t *\n\t\t\t * @param {string} key The storage key.\n\t\t\t * @return {void}\n\t\t\t */\n\t\t\tforgetDraft(key) {\n\t\t\t\tif (this.draftTimeout) {\n\t\t\t\t\tclearTimeout(this.draftTimeout)\n\t\t\t\t\tthis.draftTimeout = null\n\t\t\t\t}\n\n\t\t\t\tclearDraft(key)\n\t\t\t\tthis.draftOffer = null\n\t\t\t\tthis.draftState = 'idle'\n\t\t\t},\n\t\t},\n\t}\n}\n"],"names":["t"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isRef, ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
|
|
2
|
+
import { facetFilterParams } from '../utils/facets.js';
|
|
2
3
|
import { useObjectStore } from '../store/useObjectStore.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -163,10 +164,9 @@ function useListView(objectTypeOrOptions, options) {
|
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
for (const [key, values] of Object.entries(activeFilters.value)) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
167
|
+
// Single-value arrays are unwrapped to scalar params; the
|
|
168
|
+
// missing-value option becomes `<key>_isnull=true`.
|
|
169
|
+
Object.assign(params, facetFilterParams(key, values));
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
// Fixed filters (e.g. a route-param-scoped `pages[].config.filter`) are
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListView.js","sources":["../../../src/composables/useListView.js"],"sourcesContent":["import { computed, isRef, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useObjectStore } from '../store/index.js'\n\n/**\n * Composable for managing list view state with full objectStore integration.\n *\n * When called with an `objectType` string, connects to the objectStore and handles\n * schema loading, collection fetching, sidebar wiring, and all event handlers\n * automatically. Everything a `CnIndexPage`-based list view needs is returned\n * directly — no additional computed properties or methods required in the component.\n *\n * Backward-compatible: existing `useListView(options)` and `useListView()` calls\n * continue to work without modification.\n *\n * @param {string|Function|object} [objectTypeOrOptions] Object type slug (new API), a getter or ref returning one (the list follows it when it changes), or a legacy options object\n * @param {object} [options] Options (new API only)\n * @param {object|null} [options.objectStore] Custom object store instance (from createObjectStore). When provided, uses this store instead of the default useObjectStore(). Required when the app uses createObjectStore with a custom store ID.\n * @param {object|null} [options.sidebarState] Sidebar state object from `inject('sidebarState')`. When provided, the composable wires and unwires the sidebar automatically on mount/unmount.\n * @param {number} [options.defaultPageSize] Default `_limit` sent to the API\n * @param {number} [options.debounceMs] Search debounce in milliseconds\n * @param {object} [options.defaultSort] Default sort applied on mount e.g. `{ key: 'createdAt', order: 'desc' }`\n * @param {Array<{key: string, order: 'asc'|'desc'}>} [options.defaultSortKeys] Default MULTI-column sort applied on mount (ordered priority list). Takes precedence over `defaultSort` when both are given; used e.g. to restore a sort persisted in the route query.\n * @param {Array<string>|null} [options.defaultVisibleColumns] Initial visible-column key set. Seeds the sidebar's Columns tab with the curated default (e.g. a manifest `columns` list) so toggles add/remove from it. Omit (or `null`) for schema-driven tables where every column starts visible.\n * @param {object} [options.defaultActiveFilters] Initial facet-filter map, e.g. to restore filters persisted in the route query on mount.\n * @param {string} [options.defaultSearchTerm] Initial search term, e.g. to restore `_search` from the route query on mount.\n * @param {object|(() => object)} [options.fixedFilters] A filter map (or getter returning one) merged into every fetch AFTER the user's facet filters, so the fixed entries always win. Used e.g. by `CnIndexPage` to apply a route-param-scoped `pages[].config.filter`. Default `{}` — omitting it is behaviourally identical to before.\n * @param {Array<string>|(() => Array<string>)} [options.extend] OpenRegister `_extend[]` values (or a getter returning them) forwarded on every fetch, e.g. `['calculations']` so a schema's `materialise: false` calculations are evaluated and can be shown as columns. Default `[]` — omitting it sends no `_extend` at all, exactly as before.\n * @return {object} Reactive state and event handlers\n *\n * @example\n * // New API — minimal\n * const { schema, objects, loading, pagination,\n * onSearch, onSort, onFilterChange, onPageChange, refresh } = useListView('client')\n *\n * @example\n * // New API — with sidebar wiring\n * const list = useListView('client', {\n * sidebarState: inject('sidebarState', null),\n * })\n *\n * @example\n * // Legacy API — still works\n * const { searchTerm, filters, onSearchInput, toggleSort } = useListView({\n * objectType: 'client',\n * fetchFn: (type, params) => objectStore.fetchCollection(type, params),\n * })\n */\nexport function useListView(objectTypeOrOptions, options) {\n\t// Backward compat: if first arg is an object or absent, delegate to legacy implementation\n\tif (!objectTypeOrOptions || (typeof objectTypeOrOptions === 'object' && !isRef(objectTypeOrOptions))) {\n\t\treturn useLegacyListView(objectTypeOrOptions || {})\n\t}\n\n\t// ── New API ──────────────────────────────────────────────────────────\n\t// A string, a ref or a getter: a getter lets the page switch object type\n\t// (a folder carrying its own schema) without re-creating the list.\n\tconst typeSource = objectTypeOrOptions\n\tconst isDynamicType = typeof typeSource !== 'string'\n\tconst getType = () => (isRef(typeSource) ? typeSource.value : (typeof typeSource === 'function' ? typeSource() : typeSource))\n\tconst opts = options || {}\n\tconst sidebarState = opts.sidebarState || null\n\n\tconst objectStore = opts.objectStore || useObjectStore()\n\n\t// ── State refs ───────────────────────────────────────────────────────\n\tconst schema = ref(null)\n\tconst searchTerm = ref(opts.defaultSearchTerm || '')\n\t// `sortKeys` is the source of truth (ordered multi-column priority list);\n\t// `sortKey`/`sortOrder` are kept as a primary-entry mirror so every\n\t// existing reader of the pre-multi-sort refs keeps working unchanged.\n\tconst initialSortKeys = Array.isArray(opts.defaultSortKeys) && opts.defaultSortKeys.length > 0\n\t\t? opts.defaultSortKeys\n\t\t: (opts.defaultSort?.key ? [{ key: opts.defaultSort.key, order: opts.defaultSort.order || 'asc' }] : [])\n\tconst sortKeys = ref(initialSortKeys)\n\tconst sortKey = ref(initialSortKeys[0]?.key || null)\n\tconst sortOrder = ref(initialSortKeys[0]?.order || 'asc')\n\tconst activeFilters = ref((opts.defaultActiveFilters && typeof opts.defaultActiveFilters === 'object') ? opts.defaultActiveFilters : {})\n\t// Seed the visible-column set to the configured columns so the sidebar's\n\t// Columns tab reflects the curated default and toggles add/remove from it.\n\t// `null` (no seed) means \"all columns visible\" (schema-driven tables).\n\tconst visibleColumns = ref(opts.defaultVisibleColumns ?? null)\n\tconst pageSize = ref(opts.defaultPageSize || 20)\n\n\t// ── Computed refs from the store ─────────────────────────────────────\n\tconst objects = computed(() => objectStore.collections[getType()] || [])\n\t// True from creation until the mount sequence below has issued its first\n\t// fetch. The store's own flag only goes up once `fetchCollection` runs, and\n\t// `onMounted` awaits `fetchSchema()` first — so for the length of that round\n\t// trip the list was \"not loading\" with zero rows, and every consumer\n\t// rendered its empty state (\"No log entries to show\") before any data had\n\t// been asked for. Folded into `loading` rather than exposed separately so\n\t// existing consumers get the fix without touching their templates.\n\tconst bootstrapping = ref(true)\n\tconst loading = computed(() => bootstrapping.value || objectStore.loading[getType()] || false)\n\tconst pagination = computed(() => objectStore.pagination[getType()] || { total: 0, page: 1, pages: 1, limit: 20 })\n\t// Facets are computed by the platform over the whole query rather than the\n\t// loaded page, so anything that needs the complete set of values for a\n\t// field (a folder pane grouping by it, say) reads them here rather than\n\t// deriving them from `objects`, which is only the current page.\n\tconst facets = computed(() => objectStore.facets[getType()] || {})\n\t// Personal-lens reports of the latest response (`{ recent: { available, reason } }`).\n\t// @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-a-list-response-carries-the-report-of-the-lenses-it-was-asked-for\n\tconst lenses = computed(() => (objectStore.lenses && objectStore.lenses[getType()]) || {})\n\t// Outcome of the latest `refresh()`: the store's error for it, or null once\n\t// it succeeded or a later fetch stored new rows. The store keeps the previous\n\t// rows on a failed fetch, so this is how a consumer tells stale rows from\n\t// current ones.\n\tconst error = ref(null)\n\tlet refreshSeq = 0\n\tlet pendingRefreshes = 0\n\n\tlet searchTimeout = null\n\n\t// ── Param construction ───────────────────────────────────────────────\n\n\t/**\n\t * Resolve `opts.fixedFilters` to a plain map. Accepts a plain object OR a\n\t * function/getter returning one (so callers can derive it from reactive\n\t * sources — e.g. route params — and have it re-read on every fetch).\n\t *\n\t * @return {object} The fixed-filter map (may be empty).\n\t */\n\tfunction resolveFixedFilters() {\n\t\tconst f = typeof opts.fixedFilters === 'function' ? opts.fixedFilters() : opts.fixedFilters\n\t\treturn (f && typeof f === 'object') ? f : {}\n\t}\n\n\t/**\n\t * Resolve `opts.extend` to a list of non-empty strings.\n\t *\n\t * Accepts an array or a getter returning one, so a caller can re-scope it\n\t * between fetches the way `fixedFilters` allows.\n\t *\n\t * @return {Array<string>} The `_extend[]` values, possibly empty.\n\t */\n\tfunction resolveExtend() {\n\t\tconst e = typeof opts.extend === 'function' ? opts.extend() : opts.extend\n\t\tif (!Array.isArray(e)) {\n\t\t\treturn []\n\t\t}\n\t\treturn e.filter((v) => typeof v === 'string' && v !== '')\n\t}\n\n\t/**\n\t * Build API fetch params from current reactive state.\n\t *\n\t * @param {number} page Page number to request\n\t * @return {object} Params object ready to pass to fetchCollection\n\t */\n\tfunction buildParams(page) {\n\t\tconst params = { _limit: pageSize.value, _page: page }\n\n\t\tif (searchTerm.value) {\n\t\t\tparams._search = searchTerm.value\n\t\t}\n\n\t\tif (sortKeys.value.length > 0) {\n\t\t\t// Object key insertion order = sort priority order, matching\n\t\t\t// OpenRegister's `_order` format exactly (`ObjectsController::\n\t\t\t// normalizeOrderParameter` / `MagicMapper`'s ordering loop). A\n\t\t\t// single active key produces byte-identical output to before.\n\t\t\tparams._order = Object.fromEntries(sortKeys.value.map((k) => [k.key, k.order]))\n\t\t}\n\n\t\tfor (const [key, values] of Object.entries(activeFilters.value)) {\n\t\t\tif (values && values.length > 0) {\n\t\t\t\t// Single-value arrays are unwrapped to scalar params\n\t\t\t\tparams[key] = values.length === 1 ? values[0] : values\n\t\t\t}\n\t\t}\n\n\t\t// Fixed filters (e.g. a route-param-scoped `pages[].config.filter`) are\n\t\t// merged LAST so they always win over a colliding facet `activeFilter`.\n\t\t// Repeated `_extend[]`, serialized by the shared query builder. Set\n\t\t// before the fixed filters so an explicit `_extend` in a filter map\n\t\t// still wins, matching every other key's precedence here.\n\t\tconst extend = resolveExtend()\n\t\tif (extend.length > 0) {\n\t\t\tparams._extend = extend\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(resolveFixedFilters())) {\n\t\t\tif (value !== undefined && value !== null && value !== '') {\n\t\t\t\tparams[key] = value\n\t\t\t}\n\t\t}\n\n\t\treturn params\n\t}\n\n\t// ── Fetch ────────────────────────────────────────────────────────────\n\n\t/**\n\t * Fetch the collection using current state params and update sidebar facet data.\n\t * Only the most recently started refresh sets `error`, so an older response\n\t * settling late cannot override a newer one's outcome.\n\t *\n\t * @param {number} [page] Page to fetch\n\t * @return {Promise<void>}\n\t */\n\tasync function refresh(page = 1) {\n\t\tconst seq = ++refreshSeq\n\t\t// This call's own result. `errors[getType()]` is shared by every call\n\t\t// for the type and is not cleared on success, so it cannot say which\n\t\t// request failed; it is only the fallback for a store that ignores\n\t\t// `options.outcome`.\n\t\tconst outcome = {}\n\t\tpendingRefreshes++\n\t\ttry {\n\t\t\tawait objectStore.fetchCollection(getType(), buildParams(page), { outcome })\n\t\t} catch (e) {\n\t\t\tif (seq === refreshSeq) {\n\t\t\t\terror.value = e\n\t\t\t}\n\t\t\tthrow e\n\t\t} finally {\n\t\t\tpendingRefreshes--\n\t\t}\n\t\tif (seq === refreshSeq) {\n\t\t\terror.value = ('error' in outcome) ? outcome.error : (objectStore.errors?.[getType()] || null)\n\t\t}\n\t}\n\n\t// A fetch outside `refresh()` (e.g. the live-updates refetch) that stores\n\t// new rows recovers the list from an earlier failure. Rows written while\n\t// a refresh is pending are left to that refresh, which decides `error`.\n\twatch(\n\t\t() => objectStore.collections[getType()],\n\t\t() => {\n\t\t\tif (pendingRefreshes === 0) {\n\t\t\t\terror.value = null\n\t\t\t}\n\t\t},\n\t\t{ flush: 'sync' },\n\t)\n\n\t// ── Event handlers ───────────────────────────────────────────────────\n\n\t/**\n\t * Handle search input. Debounced by `options.debounceMs` (default 300 ms).\n\t *\n\t * @param {string} value New search string\n\t */\n\tfunction onSearch(value) {\n\t\tsearchTerm.value = value\n\t\tclearTimeout(searchTimeout)\n\t\tsearchTimeout = setTimeout(() => refresh(1), opts.debounceMs || 300)\n\t}\n\n\t/**\n\t * Handle sort change. Updates sort state and triggers refresh.\n\t *\n\t * Accepts CnDataTable's extended `sort` event payload: `keys` (the full\n\t * ordered multi-column list) is used when present; a legacy caller\n\t * passing only `{ key, order }` (no `keys`) still works exactly as\n\t * before — it's normalised to a single-entry `sortKeys` list.\n\t *\n\t * @param {{key: string|null, order: string|null, keys?: Array<{key: string, order: string}>}} sort New sort definition.\n\t */\n\tfunction onSort({ key, order, keys }) {\n\t\tconst nextKeys = Array.isArray(keys) ? keys : (key ? [{ key, order: order || 'asc' }] : [])\n\t\tsortKeys.value = nextKeys\n\t\tsortKey.value = nextKeys[0]?.key ?? null\n\t\tsortOrder.value = nextKeys[0]?.order ?? 'asc'\n\t\trefresh(1)\n\t}\n\n\t/**\n\t * Handle filter change for a single key. Empty arrays remove the key.\n\t *\n\t * @param {string} key Filter key (maps to API param name)\n\t * @param {Array} values Selected filter values\n\t */\n\tfunction onFilterChange(key, values) {\n\t\tif (!values || values.length === 0) {\n\t\t\tconst updated = { ...activeFilters.value }\n\t\t\tdelete updated[key]\n\t\t\tactiveFilters.value = updated\n\t\t} else {\n\t\t\tactiveFilters.value = { ...activeFilters.value, [key]: values }\n\t\t}\n\t\trefresh(1)\n\t}\n\n\t/**\n\t * Handle page navigation.\n\t *\n\t * @param {number} page Page number to navigate to\n\t */\n\tfunction onPageChange(page) {\n\t\trefresh(page)\n\t}\n\n\t/**\n\t * Handle page-size change. Resets to page 1.\n\t *\n\t * @param {number} size New page size\n\t */\n\tfunction onPageSizeChange(size) {\n\t\tpageSize.value = size\n\t\trefresh(1)\n\t}\n\n\t// ── Sidebar wiring ───────────────────────────────────────────────────\n\n\tfunction setupSidebar() {\n\t\tif (!sidebarState) {\n\t\t\treturn\n\t\t}\n\t\tsidebarState.active = true\n\t\tsidebarState.schema = schema.value\n\t\tsidebarState.searchValue = searchTerm.value\n\t\tsidebarState.activeFilters = {}\n\t\tsidebarState.onSearch = onSearch\n\t\tsidebarState.onColumnsChange = (cols) => {\n\t\t\tvisibleColumns.value = cols\n\t\t}\n\t\tsidebarState.onFilterChange = ({ key, values }) => onFilterChange(key, values)\n\t}\n\n\tfunction teardownSidebar() {\n\t\tif (!sidebarState) {\n\t\t\treturn\n\t\t}\n\t\tsidebarState.active = false\n\t\tsidebarState.schema = null\n\t\tsidebarState.activeFilters = {}\n\t\tsidebarState.facetData = {}\n\t\tsidebarState.onSearch = null\n\t\tsidebarState.onColumnsChange = null\n\t\tsidebarState.onFilterChange = null\n\t}\n\n\t// Push facet data to sidebar after each store update\n\tif (sidebarState) {\n\t\twatch(\n\t\t\t() => objectStore.facets[getType()],\n\t\t\t(facets) => {\n\t\t\t\tsidebarState.facetData = facets || {}\n\t\t\t},\n\t\t)\n\t}\n\n\t// A dynamic object type that changes starts that type's list from page 1\n\t// with its own schema.\n\tif (isDynamicType) {\n\t\twatch(getType, async (next, prev) => {\n\t\t\tif (!next || next === prev) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tschema.value = null\n\t\t\tschema.value = await objectStore.fetchSchema(next)\n\t\t\tawait refresh(1)\n\t\t})\n\t}\n\n\t// ── Lifecycle ────────────────────────────────────────────────────────\n\n\tonMounted(async () => {\n\t\ttry {\n\t\t\tschema.value = await objectStore.fetchSchema(getType())\n\t\t\tif (sidebarState) {\n\t\t\t\tsetupSidebar()\n\t\t\t}\n\t\t\tawait refresh(1)\n\t\t} finally {\n\t\t\t// Hand the loading state back to the store either way: a schema\n\t\t\t// fetch that rejects must not leave the list spinning forever.\n\t\t\tbootstrapping.value = false\n\t\t}\n\t})\n\n\tonBeforeUnmount(() => {\n\t\tclearTimeout(searchTimeout)\n\t\tteardownSidebar()\n\t})\n\n\t// ── Return value ─────────────────────────────────────────────────────\n\n\treturn {\n\t\t// Store-derived\n\t\tschema,\n\t\tobjects,\n\t\tloading,\n\t\tpagination,\n\t\tfacets,\n\t\tlenses,\n\t\terror,\n\t\t// Local state\n\t\tsearchTerm,\n\t\tsortKey,\n\t\tsortOrder,\n\t\tsortKeys,\n\t\tactiveFilters,\n\t\tvisibleColumns,\n\t\tpageSize,\n\t\t// Event handlers\n\t\tonSearch,\n\t\tonSort,\n\t\tonFilterChange,\n\t\tonPageChange,\n\t\tonPageSizeChange,\n\t\t// Explicit fetch\n\t\trefresh,\n\t\t// The query the list sends (search, sort, facet filters, fixed filters),\n\t\t// built from the current state, for an export that must follow the list.\n\t\tbuildParams,\n\t}\n}\n\n// ── Legacy implementation ─────────────────────────────────────────────────────\n\n/**\n * Legacy `useListView(options)` implementation.\n * Preserved verbatim for backward compatibility.\n *\n * @param {object} options Legacy options object\n * @param {string} [options.objectType] The registered object type slug\n * @param {(type: string, params: object) => Promise<Array<object>>} [options.fetchFn] Function to call: (type, params) => Promise<Array>\n * @param {number} [options.debounceMs] Search debounce in milliseconds\n * @param {number} [options.pageSize] Default page size\n * @param {object} [options.defaultSort] Default sort: { key: string, order: 'asc'|'desc' }\n * @return {object} Reactive state and methods\n */\nfunction useLegacyListView(options) {\n\tconst searchTerm = ref('')\n\tconst filters = ref({})\n\tconst sortKey = ref(options.defaultSort?.key || null)\n\tconst sortOrder = ref(options.defaultSort?.order || 'asc')\n\tconst currentPage = ref(1)\n\tconst pageSize = ref(options.pageSize || 20)\n\n\tlet searchTimeout = null\n\n\tfunction buildFetchParams() {\n\t\tconst params = {\n\t\t\t_limit: pageSize.value,\n\t\t\t_page: currentPage.value,\n\t\t}\n\n\t\tif (searchTerm.value) {\n\t\t\tparams._search = searchTerm.value\n\t\t}\n\n\t\tif (sortKey.value) {\n\t\t\tparams._order = { [sortKey.value]: sortOrder.value }\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(filters.value)) {\n\t\t\tif (value !== null && value !== '' && value !== false) {\n\t\t\t\tparams[key] = value\n\t\t\t}\n\t\t}\n\n\t\treturn params\n\t}\n\n\tasync function fetchData(page) {\n\t\tif (page !== undefined) {\n\t\t\tcurrentPage.value = page\n\t\t}\n\t\tconst params = buildFetchParams()\n\t\tif (options.fetchFn) {\n\t\t\treturn options.fetchFn(options.objectType, params)\n\t\t}\n\t}\n\n\tfunction onSearchInput(value) {\n\t\tsearchTerm.value = value\n\t\tclearTimeout(searchTimeout)\n\t\tsearchTimeout = setTimeout(() => fetchData(1), options.debounceMs || 300)\n\t}\n\n\tfunction toggleSort(key) {\n\t\tif (sortKey.value === key) {\n\t\t\tif (sortOrder.value === 'asc') {\n\t\t\t\tsortOrder.value = 'desc'\n\t\t\t} else {\n\t\t\t\tsortKey.value = null\n\t\t\t\tsortOrder.value = 'asc'\n\t\t\t}\n\t\t} else {\n\t\t\tsortKey.value = key\n\t\t\tsortOrder.value = 'asc'\n\t\t}\n\t\tfetchData(1)\n\t}\n\n\tfunction setFilter(key, value) {\n\t\tfilters.value = { ...filters.value, [key]: value }\n\t\tfetchData(1)\n\t}\n\n\tfunction clearAllFilters() {\n\t\tsearchTerm.value = ''\n\t\tfilters.value = {}\n\t\tsortKey.value = options.defaultSort?.key || null\n\t\tsortOrder.value = options.defaultSort?.order || 'asc'\n\t\tfetchData(1)\n\t}\n\n\tfunction goToPage(page) {\n\t\tcurrentPage.value = page\n\t\tfetchData()\n\t}\n\n\tonBeforeUnmount(() => clearTimeout(searchTimeout))\n\n\treturn {\n\t\tsearchTerm,\n\t\tfilters,\n\t\tsortKey,\n\t\tsortOrder,\n\t\tcurrentPage,\n\t\tpageSize,\n\t\tonSearchInput,\n\t\ttoggleSort,\n\t\tsetFilter,\n\t\tclearAllFilters,\n\t\tgoToPage,\n\t\tfetch: fetchData,\n\t\tbuildFetchParams,\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useListView.js","sources":["../../../src/composables/useListView.js"],"sourcesContent":["import { computed, isRef, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useObjectStore } from '../store/index.js'\nimport { facetFilterParams } from '../utils/facets.js'\n\n/**\n * Composable for managing list view state with full objectStore integration.\n *\n * When called with an `objectType` string, connects to the objectStore and handles\n * schema loading, collection fetching, sidebar wiring, and all event handlers\n * automatically. Everything a `CnIndexPage`-based list view needs is returned\n * directly — no additional computed properties or methods required in the component.\n *\n * Backward-compatible: existing `useListView(options)` and `useListView()` calls\n * continue to work without modification.\n *\n * @param {string|Function|object} [objectTypeOrOptions] Object type slug (new API), a getter or ref returning one (the list follows it when it changes), or a legacy options object\n * @param {object} [options] Options (new API only)\n * @param {object|null} [options.objectStore] Custom object store instance (from createObjectStore). When provided, uses this store instead of the default useObjectStore(). Required when the app uses createObjectStore with a custom store ID.\n * @param {object|null} [options.sidebarState] Sidebar state object from `inject('sidebarState')`. When provided, the composable wires and unwires the sidebar automatically on mount/unmount.\n * @param {number} [options.defaultPageSize] Default `_limit` sent to the API\n * @param {number} [options.debounceMs] Search debounce in milliseconds\n * @param {object} [options.defaultSort] Default sort applied on mount e.g. `{ key: 'createdAt', order: 'desc' }`\n * @param {Array<{key: string, order: 'asc'|'desc'}>} [options.defaultSortKeys] Default MULTI-column sort applied on mount (ordered priority list). Takes precedence over `defaultSort` when both are given; used e.g. to restore a sort persisted in the route query.\n * @param {Array<string>|null} [options.defaultVisibleColumns] Initial visible-column key set. Seeds the sidebar's Columns tab with the curated default (e.g. a manifest `columns` list) so toggles add/remove from it. Omit (or `null`) for schema-driven tables where every column starts visible.\n * @param {object} [options.defaultActiveFilters] Initial facet-filter map, e.g. to restore filters persisted in the route query on mount.\n * @param {string} [options.defaultSearchTerm] Initial search term, e.g. to restore `_search` from the route query on mount.\n * @param {object|(() => object)} [options.fixedFilters] A filter map (or getter returning one) merged into every fetch AFTER the user's facet filters, so the fixed entries always win. Used e.g. by `CnIndexPage` to apply a route-param-scoped `pages[].config.filter`. Default `{}` — omitting it is behaviourally identical to before.\n * @param {Array<string>|(() => Array<string>)} [options.extend] OpenRegister `_extend[]` values (or a getter returning them) forwarded on every fetch, e.g. `['calculations']` so a schema's `materialise: false` calculations are evaluated and can be shown as columns. Default `[]` — omitting it sends no `_extend` at all, exactly as before.\n * @return {object} Reactive state and event handlers\n *\n * @example\n * // New API — minimal\n * const { schema, objects, loading, pagination,\n * onSearch, onSort, onFilterChange, onPageChange, refresh } = useListView('client')\n *\n * @example\n * // New API — with sidebar wiring\n * const list = useListView('client', {\n * sidebarState: inject('sidebarState', null),\n * })\n *\n * @example\n * // Legacy API — still works\n * const { searchTerm, filters, onSearchInput, toggleSort } = useListView({\n * objectType: 'client',\n * fetchFn: (type, params) => objectStore.fetchCollection(type, params),\n * })\n */\nexport function useListView(objectTypeOrOptions, options) {\n\t// Backward compat: if first arg is an object or absent, delegate to legacy implementation\n\tif (!objectTypeOrOptions || (typeof objectTypeOrOptions === 'object' && !isRef(objectTypeOrOptions))) {\n\t\treturn useLegacyListView(objectTypeOrOptions || {})\n\t}\n\n\t// ── New API ──────────────────────────────────────────────────────────\n\t// A string, a ref or a getter: a getter lets the page switch object type\n\t// (a folder carrying its own schema) without re-creating the list.\n\tconst typeSource = objectTypeOrOptions\n\tconst isDynamicType = typeof typeSource !== 'string'\n\tconst getType = () => (isRef(typeSource) ? typeSource.value : (typeof typeSource === 'function' ? typeSource() : typeSource))\n\tconst opts = options || {}\n\tconst sidebarState = opts.sidebarState || null\n\n\tconst objectStore = opts.objectStore || useObjectStore()\n\n\t// ── State refs ───────────────────────────────────────────────────────\n\tconst schema = ref(null)\n\tconst searchTerm = ref(opts.defaultSearchTerm || '')\n\t// `sortKeys` is the source of truth (ordered multi-column priority list);\n\t// `sortKey`/`sortOrder` are kept as a primary-entry mirror so every\n\t// existing reader of the pre-multi-sort refs keeps working unchanged.\n\tconst initialSortKeys = Array.isArray(opts.defaultSortKeys) && opts.defaultSortKeys.length > 0\n\t\t? opts.defaultSortKeys\n\t\t: (opts.defaultSort?.key ? [{ key: opts.defaultSort.key, order: opts.defaultSort.order || 'asc' }] : [])\n\tconst sortKeys = ref(initialSortKeys)\n\tconst sortKey = ref(initialSortKeys[0]?.key || null)\n\tconst sortOrder = ref(initialSortKeys[0]?.order || 'asc')\n\tconst activeFilters = ref((opts.defaultActiveFilters && typeof opts.defaultActiveFilters === 'object') ? opts.defaultActiveFilters : {})\n\t// Seed the visible-column set to the configured columns so the sidebar's\n\t// Columns tab reflects the curated default and toggles add/remove from it.\n\t// `null` (no seed) means \"all columns visible\" (schema-driven tables).\n\tconst visibleColumns = ref(opts.defaultVisibleColumns ?? null)\n\tconst pageSize = ref(opts.defaultPageSize || 20)\n\n\t// ── Computed refs from the store ─────────────────────────────────────\n\tconst objects = computed(() => objectStore.collections[getType()] || [])\n\t// True from creation until the mount sequence below has issued its first\n\t// fetch. The store's own flag only goes up once `fetchCollection` runs, and\n\t// `onMounted` awaits `fetchSchema()` first — so for the length of that round\n\t// trip the list was \"not loading\" with zero rows, and every consumer\n\t// rendered its empty state (\"No log entries to show\") before any data had\n\t// been asked for. Folded into `loading` rather than exposed separately so\n\t// existing consumers get the fix without touching their templates.\n\tconst bootstrapping = ref(true)\n\tconst loading = computed(() => bootstrapping.value || objectStore.loading[getType()] || false)\n\tconst pagination = computed(() => objectStore.pagination[getType()] || { total: 0, page: 1, pages: 1, limit: 20 })\n\t// Facets are computed by the platform over the whole query rather than the\n\t// loaded page, so anything that needs the complete set of values for a\n\t// field (a folder pane grouping by it, say) reads them here rather than\n\t// deriving them from `objects`, which is only the current page.\n\tconst facets = computed(() => objectStore.facets[getType()] || {})\n\t// Personal-lens reports of the latest response (`{ recent: { available, reason } }`).\n\t// @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-a-list-response-carries-the-report-of-the-lenses-it-was-asked-for\n\tconst lenses = computed(() => (objectStore.lenses && objectStore.lenses[getType()]) || {})\n\t// Outcome of the latest `refresh()`: the store's error for it, or null once\n\t// it succeeded or a later fetch stored new rows. The store keeps the previous\n\t// rows on a failed fetch, so this is how a consumer tells stale rows from\n\t// current ones.\n\tconst error = ref(null)\n\tlet refreshSeq = 0\n\tlet pendingRefreshes = 0\n\n\tlet searchTimeout = null\n\n\t// ── Param construction ───────────────────────────────────────────────\n\n\t/**\n\t * Resolve `opts.fixedFilters` to a plain map. Accepts a plain object OR a\n\t * function/getter returning one (so callers can derive it from reactive\n\t * sources — e.g. route params — and have it re-read on every fetch).\n\t *\n\t * @return {object} The fixed-filter map (may be empty).\n\t */\n\tfunction resolveFixedFilters() {\n\t\tconst f = typeof opts.fixedFilters === 'function' ? opts.fixedFilters() : opts.fixedFilters\n\t\treturn (f && typeof f === 'object') ? f : {}\n\t}\n\n\t/**\n\t * Resolve `opts.extend` to a list of non-empty strings.\n\t *\n\t * Accepts an array or a getter returning one, so a caller can re-scope it\n\t * between fetches the way `fixedFilters` allows.\n\t *\n\t * @return {Array<string>} The `_extend[]` values, possibly empty.\n\t */\n\tfunction resolveExtend() {\n\t\tconst e = typeof opts.extend === 'function' ? opts.extend() : opts.extend\n\t\tif (!Array.isArray(e)) {\n\t\t\treturn []\n\t\t}\n\t\treturn e.filter((v) => typeof v === 'string' && v !== '')\n\t}\n\n\t/**\n\t * Build API fetch params from current reactive state.\n\t *\n\t * @param {number} page Page number to request\n\t * @return {object} Params object ready to pass to fetchCollection\n\t */\n\tfunction buildParams(page) {\n\t\tconst params = { _limit: pageSize.value, _page: page }\n\n\t\tif (searchTerm.value) {\n\t\t\tparams._search = searchTerm.value\n\t\t}\n\n\t\tif (sortKeys.value.length > 0) {\n\t\t\t// Object key insertion order = sort priority order, matching\n\t\t\t// OpenRegister's `_order` format exactly (`ObjectsController::\n\t\t\t// normalizeOrderParameter` / `MagicMapper`'s ordering loop). A\n\t\t\t// single active key produces byte-identical output to before.\n\t\t\tparams._order = Object.fromEntries(sortKeys.value.map((k) => [k.key, k.order]))\n\t\t}\n\n\t\tfor (const [key, values] of Object.entries(activeFilters.value)) {\n\t\t\t// Single-value arrays are unwrapped to scalar params; the\n\t\t\t// missing-value option becomes `<key>_isnull=true`.\n\t\t\tObject.assign(params, facetFilterParams(key, values))\n\t\t}\n\n\t\t// Fixed filters (e.g. a route-param-scoped `pages[].config.filter`) are\n\t\t// merged LAST so they always win over a colliding facet `activeFilter`.\n\t\t// Repeated `_extend[]`, serialized by the shared query builder. Set\n\t\t// before the fixed filters so an explicit `_extend` in a filter map\n\t\t// still wins, matching every other key's precedence here.\n\t\tconst extend = resolveExtend()\n\t\tif (extend.length > 0) {\n\t\t\tparams._extend = extend\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(resolveFixedFilters())) {\n\t\t\tif (value !== undefined && value !== null && value !== '') {\n\t\t\t\tparams[key] = value\n\t\t\t}\n\t\t}\n\n\t\treturn params\n\t}\n\n\t// ── Fetch ────────────────────────────────────────────────────────────\n\n\t/**\n\t * Fetch the collection using current state params and update sidebar facet data.\n\t * Only the most recently started refresh sets `error`, so an older response\n\t * settling late cannot override a newer one's outcome.\n\t *\n\t * @param {number} [page] Page to fetch\n\t * @return {Promise<void>}\n\t */\n\tasync function refresh(page = 1) {\n\t\tconst seq = ++refreshSeq\n\t\t// This call's own result. `errors[getType()]` is shared by every call\n\t\t// for the type and is not cleared on success, so it cannot say which\n\t\t// request failed; it is only the fallback for a store that ignores\n\t\t// `options.outcome`.\n\t\tconst outcome = {}\n\t\tpendingRefreshes++\n\t\ttry {\n\t\t\tawait objectStore.fetchCollection(getType(), buildParams(page), { outcome })\n\t\t} catch (e) {\n\t\t\tif (seq === refreshSeq) {\n\t\t\t\terror.value = e\n\t\t\t}\n\t\t\tthrow e\n\t\t} finally {\n\t\t\tpendingRefreshes--\n\t\t}\n\t\tif (seq === refreshSeq) {\n\t\t\terror.value = ('error' in outcome) ? outcome.error : (objectStore.errors?.[getType()] || null)\n\t\t}\n\t}\n\n\t// A fetch outside `refresh()` (e.g. the live-updates refetch) that stores\n\t// new rows recovers the list from an earlier failure. Rows written while\n\t// a refresh is pending are left to that refresh, which decides `error`.\n\twatch(\n\t\t() => objectStore.collections[getType()],\n\t\t() => {\n\t\t\tif (pendingRefreshes === 0) {\n\t\t\t\terror.value = null\n\t\t\t}\n\t\t},\n\t\t{ flush: 'sync' },\n\t)\n\n\t// ── Event handlers ───────────────────────────────────────────────────\n\n\t/**\n\t * Handle search input. Debounced by `options.debounceMs` (default 300 ms).\n\t *\n\t * @param {string} value New search string\n\t */\n\tfunction onSearch(value) {\n\t\tsearchTerm.value = value\n\t\tclearTimeout(searchTimeout)\n\t\tsearchTimeout = setTimeout(() => refresh(1), opts.debounceMs || 300)\n\t}\n\n\t/**\n\t * Handle sort change. Updates sort state and triggers refresh.\n\t *\n\t * Accepts CnDataTable's extended `sort` event payload: `keys` (the full\n\t * ordered multi-column list) is used when present; a legacy caller\n\t * passing only `{ key, order }` (no `keys`) still works exactly as\n\t * before — it's normalised to a single-entry `sortKeys` list.\n\t *\n\t * @param {{key: string|null, order: string|null, keys?: Array<{key: string, order: string}>}} sort New sort definition.\n\t */\n\tfunction onSort({ key, order, keys }) {\n\t\tconst nextKeys = Array.isArray(keys) ? keys : (key ? [{ key, order: order || 'asc' }] : [])\n\t\tsortKeys.value = nextKeys\n\t\tsortKey.value = nextKeys[0]?.key ?? null\n\t\tsortOrder.value = nextKeys[0]?.order ?? 'asc'\n\t\trefresh(1)\n\t}\n\n\t/**\n\t * Handle filter change for a single key. Empty arrays remove the key.\n\t *\n\t * @param {string} key Filter key (maps to API param name)\n\t * @param {Array} values Selected filter values\n\t */\n\tfunction onFilterChange(key, values) {\n\t\tif (!values || values.length === 0) {\n\t\t\tconst updated = { ...activeFilters.value }\n\t\t\tdelete updated[key]\n\t\t\tactiveFilters.value = updated\n\t\t} else {\n\t\t\tactiveFilters.value = { ...activeFilters.value, [key]: values }\n\t\t}\n\t\trefresh(1)\n\t}\n\n\t/**\n\t * Handle page navigation.\n\t *\n\t * @param {number} page Page number to navigate to\n\t */\n\tfunction onPageChange(page) {\n\t\trefresh(page)\n\t}\n\n\t/**\n\t * Handle page-size change. Resets to page 1.\n\t *\n\t * @param {number} size New page size\n\t */\n\tfunction onPageSizeChange(size) {\n\t\tpageSize.value = size\n\t\trefresh(1)\n\t}\n\n\t// ── Sidebar wiring ───────────────────────────────────────────────────\n\n\tfunction setupSidebar() {\n\t\tif (!sidebarState) {\n\t\t\treturn\n\t\t}\n\t\tsidebarState.active = true\n\t\tsidebarState.schema = schema.value\n\t\tsidebarState.searchValue = searchTerm.value\n\t\tsidebarState.activeFilters = {}\n\t\tsidebarState.onSearch = onSearch\n\t\tsidebarState.onColumnsChange = (cols) => {\n\t\t\tvisibleColumns.value = cols\n\t\t}\n\t\tsidebarState.onFilterChange = ({ key, values }) => onFilterChange(key, values)\n\t}\n\n\tfunction teardownSidebar() {\n\t\tif (!sidebarState) {\n\t\t\treturn\n\t\t}\n\t\tsidebarState.active = false\n\t\tsidebarState.schema = null\n\t\tsidebarState.activeFilters = {}\n\t\tsidebarState.facetData = {}\n\t\tsidebarState.onSearch = null\n\t\tsidebarState.onColumnsChange = null\n\t\tsidebarState.onFilterChange = null\n\t}\n\n\t// Push facet data to sidebar after each store update\n\tif (sidebarState) {\n\t\twatch(\n\t\t\t() => objectStore.facets[getType()],\n\t\t\t(facets) => {\n\t\t\t\tsidebarState.facetData = facets || {}\n\t\t\t},\n\t\t)\n\t}\n\n\t// A dynamic object type that changes starts that type's list from page 1\n\t// with its own schema.\n\tif (isDynamicType) {\n\t\twatch(getType, async (next, prev) => {\n\t\t\tif (!next || next === prev) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tschema.value = null\n\t\t\tschema.value = await objectStore.fetchSchema(next)\n\t\t\tawait refresh(1)\n\t\t})\n\t}\n\n\t// ── Lifecycle ────────────────────────────────────────────────────────\n\n\tonMounted(async () => {\n\t\ttry {\n\t\t\tschema.value = await objectStore.fetchSchema(getType())\n\t\t\tif (sidebarState) {\n\t\t\t\tsetupSidebar()\n\t\t\t}\n\t\t\tawait refresh(1)\n\t\t} finally {\n\t\t\t// Hand the loading state back to the store either way: a schema\n\t\t\t// fetch that rejects must not leave the list spinning forever.\n\t\t\tbootstrapping.value = false\n\t\t}\n\t})\n\n\tonBeforeUnmount(() => {\n\t\tclearTimeout(searchTimeout)\n\t\tteardownSidebar()\n\t})\n\n\t// ── Return value ─────────────────────────────────────────────────────\n\n\treturn {\n\t\t// Store-derived\n\t\tschema,\n\t\tobjects,\n\t\tloading,\n\t\tpagination,\n\t\tfacets,\n\t\tlenses,\n\t\terror,\n\t\t// Local state\n\t\tsearchTerm,\n\t\tsortKey,\n\t\tsortOrder,\n\t\tsortKeys,\n\t\tactiveFilters,\n\t\tvisibleColumns,\n\t\tpageSize,\n\t\t// Event handlers\n\t\tonSearch,\n\t\tonSort,\n\t\tonFilterChange,\n\t\tonPageChange,\n\t\tonPageSizeChange,\n\t\t// Explicit fetch\n\t\trefresh,\n\t\t// The query the list sends (search, sort, facet filters, fixed filters),\n\t\t// built from the current state, for an export that must follow the list.\n\t\tbuildParams,\n\t}\n}\n\n// ── Legacy implementation ─────────────────────────────────────────────────────\n\n/**\n * Legacy `useListView(options)` implementation.\n * Preserved verbatim for backward compatibility.\n *\n * @param {object} options Legacy options object\n * @param {string} [options.objectType] The registered object type slug\n * @param {(type: string, params: object) => Promise<Array<object>>} [options.fetchFn] Function to call: (type, params) => Promise<Array>\n * @param {number} [options.debounceMs] Search debounce in milliseconds\n * @param {number} [options.pageSize] Default page size\n * @param {object} [options.defaultSort] Default sort: { key: string, order: 'asc'|'desc' }\n * @return {object} Reactive state and methods\n */\nfunction useLegacyListView(options) {\n\tconst searchTerm = ref('')\n\tconst filters = ref({})\n\tconst sortKey = ref(options.defaultSort?.key || null)\n\tconst sortOrder = ref(options.defaultSort?.order || 'asc')\n\tconst currentPage = ref(1)\n\tconst pageSize = ref(options.pageSize || 20)\n\n\tlet searchTimeout = null\n\n\tfunction buildFetchParams() {\n\t\tconst params = {\n\t\t\t_limit: pageSize.value,\n\t\t\t_page: currentPage.value,\n\t\t}\n\n\t\tif (searchTerm.value) {\n\t\t\tparams._search = searchTerm.value\n\t\t}\n\n\t\tif (sortKey.value) {\n\t\t\tparams._order = { [sortKey.value]: sortOrder.value }\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(filters.value)) {\n\t\t\tif (value !== null && value !== '' && value !== false) {\n\t\t\t\tparams[key] = value\n\t\t\t}\n\t\t}\n\n\t\treturn params\n\t}\n\n\tasync function fetchData(page) {\n\t\tif (page !== undefined) {\n\t\t\tcurrentPage.value = page\n\t\t}\n\t\tconst params = buildFetchParams()\n\t\tif (options.fetchFn) {\n\t\t\treturn options.fetchFn(options.objectType, params)\n\t\t}\n\t}\n\n\tfunction onSearchInput(value) {\n\t\tsearchTerm.value = value\n\t\tclearTimeout(searchTimeout)\n\t\tsearchTimeout = setTimeout(() => fetchData(1), options.debounceMs || 300)\n\t}\n\n\tfunction toggleSort(key) {\n\t\tif (sortKey.value === key) {\n\t\t\tif (sortOrder.value === 'asc') {\n\t\t\t\tsortOrder.value = 'desc'\n\t\t\t} else {\n\t\t\t\tsortKey.value = null\n\t\t\t\tsortOrder.value = 'asc'\n\t\t\t}\n\t\t} else {\n\t\t\tsortKey.value = key\n\t\t\tsortOrder.value = 'asc'\n\t\t}\n\t\tfetchData(1)\n\t}\n\n\tfunction setFilter(key, value) {\n\t\tfilters.value = { ...filters.value, [key]: value }\n\t\tfetchData(1)\n\t}\n\n\tfunction clearAllFilters() {\n\t\tsearchTerm.value = ''\n\t\tfilters.value = {}\n\t\tsortKey.value = options.defaultSort?.key || null\n\t\tsortOrder.value = options.defaultSort?.order || 'asc'\n\t\tfetchData(1)\n\t}\n\n\tfunction goToPage(page) {\n\t\tcurrentPage.value = page\n\t\tfetchData()\n\t}\n\n\tonBeforeUnmount(() => clearTimeout(searchTimeout))\n\n\treturn {\n\t\tsearchTerm,\n\t\tfilters,\n\t\tsortKey,\n\t\tsortOrder,\n\t\tcurrentPage,\n\t\tpageSize,\n\t\tonSearchInput,\n\t\ttoggleSort,\n\t\tsetFilter,\n\t\tclearAllFilters,\n\t\tgoToPage,\n\t\tfetch: fetchData,\n\t\tbuildFetchParams,\n\t}\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/index.js
CHANGED
|
@@ -60,6 +60,8 @@ export { default as CnAppLoading } from './components/CnAppLoading/CnAppLoading.
|
|
|
60
60
|
export { default as CnAppNav } from './components/CnAppNav/CnAppNav.vue.js';
|
|
61
61
|
export { default as CnAppRoot } from './components/CnAppRoot/CnAppRoot.vue.js';
|
|
62
62
|
export { default as CnAuditTrailCard } from './components/CnAuditTrailCard/CnAuditTrailCard.vue.js';
|
|
63
|
+
export { default as CnAuditTrailWidget } from './components/CnAuditTrailWidget/CnAuditTrailWidget.vue.js';
|
|
64
|
+
export { default as CnBannerWidget } from './components/CnBannerWidget/CnBannerWidget.vue.js';
|
|
63
65
|
export { default as CnBoardView } from './components/CnBoardView/CnBoardView.vue.js';
|
|
64
66
|
export { default as CnBodySections } from './components/CnBodySections/CnBodySections.vue.js';
|
|
65
67
|
export { default as CnBreadcrumbs } from './components/CnBreadcrumbs/CnBreadcrumbs.vue.js';
|
|
@@ -75,7 +77,9 @@ export { default as CnChartWidget } from './components/CnChartWidget/CnChartWidg
|
|
|
75
77
|
export { default as CnChartWidgetForm } from './components/CnChartWidgetForm/CnChartWidgetForm.vue.js';
|
|
76
78
|
export { default as CnChatPage } from './components/CnChatPage/CnChatPage.vue.js';
|
|
77
79
|
export { default as CnChildRecordsField } from './components/CnChildRecordsField/CnChildRecordsField.vue.js';
|
|
80
|
+
export { default as CnChipsFieldWidget } from './components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js';
|
|
78
81
|
export { default as CnChoiceCards } from './components/CnChoiceCards/CnChoiceCards.vue.js';
|
|
82
|
+
export { default as CnChoicePickerWidget } from './components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js';
|
|
79
83
|
export { default as CnColorPicker } from './components/CnColorPicker/CnColorPicker.vue.js';
|
|
80
84
|
export { default as CnCommandPalette } from './components/CnCommandPalette/CnCommandPalette.vue.js';
|
|
81
85
|
export { default as CnConfigurationCard } from './components/CnConfigurationCard/CnConfigurationCard.vue.js';
|
|
@@ -87,6 +91,7 @@ export { default as CnContainerWidget } from './components/CnContainerWidget/CnC
|
|
|
87
91
|
export { default as CnContainerWidgetForm } from './components/CnContainerWidgetForm/CnContainerWidgetForm.vue.js';
|
|
88
92
|
export { default as CnContextMenu } from './components/CnContextMenu/CnContextMenu.vue.js';
|
|
89
93
|
export { default as CnConversationThread } from './components/CnConversationThread/CnConversationThread.vue.js';
|
|
94
|
+
export { default as CnConversationWidget } from './components/CnConversationThread/CnConversationWidget.vue.js';
|
|
90
95
|
export { default as CnCopyDialog } from './components/CnCopyDialog/CnCopyDialog.vue.js';
|
|
91
96
|
export { default as CnCountdownWidget } from './components/CnCountdownWidget/CnCountdownWidget.vue.js';
|
|
92
97
|
export { default as CnCountdownWidgetForm } from './components/CnCountdownWidgetForm/CnCountdownWidgetForm.vue.js';
|
|
@@ -114,6 +119,7 @@ export { default as CnDetailWidgetHost } from './components/CnDetailWidgetHost/C
|
|
|
114
119
|
export { default as CnDividerWidget } from './components/CnDividerWidget/CnDividerWidget.vue.js';
|
|
115
120
|
export { default as CnDividerWidgetForm } from './components/CnDividerWidgetForm/CnDividerWidgetForm.vue.js';
|
|
116
121
|
export { default as CnDocumentReviewList } from './components/CnDocumentReviewList/CnDocumentReviewList.vue.js';
|
|
122
|
+
export { default as CnDocumentReviewWidget } from './components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js';
|
|
117
123
|
export { default as CnDurationField } from './components/CnDurationField/CnDurationField.vue.js';
|
|
118
124
|
export { default as CnEditActionsModal } from './dialogs/CnEditActionsModal.vue.js';
|
|
119
125
|
export { default as CnEditDataModal } from './dialogs/CnEditDataModal.vue.js';
|
|
@@ -129,6 +135,7 @@ export { default as CnEmailTab } from './integrations/builtin/email/CnEmailTab.v
|
|
|
129
135
|
export { default as CnEnvironmentBanner } from './components/CnEnvironmentBanner/CnEnvironmentBanner.vue.js';
|
|
130
136
|
export { default as CnExportWizard } from './components/CnExportWizard/CnExportWizard.vue.js';
|
|
131
137
|
export { default as CnFacetSidebar } from './components/CnFacetSidebar/CnFacetSidebar.vue.js';
|
|
138
|
+
export { default as CnFactsWidget } from './components/CnFactsWidget/CnFactsWidget.vue.js';
|
|
132
139
|
export { default as CnFavouriteToggle } from './components/CnFavouriteToggle/CnFavouriteToggle.vue.js';
|
|
133
140
|
export { default as CnFeaturesAndRoadmapLink } from './components/CnFeaturesAndRoadmapLink/CnFeaturesAndRoadmapLink.vue.js';
|
|
134
141
|
export { default as CnFeaturesAndRoadmapPage } from './components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue.js';
|
|
@@ -137,6 +144,7 @@ export { default as CnFeaturesAndRoadmapView } from './components/CnFeaturesAndR
|
|
|
137
144
|
export { default as CnFeaturesTab } from './components/CnFeaturesTab/CnFeaturesTab.vue.js';
|
|
138
145
|
export { default as CnFederationStatus } from './components/CnFederationStatus/CnFederationStatus.vue.js';
|
|
139
146
|
export { default as CnFieldHelper } from './components/CnFieldHelper/CnFieldHelper.vue.js';
|
|
147
|
+
export { default as CnFieldListWidget } from './components/CnFieldListWidget/CnFieldListWidget.vue.js';
|
|
140
148
|
export { default as CnFileField } from './components/CnFileField/CnFileField.vue.js';
|
|
141
149
|
export { default as CnFileManager } from './components/CnFileManager/CnFileManager.vue.js';
|
|
142
150
|
export { default as CnFilePreview } from './components/CnFilePreview/CnFilePreview.vue.js';
|
|
@@ -179,11 +187,13 @@ export { default as CnIntegrationCard } from './components/CnIntegrationCard/CnI
|
|
|
179
187
|
export { default as CnIntegrationTab } from './components/CnIntegrationTab/CnIntegrationTab.vue.js';
|
|
180
188
|
export { default as CnIntegrationWidget } from './components/CnIntegrationWidget/CnIntegrationWidget.vue.js';
|
|
181
189
|
export { default as CnIntegrationWidgetGrid } from './components/CnIntegrationWidgetGrid/CnIntegrationWidgetGrid.vue.js';
|
|
190
|
+
export { default as CnInteractionFormWidget } from './components/CnInteractionFormWidget/CnInteractionFormWidget.vue.js';
|
|
182
191
|
export { default as CnItemCard } from './components/CnItemCard/CnItemCard.vue.js';
|
|
183
192
|
export { CnJourney } from './components/CnJourney/index.js';
|
|
184
193
|
export { CnJourneyDialog } from './components/CnJourneyDialog/index.js';
|
|
185
194
|
export { default as CnJourneyReviewList } from './components/CnJourneyReviewList/CnJourneyReviewList.vue.js';
|
|
186
195
|
export { default as CnJsonViewer } from './components/CnJsonViewer/CnJsonViewer.vue.js';
|
|
196
|
+
export { default as CnKbSearchWidget } from './components/CnKbSearchWidget/CnKbSearchWidget.vue.js';
|
|
187
197
|
export { default as CnKpiGrid } from './components/CnKpiGrid/CnKpiGrid.vue.js';
|
|
188
198
|
export { default as CnLabelWidget } from './components/CnLabelWidget/CnLabelWidget.vue.js';
|
|
189
199
|
export { default as CnLabelWidgetForm } from './components/CnLabelWidgetForm/CnLabelWidgetForm.vue.js';
|
|
@@ -215,6 +225,7 @@ export { default as CnNcDashboardWidgetForm } from './components/CnNcDashboardWi
|
|
|
215
225
|
export { default as CnNcWidgetGridPicker } from './components/CnNcWidgetGridPicker/CnNcWidgetGridPicker.vue.js';
|
|
216
226
|
export { default as CnNcWidgetWidget } from './components/CnNcWidgetWidget/CnNcWidgetWidget.vue.js';
|
|
217
227
|
export { default as CnNextStepCard } from './components/CnNextStepCard/CnNextStepCard.vue.js';
|
|
228
|
+
export { default as CnNextStepWidget } from './components/CnNextStepCard/CnNextStepWidget.vue.js';
|
|
218
229
|
export { default as CnNoteBody } from './components/CnNoteBody/CnNoteBody.vue.js';
|
|
219
230
|
export { default as CnNoteCard } from './components/CnNoteCard/CnNoteCard.vue.js';
|
|
220
231
|
export { default as CnNoteComposer } from './components/CnNoteComposer/CnNoteComposer.vue.js';
|
|
@@ -253,6 +264,8 @@ export { default as CnPropertyValueCell } from './components/CnAdvancedFormDialo
|
|
|
253
264
|
export { default as CnQuickFilterBar } from './components/CnQuickFilterBar/CnQuickFilterBar.vue.js';
|
|
254
265
|
export { default as CnQuicklinksWidget } from './components/CnQuicklinksWidget/CnQuicklinksWidget.vue.js';
|
|
255
266
|
export { default as CnQuicklinksWidgetForm } from './components/CnQuicklinksWidgetForm/CnQuicklinksWidgetForm.vue.js';
|
|
267
|
+
export { default as CnRadioCardsWidget } from './components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js';
|
|
268
|
+
export { default as CnRecordListWidget } from './components/CnRecordListWidget/CnRecordListWidget.vue.js';
|
|
256
269
|
export { default as CnReferencePreview } from './components/CnReferencePreview/CnReferencePreview.vue.js';
|
|
257
270
|
export { default as CnRegisterMapping } from './components/CnRegisterMapping/CnRegisterMapping.vue.js';
|
|
258
271
|
export { default as CnRegisterSchemaSelect } from './components/CnRegisterSchemaSelect/CnRegisterSchemaSelect.vue.js';
|
|
@@ -270,12 +283,15 @@ export { default as CnSavedViewPresentationDialog } from './components/CnSavedVi
|
|
|
270
283
|
export { default as CnSavedViewsControl } from './components/CnSavedViewsControl/CnSavedViewsControl.vue.js';
|
|
271
284
|
export { default as CnSavedViewShareDialog } from './components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue.js';
|
|
272
285
|
export { default as CnSavedViewShareFields } from './components/CnSavedViewShareFields/CnSavedViewShareFields.vue.js';
|
|
286
|
+
export { default as CnSavedViewsWidget } from './components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js';
|
|
273
287
|
export { default as CnSavedViewWidget } from './components/CnSavedViewWidget/CnSavedViewWidget.vue.js';
|
|
274
288
|
export { default as CnSavedViewWidgetForm } from './components/CnSavedViewWidgetForm/CnSavedViewWidgetForm.vue.js';
|
|
275
289
|
export { default as CnSaveViewDialog } from './components/CnSaveViewDialog/CnSaveViewDialog.vue.js';
|
|
276
290
|
export { default as CnSchemaFormDialog } from './components/CnSchemaFormDialog/CnSchemaFormDialog.vue.js';
|
|
277
291
|
export { default as CnSearchPage } from './components/CnSearchPage/CnSearchPage.vue.js';
|
|
292
|
+
export { default as CnSectionsWidget } from './components/CnSectionsWidget/CnSectionsWidget.vue.js';
|
|
278
293
|
export { default as CnSettingsCard } from './components/CnSettingsCard/CnSettingsCard.vue.js';
|
|
294
|
+
export { default as CnSettingsFormWidget } from './components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js';
|
|
279
295
|
export { default as CnSettingsPage } from './components/CnSettingsPage/CnSettingsPage.vue.js';
|
|
280
296
|
export { default as CnSettingsSection } from './components/CnSettingsSection/CnSettingsSection.vue.js';
|
|
281
297
|
export { default as CnSetupWizard } from './components/CnSetupWizard/CnSetupWizard.vue.js';
|
|
@@ -295,6 +311,7 @@ export { default as CnStructuredDocReview } from './components/CnStructuredDocRe
|
|
|
295
311
|
export { default as CnSubObjectsField } from './components/CnSubObjectsField/CnSubObjectsField.vue.js';
|
|
296
312
|
export { default as CnSummaryAggregates } from './components/CnSummaryAggregates/CnSummaryAggregates.vue.js';
|
|
297
313
|
export { default as CnSupportDialog } from './components/CnSupportDialog/CnSupportDialog.vue.js';
|
|
314
|
+
export { default as CnSwitchRowsWidget } from './components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js';
|
|
298
315
|
export { default as CnTab } from './components/CnTabs/CnTab.vue.js';
|
|
299
316
|
export { default as CnTabbedFormDialog } from './components/CnTabbedFormDialog/CnTabbedFormDialog.vue.js';
|
|
300
317
|
export { default as CnTableWidget } from './components/CnTableWidget/CnTableWidget.vue.js';
|
|
@@ -315,6 +332,7 @@ export { default as CnTileWidget } from './components/CnTileWidget/CnTileWidget.
|
|
|
315
332
|
export { default as CnTimelineStages } from './components/CnTimelineStages/CnTimelineStages.vue.js';
|
|
316
333
|
export { default as CnTimelineView } from './components/CnTimelineView/CnTimelineView.vue.js';
|
|
317
334
|
export { default as CnTimelineWidget } from './components/CnTimelineWidget/CnTimelineWidget.vue.js';
|
|
335
|
+
export { default as CnToggleTableWidget } from './components/CnToggleTableWidget/CnToggleTableWidget.vue.js';
|
|
318
336
|
export { default as CnTranslatedBadge } from './components/CnTranslatedBadge/CnTranslatedBadge.vue.js';
|
|
319
337
|
export { default as CnTreeView } from './components/CnTreeView/CnTreeView.vue.js';
|
|
320
338
|
export { default as CnUnreadMarker } from './components/CnUnreadMarker/CnUnreadMarker.vue.js';
|
|
@@ -337,6 +355,7 @@ export { default as CnWidgetStyleEditorModal } from './dialogs/CnWidgetStyleEdit
|
|
|
337
355
|
export { default as CnWidgetVisibilityRulesModal } from './dialogs/CnWidgetVisibilityRulesModal.vue.js';
|
|
338
356
|
export { default as CnWidgetWrapper } from './components/CnWidgetWrapper/CnWidgetWrapper.vue.js';
|
|
339
357
|
export { default as CnWizardDialog } from './components/CnWizardDialog/CnWizardDialog.vue.js';
|
|
358
|
+
export { default as CnWorkspaceFilterWidget } from './components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue.js';
|
|
340
359
|
export { DEFAULT_DATE_RANGE_PRESETS, resolvePresetWindow } from './components/CnDateRangePicker/CnDateRangePicker.vue2.js';
|
|
341
360
|
export { defaultPageTypes } from './components/CnPageRenderer/pageTypes.js';
|
|
342
361
|
export { fetchWebdavFolderTree } from './components/CnFolderSidebar/webdavFolders.js';
|
|
@@ -459,7 +478,8 @@ export { columnsFromSchema, fieldsFromSchema, filtersFromSchema, formatValue, va
|
|
|
459
478
|
export { findRepeatingWrite, journeyItemTargets } from './utils/journeyRepeatingWrite.js';
|
|
460
479
|
export { DYNAMIC_KEY_PREFIX, EXTENDS_FORM_KEY, PREFILL_KEY, splitDynamicFormData, usesArrayValues, valueArrayFor, valueRecordsFor } from './utils/dynamicProperties.js';
|
|
461
480
|
export { SchemaBreakingChangeError, SchemaHasObjectsError, deleteSchema, describeSchemaChange, saveSchema } from './utils/schemaApi.js';
|
|
462
|
-
export { filterWidgetsByVisibility, getCurrentUserGroups,
|
|
481
|
+
export { filterWidgetsByVisibility, getCurrentUserGroups, isWidgetVisible, resetVisibilityCache } from './utils/widgetVisibility.js';
|
|
482
|
+
export { getCurrentUserId, peekCurrentUserGroups } from './utils/userGroupsPeek.js';
|
|
463
483
|
export { safeHref, safeImageSrc, safeSvgPath } from './utils/safeHref.js';
|
|
464
484
|
export { followLinkClick, isModifiedClick, openRowTarget, resolveHref } from './utils/linkNavigation.js';
|
|
465
485
|
export { isNewTabHandled, isRowMiddleClick } from './utils/rowAuxClick.js';
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|