From b3b603bb9c448fda96cab6562d5cab31f09a8ab4 Mon Sep 17 00:00:00 2001 From: Kieran Boyle Date: Sun, 14 Jan 2024 12:46:45 -0800 Subject: [PATCH] feat(enable-dates): add support for enabled dates only --- README.md | 1 + docs/package-lock.json | 2 + docs/src/App.svelte | 25 ++++++++- docs/src/examples/single/single.svelte | 3 +- src/datepicker.d.ts | 5 ++ src/datepicker.svelte | 71 ++++++++++++++++++-------- src/datepicker.test.js | 1 + 7 files changed, 84 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index b97644b..16d1c3f 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ Try it in the [Svelte REPL](https://svelte.dev/repl/cae0ce6e92634878b6e1a587146d | endDateTime | The end date time string in 24 hour format. | `string` (default: `00:00`) | defaultYear | The year you want to show as the default. | `number` (default: `2023`) | align | The edge alignment of the datepicker. | `string` (default: `left`) +| enabledDates | An array of date strings to enable only. | `array` (default: [...]) | disabledDates | An array of date strings to disable. | `array` (default: [...]) | isRange | Changes the date picker into a range picker and allows start and end date selection. | `boolean` (default: `false`) | isMultipane | If true, two calendar months will be shown side-by-side instead of one. | `boolean` (default: `false`) diff --git a/docs/package-lock.json b/docs/package-lock.json index 3d56106..2e96b83 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -27,6 +27,7 @@ "@testing-library/svelte": "^4.0.5", "@tsconfig/svelte": "^5.0.2", "@types/node": "^20.10.5", + "@typescript-eslint/parser": "^6.18.1", "@vitest/coverage-v8": "^0.34.6", "eslint": "^8.54.0", "eslint-plugin-svelte": "^2.35.1", @@ -2225,6 +2226,7 @@ "@testing-library/svelte": "^4.0.5", "@tsconfig/svelte": "^5.0.2", "@types/node": "^20.10.5", + "@typescript-eslint/parser": "^6.18.1", "@vitest/coverage-v8": "^0.34.6", "eslint": "^8.54.0", "eslint-plugin-svelte": "^2.35.1", diff --git a/docs/src/App.svelte b/docs/src/App.svelte index 00f6004..579395d 100644 --- a/docs/src/App.svelte +++ b/docs/src/App.svelte @@ -188,6 +188,23 @@ [] + + enabledDates + array + + Determines which dates will be enabled only. +
Accepted format 'MM/DD/YYYY'
+ +
+ + + + [] + dowLabels array @@ -305,12 +322,18 @@ -

Disable Dates

+

Disabled Dates

+

Enabled Dates

+ +
+ +
+

Theme

diff --git a/docs/src/examples/single/single.svelte b/docs/src/examples/single/single.svelte index e1bd69a..41369a1 100644 --- a/docs/src/examples/single/single.svelte +++ b/docs/src/examples/single/single.svelte @@ -7,6 +7,7 @@ export let disabledDates = []; export let enableFutureDates = null; export let enablePastDates = null; + export let enabledDates = []; let startDate = new Date(); let dateFormat = 'MM/dd/yy'; @@ -56,7 +57,7 @@ $: formattedStartDate = formatDate(startDate); - + diff --git a/src/datepicker.d.ts b/src/datepicker.d.ts index 222a011..61c568c 100644 --- a/src/datepicker.d.ts +++ b/src/datepicker.d.ts @@ -75,6 +75,11 @@ export interface DatePickerProps { */ disabledDates: string[]; + /** + * An array of enabled dates. + */ + enabledDates: string[]; + /** * Callback function to handle day click events. */ diff --git a/src/datepicker.svelte b/src/datepicker.svelte index eebbd6a..6b80d4a 100644 --- a/src/datepicker.svelte +++ b/src/datepicker.svelte @@ -86,6 +86,12 @@ */ export let disabledDates = []; + /** + * An array of enabled dates. + * @type {Date[]} + */ + export let enabledDates = []; + /** * Callback function to handle day click events. * @type {(event: Object) => void} @@ -420,7 +426,7 @@ * @param {string[]} disabled - An array of disabled dates. * @returns {string[]} - An array of dates within the specified range. */ - const getDatesInRange = (startDate, endDate, disabled) => { + const getDatesInRange = (startDate, endDate) => { const dateRangeStart = new Date(startDate); const dateRangeEnd = new Date(endDate); const datesInRange = []; @@ -429,7 +435,11 @@ const formattedDate = `${ dateRangeStart.getMonth() + 1 }/${dateRangeStart.getDate()}/${dateRangeStart.getFullYear()}`; - if (!disabled.includes(formattedDate)) { + if ( + (!enabled && !disabled) || + (enabled.length && enabled.includes(formattedDate)) || + (disabled.length && !disabled.includes(formattedDate)) + ) { datesInRange.push(formattedDate); } } @@ -470,7 +480,7 @@ ...(isRange && { endDate, endDateTime, - rangeDates: getDatesInRange(startDate, endDate, disabled) + rangeDates: getDatesInRange(startDate, endDate) }) }; @@ -564,7 +574,11 @@ */ const isDisabled = (day, month, year) => { const selectedDateTimestamp = createTimestamp(year, month, day); - return disabled.map((d) => new Date(d).getTime()).includes(selectedDateTimestamp); + return ( + (!enabled && !disabled) || + (enabled.length && !enabled.map((d) => new Date(d).getTime()).includes(selectedDateTimestamp)) || + (disabled.length && disabled.map((d) => new Date(d).getTime()).includes(selectedDateTimestamp)) + ); }; /** @@ -752,11 +766,40 @@ return `${hours}:${minutes}`; }; + /** + * Returns an array of timestamps from an array of date strings. + * @param {string[]} collection - An array of date strings. + * @returns {number[]} - An array of timestamps. + */ + const getDatesFromArray = (collection) => { + return collection.reduce((acc, date) => { + let newDates = []; + + if (date instanceof Date) { + newDates = [date.getTime()]; + } else if (typeof date === 'string' && date.includes(':')) { + const [rangeStart, rangeEnd] = date.split(':'); + let dateRangeStart = new Date(rangeStart).getTime(); + let dateRangeEnd = new Date(rangeEnd).getTime(); + + for (; dateRangeStart <= dateRangeEnd; dateRangeStart += MILLISECONDS_IN_DAY) { + newDates = [...newDates, dateRangeStart]; + } + } else { + newDates = [new Date(date).getTime()]; + } + + return [...acc, ...newDates]; + }, []); + }; + $: startDate = startDate ? getTimestamp(startDate) : null; $: endDate = endDate ? getTimestamp(endDate) : null; + $: if (startDate || endDate) { updateCalendars(); } + $: todayMonth = today && today.getMonth(); $: todayDay = today && today.getDate(); $: todayYear = today && today.getFullYear(); @@ -768,24 +811,8 @@ $: endDateCalendar = calendarize(new Date(endDateYear, endDateMonth), startOfWeek); $: !isRange && (endDate = null); $: theme !== null && document.documentElement.setAttribute('data-picker-theme', theme); - $: disabled = disabledDates.reduce((acc, date) => { - let newDates = []; - if (date instanceof Date) { - newDates = [date.getTime()]; - } else if (typeof date === 'string' && date.includes(':')) { - const [rangeStart, rangeEnd] = date.split(':'); - let dateRangeStart = new Date(rangeStart).getTime(); - let dateRangeEnd = new Date(rangeEnd).getTime(); - - for (; dateRangeStart <= dateRangeEnd; dateRangeStart += MILLISECONDS_IN_DAY) { - newDates = [...newDates, dateRangeStart]; - } - } else { - newDates = [new Date(date).getTime()]; - } - - return [...acc, ...newDates]; - }, []); + $: disabled = getDatesFromArray(disabledDates); + $: enabled = getDatesFromArray(enabledDates); $: if (!startDate && !endDate) { startDateYear = Number(defaultYear); diff --git a/src/datepicker.test.js b/src/datepicker.test.js index 56d3d1b..cdf62e6 100644 --- a/src/datepicker.test.js +++ b/src/datepicker.test.js @@ -15,6 +15,7 @@ const config = { align: 'left', theme: '', disabledDates: [], + enabledDates: [], onDayClick: () => {}, showYearControls: true, showPresets: false,