react-opening-hours
Version:
A simple form UI for collecting hours of operation
460 lines (459 loc) • 18.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = __importDefault(require("react"));
const react_2 = require("react");
const timeOptions12 = [
{
value: '00:00:00',
label: '12:00am',
},
{
value: '00:30:00',
label: '12:30am',
},
{
value: '01:00:00',
label: '1:00am',
},
{
value: '01:30:00',
label: '1:30am',
},
{
value: '02:00:00',
label: '2:00am',
},
{
value: '02:30:00',
label: '2:30am',
},
{
value: '03:00:00',
label: '3:00am',
},
{
value: '03:30:00',
label: '3:30am',
},
{
value: '04:00:00',
label: '4:00am',
},
{
value: '04:30:00',
label: '4:30am',
},
{
value: '05:00:00',
label: '5:00am',
},
{
value: '05:30:00',
label: '5:30am',
},
{
value: '06:00:00',
label: '6:00am',
},
{
value: '06:30:00',
label: '6:30am',
},
{
value: '07:00:00',
label: '7:00am',
},
{
value: '07:30:00',
label: '7:30am',
},
{
value: '08:00:00',
label: '8:00am',
},
{
value: '08:30:00',
label: '8:30am',
},
{
value: '09:00:00',
label: '9:00am',
},
{
value: '09:30:00',
label: '9:30am',
},
{
value: '10:00:00',
label: '10:00am',
},
{
value: '10:30:00',
label: '10:30am',
},
{
value: '11:00:00',
label: '11:00am',
},
{
value: '11:30:00',
label: '11:30am',
},
{
value: '12:00:00',
label: '12:00pm',
},
{
value: '12:30:00',
label: '12:30pm',
},
{
value: '13:00:00',
label: '1:00pm',
},
{
value: '13:30:00',
label: '1:30pm',
},
{
value: '14:00:00',
label: '2:00pm',
},
{
value: '14:30:00',
label: '2:30pm',
},
{
value: '15:00:00',
label: '3:00pm',
},
{
value: '15:30:00',
label: '3:30pm',
},
{
value: '16:00:00',
label: '4:00pm',
},
{
value: '16:30:00',
label: '4:30pm',
},
{
value: '17:00:00',
label: '5:00pm',
},
{
value: '17:30:00',
label: '5:30pm',
},
{
value: '18:00:00',
label: '6:00pm',
},
{
value: '18:30:00',
label: '6:30pm',
},
{
value: '19:00:00',
label: '7:00pm',
},
{
value: '19:30:00',
label: '7:30pm',
},
{
value: '20:00:00',
label: '8:00pm',
},
{
value: '20:30:00',
label: '8:30pm',
},
{
value: '21:00:00',
label: '9:00pm',
},
{
value: '21:30:00',
label: '9:30pm',
},
{
value: '22:00:00',
label: '10:00pm',
},
{
value: '22:30:00',
label: '10:30pm',
},
{
value: '23:00:00',
label: '11:00pm',
},
{
value: '23:30:00',
label: '11:30pm',
},
];
const timeOptions24 = timeOptions12.map((t) => {
return { ...t, label: t.value };
});
const getMatchingDayPair = (day, daysConf) => {
const dayGroup = [];
if (day.seq % 2 === 0) {
dayGroup[0] = day;
const closeDay = daysConf[day.seq + 1];
dayGroup[1] = closeDay;
}
else {
const openDay = daysConf[day.seq - 1];
dayGroup[0] = openDay;
dayGroup[1] = day;
}
return dayGroup;
};
const shouldShowDayAsOpen = (day, daysConf) => {
const openTime = getMatchingDayPair(day, daysConf)[0].time;
const closeTime = getMatchingDayPair(day, daysConf)[1].time;
return !!(openTime !== 'closed' && closeTime !== 'closed');
};
const shouldShowDayAsClosed = (day, daysConf) => {
const openTime = getMatchingDayPair(day, daysConf)[0].time;
const closeTime = getMatchingDayPair(day, daysConf)[1].time;
return !!(openTime === 'closed' && closeTime === 'closed');
};
const shouldRenderInput = (day, daysConf) => {
return shouldShowDayAsOpen(day, daysConf);
};
const shouldDisableTimeOption = (timeOption, day, daysConf) => {
const openTime = getMatchingDayPair(day, daysConf)[0].time;
const closeTime = getMatchingDayPair(day, daysConf)[1].time;
const baseDate = new Date('1970-01-01');
const dateTimeOpen = new Date(`${baseDate.toDateString()} ${openTime}`);
const dateTimeClose = new Date(`${baseDate.toDateString()} ${closeTime}`);
const dateTimeWithTimeOption = new Date(`${baseDate.toDateString()} ${timeOption.value}`);
if (day.seq % 2 === 0) {
return dateTimeWithTimeOption >= dateTimeClose;
}
return dateTimeWithTimeOption <= dateTimeOpen;
};
const OpeningHoursUnstyled = (props) => {
const { getValues, defaultValues, ampm, verticalTimePairs, rootContainerStyles, rootContainerClassName, renderDayButton, getDayButtonLabelText, dayButtonContainerStyles, dayButtonContainerClassName, dayButtonActiveElementStyles, dayButtonInactiveElementStyles, dayButtonActiveElementClassName, dayButtonInactiveElementClassName, renderLabel, labelContainerStyles, labelContainerClassName, labelElementStyles, labelElementClassName, renderCopyButton, copyButtonContainerStyles, copyButtonContainerClassName, copyButtonElementStyles, copyButtonElementClassName, renderSelect, selectContainerStyles, selectContainerClassName, selectElementStyles, selectElementClassName, showCopyToAll, } = props;
const timeOptions = ampm ? timeOptions12 : timeOptions24;
const [daysConfig, setDaysConfig] = (0, react_2.useState)([
{ id: 'sun_open', time: 'closed', label: 'Sunday', seq: 0 },
{ id: 'sun_close', time: 'closed', label: 'Sunday', seq: 0 },
{ id: 'mon_open', time: 'closed', label: 'Monday', seq: 1 },
{ id: 'mon_close', time: 'closed', label: 'Monday', seq: 1 },
{ id: 'tue_open', time: 'closed', label: 'Tuesday', seq: 2 },
{ id: 'tue_close', time: 'closed', label: 'Tuesday', seq: 2 },
{ id: 'wed_open', time: 'closed', label: 'Wednesday', seq: 3 },
{ id: 'wed_close', time: 'closed', label: 'Wednesday', seq: 3 },
{ id: 'thu_open', time: 'closed', label: 'Thursday', seq: 4 },
{ id: 'thu_close', time: 'closed', label: 'Thursday', seq: 4 },
{ id: 'fri_open', time: 'closed', label: 'Friday', seq: 5 },
{ id: 'fri_close', time: 'closed', label: 'Friday', seq: 5 },
{ id: 'sat_open', time: 'closed', label: 'Saturday', seq: 6 },
{ id: 'sat_close', time: 'closed', label: 'Saturday', seq: 6 },
]);
(0, react_2.useEffect)(() => {
if (Array.isArray(defaultValues) && defaultValues.length) {
const withSequences = [];
for (let i = 0; i < defaultValues.length; i++) {
withSequences.push({ ...defaultValues[i], seq: i });
}
setDaysConfig(withSequences);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const hideDayToggle = (dayToHide) => {
const [openDay, closeDay] = getMatchingDayPair(dayToHide, daysConfig);
const newDaysConfig = daysConfig.map((day) => {
if (day.id === openDay?.id) {
return { ...openDay, time: 'closed' };
}
else if (day.id === closeDay?.id) {
return { ...closeDay, time: 'closed' };
}
else {
return day;
}
});
setDaysConfig(newDaysConfig);
};
const showDayToggle = (dayToShow) => {
const [openDay, closeDay] = getMatchingDayPair(dayToShow, daysConfig);
const newDaysConfig = daysConfig.map((day) => {
if (day.id === openDay?.id) {
return { ...openDay, time: '09:00:00' };
}
else if (day.id === closeDay?.id) {
return { ...closeDay, time: '17:00:00' };
}
else {
return day;
}
});
setDaysConfig(newDaysConfig);
};
const handleChangeTime = (dayToChange, newTime) => {
const oldDay = daysConfig.find((x) => x.id === dayToChange.id);
const newDaysConfig = daysConfig.map((day) => {
if (day.id === dayToChange.id) {
return { ...oldDay, time: newTime };
}
else {
return day;
}
});
setDaysConfig(newDaysConfig);
};
const copyAll = (dayToCopy) => {
const [openDay, closeDay] = getMatchingDayPair(dayToCopy, daysConfig);
const newDaysConfig = daysConfig.map((day, i, arr) => {
if (day.seq % 2 === 0 && shouldShowDayAsOpen(day, arr) && day.id !== openDay.id) {
return { ...day, time: openDay.time };
}
else if (day.seq % 2 === 1 && shouldShowDayAsOpen(day, arr) && day.id !== closeDay.id) {
return { ...day, time: closeDay.time };
}
else {
return day;
}
});
setDaysConfig(newDaysConfig);
};
//pass values up to parent
(0, react_2.useEffect)(() => {
getValues(daysConfig);
}, [getValues, daysConfig]);
return (react_1.default.createElement("div", { className: rootContainerClassName, style: rootContainerStyles
? rootContainerStyles
: !rootContainerClassName
? {
display: 'flex',
flexDirection: 'column',
justifyContent: 'start',
alignItems: 'center',
}
: undefined },
react_1.default.createElement("div", { className: dayButtonContainerClassName, style: dayButtonContainerStyles }, daysConfig &&
daysConfig.map((day, i, arr) => {
if (shouldShowDayAsOpen(day, arr) && day.seq % 2 === 0) {
if (typeof renderDayButton === 'function') {
return renderDayButton({
id: `${day.id}${i}-btn`,
text: getDayButtonLabelText ? getDayButtonLabelText(day.label) : day.label[0],
onClick: () => hideDayToggle(day),
active: true,
});
}
return (react_1.default.createElement("button", { type: "button", style: dayButtonActiveElementStyles, key: `${day.id}${i}-btn`, onClick: () => hideDayToggle(day), className: dayButtonActiveElementClassName },
react_1.default.createElement("span", null, getDayButtonLabelText ? getDayButtonLabelText(day.label) : day.label[0])));
}
else if (shouldShowDayAsClosed(day, arr) && day.seq % 2 === 0) {
if (typeof renderDayButton === 'function') {
return renderDayButton({
id: `${day.id}${i}-btn`,
text: getDayButtonLabelText ? getDayButtonLabelText(day.label) : day.label[0],
onClick: () => showDayToggle(day),
active: false,
});
}
return (react_1.default.createElement("button", { type: "button", key: `${day.id}${i}-btn`, style: dayButtonInactiveElementStyles
? dayButtonInactiveElementStyles
: dayButtonActiveElementClassName
? undefined
: {
opacity: 0.3,
}, onClick: () => showDayToggle(day), className: dayButtonInactiveElementClassName }, getDayButtonLabelText ? getDayButtonLabelText(day.label) : day.label[0]));
}
else {
return null;
}
})),
react_1.default.createElement("div", { style: {
display: 'flex',
flexDirection: 'row',
} },
react_1.default.createElement("div", { style: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
width: '100%',
} }, daysConfig &&
daysConfig.map((day, i, arr) => {
const [open, close] = getMatchingDayPair(day, arr);
if (shouldRenderInput(day, arr) && i % 2 === 0) {
return (react_1.default.createElement("div", { key: `${day.id}-${i}-itemContainerDiv`, id: `${day.id}-${i}-itemContainerDiv`, style: verticalTimePairs
? {
display: 'flex',
flexDirection: 'column',
minWidth: '260px',
marginTop: 4,
marginBottom: 4,
position: 'relative',
}
: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
marginTop: 4,
marginBottom: 4,
position: 'relative',
} },
react_1.default.createElement("div", { key: `${day.id}-${i}-labelDiv`, className: labelContainerClassName, style: labelContainerStyles }, typeof renderLabel === 'function' ? (renderLabel({
id: `${day.id}-${i}-label`,
htmlFor: `${day.id}-label`,
label: day.label,
})) : (react_1.default.createElement("label", { key: `${day.id}-${i}-label`, htmlFor: `${day.id}-label`, className: labelElementClassName, style: labelElementStyles }, day.label))),
react_1.default.createElement("div", { key: `${open.id}-${i}-selectOpenDiv`, className: selectContainerClassName, style: selectContainerStyles }, typeof renderSelect === 'function' ? (renderSelect({
id: `${open.id}${i}-select`,
day: day,
options: timeOptions.filter((t) => !shouldDisableTimeOption(t, open, arr)),
value: timeOptions.find((t) => t.value === open.time),
onChange: (event) => {
if (event.value) {
handleChangeTime(open, event.value);
}
else {
handleChangeTime(open, event.target.value);
}
},
})) : (react_1.default.createElement("select", { key: `${open.id}-select`, value: open.time, onChange: (event) => {
handleChangeTime(open, event.target.value);
}, style: selectElementStyles, className: selectElementClassName }, timeOptions.map((t) => (react_1.default.createElement("option", { key: t.value, value: t.value, disabled: shouldDisableTimeOption(t, open, arr) }, t.label)))))),
react_1.default.createElement("div", { key: `${close.id}-${i}-selectCloseDiv`, className: selectContainerClassName, style: selectContainerStyles }, typeof renderSelect === 'function' ? (renderSelect({
id: `${close.id}${i}-select`,
day: day,
options: timeOptions.filter((t) => !shouldDisableTimeOption(t, close, arr)),
value: timeOptions.find((t) => t.value === close.time),
onChange: (event) => {
if (event.value) {
handleChangeTime(close, event.value);
}
else {
handleChangeTime(close, event.target.value);
}
},
})) : (react_1.default.createElement("select", { key: `${close.id}-select`, value: close.time, onChange: (event) => {
handleChangeTime(close, event.target.value);
}, style: selectElementStyles, className: selectElementClassName }, timeOptions.map((t) => (react_1.default.createElement("option", { key: t.value, value: t.value, disabled: shouldDisableTimeOption(t, close, arr) }, t.label)))))),
showCopyToAll &&
arr
.filter((x, i, r) => shouldRenderInput(x, r))
.sort((a, b) => a.seq - b.seq)[1]?.id === close.id ? (react_1.default.createElement("div", { key: `${close.id}-${i}-div-copy`, style: copyButtonContainerStyles ?? {
position: 'absolute',
right: -60,
top: 0,
}, className: copyButtonContainerClassName },
react_1.default.createElement("div", null, typeof renderCopyButton === 'function' ? (renderCopyButton({ onClick: () => copyAll(day) })) : (react_1.default.createElement("button", { key: `${close.id}-${i}-button`, type: "button", onClick: () => copyAll(day), className: copyButtonElementClassName, style: copyButtonElementStyles }, "Copy"))))) : (react_1.default.createElement(react_1.default.Fragment, null))));
}
return null;
})))));
};
exports.default = OpeningHoursUnstyled;