149 lines
5 KiB
QML
149 lines
5 KiB
QML
import QtQuick
|
|
import "../services" as S
|
|
import NovaStats as NS
|
|
|
|
Column {
|
|
id: root
|
|
|
|
required property color accentColor
|
|
required property date currentDate
|
|
|
|
readonly property var _locale: Qt.locale()
|
|
readonly property int _year: currentDate.getFullYear()
|
|
readonly property int _month: currentDate.getMonth()
|
|
readonly property int _day: currentDate.getDate()
|
|
readonly property int _firstDayOfWeek: _locale.firstDayOfWeek
|
|
|
|
// Week number (ISO 8601)
|
|
function _weekNumber(d) {
|
|
const tmp = new Date(d.getTime());
|
|
tmp.setHours(0, 0, 0, 0);
|
|
tmp.setDate(tmp.getDate() + 3 - (tmp.getDay() + 6) % 7);
|
|
const jan4 = new Date(tmp.getFullYear(), 0, 4);
|
|
return 1 + Math.round(((tmp.getTime() - jan4.getTime()) / 86400000 - 3 + (jan4.getDay() + 6) % 7) / 7);
|
|
}
|
|
|
|
// Build 6x7 grid of day numbers for the current month view
|
|
readonly property var _calendarDays: {
|
|
const first = new Date(_year, _month, 1);
|
|
const firstDow = first.getDay();
|
|
// Offset: how many days from previous month to show
|
|
let offset = (firstDow - _firstDayOfWeek + 7) % 7;
|
|
const startDate = new Date(_year, _month, 1 - offset);
|
|
const days = [];
|
|
for (let i = 0; i < 42; i++) {
|
|
const d = new Date(startDate.getTime() + i * 86400000);
|
|
days.push({
|
|
day: d.getDate(),
|
|
month: d.getMonth(),
|
|
year: d.getFullYear(),
|
|
isCurrentMonth: d.getMonth() === _month,
|
|
isToday: d.getDate() === _day && d.getMonth() === _month && d.getFullYear() === _year
|
|
});
|
|
}
|
|
return days;
|
|
}
|
|
|
|
// Weekday header names (short, locale-aware)
|
|
readonly property var _dayNames: {
|
|
const names = [];
|
|
for (let i = 0; i < 7; i++) {
|
|
const idx = (_firstDayOfWeek + i) % 7;
|
|
names.push(_locale.dayName(idx, Locale.NarrowFormat));
|
|
}
|
|
return names;
|
|
}
|
|
|
|
// Date header
|
|
Item {
|
|
width: parent.width
|
|
height: 28
|
|
|
|
Text {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 12
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: root._locale.standaloneMonthName(root._month, Locale.LongFormat) + " " + root._year
|
|
color: root.accentColor
|
|
font.pixelSize: NS.ThemeService.fontSize
|
|
font.family: NS.ThemeService.fontFamily
|
|
font.bold: true
|
|
}
|
|
|
|
Text {
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 12
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "W" + root._weekNumber(root.currentDate)
|
|
color: NS.ThemeService.base04
|
|
font.pixelSize: NS.ThemeService.fontSize - 2
|
|
font.family: NS.ThemeService.fontFamily
|
|
}
|
|
}
|
|
|
|
// Day-of-week headers
|
|
Row {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
Repeater {
|
|
model: root._dayNames
|
|
Text {
|
|
required property string modelData
|
|
width: Math.floor((root.width - 24) / 7)
|
|
height: 18
|
|
horizontalAlignment: Text.AlignHCenter
|
|
text: modelData
|
|
color: NS.ThemeService.base04
|
|
font.pixelSize: NS.ThemeService.fontSize - 3
|
|
font.family: NS.ThemeService.fontFamily
|
|
font.bold: true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calendar grid (6 rows x 7 cols)
|
|
Grid {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
columns: 7
|
|
Repeater {
|
|
model: root._calendarDays
|
|
Item {
|
|
required property var modelData
|
|
width: Math.floor((root.width - 24) / 7)
|
|
height: Math.floor((root.width - 24) / 7)
|
|
|
|
Rectangle {
|
|
anchors.centerIn: parent
|
|
width: Math.min(parent.width, parent.height) - 4
|
|
height: width
|
|
radius: width / 2
|
|
color: modelData.isToday ? root.accentColor : "transparent"
|
|
}
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: modelData.day
|
|
color: modelData.isToday ? NS.ThemeService.base00 : modelData.isCurrentMonth ? NS.ThemeService.base05 : NS.ThemeService.base03
|
|
font.pixelSize: NS.ThemeService.fontSize - 1
|
|
font.family: NS.ThemeService.fontFamily
|
|
font.bold: modelData.isToday
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Full date line
|
|
Text {
|
|
width: parent.width - 24
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
horizontalAlignment: Text.AlignHCenter
|
|
text: root.currentDate.toLocaleDateString(root._locale, Locale.LongFormat)
|
|
color: NS.ThemeService.base04
|
|
font.pixelSize: NS.ThemeService.fontSize - 2
|
|
font.family: NS.ThemeService.fontFamily
|
|
}
|
|
|
|
Item {
|
|
width: 1
|
|
height: 4
|
|
}
|
|
}
|