import QtQuick import QtQuick.Controls import QtQuick.Layouts import Flora Item { id: cal signal daySelected(string dateStr) property date currentMonth: new Date() readonly property int year: currentMonth.getFullYear() readonly property int month: currentMonth.getMonth() function fmtDate(d) { function pad(n) { return n < 10 ? "0" + n : "" + n } return d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) } function gridModel() { const first = new Date(year, month, 1) const dowSun0 = first.getDay() const startDow = (dowSun0 + 6) % 7 const daysInMonth = new Date(year, month + 1, 0).getDate() const list = [] for (let i = 0; i < startDow; ++i) list.push(0) for (let d = 1; d <= daysInMonth; ++d) list.push(d) while (list.length % 7 !== 0) list.push(0) while (list.length < 42) list.push(0) return list } ColumnLayout { anchors.fill: parent spacing: 4 RowLayout { Layout.fillWidth: true spacing: 4 // Кнопка Назад — круглая, без прозрачности ToolButton { text: "‹" font.pixelSize: 16 font.bold: true implicitWidth: 32 implicitHeight: 32 Layout.preferredWidth: 32 Layout.preferredHeight: 32 Layout.alignment: Qt.AlignVCenter background: Rectangle { color: parent.pressed ? Theme.surfaceSelected : (parent.hovered ? Theme.surfaceHover : Theme.surface) border.color: parent.pressed ? Theme.accent : (parent.hovered ? Theme.borderStrong : Theme.border) radius: 16 } contentItem: Label { text: parent.text font: parent.font color: Theme.text horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } onClicked: cal.currentMonth = new Date(year, month - 1, 1) } Label { Layout.fillWidth: true Layout.minimumWidth: 80 Layout.preferredWidth: 120 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.bold: true color: Theme.text font.pixelSize: 12 elide: Text.ElideRight maximumLineCount: 1 wrapMode: Text.NoWrap text: Qt.locale().monthName(month, Locale.LongFormat) + " " + year } ToolButton { text: "›" font.pixelSize: 16 font.bold: true implicitWidth: 32 implicitHeight: 32 Layout.preferredWidth: 32 Layout.preferredHeight: 32 Layout.alignment: Qt.AlignVCenter background: Rectangle { color: parent.pressed ? Theme.surfaceSelected : (parent.hovered ? Theme.surfaceHover : Theme.surface) border.color: parent.pressed ? Theme.accent : (parent.hovered ? Theme.borderStrong : Theme.border) radius: 16 } contentItem: Label { text: parent.text font: parent.font color: Theme.text horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } onClicked: cal.currentMonth = new Date(year, month + 1, 1) } ToolButton { text: "\u2302" font.pixelSize: 12 implicitWidth: 32 implicitHeight: 32 Layout.preferredWidth: 32 Layout.preferredHeight: 32 Layout.alignment: Qt.AlignVCenter background: Rectangle { color: parent.pressed ? Theme.surfaceSelected : (parent.hovered ? Theme.surfaceHover : Theme.surface) border.color: parent.pressed ? Theme.accent : (parent.hovered ? Theme.borderStrong : Theme.border) radius: 16 } contentItem: Label { text: parent.text font: parent.font color: Theme.text horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } ToolTip.text: qsTr("Сегодня") ToolTip.visible: hovered onClicked: cal.currentMonth = new Date() } } RowLayout { Layout.fillWidth: true Repeater { model: ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"] Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter font.pixelSize: 10 font.bold: true color: Theme.textMuted text: modelData } } } Grid { Layout.fillWidth: true Layout.fillHeight: true columns: 7 rowSpacing: 2 columnSpacing: 2 Repeater { model: cal.gridModel() delegate: Rectangle { required property int modelData width: (cal.width - 12) / 7 height: (cal.height - 50) / 6 radius: 6 border.width: isToday ? 1 : 0 border.color: Theme.accent color: { if (modelData <= 0) return "transparent" if (isToday) return Theme.calendarToday if (isSelected) return Theme.accentMuted if (hasNote) return Theme.isDark ? "#252a33" : "#f8f9fb" return Theme.surface } readonly property var now: { const t = new Date(); return { day: t.getDate(), month: t.getMonth(), year: t.getFullYear() } } readonly property bool isToday: modelData > 0 && now.day === modelData && now.month === cal.month && now.year === cal.year readonly property string ds: modelData > 0 ? Qt.formatDate(new Date(cal.year, cal.month, modelData), "yyyy-MM-dd") : "" readonly property bool hasNote: { const _ = cal.currentMonth if (modelData <= 0 || !fileStore.ready) return false return fileStore.fileExists(fileStore.vault + "/Журнал/" + ds + ".md") } readonly property bool isSelected: false // можно подсветить выбранный день Label { id: dayLabel anchors.centerIn: parent anchors.verticalCenterOffset: hasNote ? -3 : 0 visible: modelData > 0 text: modelData font.pixelSize: 11 font.bold: isToday color: modelData > 0 ? (isToday ? Theme.accentText : Theme.text) : "transparent" } Rectangle { visible: modelData > 0 && parent.hasNote width: 5; height: 5; radius: 2.5 color: Theme.calendarDot anchors.top: dayLabel.bottom anchors.topMargin: 2 anchors.horizontalCenter: parent.horizontalCenter } MouseArea { anchors.fill: parent enabled: modelData > 0 hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { const d = new Date(cal.year, cal.month, modelData) cal.daySelected(fmtDate(d)) } } } } } // подпись Label { Layout.fillWidth: true text: qsTr("Клик — заметка дня") color: Theme.textFaint font.pixelSize: 9 horizontalAlignment: Text.AlignHCenter } } }