Skip to content

Commit

Permalink
Merge pull request #139 from breeffy/issue-138
Browse files Browse the repository at this point in the history
Allow to disable unselection of last selected date
  • Loading branch information
likern authored Nov 25, 2021
2 parents ec47003 + 5bf0bc0 commit 4713c44
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 18 deletions.
32 changes: 25 additions & 7 deletions packages/calendars/src/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ export type CalendarProps = {
*/
selectionMode?: CalendarSelectionMode;

/**
* If calendar has only one (last) selected date,
* can we unselect it by tapping on this date?
* @defaultValue `true`
*/
allowDeselectLastSelectedDate?: boolean;

/**
* How much months can be scrolled over.
* @defaultValue `multipleMonths`
Expand All @@ -115,6 +122,7 @@ export type CalendarProps = {
* If provided, will be highlighted in active color.
*/
activeCalendarDay?: CalendarDate;

/**
* Theme object to customize calendar appearance
*/
Expand Down Expand Up @@ -153,6 +161,7 @@ export const Calendar = forwardRef<CalendarMethods, CalendarProps>(
scrollMode = 'oneMonth',
scrollModeDeceleration = 'normal',
activeCalendarDay: _activeCalendarDay,
allowDeselectLastSelectedDate = true,
theme = CalendarThemeLight,
performanceProps,
style: _containerStyle,
Expand Down Expand Up @@ -265,21 +274,28 @@ export const Calendar = forwardRef<CalendarMethods, CalendarProps>(
]
);

const [selectedDates, selectDate, deselectDate] = useSelectedDates(
selectionMode,
initialSelectedDates
);
const [selectedDates, isLastSelectedDate, selectDate, deselectDate] =
useSelectedDates(selectionMode, initialSelectedDates);

const onCalendarDayStateChange = useCallback(
(day: CalendarDate, calendarKind: CalendarDayKind) => {
if (calendarKind === CalendarDayKind.SELECTED) {
selectDate(day);
onDayStateChange?.(day, calendarKind);
} else if (calendarKind === CalendarDayKind.DEFAULT) {
deselectDate(day);
deselectDate(day, allowDeselectLastSelectedDate);
if (allowDeselectLastSelectedDate || !isLastSelectedDate) {
onDayStateChange?.(day, calendarKind);
}
}
onDayStateChange?.(day, calendarKind);
},
[selectDate, deselectDate, onDayStateChange]
[
allowDeselectLastSelectedDate,
isLastSelectedDate,
selectDate,
deselectDate,
onDayStateChange
]
);

useImperativeHandle(
Expand Down Expand Up @@ -308,6 +324,7 @@ export const Calendar = forwardRef<CalendarMethods, CalendarProps>(
selectDate: selectDate,
deselectDate: deselectDate,
selectedDates: selectedDates,
allowDeselectLastSelectedDate: allowDeselectLastSelectedDate,
monthsBefore,
monthsAfter,
startCalendarYearAndMonth: startCalendarYearAndMonth,
Expand All @@ -323,6 +340,7 @@ export const Calendar = forwardRef<CalendarMethods, CalendarProps>(
selectDate,
deselectDate,
selectedDates,
allowDeselectLastSelectedDate,
monthsBefore,
monthsAfter,
startCalendarYearAndMonth,
Expand Down
33 changes: 25 additions & 8 deletions packages/calendars/src/hooks/useSelectedDates.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { invariant } from '@breeffy/invariants';
import { isCalendarDatesEqual } from '../helpers';
import type { CalendarDate, CalendarSelectionMode } from '../types';
Expand All @@ -10,8 +10,12 @@ export const useSelectedDates = (
initialSelectedDates: CalendarDate[]
): [
selectedDates: SelectedDates,
isLastSelectedDate: boolean,
selectDate: (day: CalendarDate) => void,
deselectDate: (day: CalendarDate) => void
deselectDate: (
day: CalendarDate,
allowDeselectLastSelectedDate?: boolean
) => void
] => {
invariant(
selectionMode === 'multipleDays' ||
Expand All @@ -23,6 +27,11 @@ export const useSelectedDates = (

const [selectedDates, setSelectedDates] =
useState<SelectedDates>(initialSelectedDates);

const isLastSelectedDate = useMemo(() => {
return selectedDates.length === 1;
}, [selectedDates]);

const selectDate = useCallback(
(day: CalendarDate) => {
if (selectionMode === 'singleDay') {
Expand All @@ -34,11 +43,19 @@ export const useSelectedDates = (
[selectionMode]
);

const deselectDate = useCallback((day: CalendarDate) => {
setSelectedDates(prevSelectedDates =>
prevSelectedDates.filter(it => !isCalendarDatesEqual(it, day))
);
}, []);
const deselectDate = useCallback(
(day: CalendarDate, allowDeselectLastSelectedDate: boolean = true) => {
setSelectedDates(prevSelectedDates => {
const allowDeselect =
allowDeselectLastSelectedDate || !isLastSelectedDate;
if (allowDeselect) {
return prevSelectedDates.filter(it => !isCalendarDatesEqual(it, day));
}
return prevSelectedDates;
});
},
[isLastSelectedDate]
);

return [selectedDates, selectDate, deselectDate];
return [selectedDates, isLastSelectedDate, selectDate, deselectDate];
};
10 changes: 8 additions & 2 deletions packages/calendars/src/types/public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,15 @@ export interface CalendarMethods {
select: (date: CalendarDate) => void;
/**
* Deselect specific date on calendar.
* If it is not selected function call is no-op.
*
* If `date` is not selected function call is no-op.
*
* If `allowDeselectLastSelectedDate` is `false` and `date` is only selected date call is no-op.
*/
deselect: (date: CalendarDate) => void;
deselect: (
date: CalendarDate,
allowDeselectLastSelectedDate?: boolean | undefined
) => void;
}

export type CalendarCurrentAnimatedMonthFromCommonEra =
Expand Down
3 changes: 2 additions & 1 deletion packages/example-app/src/stories/Calendar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ const CalendarMeta: ComponentMeta<typeof Calendar> = {
monthsBefore: 24,
monthsAfter: 24,
scrollMode: 'oneMonth',
selectionMode: 'singleDay'
selectionMode: 'singleDay',
allowDeselectLastSelectedDate: true
}
};

Expand Down

0 comments on commit 4713c44

Please sign in to comment.