Skip to content

Commit

Permalink
feat: add multiple mode
Browse files Browse the repository at this point in the history
  • Loading branch information
farhoudshapouran committed Jan 26, 2024
1 parent 5e35de1 commit 8151867
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 40 deletions.
59 changes: 40 additions & 19 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,44 +38,34 @@ const Locales = ['en', 'de', 'es', 'fr', 'tr'];
export default function App() {
const [mode, setMode] = useState<ModeType>('single');

const [date, setDate] = useState<DateType>();
const [date, setDate] = useState<DateType | undefined>();
const [range, setRange] = React.useState<{
startDate: DateType;
endDate: DateType;
}>({ startDate: undefined, endDate: undefined });
const [dates, setDates] = useState<DateType[] | undefined>();

const [theme, setTheme] = useState<ITheme | undefined>(Themes[0]);
const [locale, setLocale] = useState('en');

const onChangeSingle = useCallback(
(params: any) => {
setDate(params.date);
},
[setDate]
);

const onChaneRange = useCallback(
({ startDate, endDate }: any) => {
setRange({ startDate, endDate });
},
[setRange]
);

const onChangeMode = useCallback(
(value: ModeType) => {
setDate(undefined);
setRange({ startDate: undefined, endDate: undefined });
setDates(undefined);
setMode(value);
},
[setMode, setDate, setRange]
[setMode, setDate, setRange, setDates]
);

const onChange = useCallback(
(params) => {
if (mode === 'single') {
onChangeSingle(params);
setDate(params.date);
} else if (mode === 'range') {
onChaneRange(params);
setRange(params);
} else if (mode === 'multiple') {
setDates(params.dates);
}
},
[mode]
Expand Down Expand Up @@ -182,6 +172,26 @@ export default function App() {
Range
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.modeSelect,
// eslint-disable-next-line react-native/no-inline-styles
{
backgroundColor:
mode === 'multiple' ? theme?.mainColor : undefined,
},
]}
onPress={() => onChangeMode('multiple')}
>
<Text
style={[
styles.modeSelectText,
mode === 'multiple' && { color: theme?.activeTextColor },
]}
>
Multiple
</Text>
</TouchableOpacity>
</View>
<View style={styles.datePickerContainer}>
<View style={styles.datePicker}>
Expand All @@ -190,6 +200,7 @@ export default function App() {
date={date}
startDate={range.startDate}
endDate={range.endDate}
dates={dates}
//minDate={dayjs().startOf('day')}
//maxDate={dayjs().add(3, 'day').endOf('day')}
//firstDayOfWeek={1}
Expand Down Expand Up @@ -217,7 +228,7 @@ export default function App() {
.format('MMMM, DD, YYYY - HH:mm')}
</Text>
<Pressable
onPress={() => onChangeSingle({ date: dayjs() })}
onPress={() => setDate(dayjs())}
accessibilityRole="button"
accessibilityLabel="Today"
>
Expand Down Expand Up @@ -261,6 +272,16 @@ export default function App() {
: '...'}
</Text>
</View>
) : mode === 'multiple' ? (
<View style={{ gap: 3 }}>
<Text style={{ fontWeight: 'bold' }}>Selected Dates:</Text>
{dates &&
dates.map((d, index) => (
<Text key={index}>
{dayjs(d).locale(locale).format('MMMM, DD, YYYY')}
</Text>
))}
</View>
) : null}
</View>
</View>
Expand Down
80 changes: 61 additions & 19 deletions src/DateTimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
dateToUnix,
getEndOfDay,
getStartOfDay,
areDatesOnSameDay,
} from './utils';
import CalendarContext from './CalendarContext';
import { CalendarViews, CalendarActionKind } from './enums';
Expand All @@ -15,13 +16,15 @@ import type {
DatePickerBaseProps,
CalendarThemeProps,
HeaderProps,
SingleChange,
RangeChange,
MultiChange,
} from './types';
import Calendar from './components/Calendar';
import dayjs from 'dayjs';
import localeData from 'dayjs/plugin/localeData';
import relativeTime from 'dayjs/plugin/relativeTime';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import { SingleChange, RangeChange, MultiChange } from './types';

dayjs.extend(localeData);
dayjs.extend(relativeTime);
Expand Down Expand Up @@ -72,7 +75,7 @@ const DateTimePicker = (
date,
startDate,
endDate,
// dates,
dates,
onChange,
...rest
} = props;
Expand All @@ -82,6 +85,22 @@ const DateTimePicker = (
? firstDayOfWeek
: 0;

let currentDate = dayjs();

if (mode === 'single' && date) {
currentDate = dayjs(date);
}

if (mode === 'range' && startDate) {
currentDate = dayjs(startDate);
}

if (mode === 'multiple' && dates && dates.length > 0) {
currentDate = dayjs(dates[0]);
}

let currentYear = currentDate.year();

dayjs.locale(locale);

const [state, dispatch] = useReducer(
Expand Down Expand Up @@ -116,16 +135,22 @@ const DateTimePicker = (
startDate,
endDate,
};
case CalendarActionKind.CHANGE_SELECTED_MULTIPLE:
const { dates } = action.payload;

Check warning on line 139 in src/DateTimePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

'dates' is already declared in the upper scope on line 78 column 5
return {
...prevState,
dates,
};
}
},
{
date: undefined,
startDate: undefined,
endDate: undefined,
dates: [],
date,
startDate,
endDate,
dates,
calendarView: CalendarViews.day,
currentDate: new Date(),
currentYear: new Date().getFullYear(),
currentDate,
currentYear,
}
);

Expand All @@ -140,8 +165,13 @@ const DateTimePicker = (
type: CalendarActionKind.CHANGE_SELECTED_RANGE,
payload: { startDate, endDate },
});
} else if (mode === 'multiple') {
dispatch({
type: CalendarActionKind.CHANGE_SELECTED_MULTIPLE,
payload: { dates },
});
}
}, [mode, date, startDate, endDate]);
}, [mode, date, startDate, endDate, dates]);

const setCalendarView = useCallback((view: CalendarViews) => {
dispatch({ type: CalendarActionKind.SET_CALENDAR_VIEW, payload: view });
Expand All @@ -150,11 +180,12 @@ const DateTimePicker = (
const onSelectDate = useCallback(
(date: DateType) => {

Check warning on line 181 in src/DateTimePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

'date' is already declared in the upper scope on line 75 column 5
if (onChange) {
dispatch({
type: CalendarActionKind.CHANGE_CURRENT_DATE,
payload: date,
});
if (mode === 'single') {
dispatch({
type: CalendarActionKind.CHANGE_CURRENT_DATE,
payload: date,
});

(onChange as SingleChange)({
date,
});
Expand All @@ -172,13 +203,26 @@ const DateTimePicker = (
endDate: !isStart ? getEndOfDay(date) : undefined,
});
} else if (mode === 'multiple') {
// (onChange as MultiChange)({
// dates: [date],
// });
const safeDates = (state.dates as DateType[]) || [];
const newDate = getStartOfDay(date);

const exists = safeDates.some((ed) => areDatesOnSameDay(ed, newDate));

const newDates = exists
? safeDates.filter((ed) => !areDatesOnSameDay(ed, newDate))
: [...safeDates, newDate];

newDates.sort((a, b) => (dayjs(a).isAfter(dayjs(b)) ? 1 : -1));

(onChange as MultiChange)({
dates: newDates,
datePressed: newDate,
change: exists ? 'removed' : 'added',
});
}
}
},
[onChange, mode, state.startDate, state.endDate]
[onChange, mode, state.startDate, state.endDate, state.dates]
);

const onSelectMonth = useCallback(
Expand Down Expand Up @@ -233,8 +277,6 @@ const DateTimePicker = (
<CalendarContext.Provider
value={{
...state,
startDate,
endDate,
locale,
mode,
displayFullDays,
Expand Down
45 changes: 44 additions & 1 deletion src/components/DaySelector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useMemo, useCallback } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import dayjs from 'dayjs';
import { useCalendarContext } from '../CalendarContext';
import Day, { EmptyDay } from './Day';
import {
Expand All @@ -16,10 +17,11 @@ import {
const DaySelector = () => {
const {
mode,
date,
startDate,
endDate,
dates,
currentDate,
date,
onSelectDate,
displayFullDays,
minDate,
Expand Down Expand Up @@ -85,6 +87,46 @@ const DaySelector = () => {
) {
inRange = false;
}
} else if (mode === 'multiple') {
const safeDates = dates || [];
isSelected = safeDates.some((d) => areDatesOnSameDay(day.date, d));

const yesterday = dayjs(day.date).add(-1, 'day');
const tomorrow = dayjs(day.date).add(1, 'day');

const yesterdaySelected = safeDates.some((d) =>
areDatesOnSameDay(d, yesterday)
);
const tomorrowSelected = safeDates.some((d) =>
areDatesOnSameDay(d, tomorrow)
);

if (isSelected) {
if (tomorrowSelected && yesterdaySelected) {
inRange = true;
}
if (tomorrowSelected && !yesterdaySelected) {
inRange = true;
leftCrop = true;
}

if (yesterdaySelected && !tomorrowSelected) {
inRange = true;
rightCrop = true;
}

if (isFirstDayOfMonth && !tomorrowSelected) {
inRange = false;
}

if (isLastDayOfMonth && !yesterdaySelected) {
inRange = false;
}

if (inRange && !leftCrop && !rightCrop) {
isSelected = false;
}
}
} else if (mode === 'single') {
isSelected = areDatesOnSameDay(day.date, date);
}
Expand Down Expand Up @@ -114,6 +156,7 @@ const DaySelector = () => {
date,
startDate,
endDate,
dates,
]
);

Expand Down
1 change: 1 addition & 0 deletions src/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export enum CalendarActionKind {
CHANGE_CURRENT_YEAR = 'CHANGE_CURRENT_YEAR',
CHANGE_SELECTED_DATE = 'CHANGE_SELECTED_DATE',
CHANGE_SELECTED_RANGE = 'CHANGE_SELECTED_RANGE',
CHANGE_SELECTED_MULTIPLE = 'CHANGE_SELECTED_MULTIPLE',
}

export const CALENDAR_HEIGHT = 300;
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export type RangeChange = (params: {

export type MultiChange = (params: {
dates: DateType[];
datePressed: Date;
datePressed: DateType;
change: 'added' | 'removed';
}) => any;

Expand Down

0 comments on commit 8151867

Please sign in to comment.