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/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/index.d.ts b/package/src/index.d.ts index e6145587..df4585eb 100644 --- a/package/src/index.d.ts +++ b/package/src/index.d.ts @@ -1,4 +1,5 @@ import { + TypesCalendar, IActions, ICSSClasses, IDate, @@ -22,7 +23,7 @@ type Settings = { export type Options = { input?: boolean; - type?: 'default' | 'multiple' | 'month' | 'year'; + type?: TypesCalendar; months?: number; jumpMonths?: number; date?: Partial; diff --git a/package/src/scripts/helpers/getColumnID.ts b/package/src/scripts/helpers/getColumnID.ts new file mode 100644 index 00000000..dc799743 --- /dev/null +++ b/package/src/scripts/helpers/getColumnID.ts @@ -0,0 +1,20 @@ +import { IVanillaCalendar } from '../../types'; + +const getColumnID = (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 getColumnID; diff --git a/package/src/scripts/methods/clickCalendar.ts b/package/src/scripts/methods/clickCalendar.ts index 4491ecce..59ad66e8 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 getColumnID from '../helpers/getColumnID'; 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' + ? getColumnID(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' + ? getColumnID(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..a94d88ae 100644 --- a/package/src/scripts/methods/createDOM.ts +++ b/package/src/scripts/methods/createDOM.ts @@ -1,10 +1,21 @@ import { IVanillaCalendar } from '../../types'; import { DOMParser, MultipleParser } from '../helpers/parserComponent'; -const createDOM = (self: IVanillaCalendar) => { +const createDOM = (self: IVanillaCalendar, 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 (self.type === '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 (self.type === '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..f94b53cc 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, 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..58e765ca 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, 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/mainMethod.ts b/package/src/scripts/methods/mainMethod.ts index 659e42f1..c59fa5a9 100644 --- a/package/src/scripts/methods/mainMethod.ts +++ b/package/src/scripts/methods/mainMethod.ts @@ -12,6 +12,23 @@ import getLocale from './getLocale'; import setTheme from './setTheme'; const mainMethod = (self: IVanillaCalendar) => { + const typeMapper = { + default() { + createWeek(self); + createDays(self); + }, + multiple() { + createWeek(self); + createDays(self); + }, + month() { + createMonths(self); + }, + year() { + createYears(self); + }, + }; + setTheme(self); getLocale(self); createDOM(self); @@ -20,14 +37,7 @@ const mainMethod = (self: IVanillaCalendar) => { controlArrows(self); createTime(self); - if (self.currentType === 'default' || self.currentType === 'multiple') { - createWeek(self); - createDays(self); - } else if (self.currentType === 'month') { - createMonths(self); - } else if (self.currentType === 'year') { - createYears(self); - } + typeMapper[self.currentType](); }; export default mainMethod; 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/scripts/vanilla-calendar.ts b/package/src/scripts/vanilla-calendar.ts index b280db13..4f07779b 100644 --- a/package/src/scripts/vanilla-calendar.ts +++ b/package/src/scripts/vanilla-calendar.ts @@ -7,6 +7,7 @@ import { IPopups, ICSSClasses, IDOMTemplates, + TypesCalendar, } from '../types'; import resetCalendar from './methods/resetCalendar'; import updateCalendar from './methods/updateCalendar'; @@ -22,7 +23,7 @@ export default class VanillaCalendar void; update: () => void; init: () => void;