Skip to content

Commit

Permalink
added onFocus and onBeforeSelect options, #526
Browse files Browse the repository at this point in the history
  • Loading branch information
t1m0n committed Jul 24, 2023
1 parent 0b37605 commit a0b34b3
Show file tree
Hide file tree
Showing 19 changed files with 330 additions and 70 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

### v3.4.0
* added new options `onFocus` and `onBeforeSelect` grant you more control over range selection behaviour and more [#526](https://github.com/t1m0n/air-datepicker/issues/526)
* added new method `getViewDates()` allows you to get all dates that should be currently displayed in calendar [#536](https://github.com/t1m0n/air-datepicker/issues/536)
* `toggleSelected` now can be a function [#534](https://github.com/t1m0n/air-datepicker/issues/534)

Expand Down
120 changes: 61 additions & 59 deletions dist/air-datepicker.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,78 +44,80 @@ export declare type AirDatepickerPositionCallback = (
done: () => void
}) => void | (() => void)

export declare type AirDatepickerOptions = {
classes: string
inline: boolean,
locale: Partial<AirDatepickerLocale>,
startDate: AirDatepickerDate,
firstDay: number,
isMobile: boolean,
visible: boolean,
weekends: [number, number],
dateFormat: string | ((d: Date) => string),
altField: AirDatepickerSelector,
altFieldDateFormat: string,
toggleSelected: boolean,
keyboardNav: boolean,
selectedDates: AirDatepickerDate[] | false,
container: AirDatepickerSelector,
position: AirDatepickerPosition | AirDatepickerPositionCallback,
offset: number,
view: AirDatepickerViews,
minView: AirDatepickerViews,
showOtherMonths: boolean,
selectOtherMonths: boolean,
moveToOtherMonthsOnSelect: boolean,
showOtherYears: boolean,
selectOtherYears: boolean,
moveToOtherYearsOnSelect: boolean,
minDate: AirDatepickerDate | false,
maxDate: AirDatepickerDate | false,
disableNavWhenOutOfRange: true,
multipleDates: number | true | false,
multipleDatesSeparator: string,
range: boolean,
dynamicRange: boolean,
buttons: AirDatepickerButtonPresets | AirDatepickerButton | (AirDatepickerButtonPresets| AirDatepickerButton)[] | false,
monthsField: keyof AirDatepickerLocale,
showEvent: string,
autoClose: boolean,
prevHtml: string,
nextHtml: string,
navTitles: {
export declare type AirDatepickerOptions<E extends HTMLElement = HTMLInputElement> = {
classes?: string
inline?: boolean,
locale?: Partial<AirDatepickerLocale>,
startDate?: AirDatepickerDate,
firstDay?: number,
isMobile?: boolean,
visible?: boolean,
weekends?: [number, number],
dateFormat?: string | ((d: Date) => string),
altField?: AirDatepickerSelector,
altFieldDateFormat?: string,
toggleSelected?: boolean | (({datepicker, date}:{datepicker: AirDatepicker<E>, date: Date}) => boolean),
keyboardNav?: boolean,
selectedDates?: AirDatepickerDate[] | false,
container?: AirDatepickerSelector,
position?: AirDatepickerPosition | AirDatepickerPositionCallback,
offset?: number,
view?: AirDatepickerViews,
minView?: AirDatepickerViews,
showOtherMonths?: boolean,
selectOtherMonths?: boolean,
moveToOtherMonthsOnSelect?: boolean,
showOtherYears?: boolean,
selectOtherYears?: boolean,
moveToOtherYearsOnSelect?: boolean,
minDate?: AirDatepickerDate | false,
maxDate?: AirDatepickerDate | false,
disableNavWhenOutOfRange?: true,
multipleDates?: number | true | false,
multipleDatesSeparator?: string,
range?: boolean,
dynamicRange?: boolean,
buttons?: AirDatepickerButtonPresets | AirDatepickerButton | (AirDatepickerButtonPresets| AirDatepickerButton)[] | false,
monthsField?: keyof AirDatepickerLocale,
showEvent?: string,
autoClose?: boolean,
prevHtml?: string,
nextHtml?: string,
navTitles?: {
days?: AirDatepickerNavEntry,
months?: AirDatepickerNavEntry,
years?: AirDatepickerNavEntry
},
timepicker: boolean,
onlyTimepicker: boolean,
dateTimeSeparator: string,
timeFormat: string,
minHours: number,
maxHours: number,
minMinutes: number,
maxMinutes: number,
hoursStep: number,
minutesStep: number,
timepicker?: boolean,
onlyTimepicker?: boolean,
dateTimeSeparator?: string,
timeFormat?: string,
minHours?: number,
maxHours?: number,
minMinutes?: number,
maxMinutes?: number,
hoursStep?: number,
minutesStep?: number,

onSelect: ({date, formattedDate, datepicker}: {date: Date | Date[], formattedDate: string | string[], datepicker: AirDatepicker}) => void,
onChangeViewDate: ({month, year, decade}: {month: number, year: number, decade: AirDatepickerDecade}) => void,
onChangeView: (view: AirDatepickerViews) => void,
onRenderCell: (params: {date: Date, cellType: AirDatepickerViewsSingle, datepicker: AirDatepicker}) => ({
onSelect?: ({date, formattedDate, datepicker}: {date: Date | Date[], formattedDate: string | string[], datepicker: AirDatepicker<E>}) => void,
onChangeViewDate?: ({month, year, decade}: {month: number, year: number, decade: AirDatepickerDecade}) => void,
onChangeView?: (view: AirDatepickerViews) => void,
onRenderCell?: (params: {date: Date, cellType: AirDatepickerViewsSingle, datepicker: AirDatepicker<E>}) => ({
disabled?: boolean,
classes?: string,
html?: string
attrs?: Record<string, string | number | undefined>
} | void),
onShow: (isAnimationComplete: boolean) => void,
onHide: (isAnimationComplete: boolean) => void,
onClickDayName: ({dayIndex, datepicker}: {dayIndex: number, datepicker: AirDatepicker}) => void
onShow?: (isAnimationComplete: boolean) => void,
onHide?: (isAnimationComplete: boolean) => void,
onClickDayName?: ({dayIndex, datepicker}: {dayIndex: number, datepicker: AirDatepicker<E>}) => void
onBeforeSelect?: ({date, datepicker}: {date: Date, datepicker: AirDatepicker}) => boolean;
onFocus?: ({date, datepicker}: {date: Date, datepicker: AirDatepicker}) => void;
}


declare class AirDatepicker<E extends HTMLElement = HTMLInputElement> {
constructor(el: string | E, opts? : Partial<AirDatepickerOptions>)
constructor(el: string | E, opts? : AirDatepickerOptions<E>)
static defaults: AirDatepickerOptions
static version: string
static defaultGlobalContainerId: string
Expand All @@ -129,7 +131,7 @@ declare class AirDatepicker<E extends HTMLElement = HTMLInputElement> {
clear: () => void
formatDate: (date: AirDatepickerDate, format: string) => string
destroy: () => void
update: (newOpts: Partial<AirDatepickerOptions>) => void
update: (newOpts?: AirDatepickerOptions) => void
setCurrentView: (newView: AirDatepickerViews) => void
setViewDate: (newViewDate: AirDatepickerDate) => void
setFocusDate: (date: AirDatepickerDate | false, opts?: {viewDateTransition?: boolean}) => void
Expand Down
2 changes: 1 addition & 1 deletion dist/air-datepicker.js

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions dist/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
"browser": "open-cli http://localhost:8080 -- 'chrome' ",
"build": "node scripts/build.js",
"lint-js": "eslint --fix ./src/*.js",
"test": "jest"
"test": "jest",
"check-gzip-size": "node scripts/checkGzipSize.js"
},
"devDependencies": {
"@babel/cli": "^7.20.7",
"@babel/core": "^7.20.12",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/preset-env": "^7.20.2",
"@popperjs/core": "^2.10.2",
Expand All @@ -32,12 +34,13 @@
"html-webpack-plugin": "^5.3.1",
"jest": "^26.6.3",
"mini-css-extract-plugin": "^1.6.0",
"node-gzip": "^1.1.2",
"open-cli": "^6.0.1",
"postcss": "^8.4.21",
"postcss-loader": "^6.2.1",
"pug": "^3.0.2",
"rimraf": "^2.7.1",
"sass": "^1.57.1",
"sass": "^1.63.4",
"sass-loader": "^13.2.0",
"style-loader": "^3.3.1",
"typescript": "^4.5.4",
Expand Down
File renamed without changes.
59 changes: 59 additions & 0 deletions docs/examples/code/disabledRangeDateExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export const disabledRangeDateExample = (msg) =>
`import AirDatepicker from 'air-datepicker';
import isWithinInterval from 'date-fns/isWithinInterval';
import isEqual from 'date-fns/isEqual';
const disabledDate = new Date('2023-07-13T00:00:00');
// ${msg.exampleRangeDisabledComment}
const isDisabledDateIsInRange = ({date, datepicker}) => {
const selectedDate = datepicker.selectedDates[0];
if (selectedDate && datepicker.selectedDates.length === 1) {
const sortedDates = [selectedDate, date].toSorted((a, b) => {
if (a.getTime() > b.getTime()) {
return 1;
}
return -1;
})
return (isWithinInterval(disabledDate, {
start: sortedDates[0],
end: sortedDates[1]
}))
}
}
new AirDatepicker('#el', {
startDate: '2023-07-19',
range: true,
onBeforeSelect: ({date, datepicker}) => {
// ${msg.exampleRangeDisabledComment2}
return !isDisabledDateIsInRange({date, datepicker});
},
onFocus: ({date, datepicker}) => {
if (isDisabledDateIsInRange({date, datepicker}) || isEqual(date, disabledDate)) {
datepicker.$datepicker.classList.add('-disabled-range-')
} else {
datepicker.$datepicker.classList.remove('-disabled-range-')
}
}
onRenderCell: ({date}) => {
if (date.toLocaleDateString() === disabledDate.toLocaleDateString()) {
return {
disabled: true
}
}
}}
});
`


export const disabledRangeDateExampleCSS =
`
.air-datepicker.-disabled-range- {
--adp-cell-background-color-in-range: #eeeeee;
--adp-cell-background-color-selected: #d0d0d0;
}
`
50 changes: 50 additions & 0 deletions docs/examples/snippets/ExampleDisabledRange.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import AirDatepicker from 'components/airDatepicker';
import isWithinInterval from 'date-fns/isWithinInterval';
import isEqual from 'date-fns/isEqual';
import {useCallback} from 'react';

const disabledDate = new Date('2023-07-13T00:00:00');
const isDateBetweenRange = ({date, datepicker}) => {
const selectedDate = datepicker.selectedDates[0];
if (selectedDate && datepicker.selectedDates.length === 1) {
const sortedDates = [selectedDate, date].toSorted((a, b) => {
if (a.getTime() > b.getTime()) {
return 1;
}
return -1;
})

return (isWithinInterval(disabledDate, {
start: sortedDates[0],
end: sortedDates[1]
}))
}
}
export const ExampleDisabledRange = () => {
const onBeforeSelect = useCallback(({date, datepicker}) => {
return !isDateBetweenRange({date, datepicker});
}, [])

const onFocus = useCallback(({date, datepicker}) => {
if (isDateBetweenRange({date, datepicker}) || isEqual(date, disabledDate)) {
datepicker.$datepicker.classList.add('-disabled-range-')
} else {
datepicker.$datepicker.classList.remove('-disabled-range-')
}
}, [])

return <AirDatepicker
startDate={'2023-07-19'}
inlineInput
range
onBeforeSelect={onBeforeSelect}
onFocus={onFocus}
onRenderCell={({date}) => {
if (date.toLocaleDateString() === disabledDate.toLocaleDateString()) {
return {
disabled: true
}
}
}}
/>
}
8 changes: 8 additions & 0 deletions docs/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export default {

exampleRangeTitle: 'Range of dates',
exampleRangeNote: 'For choosing range of dates use parameter {param}. After selecting both dates you could correct them by dragging active dates.',
exampleRangeDisabledTitle: 'Prohibiting range selection',
exampleRangeDisabledNote: 'Sometimes there is a need to prohibit the user from selecting a date range if there are disabled cells in it, for this you can use the options {onBeforeSelectLink} and {onFocusLink}, as well as a small stylization of our range. Try to select a range from July 9 to 15:',
exampleRangeDisabledComment: 'Check if disabled date is in the range',
exampleRangeDisabledComment2: 'Dont allow user to select date, if disabled date is in the range',
exampleRangeMinMaxTitle: 'Minimum and maximum dates',
exampleRangeMinMaxNote: 'For manipulation with minimum and maximum possible dates use options {minDate} and {maxDate}. In combination with {update} function you could implement limited date range that user could choose.',
exampleTimeTitle: 'Timepicker',
Expand Down Expand Up @@ -190,6 +194,8 @@ export default {
eventsOnSelectDate: 'selected date, if multiple date mode is on, then array of dates will be passed.',
eventsOnSelectFormattedDate: 'formatted selected date, or array in case of multiple dates mode.',
eventsOnSelectAirDatepicker: 'calendar instance',
eventsOnBeforeSelect: 'Triggered before cell should be selected. If returns {trueField} then cell will be selected, if {falseField} then not',
eventsOnBeforeSelectDate: 'the date to be selected',
eventsOnChangeViewDate: 'Triggered when navigating back and forth. It takes object as and argument with actual values of month, year and decade',
eventsOnChangeViewDateMonth: 'month of viewed date',
eventsOnChangeViewDateYear: 'year of viewed date',
Expand All @@ -212,6 +218,8 @@ export default {
eventsOnClickDayName: 'Adds possibility to click on the names of days in calendar. In case callback is passed {className} will be added to those elements',
eventsOnClickDayNameDayIndex: 'day of week index, where is 0 - Sunday, 6 - Saturday',
eventsOnClickDayNameDatepicker: 'calendar instance',
eventsOnFocus: 'Callback which is triggered when cell receives focus',
eventsOnFocusDate: 'date on which focus is triggered',

apiMethodsTitle: 'Methods',
apiPropertiesTitle: 'Properties',
Expand Down
8 changes: 8 additions & 0 deletions docs/locales/ru.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export default {

exampleRangeTitle: 'Диапазон дат',
exampleRangeNote: 'Для того, чтобы выбрать диапазон дат, передайте параметр {param}. После выбора обеих дат, можно скорректировать выбор просто перетаскивая активные даты.',
exampleRangeDisabledTitle: 'Запрет на выбор диапазона',
exampleRangeDisabledNote: 'Иногда есть необходимость запретить пользователю выбирать диапазон дат, если в нем есть недопустимые ячейки, для этого можно воспользоваться опциями {onBeforeSelectLink} и {onFocusLink}, а также небольшой стилизацией нашего диапазона. Попробуйте выбрать диапазон от 10 Июля до 16:',
exampleRangeDisabledComment: 'Проверяем, находится ли недопустимая дата в диапазоне',
exampleRangeDisabledComment2: 'Не даем выбрать дату, если недопустимая дата в диапазоне',
exampleRangeMinMaxTitle: 'Минимальная и максимальные даты',
exampleRangeMinMaxNote: 'Для манипуляции с минимально возможной и максимально возможной датой используйте опции {minDate} и {maxDate}. В сочетании с функцией {update} можно реализовать выбор дат только в ограниченном диапазоне.',
exampleTimeTitle: 'Выбор времени',
Expand Down Expand Up @@ -190,6 +194,8 @@ export default {
eventsOnSelectDate: 'выбранная дата, если включен режим выбора нескольких дат, то будет передан массив.',
eventsOnSelectFormattedDate: 'отформатированная выбранная дата, или массив в случае с мульти выбором.',
eventsOnSelectAirDatepicker: 'экземпляр календаря',
eventsOnBeforeSelect: 'Срабатывает до выбора даты. Если возвращает {trueField}, то дата будет выбрана, если {falseField} - нет. Полезно когда нужно запретить выбирать дату, но при этом сохранить обычный вид ячейки.',
eventsOnBeforeSelectDate: 'дата, которая должна быть выбрана',
eventsOnChangeViewDate: 'Срабатывает при перелистывании календаря вперед или назад. Получает в качестве аргумента объект с актуальными значениям месяца, года и декады.',
eventsOnChangeViewDateMonth: 'месяц просматриваемой даты',
eventsOnChangeViewDateYear: 'год просматриваемой даты',
Expand All @@ -212,6 +218,8 @@ export default {
eventsOnClickDayName: 'Добавляет возможность кликать по названиям дней в календаре. При этом, этим элементах будет добавлен класс {className}',
eventsOnClickDayNameDayIndex: 'индекс дня недели, где 0 - воскресенье, 6 - суббота',
eventsOnClickDayNameDatepicker: 'экземпляр календаря',
eventsOnFocus: 'Событие, срабатывающее при фокусировании на ячейке',
eventsOnFocusDate: 'дата, на которой срабатывает фокус',

apiMethodsTitle: 'Методы',
apiPropertiesTitle: 'Свойства',
Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"classnames": "^2.2.6",
"clone-deep": "^4.0.1",
"copy-to-clipboard": "^3.3.1",
"date-fns": "^2.23.0",
"date-fns": "^2.30.0",
"fuzzysearch": "^1.0.3",
"github-buttons": "^2.18.1",
"next": "12.1.6",
Expand Down
Loading

0 comments on commit a0b34b3

Please sign in to comment.