This commit is contained in:
parent
7c3d24255b
commit
69200c0ae5
|
|
@ -4,393 +4,464 @@ import React, { useCallback, useContext } from "react";
|
|||
|
||||
import { BG_COLOR, TEXT_COLOR } from "../../constants";
|
||||
import DatepickerContext from "../../contexts/DatepickerContext";
|
||||
import { formatDate, nextMonth, previousMonth, classNames as cn } from "../../helpers";
|
||||
import {
|
||||
formatDate,
|
||||
nextMonth,
|
||||
previousMonth,
|
||||
classNames as cn,
|
||||
} from "../../helpers";
|
||||
import { Period } from "../../types";
|
||||
|
||||
dayjs.extend(isBetween);
|
||||
|
||||
interface Props {
|
||||
calendarData: {
|
||||
date: dayjs.Dayjs;
|
||||
days: {
|
||||
previous: number[];
|
||||
current: number[];
|
||||
next: number[];
|
||||
};
|
||||
calendarData: {
|
||||
date: dayjs.Dayjs;
|
||||
days: {
|
||||
previous: number[];
|
||||
current: number[];
|
||||
next: number[];
|
||||
};
|
||||
onClickPreviousDays: (day: number) => void;
|
||||
onClickDay: (day: number) => void;
|
||||
onClickNextDays: (day: number) => void;
|
||||
};
|
||||
onClickPreviousDays: (day: number) => void;
|
||||
onClickDay: (day: number) => void;
|
||||
onClickNextDays: (day: number) => void;
|
||||
onIcon?: (day: number, date: Date) => any;
|
||||
}
|
||||
|
||||
const Days: React.FC<Props> = ({
|
||||
calendarData,
|
||||
onClickPreviousDays,
|
||||
onClickDay,
|
||||
onClickNextDays
|
||||
calendarData,
|
||||
onClickPreviousDays,
|
||||
onClickDay,
|
||||
onClickNextDays,
|
||||
onIcon,
|
||||
}) => {
|
||||
// Contexts
|
||||
const {
|
||||
primaryColor,
|
||||
period,
|
||||
changePeriod,
|
||||
dayHover,
|
||||
changeDayHover,
|
||||
minDate,
|
||||
maxDate,
|
||||
disabledDates
|
||||
} = useContext(DatepickerContext);
|
||||
// Contexts
|
||||
const {
|
||||
primaryColor,
|
||||
period,
|
||||
changePeriod,
|
||||
dayHover,
|
||||
changeDayHover,
|
||||
minDate,
|
||||
maxDate,
|
||||
disabledDates,
|
||||
} = useContext(DatepickerContext);
|
||||
|
||||
// Functions
|
||||
const currentDateClass = useCallback(
|
||||
(item: number) => {
|
||||
const itemDate = `${calendarData.date.year()}-${calendarData.date.month() + 1}-${
|
||||
item >= 10 ? item : "0" + item
|
||||
}`;
|
||||
if (formatDate(dayjs()) === formatDate(dayjs(itemDate)))
|
||||
return TEXT_COLOR["500"][primaryColor as keyof (typeof TEXT_COLOR)["500"]];
|
||||
return "";
|
||||
},
|
||||
[calendarData.date, primaryColor]
|
||||
);
|
||||
// Functions
|
||||
const currentDateClass = useCallback(
|
||||
(item: number) => {
|
||||
const itemDate = `${calendarData.date.year()}-${
|
||||
calendarData.date.month() + 1
|
||||
}-${item >= 10 ? item : "0" + item}`;
|
||||
if (formatDate(dayjs()) === formatDate(dayjs(itemDate)))
|
||||
return TEXT_COLOR["500"][
|
||||
primaryColor as keyof (typeof TEXT_COLOR)["500"]
|
||||
];
|
||||
return "";
|
||||
},
|
||||
[calendarData.date, primaryColor]
|
||||
);
|
||||
|
||||
const activeDateData = useCallback(
|
||||
(day: number) => {
|
||||
const fullDay = `${calendarData.date.year()}-${calendarData.date.month() + 1}-${day}`;
|
||||
let className = "";
|
||||
const activeDateData = useCallback(
|
||||
(day: number) => {
|
||||
const fullDay = `${calendarData.date.year()}-${
|
||||
calendarData.date.month() + 1
|
||||
}-${day}`;
|
||||
let className = "";
|
||||
|
||||
if (dayjs(fullDay).isSame(period.start) && dayjs(fullDay).isSame(period.end)) {
|
||||
className = ` ${BG_COLOR["500"][primaryColor]} c-text-white c-font-medium rounded-full`;
|
||||
} else if (dayjs(fullDay).isSame(period.start)) {
|
||||
className = ` ${BG_COLOR["500"][primaryColor]} c-text-white c-font-medium ${
|
||||
dayjs(fullDay).isSame(dayHover) && !period.end
|
||||
? "c-rounded-full"
|
||||
: "c-rounded-l-full"
|
||||
}`;
|
||||
} else if (dayjs(fullDay).isSame(period.end)) {
|
||||
className = ` ${BG_COLOR["500"][primaryColor]} c-text-white c-font-medium ${
|
||||
dayjs(fullDay).isSame(dayHover) && !period.start
|
||||
? "c-rounded-full"
|
||||
: "c-rounded-r-full"
|
||||
}`;
|
||||
}
|
||||
if (
|
||||
dayjs(fullDay).isSame(period.start) &&
|
||||
dayjs(fullDay).isSame(period.end)
|
||||
) {
|
||||
className = ` ${BG_COLOR["500"][primaryColor]} c-text-white c-font-medium rounded-full`;
|
||||
} else if (dayjs(fullDay).isSame(period.start)) {
|
||||
className = ` ${
|
||||
BG_COLOR["500"][primaryColor]
|
||||
} c-text-white c-font-medium ${
|
||||
dayjs(fullDay).isSame(dayHover) && !period.end
|
||||
? "c-rounded-full"
|
||||
: "c-rounded-l-full"
|
||||
}`;
|
||||
} else if (dayjs(fullDay).isSame(period.end)) {
|
||||
className = ` ${
|
||||
BG_COLOR["500"][primaryColor]
|
||||
} c-text-white c-font-medium ${
|
||||
dayjs(fullDay).isSame(dayHover) && !period.start
|
||||
? "c-rounded-full"
|
||||
: "c-rounded-r-full"
|
||||
}`;
|
||||
}
|
||||
|
||||
return {
|
||||
active: dayjs(fullDay).isSame(period.start) || dayjs(fullDay).isSame(period.end),
|
||||
className: className
|
||||
};
|
||||
},
|
||||
[calendarData.date, dayHover, period.end, period.start, primaryColor]
|
||||
);
|
||||
return {
|
||||
active:
|
||||
dayjs(fullDay).isSame(period.start) ||
|
||||
dayjs(fullDay).isSame(period.end),
|
||||
className: className,
|
||||
};
|
||||
},
|
||||
[calendarData.date, dayHover, period.end, period.start, primaryColor]
|
||||
);
|
||||
|
||||
const hoverClassByDay = useCallback(
|
||||
(day: number) => {
|
||||
let className = currentDateClass(day);
|
||||
const fullDay = `${calendarData.date.year()}-${calendarData.date.month() + 1}-${
|
||||
day >= 10 ? day : "0" + day
|
||||
}`;
|
||||
const hoverClassByDay = useCallback(
|
||||
(day: number) => {
|
||||
let className = currentDateClass(day);
|
||||
const fullDay = `${calendarData.date.year()}-${
|
||||
calendarData.date.month() + 1
|
||||
}-${day >= 10 ? day : "0" + day}`;
|
||||
|
||||
if (period.start && period.end) {
|
||||
if (dayjs(fullDay).isBetween(period.start, period.end, "day", "[)")) {
|
||||
return ` ${BG_COLOR["100"][primaryColor]} ${currentDateClass(
|
||||
day
|
||||
)} dark:bg-white/10`;
|
||||
}
|
||||
}
|
||||
if (period.start && period.end) {
|
||||
if (dayjs(fullDay).isBetween(period.start, period.end, "day", "[)")) {
|
||||
return ` ${BG_COLOR["100"][primaryColor]} ${currentDateClass(
|
||||
day
|
||||
)} dark:bg-white/10`;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dayHover) {
|
||||
return className;
|
||||
}
|
||||
if (!dayHover) {
|
||||
return className;
|
||||
}
|
||||
|
||||
if (period.start && dayjs(fullDay).isBetween(period.start, dayHover, "day", "[)")) {
|
||||
className = ` ${BG_COLOR["100"][primaryColor]} ${currentDateClass(
|
||||
day
|
||||
)} dark:bg-white/10`;
|
||||
}
|
||||
if (
|
||||
period.start &&
|
||||
dayjs(fullDay).isBetween(period.start, dayHover, "day", "[)")
|
||||
) {
|
||||
className = ` ${BG_COLOR["100"][primaryColor]} ${currentDateClass(
|
||||
day
|
||||
)} dark:bg-white/10`;
|
||||
}
|
||||
|
||||
if (period.end && dayjs(fullDay).isBetween(dayHover, period.end, "day", "[)")) {
|
||||
className = ` ${BG_COLOR["100"][primaryColor]} ${currentDateClass(
|
||||
day
|
||||
)} dark:bg-white/10`;
|
||||
}
|
||||
if (
|
||||
period.end &&
|
||||
dayjs(fullDay).isBetween(dayHover, period.end, "day", "[)")
|
||||
) {
|
||||
className = ` ${BG_COLOR["100"][primaryColor]} ${currentDateClass(
|
||||
day
|
||||
)} dark:bg-white/10`;
|
||||
}
|
||||
|
||||
if (dayHover === fullDay) {
|
||||
const bgColor = BG_COLOR["500"][primaryColor];
|
||||
className = ` transition-all duration-500 text-white font-medium ${bgColor} ${
|
||||
period.start ? "rounded-r-full" : "rounded-l-full"
|
||||
}`;
|
||||
}
|
||||
if (dayHover === fullDay) {
|
||||
const bgColor = BG_COLOR["500"][primaryColor];
|
||||
className = ` transition-all duration-500 text-white font-medium ${bgColor} ${
|
||||
period.start ? "rounded-r-full" : "rounded-l-full"
|
||||
}`;
|
||||
}
|
||||
|
||||
return className;
|
||||
},
|
||||
[calendarData.date, currentDateClass, dayHover, period.end, period.start, primaryColor]
|
||||
);
|
||||
return className;
|
||||
},
|
||||
[
|
||||
calendarData.date,
|
||||
currentDateClass,
|
||||
dayHover,
|
||||
period.end,
|
||||
period.start,
|
||||
primaryColor,
|
||||
]
|
||||
);
|
||||
|
||||
const isDateTooEarly = useCallback(
|
||||
(day: number, type: "current" | "previous" | "next") => {
|
||||
if (!minDate) {
|
||||
return false;
|
||||
}
|
||||
const object = {
|
||||
previous: previousMonth(calendarData.date),
|
||||
current: calendarData.date,
|
||||
next: nextMonth(calendarData.date)
|
||||
};
|
||||
const newDate = object[type as keyof typeof object];
|
||||
const formattedDate = newDate.set("date", day);
|
||||
return dayjs(formattedDate).isSame(dayjs(minDate), "day")
|
||||
? false
|
||||
: dayjs(formattedDate).isBefore(dayjs(minDate));
|
||||
},
|
||||
[calendarData.date, minDate]
|
||||
);
|
||||
const isDateTooEarly = useCallback(
|
||||
(day: number, type: "current" | "previous" | "next") => {
|
||||
if (!minDate) {
|
||||
return false;
|
||||
}
|
||||
const object = {
|
||||
previous: previousMonth(calendarData.date),
|
||||
current: calendarData.date,
|
||||
next: nextMonth(calendarData.date),
|
||||
};
|
||||
const newDate = object[type as keyof typeof object];
|
||||
const formattedDate = newDate.set("date", day);
|
||||
return dayjs(formattedDate).isSame(dayjs(minDate), "day")
|
||||
? false
|
||||
: dayjs(formattedDate).isBefore(dayjs(minDate));
|
||||
},
|
||||
[calendarData.date, minDate]
|
||||
);
|
||||
|
||||
const isDateTooLate = useCallback(
|
||||
(day: number, type: "current" | "previous" | "next") => {
|
||||
if (!maxDate) {
|
||||
return false;
|
||||
}
|
||||
const object = {
|
||||
previous: previousMonth(calendarData.date),
|
||||
current: calendarData.date,
|
||||
next: nextMonth(calendarData.date)
|
||||
};
|
||||
const newDate = object[type as keyof typeof object];
|
||||
const formattedDate = newDate.set("date", day);
|
||||
return dayjs(formattedDate).isSame(dayjs(maxDate), "day")
|
||||
? false
|
||||
: dayjs(formattedDate).isAfter(dayjs(maxDate));
|
||||
},
|
||||
[calendarData.date, maxDate]
|
||||
);
|
||||
const isDateTooLate = useCallback(
|
||||
(day: number, type: "current" | "previous" | "next") => {
|
||||
if (!maxDate) {
|
||||
return false;
|
||||
}
|
||||
const object = {
|
||||
previous: previousMonth(calendarData.date),
|
||||
current: calendarData.date,
|
||||
next: nextMonth(calendarData.date),
|
||||
};
|
||||
const newDate = object[type as keyof typeof object];
|
||||
const formattedDate = newDate.set("date", day);
|
||||
return dayjs(formattedDate).isSame(dayjs(maxDate), "day")
|
||||
? false
|
||||
: dayjs(formattedDate).isAfter(dayjs(maxDate));
|
||||
},
|
||||
[calendarData.date, maxDate]
|
||||
);
|
||||
|
||||
const isDateDisabled = useCallback(
|
||||
(day: number, type: "current" | "previous" | "next") => {
|
||||
if (isDateTooEarly(day, type) || isDateTooLate(day, type)) {
|
||||
return true;
|
||||
}
|
||||
const object = {
|
||||
previous: previousMonth(calendarData.date),
|
||||
current: calendarData.date,
|
||||
next: nextMonth(calendarData.date)
|
||||
};
|
||||
const newDate = object[type as keyof typeof object];
|
||||
const formattedDate = `${newDate.year()}-${newDate.month() + 1}-${
|
||||
day >= 10 ? day : "0" + day
|
||||
}`;
|
||||
const isDateDisabled = useCallback(
|
||||
(day: number, type: "current" | "previous" | "next") => {
|
||||
if (isDateTooEarly(day, type) || isDateTooLate(day, type)) {
|
||||
return true;
|
||||
}
|
||||
const object = {
|
||||
previous: previousMonth(calendarData.date),
|
||||
current: calendarData.date,
|
||||
next: nextMonth(calendarData.date),
|
||||
};
|
||||
const newDate = object[type as keyof typeof object];
|
||||
const formattedDate = `${newDate.year()}-${newDate.month() + 1}-${
|
||||
day >= 10 ? day : "0" + day
|
||||
}`;
|
||||
|
||||
if (!disabledDates || (Array.isArray(disabledDates) && !disabledDates.length)) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
!disabledDates ||
|
||||
(Array.isArray(disabledDates) && !disabledDates.length)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let matchingCount = 0;
|
||||
disabledDates?.forEach(dateRange => {
|
||||
if (
|
||||
dayjs(formattedDate).isAfter(dateRange.startDate) &&
|
||||
dayjs(formattedDate).isBefore(dateRange.endDate)
|
||||
) {
|
||||
matchingCount++;
|
||||
}
|
||||
if (
|
||||
dayjs(formattedDate).isSame(dateRange.startDate) ||
|
||||
dayjs(formattedDate).isSame(dateRange.endDate)
|
||||
) {
|
||||
matchingCount++;
|
||||
}
|
||||
let matchingCount = 0;
|
||||
disabledDates?.forEach((dateRange) => {
|
||||
if (
|
||||
dayjs(formattedDate).isAfter(dateRange.startDate) &&
|
||||
dayjs(formattedDate).isBefore(dateRange.endDate)
|
||||
) {
|
||||
matchingCount++;
|
||||
}
|
||||
if (
|
||||
dayjs(formattedDate).isSame(dateRange.startDate) ||
|
||||
dayjs(formattedDate).isSame(dateRange.endDate)
|
||||
) {
|
||||
matchingCount++;
|
||||
}
|
||||
});
|
||||
return matchingCount > 0;
|
||||
},
|
||||
[calendarData.date, isDateTooEarly, isDateTooLate, disabledDates]
|
||||
);
|
||||
|
||||
const buttonClass = useCallback(
|
||||
(day: number, type: "current" | "next" | "previous") => {
|
||||
const baseClass =
|
||||
"c-flex c-items-center c-justify-center c-w-12 c-h-12 lg:c-w-10 lg:c-h-10 c-relative";
|
||||
if (type === "current") {
|
||||
return cn(
|
||||
baseClass,
|
||||
!activeDateData(day).active
|
||||
? hoverClassByDay(day)
|
||||
: activeDateData(day).className,
|
||||
isDateDisabled(day, type) && "c-text-gray-400 c-cursor-not-allowed"
|
||||
);
|
||||
}
|
||||
return cn(
|
||||
baseClass,
|
||||
isDateDisabled(day, type) && "c-cursor-not-allowed",
|
||||
"c-text-gray-400"
|
||||
);
|
||||
},
|
||||
[activeDateData, hoverClassByDay, isDateDisabled]
|
||||
);
|
||||
|
||||
const checkIfHoverPeriodContainsDisabledPeriod = useCallback(
|
||||
(hoverPeriod: Period) => {
|
||||
if (!Array.isArray(disabledDates)) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < disabledDates.length; i++) {
|
||||
if (
|
||||
dayjs(hoverPeriod.start).isBefore(disabledDates[i].startDate) &&
|
||||
dayjs(hoverPeriod.end).isAfter(disabledDates[i].endDate)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[disabledDates]
|
||||
);
|
||||
|
||||
const getMetaData = useCallback(() => {
|
||||
return {
|
||||
previous: previousMonth(calendarData.date),
|
||||
current: calendarData.date,
|
||||
next: nextMonth(calendarData.date),
|
||||
};
|
||||
}, [calendarData.date]);
|
||||
|
||||
const hoverDay = useCallback(
|
||||
(day: number, type: string) => {
|
||||
const object = getMetaData();
|
||||
const newDate = object[type as keyof typeof object];
|
||||
const newHover = `${newDate.year()}-${newDate.month() + 1}-${
|
||||
day >= 10 ? day : "0" + day
|
||||
}`;
|
||||
|
||||
if (period.start && !period.end) {
|
||||
const hoverPeriod = { ...period, end: newHover };
|
||||
if (dayjs(newHover).isBefore(dayjs(period.start))) {
|
||||
hoverPeriod.start = newHover;
|
||||
hoverPeriod.end = period.start;
|
||||
if (!checkIfHoverPeriodContainsDisabledPeriod(hoverPeriod)) {
|
||||
changePeriod({
|
||||
start: null,
|
||||
end: period.start,
|
||||
});
|
||||
return matchingCount > 0;
|
||||
},
|
||||
[calendarData.date, isDateTooEarly, isDateTooLate, disabledDates]
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!checkIfHoverPeriodContainsDisabledPeriod(hoverPeriod)) {
|
||||
changeDayHover(newHover);
|
||||
}
|
||||
}
|
||||
|
||||
const buttonClass = useCallback(
|
||||
(day: number, type: "current" | "next" | "previous") => {
|
||||
const baseClass = "c-flex c-items-center c-justify-center c-w-12 c-h-12 lg:c-w-10 lg:c-h-10";
|
||||
if (type === "current") {
|
||||
return cn(
|
||||
baseClass,
|
||||
!activeDateData(day).active
|
||||
? hoverClassByDay(day)
|
||||
: activeDateData(day).className,
|
||||
isDateDisabled(day, type) && "c-text-gray-400 c-cursor-not-allowed"
|
||||
);
|
||||
}
|
||||
return cn(baseClass, isDateDisabled(day, type) && "c-cursor-not-allowed", "c-text-gray-400");
|
||||
},
|
||||
[activeDateData, hoverClassByDay, isDateDisabled]
|
||||
);
|
||||
if (!period.start && period.end) {
|
||||
const hoverPeriod = { ...period, start: newHover };
|
||||
if (dayjs(newHover).isAfter(dayjs(period.end))) {
|
||||
hoverPeriod.start = period.end;
|
||||
hoverPeriod.end = newHover;
|
||||
if (!checkIfHoverPeriodContainsDisabledPeriod(hoverPeriod)) {
|
||||
changePeriod({
|
||||
start: period.end,
|
||||
end: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!checkIfHoverPeriodContainsDisabledPeriod(hoverPeriod)) {
|
||||
changeDayHover(newHover);
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
changeDayHover,
|
||||
changePeriod,
|
||||
checkIfHoverPeriodContainsDisabledPeriod,
|
||||
getMetaData,
|
||||
period,
|
||||
]
|
||||
);
|
||||
|
||||
const checkIfHoverPeriodContainsDisabledPeriod = useCallback(
|
||||
(hoverPeriod: Period) => {
|
||||
if (!Array.isArray(disabledDates)) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < disabledDates.length; i++) {
|
||||
if (
|
||||
dayjs(hoverPeriod.start).isBefore(disabledDates[i].startDate) &&
|
||||
dayjs(hoverPeriod.end).isAfter(disabledDates[i].endDate)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[disabledDates]
|
||||
);
|
||||
const handleClickDay = useCallback(
|
||||
(day: number, type: "previous" | "current" | "next") => {
|
||||
function continueClick() {
|
||||
if (type === "previous") {
|
||||
onClickPreviousDays(day);
|
||||
}
|
||||
|
||||
const getMetaData = useCallback(() => {
|
||||
return {
|
||||
previous: previousMonth(calendarData.date),
|
||||
current: calendarData.date,
|
||||
next: nextMonth(calendarData.date)
|
||||
};
|
||||
}, [calendarData.date]);
|
||||
if (type === "current") {
|
||||
onClickDay(day);
|
||||
}
|
||||
|
||||
const hoverDay = useCallback(
|
||||
(day: number, type: string) => {
|
||||
const object = getMetaData();
|
||||
const newDate = object[type as keyof typeof object];
|
||||
const newHover = `${newDate.year()}-${newDate.month() + 1}-${
|
||||
day >= 10 ? day : "0" + day
|
||||
}`;
|
||||
if (type === "next") {
|
||||
onClickNextDays(day);
|
||||
}
|
||||
}
|
||||
|
||||
if (period.start && !period.end) {
|
||||
const hoverPeriod = { ...period, end: newHover };
|
||||
if (dayjs(newHover).isBefore(dayjs(period.start))) {
|
||||
hoverPeriod.start = newHover;
|
||||
hoverPeriod.end = period.start;
|
||||
if (!checkIfHoverPeriodContainsDisabledPeriod(hoverPeriod)) {
|
||||
changePeriod({
|
||||
start: null,
|
||||
end: period.start
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!checkIfHoverPeriodContainsDisabledPeriod(hoverPeriod)) {
|
||||
changeDayHover(newHover);
|
||||
}
|
||||
}
|
||||
if (disabledDates?.length) {
|
||||
const object = getMetaData();
|
||||
const newDate = object[type as keyof typeof object];
|
||||
const clickDay = `${newDate.year()}-${newDate.month() + 1}-${
|
||||
day >= 10 ? day : "0" + day
|
||||
}`;
|
||||
|
||||
if (!period.start && period.end) {
|
||||
const hoverPeriod = { ...period, start: newHover };
|
||||
if (dayjs(newHover).isAfter(dayjs(period.end))) {
|
||||
hoverPeriod.start = period.end;
|
||||
hoverPeriod.end = newHover;
|
||||
if (!checkIfHoverPeriodContainsDisabledPeriod(hoverPeriod)) {
|
||||
changePeriod({
|
||||
start: period.end,
|
||||
end: null
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!checkIfHoverPeriodContainsDisabledPeriod(hoverPeriod)) {
|
||||
changeDayHover(newHover);
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
changeDayHover,
|
||||
changePeriod,
|
||||
checkIfHoverPeriodContainsDisabledPeriod,
|
||||
getMetaData,
|
||||
period
|
||||
]
|
||||
);
|
||||
if (period.start && !period.end) {
|
||||
dayjs(clickDay).isSame(dayHover) && continueClick();
|
||||
} else if (!period.start && period.end) {
|
||||
dayjs(clickDay).isSame(dayHover) && continueClick();
|
||||
} else {
|
||||
continueClick();
|
||||
}
|
||||
} else {
|
||||
continueClick();
|
||||
}
|
||||
},
|
||||
[
|
||||
dayHover,
|
||||
disabledDates?.length,
|
||||
getMetaData,
|
||||
onClickDay,
|
||||
onClickNextDays,
|
||||
onClickPreviousDays,
|
||||
period.end,
|
||||
period.start,
|
||||
]
|
||||
);
|
||||
const load_marker = (day: number, type: string) => {
|
||||
let fullDay = `${calendarData.date.year()}-${
|
||||
calendarData.date.month() + 1
|
||||
}-${day >= 10 ? day : "0" + day}`;
|
||||
if (type === "previous") {
|
||||
const newDate = previousMonth(calendarData.date);
|
||||
fullDay = `${newDate.year()}-${newDate.month() + 1}-${
|
||||
day >= 10 ? day : "0" + day
|
||||
}`;
|
||||
}
|
||||
if (type === "next") {
|
||||
const newDate = nextMonth(calendarData.date);
|
||||
fullDay = `${newDate.year()}-${newDate.month() + 1}-${
|
||||
day >= 10 ? day : "0" + day
|
||||
}`;
|
||||
}
|
||||
const res = new Date(fullDay);
|
||||
return typeof onIcon === "function" ? onIcon(day, res) : null;
|
||||
};
|
||||
return (
|
||||
<div className="c-grid c-grid-cols-7 c-gap-y-0.5 c-my-1">
|
||||
{calendarData.days.previous.map((item, index) => (
|
||||
<button
|
||||
type="button"
|
||||
key={index}
|
||||
disabled={isDateDisabled(item, "previous")}
|
||||
className={`${buttonClass(item, "previous")}`}
|
||||
onClick={() => handleClickDay(item, "previous")}
|
||||
onMouseOver={() => {
|
||||
hoverDay(item, "previous");
|
||||
}}
|
||||
>
|
||||
<span className="c-relative">
|
||||
{item}
|
||||
<span className="c-relative">{load_marker(item, "previous")}</span>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
const handleClickDay = useCallback(
|
||||
(day: number, type: "previous" | "current" | "next") => {
|
||||
function continueClick() {
|
||||
if (type === "previous") {
|
||||
onClickPreviousDays(day);
|
||||
}
|
||||
{calendarData.days.current.map((item, index) => (
|
||||
<button
|
||||
type="button"
|
||||
key={index}
|
||||
disabled={isDateDisabled(item, "current")}
|
||||
className={cx(
|
||||
`${buttonClass(item, "current")}`,
|
||||
item === 1 && "highlight"
|
||||
)}
|
||||
onClick={() => handleClickDay(item, "current")}
|
||||
onMouseOver={() => {
|
||||
hoverDay(item, "current");
|
||||
}}
|
||||
>
|
||||
<span className="c-relative">
|
||||
{item}
|
||||
<span className="c-relative">{load_marker(item, "current")}</span>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
if (type === "current") {
|
||||
onClickDay(day);
|
||||
}
|
||||
|
||||
if (type === "next") {
|
||||
onClickNextDays(day);
|
||||
}
|
||||
}
|
||||
|
||||
if (disabledDates?.length) {
|
||||
const object = getMetaData();
|
||||
const newDate = object[type as keyof typeof object];
|
||||
const clickDay = `${newDate.year()}-${newDate.month() + 1}-${
|
||||
day >= 10 ? day : "0" + day
|
||||
}`;
|
||||
|
||||
if (period.start && !period.end) {
|
||||
dayjs(clickDay).isSame(dayHover) && continueClick();
|
||||
} else if (!period.start && period.end) {
|
||||
dayjs(clickDay).isSame(dayHover) && continueClick();
|
||||
} else {
|
||||
continueClick();
|
||||
}
|
||||
} else {
|
||||
continueClick();
|
||||
}
|
||||
},
|
||||
[
|
||||
dayHover,
|
||||
disabledDates?.length,
|
||||
getMetaData,
|
||||
onClickDay,
|
||||
onClickNextDays,
|
||||
onClickPreviousDays,
|
||||
period.end,
|
||||
period.start
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="c-grid c-grid-cols-7 c-gap-y-0.5 c-my-1">
|
||||
{calendarData.days.previous.map((item, index) => (
|
||||
<button
|
||||
type="button"
|
||||
key={index}
|
||||
disabled={isDateDisabled(item, "previous")}
|
||||
className={`${buttonClass(item, "previous")}`}
|
||||
onClick={() => handleClickDay(item, "previous")}
|
||||
onMouseOver={() => {
|
||||
hoverDay(item, "previous");
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
))}
|
||||
|
||||
{calendarData.days.current.map((item, index) => (
|
||||
<button
|
||||
type="button"
|
||||
key={index}
|
||||
disabled={isDateDisabled(item, "current")}
|
||||
className={cx(`${buttonClass(item, "current")}`, item === 1 && "highlight")}
|
||||
onClick={() => handleClickDay(item, "current")}
|
||||
onMouseOver={() => {
|
||||
hoverDay(item, "current");
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
))}
|
||||
|
||||
{calendarData.days.next.map((item, index) => (
|
||||
<button
|
||||
type="button"
|
||||
key={index}
|
||||
disabled={isDateDisabled(item, "next")}
|
||||
className={`${buttonClass(item, "next")}`}
|
||||
onClick={() => handleClickDay(item, "next")}
|
||||
onMouseOver={() => {
|
||||
hoverDay(item, "next");
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
{calendarData.days.next.map((item, index) => (
|
||||
<button
|
||||
type="button"
|
||||
key={index}
|
||||
disabled={isDateDisabled(item, "next")}
|
||||
className={`${buttonClass(item, "next")}`}
|
||||
onClick={() => handleClickDay(item, "next")}
|
||||
onMouseOver={() => {
|
||||
hoverDay(item, "next");
|
||||
}}
|
||||
>
|
||||
<span className="c-relative">
|
||||
{item}
|
||||
<span className="c-relative">{load_marker(item, "next")}</span>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Days;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ interface Props {
|
|||
changeMonth: (month: number) => void;
|
||||
changeYear: (year: number) => void;
|
||||
mode?: "monthly" | "daily";
|
||||
onMark?: (day: number, date: Date) => any;
|
||||
}
|
||||
|
||||
const Calendar: React.FC<Props> = ({
|
||||
|
|
@ -54,6 +55,7 @@ const Calendar: React.FC<Props> = ({
|
|||
onClickNext,
|
||||
changeMonth,
|
||||
changeYear,
|
||||
onMark,
|
||||
mode = "daily",
|
||||
}) => {
|
||||
// Contexts
|
||||
|
|
@ -366,6 +368,21 @@ const Calendar: React.FC<Props> = ({
|
|||
onClickPreviousDays={clickPreviousDays}
|
||||
onClickDay={clickDay}
|
||||
onClickNextDays={clickNextDays}
|
||||
onIcon={(day, date) => {
|
||||
if(typeof onMark === "function"){
|
||||
return onMark(day, date)
|
||||
}
|
||||
return <></>
|
||||
if (new Date().getDate() === day)
|
||||
return (
|
||||
<div className="c-absolute c-inset-y-0 c-left-0 -c-translate-y-1/2 -c-translate-x-1/2">
|
||||
<div className="c-w-full c-h-full c-flex c-flex-row c-items-center c-justif-center c-px-0.5">
|
||||
!
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <></>
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,10 @@ export interface DatepickerType {
|
|||
disabledDates?: DateRangeType[] | null;
|
||||
startWeekOn?: string | null;
|
||||
popoverDirection?: PopoverDirectionType;
|
||||
mode?: "daily" | "monthly"
|
||||
mode?: "daily" | "monthly";
|
||||
onMark?: (day: number, date: Date) => any;
|
||||
onLoad?: () => Promise<void>
|
||||
|
||||
}
|
||||
|
||||
export type ColorKeys = (typeof COLORS)[number]; // "blue" | "orange"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
import { FC } from "react";
|
||||
import QRCode from 'react-qr-code';
|
||||
|
||||
export const QrLabel: FC<{
|
||||
value: string,
|
||||
bgcolor: string,
|
||||
fgcolor: string,
|
||||
size: number
|
||||
}> = ({ value, bgcolor, fgcolor, size }) => {
|
||||
return (
|
||||
<div>
|
||||
<QRCode value={value} size={size}
|
||||
bgColor={bgcolor}
|
||||
fgColor={fgcolor}
|
||||
level={"L"} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import QrScanner from "qr-scanner";
|
||||
|
||||
export const QrReader = () => {
|
||||
const scanner = useRef<QrScanner>();
|
||||
const videoEl = useRef<HTMLVideoElement>(null);
|
||||
const qrBoxEl = useRef<HTMLDivElement>(null);
|
||||
const [qrOn, setQrOn] = useState<boolean>(true);
|
||||
const [scannedResult, setScannedResult] = useState<string | undefined>("");
|
||||
|
||||
// Success
|
||||
const onScanSuccess = (result: QrScanner.ScanResult) => {
|
||||
console.log(result);
|
||||
setScannedResult(result?.data);
|
||||
};
|
||||
|
||||
// Fail
|
||||
const onScanFail = (err: string | Error) => {
|
||||
console.log(err);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (videoEl?.current && !scanner.current) {
|
||||
scanner.current = new QrScanner(videoEl?.current, onScanSuccess, {
|
||||
onDecodeError: onScanFail,
|
||||
preferredCamera: "environment",
|
||||
highlightScanRegion: true,
|
||||
highlightCodeOutline: true,
|
||||
overlay: qrBoxEl?.current || undefined,
|
||||
});
|
||||
|
||||
scanner?.current
|
||||
?.start()
|
||||
.then(() => setQrOn(true))
|
||||
.catch((err) => {
|
||||
if (err) setQrOn(false);
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (!videoEl?.current) {
|
||||
scanner?.current?.stop();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!qrOn)
|
||||
alert(
|
||||
"Camera is blocked or not accessible. Please allow camera in your browser permissions and Reload."
|
||||
);
|
||||
}, [qrOn]);
|
||||
|
||||
return (
|
||||
<div className="qr-reader">
|
||||
{/* QR */}
|
||||
<video ref={videoEl}></video>
|
||||
<div ref={qrBoxEl} className="qr-box">
|
||||
|
||||
</div>
|
||||
|
||||
{/* Show Data Result if scan is success */}
|
||||
{scannedResult && (
|
||||
<p
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 99999,
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
Scanned Result: {scannedResult}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Loading…
Reference in New Issue