UNPKG

@onesy/ui-react

Version:
659 lines (633 loc) 25.9 kB
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; const _excluded = ["tonal", "color", "size", "value", "valueDefault", "onChange", "calendar", "calendarDefault", "onChangeCalendar", "colorSelected", "selected", "onTimeClick", "range", "offset", "outside", "weekStartDay", "now", "min", "max", "validate", "labels", "dayNamesFull", "noTransition", "valid", "renderDay", "renderDayValue", "renderDayName", "disabled", "DayNameProps", "PaginationItemProps", "TransitionProps", "TransitionsProps", "className"]; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } import React from 'react'; import { classNames, style, useOnesyTheme } from '@onesy/style-react'; import { arrayToParts, is } from '@onesy/utils'; import { add, OnesyDate, endOf, format, remove, set, startOf, is as isOnesyDate } from '@onesy/date'; import LineElement from '../Line'; import SurfaceElement from '../Surface'; import TypeElement from '../Type'; import PaginationItemElement from '../PaginationItem'; import TransitionsElement from '../Transitions'; import TransitionElement from '../Transition'; import { staticClassName } from '../utils'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const useStyle = style(theme => ({ root: { position: 'relative' }, root_transition: { overflow: 'hidden', position: 'relative' }, size_small: { height: '250px' }, size_regular: { height: '300px' }, size_large: { height: '370px' }, root_no_labels: { height: '260px' }, dayNames: { width: '100%' }, dayName: { flex: '1 1 auto', userSelect: 'none' }, dayName_size_small: { width: '30px', height: '30px' }, dayName_size_regular: { width: '40px', height: '40px' }, dayName_size_large: { width: '50px', height: '50px' }, day: { flex: '1 1 auto', position: 'relative' }, day_size_small: { width: '30px', height: '30px' }, day_size_regular: { width: '40px', height: '40px' }, day_size_large: { width: '50px', height: '50px' }, day_out: { opacity: '0.4' }, day_out_no: { visibility: 'hidden', opacity: '0' }, daySelected: { '&.onesy-Button-disabled': { opacity: 1 } }, dayStart: { borderRadius: `${theme.methods.shape.radius.value(40, 'px')} 0 0 ${theme.methods.shape.radius.value(40, 'px')}` }, dayEnd: { borderRadius: `0 ${theme.methods.shape.radius.value(40, 'px')} ${theme.methods.shape.radius.value(40, 'px')} 0` }, dayStartEnd: { borderRadius: theme.methods.shape.radius.value(40, 'px') }, dayStartSelection: { '&::after': { content: '""', position: 'absolute', left: '50%', top: '0', height: '100%', width: '50%', zIndex: 0 }, '&.onesy-enabled::after': { backgroundColor: `hsl(from var(--onesy-color) h s ${theme.palette.light ? 85 : 25})` }, '&.onesy-disabled::after': { backgroundColor: `hsl(from var(--onesy-color) h s ${theme.palette.light ? 90 : 20})` } }, dayEndSelection: { '&::before': { content: '""', position: 'absolute', left: '0', right: '50%', top: '0', height: '100%', width: '50%', zIndex: 0 }, '&.onesy-enabled::before': { backgroundColor: `hsl(from var(--onesy-color) h s ${theme.palette.light ? 85 : 25})` }, '&.onesy-disabled::before': { backgroundColor: `hsl(from var(--onesy-color) h s ${theme.palette.light ? 90 : 20})` } }, weeks: { width: '100%', position: 'absolute', left: '0px', transition: theme.methods.transitions.make(['opacity', 'transform']) }, weeks_size_small: { top: '30px' }, weeks_size_regular: { top: '40px' }, weeks_size_large: { top: '50px' }, weeks_no_labels: { top: '0px' }, week: { width: '100%' }, move_previous: { '& .weeks_enter': { opacity: '0', transform: 'translateX(100%)' }, '& .weeks_entering': { opacity: '1', transform: 'translateX(0%)' }, '& .weeks_exit': { opacity: '1', transform: 'translateX(0%)' }, '& .weeks_exiting': { opacity: '0', transform: 'translateX(-100%)' } }, move_next: { '& .weeks_enter': { opacity: '0', transform: 'translateX(-100%)' }, '& .weeks_entering': { opacity: '1', transform: 'translateX(0%)' }, '& .weeks_exit': { opacity: '1', transform: 'translateX(0%)' }, '& .weeks_exiting': { opacity: '0', transform: 'translateX(100%)' } }, dayValue: { position: 'relative', zIndex: 1, '&:hover': { boxShadow: 'inset 0px 0px 0px 1px currentColor !important' } }, disabled: { opacity: '0.54', pointerEvents: 'none' } }), { name: 'onesy-CalendarMonth' }); const CalendarMonth = props__ => { const theme = useOnesyTheme(); const l = theme.l; const props = _objectSpread(_objectSpread(_objectSpread({}, theme?.ui?.elements?.all?.props?.default), theme?.ui?.elements?.onesyCalendarMonth?.props?.default), props__); const Line = theme?.elements?.Line || LineElement; const Surface = theme?.elements?.Surface || SurfaceElement; const Type = theme?.elements?.Type || TypeElement; const PaginationItem = theme?.elements?.PaginationItem || PaginationItemElement; const Transitions = theme?.elements?.Transitions || TransitionsElement; const Transition = theme?.elements?.Transition || TransitionElement; const { tonal = true, color = 'primary', size = 'regular', value: value_, valueDefault, onChange, calendar: calendar_, calendarDefault, onChangeCalendar, colorSelected = 'secondary', selected, onTimeClick, range, offset = 0, outside = true, weekStartDay: weekStartDay_ = 'Monday', now = true, min, max, validate, labels = true, dayNamesFull, noTransition = true, valid: valid_, renderDay, renderDayValue, renderDayName, disabled, DayNameProps, PaginationItemProps, TransitionProps, TransitionsProps, className } = props, other = _objectWithoutProperties(props, _excluded); const { classes } = useStyle(); const [value, setValue] = React.useState(() => { const valueResult = (valueDefault !== undefined ? valueDefault : value_) || now && (range ? [new OnesyDate(), new OnesyDate()] : [new OnesyDate()]); return (is('array', valueResult) ? valueResult : [valueResult]).filter(Boolean); }); const [calendar, setCalendar] = React.useState((calendarDefault !== undefined ? calendarDefault : calendar_) || new OnesyDate()); let month = calendar || value[0] || new OnesyDate(); if (!month?.valid) month = new OnesyDate(); const refs = { id: React.useRef(`${month.year} ${month.month}`), previous: React.useRef(month), previousTheme: React.useRef(theme.palette.light), move: React.useRef(null), noTransition: React.useRef(null) }; const weekStartDay = !['Monday', 'Sunday'].includes(weekStartDay_) ? 'Monday' : weekStartDay_; // Value React.useEffect(() => { if (value_ !== undefined && value_ !== value) setValue((is('array', value_) ? value_ : [value_]).filter(Boolean)); }, [value_]); // Calendar React.useEffect(() => { if (calendar_ !== undefined && calendar_ !== calendar) setCalendar(calendar_); }, [calendar_]); const valid = (...args) => { if (is('function', valid_)) return valid_(...args); const onesyDate = args[0]; if (min || max || validate) { let response = true; if (is('function', validate)) response = validate(onesyDate); if (min !== undefined) response = response && isOnesyDate(onesyDate, 'after or same', min); if (max !== undefined) response = response && isOnesyDate(onesyDate, 'before or same', max); return response; } return true; }; const onUpdateCalendar = valueNew => { // Inner update if (!props.hasOwnProperty('calendar')) setCalendar(valueNew); if (is('function', onChangeCalendar)) onChangeCalendar(valueNew); }; const onUpdate = (valueUpdated, offsetMultiplier) => { let valueNew_0 = [valueUpdated, value[1]].filter(Boolean); // Previous // Range reset // If value is range, and is the same (in terms of day, month, year as from and/or to) // make a reset, ie. make both values that same date if (range) { if (value.filter(Boolean).some(item => valueUpdated.year === item.year && valueUpdated.month === item.month && valueUpdated.day === item.day)) valueNew_0 = [valueUpdated, valueUpdated];else { // Update the value closest to from, to value let index; // Update from or to // based on if value is closer to the middle from, or middle to if (!value[0]) index = 0;else if (!value[1]) index = 1;else if (valueUpdated.milliseconds < value[0]?.milliseconds) index = 0;else if (valueUpdated.milliseconds > value[1]?.milliseconds) index = 1;else { const middle = Math.abs(value[1].milliseconds - value[0]?.milliseconds) / 2; index = valueUpdated.milliseconds <= value[0]?.milliseconds + middle ? 0 : 1; } valueNew_0 = [...value]; valueNew_0[index] = valueUpdated; } // start of the day if (valueNew_0[0]) { valueNew_0[0] = startOf(valueNew_0[0], 'day'); } // end of the day if (valueNew_0[1]) { valueNew_0[1] = endOf(valueNew_0[1], 'day'); } } // If value isnt's same as the calendar // update calendar to the value if (!(valueUpdated.year === calendar.year && valueUpdated.month === calendar.month)) { onUpdateCalendar(add(offsetMultiplier !== undefined ? offsetMultiplier * offset : 0, 'month', valueUpdated)); } // Inner value update if (!props.hasOwnProperty('value')) setValue(valueNew_0); if (is('function', onChange)) onChange(!range ? valueNew_0[0] : valueNew_0); }; const dayNames = [1, 2, 3, 4, 5, 6]; if (weekStartDay === 'Monday') dayNames.push(7);else dayNames.unshift(7); const renderDayNameMethod = is('function', renderDayName) ? renderDayName : order_ => { const values = { 1: dayNamesFull ? l('Monday') : l('Mo'), 2: dayNamesFull ? l('Tuesday') : l('Tu'), 3: dayNamesFull ? l('Wednesday') : l('We'), 4: dayNamesFull ? l('Thursday') : l('Th'), 5: dayNamesFull ? l('Friday') : l('Fr'), 6: dayNamesFull ? l('Saturday') : l('Sa'), 7: dayNamesFull ? l('Sunday') : l('Su') }; return values[order_]; }; const monthNow = new OnesyDate(); // value or value range selected value let id = `${month.year} ${month.month} ${month.day} ${theme.palette.light}`; value.forEach(item_0 => id += ` ${item_0.year} ${item_0.month} ${item_0.day}`); const monthStart = startOf(month, 'month'); const previousMonth = remove(1, 'month', month); const previousMonthEnd = endOf(previousMonth, 'month'); const nextMonth = add(1, 'month', month); const monthSame = refs.previous.current?.year === calendar?.year && refs.previous.current?.month === calendar?.month; const getRanges = ranges => { // Convert all ranges to start/end timestamps const rangeTimestamps = ranges.filter(itemRange => itemRange.filter(Boolean).length).map(itemRange_0 => ({ start: itemRange_0[0].milliseconds, end: (itemRange_0[1] || itemRange_0[0]).milliseconds })); // Sort by start time rangeTimestamps.sort((a, b) => a.start - b.start); // Merge overlapping ranges const mergedRanges = []; let currentRange = rangeTimestamps[0]; for (let i = 1; i < rangeTimestamps.length; i++) { if (rangeTimestamps[i].start <= currentRange.end) { // Ranges overlap, merge them currentRange.end = Math.max(currentRange.end, rangeTimestamps[i].end); } else { // No overlap, push current range and start new one mergedRanges.push(currentRange); currentRange = rangeTimestamps[i]; } } mergedRanges.push(currentRange); return mergedRanges.filter(Boolean); }; const rangesValue = React.useMemo(() => { const ranges_0 = [value].map(item_1 => { return item_1.map((itemRange_1, index_0) => !index_0 ? startOf(itemRange_1, 'day') : endOf(itemRange_1, 'day')); }); return getRanges(ranges_0); }, [value]); const rangesSelected = React.useMemo(() => { const ranges_1 = [...(selected || [])].map(item_2 => { return item_2.map((itemRange_2, index_1) => !index_1 ? startOf(itemRange_2, 'day') : endOf(itemRange_2, 'day')); }); return getRanges(ranges_1); }, [selected]); const getDetails = day => { const dayFormat = format(day, 'DD-MM-YYYY'); const dayMonthStart = format(startOf(day, 'month'), 'DD-MM-YYYY'); const dayMonthEnd = format(endOf(day, 'month'), 'DD-MM-YYYY'); const result = { start: false, between: false, end: false, same: false, selected: false, outside: false, fromSelected: false, monthStart: dayMonthStart === dayFormat, monthEnd: dayMonthEnd === dayFormat, selectedIndex: value.findIndex(item_3 => item_3.year === day.year && item_3.month === day.month && item_3.day === day.day), selectedSame: value.filter(item_4 => item_4.year === day.year && item_4.month === day.month && item_4.day === day.day).length === 2 }; // value for (const itemRange_3 of rangesValue) { const rangeStartDay = format(new OnesyDate(itemRange_3.start), 'DD-MM-YYYY'); const rangeEndDay = format(new OnesyDate(itemRange_3.end), 'DD-MM-YYYY'); // start if (dayFormat === rangeStartDay) result.selected = result.start = true; // end if (dayFormat === rangeEndDay) result.selected = result.end = true; // between if (day.milliseconds >= itemRange_3.start && day.milliseconds <= itemRange_3.end) result.selected = result.between = true; // same result.same = result.start && result.end; if (result.selected) return result; } // selected for (const itemRange_4 of rangesSelected) { const rangeStartDay_0 = format(new OnesyDate(itemRange_4.start), 'DD-MM-YYYY'); const rangeEndDay_0 = format(new OnesyDate(itemRange_4.end), 'DD-MM-YYYY'); // start if (dayFormat === rangeStartDay_0) result.selected = result.start = true; // end if (dayFormat === rangeEndDay_0) result.selected = result.end = true; // between if (day.milliseconds >= itemRange_4.start && day.milliseconds <= itemRange_4.end) result.selected = result.between = true; // same result.same = result.start && result.end; if (result.selected) { result.fromSelected = true; return result; } } result.outside = true; return result; }; const days = React.useMemo(() => { const items = []; const nowDate = new OnesyDate(); // Add all month days for (let i_0 = 0; i_0 < month.daysInMonth; i_0++) { let day_0 = set(i_0 + 1, 'day', month); day_0 = set(14, 'hour', day_0); const details = getDetails(day_0); items.push({ value: i_0 + 1, in: true, dayWeek: day_0.dayWeek, weekend: [0, 6].includes(day_0.dayWeek), today: day_0.year === monthNow.year && day_0.dayYear === monthNow.dayYear, future: day_0.milliseconds > nowDate.milliseconds, is: _objectSpread({}, details), onesyDate: day_0 }); } items[0].start = true; items[items.length - 1].end = true; // Add to start if (weekStartDay === 'Sunday' && monthStart.dayWeek !== 0 || weekStartDay === 'Monday' && monthStart.dayWeek !== 1) { let toAdd = monthStart.dayWeek === 0 ? 6 : monthStart.dayWeek - 1; if (weekStartDay === 'Sunday') toAdd++; for (let i_1 = 0; i_1 < toAdd; i_1++) { const day_1 = set(previousMonthEnd.day - i_1, 'day', previousMonth); const details_0 = getDetails(day_1); items.unshift({ value: day_1.day, in: false, dayWeek: day_1.dayWeek, weekend: [0, 6].includes(day_1.dayWeek), today: day_1.year === monthNow.year && day_1.dayYear === monthNow.dayYear, future: day_1.milliseconds > nowDate.milliseconds, is: _objectSpread({}, details_0), start: true, onesyDate: day_1 }); } } // Add to end const dayLast = items[items.length - 1]; if (dayLast.dayWeek < 7) { let toAdd_0 = 7 - dayLast.dayWeek; if (items.length + toAdd_0 - 1 < 42) toAdd_0 += 41 - (items.length + toAdd_0 - 1); for (let i_2 = 0; i_2 < toAdd_0; i_2++) { const day_2 = set(i_2 + 1, 'day', nextMonth); const details_1 = getDetails(day_2); items.push({ value: i_2 + 1, in: false, dayWeek: day_2.dayWeek, weekend: [0, 6].includes(day_2.dayWeek), today: day_2.year === monthNow.year && day_2.dayYear === monthNow.dayYear, future: day_2.milliseconds > nowDate.milliseconds, is: _objectSpread({}, details_1), end: true, onesyDate: day_2 }); } } return items; }, [month, rangesValue, rangesSelected]); const colorSelectedTheme = React.useMemo(() => { return theme.palette.color[colorSelected] || theme.methods.color(colorSelected); }, [colorSelected, theme]); // noTransition refs.noTransition.current = monthSame; // Update previous if (refs.id.current !== `${month.year} ${month.month}`) { refs.move.current = refs.previous.current?.milliseconds > month.milliseconds ? 'next' : 'previous'; refs.id.current = `${month.year} ${month.month}`; refs.previous.current = month; refs.noTransition.current = false; } const weeks = arrayToParts(days, 7); const getCalendar = status => { return /*#__PURE__*/_jsx(Surface, { color: color, tonal: tonal, children: ({ palette }) => /*#__PURE__*/_jsx(Line, { gap: 0.5, direction: "column", align: "unset", justify: "unset", className: classNames([staticClassName('CalendarMonth', theme) && ['onesy-CalendarMonth-weeks'], classes.weeks, classes[`weeks_size_${size}`], status && [`weeks_${status}`], !labels && classes.weeks_no_labels]), children: weeks.map((week, index_2) => /*#__PURE__*/ // Week _jsx(Line, { gap: 0, direction: "row", align: "unset", justify: "space-between", className: classNames([staticClassName('CalendarMonth', theme) && ['onesy-CalendarMonth-week'], classes.week]), children: week.map((day_3, index_) => { const propsDay = { onClick: () => onUpdate(day_3.onesyDate, day_3.start || day_3.end ? -1 : undefined), disabled: !day_3.in && !outside || !valid(day_3.onesyDate, 'day') || // not prior to 1970, we may potentially update this in the future day_3.onesyDate.year < 1970 }; const isEdge = day_3.is.same || (day_3.is.monthStart || day_3.is.monthEnd) && day_3.is.selected; return /*#__PURE__*/_jsx(Line, { direction: "row", align: "center", justify: "center", onClick: event => { const timeDate = startOf(day_3.onesyDate, 'hour'); onTimeClick?.(timeDate, 'month', event); }, className: classNames([staticClassName('CalendarMonth', theme) && ['onesy-CalendarMonth-day', `onesy-CalendarMonth-day-${day_3.in ? 'in' : 'out'}`], classes.day, classes[`day_size_${size}`], classes[`day_${day_3.in ? 'in' : 'out'}`], !day_3.in && !outside && classes.day_out_no, !propsDay.disabled ? 'onesy-enabled' : 'onesy-disabled', // same day // (day.is.same || ((day.is.monthStart || day.is.monthEnd) && day.is.selected)) && classes.dayStartEnd, // start day_3.is.start && !isEdge && classes.dayStart, // end day_3.is.end && !isEdge && classes.dayEnd, // between day_3.is.between && !day_3.is.same && [!day_3.is.end && day_3.dayWeek !== 0 && !day_3.is.monthEnd && classes.dayStartSelection, !day_3.is.start && day_3.dayWeek !== 1 && !day_3.is.monthStart && classes.dayEndSelection]]), style: { '--onesy-color': theme.methods.palette.color.alpha(day_3.is.fromSelected ? colorSelectedTheme?.main : palette?.main, 1) }, children: is('function', renderDay) ? renderDay(day_3.onesyDate, propsDay, day_3, outside) : /*#__PURE__*/_jsx(PaginationItem, _objectSpread(_objectSpread(_objectSpread({ tonal: tonal, color: "inherit", size: size, InteractionProps: { background: false }, TypeProps: { version: size === 'large' ? 'b1' : size === 'regular' ? 'b2' : 'b3', priority: !day_3.is.selected ? !day_3.weekend ? 'primary' : 'secondary' : undefined }, "aria-label": format(day_3.onesyDate, 'DD-MM-YYYY', { l }) }, PaginationItemProps), {}, { className: classNames([staticClassName('CalendarMonth', theme) && ['onesy-CalendarMonth-day-value', day_3.in && 'onesy-CalendarMonth-day-in', day_3.dayWeek && 'onesy-CalendarMonth-day-day-week', day_3.weekend && 'onesy-CalendarMonth-day-weekend', day_3.today && 'onesy-CalendarMonth-day-today', day_3.is.between && 'onesy-CalendarMonth-day-between', day_3.is.selected && 'onesy-CalendarMonth-day-selected', day_3.is.start && 'onesy-CalendarMonth-day-start', day_3.is.end && 'onesy-CalendarMonth-day-end'], PaginationItemProps?.className, classes.dayValue, day_3.is.selected && classes.daySelected, classes[`day_size_${size}`]]), style: _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, day_3.today ? { boxShadow: `inset 0px 0px 0px 1px ${palette[theme.palette.light ? 40 : 60]}` } : undefined), day_3.is.selected && day_3.is.between && { color: `hsl(from var(--onesy-color) h s ${theme.palette.light ? 10 : 98})`, backgroundColor: `hsl(from var(--onesy-color) h s ${theme.palette.light ? propsDay.disabled ? 90 : 85 : propsDay.disabled ? 20 : 25})` }), day_3.is.selected && (day_3.is.start || day_3.is.end) && { color: `hsl(from var(--onesy-color) h s ${theme.palette.light ? 98 : 10})`, backgroundColor: `hsl(from var(--onesy-color) h s ${theme.palette.light ? propsDay.disabled ? 40 : 50 : propsDay.disabled ? 60 : 70})` }), PaginationItemProps?.style) }, propsDay), {}, { children: is('function', renderDayValue) ? renderDayValue(day_3.value, day_3.onesyDate, day_3) : day_3.value })) }, index_); }) }, index_2)) }) }); }; return /*#__PURE__*/_jsxs(Line, _objectSpread(_objectSpread({ gap: 0, direction: "column", className: classNames([staticClassName('CalendarMonth', theme) && ['onesy-CalendarMonth-root', `onesy-CalendarMonth-size-${size}`], className, noTransition ? classes.root : classes.root_transition, classes[`size_${size}`], classes[`move_${refs.move.current}`], !labels && classes.root_no_labels, disabled && classes.disabled]) }, other), {}, { children: [labels && /*#__PURE__*/_jsx(Line, { gap: 0, direction: "row", align: "center", justify: "space-between", className: classNames([staticClassName('CalendarMonth', theme) && ['onesy-CalendarMonth-day-names'], classes.dayNames]), children: dayNames.map((day_4, index_3) => /*#__PURE__*/_jsx(Line, { direction: "column", align: "center", justify: "center", className: classNames([staticClassName('CalendarMonth', theme) && ['onesy-CalendarMonth-day-name'], classes.dayName, classes[`dayName_size_${size}`]]), children: /*#__PURE__*/_jsx(Type, _objectSpread(_objectSpread({ version: size === 'large' ? 'b1' : size === 'regular' ? 'b2' : 'b3', align: "center" }, DayNameProps), {}, { children: renderDayNameMethod(day_4) })) }, index_3)) }), noTransition && getCalendar(), !noTransition && /*#__PURE__*/_jsx(Transitions, _objectSpread(_objectSpread({ id: refs.id.current, mode: "in-out-follow", switch: true }, TransitionsProps), {}, { children: /*#__PURE__*/_jsx(Transition, _objectSpread(_objectSpread({ in: true }, TransitionProps), {}, { children: getCalendar }), id) }))] })); }; CalendarMonth.displayName = 'onesy-CalendarMonth'; export default CalendarMonth;