diff --git a/demo/index.css b/demo/index.css index bd820791..8a976e11 100644 --- a/demo/index.css +++ b/demo/index.css @@ -17,3 +17,7 @@ color: white !important; } } + +.wrapper-calendar { + @apply w-[550px] +} diff --git a/demo/index.html b/demo/index.html index 0c6370f0..29a96adf 100644 --- a/demo/index.html +++ b/demo/index.html @@ -5,7 +5,7 @@ Vanilla Calendar - A pure JavaScript date and time picker using TypeScript so it supports any JS framework and library. - + diff --git a/demo/main.ts b/demo/main.ts index 4aefb6b1..b1a3e281 100644 --- a/demo/main.ts +++ b/demo/main.ts @@ -7,13 +7,12 @@ import VanillaCalendar from '@/package/src/scripts/vanilla-calendar'; import IVanillaCalendar, { Options } from '@/package/src'; const config: Options = { - type: 'multiple', - // settings: { - // selected: { - // month: 3, - // year: 2023, - // }, - // }, + settings: { + selected: { + month: 3, + year: 2023, + }, + }, }; document.addEventListener('DOMContentLoaded', () => { diff --git a/demo/pages/multiple/index.html b/demo/pages/multiple/index.html index 3d457983..0de7d004 100644 --- a/demo/pages/multiple/index.html +++ b/demo/pages/multiple/index.html @@ -12,6 +12,8 @@

Vanilla Calendar

A pure JavaScript date and time picker using TypeScript so it supports any JS framework and library.

-
+
+
+
diff --git a/package/src/classes.ts b/package/src/classes.ts index fececddf..d23648d2 100644 --- a/package/src/classes.ts +++ b/package/src/classes.ts @@ -9,7 +9,10 @@ const classes = { calendarInputWrapper: 'vanilla-calendar-input-wrapper', controls: 'vanilla-calendar-controls', grid: 'vanilla-calendar-grid', + gridDisabled: 'vanilla-calendar-grid_disabled', column: 'vanilla-calendar-column', + columnMonth: 'vanilla-calendar-column_month', + columnYear: 'vanilla-calendar-column_year', header: 'vanilla-calendar-header', headerContent: 'vanilla-calendar-header__content', month: 'vanilla-calendar-month', diff --git a/package/src/scripts/helpers/getColumn.ts b/package/src/scripts/helpers/getColumn.ts new file mode 100644 index 00000000..eb4ed91d --- /dev/null +++ b/package/src/scripts/helpers/getColumn.ts @@ -0,0 +1,20 @@ +import { IVanillaCalendar } from '../../types'; + +const getColumn = (self: IVanillaCalendar, columnClass: string, personalClass: string, id: number, dataAttr: string) => { + const columnEls = (self.HTMLElement as HTMLElement).querySelectorAll(`.${self.CSSClasses.column}`) as NodeListOf; + const firstColumnID = Number((columnEls[0].querySelector(`.${personalClass}`) as HTMLElement).getAttribute(dataAttr)); + const lastColumnID = Number((columnEls[columnEls.length - 1].querySelector(`.${personalClass}`) as HTMLElement).getAttribute(dataAttr)); + const indexColumn = [...columnEls].findIndex((column) => column.classList.contains(columnClass)); + + if (firstColumnID === lastColumnID || indexColumn < 0) { + return id; + } + + if (firstColumnID < lastColumnID || (self.currentType !== 'year' && firstColumnID > lastColumnID)) { + return id - indexColumn; + } + + return id; +}; + +export default getColumn; diff --git a/package/src/scripts/methods/clickCalendar.ts b/package/src/scripts/methods/clickCalendar.ts index 4491ecce..5d6344fd 100644 --- a/package/src/scripts/methods/clickCalendar.ts +++ b/package/src/scripts/methods/clickCalendar.ts @@ -6,6 +6,7 @@ import createYears from './createYears'; import generateDate from '../helpers/generateDate'; import mainMethod from './mainMethod'; import handlerMultipleRanged from './handlerMultipleRanged'; +import getColumn from '../helpers/getColumn'; const clickCalendar = (self: IVanillaCalendar) => { (self.HTMLElement as HTMLElement).addEventListener('click', (e) => { @@ -22,6 +23,8 @@ const clickCalendar = (self: IVanillaCalendar) => { const yearItemEl: HTMLElement | null = element.closest(`.${self.CSSClasses.yearsYear}`); const monthHeaderEl: HTMLElement | null = element.closest(`.${self.CSSClasses.month}`); const monthItemEl: HTMLElement | null = element.closest(`.${self.CSSClasses.monthsMonth}`); + const gridEl: HTMLElement | null = element.closest(`.${self.CSSClasses.grid}`); + const columnEl: HTMLElement | null = element.closest(`.${self.CSSClasses.column}`); const clickArrowMonth = () => { if (arrowEl && self.currentType !== 'year' && self.currentType !== 'month') { @@ -141,7 +144,7 @@ const clickCalendar = (self: IVanillaCalendar) => { }; const clickYear = () => { - if (!self.settings.selection.year || self.currentType === 'multiple') return; + if (!self.settings.selection.year) return; if (arrowEl && self.currentType === 'year') { if (self.viewYear === undefined) return; if (arrowNextEl) { @@ -149,15 +152,17 @@ const clickCalendar = (self: IVanillaCalendar) => { } else if (arrowPrevEl) { self.viewYear -= 15; } - createYears(self); + createYears(self, e.target as HTMLElement); } else if (self.currentType !== 'year' && yearHeaderEl) { - createYears(self); + createYears(self, e.target as HTMLElement); } else if (self.currentType === 'year' && yearHeaderEl) { self.currentType = self.type; mainMethod(self); } else if (yearItemEl) { if (self.selectedMonth === undefined || !self.dateMin || !self.dateMax) return; - self.selectedYear = Number(yearItemEl.dataset.calendarYear); + self.selectedYear = self.type === 'multiple' + ? getColumn(self, self.CSSClasses.columnYear, self.CSSClasses.year, Number(yearItemEl.dataset.calendarYear), 'data-calendar-selected-year') + : Number(yearItemEl.dataset.calendarYear); self.currentType = self.type; if (self.selectedMonth < self.dateMin.getMonth() && self.selectedYear === self.dateMin.getFullYear()) { self.selectedMonth = self.dateMin.getMonth(); @@ -167,21 +172,34 @@ const clickCalendar = (self: IVanillaCalendar) => { } if (self.actions.clickYear) self.actions.clickYear(e, self.selectedYear); mainMethod(self); + } else if (self.type === 'multiple' && self.currentType === 'year' && gridEl && !columnEl) { + self.currentType = self.type; + mainMethod(self); } }; const clickMonth = () => { - if (!self.settings.selection.month || self.currentType === 'multiple') return; + if (!self.settings.selection.month) return; if (self.currentType !== 'month' && monthHeaderEl) { - createMonths(self); + createMonths(self, e.target as HTMLElement); } else if (self.currentType === 'month' && monthHeaderEl) { self.currentType = self.type; mainMethod(self); } else if (monthItemEl) { - self.selectedMonth = Number(monthItemEl.dataset.calendarMonth); + self.selectedMonth = self.type === 'multiple' + ? getColumn(self, self.CSSClasses.columnMonth, self.CSSClasses.month, Number(monthItemEl.dataset.calendarMonth), 'data-calendar-selected-month') + : Number(monthItemEl.dataset.calendarMonth); + if (self.type === 'multiple') { + const column = monthItemEl.closest(`.${self.CSSClasses.columnMonth}`) as HTMLElement; + const year = column.querySelector(`.${self.CSSClasses.year}`) as HTMLElement; + self.selectedYear = Number(year.dataset.calendarSelectedYear); + } self.currentType = self.type; if (self.actions.clickMonth) self.actions.clickMonth(e, self.selectedMonth); mainMethod(self); + } else if (self.type === 'multiple' && self.currentType === 'month' && gridEl && !columnEl) { + self.currentType = self.type; + mainMethod(self); } }; diff --git a/package/src/scripts/methods/createDOM.ts b/package/src/scripts/methods/createDOM.ts index 892e0c3a..8d47777b 100644 --- a/package/src/scripts/methods/createDOM.ts +++ b/package/src/scripts/methods/createDOM.ts @@ -1,10 +1,21 @@ -import { IVanillaCalendar } from '../../types'; +import { IVanillaCalendar, TypesCalendar } from '../../types'; import { DOMParser, MultipleParser } from '../helpers/parserComponent'; -const createDOM = (self: IVanillaCalendar) => { +const createDOM = (self: IVanillaCalendar, initType?: TypesCalendar, target?: HTMLElement) => { const calendarElement = (self.HTMLElement as HTMLElement); calendarElement.classList.add(self.CSSClasses.calendar); + const switcherTypeMultiple = (columnClass: string, DOMTemplates: string) => { + if (!target) return; + const controls = (self.HTMLElement as HTMLElement).querySelector(`.${self.CSSClasses.controls}`); + if (controls) (self.HTMLElement as HTMLElement).removeChild(controls); + const grid = (self.HTMLElement as HTMLElement).querySelector(`.${self.CSSClasses.grid}`) as HTMLElement; + grid.classList.add(self.CSSClasses.gridDisabled); + const columnElement = target.closest(`.${self.CSSClasses.column}`) as HTMLElement; + columnElement.classList.add(columnClass); + columnElement.innerHTML = DOMParser(self, DOMTemplates); + }; + switch (self.currentType) { case 'default': calendarElement.classList.add(self.CSSClasses.calendarDefault); @@ -20,12 +31,20 @@ const createDOM = (self: IVanillaCalendar) => { calendarElement.innerHTML = MultipleParser(self, DOMParser(self, self.DOMTemplates.multiple)); break; case 'month': + if (initType === 'multiple') { + switcherTypeMultiple(self.CSSClasses.columnMonth, self.DOMTemplates.month); + break; + } calendarElement.classList.remove(self.CSSClasses.calendarDefault); calendarElement.classList.add(self.CSSClasses.calendarMonth); calendarElement.classList.remove(self.CSSClasses.calendarYear); calendarElement.innerHTML = DOMParser(self, self.DOMTemplates.month); break; case 'year': + if (initType === 'multiple') { + switcherTypeMultiple(self.CSSClasses.columnYear, self.DOMTemplates.year); + break; + } calendarElement.classList.remove(self.CSSClasses.calendarDefault); calendarElement.classList.remove(self.CSSClasses.calendarMonth); calendarElement.classList.add(self.CSSClasses.calendarYear); diff --git a/package/src/scripts/methods/createMonths.ts b/package/src/scripts/methods/createMonths.ts index bc3691fe..7b4856af 100644 --- a/package/src/scripts/methods/createMonths.ts +++ b/package/src/scripts/methods/createMonths.ts @@ -3,14 +3,15 @@ import createDOM from './createDOM'; import showMonth from './showMonth'; import showYear from './showYear'; -const createMonths = (self: IVanillaCalendar) => { +const createMonths = (self: IVanillaCalendar, target?: HTMLElement) => { + const selectedMonth = target?.dataset.calendarSelectedMonth ? Number(target?.dataset.calendarSelectedMonth) : self.selectedMonth; self.currentType = 'month'; - createDOM(self); + createDOM(self, self.type, target); showMonth(self); showYear(self); const monthsEl = (self.HTMLElement as HTMLElement).querySelector(`.${self.CSSClasses.months}`); - if (self.selectedMonth === undefined || self.selectedYear === undefined || !self.dateMin || !self.dateMax || !monthsEl) return; + if (self.selectedYear === undefined || !self.dateMin || !self.dateMax || !monthsEl) return; if (self.settings.selection.month) monthsEl.classList.add(self.CSSClasses.monthsSelecting); @@ -22,7 +23,7 @@ const createMonths = (self: IVanillaCalendar) => { const month = self.locale.months[i]; const monthEl = templateMonthEl.cloneNode(true) as HTMLButtonElement; - if (i === self.selectedMonth) { + if (i === selectedMonth) { monthEl.classList.add(self.CSSClasses.monthsMonthSelected); } if (i < self.dateMin.getMonth() && self.selectedYear === self.dateMin.getFullYear()) { diff --git a/package/src/scripts/methods/createYears.ts b/package/src/scripts/methods/createYears.ts index 37bd6a18..28e88eba 100644 --- a/package/src/scripts/methods/createYears.ts +++ b/package/src/scripts/methods/createYears.ts @@ -4,10 +4,11 @@ import createDOM from './createDOM'; import showMonth from './showMonth'; import showYear from './showYear'; -const createYears = (self: IVanillaCalendar) => { +const createYears = (self: IVanillaCalendar, target?: HTMLElement) => { if (self.viewYear === undefined || !self.dateMin || !self.dateMax) return; + const selectedYear = target?.dataset.calendarSelectedYear ? Number(target?.dataset.calendarSelectedYear) : self.selectedYear; self.currentType = 'year'; - createDOM(self); + createDOM(self, self.type, target); showMonth(self); showYear(self); controlArrows(self); @@ -23,7 +24,7 @@ const createYears = (self: IVanillaCalendar) => { const year = i; const yearEl = templateYearEl.cloneNode(true) as HTMLButtonElement; - if (year === self.selectedYear) { + if (year === selectedYear) { yearEl.classList.add(self.CSSClasses.yearsYearSelected); } if (year < self.dateMin.getFullYear()) { diff --git a/package/src/scripts/methods/showMonth.ts b/package/src/scripts/methods/showMonth.ts index 2614d07b..7bc3ef84 100644 --- a/package/src/scripts/methods/showMonth.ts +++ b/package/src/scripts/methods/showMonth.ts @@ -11,7 +11,7 @@ const showMonth = (self: IVanillaCalendar) => { months[index].dataset.calendarSelectedMonth = String(selectedMonth); months[index].innerText = self.locale.months[selectedMonth]; - if (!self.settings.selection.month || self.currentType === 'multiple') { + if (!self.settings.selection.month) { months[index].tabIndex = -1; months[index].classList.add(self.CSSClasses.monthDisabled); } else { diff --git a/package/src/scripts/methods/showYear.ts b/package/src/scripts/methods/showYear.ts index e6f23dc7..94611fa3 100644 --- a/package/src/scripts/methods/showYear.ts +++ b/package/src/scripts/methods/showYear.ts @@ -11,7 +11,7 @@ const showYear = (self: IVanillaCalendar) => { years[index].dataset.calendarSelectedYear = String(selectedYear); years[index].innerText = String(selectedYear); - if (!self.settings.selection.year || self.currentType === 'multiple') { + if (!self.settings.selection.year) { years[index].tabIndex = -1; years[index].classList.add(self.CSSClasses.yearDisabled); } else { diff --git a/package/src/styles/themes/dark.css b/package/src/styles/themes/dark.css index 3fe37207..afe6fc79 100644 --- a/package/src/styles/themes/dark.css +++ b/package/src/styles/themes/dark.css @@ -6,16 +6,6 @@ @apply outline-orange-300 } -[data-calendar-theme="dark"].vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-month, -[data-calendar-theme="dark"].vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-year { - @apply text-white hover:text-white -} - -[data-calendar-theme="dark"].vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-month.vanilla-calendar-month_disabled, -[data-calendar-theme="dark"].vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-year.vanilla-calendar-year_disabled { - @apply text-white -} - [data-calendar-theme="dark"] .vanilla-calendar-arrow::before { @apply bg-white; } diff --git a/package/src/styles/themes/light.css b/package/src/styles/themes/light.css index 513250ef..99650094 100644 --- a/package/src/styles/themes/light.css +++ b/package/src/styles/themes/light.css @@ -6,16 +6,6 @@ @apply outline-orange-300 } -[data-calendar-theme="light"].vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-month, -[data-calendar-theme="light"].vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-year { - @apply text-slate-900 hover:text-slate-900 -} - -[data-calendar-theme="light"].vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-month.vanilla-calendar-month_disabled, -[data-calendar-theme="light"].vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-year.vanilla-calendar-year_disabled { - @apply text-slate-900 -} - [data-calendar-theme="light"] .vanilla-calendar-arrow::before { @apply bg-slate-900; } diff --git a/package/src/styles/vanilla-calendar.css b/package/src/styles/vanilla-calendar.css index 0a799b9c..e4cf7ed4 100644 --- a/package/src/styles/vanilla-calendar.css +++ b/package/src/styles/vanilla-calendar.css @@ -1,21 +1,11 @@ .vanilla-calendar { - @apply p-4 rounded-xl relative flex flex-col min-w-[275px] w-max h-max + @apply p-4 rounded-xl relative flex flex-col min-w-[275px] max-w-full } .vanilla-calendar button:focus-visible { @apply outline outline-1 rounded-lg } -.vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-month, -.vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-year { - @apply cursor-default -} - -.vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-month.vanilla-calendar-month_disabled, -.vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-year.vanilla-calendar-year_disabled { - @apply pointer-events-auto -} - .vanilla-calendar.vanilla-calendar_multiple .vanilla-calendar-days { @apply grow-0 } @@ -56,7 +46,16 @@ } .vanilla-calendar-grid { - @apply grid grid-flow-col gap-7 grow + @apply grid grid-cols-[repeat(auto-fill,_minmax(220px,_1fr))] gap-7 grow +} + +.vanilla-calendar-grid.vanilla-calendar-grid_disabled .vanilla-calendar-column { + @apply opacity-30 pointer-events-none +} + +.vanilla-calendar-grid.vanilla-calendar-grid_disabled .vanilla-calendar-column.vanilla-calendar-column_month, +.vanilla-calendar-grid.vanilla-calendar-grid_disabled .vanilla-calendar-column.vanilla-calendar-column_year { + @apply opacity-100 pointer-events-auto } .vanilla-calendar-column { diff --git a/package/src/types.ts b/package/src/types.ts index 0714411f..d28e5805 100644 --- a/package/src/types.ts +++ b/package/src/types.ts @@ -104,7 +104,10 @@ export interface ICSSClasses { calendarInputWrapper: string; controls: string; grid: string; + gridDisabled: string; column: string; + columnMonth: string; + columnYear: string; header: string; headerContent: string; month: string;