import "root:/widgets" import "root:/services" import "root:/config" import QtQuick Column { id: root spacing: Appearance.spacing.normal width: 250 property date currentDate: new Date() property bool isCurrentMonth: true // Time and Date header Column { anchors.horizontalCenter: parent.horizontalCenter spacing: Appearance.spacing.small StyledText { anchors.horizontalCenter: parent.horizontalCenter text: Qt.formatDateTime(new Date(), "HH:mm:ss") font.pointSize: Appearance.font.size.larger font.family: Appearance.font.family.mono } Row { anchors.horizontalCenter: parent.horizontalCenter spacing: Appearance.spacing.small StyledText { text: Qt.formatDateTime(root.currentDate, "yyyy") font.pointSize: Appearance.font.size.large font.weight: 700 } Row { spacing: Appearance.spacing.small MaterialIcon { text: "chevron_left" color: Colours.palette.m3onSurfaceVariant MouseArea { anchors.fill: parent onClicked: { monthView.decrementCurrentIndex() } } } StyledText { text: Qt.formatDateTime(root.currentDate, "MMMM") font.pointSize: Appearance.font.size.large font.weight: 400 color: Colours.palette.m3onSurfaceVariant } MaterialIcon { text: "chevron_right" color: Colours.palette.m3onSurfaceVariant MouseArea { anchors.fill: parent onClicked: { monthView.incrementCurrentIndex() } } } } } } // Calendar grid Item { width: 250 height: 200 anchors.horizontalCenter: parent.horizontalCenter clip: true ListView { id: monthView anchors.fill: parent orientation: ListView.Horizontal snapMode: ListView.SnapOneItem highlightRangeMode: ListView.StrictlyEnforceRange highlightMoveDuration: Appearance.anim.durations.normal highlightMoveVelocity: -1 highlightResizeDuration: Appearance.anim.durations.normal highlightResizeVelocity: -1 model: 12 // Show all months currentIndex: new Date().getMonth() delegate: Grid { width: monthView.width height: monthView.height columns: 7 spacing: Appearance.spacing.small // Day headers Repeater { model: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"] StyledText { width: 30 horizontalAlignment: Text.AlignHCenter text: modelData font.pointSize: Appearance.font.size.small color: Colours.palette.m3onSurfaceVariant } } // Calendar days Repeater { model: { const firstDay = new Date(new Date().getFullYear(), monthView.currentIndex, 1); const lastDay = new Date(new Date().getFullYear(), monthView.currentIndex + 1, 0); const daysInMonth = lastDay.getDate(); const firstDayOfWeek = firstDay.getDay() || 7; // Convert Sunday (0) to 7 const today = new Date(); let days = []; // Add empty cells for days before the first of the month for (let i = 1; i < firstDayOfWeek; i++) { days.push({ day: "", isCurrentMonth: false }); } // Add days of the current month for (let i = 1; i <= daysInMonth; i++) { const isToday = i === today.getDate() && monthView.currentIndex === today.getMonth() && new Date().getFullYear() === today.getFullYear(); days.push({ day: i, isCurrentMonth: true, isToday: isToday }); } return days; } Rectangle { width: 30 height: 30 radius: Appearance.rounding.full color: modelData.isToday ? Colours.palette.m3primary : "transparent" StyledText { anchors.centerIn: parent text: modelData.day font.pointSize: Appearance.font.size.small color: modelData.isToday ? Colours.palette.m3onPrimary : modelData.isCurrentMonth ? Colours.palette.m3onSurface : Colours.palette.m3outline } } } } onCurrentIndexChanged: { root.currentDate = new Date(new Date().getFullYear(), currentIndex, 1) root.isCurrentMonth = currentIndex === new Date().getMonth() } } } // Reset to current month when popout closes Connections { target: root.parent function onVisibleChanged() { if (!root.parent.visible) { monthView.currentIndex = new Date().getMonth() root.currentDate = new Date() root.isCurrentMonth = true } } } }