@conduction/nextcloud-vue 2.85.0 → 2.87.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/CnAdvancedFormDialog/CnMetadataTab.vue.js +1 -1
- package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue2.js +3 -2
- package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue2.js.map +1 -1
- package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue3.js.map +1 -1
- package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue.js +1 -1
- package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue2.js +5 -4
- package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue3.js.map +1 -1
- package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue.js +1 -1
- package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue2.js +2 -1
- package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue2.js.map +1 -1
- package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +2 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +156 -14
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +105 -74
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue.js +1 -1
- package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue2.js +2 -1
- package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue2.js.map +1 -1
- package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue3.js.map +1 -1
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue.js +1 -1
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js +2 -1
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +6 -5
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue.js +1 -1
- package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue2.js +2 -1
- package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue2.js.map +1 -1
- package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue3.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +2 -1
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +2 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +2 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue.js +1 -1
- package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue2.js +3 -1
- package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue2.js.map +1 -1
- package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailHeaderChips.vue2.js +2 -1
- package/dist/esm/components/CnDetailPage/CnDetailHeaderChips.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailHeaderChips.vue3.js.map +1 -1
- package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue.js +1 -1
- package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue2.js +2 -1
- package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue2.js.map +1 -1
- package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue3.js.map +1 -1
- package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue.js +1 -1
- package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue2.js +3 -1
- package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue2.js.map +1 -1
- package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue3.js.map +1 -1
- package/dist/esm/components/CnFileManager/CnFileManager.vue.js +1 -1
- package/dist/esm/components/CnFileManager/CnFileManager.vue2.js +3 -1
- package/dist/esm/components/CnFileManager/CnFileManager.vue2.js.map +1 -1
- package/dist/esm/components/CnFileManager/CnFileManager.vue3.js.map +1 -1
- package/dist/esm/components/CnFilePreview/CnFilePreview.vue.js +1 -1
- package/dist/esm/components/CnFilePreview/CnFilePreview.vue2.js +2 -1
- package/dist/esm/components/CnFilePreview/CnFilePreview.vue2.js.map +1 -1
- package/dist/esm/components/CnFilePreview/CnFilePreview.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +49 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +3 -3
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue.js +1 -1
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js +2 -1
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js.map +1 -1
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +30 -5
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js +2 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue.js +1 -1
- package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue2.js +3 -2
- package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +28 -0
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +82 -75
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue2.js +5 -4
- package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue2.js +2 -1
- package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue2.js +2 -1
- package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +2 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue2.js +2 -1
- package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue3.js.map +1 -1
- package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue.js +1 -1
- package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue2.js +2 -1
- package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue2.js.map +1 -1
- package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue3.js.map +1 -1
- package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue.js +1 -1
- package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue2.js +2 -1
- package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue2.js.map +1 -1
- package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue3.js.map +1 -1
- package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue2.js +2 -0
- package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue3.js +2 -2
- package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue.js +1 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js +2 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js.map +1 -1
- package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue.js +1 -1
- package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue2.js +2 -1
- package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue2.js.map +1 -1
- package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +36 -0
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +55 -40
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksCard/CnTasksCard.vue.js +1 -1
- package/dist/esm/components/CnTasksCard/CnTasksCard.vue2.js +2 -1
- package/dist/esm/components/CnTasksCard/CnTasksCard.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksCard/CnTasksCard.vue3.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +2 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTranslatedBadge/CnTranslatedBadge.vue.js +1 -1
- package/dist/esm/components/CnTranslatedBadge/CnTranslatedBadge.vue2.js +2 -1
- package/dist/esm/components/CnTranslatedBadge/CnTranslatedBadge.vue2.js.map +1 -1
- package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue.js +1 -1
- package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue2.js +2 -1
- package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue2.js.map +1 -1
- package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue.js +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +3 -26
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
- package/dist/esm/dialogs/CnNoteHistoryDialog.vue.js +1 -1
- package/dist/esm/dialogs/CnNoteHistoryDialog.vue2.js +2 -1
- package/dist/esm/dialogs/CnNoteHistoryDialog.vue2.js.map +1 -1
- package/dist/esm/dialogs/CnNoteHistoryDialog.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/activity/CnActivityCard.vue.js +1 -1
- package/dist/esm/integrations/builtin/activity/CnActivityCard.vue2.js +3 -2
- package/dist/esm/integrations/builtin/activity/CnActivityCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/activity/CnActivityCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/activity/CnActivityTab.vue.js +1 -1
- package/dist/esm/integrations/builtin/activity/CnActivityTab.vue2.js +4 -3
- package/dist/esm/integrations/builtin/activity/CnActivityTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/activity/CnActivityTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue.js +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -2
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue.js +1 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue.js +1 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js +6 -5
- package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue.js +1 -1
- package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue.js +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue.js +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue.js +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue.js +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue.js +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue.js +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +35 -2
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +38 -2
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +1068 -962
- package/dist/esm/store/useObjectStore.js +7 -2
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/boardColumns.js +50 -1
- package/dist/esm/utils/boardColumns.js.map +1 -1
- package/dist/esm/utils/boardReferenceColumns.js +115 -4
- package/dist/esm/utils/boardReferenceColumns.js.map +1 -1
- package/dist/esm/utils/contentHeaderLink.js +48 -1
- package/dist/esm/utils/contentHeaderLink.js.map +1 -1
- package/dist/esm/utils/schema.js +5 -4
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/userLocale.js +30 -0
- package/dist/esm/utils/userLocale.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +1088 -476
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1068 -962
- package/l10n/en.json +35 -2
- package/l10n/nl.json +38 -2
- package/package.json +1 -1
- package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +3 -2
- package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +5 -4
- package/src/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue +2 -1
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +2 -1
- package/src/components/CnBoardView/CnBoardView.vue +189 -24
- package/src/components/CnCalendarEventPicker/CnCalendarEventPicker.vue +2 -1
- package/src/components/CnCalendarWidget/CnCalendarWidget.vue +2 -1
- package/src/components/CnChartWidget/CnChartWidget.vue +6 -5
- package/src/components/CnCollectivePagePicker/CnCollectivePagePicker.vue +2 -1
- package/src/components/CnConversationThread/CnConversationThread.vue +2 -1
- package/src/components/CnCountdownWidget/CnCountdownWidget.vue +2 -1
- package/src/components/CnDashboardPage/CnDashboardPage.vue +2 -1
- package/src/components/CnDataMatrix/CnDataMatrix.vue +2 -1
- package/src/components/CnDetailPage/CnDetailHeaderChips.vue +2 -1
- package/src/components/CnEmailPicker/CnEmailPicker.vue +2 -1
- package/src/components/CnFederationStatus/CnFederationStatus.vue +3 -1
- package/src/components/CnFileManager/CnFileManager.vue +2 -1
- package/src/components/CnFilePreview/CnFilePreview.vue +2 -1
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +54 -6
- package/src/components/CnFollowToggle/CnFollowToggle.vue +2 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +30 -4
- package/src/components/CnNotesCard/CnNotesCard.vue +2 -1
- package/src/components/CnObjectCalendar/CnObjectCalendar.vue +3 -2
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +36 -0
- package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +5 -4
- package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +2 -1
- package/src/components/CnObjectSidebar/CnFlowTasksPanel.vue +2 -1
- package/src/components/CnObjectSidebar/CnNotesTab.vue +2 -1
- package/src/components/CnObjectSidebar/CnTasksTab.vue +2 -1
- package/src/components/CnPropertySourceField/CnPropertySourceField.vue +2 -1
- package/src/components/CnRoadmapItem/CnRoadmapItem.vue +2 -1
- package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +4 -2
- package/src/components/CnStatsBlock/CnStatsBlock.vue +2 -1
- package/src/components/CnSummaryAggregates/CnSummaryAggregates.vue +2 -1
- package/src/components/CnTabsWidget/CnTabsWidget.vue +71 -0
- package/src/components/CnTasksCard/CnTasksCard.vue +2 -1
- package/src/components/CnTimelineWidget/CnTimelineWidget.vue +2 -1
- package/src/components/CnTranslatedBadge/CnTranslatedBadge.vue +2 -1
- package/src/components/CnVersionHistory/CnVersionHistory.vue +2 -1
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +3 -26
- package/src/css/board.css +40 -0
- package/src/dialogs/CnNoteHistoryDialog.vue +2 -1
- package/src/integrations/builtin/activity/CnActivityCard.vue +3 -2
- package/src/integrations/builtin/activity/CnActivityTab.vue +4 -3
- package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +3 -2
- package/src/integrations/builtin/calendar/CnCalendarCard.vue +2 -1
- package/src/integrations/builtin/calendar/CnCalendarTab.vue +6 -5
- package/src/integrations/builtin/contactmoment/CnContactmomentCard.vue +2 -1
- package/src/integrations/builtin/cospend/CnCospendTab.vue +2 -1
- package/src/integrations/builtin/deck/CnDeckTab.vue +2 -1
- package/src/integrations/builtin/email/CnEmailCard.vue +2 -1
- package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +2 -1
- package/src/integrations/builtin/field-inspection/CnFieldInspectionTab.vue +2 -1
- package/src/integrations/builtin/photos/CnPhotosTab.vue +2 -1
- package/src/schemas/app-manifest-v2.schema.json +4 -1
- package/src/store/useObjectStore.js +7 -2
- package/src/utils/boardColumns.js +49 -0
- package/src/utils/boardReferenceColumns.js +114 -3
- package/src/utils/contentHeaderLink.js +47 -0
- package/src/utils/schema.js +5 -4
- package/src/utils/userLocale.js +26 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -2,7 +2,7 @@ import script from './CnFilePreview.vue2.js';
|
|
|
2
2
|
import { render } from './CnFilePreview.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-a604b852";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -3,6 +3,7 @@ import { generateUrl } from '@nextcloud/router';
|
|
|
3
3
|
import { NcButton, NcDialog, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue';
|
|
4
4
|
import { previewKindOf, fileContentUrl } from '../../composables/useFileOpener.js';
|
|
5
5
|
import { parseDelimited } from '../../utils/parseDelimited.js';
|
|
6
|
+
import { userLocale } from '../../utils/userLocale.js';
|
|
6
7
|
import { prefixUrl, buildHeaders } from '../../utils/headers.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -135,7 +136,7 @@ var script = {
|
|
|
135
136
|
if (estimate > shown) {
|
|
136
137
|
return translate('nextcloud-vue', 'Showing the first {shown} of about {total} rows', {
|
|
137
138
|
shown,
|
|
138
|
-
total: estimate.toLocaleString(),
|
|
139
|
+
total: estimate.toLocaleString(userLocale()),
|
|
139
140
|
})
|
|
140
141
|
}
|
|
141
142
|
return translate('nextcloud-vue', 'Showing the first {shown} rows', { shown })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFilePreview.vue2.js","sources":["../../../../src/components/CnFilePreview/CnFilePreview.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<NcDialog\n\t\tv-if=\"dialog\"\n\t\t:name=\"fileName\"\n\t\tsize=\"large\"\n\t\t@closing=\"$emit('close')\">\n\t\t<div class=\"cn-file-preview\" data-testid=\"cn-file-preview\">\n\t\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t\t<NcNoteCard v-else-if=\"failed\" type=\"error\">\n\t\t\t\t{{ failedLabel }}\n\t\t\t</NcNoteCard>\n\t\t\t<template v-else>\n\t\t\t\t<NcNoteCard v-if=\"unreadable\" type=\"warning\" data-testid=\"cn-file-preview-unreadable\">\n\t\t\t\t\t{{ unreadableLabel }}\n\t\t\t\t</NcNoteCard>\n\t\t\t\t<template v-if=\"mode === 'table'\">\n\t\t\t\t\t<div class=\"cn-file-preview__scroll\">\n\t\t\t\t\t\t<table class=\"cn-file-preview__table\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th v-for=\"(cell, c) in header\" :key=\"c\" scope=\"col\">\n\t\t\t\t\t\t\t\t\t\t{{ cell }}\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t<tr v-for=\"(row, r) in body\" :key=\"r\">\n\t\t\t\t\t\t\t\t\t<td v-for=\"(cell, c) in row\" :key=\"c\">\n\t\t\t\t\t\t\t\t\t\t{{ cell }}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</div>\n\t\t\t\t\t<p class=\"cn-file-preview__count\" data-testid=\"cn-file-preview-count\">\n\t\t\t\t\t\t{{ countLabel }}\n\t\t\t\t\t</p>\n\t\t\t\t</template>\n\t\t\t\t<pre v-else class=\"cn-file-preview__text\" data-testid=\"cn-file-preview-text\">{{ text }}</pre>\n\t\t\t\t<p v-if=\"truncatedText && mode !== 'table'\" class=\"cn-file-preview__count\">\n\t\t\t\t\t{{ truncatedLabel }}\n\t\t\t\t</p>\n\t\t\t</template>\n\t\t</div>\n\t\t<template #actions>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"filesUrl\"\n\t\t\t\t:href=\"filesUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t{{ openInFilesLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"contentUrl\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"contentUrl\"\n\t\t\t\t:download=\"fileName\">\n\t\t\t\t{{ downloadLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</NcDialog>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcDialog, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { fileContentUrl, previewKindOf } from '../../composables/useFileOpener.js'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { parseDelimited } from '../../utils/parseDelimited.js'\n\n/**\n * CnFilePreview — an in-page preview of a data file the Nextcloud Viewer does\n * not show: the first rows of a CSV or TSV as a table, JSON and XML as\n * formatted read-only text, plain text as is. It fetches at most the first\n * megabyte, offers Download and Open in Files, and renders every cell as text,\n * never as HTML. A file that cannot be read as a table shows its raw text with\n * a sentence saying so.\n *\n * ```vue\n * <CnFilePreview :file=\"{ name: 'data.csv', accessUrl: '/s/abc/download', id: 12 }\" @close=\"file = null\" />\n * ```\n */\nexport default {\n\tname: 'CnFilePreview',\n\n\tcomponents: { NcButton, NcDialog, NcLoadingIcon, NcNoteCard },\n\n\tprops: {\n\t\t/**\n\t\t * The file row: `name`, a content URL (`accessUrl`, `downloadUrl` or\n\t\t * `url`), optionally `type` (mime), `size` and `id` (for Open in Files).\n\t\t *\n\t\t * @type {{name?: string, title?: string, type?: string, mimetype?: string, size?: number, id?: number|string, accessUrl?: string, downloadUrl?: string, url?: string}}\n\t\t */\n\t\tfile: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Rows shown for a CSV or TSV, the header excluded. */\n\t\tmaxRows: {\n\t\t\ttype: Number,\n\t\t\tdefault: 100,\n\t\t},\n\n\t\t/** Bytes fetched at most. */\n\t\tmaxBytes: {\n\t\t\ttype: Number,\n\t\t\tdefault: 1048576,\n\t\t},\n\n\t\t/** Wrap the preview in a dialog. Pass `false` to embed it. */\n\t\tdialog: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when the dialog is closed.\n\t\t *\n\t\t * @event close\n\t\t */\n\t\t'close',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\tfailed: false,\n\t\t\tunreadable: false,\n\t\t\ttext: '',\n\t\t\theader: [],\n\t\t\tbody: [],\n\t\t\tmode: 'text',\n\t\t\tbytesRead: 0,\n\t\t\ttotalBytes: 0,\n\t\t\tsampleTruncated: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tfileName() {\n\t\t\treturn this.file?.name || this.file?.title || ''\n\t\t},\n\n\t\tkind() {\n\t\t\treturn previewKindOf(this.file) || 'text'\n\t\t},\n\n\t\tcontentUrl() {\n\t\t\treturn fileContentUrl(this.file)\n\t\t},\n\n\t\tfilesUrl() {\n\t\t\tconst id = this.file?.id ?? this.file?.fileId\n\t\t\treturn id ? generateUrl('/f/{fileid}', { fileid: id }) : ''\n\t\t},\n\n\t\ttruncatedText() {\n\t\t\treturn this.sampleTruncated\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\n\t\tfailedLabel() {\n\t\t\treturn t('nextcloud-vue', 'The preview could not be loaded.')\n\t\t},\n\n\t\tunreadableLabel() {\n\t\t\treturn t('nextcloud-vue', 'This file could not be read as a table, so the raw text is shown.')\n\t\t},\n\n\t\tdownloadLabel() {\n\t\t\treturn t('nextcloud-vue', 'Download')\n\t\t},\n\n\t\topenInFilesLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open in Files')\n\t\t},\n\n\t\ttruncatedLabel() {\n\t\t\treturn t('nextcloud-vue', 'Only the first part of the file is shown.')\n\t\t},\n\n\t\t/** \"Showing the first 100 of about 40,000 rows\" or \"Showing all 12 rows\". */\n\t\tcountLabel() {\n\t\t\tconst shown = this.body.length\n\t\t\tif (!this.sampleTruncated) {\n\t\t\t\treturn t('nextcloud-vue', 'Showing all {count} rows', { count: shown })\n\t\t\t}\n\t\t\tconst size = this.totalBytes || Number(this.file?.size) || 0\n\t\t\tconst perRow = this.bytesRead > 0 && shown > 0 ? this.bytesRead / (shown + 1) : 0\n\t\t\tconst estimate = perRow > 0 && size > 0 ? Math.max(shown, Math.round(size / perRow) - 1) : 0\n\t\t\tif (estimate > shown) {\n\t\t\t\treturn t('nextcloud-vue', 'Showing the first {shown} of about {total} rows', {\n\t\t\t\t\tshown,\n\t\t\t\t\ttotal: estimate.toLocaleString(),\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Showing the first {shown} rows', { shown })\n\t\t},\n\t},\n\n\twatch: {\n\t\tfile: 'load',\n\t},\n\n\tcreated() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\t/** Fetch the first megabyte and shape it for display. */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\tthis.unreadable = false\n\t\t\tconst url = this.contentUrl\n\t\t\tif (!url) {\n\t\t\t\tthis.failed = true\n\t\t\t\tthis.loading = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(url), {\n\t\t\t\t\theaders: { ...buildHeaders(null), Range: `bytes=0-${this.maxBytes - 1}` },\n\t\t\t\t\tcredentials: 'same-origin',\n\t\t\t\t})\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`HTTP ${response.status}`)\n\t\t\t\t}\n\t\t\t\tconst full = await response.text()\n\t\t\t\tconst cut = full.length > this.maxBytes\n\t\t\t\tconst raw = cut ? full.slice(0, this.maxBytes) : full\n\t\t\t\tthis.bytesRead = raw.length\n\t\t\t\tconst range = response.headers?.get?.('Content-Range') || ''\n\t\t\t\tconst total = Number(range.split('/')[1])\n\t\t\t\tthis.totalBytes = Number.isFinite(total) && total > 0 ? total : 0\n\t\t\t\tconst size = this.totalBytes || Number(this.file?.size) || 0\n\t\t\t\tthis.sampleTruncated = cut || size > raw.length\n\t\t\t\tthis.shape(raw)\n\t\t\t} catch {\n\t\t\t\tthis.failed = true\n\t\t\t}\n\t\t\tthis.loading = false\n\t\t},\n\n\t\t/**\n\t\t * Turn the fetched text into a table or formatted text.\n\t\t *\n\t\t * @param {string} raw The fetched text.\n\t\t */\n\t\tshape(raw) {\n\t\t\tif (this.kind === 'csv' || this.kind === 'tsv') {\n\t\t\t\tconst parsed = parseDelimited(raw, this.kind === 'tsv' ? '\\t' : ',', this.maxRows + 1)\n\t\t\t\tif (!parsed.balanced || parsed.rows.length === 0) {\n\t\t\t\t\tthis.mode = 'text'\n\t\t\t\t\tthis.text = raw\n\t\t\t\t\tthis.unreadable = true\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.mode = 'table'\n\t\t\t\tthis.header = parsed.rows[0]\n\t\t\t\tthis.body = parsed.rows.slice(1, this.maxRows + 1)\n\t\t\t\tthis.sampleTruncated = this.sampleTruncated || parsed.truncated\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.mode = 'text'\n\t\t\tthis.text = this.kind === 'json' ? this.formatJson(raw) : raw\n\t\t},\n\n\t\t/**\n\t\t * Pretty-print JSON; text that does not parse is returned as is.\n\t\t *\n\t\t * @param {string} raw The JSON text.\n\t\t * @return {string} The formatted text.\n\t\t */\n\t\tformatJson(raw) {\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(JSON.parse(raw), null, 2)\n\t\t\t} catch {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-file-preview {\n\tmin-height: 120px;\n}\n\n.cn-file-preview__scroll {\n\tmax-height: 60vh;\n\toverflow: auto;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-file-preview__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tfont-size: 13px;\n}\n\n.cn-file-preview__table th,\n.cn-file-preview__table td {\n\tpadding: 6px 10px;\n\ttext-align: start;\n\tborder-bottom: 1px solid var(--color-border);\n\twhite-space: nowrap;\n}\n\n.cn-file-preview__table th {\n\tposition: sticky;\n\ttop: 0;\n\tbackground: var(--color-background-dark);\n\tfont-weight: 600;\n}\n\n.cn-file-preview__count {\n\tmargin: 8px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-file-preview__text {\n\tmax-height: 60vh;\n\toverflow: auto;\n\tmargin: 0;\n\tpadding: 12px;\n\tbackground: var(--color-background-dark);\n\tborder-radius: var(--border-radius-large, 8px);\n\tfont-size: 12px;\n\twhite-space: pre-wrap;\n\toverflow-wrap: anywhere;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFilePreview.vue2.js","sources":["../../../../src/components/CnFilePreview/CnFilePreview.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<NcDialog\n\t\tv-if=\"dialog\"\n\t\t:name=\"fileName\"\n\t\tsize=\"large\"\n\t\t@closing=\"$emit('close')\">\n\t\t<div class=\"cn-file-preview\" data-testid=\"cn-file-preview\">\n\t\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t\t<NcNoteCard v-else-if=\"failed\" type=\"error\">\n\t\t\t\t{{ failedLabel }}\n\t\t\t</NcNoteCard>\n\t\t\t<template v-else>\n\t\t\t\t<NcNoteCard v-if=\"unreadable\" type=\"warning\" data-testid=\"cn-file-preview-unreadable\">\n\t\t\t\t\t{{ unreadableLabel }}\n\t\t\t\t</NcNoteCard>\n\t\t\t\t<template v-if=\"mode === 'table'\">\n\t\t\t\t\t<div class=\"cn-file-preview__scroll\">\n\t\t\t\t\t\t<table class=\"cn-file-preview__table\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th v-for=\"(cell, c) in header\" :key=\"c\" scope=\"col\">\n\t\t\t\t\t\t\t\t\t\t{{ cell }}\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t<tr v-for=\"(row, r) in body\" :key=\"r\">\n\t\t\t\t\t\t\t\t\t<td v-for=\"(cell, c) in row\" :key=\"c\">\n\t\t\t\t\t\t\t\t\t\t{{ cell }}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</div>\n\t\t\t\t\t<p class=\"cn-file-preview__count\" data-testid=\"cn-file-preview-count\">\n\t\t\t\t\t\t{{ countLabel }}\n\t\t\t\t\t</p>\n\t\t\t\t</template>\n\t\t\t\t<pre v-else class=\"cn-file-preview__text\" data-testid=\"cn-file-preview-text\">{{ text }}</pre>\n\t\t\t\t<p v-if=\"truncatedText && mode !== 'table'\" class=\"cn-file-preview__count\">\n\t\t\t\t\t{{ truncatedLabel }}\n\t\t\t\t</p>\n\t\t\t</template>\n\t\t</div>\n\t\t<template #actions>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"filesUrl\"\n\t\t\t\t:href=\"filesUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t{{ openInFilesLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"contentUrl\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"contentUrl\"\n\t\t\t\t:download=\"fileName\">\n\t\t\t\t{{ downloadLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</NcDialog>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcDialog, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { fileContentUrl, previewKindOf } from '../../composables/useFileOpener.js'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { parseDelimited } from '../../utils/parseDelimited.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\n/**\n * CnFilePreview — an in-page preview of a data file the Nextcloud Viewer does\n * not show: the first rows of a CSV or TSV as a table, JSON and XML as\n * formatted read-only text, plain text as is. It fetches at most the first\n * megabyte, offers Download and Open in Files, and renders every cell as text,\n * never as HTML. A file that cannot be read as a table shows its raw text with\n * a sentence saying so.\n *\n * ```vue\n * <CnFilePreview :file=\"{ name: 'data.csv', accessUrl: '/s/abc/download', id: 12 }\" @close=\"file = null\" />\n * ```\n */\nexport default {\n\tname: 'CnFilePreview',\n\n\tcomponents: { NcButton, NcDialog, NcLoadingIcon, NcNoteCard },\n\n\tprops: {\n\t\t/**\n\t\t * The file row: `name`, a content URL (`accessUrl`, `downloadUrl` or\n\t\t * `url`), optionally `type` (mime), `size` and `id` (for Open in Files).\n\t\t *\n\t\t * @type {{name?: string, title?: string, type?: string, mimetype?: string, size?: number, id?: number|string, accessUrl?: string, downloadUrl?: string, url?: string}}\n\t\t */\n\t\tfile: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Rows shown for a CSV or TSV, the header excluded. */\n\t\tmaxRows: {\n\t\t\ttype: Number,\n\t\t\tdefault: 100,\n\t\t},\n\n\t\t/** Bytes fetched at most. */\n\t\tmaxBytes: {\n\t\t\ttype: Number,\n\t\t\tdefault: 1048576,\n\t\t},\n\n\t\t/** Wrap the preview in a dialog. Pass `false` to embed it. */\n\t\tdialog: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when the dialog is closed.\n\t\t *\n\t\t * @event close\n\t\t */\n\t\t'close',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\tfailed: false,\n\t\t\tunreadable: false,\n\t\t\ttext: '',\n\t\t\theader: [],\n\t\t\tbody: [],\n\t\t\tmode: 'text',\n\t\t\tbytesRead: 0,\n\t\t\ttotalBytes: 0,\n\t\t\tsampleTruncated: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tfileName() {\n\t\t\treturn this.file?.name || this.file?.title || ''\n\t\t},\n\n\t\tkind() {\n\t\t\treturn previewKindOf(this.file) || 'text'\n\t\t},\n\n\t\tcontentUrl() {\n\t\t\treturn fileContentUrl(this.file)\n\t\t},\n\n\t\tfilesUrl() {\n\t\t\tconst id = this.file?.id ?? this.file?.fileId\n\t\t\treturn id ? generateUrl('/f/{fileid}', { fileid: id }) : ''\n\t\t},\n\n\t\ttruncatedText() {\n\t\t\treturn this.sampleTruncated\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\n\t\tfailedLabel() {\n\t\t\treturn t('nextcloud-vue', 'The preview could not be loaded.')\n\t\t},\n\n\t\tunreadableLabel() {\n\t\t\treturn t('nextcloud-vue', 'This file could not be read as a table, so the raw text is shown.')\n\t\t},\n\n\t\tdownloadLabel() {\n\t\t\treturn t('nextcloud-vue', 'Download')\n\t\t},\n\n\t\topenInFilesLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open in Files')\n\t\t},\n\n\t\ttruncatedLabel() {\n\t\t\treturn t('nextcloud-vue', 'Only the first part of the file is shown.')\n\t\t},\n\n\t\t/** \"Showing the first 100 of about 40,000 rows\" or \"Showing all 12 rows\". */\n\t\tcountLabel() {\n\t\t\tconst shown = this.body.length\n\t\t\tif (!this.sampleTruncated) {\n\t\t\t\treturn t('nextcloud-vue', 'Showing all {count} rows', { count: shown })\n\t\t\t}\n\t\t\tconst size = this.totalBytes || Number(this.file?.size) || 0\n\t\t\tconst perRow = this.bytesRead > 0 && shown > 0 ? this.bytesRead / (shown + 1) : 0\n\t\t\tconst estimate = perRow > 0 && size > 0 ? Math.max(shown, Math.round(size / perRow) - 1) : 0\n\t\t\tif (estimate > shown) {\n\t\t\t\treturn t('nextcloud-vue', 'Showing the first {shown} of about {total} rows', {\n\t\t\t\t\tshown,\n\t\t\t\t\ttotal: estimate.toLocaleString(userLocale()),\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Showing the first {shown} rows', { shown })\n\t\t},\n\t},\n\n\twatch: {\n\t\tfile: 'load',\n\t},\n\n\tcreated() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\t/** Fetch the first megabyte and shape it for display. */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\tthis.unreadable = false\n\t\t\tconst url = this.contentUrl\n\t\t\tif (!url) {\n\t\t\t\tthis.failed = true\n\t\t\t\tthis.loading = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(url), {\n\t\t\t\t\theaders: { ...buildHeaders(null), Range: `bytes=0-${this.maxBytes - 1}` },\n\t\t\t\t\tcredentials: 'same-origin',\n\t\t\t\t})\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`HTTP ${response.status}`)\n\t\t\t\t}\n\t\t\t\tconst full = await response.text()\n\t\t\t\tconst cut = full.length > this.maxBytes\n\t\t\t\tconst raw = cut ? full.slice(0, this.maxBytes) : full\n\t\t\t\tthis.bytesRead = raw.length\n\t\t\t\tconst range = response.headers?.get?.('Content-Range') || ''\n\t\t\t\tconst total = Number(range.split('/')[1])\n\t\t\t\tthis.totalBytes = Number.isFinite(total) && total > 0 ? total : 0\n\t\t\t\tconst size = this.totalBytes || Number(this.file?.size) || 0\n\t\t\t\tthis.sampleTruncated = cut || size > raw.length\n\t\t\t\tthis.shape(raw)\n\t\t\t} catch {\n\t\t\t\tthis.failed = true\n\t\t\t}\n\t\t\tthis.loading = false\n\t\t},\n\n\t\t/**\n\t\t * Turn the fetched text into a table or formatted text.\n\t\t *\n\t\t * @param {string} raw The fetched text.\n\t\t */\n\t\tshape(raw) {\n\t\t\tif (this.kind === 'csv' || this.kind === 'tsv') {\n\t\t\t\tconst parsed = parseDelimited(raw, this.kind === 'tsv' ? '\\t' : ',', this.maxRows + 1)\n\t\t\t\tif (!parsed.balanced || parsed.rows.length === 0) {\n\t\t\t\t\tthis.mode = 'text'\n\t\t\t\t\tthis.text = raw\n\t\t\t\t\tthis.unreadable = true\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.mode = 'table'\n\t\t\t\tthis.header = parsed.rows[0]\n\t\t\t\tthis.body = parsed.rows.slice(1, this.maxRows + 1)\n\t\t\t\tthis.sampleTruncated = this.sampleTruncated || parsed.truncated\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.mode = 'text'\n\t\t\tthis.text = this.kind === 'json' ? this.formatJson(raw) : raw\n\t\t},\n\n\t\t/**\n\t\t * Pretty-print JSON; text that does not parse is returned as is.\n\t\t *\n\t\t * @param {string} raw The JSON text.\n\t\t * @return {string} The formatted text.\n\t\t */\n\t\tformatJson(raw) {\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(JSON.parse(raw), null, 2)\n\t\t\t} catch {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-file-preview {\n\tmin-height: 120px;\n}\n\n.cn-file-preview__scroll {\n\tmax-height: 60vh;\n\toverflow: auto;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-file-preview__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tfont-size: 13px;\n}\n\n.cn-file-preview__table th,\n.cn-file-preview__table td {\n\tpadding: 6px 10px;\n\ttext-align: start;\n\tborder-bottom: 1px solid var(--color-border);\n\twhite-space: nowrap;\n}\n\n.cn-file-preview__table th {\n\tposition: sticky;\n\ttop: 0;\n\tbackground: var(--color-background-dark);\n\tfont-weight: 600;\n}\n\n.cn-file-preview__count {\n\tmargin: 8px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-file-preview__text {\n\tmax-height: 60vh;\n\toverflow: auto;\n\tmargin: 0;\n\tpadding: 12px;\n\tbackground: var(--color-background-dark);\n\tborder-radius: var(--border-radius-large, 8px);\n\tfont-size: 12px;\n\twhite-space: pre-wrap;\n\toverflow-wrap: anywhere;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFilePreview.vue3.js","sources":["../../../../src/components/CnFilePreview/CnFilePreview.vue?vue&type=template&id=be44a5f6&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<NcDialog\n\t\tv-if=\"dialog\"\n\t\t:name=\"fileName\"\n\t\tsize=\"large\"\n\t\t@closing=\"$emit('close')\">\n\t\t<div class=\"cn-file-preview\" data-testid=\"cn-file-preview\">\n\t\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t\t<NcNoteCard v-else-if=\"failed\" type=\"error\">\n\t\t\t\t{{ failedLabel }}\n\t\t\t</NcNoteCard>\n\t\t\t<template v-else>\n\t\t\t\t<NcNoteCard v-if=\"unreadable\" type=\"warning\" data-testid=\"cn-file-preview-unreadable\">\n\t\t\t\t\t{{ unreadableLabel }}\n\t\t\t\t</NcNoteCard>\n\t\t\t\t<template v-if=\"mode === 'table'\">\n\t\t\t\t\t<div class=\"cn-file-preview__scroll\">\n\t\t\t\t\t\t<table class=\"cn-file-preview__table\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th v-for=\"(cell, c) in header\" :key=\"c\" scope=\"col\">\n\t\t\t\t\t\t\t\t\t\t{{ cell }}\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t<tr v-for=\"(row, r) in body\" :key=\"r\">\n\t\t\t\t\t\t\t\t\t<td v-for=\"(cell, c) in row\" :key=\"c\">\n\t\t\t\t\t\t\t\t\t\t{{ cell }}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</div>\n\t\t\t\t\t<p class=\"cn-file-preview__count\" data-testid=\"cn-file-preview-count\">\n\t\t\t\t\t\t{{ countLabel }}\n\t\t\t\t\t</p>\n\t\t\t\t</template>\n\t\t\t\t<pre v-else class=\"cn-file-preview__text\" data-testid=\"cn-file-preview-text\">{{ text }}</pre>\n\t\t\t\t<p v-if=\"truncatedText && mode !== 'table'\" class=\"cn-file-preview__count\">\n\t\t\t\t\t{{ truncatedLabel }}\n\t\t\t\t</p>\n\t\t\t</template>\n\t\t</div>\n\t\t<template #actions>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"filesUrl\"\n\t\t\t\t:href=\"filesUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t{{ openInFilesLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"contentUrl\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"contentUrl\"\n\t\t\t\t:download=\"fileName\">\n\t\t\t\t{{ downloadLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</NcDialog>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcDialog, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { fileContentUrl, previewKindOf } from '../../composables/useFileOpener.js'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { parseDelimited } from '../../utils/parseDelimited.js'\n\n/**\n * CnFilePreview — an in-page preview of a data file the Nextcloud Viewer does\n * not show: the first rows of a CSV or TSV as a table, JSON and XML as\n * formatted read-only text, plain text as is. It fetches at most the first\n * megabyte, offers Download and Open in Files, and renders every cell as text,\n * never as HTML. A file that cannot be read as a table shows its raw text with\n * a sentence saying so.\n *\n * ```vue\n * <CnFilePreview :file=\"{ name: 'data.csv', accessUrl: '/s/abc/download', id: 12 }\" @close=\"file = null\" />\n * ```\n */\nexport default {\n\tname: 'CnFilePreview',\n\n\tcomponents: { NcButton, NcDialog, NcLoadingIcon, NcNoteCard },\n\n\tprops: {\n\t\t/**\n\t\t * The file row: `name`, a content URL (`accessUrl`, `downloadUrl` or\n\t\t * `url`), optionally `type` (mime), `size` and `id` (for Open in Files).\n\t\t *\n\t\t * @type {{name?: string, title?: string, type?: string, mimetype?: string, size?: number, id?: number|string, accessUrl?: string, downloadUrl?: string, url?: string}}\n\t\t */\n\t\tfile: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Rows shown for a CSV or TSV, the header excluded. */\n\t\tmaxRows: {\n\t\t\ttype: Number,\n\t\t\tdefault: 100,\n\t\t},\n\n\t\t/** Bytes fetched at most. */\n\t\tmaxBytes: {\n\t\t\ttype: Number,\n\t\t\tdefault: 1048576,\n\t\t},\n\n\t\t/** Wrap the preview in a dialog. Pass `false` to embed it. */\n\t\tdialog: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when the dialog is closed.\n\t\t *\n\t\t * @event close\n\t\t */\n\t\t'close',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\tfailed: false,\n\t\t\tunreadable: false,\n\t\t\ttext: '',\n\t\t\theader: [],\n\t\t\tbody: [],\n\t\t\tmode: 'text',\n\t\t\tbytesRead: 0,\n\t\t\ttotalBytes: 0,\n\t\t\tsampleTruncated: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tfileName() {\n\t\t\treturn this.file?.name || this.file?.title || ''\n\t\t},\n\n\t\tkind() {\n\t\t\treturn previewKindOf(this.file) || 'text'\n\t\t},\n\n\t\tcontentUrl() {\n\t\t\treturn fileContentUrl(this.file)\n\t\t},\n\n\t\tfilesUrl() {\n\t\t\tconst id = this.file?.id ?? this.file?.fileId\n\t\t\treturn id ? generateUrl('/f/{fileid}', { fileid: id }) : ''\n\t\t},\n\n\t\ttruncatedText() {\n\t\t\treturn this.sampleTruncated\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\n\t\tfailedLabel() {\n\t\t\treturn t('nextcloud-vue', 'The preview could not be loaded.')\n\t\t},\n\n\t\tunreadableLabel() {\n\t\t\treturn t('nextcloud-vue', 'This file could not be read as a table, so the raw text is shown.')\n\t\t},\n\n\t\tdownloadLabel() {\n\t\t\treturn t('nextcloud-vue', 'Download')\n\t\t},\n\n\t\topenInFilesLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open in Files')\n\t\t},\n\n\t\ttruncatedLabel() {\n\t\t\treturn t('nextcloud-vue', 'Only the first part of the file is shown.')\n\t\t},\n\n\t\t/** \"Showing the first 100 of about 40,000 rows\" or \"Showing all 12 rows\". */\n\t\tcountLabel() {\n\t\t\tconst shown = this.body.length\n\t\t\tif (!this.sampleTruncated) {\n\t\t\t\treturn t('nextcloud-vue', 'Showing all {count} rows', { count: shown })\n\t\t\t}\n\t\t\tconst size = this.totalBytes || Number(this.file?.size) || 0\n\t\t\tconst perRow = this.bytesRead > 0 && shown > 0 ? this.bytesRead / (shown + 1) : 0\n\t\t\tconst estimate = perRow > 0 && size > 0 ? Math.max(shown, Math.round(size / perRow) - 1) : 0\n\t\t\tif (estimate > shown) {\n\t\t\t\treturn t('nextcloud-vue', 'Showing the first {shown} of about {total} rows', {\n\t\t\t\t\tshown,\n\t\t\t\t\ttotal: estimate.toLocaleString(),\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Showing the first {shown} rows', { shown })\n\t\t},\n\t},\n\n\twatch: {\n\t\tfile: 'load',\n\t},\n\n\tcreated() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\t/** Fetch the first megabyte and shape it for display. */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\tthis.unreadable = false\n\t\t\tconst url = this.contentUrl\n\t\t\tif (!url) {\n\t\t\t\tthis.failed = true\n\t\t\t\tthis.loading = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(url), {\n\t\t\t\t\theaders: { ...buildHeaders(null), Range: `bytes=0-${this.maxBytes - 1}` },\n\t\t\t\t\tcredentials: 'same-origin',\n\t\t\t\t})\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`HTTP ${response.status}`)\n\t\t\t\t}\n\t\t\t\tconst full = await response.text()\n\t\t\t\tconst cut = full.length > this.maxBytes\n\t\t\t\tconst raw = cut ? full.slice(0, this.maxBytes) : full\n\t\t\t\tthis.bytesRead = raw.length\n\t\t\t\tconst range = response.headers?.get?.('Content-Range') || ''\n\t\t\t\tconst total = Number(range.split('/')[1])\n\t\t\t\tthis.totalBytes = Number.isFinite(total) && total > 0 ? total : 0\n\t\t\t\tconst size = this.totalBytes || Number(this.file?.size) || 0\n\t\t\t\tthis.sampleTruncated = cut || size > raw.length\n\t\t\t\tthis.shape(raw)\n\t\t\t} catch {\n\t\t\t\tthis.failed = true\n\t\t\t}\n\t\t\tthis.loading = false\n\t\t},\n\n\t\t/**\n\t\t * Turn the fetched text into a table or formatted text.\n\t\t *\n\t\t * @param {string} raw The fetched text.\n\t\t */\n\t\tshape(raw) {\n\t\t\tif (this.kind === 'csv' || this.kind === 'tsv') {\n\t\t\t\tconst parsed = parseDelimited(raw, this.kind === 'tsv' ? '\\t' : ',', this.maxRows + 1)\n\t\t\t\tif (!parsed.balanced || parsed.rows.length === 0) {\n\t\t\t\t\tthis.mode = 'text'\n\t\t\t\t\tthis.text = raw\n\t\t\t\t\tthis.unreadable = true\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.mode = 'table'\n\t\t\t\tthis.header = parsed.rows[0]\n\t\t\t\tthis.body = parsed.rows.slice(1, this.maxRows + 1)\n\t\t\t\tthis.sampleTruncated = this.sampleTruncated || parsed.truncated\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.mode = 'text'\n\t\t\tthis.text = this.kind === 'json' ? this.formatJson(raw) : raw\n\t\t},\n\n\t\t/**\n\t\t * Pretty-print JSON; text that does not parse is returned as is.\n\t\t *\n\t\t * @param {string} raw The JSON text.\n\t\t * @return {string} The formatted text.\n\t\t */\n\t\tformatJson(raw) {\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(JSON.parse(raw), null, 2)\n\t\t\t} catch {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-file-preview {\n\tmin-height: 120px;\n}\n\n.cn-file-preview__scroll {\n\tmax-height: 60vh;\n\toverflow: auto;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-file-preview__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tfont-size: 13px;\n}\n\n.cn-file-preview__table th,\n.cn-file-preview__table td {\n\tpadding: 6px 10px;\n\ttext-align: start;\n\tborder-bottom: 1px solid var(--color-border);\n\twhite-space: nowrap;\n}\n\n.cn-file-preview__table th {\n\tposition: sticky;\n\ttop: 0;\n\tbackground: var(--color-background-dark);\n\tfont-weight: 600;\n}\n\n.cn-file-preview__count {\n\tmargin: 8px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-file-preview__text {\n\tmax-height: 60vh;\n\toverflow: auto;\n\tmargin: 0;\n\tpadding: 12px;\n\tbackground: var(--color-background-dark);\n\tborder-radius: var(--border-radius-large, 8px);\n\tfont-size: 12px;\n\twhite-space: pre-wrap;\n\toverflow-wrap: anywhere;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_Fragment","_createElementVNode","_openBlock","_renderList","_toDisplayString"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFilePreview.vue3.js","sources":["../../../../src/components/CnFilePreview/CnFilePreview.vue?vue&type=template&id=a604b852&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<NcDialog\n\t\tv-if=\"dialog\"\n\t\t:name=\"fileName\"\n\t\tsize=\"large\"\n\t\t@closing=\"$emit('close')\">\n\t\t<div class=\"cn-file-preview\" data-testid=\"cn-file-preview\">\n\t\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t\t<NcNoteCard v-else-if=\"failed\" type=\"error\">\n\t\t\t\t{{ failedLabel }}\n\t\t\t</NcNoteCard>\n\t\t\t<template v-else>\n\t\t\t\t<NcNoteCard v-if=\"unreadable\" type=\"warning\" data-testid=\"cn-file-preview-unreadable\">\n\t\t\t\t\t{{ unreadableLabel }}\n\t\t\t\t</NcNoteCard>\n\t\t\t\t<template v-if=\"mode === 'table'\">\n\t\t\t\t\t<div class=\"cn-file-preview__scroll\">\n\t\t\t\t\t\t<table class=\"cn-file-preview__table\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th v-for=\"(cell, c) in header\" :key=\"c\" scope=\"col\">\n\t\t\t\t\t\t\t\t\t\t{{ cell }}\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t<tr v-for=\"(row, r) in body\" :key=\"r\">\n\t\t\t\t\t\t\t\t\t<td v-for=\"(cell, c) in row\" :key=\"c\">\n\t\t\t\t\t\t\t\t\t\t{{ cell }}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</div>\n\t\t\t\t\t<p class=\"cn-file-preview__count\" data-testid=\"cn-file-preview-count\">\n\t\t\t\t\t\t{{ countLabel }}\n\t\t\t\t\t</p>\n\t\t\t\t</template>\n\t\t\t\t<pre v-else class=\"cn-file-preview__text\" data-testid=\"cn-file-preview-text\">{{ text }}</pre>\n\t\t\t\t<p v-if=\"truncatedText && mode !== 'table'\" class=\"cn-file-preview__count\">\n\t\t\t\t\t{{ truncatedLabel }}\n\t\t\t\t</p>\n\t\t\t</template>\n\t\t</div>\n\t\t<template #actions>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"filesUrl\"\n\t\t\t\t:href=\"filesUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t{{ openInFilesLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"contentUrl\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"contentUrl\"\n\t\t\t\t:download=\"fileName\">\n\t\t\t\t{{ downloadLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</NcDialog>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcDialog, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { fileContentUrl, previewKindOf } from '../../composables/useFileOpener.js'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { parseDelimited } from '../../utils/parseDelimited.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\n/**\n * CnFilePreview — an in-page preview of a data file the Nextcloud Viewer does\n * not show: the first rows of a CSV or TSV as a table, JSON and XML as\n * formatted read-only text, plain text as is. It fetches at most the first\n * megabyte, offers Download and Open in Files, and renders every cell as text,\n * never as HTML. A file that cannot be read as a table shows its raw text with\n * a sentence saying so.\n *\n * ```vue\n * <CnFilePreview :file=\"{ name: 'data.csv', accessUrl: '/s/abc/download', id: 12 }\" @close=\"file = null\" />\n * ```\n */\nexport default {\n\tname: 'CnFilePreview',\n\n\tcomponents: { NcButton, NcDialog, NcLoadingIcon, NcNoteCard },\n\n\tprops: {\n\t\t/**\n\t\t * The file row: `name`, a content URL (`accessUrl`, `downloadUrl` or\n\t\t * `url`), optionally `type` (mime), `size` and `id` (for Open in Files).\n\t\t *\n\t\t * @type {{name?: string, title?: string, type?: string, mimetype?: string, size?: number, id?: number|string, accessUrl?: string, downloadUrl?: string, url?: string}}\n\t\t */\n\t\tfile: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Rows shown for a CSV or TSV, the header excluded. */\n\t\tmaxRows: {\n\t\t\ttype: Number,\n\t\t\tdefault: 100,\n\t\t},\n\n\t\t/** Bytes fetched at most. */\n\t\tmaxBytes: {\n\t\t\ttype: Number,\n\t\t\tdefault: 1048576,\n\t\t},\n\n\t\t/** Wrap the preview in a dialog. Pass `false` to embed it. */\n\t\tdialog: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when the dialog is closed.\n\t\t *\n\t\t * @event close\n\t\t */\n\t\t'close',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\tfailed: false,\n\t\t\tunreadable: false,\n\t\t\ttext: '',\n\t\t\theader: [],\n\t\t\tbody: [],\n\t\t\tmode: 'text',\n\t\t\tbytesRead: 0,\n\t\t\ttotalBytes: 0,\n\t\t\tsampleTruncated: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tfileName() {\n\t\t\treturn this.file?.name || this.file?.title || ''\n\t\t},\n\n\t\tkind() {\n\t\t\treturn previewKindOf(this.file) || 'text'\n\t\t},\n\n\t\tcontentUrl() {\n\t\t\treturn fileContentUrl(this.file)\n\t\t},\n\n\t\tfilesUrl() {\n\t\t\tconst id = this.file?.id ?? this.file?.fileId\n\t\t\treturn id ? generateUrl('/f/{fileid}', { fileid: id }) : ''\n\t\t},\n\n\t\ttruncatedText() {\n\t\t\treturn this.sampleTruncated\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\n\t\tfailedLabel() {\n\t\t\treturn t('nextcloud-vue', 'The preview could not be loaded.')\n\t\t},\n\n\t\tunreadableLabel() {\n\t\t\treturn t('nextcloud-vue', 'This file could not be read as a table, so the raw text is shown.')\n\t\t},\n\n\t\tdownloadLabel() {\n\t\t\treturn t('nextcloud-vue', 'Download')\n\t\t},\n\n\t\topenInFilesLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open in Files')\n\t\t},\n\n\t\ttruncatedLabel() {\n\t\t\treturn t('nextcloud-vue', 'Only the first part of the file is shown.')\n\t\t},\n\n\t\t/** \"Showing the first 100 of about 40,000 rows\" or \"Showing all 12 rows\". */\n\t\tcountLabel() {\n\t\t\tconst shown = this.body.length\n\t\t\tif (!this.sampleTruncated) {\n\t\t\t\treturn t('nextcloud-vue', 'Showing all {count} rows', { count: shown })\n\t\t\t}\n\t\t\tconst size = this.totalBytes || Number(this.file?.size) || 0\n\t\t\tconst perRow = this.bytesRead > 0 && shown > 0 ? this.bytesRead / (shown + 1) : 0\n\t\t\tconst estimate = perRow > 0 && size > 0 ? Math.max(shown, Math.round(size / perRow) - 1) : 0\n\t\t\tif (estimate > shown) {\n\t\t\t\treturn t('nextcloud-vue', 'Showing the first {shown} of about {total} rows', {\n\t\t\t\t\tshown,\n\t\t\t\t\ttotal: estimate.toLocaleString(userLocale()),\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Showing the first {shown} rows', { shown })\n\t\t},\n\t},\n\n\twatch: {\n\t\tfile: 'load',\n\t},\n\n\tcreated() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\t/** Fetch the first megabyte and shape it for display. */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\tthis.unreadable = false\n\t\t\tconst url = this.contentUrl\n\t\t\tif (!url) {\n\t\t\t\tthis.failed = true\n\t\t\t\tthis.loading = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(url), {\n\t\t\t\t\theaders: { ...buildHeaders(null), Range: `bytes=0-${this.maxBytes - 1}` },\n\t\t\t\t\tcredentials: 'same-origin',\n\t\t\t\t})\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`HTTP ${response.status}`)\n\t\t\t\t}\n\t\t\t\tconst full = await response.text()\n\t\t\t\tconst cut = full.length > this.maxBytes\n\t\t\t\tconst raw = cut ? full.slice(0, this.maxBytes) : full\n\t\t\t\tthis.bytesRead = raw.length\n\t\t\t\tconst range = response.headers?.get?.('Content-Range') || ''\n\t\t\t\tconst total = Number(range.split('/')[1])\n\t\t\t\tthis.totalBytes = Number.isFinite(total) && total > 0 ? total : 0\n\t\t\t\tconst size = this.totalBytes || Number(this.file?.size) || 0\n\t\t\t\tthis.sampleTruncated = cut || size > raw.length\n\t\t\t\tthis.shape(raw)\n\t\t\t} catch {\n\t\t\t\tthis.failed = true\n\t\t\t}\n\t\t\tthis.loading = false\n\t\t},\n\n\t\t/**\n\t\t * Turn the fetched text into a table or formatted text.\n\t\t *\n\t\t * @param {string} raw The fetched text.\n\t\t */\n\t\tshape(raw) {\n\t\t\tif (this.kind === 'csv' || this.kind === 'tsv') {\n\t\t\t\tconst parsed = parseDelimited(raw, this.kind === 'tsv' ? '\\t' : ',', this.maxRows + 1)\n\t\t\t\tif (!parsed.balanced || parsed.rows.length === 0) {\n\t\t\t\t\tthis.mode = 'text'\n\t\t\t\t\tthis.text = raw\n\t\t\t\t\tthis.unreadable = true\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.mode = 'table'\n\t\t\t\tthis.header = parsed.rows[0]\n\t\t\t\tthis.body = parsed.rows.slice(1, this.maxRows + 1)\n\t\t\t\tthis.sampleTruncated = this.sampleTruncated || parsed.truncated\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.mode = 'text'\n\t\t\tthis.text = this.kind === 'json' ? this.formatJson(raw) : raw\n\t\t},\n\n\t\t/**\n\t\t * Pretty-print JSON; text that does not parse is returned as is.\n\t\t *\n\t\t * @param {string} raw The JSON text.\n\t\t * @return {string} The formatted text.\n\t\t */\n\t\tformatJson(raw) {\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(JSON.parse(raw), null, 2)\n\t\t\t} catch {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-file-preview {\n\tmin-height: 120px;\n}\n\n.cn-file-preview__scroll {\n\tmax-height: 60vh;\n\toverflow: auto;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-file-preview__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tfont-size: 13px;\n}\n\n.cn-file-preview__table th,\n.cn-file-preview__table td {\n\tpadding: 6px 10px;\n\ttext-align: start;\n\tborder-bottom: 1px solid var(--color-border);\n\twhite-space: nowrap;\n}\n\n.cn-file-preview__table th {\n\tposition: sticky;\n\ttop: 0;\n\tbackground: var(--color-background-dark);\n\tfont-weight: 600;\n}\n\n.cn-file-preview__count {\n\tmargin: 8px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-file-preview__text {\n\tmax-height: 60vh;\n\toverflow: auto;\n\tmargin: 0;\n\tpadding: 12px;\n\tbackground: var(--color-background-dark);\n\tborder-radius: var(--border-radius-large, 8px);\n\tfont-size: 12px;\n\twhite-space: pre-wrap;\n\toverflow-wrap: anywhere;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_Fragment","_createElementVNode","_openBlock","_renderList","_toDisplayString"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnFilesBrowser.vue2.js';
|
|
|
2
2
|
import { render } from './CnFilesBrowser.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-120dc0ec";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getCurrentUser } from '@nextcloud/auth';
|
|
2
2
|
import axios__default from '@nextcloud/axios';
|
|
3
|
+
import { subscribe, unsubscribe } from '@nextcloud/event-bus';
|
|
3
4
|
import { View, sortNodes, getNewFileMenuEntries, FileType, getFileActions, formatFileSize } from '@nextcloud/files';
|
|
4
5
|
import { getRootPath, getClient, resultToNode, getRemoteURL, getDefaultPropfind } from '@nextcloud/files/dav';
|
|
5
6
|
import { translate, translatePlural } from '@nextcloud/l10n';
|
|
@@ -30,6 +31,12 @@ let uploadSeq = 0;
|
|
|
30
31
|
/** A folder glyph, the icon the Files app's View constructor requires. */
|
|
31
32
|
const VIEW_ICON = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>';
|
|
32
33
|
|
|
34
|
+
// Event-bus channels that mean the open folder may have changed: the page's
|
|
35
|
+
// Refresh signal, and the node events the Files app's actions and uploader emit.
|
|
36
|
+
const FOLDER_CHANGED_CHANNELS = ['cn:page:refresh', 'files:node:created', 'files:node:deleted', 'files:node:updated', 'files:node:moved'];
|
|
37
|
+
|
|
38
|
+
const FOLDER_CHANGED_DEBOUNCE_MS = 300;
|
|
39
|
+
|
|
33
40
|
var script = {
|
|
34
41
|
name: 'CnFilesBrowser',
|
|
35
42
|
|
|
@@ -66,6 +73,10 @@ var script = {
|
|
|
66
73
|
// The page's pre-bound action runner (CnPageRenderer provides it);
|
|
67
74
|
// absent outside a page tree, in which case the bare dispatcher runs.
|
|
68
75
|
cnDispatchAction: { default: null },
|
|
76
|
+
// The host app's translate function (CnAppRoot provides it). A row
|
|
77
|
+
// action or column the manifest declares is authored in English as
|
|
78
|
+
// the catalogue key, and read through this in the user's language.
|
|
79
|
+
cnTranslate: { default: () => (key) => key },
|
|
69
80
|
},
|
|
70
81
|
|
|
71
82
|
props: {
|
|
@@ -726,6 +737,19 @@ var script = {
|
|
|
726
737
|
mounted() {
|
|
727
738
|
this.loadColumnChoice();
|
|
728
739
|
this.refresh();
|
|
740
|
+
// Another surface on the page changed the folder: the page's Refresh,
|
|
741
|
+
// a dialog that filed a document, or a Files action whose work ends
|
|
742
|
+
// after its own call returned (the delete confirmation, an upload).
|
|
743
|
+
for (const channel of FOLDER_CHANGED_CHANNELS) {
|
|
744
|
+
subscribe(channel, this.onFolderChanged);
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
|
|
748
|
+
beforeUnmount() {
|
|
749
|
+
for (const channel of FOLDER_CHANGED_CHANNELS) {
|
|
750
|
+
unsubscribe(channel, this.onFolderChanged);
|
|
751
|
+
}
|
|
752
|
+
clearTimeout(this._folderChangedTimer);
|
|
729
753
|
},
|
|
730
754
|
|
|
731
755
|
methods: {
|
|
@@ -1013,6 +1037,30 @@ var script = {
|
|
|
1013
1037
|
* @param {object} node The node.
|
|
1014
1038
|
* @return {string} The label.
|
|
1015
1039
|
*/
|
|
1040
|
+
/**
|
|
1041
|
+
* A host action's label in the user's language.
|
|
1042
|
+
*
|
|
1043
|
+
* @spec openspec/changes/files-browser-hears-the-page/specs/files-browser/spec.md#requirement-a-declared-row-action-reads-in-the-users-language
|
|
1044
|
+
* @param {object} action The manifest-declared action.
|
|
1045
|
+
* @return {string} The label.
|
|
1046
|
+
*/
|
|
1047
|
+
hostLabel(action) {
|
|
1048
|
+
return this.cnTranslate(String(action?.label ?? ''))
|
|
1049
|
+
},
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* Re-read the folder once after a burst of change signals. One upload
|
|
1053
|
+
* of ten files answers ten `files:node:created`, and one PROPFIND
|
|
1054
|
+
* covers them all.
|
|
1055
|
+
*
|
|
1056
|
+
* @spec openspec/changes/files-browser-hears-the-page/specs/files-browser/spec.md#requirement-the-files-browser-re-reads-when-the-page-says-files-changed
|
|
1057
|
+
* @return {void}
|
|
1058
|
+
*/
|
|
1059
|
+
onFolderChanged() {
|
|
1060
|
+
clearTimeout(this._folderChangedTimer);
|
|
1061
|
+
this._folderChangedTimer = setTimeout(() => this.refresh(), FOLDER_CHANGED_DEBOUNCE_MS);
|
|
1062
|
+
},
|
|
1063
|
+
|
|
1016
1064
|
labelOf(action, node) {
|
|
1017
1065
|
try {
|
|
1018
1066
|
return action.displayName(this.contextFor(node))
|
|
@@ -1404,7 +1452,7 @@ var script = {
|
|
|
1404
1452
|
*/
|
|
1405
1453
|
columnLabel(column) {
|
|
1406
1454
|
if (column && typeof column.label === 'string' && column.label !== '') {
|
|
1407
|
-
return column.label
|
|
1455
|
+
return this.cnTranslate(column.label)
|
|
1408
1456
|
}
|
|
1409
1457
|
return this.builtInColumnLabels[column && column.key] || (column && column.key) || ''
|
|
1410
1458
|
},
|