@conduction/nextcloud-vue 2.56.0-beta.37 → 2.56.0-beta.38
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 +28 -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/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/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 +994 -909
- 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 +55 -3
- package/dist/esm/utils/boardReferenceColumns.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 +837 -359
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +994 -909
- 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 +28 -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/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/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 +2 -1
- package/src/store/useObjectStore.js +7 -2
- package/src/utils/boardColumns.js +49 -0
- package/src/utils/boardReferenceColumns.js +54 -2
- 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 './CnCalendarWidget.vue2.js';
|
|
|
2
2
|
import { render } from './CnCalendarWidget.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-766dee4e";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { translate } from '@nextcloud/l10n';
|
|
2
|
+
import { userLocale } from '../../utils/userLocale.js';
|
|
2
3
|
|
|
3
4
|
const VIEW_MODES = ['month', 'week', 'agenda'];
|
|
4
5
|
const DEFAULT_DAYS_AHEAD = 14;
|
|
@@ -473,7 +474,7 @@ var script = {
|
|
|
473
474
|
* @return {string} the header label.
|
|
474
475
|
*/
|
|
475
476
|
formatDayHeader(date) {
|
|
476
|
-
const month = date.toLocaleString(
|
|
477
|
+
const month = date.toLocaleString(userLocale(), { month: 'short' });
|
|
477
478
|
return `${this.weekdayHeaders[date.getDay()]} ${date.getDate()} ${month}`
|
|
478
479
|
},
|
|
479
480
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnCalendarWidget.vue2.js","sources":["../../../../src/components/CnCalendarWidget/CnCalendarWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-calendar-widget\">\n\t\t<!-- `content.showTitle: false` and `content.showViewModes: false` drop\n\t\t the sub-heading and the Month / Week / Agenda buttons, for an agenda\n\t\t placement whose card already names it; without both the header\n\t\t row goes. -->\n\t\t<header v-if=\"showsTitle || showsViewModes\" class=\"cn-calendar-widget__header\">\n\t\t\t<span v-if=\"showsTitle\" class=\"cn-calendar-widget__title\">{{ t('nextcloud-vue', 'Calendar') }}</span>\n\t\t\t<div v-if=\"showsViewModes\" class=\"cn-calendar-widget__modes\">\n\t\t\t\t<button\n\t\t\t\t\tv-for=\"mode in viewModes\"\n\t\t\t\t\t:key=\"mode\"\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-calendar-widget__mode-btn\"\n\t\t\t\t\t:class=\"{ 'is-active': activeMode === mode }\"\n\t\t\t\t\t@click=\"setMode(mode)\">\n\t\t\t\t\t{{ modeLabel(mode) }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</header>\n\n\t\t<div class=\"cn-calendar-widget__body\">\n\t\t\t<div v-if=\"loading\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'Loading calendars…') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"error\" class=\"cn-calendar-widget__state cn-calendar-widget__state--error\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Failed to load events') }}</p>\n\t\t\t\t<button type=\"button\" class=\"cn-calendar-widget__retry\" @click=\"fetchEvents\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Retry') }}\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"!hasSource\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'No calendar available') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"hasNoCalendarChosen\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'No calendar is selected yet. Choose one in this widget\\u2019s settings.') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"events.length === 0 && activeMode === 'agenda'\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ emptyMessage }}\n\t\t\t</div>\n\n\t\t\t<!-- Month view: 7-column grid padded to whole weeks. -->\n\t\t\t<div v-else-if=\"activeMode === 'month'\" class=\"cn-calendar-widget__month\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(weekday, idx) in weekdayHeaders\"\n\t\t\t\t\t:key=\"'wh-' + idx\"\n\t\t\t\t\tclass=\"cn-calendar-widget__month-header\">\n\t\t\t\t\t{{ weekday }}\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"day in monthGrid\"\n\t\t\t\t\t:key=\"day.iso\"\n\t\t\t\t\tclass=\"cn-calendar-widget__month-cell\"\n\t\t\t\t\t:class=\"{ 'is-today': day.isToday, 'is-other-month': day.isOtherMonth }\">\n\t\t\t\t\t<span class=\"cn-calendar-widget__month-day\">{{ day.dayNum }}</span>\n\t\t\t\t\t<ul v-if=\"day.events.length\" class=\"cn-calendar-widget__month-events\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"event in day.events.slice(0, 3)\"\n\t\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__month-event\"\n\t\t\t\t\t\t\t:title=\"event.title\"\n\t\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t\t{{ event.title }}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-if=\"day.events.length > 3\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__month-overflow\">\n\t\t\t\t\t\t\t+{{ day.events.length - 3 }}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<!-- Week view: 7 day columns. -->\n\t\t\t<div v-else-if=\"activeMode === 'week'\" class=\"cn-calendar-widget__week\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"day in weekDays\"\n\t\t\t\t\t:key=\"day.iso\"\n\t\t\t\t\tclass=\"cn-calendar-widget__week-col\"\n\t\t\t\t\t:class=\"{ 'is-today': day.isToday }\">\n\t\t\t\t\t<header class=\"cn-calendar-widget__week-day\">\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-name\">{{ day.weekday }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-num\">{{ day.dayNum }}</span>\n\t\t\t\t\t</header>\n\t\t\t\t\t<ul v-if=\"day.events.length\" class=\"cn-calendar-widget__week-events\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"event in day.events\"\n\t\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__week-event\"\n\t\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-time\">{{ formatTime(event) }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-title\">{{ event.title }}</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<p v-else class=\"cn-calendar-widget__week-empty\">\n\t\t\t\t\t\t—\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<!-- Agenda view: chronological list grouped by day. -->\n\t\t\t<ul v-else class=\"cn-calendar-widget__agenda\">\n\t\t\t\t<template v-for=\"group in agendaGroups\" :key=\"group.iso\">\n\t\t\t\t\t<li class=\"cn-calendar-widget__agenda-header\">\n\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t</li>\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"event in group.events\"\n\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\tclass=\"cn-calendar-widget__agenda-row\"\n\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__agenda-time\">{{ formatTime(event) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__agenda-title\">{{ event.title }}</span>\n\t\t\t\t\t\t<span v-if=\"event.calendarName\" class=\"cn-calendar-widget__agenda-cal\">{{ event.calendarName }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</template>\n\t\t\t</ul>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\n\nconst VIEW_MODES = ['month', 'week', 'agenda']\nconst DEFAULT_DAYS_AHEAD = 14\n\n/**\n * CnCalendarWidget — renders aggregated upcoming events as a chronological,\n * per-day grouped agenda.\n *\n * Data origin (decoupling seam): events are read through a consumer-supplied\n * `dataSource` prop OR the `cnCalendarSource` injection — an object exposing\n * `fetchEvents({ from, to }) => Promise<{ events, failures }>`. The canonical\n * backing is the Nextcloud Calendar app (internal calendars + external ICS\n * feeds) surfaced by the consuming app; this renderer never imports a\n * sibling-app module path nor assumes a backend route. When neither prop nor\n * injection is supplied the widget shows an empty agenda (\"No calendar\n * available\") and performs no request; an empty result simply renders the\n * \"no events\" empty state.\n *\n * No event field is ever rendered via `v-html` — all values flow through Vue\n * interpolation, so a malicious ICS feed cannot inject markup.\n *\n * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n */\nexport default {\n\tname: 'CnCalendarWidget',\n\n\tinject: {\n\t\tcnCalendarSource: {\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content `{viewMode, daysAhead, colorByCalendar,\n\t\t * showTitle, showViewModes}`. `showTitle: false` drops the \"Calendar\"\n\t\t * sub-heading and `showViewModes: false` the Month / Week / Agenda\n\t\t * buttons (both shown by default).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Consumer-supplied data source overriding the `cnCalendarSource`\n\t\t * injection. Must expose `fetchEvents({from, to}) => Promise<{events,\n\t\t * failures}>`. When `null` the injection (then the empty state) is used.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tdataSource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tconst initialMode = VIEW_MODES.includes(this.content && this.content.viewMode)\n\t\t\t? this.content.viewMode\n\t\t\t: 'agenda'\n\t\treturn {\n\t\t\tactiveMode: initialMode,\n\t\t\tevents: [],\n\t\t\tfailures: [],\n\t\t\tloading: false,\n\t\t\terror: null,\n\t\t\ttoday: new Date(),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the \"Calendar\" sub-heading renders (`content.showTitle`).\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-agenda-placement-can-drop-the-calendar-chrome\n\t\t * @return {boolean}\n\t\t */\n\t\tshowsTitle() {\n\t\t\treturn !(this.content && this.content.showTitle === false)\n\t\t},\n\n\t\t/**\n\t\t * Whether the Month / Week / Agenda buttons render\n\t\t * (`content.showViewModes`).\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-agenda-placement-can-drop-the-calendar-chrome\n\t\t * @return {boolean}\n\t\t */\n\t\tshowsViewModes() {\n\t\t\treturn !(this.content && this.content.showViewModes === false)\n\t\t},\n\n\t\t/**\n\t\t * The available view modes.\n\t\t *\n\t\t * @return {string[]} the mode keys.\n\t\t */\n\t\tviewModes() {\n\t\t\treturn VIEW_MODES\n\t\t},\n\n\t\t/**\n\t\t * The active data source (prop wins over injection).\n\t\t *\n\t\t * @return {object|null} the resolved source, or `null`.\n\t\t */\n\t\tsource() {\n\t\t\tconst candidate = this.dataSource || this.cnCalendarSource\n\t\t\treturn candidate && typeof candidate.fetchEvents === 'function' ? candidate : null\n\t\t},\n\n\t\t/**\n\t\t * Whether a usable calendar source is configured.\n\t\t *\n\t\t * @return {boolean} true when a source exists.\n\t\t */\n\t\thasSource() {\n\t\t\treturn this.source !== null\n\t\t},\n\n\t\t/**\n\t\t * The validated look-ahead window in days.\n\t\t *\n\t\t * @return {number} the days-ahead value.\n\t\t */\n\t\tdaysAhead() {\n\t\t\tconst value = parseInt((this.content && this.content.daysAhead) ?? DEFAULT_DAYS_AHEAD, 10)\n\t\t\treturn Number.isFinite(value) && value > 0 ? value : DEFAULT_DAYS_AHEAD\n\t\t},\n\n\t\t/**\n\t\t * Whether events are tinted by their calendar colour.\n\t\t *\n\t\t * @return {boolean} the colorByCalendar flag.\n\t\t */\n\t\tcolorByCalendar() {\n\t\t\treturn !(this.content && this.content.colorByCalendar === false)\n\t\t},\n\n\t\t/**\n\t\t * Whether the widget is configured with no calendar at all.\n\t\t *\n\t\t * The host fetches events for the calendars named in\n\t\t * `content.internalCalendars` / `content.externalIcsUrls`, and it\n\t\t * skips the fetch entirely when both are empty: LaunchPad's\n\t\t * `CalendarWidgetService::getEvents()` guards each branch with\n\t\t * `!== []`, so an unconfigured widget returns zero events AND zero\n\t\t * failures. That is indistinguishable from a genuinely empty week.\n\t\t *\n\t\t * It is not a rare state. The registry's own `defaultContent` for\n\t\t * this widget type is `internalCalendars: []`, so EVERY freshly added\n\t\t * Calendar widget starts here and says \"No events in the next 14\n\t\t * days\" forever, which is a false statement about the user's diary\n\t\t * rather than a prompt to finish configuring it.\n\t\t *\n\t\t * Only claims the state when both lists are present and empty. A host\n\t\t * that drives the widget through `dataSource` alone, with no content\n\t\t * blob, keeps the ordinary empty message.\n\t\t *\n\t\t * @return {boolean} true when no calendar has been chosen.\n\t\t */\n\t\thasNoCalendarChosen() {\n\t\t\tconst c = this.content\n\t\t\tif (!c) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst internal = c.internalCalendars\n\t\t\tconst external = c.externalIcsUrls\n\t\t\tif (!Array.isArray(internal) && !Array.isArray(external)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn (internal || []).length === 0 && (external || []).length === 0\n\t\t},\n\n\t\t/**\n\t\t * The empty-agenda message.\n\t\t *\n\t\t * @return {string} the empty message.\n\t\t */\n\t\temptyMessage() {\n\t\t\treturn t('nextcloud-vue', 'No events in the next {n} days').replace('{n}', String(this.daysAhead))\n\t\t},\n\n\t\t/**\n\t\t * Localised short weekday names (Sun-first).\n\t\t *\n\t\t * @return {string[]} the weekday labels.\n\t\t */\n\t\tweekdayHeaders() {\n\t\t\treturn [\n\t\t\t\tt('nextcloud-vue', 'Sun'),\n\t\t\t\tt('nextcloud-vue', 'Mon'),\n\t\t\t\tt('nextcloud-vue', 'Tue'),\n\t\t\t\tt('nextcloud-vue', 'Wed'),\n\t\t\t\tt('nextcloud-vue', 'Thu'),\n\t\t\t\tt('nextcloud-vue', 'Fri'),\n\t\t\t\tt('nextcloud-vue', 'Sat'),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * The first/last day of the current month (month-view window).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the month window.\n\t\t */\n\t\tmonthRange() {\n\t\t\tconst today = this.today\n\t\t\tconst first = new Date(today.getFullYear(), today.getMonth(), 1)\n\t\t\tconst last = new Date(today.getFullYear(), today.getMonth() + 1, 0)\n\t\t\treturn { from: first, to: last }\n\t\t},\n\n\t\t/**\n\t\t * The Sun–Sat window containing today (week-view window).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the week window.\n\t\t */\n\t\tweekRange() {\n\t\t\tconst today = this.today\n\t\t\tconst start = new Date(today)\n\t\t\tstart.setDate(today.getDate() - today.getDay())\n\t\t\tstart.setHours(0, 0, 0, 0)\n\t\t\tconst end = new Date(start)\n\t\t\tend.setDate(start.getDate() + 6)\n\t\t\tend.setHours(23, 59, 59, 999)\n\t\t\treturn { from: start, to: end }\n\t\t},\n\n\t\t/**\n\t\t * Events bucketed by `YYYY-MM-DD` for grid lookup.\n\t\t *\n\t\t * @return {Record<string, object[]>} iso-date → events.\n\t\t */\n\t\teventsByDay() {\n\t\t\tconst buckets = {}\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst iso = this.toIsoDate(this.parseDate(event.start))\n\t\t\t\tif (!buckets[iso]) {\n\t\t\t\t\tbuckets[iso] = []\n\t\t\t\t}\n\t\t\t\tbuckets[iso].push(event)\n\t\t\t}\n\t\t\treturn buckets\n\t\t},\n\n\t\t/**\n\t\t * The 7-column month grid (leading/trailing days padded to whole weeks).\n\t\t *\n\t\t * @return {Array<object>} grid cells with day metadata + events.\n\t\t */\n\t\tmonthGrid() {\n\t\t\tconst { from, to } = this.monthRange\n\t\t\tconst cells = []\n\t\t\tconst gridStart = new Date(from)\n\t\t\tgridStart.setDate(from.getDate() - from.getDay())\n\t\t\tconst totalDays = Math.ceil((to.getDate() + from.getDay()) / 7) * 7\n\t\t\tfor (let i = 0; i < totalDays; i++) {\n\t\t\t\tconst cellDate = new Date(gridStart)\n\t\t\t\tcellDate.setDate(gridStart.getDate() + i)\n\t\t\t\tconst iso = this.toIsoDate(cellDate)\n\t\t\t\tcells.push({\n\t\t\t\t\tiso,\n\t\t\t\t\tdayNum: cellDate.getDate(),\n\t\t\t\t\tisToday: this.toIsoDate(this.today) === iso,\n\t\t\t\t\tisOtherMonth: cellDate.getMonth() !== from.getMonth(),\n\t\t\t\t\tevents: this.eventsByDay[iso] || [],\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn cells\n\t\t},\n\n\t\t/**\n\t\t * The 7 day-columns for the week view.\n\t\t *\n\t\t * @return {Array<object>} day columns with metadata + events.\n\t\t */\n\t\tweekDays() {\n\t\t\tconst { from } = this.weekRange\n\t\t\tconst out = []\n\t\t\tfor (let i = 0; i < 7; i++) {\n\t\t\t\tconst day = new Date(from)\n\t\t\t\tday.setDate(from.getDate() + i)\n\t\t\t\tconst iso = this.toIsoDate(day)\n\t\t\t\tout.push({\n\t\t\t\t\tiso,\n\t\t\t\t\tweekday: this.weekdayHeaders[day.getDay()],\n\t\t\t\t\tdayNum: day.getDate(),\n\t\t\t\t\tisToday: this.toIsoDate(this.today) === iso,\n\t\t\t\t\tevents: this.eventsByDay[iso] || [],\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * Events grouped into per-day buckets in chronological order.\n\t\t *\n\t\t * @return {Array<{iso: string, label: string, events: object[]}>} the groups.\n\t\t */\n\t\tagendaGroups() {\n\t\t\tconst groups = []\n\t\t\tconst seen = {}\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst date = this.parseDate(event.start)\n\t\t\t\tconst iso = this.toIsoDate(date)\n\t\t\t\tif (!seen[iso]) {\n\t\t\t\t\tseen[iso] = { iso, label: this.formatDayHeader(date), events: [] }\n\t\t\t\t\tgroups.push(seen[iso])\n\t\t\t\t}\n\t\t\t\tseen[iso].events.push(event)\n\t\t\t}\n\t\t\treturn groups\n\t\t},\n\t},\n\n\twatch: {\n\t\tcontent: {\n\t\t\tdeep: true,\n\t\t\thandler() {\n\t\t\t\tthis.fetchEvents()\n\t\t\t},\n\t\t},\n\n\t\t// Month/week/agenda use different fetch windows — refetch on switch.\n\t\tactiveMode() {\n\t\t\tthis.fetchEvents()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.fetchEvents()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Switch the active view mode.\n\t\t *\n\t\t * @param {string} mode the mode to activate.\n\t\t * @return {void}\n\t\t */\n\t\tsetMode(mode) {\n\t\t\tif (VIEW_MODES.includes(mode)) {\n\t\t\t\tthis.activeMode = mode\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The label for a view mode.\n\t\t *\n\t\t * @param {string} mode the mode key.\n\t\t * @return {string} the localised label.\n\t\t */\n\t\tmodeLabel(mode) {\n\t\t\tif (mode === 'month') {\n\t\t\t\treturn t('nextcloud-vue', 'Month')\n\t\t\t}\n\t\t\tif (mode === 'week') {\n\t\t\t\treturn t('nextcloud-vue', 'Week')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Agenda')\n\t\t},\n\n\t\t/**\n\t\t * Compute the fetch window for the active view: the whole month in\n\t\t * month view, the Sun–Sat week in week view, else the daysAhead\n\t\t * look-ahead (agenda).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the window.\n\t\t */\n\t\tcomputeRange() {\n\t\t\tif (this.activeMode === 'month') {\n\t\t\t\tconst { from, to } = this.monthRange\n\t\t\t\tconst start = new Date(from)\n\t\t\t\tstart.setHours(0, 0, 0, 0)\n\t\t\t\tconst end = new Date(to)\n\t\t\t\tend.setHours(23, 59, 59, 999)\n\t\t\t\treturn { from: start, to: end }\n\t\t\t}\n\t\t\tif (this.activeMode === 'week') {\n\t\t\t\treturn this.weekRange\n\t\t\t}\n\t\t\tconst from = new Date(this.today)\n\t\t\tfrom.setHours(0, 0, 0, 0)\n\t\t\tconst to = new Date(from)\n\t\t\tto.setDate(from.getDate() + this.daysAhead)\n\t\t\tto.setHours(23, 59, 59, 999)\n\t\t\treturn { from, to }\n\t\t},\n\n\t\t/**\n\t\t * Load events from the configured source. A missing source or a fetch\n\t\t * failure degrades to the empty / error state without throwing.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchEvents() {\n\t\t\tif (!this.source) {\n\t\t\t\tthis.events = []\n\t\t\t\tthis.failures = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst { from, to } = this.computeRange()\n\t\t\tthis.loading = true\n\t\t\tthis.error = null\n\t\t\ttry {\n\t\t\t\tconst payload = await this.source.fetchEvents({\n\t\t\t\t\tfrom: from.toISOString(),\n\t\t\t\t\tto: to.toISOString(),\n\t\t\t\t}) || {}\n\t\t\t\tthis.events = Array.isArray(payload.events) ? payload.events : []\n\t\t\t\tthis.failures = Array.isArray(payload.failures) ? payload.failures : []\n\t\t\t} catch (err) {\n\t\t\t\tthis.error = err\n\t\t\t\tthis.events = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Parse a date value, falling back to now on invalid input.\n\t\t *\n\t\t * @param {string|number|Date} value the value to parse.\n\t\t * @return {Date} the parsed date.\n\t\t */\n\t\tparseDate(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn new Date()\n\t\t\t}\n\t\t\tconst d = new Date(value)\n\t\t\treturn Number.isNaN(d.getTime()) ? new Date() : d\n\t\t},\n\n\t\t/**\n\t\t * Format a date as `YYYY-MM-DD`.\n\t\t *\n\t\t * @param {Date} date the date.\n\t\t * @return {string} the ISO date string.\n\t\t */\n\t\ttoIsoDate(date) {\n\t\t\tconst yyyy = date.getFullYear()\n\t\t\tconst mm = String(date.getMonth() + 1).padStart(2, '0')\n\t\t\tconst dd = String(date.getDate()).padStart(2, '0')\n\t\t\treturn `${yyyy}-${mm}-${dd}`\n\t\t},\n\n\t\t/**\n\t\t * Format an event's start time (or \"All day\").\n\t\t *\n\t\t * @param {object} event the event.\n\t\t * @return {string} the time label.\n\t\t */\n\t\tformatTime(event) {\n\t\t\tif (event.allDay) {\n\t\t\t\treturn t('nextcloud-vue', 'All day')\n\t\t\t}\n\t\t\tconst start = this.parseDate(event.start)\n\t\t\tconst hh = String(start.getHours()).padStart(2, '0')\n\t\t\tconst mm = String(start.getMinutes()).padStart(2, '0')\n\t\t\treturn `${hh}:${mm}`\n\t\t},\n\n\t\t/**\n\t\t * Format a per-day agenda group header.\n\t\t *\n\t\t * @param {Date} date the group date.\n\t\t * @return {string} the header label.\n\t\t */\n\t\tformatDayHeader(date) {\n\t\t\tconst month = date.toLocaleString(undefined, { month: 'short' })\n\t\t\treturn `${this.weekdayHeaders[date.getDay()]} ${date.getDate()} ${month}`\n\t\t},\n\n\t\t/**\n\t\t * Compute the left-border colour style for an event row.\n\t\t *\n\t\t * @param {object} event the event.\n\t\t * @return {object} the style object (empty when colouring is off).\n\t\t */\n\t\teventStyle(event) {\n\t\t\tif (!this.colorByCalendar) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst color = event.color\n\t\t\tif (typeof color === 'string' && color !== '') {\n\t\t\t\treturn { 'border-left': `3px solid ${color}` }\n\t\t\t}\n\t\t\treturn {}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-widget {\n\tdisplay: flex;\n\tflex-direction: column;\n\twidth: 100%;\n\theight: 100%;\n\toverflow: hidden;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-calendar-widget__header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tpadding: 8px 12px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-calendar-widget__title {\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__modes {\n\tdisplay: flex;\n\tgap: 4px;\n}\n\n.cn-calendar-widget__mode-btn {\n\tborder: 1px solid var(--color-border);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tborder-radius: var(--border-radius);\n\tpadding: 2px 8px;\n\tfont-size: 12px;\n\tcursor: pointer;\n}\n\n.cn-calendar-widget__mode-btn.is-active {\n\tbackground: var(--color-primary-element-light, var(--color-primary-element));\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-calendar-widget__body {\n\tflex: 1;\n\toverflow: auto;\n\tpadding: 8px 12px;\n}\n\n.cn-calendar-widget__state {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tpadding: 16px;\n\ttext-align: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__retry {\n\tpadding: 4px 12px;\n\tborder: 1px solid var(--color-border);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tcursor: pointer;\n\tborder-radius: var(--border-radius);\n}\n\n.cn-calendar-widget__month {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(7, 1fr);\n\tgap: 2px;\n}\n\n.cn-calendar-widget__month-header {\n\tfont-size: 11px;\n\tfont-weight: 600;\n\ttext-align: center;\n\tpadding: 4px 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__month-cell {\n\tmin-height: 60px;\n\tpadding: 2px 4px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 2px;\n\tbackground: var(--color-main-background);\n}\n\n.cn-calendar-widget__month-cell.is-today {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-calendar-widget__month-cell.is-other-month {\n\topacity: 0.45;\n}\n\n.cn-calendar-widget__month-day {\n\tfont-size: 11px;\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__month-events {\n\tlist-style: none;\n\tmargin: 2px 0 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__month-event {\n\tfont-size: 10px;\n\tpadding: 1px 3px;\n\tmargin-bottom: 1px;\n\tborder-radius: 2px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-widget__month-overflow {\n\tfont-size: 10px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__week {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(7, 1fr);\n\tgap: 4px;\n}\n\n.cn-calendar-widget__week-col {\n\tdisplay: flex;\n\tflex-direction: column;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 4px;\n}\n\n.cn-calendar-widget__week-col.is-today {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-calendar-widget__week-day {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tborder-bottom: 1px solid var(--color-border);\n\tpadding-bottom: 2px;\n\tmargin-bottom: 4px;\n}\n\n.cn-calendar-widget__week-name {\n\tfont-size: 10px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__week-num {\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__week-events {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__week-event {\n\tfont-size: 11px;\n\tpadding: 2px 4px;\n\tmargin-bottom: 2px;\n\tbackground: var(--color-background-dark);\n\tborder-radius: 2px;\n}\n\n.cn-calendar-widget__week-time {\n\tfont-weight: 600;\n\tmargin-right: 4px;\n}\n\n.cn-calendar-widget__week-empty {\n\tfont-size: 11px;\n\ttext-align: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__agenda {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__agenda-header {\n\tfont-size: 12px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-top: 8px;\n\tmargin-bottom: 4px;\n}\n\n.cn-calendar-widget__agenda-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 4px 6px;\n\tmargin-bottom: 2px;\n\tbackground: var(--color-background-hover);\n\tborder-radius: 2px;\n}\n\n.cn-calendar-widget__agenda-time {\n\tfont-weight: 600;\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-calendar-widget__agenda-title {\n\tflex: 1;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-widget__agenda-cal {\n\tfont-size: 11px;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnCalendarWidget.vue2.js","sources":["../../../../src/components/CnCalendarWidget/CnCalendarWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-calendar-widget\">\n\t\t<!-- `content.showTitle: false` and `content.showViewModes: false` drop\n\t\t the sub-heading and the Month / Week / Agenda buttons, for an agenda\n\t\t placement whose card already names it; without both the header\n\t\t row goes. -->\n\t\t<header v-if=\"showsTitle || showsViewModes\" class=\"cn-calendar-widget__header\">\n\t\t\t<span v-if=\"showsTitle\" class=\"cn-calendar-widget__title\">{{ t('nextcloud-vue', 'Calendar') }}</span>\n\t\t\t<div v-if=\"showsViewModes\" class=\"cn-calendar-widget__modes\">\n\t\t\t\t<button\n\t\t\t\t\tv-for=\"mode in viewModes\"\n\t\t\t\t\t:key=\"mode\"\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-calendar-widget__mode-btn\"\n\t\t\t\t\t:class=\"{ 'is-active': activeMode === mode }\"\n\t\t\t\t\t@click=\"setMode(mode)\">\n\t\t\t\t\t{{ modeLabel(mode) }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</header>\n\n\t\t<div class=\"cn-calendar-widget__body\">\n\t\t\t<div v-if=\"loading\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'Loading calendars…') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"error\" class=\"cn-calendar-widget__state cn-calendar-widget__state--error\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Failed to load events') }}</p>\n\t\t\t\t<button type=\"button\" class=\"cn-calendar-widget__retry\" @click=\"fetchEvents\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Retry') }}\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"!hasSource\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'No calendar available') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"hasNoCalendarChosen\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'No calendar is selected yet. Choose one in this widget\\u2019s settings.') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"events.length === 0 && activeMode === 'agenda'\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ emptyMessage }}\n\t\t\t</div>\n\n\t\t\t<!-- Month view: 7-column grid padded to whole weeks. -->\n\t\t\t<div v-else-if=\"activeMode === 'month'\" class=\"cn-calendar-widget__month\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(weekday, idx) in weekdayHeaders\"\n\t\t\t\t\t:key=\"'wh-' + idx\"\n\t\t\t\t\tclass=\"cn-calendar-widget__month-header\">\n\t\t\t\t\t{{ weekday }}\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"day in monthGrid\"\n\t\t\t\t\t:key=\"day.iso\"\n\t\t\t\t\tclass=\"cn-calendar-widget__month-cell\"\n\t\t\t\t\t:class=\"{ 'is-today': day.isToday, 'is-other-month': day.isOtherMonth }\">\n\t\t\t\t\t<span class=\"cn-calendar-widget__month-day\">{{ day.dayNum }}</span>\n\t\t\t\t\t<ul v-if=\"day.events.length\" class=\"cn-calendar-widget__month-events\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"event in day.events.slice(0, 3)\"\n\t\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__month-event\"\n\t\t\t\t\t\t\t:title=\"event.title\"\n\t\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t\t{{ event.title }}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-if=\"day.events.length > 3\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__month-overflow\">\n\t\t\t\t\t\t\t+{{ day.events.length - 3 }}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<!-- Week view: 7 day columns. -->\n\t\t\t<div v-else-if=\"activeMode === 'week'\" class=\"cn-calendar-widget__week\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"day in weekDays\"\n\t\t\t\t\t:key=\"day.iso\"\n\t\t\t\t\tclass=\"cn-calendar-widget__week-col\"\n\t\t\t\t\t:class=\"{ 'is-today': day.isToday }\">\n\t\t\t\t\t<header class=\"cn-calendar-widget__week-day\">\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-name\">{{ day.weekday }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-num\">{{ day.dayNum }}</span>\n\t\t\t\t\t</header>\n\t\t\t\t\t<ul v-if=\"day.events.length\" class=\"cn-calendar-widget__week-events\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"event in day.events\"\n\t\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__week-event\"\n\t\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-time\">{{ formatTime(event) }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-title\">{{ event.title }}</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<p v-else class=\"cn-calendar-widget__week-empty\">\n\t\t\t\t\t\t—\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<!-- Agenda view: chronological list grouped by day. -->\n\t\t\t<ul v-else class=\"cn-calendar-widget__agenda\">\n\t\t\t\t<template v-for=\"group in agendaGroups\" :key=\"group.iso\">\n\t\t\t\t\t<li class=\"cn-calendar-widget__agenda-header\">\n\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t</li>\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"event in group.events\"\n\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\tclass=\"cn-calendar-widget__agenda-row\"\n\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__agenda-time\">{{ formatTime(event) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__agenda-title\">{{ event.title }}</span>\n\t\t\t\t\t\t<span v-if=\"event.calendarName\" class=\"cn-calendar-widget__agenda-cal\">{{ event.calendarName }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</template>\n\t\t\t</ul>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { userLocale } from '../../utils/userLocale.js'\n\nconst VIEW_MODES = ['month', 'week', 'agenda']\nconst DEFAULT_DAYS_AHEAD = 14\n\n/**\n * CnCalendarWidget — renders aggregated upcoming events as a chronological,\n * per-day grouped agenda.\n *\n * Data origin (decoupling seam): events are read through a consumer-supplied\n * `dataSource` prop OR the `cnCalendarSource` injection — an object exposing\n * `fetchEvents({ from, to }) => Promise<{ events, failures }>`. The canonical\n * backing is the Nextcloud Calendar app (internal calendars + external ICS\n * feeds) surfaced by the consuming app; this renderer never imports a\n * sibling-app module path nor assumes a backend route. When neither prop nor\n * injection is supplied the widget shows an empty agenda (\"No calendar\n * available\") and performs no request; an empty result simply renders the\n * \"no events\" empty state.\n *\n * No event field is ever rendered via `v-html` — all values flow through Vue\n * interpolation, so a malicious ICS feed cannot inject markup.\n *\n * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n */\nexport default {\n\tname: 'CnCalendarWidget',\n\n\tinject: {\n\t\tcnCalendarSource: {\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content `{viewMode, daysAhead, colorByCalendar,\n\t\t * showTitle, showViewModes}`. `showTitle: false` drops the \"Calendar\"\n\t\t * sub-heading and `showViewModes: false` the Month / Week / Agenda\n\t\t * buttons (both shown by default).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Consumer-supplied data source overriding the `cnCalendarSource`\n\t\t * injection. Must expose `fetchEvents({from, to}) => Promise<{events,\n\t\t * failures}>`. When `null` the injection (then the empty state) is used.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tdataSource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tconst initialMode = VIEW_MODES.includes(this.content && this.content.viewMode)\n\t\t\t? this.content.viewMode\n\t\t\t: 'agenda'\n\t\treturn {\n\t\t\tactiveMode: initialMode,\n\t\t\tevents: [],\n\t\t\tfailures: [],\n\t\t\tloading: false,\n\t\t\terror: null,\n\t\t\ttoday: new Date(),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the \"Calendar\" sub-heading renders (`content.showTitle`).\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-agenda-placement-can-drop-the-calendar-chrome\n\t\t * @return {boolean}\n\t\t */\n\t\tshowsTitle() {\n\t\t\treturn !(this.content && this.content.showTitle === false)\n\t\t},\n\n\t\t/**\n\t\t * Whether the Month / Week / Agenda buttons render\n\t\t * (`content.showViewModes`).\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-agenda-placement-can-drop-the-calendar-chrome\n\t\t * @return {boolean}\n\t\t */\n\t\tshowsViewModes() {\n\t\t\treturn !(this.content && this.content.showViewModes === false)\n\t\t},\n\n\t\t/**\n\t\t * The available view modes.\n\t\t *\n\t\t * @return {string[]} the mode keys.\n\t\t */\n\t\tviewModes() {\n\t\t\treturn VIEW_MODES\n\t\t},\n\n\t\t/**\n\t\t * The active data source (prop wins over injection).\n\t\t *\n\t\t * @return {object|null} the resolved source, or `null`.\n\t\t */\n\t\tsource() {\n\t\t\tconst candidate = this.dataSource || this.cnCalendarSource\n\t\t\treturn candidate && typeof candidate.fetchEvents === 'function' ? candidate : null\n\t\t},\n\n\t\t/**\n\t\t * Whether a usable calendar source is configured.\n\t\t *\n\t\t * @return {boolean} true when a source exists.\n\t\t */\n\t\thasSource() {\n\t\t\treturn this.source !== null\n\t\t},\n\n\t\t/**\n\t\t * The validated look-ahead window in days.\n\t\t *\n\t\t * @return {number} the days-ahead value.\n\t\t */\n\t\tdaysAhead() {\n\t\t\tconst value = parseInt((this.content && this.content.daysAhead) ?? DEFAULT_DAYS_AHEAD, 10)\n\t\t\treturn Number.isFinite(value) && value > 0 ? value : DEFAULT_DAYS_AHEAD\n\t\t},\n\n\t\t/**\n\t\t * Whether events are tinted by their calendar colour.\n\t\t *\n\t\t * @return {boolean} the colorByCalendar flag.\n\t\t */\n\t\tcolorByCalendar() {\n\t\t\treturn !(this.content && this.content.colorByCalendar === false)\n\t\t},\n\n\t\t/**\n\t\t * Whether the widget is configured with no calendar at all.\n\t\t *\n\t\t * The host fetches events for the calendars named in\n\t\t * `content.internalCalendars` / `content.externalIcsUrls`, and it\n\t\t * skips the fetch entirely when both are empty: LaunchPad's\n\t\t * `CalendarWidgetService::getEvents()` guards each branch with\n\t\t * `!== []`, so an unconfigured widget returns zero events AND zero\n\t\t * failures. That is indistinguishable from a genuinely empty week.\n\t\t *\n\t\t * It is not a rare state. The registry's own `defaultContent` for\n\t\t * this widget type is `internalCalendars: []`, so EVERY freshly added\n\t\t * Calendar widget starts here and says \"No events in the next 14\n\t\t * days\" forever, which is a false statement about the user's diary\n\t\t * rather than a prompt to finish configuring it.\n\t\t *\n\t\t * Only claims the state when both lists are present and empty. A host\n\t\t * that drives the widget through `dataSource` alone, with no content\n\t\t * blob, keeps the ordinary empty message.\n\t\t *\n\t\t * @return {boolean} true when no calendar has been chosen.\n\t\t */\n\t\thasNoCalendarChosen() {\n\t\t\tconst c = this.content\n\t\t\tif (!c) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst internal = c.internalCalendars\n\t\t\tconst external = c.externalIcsUrls\n\t\t\tif (!Array.isArray(internal) && !Array.isArray(external)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn (internal || []).length === 0 && (external || []).length === 0\n\t\t},\n\n\t\t/**\n\t\t * The empty-agenda message.\n\t\t *\n\t\t * @return {string} the empty message.\n\t\t */\n\t\temptyMessage() {\n\t\t\treturn t('nextcloud-vue', 'No events in the next {n} days').replace('{n}', String(this.daysAhead))\n\t\t},\n\n\t\t/**\n\t\t * Localised short weekday names (Sun-first).\n\t\t *\n\t\t * @return {string[]} the weekday labels.\n\t\t */\n\t\tweekdayHeaders() {\n\t\t\treturn [\n\t\t\t\tt('nextcloud-vue', 'Sun'),\n\t\t\t\tt('nextcloud-vue', 'Mon'),\n\t\t\t\tt('nextcloud-vue', 'Tue'),\n\t\t\t\tt('nextcloud-vue', 'Wed'),\n\t\t\t\tt('nextcloud-vue', 'Thu'),\n\t\t\t\tt('nextcloud-vue', 'Fri'),\n\t\t\t\tt('nextcloud-vue', 'Sat'),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * The first/last day of the current month (month-view window).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the month window.\n\t\t */\n\t\tmonthRange() {\n\t\t\tconst today = this.today\n\t\t\tconst first = new Date(today.getFullYear(), today.getMonth(), 1)\n\t\t\tconst last = new Date(today.getFullYear(), today.getMonth() + 1, 0)\n\t\t\treturn { from: first, to: last }\n\t\t},\n\n\t\t/**\n\t\t * The Sun–Sat window containing today (week-view window).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the week window.\n\t\t */\n\t\tweekRange() {\n\t\t\tconst today = this.today\n\t\t\tconst start = new Date(today)\n\t\t\tstart.setDate(today.getDate() - today.getDay())\n\t\t\tstart.setHours(0, 0, 0, 0)\n\t\t\tconst end = new Date(start)\n\t\t\tend.setDate(start.getDate() + 6)\n\t\t\tend.setHours(23, 59, 59, 999)\n\t\t\treturn { from: start, to: end }\n\t\t},\n\n\t\t/**\n\t\t * Events bucketed by `YYYY-MM-DD` for grid lookup.\n\t\t *\n\t\t * @return {Record<string, object[]>} iso-date → events.\n\t\t */\n\t\teventsByDay() {\n\t\t\tconst buckets = {}\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst iso = this.toIsoDate(this.parseDate(event.start))\n\t\t\t\tif (!buckets[iso]) {\n\t\t\t\t\tbuckets[iso] = []\n\t\t\t\t}\n\t\t\t\tbuckets[iso].push(event)\n\t\t\t}\n\t\t\treturn buckets\n\t\t},\n\n\t\t/**\n\t\t * The 7-column month grid (leading/trailing days padded to whole weeks).\n\t\t *\n\t\t * @return {Array<object>} grid cells with day metadata + events.\n\t\t */\n\t\tmonthGrid() {\n\t\t\tconst { from, to } = this.monthRange\n\t\t\tconst cells = []\n\t\t\tconst gridStart = new Date(from)\n\t\t\tgridStart.setDate(from.getDate() - from.getDay())\n\t\t\tconst totalDays = Math.ceil((to.getDate() + from.getDay()) / 7) * 7\n\t\t\tfor (let i = 0; i < totalDays; i++) {\n\t\t\t\tconst cellDate = new Date(gridStart)\n\t\t\t\tcellDate.setDate(gridStart.getDate() + i)\n\t\t\t\tconst iso = this.toIsoDate(cellDate)\n\t\t\t\tcells.push({\n\t\t\t\t\tiso,\n\t\t\t\t\tdayNum: cellDate.getDate(),\n\t\t\t\t\tisToday: this.toIsoDate(this.today) === iso,\n\t\t\t\t\tisOtherMonth: cellDate.getMonth() !== from.getMonth(),\n\t\t\t\t\tevents: this.eventsByDay[iso] || [],\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn cells\n\t\t},\n\n\t\t/**\n\t\t * The 7 day-columns for the week view.\n\t\t *\n\t\t * @return {Array<object>} day columns with metadata + events.\n\t\t */\n\t\tweekDays() {\n\t\t\tconst { from } = this.weekRange\n\t\t\tconst out = []\n\t\t\tfor (let i = 0; i < 7; i++) {\n\t\t\t\tconst day = new Date(from)\n\t\t\t\tday.setDate(from.getDate() + i)\n\t\t\t\tconst iso = this.toIsoDate(day)\n\t\t\t\tout.push({\n\t\t\t\t\tiso,\n\t\t\t\t\tweekday: this.weekdayHeaders[day.getDay()],\n\t\t\t\t\tdayNum: day.getDate(),\n\t\t\t\t\tisToday: this.toIsoDate(this.today) === iso,\n\t\t\t\t\tevents: this.eventsByDay[iso] || [],\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * Events grouped into per-day buckets in chronological order.\n\t\t *\n\t\t * @return {Array<{iso: string, label: string, events: object[]}>} the groups.\n\t\t */\n\t\tagendaGroups() {\n\t\t\tconst groups = []\n\t\t\tconst seen = {}\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst date = this.parseDate(event.start)\n\t\t\t\tconst iso = this.toIsoDate(date)\n\t\t\t\tif (!seen[iso]) {\n\t\t\t\t\tseen[iso] = { iso, label: this.formatDayHeader(date), events: [] }\n\t\t\t\t\tgroups.push(seen[iso])\n\t\t\t\t}\n\t\t\t\tseen[iso].events.push(event)\n\t\t\t}\n\t\t\treturn groups\n\t\t},\n\t},\n\n\twatch: {\n\t\tcontent: {\n\t\t\tdeep: true,\n\t\t\thandler() {\n\t\t\t\tthis.fetchEvents()\n\t\t\t},\n\t\t},\n\n\t\t// Month/week/agenda use different fetch windows — refetch on switch.\n\t\tactiveMode() {\n\t\t\tthis.fetchEvents()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.fetchEvents()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Switch the active view mode.\n\t\t *\n\t\t * @param {string} mode the mode to activate.\n\t\t * @return {void}\n\t\t */\n\t\tsetMode(mode) {\n\t\t\tif (VIEW_MODES.includes(mode)) {\n\t\t\t\tthis.activeMode = mode\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The label for a view mode.\n\t\t *\n\t\t * @param {string} mode the mode key.\n\t\t * @return {string} the localised label.\n\t\t */\n\t\tmodeLabel(mode) {\n\t\t\tif (mode === 'month') {\n\t\t\t\treturn t('nextcloud-vue', 'Month')\n\t\t\t}\n\t\t\tif (mode === 'week') {\n\t\t\t\treturn t('nextcloud-vue', 'Week')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Agenda')\n\t\t},\n\n\t\t/**\n\t\t * Compute the fetch window for the active view: the whole month in\n\t\t * month view, the Sun–Sat week in week view, else the daysAhead\n\t\t * look-ahead (agenda).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the window.\n\t\t */\n\t\tcomputeRange() {\n\t\t\tif (this.activeMode === 'month') {\n\t\t\t\tconst { from, to } = this.monthRange\n\t\t\t\tconst start = new Date(from)\n\t\t\t\tstart.setHours(0, 0, 0, 0)\n\t\t\t\tconst end = new Date(to)\n\t\t\t\tend.setHours(23, 59, 59, 999)\n\t\t\t\treturn { from: start, to: end }\n\t\t\t}\n\t\t\tif (this.activeMode === 'week') {\n\t\t\t\treturn this.weekRange\n\t\t\t}\n\t\t\tconst from = new Date(this.today)\n\t\t\tfrom.setHours(0, 0, 0, 0)\n\t\t\tconst to = new Date(from)\n\t\t\tto.setDate(from.getDate() + this.daysAhead)\n\t\t\tto.setHours(23, 59, 59, 999)\n\t\t\treturn { from, to }\n\t\t},\n\n\t\t/**\n\t\t * Load events from the configured source. A missing source or a fetch\n\t\t * failure degrades to the empty / error state without throwing.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchEvents() {\n\t\t\tif (!this.source) {\n\t\t\t\tthis.events = []\n\t\t\t\tthis.failures = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst { from, to } = this.computeRange()\n\t\t\tthis.loading = true\n\t\t\tthis.error = null\n\t\t\ttry {\n\t\t\t\tconst payload = await this.source.fetchEvents({\n\t\t\t\t\tfrom: from.toISOString(),\n\t\t\t\t\tto: to.toISOString(),\n\t\t\t\t}) || {}\n\t\t\t\tthis.events = Array.isArray(payload.events) ? payload.events : []\n\t\t\t\tthis.failures = Array.isArray(payload.failures) ? payload.failures : []\n\t\t\t} catch (err) {\n\t\t\t\tthis.error = err\n\t\t\t\tthis.events = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Parse a date value, falling back to now on invalid input.\n\t\t *\n\t\t * @param {string|number|Date} value the value to parse.\n\t\t * @return {Date} the parsed date.\n\t\t */\n\t\tparseDate(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn new Date()\n\t\t\t}\n\t\t\tconst d = new Date(value)\n\t\t\treturn Number.isNaN(d.getTime()) ? new Date() : d\n\t\t},\n\n\t\t/**\n\t\t * Format a date as `YYYY-MM-DD`.\n\t\t *\n\t\t * @param {Date} date the date.\n\t\t * @return {string} the ISO date string.\n\t\t */\n\t\ttoIsoDate(date) {\n\t\t\tconst yyyy = date.getFullYear()\n\t\t\tconst mm = String(date.getMonth() + 1).padStart(2, '0')\n\t\t\tconst dd = String(date.getDate()).padStart(2, '0')\n\t\t\treturn `${yyyy}-${mm}-${dd}`\n\t\t},\n\n\t\t/**\n\t\t * Format an event's start time (or \"All day\").\n\t\t *\n\t\t * @param {object} event the event.\n\t\t * @return {string} the time label.\n\t\t */\n\t\tformatTime(event) {\n\t\t\tif (event.allDay) {\n\t\t\t\treturn t('nextcloud-vue', 'All day')\n\t\t\t}\n\t\t\tconst start = this.parseDate(event.start)\n\t\t\tconst hh = String(start.getHours()).padStart(2, '0')\n\t\t\tconst mm = String(start.getMinutes()).padStart(2, '0')\n\t\t\treturn `${hh}:${mm}`\n\t\t},\n\n\t\t/**\n\t\t * Format a per-day agenda group header.\n\t\t *\n\t\t * @param {Date} date the group date.\n\t\t * @return {string} the header label.\n\t\t */\n\t\tformatDayHeader(date) {\n\t\t\tconst month = date.toLocaleString(userLocale(), { month: 'short' })\n\t\t\treturn `${this.weekdayHeaders[date.getDay()]} ${date.getDate()} ${month}`\n\t\t},\n\n\t\t/**\n\t\t * Compute the left-border colour style for an event row.\n\t\t *\n\t\t * @param {object} event the event.\n\t\t * @return {object} the style object (empty when colouring is off).\n\t\t */\n\t\teventStyle(event) {\n\t\t\tif (!this.colorByCalendar) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst color = event.color\n\t\t\tif (typeof color === 'string' && color !== '') {\n\t\t\t\treturn { 'border-left': `3px solid ${color}` }\n\t\t\t}\n\t\t\treturn {}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-widget {\n\tdisplay: flex;\n\tflex-direction: column;\n\twidth: 100%;\n\theight: 100%;\n\toverflow: hidden;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-calendar-widget__header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tpadding: 8px 12px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-calendar-widget__title {\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__modes {\n\tdisplay: flex;\n\tgap: 4px;\n}\n\n.cn-calendar-widget__mode-btn {\n\tborder: 1px solid var(--color-border);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tborder-radius: var(--border-radius);\n\tpadding: 2px 8px;\n\tfont-size: 12px;\n\tcursor: pointer;\n}\n\n.cn-calendar-widget__mode-btn.is-active {\n\tbackground: var(--color-primary-element-light, var(--color-primary-element));\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-calendar-widget__body {\n\tflex: 1;\n\toverflow: auto;\n\tpadding: 8px 12px;\n}\n\n.cn-calendar-widget__state {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tpadding: 16px;\n\ttext-align: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__retry {\n\tpadding: 4px 12px;\n\tborder: 1px solid var(--color-border);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tcursor: pointer;\n\tborder-radius: var(--border-radius);\n}\n\n.cn-calendar-widget__month {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(7, 1fr);\n\tgap: 2px;\n}\n\n.cn-calendar-widget__month-header {\n\tfont-size: 11px;\n\tfont-weight: 600;\n\ttext-align: center;\n\tpadding: 4px 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__month-cell {\n\tmin-height: 60px;\n\tpadding: 2px 4px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 2px;\n\tbackground: var(--color-main-background);\n}\n\n.cn-calendar-widget__month-cell.is-today {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-calendar-widget__month-cell.is-other-month {\n\topacity: 0.45;\n}\n\n.cn-calendar-widget__month-day {\n\tfont-size: 11px;\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__month-events {\n\tlist-style: none;\n\tmargin: 2px 0 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__month-event {\n\tfont-size: 10px;\n\tpadding: 1px 3px;\n\tmargin-bottom: 1px;\n\tborder-radius: 2px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-widget__month-overflow {\n\tfont-size: 10px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__week {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(7, 1fr);\n\tgap: 4px;\n}\n\n.cn-calendar-widget__week-col {\n\tdisplay: flex;\n\tflex-direction: column;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 4px;\n}\n\n.cn-calendar-widget__week-col.is-today {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-calendar-widget__week-day {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tborder-bottom: 1px solid var(--color-border);\n\tpadding-bottom: 2px;\n\tmargin-bottom: 4px;\n}\n\n.cn-calendar-widget__week-name {\n\tfont-size: 10px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__week-num {\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__week-events {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__week-event {\n\tfont-size: 11px;\n\tpadding: 2px 4px;\n\tmargin-bottom: 2px;\n\tbackground: var(--color-background-dark);\n\tborder-radius: 2px;\n}\n\n.cn-calendar-widget__week-time {\n\tfont-weight: 600;\n\tmargin-right: 4px;\n}\n\n.cn-calendar-widget__week-empty {\n\tfont-size: 11px;\n\ttext-align: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__agenda {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__agenda-header {\n\tfont-size: 12px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-top: 8px;\n\tmargin-bottom: 4px;\n}\n\n.cn-calendar-widget__agenda-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 4px 6px;\n\tmargin-bottom: 2px;\n\tbackground: var(--color-background-hover);\n\tborder-radius: 2px;\n}\n\n.cn-calendar-widget__agenda-time {\n\tfont-weight: 600;\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-calendar-widget__agenda-title {\n\tflex: 1;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-widget__agenda-cal {\n\tfont-size: 11px;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnCalendarWidget.vue3.js","sources":["../../../../src/components/CnCalendarWidget/CnCalendarWidget.vue?vue&type=template&id=056be04f&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-calendar-widget\">\n\t\t<!-- `content.showTitle: false` and `content.showViewModes: false` drop\n\t\t the sub-heading and the Month / Week / Agenda buttons, for an agenda\n\t\t placement whose card already names it; without both the header\n\t\t row goes. -->\n\t\t<header v-if=\"showsTitle || showsViewModes\" class=\"cn-calendar-widget__header\">\n\t\t\t<span v-if=\"showsTitle\" class=\"cn-calendar-widget__title\">{{ t('nextcloud-vue', 'Calendar') }}</span>\n\t\t\t<div v-if=\"showsViewModes\" class=\"cn-calendar-widget__modes\">\n\t\t\t\t<button\n\t\t\t\t\tv-for=\"mode in viewModes\"\n\t\t\t\t\t:key=\"mode\"\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-calendar-widget__mode-btn\"\n\t\t\t\t\t:class=\"{ 'is-active': activeMode === mode }\"\n\t\t\t\t\t@click=\"setMode(mode)\">\n\t\t\t\t\t{{ modeLabel(mode) }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</header>\n\n\t\t<div class=\"cn-calendar-widget__body\">\n\t\t\t<div v-if=\"loading\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'Loading calendars…') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"error\" class=\"cn-calendar-widget__state cn-calendar-widget__state--error\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Failed to load events') }}</p>\n\t\t\t\t<button type=\"button\" class=\"cn-calendar-widget__retry\" @click=\"fetchEvents\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Retry') }}\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"!hasSource\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'No calendar available') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"hasNoCalendarChosen\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'No calendar is selected yet. Choose one in this widget\\u2019s settings.') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"events.length === 0 && activeMode === 'agenda'\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ emptyMessage }}\n\t\t\t</div>\n\n\t\t\t<!-- Month view: 7-column grid padded to whole weeks. -->\n\t\t\t<div v-else-if=\"activeMode === 'month'\" class=\"cn-calendar-widget__month\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(weekday, idx) in weekdayHeaders\"\n\t\t\t\t\t:key=\"'wh-' + idx\"\n\t\t\t\t\tclass=\"cn-calendar-widget__month-header\">\n\t\t\t\t\t{{ weekday }}\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"day in monthGrid\"\n\t\t\t\t\t:key=\"day.iso\"\n\t\t\t\t\tclass=\"cn-calendar-widget__month-cell\"\n\t\t\t\t\t:class=\"{ 'is-today': day.isToday, 'is-other-month': day.isOtherMonth }\">\n\t\t\t\t\t<span class=\"cn-calendar-widget__month-day\">{{ day.dayNum }}</span>\n\t\t\t\t\t<ul v-if=\"day.events.length\" class=\"cn-calendar-widget__month-events\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"event in day.events.slice(0, 3)\"\n\t\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__month-event\"\n\t\t\t\t\t\t\t:title=\"event.title\"\n\t\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t\t{{ event.title }}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-if=\"day.events.length > 3\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__month-overflow\">\n\t\t\t\t\t\t\t+{{ day.events.length - 3 }}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<!-- Week view: 7 day columns. -->\n\t\t\t<div v-else-if=\"activeMode === 'week'\" class=\"cn-calendar-widget__week\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"day in weekDays\"\n\t\t\t\t\t:key=\"day.iso\"\n\t\t\t\t\tclass=\"cn-calendar-widget__week-col\"\n\t\t\t\t\t:class=\"{ 'is-today': day.isToday }\">\n\t\t\t\t\t<header class=\"cn-calendar-widget__week-day\">\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-name\">{{ day.weekday }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-num\">{{ day.dayNum }}</span>\n\t\t\t\t\t</header>\n\t\t\t\t\t<ul v-if=\"day.events.length\" class=\"cn-calendar-widget__week-events\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"event in day.events\"\n\t\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__week-event\"\n\t\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-time\">{{ formatTime(event) }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-title\">{{ event.title }}</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<p v-else class=\"cn-calendar-widget__week-empty\">\n\t\t\t\t\t\t—\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<!-- Agenda view: chronological list grouped by day. -->\n\t\t\t<ul v-else class=\"cn-calendar-widget__agenda\">\n\t\t\t\t<template v-for=\"group in agendaGroups\" :key=\"group.iso\">\n\t\t\t\t\t<li class=\"cn-calendar-widget__agenda-header\">\n\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t</li>\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"event in group.events\"\n\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\tclass=\"cn-calendar-widget__agenda-row\"\n\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__agenda-time\">{{ formatTime(event) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__agenda-title\">{{ event.title }}</span>\n\t\t\t\t\t\t<span v-if=\"event.calendarName\" class=\"cn-calendar-widget__agenda-cal\">{{ event.calendarName }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</template>\n\t\t\t</ul>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\n\nconst VIEW_MODES = ['month', 'week', 'agenda']\nconst DEFAULT_DAYS_AHEAD = 14\n\n/**\n * CnCalendarWidget — renders aggregated upcoming events as a chronological,\n * per-day grouped agenda.\n *\n * Data origin (decoupling seam): events are read through a consumer-supplied\n * `dataSource` prop OR the `cnCalendarSource` injection — an object exposing\n * `fetchEvents({ from, to }) => Promise<{ events, failures }>`. The canonical\n * backing is the Nextcloud Calendar app (internal calendars + external ICS\n * feeds) surfaced by the consuming app; this renderer never imports a\n * sibling-app module path nor assumes a backend route. When neither prop nor\n * injection is supplied the widget shows an empty agenda (\"No calendar\n * available\") and performs no request; an empty result simply renders the\n * \"no events\" empty state.\n *\n * No event field is ever rendered via `v-html` — all values flow through Vue\n * interpolation, so a malicious ICS feed cannot inject markup.\n *\n * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n */\nexport default {\n\tname: 'CnCalendarWidget',\n\n\tinject: {\n\t\tcnCalendarSource: {\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content `{viewMode, daysAhead, colorByCalendar,\n\t\t * showTitle, showViewModes}`. `showTitle: false` drops the \"Calendar\"\n\t\t * sub-heading and `showViewModes: false` the Month / Week / Agenda\n\t\t * buttons (both shown by default).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Consumer-supplied data source overriding the `cnCalendarSource`\n\t\t * injection. Must expose `fetchEvents({from, to}) => Promise<{events,\n\t\t * failures}>`. When `null` the injection (then the empty state) is used.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tdataSource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tconst initialMode = VIEW_MODES.includes(this.content && this.content.viewMode)\n\t\t\t? this.content.viewMode\n\t\t\t: 'agenda'\n\t\treturn {\n\t\t\tactiveMode: initialMode,\n\t\t\tevents: [],\n\t\t\tfailures: [],\n\t\t\tloading: false,\n\t\t\terror: null,\n\t\t\ttoday: new Date(),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the \"Calendar\" sub-heading renders (`content.showTitle`).\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-agenda-placement-can-drop-the-calendar-chrome\n\t\t * @return {boolean}\n\t\t */\n\t\tshowsTitle() {\n\t\t\treturn !(this.content && this.content.showTitle === false)\n\t\t},\n\n\t\t/**\n\t\t * Whether the Month / Week / Agenda buttons render\n\t\t * (`content.showViewModes`).\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-agenda-placement-can-drop-the-calendar-chrome\n\t\t * @return {boolean}\n\t\t */\n\t\tshowsViewModes() {\n\t\t\treturn !(this.content && this.content.showViewModes === false)\n\t\t},\n\n\t\t/**\n\t\t * The available view modes.\n\t\t *\n\t\t * @return {string[]} the mode keys.\n\t\t */\n\t\tviewModes() {\n\t\t\treturn VIEW_MODES\n\t\t},\n\n\t\t/**\n\t\t * The active data source (prop wins over injection).\n\t\t *\n\t\t * @return {object|null} the resolved source, or `null`.\n\t\t */\n\t\tsource() {\n\t\t\tconst candidate = this.dataSource || this.cnCalendarSource\n\t\t\treturn candidate && typeof candidate.fetchEvents === 'function' ? candidate : null\n\t\t},\n\n\t\t/**\n\t\t * Whether a usable calendar source is configured.\n\t\t *\n\t\t * @return {boolean} true when a source exists.\n\t\t */\n\t\thasSource() {\n\t\t\treturn this.source !== null\n\t\t},\n\n\t\t/**\n\t\t * The validated look-ahead window in days.\n\t\t *\n\t\t * @return {number} the days-ahead value.\n\t\t */\n\t\tdaysAhead() {\n\t\t\tconst value = parseInt((this.content && this.content.daysAhead) ?? DEFAULT_DAYS_AHEAD, 10)\n\t\t\treturn Number.isFinite(value) && value > 0 ? value : DEFAULT_DAYS_AHEAD\n\t\t},\n\n\t\t/**\n\t\t * Whether events are tinted by their calendar colour.\n\t\t *\n\t\t * @return {boolean} the colorByCalendar flag.\n\t\t */\n\t\tcolorByCalendar() {\n\t\t\treturn !(this.content && this.content.colorByCalendar === false)\n\t\t},\n\n\t\t/**\n\t\t * Whether the widget is configured with no calendar at all.\n\t\t *\n\t\t * The host fetches events for the calendars named in\n\t\t * `content.internalCalendars` / `content.externalIcsUrls`, and it\n\t\t * skips the fetch entirely when both are empty: LaunchPad's\n\t\t * `CalendarWidgetService::getEvents()` guards each branch with\n\t\t * `!== []`, so an unconfigured widget returns zero events AND zero\n\t\t * failures. That is indistinguishable from a genuinely empty week.\n\t\t *\n\t\t * It is not a rare state. The registry's own `defaultContent` for\n\t\t * this widget type is `internalCalendars: []`, so EVERY freshly added\n\t\t * Calendar widget starts here and says \"No events in the next 14\n\t\t * days\" forever, which is a false statement about the user's diary\n\t\t * rather than a prompt to finish configuring it.\n\t\t *\n\t\t * Only claims the state when both lists are present and empty. A host\n\t\t * that drives the widget through `dataSource` alone, with no content\n\t\t * blob, keeps the ordinary empty message.\n\t\t *\n\t\t * @return {boolean} true when no calendar has been chosen.\n\t\t */\n\t\thasNoCalendarChosen() {\n\t\t\tconst c = this.content\n\t\t\tif (!c) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst internal = c.internalCalendars\n\t\t\tconst external = c.externalIcsUrls\n\t\t\tif (!Array.isArray(internal) && !Array.isArray(external)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn (internal || []).length === 0 && (external || []).length === 0\n\t\t},\n\n\t\t/**\n\t\t * The empty-agenda message.\n\t\t *\n\t\t * @return {string} the empty message.\n\t\t */\n\t\temptyMessage() {\n\t\t\treturn t('nextcloud-vue', 'No events in the next {n} days').replace('{n}', String(this.daysAhead))\n\t\t},\n\n\t\t/**\n\t\t * Localised short weekday names (Sun-first).\n\t\t *\n\t\t * @return {string[]} the weekday labels.\n\t\t */\n\t\tweekdayHeaders() {\n\t\t\treturn [\n\t\t\t\tt('nextcloud-vue', 'Sun'),\n\t\t\t\tt('nextcloud-vue', 'Mon'),\n\t\t\t\tt('nextcloud-vue', 'Tue'),\n\t\t\t\tt('nextcloud-vue', 'Wed'),\n\t\t\t\tt('nextcloud-vue', 'Thu'),\n\t\t\t\tt('nextcloud-vue', 'Fri'),\n\t\t\t\tt('nextcloud-vue', 'Sat'),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * The first/last day of the current month (month-view window).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the month window.\n\t\t */\n\t\tmonthRange() {\n\t\t\tconst today = this.today\n\t\t\tconst first = new Date(today.getFullYear(), today.getMonth(), 1)\n\t\t\tconst last = new Date(today.getFullYear(), today.getMonth() + 1, 0)\n\t\t\treturn { from: first, to: last }\n\t\t},\n\n\t\t/**\n\t\t * The Sun–Sat window containing today (week-view window).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the week window.\n\t\t */\n\t\tweekRange() {\n\t\t\tconst today = this.today\n\t\t\tconst start = new Date(today)\n\t\t\tstart.setDate(today.getDate() - today.getDay())\n\t\t\tstart.setHours(0, 0, 0, 0)\n\t\t\tconst end = new Date(start)\n\t\t\tend.setDate(start.getDate() + 6)\n\t\t\tend.setHours(23, 59, 59, 999)\n\t\t\treturn { from: start, to: end }\n\t\t},\n\n\t\t/**\n\t\t * Events bucketed by `YYYY-MM-DD` for grid lookup.\n\t\t *\n\t\t * @return {Record<string, object[]>} iso-date → events.\n\t\t */\n\t\teventsByDay() {\n\t\t\tconst buckets = {}\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst iso = this.toIsoDate(this.parseDate(event.start))\n\t\t\t\tif (!buckets[iso]) {\n\t\t\t\t\tbuckets[iso] = []\n\t\t\t\t}\n\t\t\t\tbuckets[iso].push(event)\n\t\t\t}\n\t\t\treturn buckets\n\t\t},\n\n\t\t/**\n\t\t * The 7-column month grid (leading/trailing days padded to whole weeks).\n\t\t *\n\t\t * @return {Array<object>} grid cells with day metadata + events.\n\t\t */\n\t\tmonthGrid() {\n\t\t\tconst { from, to } = this.monthRange\n\t\t\tconst cells = []\n\t\t\tconst gridStart = new Date(from)\n\t\t\tgridStart.setDate(from.getDate() - from.getDay())\n\t\t\tconst totalDays = Math.ceil((to.getDate() + from.getDay()) / 7) * 7\n\t\t\tfor (let i = 0; i < totalDays; i++) {\n\t\t\t\tconst cellDate = new Date(gridStart)\n\t\t\t\tcellDate.setDate(gridStart.getDate() + i)\n\t\t\t\tconst iso = this.toIsoDate(cellDate)\n\t\t\t\tcells.push({\n\t\t\t\t\tiso,\n\t\t\t\t\tdayNum: cellDate.getDate(),\n\t\t\t\t\tisToday: this.toIsoDate(this.today) === iso,\n\t\t\t\t\tisOtherMonth: cellDate.getMonth() !== from.getMonth(),\n\t\t\t\t\tevents: this.eventsByDay[iso] || [],\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn cells\n\t\t},\n\n\t\t/**\n\t\t * The 7 day-columns for the week view.\n\t\t *\n\t\t * @return {Array<object>} day columns with metadata + events.\n\t\t */\n\t\tweekDays() {\n\t\t\tconst { from } = this.weekRange\n\t\t\tconst out = []\n\t\t\tfor (let i = 0; i < 7; i++) {\n\t\t\t\tconst day = new Date(from)\n\t\t\t\tday.setDate(from.getDate() + i)\n\t\t\t\tconst iso = this.toIsoDate(day)\n\t\t\t\tout.push({\n\t\t\t\t\tiso,\n\t\t\t\t\tweekday: this.weekdayHeaders[day.getDay()],\n\t\t\t\t\tdayNum: day.getDate(),\n\t\t\t\t\tisToday: this.toIsoDate(this.today) === iso,\n\t\t\t\t\tevents: this.eventsByDay[iso] || [],\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * Events grouped into per-day buckets in chronological order.\n\t\t *\n\t\t * @return {Array<{iso: string, label: string, events: object[]}>} the groups.\n\t\t */\n\t\tagendaGroups() {\n\t\t\tconst groups = []\n\t\t\tconst seen = {}\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst date = this.parseDate(event.start)\n\t\t\t\tconst iso = this.toIsoDate(date)\n\t\t\t\tif (!seen[iso]) {\n\t\t\t\t\tseen[iso] = { iso, label: this.formatDayHeader(date), events: [] }\n\t\t\t\t\tgroups.push(seen[iso])\n\t\t\t\t}\n\t\t\t\tseen[iso].events.push(event)\n\t\t\t}\n\t\t\treturn groups\n\t\t},\n\t},\n\n\twatch: {\n\t\tcontent: {\n\t\t\tdeep: true,\n\t\t\thandler() {\n\t\t\t\tthis.fetchEvents()\n\t\t\t},\n\t\t},\n\n\t\t// Month/week/agenda use different fetch windows — refetch on switch.\n\t\tactiveMode() {\n\t\t\tthis.fetchEvents()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.fetchEvents()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Switch the active view mode.\n\t\t *\n\t\t * @param {string} mode the mode to activate.\n\t\t * @return {void}\n\t\t */\n\t\tsetMode(mode) {\n\t\t\tif (VIEW_MODES.includes(mode)) {\n\t\t\t\tthis.activeMode = mode\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The label for a view mode.\n\t\t *\n\t\t * @param {string} mode the mode key.\n\t\t * @return {string} the localised label.\n\t\t */\n\t\tmodeLabel(mode) {\n\t\t\tif (mode === 'month') {\n\t\t\t\treturn t('nextcloud-vue', 'Month')\n\t\t\t}\n\t\t\tif (mode === 'week') {\n\t\t\t\treturn t('nextcloud-vue', 'Week')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Agenda')\n\t\t},\n\n\t\t/**\n\t\t * Compute the fetch window for the active view: the whole month in\n\t\t * month view, the Sun–Sat week in week view, else the daysAhead\n\t\t * look-ahead (agenda).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the window.\n\t\t */\n\t\tcomputeRange() {\n\t\t\tif (this.activeMode === 'month') {\n\t\t\t\tconst { from, to } = this.monthRange\n\t\t\t\tconst start = new Date(from)\n\t\t\t\tstart.setHours(0, 0, 0, 0)\n\t\t\t\tconst end = new Date(to)\n\t\t\t\tend.setHours(23, 59, 59, 999)\n\t\t\t\treturn { from: start, to: end }\n\t\t\t}\n\t\t\tif (this.activeMode === 'week') {\n\t\t\t\treturn this.weekRange\n\t\t\t}\n\t\t\tconst from = new Date(this.today)\n\t\t\tfrom.setHours(0, 0, 0, 0)\n\t\t\tconst to = new Date(from)\n\t\t\tto.setDate(from.getDate() + this.daysAhead)\n\t\t\tto.setHours(23, 59, 59, 999)\n\t\t\treturn { from, to }\n\t\t},\n\n\t\t/**\n\t\t * Load events from the configured source. A missing source or a fetch\n\t\t * failure degrades to the empty / error state without throwing.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchEvents() {\n\t\t\tif (!this.source) {\n\t\t\t\tthis.events = []\n\t\t\t\tthis.failures = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst { from, to } = this.computeRange()\n\t\t\tthis.loading = true\n\t\t\tthis.error = null\n\t\t\ttry {\n\t\t\t\tconst payload = await this.source.fetchEvents({\n\t\t\t\t\tfrom: from.toISOString(),\n\t\t\t\t\tto: to.toISOString(),\n\t\t\t\t}) || {}\n\t\t\t\tthis.events = Array.isArray(payload.events) ? payload.events : []\n\t\t\t\tthis.failures = Array.isArray(payload.failures) ? payload.failures : []\n\t\t\t} catch (err) {\n\t\t\t\tthis.error = err\n\t\t\t\tthis.events = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Parse a date value, falling back to now on invalid input.\n\t\t *\n\t\t * @param {string|number|Date} value the value to parse.\n\t\t * @return {Date} the parsed date.\n\t\t */\n\t\tparseDate(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn new Date()\n\t\t\t}\n\t\t\tconst d = new Date(value)\n\t\t\treturn Number.isNaN(d.getTime()) ? new Date() : d\n\t\t},\n\n\t\t/**\n\t\t * Format a date as `YYYY-MM-DD`.\n\t\t *\n\t\t * @param {Date} date the date.\n\t\t * @return {string} the ISO date string.\n\t\t */\n\t\ttoIsoDate(date) {\n\t\t\tconst yyyy = date.getFullYear()\n\t\t\tconst mm = String(date.getMonth() + 1).padStart(2, '0')\n\t\t\tconst dd = String(date.getDate()).padStart(2, '0')\n\t\t\treturn `${yyyy}-${mm}-${dd}`\n\t\t},\n\n\t\t/**\n\t\t * Format an event's start time (or \"All day\").\n\t\t *\n\t\t * @param {object} event the event.\n\t\t * @return {string} the time label.\n\t\t */\n\t\tformatTime(event) {\n\t\t\tif (event.allDay) {\n\t\t\t\treturn t('nextcloud-vue', 'All day')\n\t\t\t}\n\t\t\tconst start = this.parseDate(event.start)\n\t\t\tconst hh = String(start.getHours()).padStart(2, '0')\n\t\t\tconst mm = String(start.getMinutes()).padStart(2, '0')\n\t\t\treturn `${hh}:${mm}`\n\t\t},\n\n\t\t/**\n\t\t * Format a per-day agenda group header.\n\t\t *\n\t\t * @param {Date} date the group date.\n\t\t * @return {string} the header label.\n\t\t */\n\t\tformatDayHeader(date) {\n\t\t\tconst month = date.toLocaleString(undefined, { month: 'short' })\n\t\t\treturn `${this.weekdayHeaders[date.getDay()]} ${date.getDate()} ${month}`\n\t\t},\n\n\t\t/**\n\t\t * Compute the left-border colour style for an event row.\n\t\t *\n\t\t * @param {object} event the event.\n\t\t * @return {object} the style object (empty when colouring is off).\n\t\t */\n\t\teventStyle(event) {\n\t\t\tif (!this.colorByCalendar) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst color = event.color\n\t\t\tif (typeof color === 'string' && color !== '') {\n\t\t\t\treturn { 'border-left': `3px solid ${color}` }\n\t\t\t}\n\t\t\treturn {}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-widget {\n\tdisplay: flex;\n\tflex-direction: column;\n\twidth: 100%;\n\theight: 100%;\n\toverflow: hidden;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-calendar-widget__header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tpadding: 8px 12px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-calendar-widget__title {\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__modes {\n\tdisplay: flex;\n\tgap: 4px;\n}\n\n.cn-calendar-widget__mode-btn {\n\tborder: 1px solid var(--color-border);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tborder-radius: var(--border-radius);\n\tpadding: 2px 8px;\n\tfont-size: 12px;\n\tcursor: pointer;\n}\n\n.cn-calendar-widget__mode-btn.is-active {\n\tbackground: var(--color-primary-element-light, var(--color-primary-element));\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-calendar-widget__body {\n\tflex: 1;\n\toverflow: auto;\n\tpadding: 8px 12px;\n}\n\n.cn-calendar-widget__state {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tpadding: 16px;\n\ttext-align: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__retry {\n\tpadding: 4px 12px;\n\tborder: 1px solid var(--color-border);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tcursor: pointer;\n\tborder-radius: var(--border-radius);\n}\n\n.cn-calendar-widget__month {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(7, 1fr);\n\tgap: 2px;\n}\n\n.cn-calendar-widget__month-header {\n\tfont-size: 11px;\n\tfont-weight: 600;\n\ttext-align: center;\n\tpadding: 4px 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__month-cell {\n\tmin-height: 60px;\n\tpadding: 2px 4px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 2px;\n\tbackground: var(--color-main-background);\n}\n\n.cn-calendar-widget__month-cell.is-today {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-calendar-widget__month-cell.is-other-month {\n\topacity: 0.45;\n}\n\n.cn-calendar-widget__month-day {\n\tfont-size: 11px;\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__month-events {\n\tlist-style: none;\n\tmargin: 2px 0 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__month-event {\n\tfont-size: 10px;\n\tpadding: 1px 3px;\n\tmargin-bottom: 1px;\n\tborder-radius: 2px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-widget__month-overflow {\n\tfont-size: 10px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__week {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(7, 1fr);\n\tgap: 4px;\n}\n\n.cn-calendar-widget__week-col {\n\tdisplay: flex;\n\tflex-direction: column;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 4px;\n}\n\n.cn-calendar-widget__week-col.is-today {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-calendar-widget__week-day {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tborder-bottom: 1px solid var(--color-border);\n\tpadding-bottom: 2px;\n\tmargin-bottom: 4px;\n}\n\n.cn-calendar-widget__week-name {\n\tfont-size: 10px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__week-num {\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__week-events {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__week-event {\n\tfont-size: 11px;\n\tpadding: 2px 4px;\n\tmargin-bottom: 2px;\n\tbackground: var(--color-background-dark);\n\tborder-radius: 2px;\n}\n\n.cn-calendar-widget__week-time {\n\tfont-weight: 600;\n\tmargin-right: 4px;\n}\n\n.cn-calendar-widget__week-empty {\n\tfont-size: 11px;\n\ttext-align: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__agenda {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__agenda-header {\n\tfont-size: 12px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-top: 8px;\n\tmargin-bottom: 4px;\n}\n\n.cn-calendar-widget__agenda-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 4px 6px;\n\tmargin-bottom: 2px;\n\tbackground: var(--color-background-hover);\n\tborder-radius: 2px;\n}\n\n.cn-calendar-widget__agenda-time {\n\tfont-weight: 600;\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-calendar-widget__agenda-title {\n\tflex: 1;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-widget__agenda-cal {\n\tfont-size: 11px;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_Fragment","_renderList","_normalizeClass","_createElementVNode","_normalizeStyle"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnCalendarWidget.vue3.js","sources":["../../../../src/components/CnCalendarWidget/CnCalendarWidget.vue?vue&type=template&id=766dee4e&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-calendar-widget\">\n\t\t<!-- `content.showTitle: false` and `content.showViewModes: false` drop\n\t\t the sub-heading and the Month / Week / Agenda buttons, for an agenda\n\t\t placement whose card already names it; without both the header\n\t\t row goes. -->\n\t\t<header v-if=\"showsTitle || showsViewModes\" class=\"cn-calendar-widget__header\">\n\t\t\t<span v-if=\"showsTitle\" class=\"cn-calendar-widget__title\">{{ t('nextcloud-vue', 'Calendar') }}</span>\n\t\t\t<div v-if=\"showsViewModes\" class=\"cn-calendar-widget__modes\">\n\t\t\t\t<button\n\t\t\t\t\tv-for=\"mode in viewModes\"\n\t\t\t\t\t:key=\"mode\"\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-calendar-widget__mode-btn\"\n\t\t\t\t\t:class=\"{ 'is-active': activeMode === mode }\"\n\t\t\t\t\t@click=\"setMode(mode)\">\n\t\t\t\t\t{{ modeLabel(mode) }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</header>\n\n\t\t<div class=\"cn-calendar-widget__body\">\n\t\t\t<div v-if=\"loading\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'Loading calendars…') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"error\" class=\"cn-calendar-widget__state cn-calendar-widget__state--error\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Failed to load events') }}</p>\n\t\t\t\t<button type=\"button\" class=\"cn-calendar-widget__retry\" @click=\"fetchEvents\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Retry') }}\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"!hasSource\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'No calendar available') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"hasNoCalendarChosen\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ t('nextcloud-vue', 'No calendar is selected yet. Choose one in this widget\\u2019s settings.') }}\n\t\t\t</div>\n\n\t\t\t<div v-else-if=\"events.length === 0 && activeMode === 'agenda'\" class=\"cn-calendar-widget__state\">\n\t\t\t\t{{ emptyMessage }}\n\t\t\t</div>\n\n\t\t\t<!-- Month view: 7-column grid padded to whole weeks. -->\n\t\t\t<div v-else-if=\"activeMode === 'month'\" class=\"cn-calendar-widget__month\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(weekday, idx) in weekdayHeaders\"\n\t\t\t\t\t:key=\"'wh-' + idx\"\n\t\t\t\t\tclass=\"cn-calendar-widget__month-header\">\n\t\t\t\t\t{{ weekday }}\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"day in monthGrid\"\n\t\t\t\t\t:key=\"day.iso\"\n\t\t\t\t\tclass=\"cn-calendar-widget__month-cell\"\n\t\t\t\t\t:class=\"{ 'is-today': day.isToday, 'is-other-month': day.isOtherMonth }\">\n\t\t\t\t\t<span class=\"cn-calendar-widget__month-day\">{{ day.dayNum }}</span>\n\t\t\t\t\t<ul v-if=\"day.events.length\" class=\"cn-calendar-widget__month-events\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"event in day.events.slice(0, 3)\"\n\t\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__month-event\"\n\t\t\t\t\t\t\t:title=\"event.title\"\n\t\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t\t{{ event.title }}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-if=\"day.events.length > 3\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__month-overflow\">\n\t\t\t\t\t\t\t+{{ day.events.length - 3 }}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<!-- Week view: 7 day columns. -->\n\t\t\t<div v-else-if=\"activeMode === 'week'\" class=\"cn-calendar-widget__week\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"day in weekDays\"\n\t\t\t\t\t:key=\"day.iso\"\n\t\t\t\t\tclass=\"cn-calendar-widget__week-col\"\n\t\t\t\t\t:class=\"{ 'is-today': day.isToday }\">\n\t\t\t\t\t<header class=\"cn-calendar-widget__week-day\">\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-name\">{{ day.weekday }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-num\">{{ day.dayNum }}</span>\n\t\t\t\t\t</header>\n\t\t\t\t\t<ul v-if=\"day.events.length\" class=\"cn-calendar-widget__week-events\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"event in day.events\"\n\t\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\t\tclass=\"cn-calendar-widget__week-event\"\n\t\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-time\">{{ formatTime(event) }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-calendar-widget__week-title\">{{ event.title }}</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<p v-else class=\"cn-calendar-widget__week-empty\">\n\t\t\t\t\t\t—\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<!-- Agenda view: chronological list grouped by day. -->\n\t\t\t<ul v-else class=\"cn-calendar-widget__agenda\">\n\t\t\t\t<template v-for=\"group in agendaGroups\" :key=\"group.iso\">\n\t\t\t\t\t<li class=\"cn-calendar-widget__agenda-header\">\n\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t</li>\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"event in group.events\"\n\t\t\t\t\t\t:key=\"event.uid + '-' + event.start\"\n\t\t\t\t\t\tclass=\"cn-calendar-widget__agenda-row\"\n\t\t\t\t\t\t:style=\"eventStyle(event)\">\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__agenda-time\">{{ formatTime(event) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-widget__agenda-title\">{{ event.title }}</span>\n\t\t\t\t\t\t<span v-if=\"event.calendarName\" class=\"cn-calendar-widget__agenda-cal\">{{ event.calendarName }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</template>\n\t\t\t</ul>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { userLocale } from '../../utils/userLocale.js'\n\nconst VIEW_MODES = ['month', 'week', 'agenda']\nconst DEFAULT_DAYS_AHEAD = 14\n\n/**\n * CnCalendarWidget — renders aggregated upcoming events as a chronological,\n * per-day grouped agenda.\n *\n * Data origin (decoupling seam): events are read through a consumer-supplied\n * `dataSource` prop OR the `cnCalendarSource` injection — an object exposing\n * `fetchEvents({ from, to }) => Promise<{ events, failures }>`. The canonical\n * backing is the Nextcloud Calendar app (internal calendars + external ICS\n * feeds) surfaced by the consuming app; this renderer never imports a\n * sibling-app module path nor assumes a backend route. When neither prop nor\n * injection is supplied the widget shows an empty agenda (\"No calendar\n * available\") and performs no request; an empty result simply renders the\n * \"no events\" empty state.\n *\n * No event field is ever rendered via `v-html` — all values flow through Vue\n * interpolation, so a malicious ICS feed cannot inject markup.\n *\n * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n */\nexport default {\n\tname: 'CnCalendarWidget',\n\n\tinject: {\n\t\tcnCalendarSource: {\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content `{viewMode, daysAhead, colorByCalendar,\n\t\t * showTitle, showViewModes}`. `showTitle: false` drops the \"Calendar\"\n\t\t * sub-heading and `showViewModes: false` the Month / Week / Agenda\n\t\t * buttons (both shown by default).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Consumer-supplied data source overriding the `cnCalendarSource`\n\t\t * injection. Must expose `fetchEvents({from, to}) => Promise<{events,\n\t\t * failures}>`. When `null` the injection (then the empty state) is used.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tdataSource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tconst initialMode = VIEW_MODES.includes(this.content && this.content.viewMode)\n\t\t\t? this.content.viewMode\n\t\t\t: 'agenda'\n\t\treturn {\n\t\t\tactiveMode: initialMode,\n\t\t\tevents: [],\n\t\t\tfailures: [],\n\t\t\tloading: false,\n\t\t\terror: null,\n\t\t\ttoday: new Date(),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the \"Calendar\" sub-heading renders (`content.showTitle`).\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-agenda-placement-can-drop-the-calendar-chrome\n\t\t * @return {boolean}\n\t\t */\n\t\tshowsTitle() {\n\t\t\treturn !(this.content && this.content.showTitle === false)\n\t\t},\n\n\t\t/**\n\t\t * Whether the Month / Week / Agenda buttons render\n\t\t * (`content.showViewModes`).\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-agenda-placement-can-drop-the-calendar-chrome\n\t\t * @return {boolean}\n\t\t */\n\t\tshowsViewModes() {\n\t\t\treturn !(this.content && this.content.showViewModes === false)\n\t\t},\n\n\t\t/**\n\t\t * The available view modes.\n\t\t *\n\t\t * @return {string[]} the mode keys.\n\t\t */\n\t\tviewModes() {\n\t\t\treturn VIEW_MODES\n\t\t},\n\n\t\t/**\n\t\t * The active data source (prop wins over injection).\n\t\t *\n\t\t * @return {object|null} the resolved source, or `null`.\n\t\t */\n\t\tsource() {\n\t\t\tconst candidate = this.dataSource || this.cnCalendarSource\n\t\t\treturn candidate && typeof candidate.fetchEvents === 'function' ? candidate : null\n\t\t},\n\n\t\t/**\n\t\t * Whether a usable calendar source is configured.\n\t\t *\n\t\t * @return {boolean} true when a source exists.\n\t\t */\n\t\thasSource() {\n\t\t\treturn this.source !== null\n\t\t},\n\n\t\t/**\n\t\t * The validated look-ahead window in days.\n\t\t *\n\t\t * @return {number} the days-ahead value.\n\t\t */\n\t\tdaysAhead() {\n\t\t\tconst value = parseInt((this.content && this.content.daysAhead) ?? DEFAULT_DAYS_AHEAD, 10)\n\t\t\treturn Number.isFinite(value) && value > 0 ? value : DEFAULT_DAYS_AHEAD\n\t\t},\n\n\t\t/**\n\t\t * Whether events are tinted by their calendar colour.\n\t\t *\n\t\t * @return {boolean} the colorByCalendar flag.\n\t\t */\n\t\tcolorByCalendar() {\n\t\t\treturn !(this.content && this.content.colorByCalendar === false)\n\t\t},\n\n\t\t/**\n\t\t * Whether the widget is configured with no calendar at all.\n\t\t *\n\t\t * The host fetches events for the calendars named in\n\t\t * `content.internalCalendars` / `content.externalIcsUrls`, and it\n\t\t * skips the fetch entirely when both are empty: LaunchPad's\n\t\t * `CalendarWidgetService::getEvents()` guards each branch with\n\t\t * `!== []`, so an unconfigured widget returns zero events AND zero\n\t\t * failures. That is indistinguishable from a genuinely empty week.\n\t\t *\n\t\t * It is not a rare state. The registry's own `defaultContent` for\n\t\t * this widget type is `internalCalendars: []`, so EVERY freshly added\n\t\t * Calendar widget starts here and says \"No events in the next 14\n\t\t * days\" forever, which is a false statement about the user's diary\n\t\t * rather than a prompt to finish configuring it.\n\t\t *\n\t\t * Only claims the state when both lists are present and empty. A host\n\t\t * that drives the widget through `dataSource` alone, with no content\n\t\t * blob, keeps the ordinary empty message.\n\t\t *\n\t\t * @return {boolean} true when no calendar has been chosen.\n\t\t */\n\t\thasNoCalendarChosen() {\n\t\t\tconst c = this.content\n\t\t\tif (!c) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst internal = c.internalCalendars\n\t\t\tconst external = c.externalIcsUrls\n\t\t\tif (!Array.isArray(internal) && !Array.isArray(external)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn (internal || []).length === 0 && (external || []).length === 0\n\t\t},\n\n\t\t/**\n\t\t * The empty-agenda message.\n\t\t *\n\t\t * @return {string} the empty message.\n\t\t */\n\t\temptyMessage() {\n\t\t\treturn t('nextcloud-vue', 'No events in the next {n} days').replace('{n}', String(this.daysAhead))\n\t\t},\n\n\t\t/**\n\t\t * Localised short weekday names (Sun-first).\n\t\t *\n\t\t * @return {string[]} the weekday labels.\n\t\t */\n\t\tweekdayHeaders() {\n\t\t\treturn [\n\t\t\t\tt('nextcloud-vue', 'Sun'),\n\t\t\t\tt('nextcloud-vue', 'Mon'),\n\t\t\t\tt('nextcloud-vue', 'Tue'),\n\t\t\t\tt('nextcloud-vue', 'Wed'),\n\t\t\t\tt('nextcloud-vue', 'Thu'),\n\t\t\t\tt('nextcloud-vue', 'Fri'),\n\t\t\t\tt('nextcloud-vue', 'Sat'),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * The first/last day of the current month (month-view window).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the month window.\n\t\t */\n\t\tmonthRange() {\n\t\t\tconst today = this.today\n\t\t\tconst first = new Date(today.getFullYear(), today.getMonth(), 1)\n\t\t\tconst last = new Date(today.getFullYear(), today.getMonth() + 1, 0)\n\t\t\treturn { from: first, to: last }\n\t\t},\n\n\t\t/**\n\t\t * The Sun–Sat window containing today (week-view window).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the week window.\n\t\t */\n\t\tweekRange() {\n\t\t\tconst today = this.today\n\t\t\tconst start = new Date(today)\n\t\t\tstart.setDate(today.getDate() - today.getDay())\n\t\t\tstart.setHours(0, 0, 0, 0)\n\t\t\tconst end = new Date(start)\n\t\t\tend.setDate(start.getDate() + 6)\n\t\t\tend.setHours(23, 59, 59, 999)\n\t\t\treturn { from: start, to: end }\n\t\t},\n\n\t\t/**\n\t\t * Events bucketed by `YYYY-MM-DD` for grid lookup.\n\t\t *\n\t\t * @return {Record<string, object[]>} iso-date → events.\n\t\t */\n\t\teventsByDay() {\n\t\t\tconst buckets = {}\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst iso = this.toIsoDate(this.parseDate(event.start))\n\t\t\t\tif (!buckets[iso]) {\n\t\t\t\t\tbuckets[iso] = []\n\t\t\t\t}\n\t\t\t\tbuckets[iso].push(event)\n\t\t\t}\n\t\t\treturn buckets\n\t\t},\n\n\t\t/**\n\t\t * The 7-column month grid (leading/trailing days padded to whole weeks).\n\t\t *\n\t\t * @return {Array<object>} grid cells with day metadata + events.\n\t\t */\n\t\tmonthGrid() {\n\t\t\tconst { from, to } = this.monthRange\n\t\t\tconst cells = []\n\t\t\tconst gridStart = new Date(from)\n\t\t\tgridStart.setDate(from.getDate() - from.getDay())\n\t\t\tconst totalDays = Math.ceil((to.getDate() + from.getDay()) / 7) * 7\n\t\t\tfor (let i = 0; i < totalDays; i++) {\n\t\t\t\tconst cellDate = new Date(gridStart)\n\t\t\t\tcellDate.setDate(gridStart.getDate() + i)\n\t\t\t\tconst iso = this.toIsoDate(cellDate)\n\t\t\t\tcells.push({\n\t\t\t\t\tiso,\n\t\t\t\t\tdayNum: cellDate.getDate(),\n\t\t\t\t\tisToday: this.toIsoDate(this.today) === iso,\n\t\t\t\t\tisOtherMonth: cellDate.getMonth() !== from.getMonth(),\n\t\t\t\t\tevents: this.eventsByDay[iso] || [],\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn cells\n\t\t},\n\n\t\t/**\n\t\t * The 7 day-columns for the week view.\n\t\t *\n\t\t * @return {Array<object>} day columns with metadata + events.\n\t\t */\n\t\tweekDays() {\n\t\t\tconst { from } = this.weekRange\n\t\t\tconst out = []\n\t\t\tfor (let i = 0; i < 7; i++) {\n\t\t\t\tconst day = new Date(from)\n\t\t\t\tday.setDate(from.getDate() + i)\n\t\t\t\tconst iso = this.toIsoDate(day)\n\t\t\t\tout.push({\n\t\t\t\t\tiso,\n\t\t\t\t\tweekday: this.weekdayHeaders[day.getDay()],\n\t\t\t\t\tdayNum: day.getDate(),\n\t\t\t\t\tisToday: this.toIsoDate(this.today) === iso,\n\t\t\t\t\tevents: this.eventsByDay[iso] || [],\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * Events grouped into per-day buckets in chronological order.\n\t\t *\n\t\t * @return {Array<{iso: string, label: string, events: object[]}>} the groups.\n\t\t */\n\t\tagendaGroups() {\n\t\t\tconst groups = []\n\t\t\tconst seen = {}\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst date = this.parseDate(event.start)\n\t\t\t\tconst iso = this.toIsoDate(date)\n\t\t\t\tif (!seen[iso]) {\n\t\t\t\t\tseen[iso] = { iso, label: this.formatDayHeader(date), events: [] }\n\t\t\t\t\tgroups.push(seen[iso])\n\t\t\t\t}\n\t\t\t\tseen[iso].events.push(event)\n\t\t\t}\n\t\t\treturn groups\n\t\t},\n\t},\n\n\twatch: {\n\t\tcontent: {\n\t\t\tdeep: true,\n\t\t\thandler() {\n\t\t\t\tthis.fetchEvents()\n\t\t\t},\n\t\t},\n\n\t\t// Month/week/agenda use different fetch windows — refetch on switch.\n\t\tactiveMode() {\n\t\t\tthis.fetchEvents()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.fetchEvents()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Switch the active view mode.\n\t\t *\n\t\t * @param {string} mode the mode to activate.\n\t\t * @return {void}\n\t\t */\n\t\tsetMode(mode) {\n\t\t\tif (VIEW_MODES.includes(mode)) {\n\t\t\t\tthis.activeMode = mode\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The label for a view mode.\n\t\t *\n\t\t * @param {string} mode the mode key.\n\t\t * @return {string} the localised label.\n\t\t */\n\t\tmodeLabel(mode) {\n\t\t\tif (mode === 'month') {\n\t\t\t\treturn t('nextcloud-vue', 'Month')\n\t\t\t}\n\t\t\tif (mode === 'week') {\n\t\t\t\treturn t('nextcloud-vue', 'Week')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Agenda')\n\t\t},\n\n\t\t/**\n\t\t * Compute the fetch window for the active view: the whole month in\n\t\t * month view, the Sun–Sat week in week view, else the daysAhead\n\t\t * look-ahead (agenda).\n\t\t *\n\t\t * @return {{from: Date, to: Date}} the window.\n\t\t */\n\t\tcomputeRange() {\n\t\t\tif (this.activeMode === 'month') {\n\t\t\t\tconst { from, to } = this.monthRange\n\t\t\t\tconst start = new Date(from)\n\t\t\t\tstart.setHours(0, 0, 0, 0)\n\t\t\t\tconst end = new Date(to)\n\t\t\t\tend.setHours(23, 59, 59, 999)\n\t\t\t\treturn { from: start, to: end }\n\t\t\t}\n\t\t\tif (this.activeMode === 'week') {\n\t\t\t\treturn this.weekRange\n\t\t\t}\n\t\t\tconst from = new Date(this.today)\n\t\t\tfrom.setHours(0, 0, 0, 0)\n\t\t\tconst to = new Date(from)\n\t\t\tto.setDate(from.getDate() + this.daysAhead)\n\t\t\tto.setHours(23, 59, 59, 999)\n\t\t\treturn { from, to }\n\t\t},\n\n\t\t/**\n\t\t * Load events from the configured source. A missing source or a fetch\n\t\t * failure degrades to the empty / error state without throwing.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchEvents() {\n\t\t\tif (!this.source) {\n\t\t\t\tthis.events = []\n\t\t\t\tthis.failures = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst { from, to } = this.computeRange()\n\t\t\tthis.loading = true\n\t\t\tthis.error = null\n\t\t\ttry {\n\t\t\t\tconst payload = await this.source.fetchEvents({\n\t\t\t\t\tfrom: from.toISOString(),\n\t\t\t\t\tto: to.toISOString(),\n\t\t\t\t}) || {}\n\t\t\t\tthis.events = Array.isArray(payload.events) ? payload.events : []\n\t\t\t\tthis.failures = Array.isArray(payload.failures) ? payload.failures : []\n\t\t\t} catch (err) {\n\t\t\t\tthis.error = err\n\t\t\t\tthis.events = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Parse a date value, falling back to now on invalid input.\n\t\t *\n\t\t * @param {string|number|Date} value the value to parse.\n\t\t * @return {Date} the parsed date.\n\t\t */\n\t\tparseDate(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn new Date()\n\t\t\t}\n\t\t\tconst d = new Date(value)\n\t\t\treturn Number.isNaN(d.getTime()) ? new Date() : d\n\t\t},\n\n\t\t/**\n\t\t * Format a date as `YYYY-MM-DD`.\n\t\t *\n\t\t * @param {Date} date the date.\n\t\t * @return {string} the ISO date string.\n\t\t */\n\t\ttoIsoDate(date) {\n\t\t\tconst yyyy = date.getFullYear()\n\t\t\tconst mm = String(date.getMonth() + 1).padStart(2, '0')\n\t\t\tconst dd = String(date.getDate()).padStart(2, '0')\n\t\t\treturn `${yyyy}-${mm}-${dd}`\n\t\t},\n\n\t\t/**\n\t\t * Format an event's start time (or \"All day\").\n\t\t *\n\t\t * @param {object} event the event.\n\t\t * @return {string} the time label.\n\t\t */\n\t\tformatTime(event) {\n\t\t\tif (event.allDay) {\n\t\t\t\treturn t('nextcloud-vue', 'All day')\n\t\t\t}\n\t\t\tconst start = this.parseDate(event.start)\n\t\t\tconst hh = String(start.getHours()).padStart(2, '0')\n\t\t\tconst mm = String(start.getMinutes()).padStart(2, '0')\n\t\t\treturn `${hh}:${mm}`\n\t\t},\n\n\t\t/**\n\t\t * Format a per-day agenda group header.\n\t\t *\n\t\t * @param {Date} date the group date.\n\t\t * @return {string} the header label.\n\t\t */\n\t\tformatDayHeader(date) {\n\t\t\tconst month = date.toLocaleString(userLocale(), { month: 'short' })\n\t\t\treturn `${this.weekdayHeaders[date.getDay()]} ${date.getDate()} ${month}`\n\t\t},\n\n\t\t/**\n\t\t * Compute the left-border colour style for an event row.\n\t\t *\n\t\t * @param {object} event the event.\n\t\t * @return {object} the style object (empty when colouring is off).\n\t\t */\n\t\teventStyle(event) {\n\t\t\tif (!this.colorByCalendar) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst color = event.color\n\t\t\tif (typeof color === 'string' && color !== '') {\n\t\t\t\treturn { 'border-left': `3px solid ${color}` }\n\t\t\t}\n\t\t\treturn {}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-widget {\n\tdisplay: flex;\n\tflex-direction: column;\n\twidth: 100%;\n\theight: 100%;\n\toverflow: hidden;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-calendar-widget__header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tpadding: 8px 12px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-calendar-widget__title {\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__modes {\n\tdisplay: flex;\n\tgap: 4px;\n}\n\n.cn-calendar-widget__mode-btn {\n\tborder: 1px solid var(--color-border);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tborder-radius: var(--border-radius);\n\tpadding: 2px 8px;\n\tfont-size: 12px;\n\tcursor: pointer;\n}\n\n.cn-calendar-widget__mode-btn.is-active {\n\tbackground: var(--color-primary-element-light, var(--color-primary-element));\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-calendar-widget__body {\n\tflex: 1;\n\toverflow: auto;\n\tpadding: 8px 12px;\n}\n\n.cn-calendar-widget__state {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tpadding: 16px;\n\ttext-align: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__retry {\n\tpadding: 4px 12px;\n\tborder: 1px solid var(--color-border);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tcursor: pointer;\n\tborder-radius: var(--border-radius);\n}\n\n.cn-calendar-widget__month {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(7, 1fr);\n\tgap: 2px;\n}\n\n.cn-calendar-widget__month-header {\n\tfont-size: 11px;\n\tfont-weight: 600;\n\ttext-align: center;\n\tpadding: 4px 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__month-cell {\n\tmin-height: 60px;\n\tpadding: 2px 4px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 2px;\n\tbackground: var(--color-main-background);\n}\n\n.cn-calendar-widget__month-cell.is-today {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-calendar-widget__month-cell.is-other-month {\n\topacity: 0.45;\n}\n\n.cn-calendar-widget__month-day {\n\tfont-size: 11px;\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__month-events {\n\tlist-style: none;\n\tmargin: 2px 0 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__month-event {\n\tfont-size: 10px;\n\tpadding: 1px 3px;\n\tmargin-bottom: 1px;\n\tborder-radius: 2px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-widget__month-overflow {\n\tfont-size: 10px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__week {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(7, 1fr);\n\tgap: 4px;\n}\n\n.cn-calendar-widget__week-col {\n\tdisplay: flex;\n\tflex-direction: column;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 4px;\n}\n\n.cn-calendar-widget__week-col.is-today {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-calendar-widget__week-day {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tborder-bottom: 1px solid var(--color-border);\n\tpadding-bottom: 2px;\n\tmargin-bottom: 4px;\n}\n\n.cn-calendar-widget__week-name {\n\tfont-size: 10px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__week-num {\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-calendar-widget__week-events {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__week-event {\n\tfont-size: 11px;\n\tpadding: 2px 4px;\n\tmargin-bottom: 2px;\n\tbackground: var(--color-background-dark);\n\tborder-radius: 2px;\n}\n\n.cn-calendar-widget__week-time {\n\tfont-weight: 600;\n\tmargin-right: 4px;\n}\n\n.cn-calendar-widget__week-empty {\n\tfont-size: 11px;\n\ttext-align: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-widget__agenda {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-calendar-widget__agenda-header {\n\tfont-size: 12px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-top: 8px;\n\tmargin-bottom: 4px;\n}\n\n.cn-calendar-widget__agenda-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 4px 6px;\n\tmargin-bottom: 2px;\n\tbackground: var(--color-background-hover);\n\tborder-radius: 2px;\n}\n\n.cn-calendar-widget__agenda-time {\n\tfont-weight: 600;\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-calendar-widget__agenda-title {\n\tflex: 1;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-widget__agenda-cal {\n\tfont-size: 11px;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_Fragment","_renderList","_normalizeClass","_createElementVNode","_normalizeStyle"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnChartWidget.vue2.js';
|
|
|
2
2
|
import { render } from './CnChartWidget.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-c850416a";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -9,6 +9,7 @@ import { resolveObjectOpType } from '../../utils/actionsDispatcher.js';
|
|
|
9
9
|
import { resolveObjectTokenContext } from '../../utils/detailObjectContext.js';
|
|
10
10
|
import { safeCurrencyCode } from '../../utils/formatMetric.js';
|
|
11
11
|
import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js';
|
|
12
|
+
import { userLocale } from '../../utils/userLocale.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Nice-ceiling ladder. Deliberately fine-grained: a coarse one (1 / 2 / 5 / 10)
|
|
@@ -1567,16 +1568,16 @@ var script = {
|
|
|
1567
1568
|
const year = d.getFullYear() === thisYear ? {} : { year: 'numeric' };
|
|
1568
1569
|
switch (String(interval || '').toLowerCase()) {
|
|
1569
1570
|
case 'minute':
|
|
1570
|
-
return d.toLocaleString(
|
|
1571
|
+
return d.toLocaleString(userLocale(), { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })
|
|
1571
1572
|
case 'hour':
|
|
1572
|
-
return d.toLocaleString(
|
|
1573
|
+
return d.toLocaleString(userLocale(), { month: 'short', day: 'numeric', hour: '2-digit' })
|
|
1573
1574
|
case 'day':
|
|
1574
1575
|
case 'week':
|
|
1575
|
-
return d.toLocaleDateString(
|
|
1576
|
+
return d.toLocaleDateString(userLocale(), { day: 'numeric', month: 'short', ...year })
|
|
1576
1577
|
case 'year':
|
|
1577
|
-
return d.toLocaleDateString(
|
|
1578
|
+
return d.toLocaleDateString(userLocale(), { year: 'numeric' })
|
|
1578
1579
|
default:
|
|
1579
|
-
return d.toLocaleDateString(
|
|
1580
|
+
return d.toLocaleDateString(userLocale(), { month: 'short', year: '2-digit' })
|
|
1580
1581
|
}
|
|
1581
1582
|
},
|
|
1582
1583
|
|