Skip to content

Commit faf885f

Browse files
authored
Merge pull request #30 from NoahMLoomis/add-dates-restriction-props
Add dates disabling/restriction props
2 parents 08d9736 + 05c5207 commit faf885f

File tree

9 files changed

+270
-38
lines changed

9 files changed

+270
-38
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
- ✅ TypeScript support
3232
- ✅ Localization(i18n)
3333
- ✅ Date formatting
34-
- ⬜ Disable specific dates
34+
- ✅ Disable specific dates
35+
- ✅ Minimum Date and Maximum Date
3536
- ⬜ Custom shortcuts
3637

3738
## Documentation

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"pret:fix": "prettier -w .",
1717
"format": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc",
1818
"build": "npm run pret && npm run lint && npm run clean && rollup -c",
19+
"pub": "npm run build && npm publish",
1920
"dev": "next dev -p 8888"
2021
},
2122
"repository": {

pages/index.js

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ export default function Playground() {
2323
const [containerClassName, setContainerClassName] = useState("");
2424
const [displayFormat, setDisplayFormat] = useState("YYYY-MM-DD");
2525
const [readOnly, setReadOnly] = useState(false);
26-
const [startFrom, setStartFrom] = useState("2023-03-01");
26+
const [startFrom, setStartFrom] = useState("2023-01-02");
27+
const [minDate, setMinDate] = useState("");
28+
const [maxDate, setMaxDate] = useState("");
29+
const [disabledDates, setDisabledDates] = useState([]);
30+
const [newDisabledDates, setNewDisabledDates] = useState({ startDate: "", endDate: "" });
2731

2832
return (
2933
<div className="px-4 py-8">
@@ -58,6 +62,9 @@ export default function Playground() {
5862
containerClassName={containerClassName}
5963
displayFormat={displayFormat}
6064
readOnly={readOnly}
65+
minDate={minDate}
66+
maxDate={maxDate}
67+
disabledDates={disabledDates}
6168
/>
6269
</div>
6370

@@ -207,6 +214,19 @@ export default function Playground() {
207214
}}
208215
/>
209216
</div>
217+
<div className="mb-2">
218+
<label className="block" htmlFor="minDate">
219+
Minimum Date
220+
</label>
221+
<input
222+
className="rounded border px-4 py-2 w-full border-gray-200"
223+
id="minDate"
224+
value={minDate}
225+
onChange={e => {
226+
setMinDate(e.target.value);
227+
}}
228+
/>
229+
</div>
210230
</div>
211231
<div className="w-full sm:w-1/3 pr-2 flex flex-col">
212232
<div className="mb-2">
@@ -261,6 +281,97 @@ export default function Playground() {
261281
}}
262282
/>
263283
</div>
284+
<div className="mb-2">
285+
<label className="block" htmlFor="maxDate">
286+
Maximum Date
287+
</label>
288+
<input
289+
className="rounded border px-4 py-2 w-full border-gray-200"
290+
id="maxDate"
291+
value={maxDate}
292+
onChange={e => {
293+
setMaxDate(e.target.value);
294+
}}
295+
/>
296+
</div>
297+
</div>
298+
<div className="w-full grid sm:grid-cols-3">
299+
<div className="sm:col-start-2 sm:col-span-2 p-2 border-t grid grid-cols-2">
300+
<h1 className="mb-2 text-lg font-semibold text-center col-span-3">
301+
Disable Dates
302+
</h1>
303+
<div className="mb-2 sm:col-span-2 mr-2">
304+
<label className="block" htmlFor="startDate">
305+
Start Date
306+
</label>
307+
<input
308+
className="rounded border px-4 py-2 border-gray-200 sm:w-full w-3/4"
309+
id="startDate"
310+
value={newDisabledDates.startDate}
311+
onChange={e => {
312+
setNewDisabledDates(prev => {
313+
return {
314+
...prev,
315+
startDate: e.target.value
316+
};
317+
});
318+
}}
319+
/>
320+
</div>
321+
<div className="mb-2">
322+
<label className="block" htmlFor="endDate">
323+
End Date
324+
</label>
325+
<input
326+
className="rounded border px-4 py-2 border-gray-200 sm:w-full w-3/4"
327+
id="endDate"
328+
value={newDisabledDates.endDate}
329+
onChange={e => {
330+
setNewDisabledDates(prev => {
331+
return {
332+
...prev,
333+
endDate: e.target.value
334+
};
335+
});
336+
}}
337+
/>
338+
</div>
339+
<div className="mb-2 col-span-3">
340+
<button
341+
onClick={() => {
342+
if (
343+
newDisabledDates.startDate !== "" &&
344+
newDisabledDates.endDate !== ""
345+
) {
346+
setDisabledDates(prev => [...prev, newDisabledDates]);
347+
setNewDisabledDates({ startDate: "", endDate: "" });
348+
}
349+
}}
350+
className="w-full bg-black text-white text-lg text-center p-2 rounded-lg"
351+
>
352+
Add
353+
</button>
354+
</div>
355+
<div className="mb-2 grid col-span-3 grid-col-2">
356+
{disabledDates.map((range, index) => (
357+
<div className="mb-2 p-2" key={index}>
358+
<button
359+
className="bg-red-500 text-white text-center rounded-xl p-2"
360+
onClick={() => {
361+
setDisabledDates(
362+
disabledDates.filter(r => r !== range)
363+
);
364+
}}
365+
>
366+
Delete
367+
</button>
368+
<span className="pl-2">
369+
{range.startDate} - {range.endDate}
370+
</span>
371+
</div>
372+
))}
373+
</div>
374+
</div>
264375
</div>
265376
</div>
266377
<div className="flex flex-row flex-wrap items-center justify-center w-full">

src/components/Calendar/Days.tsx

Lines changed: 110 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import React, { useCallback, useContext } from "react";
33

44
import { BG_COLOR } from "../../constants";
55
import DatepickerContext from "../../contexts/DatepickerContext";
6-
import { formatDate, getTextColorByPrimaryColor, nextMonth, previousMonth } from "../../helpers";
6+
import {
7+
formatDate,
8+
getTextColorByPrimaryColor,
9+
nextMonth,
10+
previousMonth,
11+
classNames as cn
12+
} from "../../helpers";
713

814
const isBetween = require("dayjs/plugin/isBetween");
915
dayjs.extend(isBetween);
@@ -29,8 +35,16 @@ const Days: React.FC<Props> = ({
2935
onClickNextDays
3036
}) => {
3137
// Contexts
32-
const { primaryColor, period, changePeriod, dayHover, changeDayHover } =
33-
useContext(DatepickerContext);
38+
const {
39+
primaryColor,
40+
period,
41+
changePeriod,
42+
dayHover,
43+
changeDayHover,
44+
minDate,
45+
maxDate,
46+
disabledDates
47+
} = useContext(DatepickerContext);
3448

3549
// Functions
3650
const currentDateClass = useCallback(
@@ -139,16 +153,97 @@ const Days: React.FC<Props> = ({
139153
[calendarData.date, currentDateClass, dayHover, period.end, period.start, primaryColor]
140154
);
141155

142-
const buttonCass = useCallback(
143-
(day: number) => {
144-
const baseClass = "flex items-center justify-center w-12 h-12 lg:w-10 lg:h-10";
145-
return `${baseClass}${
146-
!activeDateData(day).active
147-
? ` ${hoverClassByDay(day)}`
148-
: activeDateData(day).className
156+
const isDateTooEarly = useCallback(
157+
(day: number, type: string) => {
158+
if (!minDate) {
159+
return false;
160+
}
161+
const object = {
162+
previous: previousMonth(calendarData.date),
163+
current: calendarData.date,
164+
next: nextMonth(calendarData.date)
165+
};
166+
const newDate = object[type as keyof typeof object];
167+
const formattedDate = `${newDate.year()}-${newDate.month() + 1}-${
168+
day >= 10 ? day : "0" + day
169+
}`;
170+
return dayjs(formattedDate).isSame(dayjs(minDate))
171+
? false
172+
: dayjs(formattedDate).isBefore(dayjs(minDate));
173+
},
174+
[calendarData.date, minDate]
175+
);
176+
177+
const isDateTooLate = useCallback(
178+
(day: number, type: string) => {
179+
if (!maxDate) {
180+
return false;
181+
}
182+
const object = {
183+
previous: previousMonth(calendarData.date),
184+
current: calendarData.date,
185+
next: nextMonth(calendarData.date)
186+
};
187+
const newDate = object[type as keyof typeof object];
188+
const formattedDate = `${newDate.year()}-${newDate.month() + 1}-${
189+
day >= 10 ? day : "0" + day
149190
}`;
191+
return dayjs(formattedDate).isSame(maxDate)
192+
? false
193+
: dayjs(formattedDate).isAfter(dayjs(maxDate));
194+
},
195+
[calendarData.date, maxDate]
196+
);
197+
198+
const isDateDisabled = useCallback(
199+
(day: number, type: string) => {
200+
if (isDateTooEarly(day, type) || isDateTooLate(day, type)) {
201+
return true;
202+
}
203+
const object = {
204+
previous: previousMonth(calendarData.date),
205+
current: calendarData.date,
206+
next: nextMonth(calendarData.date)
207+
};
208+
const newDate = object[type as keyof typeof object];
209+
const formattedDate = `${newDate.year()}-${newDate.month() + 1}-${
210+
day >= 10 ? day : "0" + day
211+
}`;
212+
213+
if (!disabledDates || disabledDates?.length <= 0) {
214+
return false;
215+
}
216+
217+
let matchingCount = 0;
218+
disabledDates?.forEach(dateRange => {
219+
if (
220+
dayjs(formattedDate).isAfter(dateRange.startDate) &&
221+
dayjs(formattedDate).isBefore(dateRange.endDate)
222+
) {
223+
matchingCount++;
224+
}
225+
if (
226+
dayjs(formattedDate).isSame(dateRange.startDate) ||
227+
dayjs(formattedDate).isSame(dateRange.endDate)
228+
) {
229+
matchingCount++;
230+
}
231+
});
232+
return matchingCount > 0;
233+
},
234+
[calendarData.date, isDateTooEarly, isDateTooLate]
235+
);
236+
237+
const buttonClass = useCallback(
238+
(day: number, type: string) => {
239+
const baseClass = "flex items-center justify-center w-12 h-12 lg:w-10 lg:h-10";
240+
return cn(
241+
baseClass,
242+
!activeDateData(day).active ? hoverClassByDay(day) : activeDateData(day).className,
243+
isDateDisabled(day, type) && "line-through"
244+
);
150245
},
151-
[activeDateData, hoverClassByDay]
246+
[activeDateData, hoverClassByDay, isDateDisabled]
152247
);
153248

154249
const hoverDay = useCallback(
@@ -192,6 +287,7 @@ const Days: React.FC<Props> = ({
192287
<button
193288
type="button"
194289
key={index}
290+
disabled={isDateDisabled(item, "previous")}
195291
className="flex items-center justify-center text-gray-400 h-12 w-12 lg:w-10 lg:h-10"
196292
onClick={() => onClickPreviousDays(item)}
197293
onMouseOver={() => {
@@ -206,7 +302,8 @@ const Days: React.FC<Props> = ({
206302
<button
207303
type="button"
208304
key={index}
209-
className={buttonCass(item)}
305+
disabled={isDateDisabled(item, "current")}
306+
className={`${buttonClass(item, "current")}`}
210307
onClick={() => {
211308
onClickDay(item);
212309
}}
@@ -222,6 +319,7 @@ const Days: React.FC<Props> = ({
222319
<button
223320
type="button"
224321
key={index}
322+
disabled={isDateDisabled(index, "previous")}
225323
className="flex items-center justify-center text-gray-400 h-12 w-12 lg:w-10 lg:h-10"
226324
onClick={() => {
227325
onClickNextDays(item);

src/components/Calendar/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,13 @@ const Calendar: React.FC<Props> = ({
7979
);
8080
}, [current, date, previous]);
8181

82-
const hiddeMonths = useCallback(() => {
82+
const hideMonths = useCallback(() => {
8383
if (showMonths) {
8484
setShowMonths(false);
8585
}
8686
}, [showMonths]);
8787

88-
const hiddeYears = useCallback(() => {
88+
const hideYears = useCallback(() => {
8989
if (showYears) {
9090
setShowYears(false);
9191
}
@@ -254,7 +254,7 @@ const Calendar: React.FC<Props> = ({
254254
<RoundedButton
255255
onClick={() => {
256256
setShowMonths(!showMonths);
257-
hiddeYears();
257+
hideYears();
258258
}}
259259
>
260260
<>{shortString(calendarData.date.locale(i18n).format("MMM"))}</>
@@ -265,7 +265,7 @@ const Calendar: React.FC<Props> = ({
265265
<RoundedButton
266266
onClick={() => {
267267
setShowYears(!showYears);
268-
hiddeMonths();
268+
hideMonths();
269269
}}
270270
>
271271
<>{calendarData.date.year()}</>

0 commit comments

Comments
 (0)