diff --git a/Calendar.js b/Calendar.js new file mode 100644 index 0000000..fa2b76f --- /dev/null +++ b/Calendar.js @@ -0,0 +1,100 @@ +import {getDaysInMonth, getFirstDayInMonth, getWeekdaysForLocale} from "./utils.js"; + +class Calendar { + constructor(calendarDiv, options = {locale: 'en-GB', firstDayInWeek: 'Sunday'}) { + this.calendarDiv = calendarDiv + this.options = options + this.date = new Date(Date.now()) + // this.date = new Date(2021, 5, 12) + console.log(this.date) + this.visiableDays = 42 + + this.locale = this.options.locale + this.firstDayInWeek = this.options.firstDayInWeek + + this.weekdays = getWeekdaysForLocale(this.locale, 'long', this.firstDayInWeek) + this.randomImgUrl = 'https://picsum.photos/1920/1080' + } + + init() { + const body = document.querySelector('body') + if (body) { + body.style.backgroundImage = 'url(' + this.randomImgUrl + ')' + body.style.backgroundSize = 'cover' + body.style.backgroundRepeat = 'no-repeat' + body.style.backgroundPosition = 'center center' + } + } + + render() { + const calendarHeader = this.calendarDiv.querySelector('#calendar__header') + if (calendarHeader) { + const yearDiv = calendarHeader.querySelector('#year') + const monthDiv = calendarHeader.querySelector('#month') + + if (yearDiv) { + yearDiv.innerText = Intl.DateTimeFormat(this.locale, {year: 'numeric'}).format(this.date) + } + + if (monthDiv) { + monthDiv.innerText = Intl.DateTimeFormat(this.locale, {month: 'long'}).format(this.date) + } + } + + const calendarBody = this.calendarDiv.querySelector('#calendar__body') + if (calendarBody) { + // Weekdays + for (let i = 0; i < 7; i++) { + const weekday = document.createElement('div') + weekday.classList.add('day', 'weekday') + weekday.textContent = this.weekdays[i] + calendarBody.appendChild(weekday) + } + + // Days + const currentMonthDays = getDaysInMonth(this.date.getMonth(), this.date.getFullYear()) + const lastMonthDays = getDaysInMonth(this.date.getMonth() - 1, this.date.getFullYear()) + const nextMonthDays = getDaysInMonth(this.date.getMonth() + 1, this.date.getFullYear()) + const firstDayInMonth = getFirstDayInMonth(this.date.getMonth(), this.date.getFullYear()) + + let daysAtEndOfMonth = this.visiableDays - currentMonthDays - firstDayInMonth + 1 + let daysAtStartOfMonth = this.visiableDays - daysAtEndOfMonth - currentMonthDays + if (daysAtStartOfMonth < 0) { + daysAtStartOfMonth = daysAtStartOfMonth + 7 + daysAtEndOfMonth = daysAtEndOfMonth - 7 + } + console.log(daysAtStartOfMonth, daysAtEndOfMonth) + + for (let i = 0; i < this.visiableDays; i++) { + const day = document.createElement('div') + const dateSpan = document.createElement('span') + dateSpan.id = 'date' + day.classList.add('day', 'monthday') + + let visibleDate = i - daysAtStartOfMonth + 1 + + // last month date + if (i < daysAtStartOfMonth) { + visibleDate = lastMonthDays + visibleDate + day.classList.add('dim') + } + + // next month date + if (this.visiableDays - i - 1 < daysAtEndOfMonth) { + visibleDate = visibleDate - currentMonthDays + day.classList.add('dim') + } + + dateSpan.innerText = visibleDate.toString() + + const dayId = `${this.date.getFullYear()}${this.date.getMonth()}${this.date.getDay()}` + day.classList.add('day-' + dayId) + + day.appendChild(dateSpan) + calendarBody.appendChild(day) + } + } + } +} + +export default Calendar; diff --git a/index.html b/index.html index ee03b3b..cdc673b 100644 --- a/index.html +++ b/index.html @@ -8,19 +8,19 @@ Calendar - +
-
+
-
2025
+
2024
- + - +
-
September
+
September
@@ -28,6 +28,23 @@
- +
+
+ © 2025 VikingOwl +
+
+ Gitea +
+ + diff --git a/script.js b/script.js deleted file mode 100644 index e9fc032..0000000 --- a/script.js +++ /dev/null @@ -1,63 +0,0 @@ -const WEEKDAYS = Object.freeze({ - MONDAY: 0, - TUESDAY: 1, - WEDNESDAY: 2, - THURSDAY: 3, - FRIDAY: 4, - SATURDAY: 5, - SUNDAY: 6 -}); - -const MONTHS = Object.freeze({ - JANUARY: 0, - FEBRUARY: 1, - MARCH: 2, - APRIL: 3, - MAY: 4, - JUNE: 5, - JULY: 6, - AUGUST: 7, - SEPTEMBER: 8, - OCTOBER: 9, - NOVEMBER: 10, - DEZEMBER: 11 -}) - -function generateDays() { - const visibleDays = 42 // 7 col, 5 row - const calendarBody = document.getElementById('calendar__body') - if (calendarBody) { - for (let i = 0; i < visibleDays; i++) { - const day = document.createElement('div') - day.classList.add('day') - if (i < Object.keys(WEEKDAYS).length) { - day.classList.add('weekday') - day.textContent = Object.keys(WEEKDAYS)[i] - } else { - const dayId = i % 7 + 1 - day.classList.add('day-' + dayId) - } - calendarBody.appendChild(day) - } - } -} - -function addCalendarDays() { - const date = new Date() - - console.log(date.getMonth()) -} - -function setHeaderInfo(month, year) { - const yearDOM = document.querySelector('#calendar__header .year') - const monthDOM = document.querySelector('#calendar__header .month') - - if (month) { - month.textContent = month - } -} - -document.addEventListener('DOMContentLoaded', () => { - generateDays() - addCalendarDays() -}) diff --git a/style.css b/style.css index 51486af..85d773e 100644 --- a/style.css +++ b/style.css @@ -3,47 +3,65 @@ margin: 0; padding: 0; font-family: 'Poppins', sans-serif; + + --footer-height: 3rem; + --calendar-header-height: 4rem; +} + +body { + background: #000; +} + +footer { + height: var(--footer-height); + background: rgba(0, 0, 0, 0.4); + color: #fff; + display: flex; + justify-content: center; + align-items: center; +} + +footer a { + color: #76d714; + text-decoration: none; +} + +footer a:hover { + color: #609926; } .container { width: 100%; - height: 100dvh; + height: calc(100vh - var(--footer-height)); display: flex; justify-content: center; align-items: center; flex: 0 1 auto; + border-radius: .4rem; } -.calendar { - width: 900px; - height: 650px; - background: floralwhite; - border: 1px solid black; +#calendar { + width: 1080px; + height: 910px; + + /* From https://css.glass */ + background: rgba(62, 31, 31, 0.2); + border-radius: .4rem; + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(5px); + border: 1px solid rgba(255, 255, 255, 0.3); } +/* ===== HEADER ===== */ #calendar__header { - background: aliceblue; + background: rgba(0, 0, 0, 0.3); + color: #fff; display: flex; justify-content: space-between; - height: 3rem; -} - -#calendar__body { - width: 100%; - height: calc(100% - 3rem); - padding: 0.5rem; - - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; - grid-template-rows: auto 1fr 1fr 1fr 1fr 1fr; - gap: 0.5rem 0.5rem; - grid-template-areas: - ". . . . . . ." - ". . . . . . ." - ". . . . . . ." - ". . . . . . ." - ". . . . . . ." - ". . . . . . ."; + height: var(--calendar-header-height); + border-radius: .4rem .4rem 0 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.3); } #calendar__header * { @@ -53,6 +71,10 @@ display: flex; align-items: center; font-size: 1.2rem; + font-weight: bold; + text-transform: uppercase; + cursor: pointer; + user-select: none; } #calendar__header .year { @@ -67,7 +89,7 @@ display: flex; justify-content: center; align-items: center; - gap: .5rem; + gap: 1rem; } #calendar__header .actions button { @@ -75,15 +97,60 @@ justify-content: center; align-items: center; padding: 1rem; + + background: none; + color: white; + border: 2px solid rgba(255, 255, 255, 0.3); + border-radius: 5px; } -.day { - background: burlywood; +/* ===== BODY ===== */ +#calendar__body { + width: 100%; + height: calc(100% - var(--calendar-header-height)); + padding: 1rem; + border-radius: 10px; + + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + grid-template-rows: auto 1fr 1fr 1fr 1fr 1fr 1fr; + gap: 0.5rem 0.5rem; + grid-template-areas: + ". . . . . . ." + ". . . . . . ." + ". . . . . . ." + ". . . . . . ." + ". . . . . . ." + ". . . . . . ." + ". . . . . . ."; } -.weekday { +#calendar__body .day { + background: rgba(255, 255, 255, 0.7); + border-radius: 0.2rem; + cursor: pointer; +} + +#calendar__body .dim { + background: rgba(192, 192, 192, 0.7); +} + +#calendar__body .weekday { height: 2.5rem; display: flex; justify-content: center; align-items: center; + user-select: none; + cursor: initial; + text-transform: uppercase; +} + +#calendar__body .monthday { + display: flex; + border: 1px solid rgba(255, 255, 255, 0.3); +} + +#calendar__body .monthday #date { + margin-top: .3rem; + margin-left: .5rem; } diff --git a/utils.js b/utils.js new file mode 100644 index 0000000..cae82f9 --- /dev/null +++ b/utils.js @@ -0,0 +1,34 @@ +export function getWeekdaysForLocale(localeName = 'en-GB', weekday = 'long', firstDayInWeek = 'Sunday') { + const {format} = new Intl.DateTimeFormat(localeName, {weekday}) + const year = firstDayInWeek === 'Sunday' ? 2023 : firstDayInWeek === 'Monday' ? 2022 : 2023 + const month = firstDayInWeek === 'Sunday' ? 4 : firstDayInWeek === 'Monday' ? 1 : 4 + + return [...Array(7).keys()] + .map((day) => format(new Date(Date.UTC(year, month, day)))) +} + +export function getFirstDayInMonth(month = 0, year = 2025) { + if (month === -1) { + month = 11 + year-- + } + if (month === 12) { + month = 0 + year++ + } + return new Date(year, month, 1).getDay() +} + +export function getDaysInMonth(month = 0, year = 2025) { + month++ + if (month === -1) { + month = 11 + year-- + } + if (month === 12) { + month = 0 + year++ + } + + return new Date(year, month, 0).getDate() +}