UNPKG

@onesy/ui-react

Version:
718 lines (717 loc) 26.3 kB
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; const _excluded = ["name", "date", "dateDefault", "times", "events", "meta", "views", "onUpdate", "onRemove", "onChangeDate", "startHeader", "endHeader", "startLeft", "endLeft", "startRight", "endRight", "startLeftModal", "endLeftModal", "startRightModal", "endRightModal", "Component", "IconEdit", "IconPrevious", "IconNext", "IconRemove", "IconClose", "WeekProps", "DayProps", "IconProps", "IconButtonProps", "className", "children"]; 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 { capitalize, cleanValue, is, textToInnerHTML } from '@onesy/utils'; import { style as styleMethod, classNames, useOnesyTheme, colors } from '@onesy/style-react'; import { OnesyDate, add, endOf, format, remove, set, startOf } from '@onesy/date'; import IconMaterialEdit from '@onesy/icons-material-rounded-react/IconMaterialEditW100'; import IconMaterialKeyboardArrowDown from '@onesy/icons-material-rounded-react/IconMaterialKeyboardArrowDownW100'; import IconMaterialArrowForwardIos from '@onesy/icons-material-rounded-react/IconMaterialArrowForwardIosW100'; import IconMaterialArrowBackIosNew from '@onesy/icons-material-rounded-react/IconMaterialArrowBackIosNewW100'; import IconMaterialDelete from '@onesy/icons-material-rounded-react/IconMaterialDeleteW100'; import CalendarWeekElement from '../CalendarWeek'; import SelectElement from '../Select'; import ButtonElement from '../Button'; import LineElement from '../Line'; import ModalElement from '../Modal'; import ModalHeaderElement from '../ModalHeader'; import ModalMainElement from '../ModalMain'; import TypeElement from '../Type'; import TooltipElement from '../Tooltip'; import IconButtonElement from '../IconButton'; import LabelElement from '../Label'; import SlideElement from '../Slide'; import SwitchElement from '../Switch'; import { formats, staticClassName } from '../utils'; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; const useStyle = styleMethod(theme => ({ root: { padding: '16px', paddingBottom: '24px', color: theme.methods.palette.color.value('primary', 10), background: theme.palette.background.default.primary, '& .onesy-Label-text': { whiteSpace: 'nowrap' } }, calendar: { padding: '12px 8px', background: theme.palette.background.default.primary }, aside: { width: 'auto', maxWidth: '100%' }, weekDay: { width: '47px', height: '47px', borderRadius: '50%' }, today: { background: theme.palette.color.primary[40], color: '#fff' }, palettePreview: { width: '17px', height: '17px', boxShadow: theme.palette.light ? '0px 1px 1px 0px rgba(0, 0, 0, 0.07), 0px 2px 1px -1px rgba(0, 0, 0, 0.04), 0px 1px 3px 0px rgba(0, 0, 0, 0.11)' : '0px 1px 1px 0px rgba(255, 255, 255, 0.21), 0px 2px 1px -1px rgba(255, 255, 255, 0.12), 0px 1px 3px 0px rgba(255, 255, 255, 0.40)', borderRadius: '50%', cursor: 'default', flex: '0 0 auto', transition: theme.methods.transitions.make('transform'), '& > *': { width: '100% !important', height: 'calc(100% + 12px) !important' }, '&:active': { transform: 'scale(0.94)' } }, dayWeekSimple: { maxWidth: 184 }, simpleTimes: { '&.onesy-Line-direction-row': { '& > *': { flex: '1 1 auto' } }, '&.onesy-Line-direction-column': { '& > *': { width: '100%' } } }, legend: { padding: '2px', alignSelf: 'center', maxWidth: '100%', overflow: 'auto hidden' }, itemLegend: { cursor: 'pointer', userSelect: 'none', opacity: 0.5, transition: theme.methods.transitions.make(['opacity', 'transform']), '&:active': { transform: 'scale(0.94)' } }, itemLegendActive: { opacity: 1 }, overflowX: { padding: '2px 0', overflow: 'auto hidden' } }), { name: 'onesy-CalendarAvailability' }); const CalendarAvailability = props_ => { const theme = useOnesyTheme(); const l = theme.l; const props = _objectSpread(_objectSpread(_objectSpread({}, theme?.ui?.elements?.all?.props?.default), theme?.ui?.elements?.onesyCalendarAvailability?.props?.default), props_); const Line = theme?.elements?.Line || LineElement; const Type = theme?.elements?.Type || TypeElement; const Tooltip = theme?.elements?.Tooltip || TooltipElement; const IconButton = theme?.elements?.IconButton || IconButtonElement; const Label = theme?.elements?.Label || LabelElement; const Switch = theme?.elements?.Switch || SwitchElement; const Modal = theme?.elements?.Modal || ModalElement; const ModalHeader = theme?.elements?.ModalHeader || ModalHeaderElement; const ModalMain = theme?.elements?.ModalMain || ModalMainElement; const Slide = theme?.elements?.Slide || SlideElement; const Button = theme?.elements?.Button || ButtonElement; const Select = theme?.elements?.Select || SelectElement; const CalendarWeek = theme?.elements?.CalendarWeek || CalendarWeekElement; const { name, date: date_, dateDefault, times: timesProps, events, meta, views: viewsProps = [{ name: l('Week'), value: 'week' }, { name: l('Day'), value: 'day' }, { name: l('Simple'), value: 'simple' }], onUpdate, onRemove, onChangeDate: onChangeDateProps, startHeader, endHeader, startLeft, endLeft, startRight, endRight, startLeftModal, endLeftModal, startRightModal, endRightModal, Component = Line, IconEdit = IconMaterialEdit, IconPrevious = IconMaterialArrowBackIosNew, IconNext = IconMaterialArrowForwardIos, IconRemove = IconMaterialDelete, IconClose = IconMaterialKeyboardArrowDown, WeekProps, DayProps, IconProps, IconButtonProps, className, children } = props, other = _objectWithoutProperties(props, _excluded); const { classes } = useStyle(); const [now, setNow] = React.useState(new OnesyDate()); const [date, setDate] = React.useState(dateDefault || date_ || new OnesyDate()); const [view, setView] = React.useState('week'); const [displayTime, setDisplayTime] = React.useState(true); const [modal, setModal] = React.useState(); const [statuses, setStatuses] = React.useState({}); const refs = { date: React.useRef(date), displayTime: React.useRef(displayTime), interval: React.useRef(undefined), calendar: React.useRef(undefined), days: React.useRef({}), overlaping: React.useRef({}), statuses: React.useRef(statuses) }; refs.date.current = date; refs.displayTime.current = displayTime; refs.statuses.current = statuses; const times = React.useMemo(() => { if (events) { return [{ dates: { active: true, values: (is('array', events) ? events : [events]).filter(Boolean) } }]; } return (is('array', timesProps) ? timesProps : [timesProps]).filter(Boolean); }, [events, timesProps]); const onStatusToggle = (value = 'working') => { setStatuses(previous => _objectSpread(_objectSpread({}, previous), {}, { [value]: previous[value] === undefined ? false : !previous[value] })); }; const rangeShade = theme.palette.light ? 70 : 40; React.useEffect(() => { // 1 minute refs.interval.current = setInterval(() => { setNow(new OnesyDate()); }, 60 * 1e3); return () => { clearInterval(refs.interval.current); }; }, []); // Date React.useEffect(() => { if (date_ !== undefined && date_ !== date) setDate(date_); }, [date_]); const onOpen = item => { setModal(_objectSpread(_objectSpread({}, item), {}, { open: true })); }; const onClose = () => { setModal(item_0 => _objectSpread(_objectSpread({}, item_0), {}, { open: false })); }; const onChangeDisplayTime = valueNew => { setDisplayTime(valueNew); }; const onChangeView = valueNew_0 => { setView(valueNew_0); }; const optionsStatus = React.useMemo(() => { return [{ name: l('Working'), value: 'working' }, { name: l('Not working'), value: 'not-working' }, { name: l('On a break'), value: 'break' }, { name: l('Scheduled'), value: 'pending' }, { name: l('Rescheduled'), value: 'rescheduled' }, { name: l('Cancelled'), value: 'canceled' }, { name: l('Other'), value: 'other' }]; }, []); const onToday = () => { const valueNew_1 = new OnesyDate(); setDate(valueNew_1); if (is('function', onChangeDateProps)) onChangeDateProps(valueNew_1); }; const onPrevious = () => { let valueNew_2 = new OnesyDate(); setDate(previous_0 => { valueNew_2 = remove(1, ['week', 'simple'].includes(view) ? 'week' : view, previous_0); return valueNew_2; }); if (is('function', onChangeDateProps)) onChangeDateProps(valueNew_2); }; const onNext = () => { let valueNew_3 = new OnesyDate(); setDate(previous_1 => { valueNew_3 = add(1, ['week', 'simple'].includes(view) ? 'week' : view, previous_1); return valueNew_3; }); if (is('function', onChangeDateProps)) onChangeDateProps(valueNew_3); }; const getDates = available => { const values = available.dates?.values || []; return values.map(item_1 => { if (item_1.entire) { if (item_1.from) { let from = new OnesyDate(item_1.from); let to; if (['day', 'week', 'month', 'year'].includes(item_1.entire)) from = startOf(from, 'day'); if (item_1.entire === 'minute') from = startOf(from, 'minute'); if (item_1.entire === 'hour') from = startOf(from, 'hour'); to = endOf(from, item_1.entire); item_1.from = from.milliseconds; item_1.to = to.milliseconds; } } return item_1; }); }; const getDatesWeek = available_0 => { const weekFrom = startOf(date, 'week'); const weekTo = endOf(date, 'week'); return getDates(available_0).filter(item_2 => { const from_0 = new OnesyDate(item_2.from); const to_0 = new OnesyDate(item_2.to); return !(from_0.milliseconds >= weekTo.milliseconds || to_0.milliseconds <= weekFrom.milliseconds); }); }; const getColor = item_3 => { let palette = theme.palette.color.neutral; if (item_3?.status === 'working') palette = theme.palette.color.success; if (item_3?.status === 'not-working') palette = theme.palette.color.info; if (item_3?.status === 'break') palette = theme.palette.color.warning; if (item_3?.status === 'pending') palette = theme.methods.color(colors.yellow[50]); if (item_3?.status === 'rescheduled') palette = theme.methods.color(colors.purple[50]); if (item_3?.status === 'canceled') palette = theme.palette.color.error; if (item_3?.status === 'other') palette = theme.palette.color.neutral; return palette[rangeShade]; }; const itemToText = item_4 => { if (item_4 === 'pending') return l('Scheduled'); if (item_4 === 'not-count-workout-session') return l(`Don't count workout session`); return optionsStatus?.find(itemStatus => itemStatus.value === item_4)?.name ?? l(item_4); }; const viewOptions = React.useMemo(() => { return viewsProps?.map(item_5 => ({ name: capitalize(item_5?.name), value: item_5?.value })); }, [viewsProps]); const days = React.useMemo(() => { const weekStartDate = set(4, 'hour', startOf(date, 'week')); return Array.from({ length: 7 }).map((_, index) => add(index, 'day', weekStartDate)); }, [date]); const simpleTimesUI = () => { return /*#__PURE__*/_jsx(Line, { gap: 3, direction: { default: 'row', 1400: 'column' }, className: classes.simpleTimes, fullWidth: true, children: days.map((itemDay, index_0) => { const values_0 = times.filter(item_6 => item_6.weekly.days[index_0 + 1]?.active).flatMap(item_7 => item_7.weekly.days[index_0 + 1].values).filter(item_8 => item_8 && [undefined, true].includes(refs.statuses.current[item_8.status || 'working'])); values_0.sort((a, b) => b.from > a.from ? -1 : 1); return /*#__PURE__*/_jsxs(Line, { gap: 1.5, direction: "column", children: [/*#__PURE__*/_jsxs(Line, { gap: 0, align: "center", fullWidth: true, children: [/*#__PURE__*/_jsx(Type, { version: "h3", weight: 400, children: format(itemDay, 'dd', { l }) }), /*#__PURE__*/_jsx(Line, { align: "center", justify: "center", className: classNames([classes.weekDay, itemDay.year === now.year && itemDay.dayYear === now.dayYear && classes.today]), children: /*#__PURE__*/_jsx(Type, { version: "b2", weight: 200, children: format(itemDay, 'DD.MM.', { l }) }) })] }), /*#__PURE__*/_jsx(Line, { gap: 2, fullWidth: true, children: !!values_0.length ? values_0.map((itemValue, indexItem) => { const itemValueFrom = new OnesyDate(itemValue.from); const itemValueTo = new OnesyDate(itemValue.to); return /*#__PURE__*/_jsxs(Line, { gap: 0.5, className: classes.dayWeekSimple, children: [/*#__PURE__*/_jsxs(Line, { gap: 1, direction: "row", align: "center", children: [/*#__PURE__*/_jsx(Line, { className: classes.palettePreview, style: { background: getColor(itemValue) } }), /*#__PURE__*/_jsxs(Type, { version: "b2", weight: 300, children: [format(itemValueFrom, 'hh:mm a', { l }), " \u2014 ", format(itemValueTo, 'hh:mm a', { l })] })] }), itemValue.description && /*#__PURE__*/_jsx(Type, { version: "b2", weight: 200, whiteSpace: "pre-wrap", className: classNames([classes.timeDescription, !refs.displayTime.current && 'onesy-work-day-time']), dangerouslySetInnerHTML: { __html: textToInnerHTML(itemValue.description) } })] }, indexItem); }) : /*#__PURE__*/_jsx(Type, { version: "b2", children: l('No information for this day') }) })] }, index_0); }) }); }; const simpleExceptionsUI = () => { const items = times.flatMap(item_9 => getDatesWeek(item_9)); if (!items.length) return /*#__PURE__*/_jsx(Type, { version: "b1", children: l('No exceptions this week') }); return /*#__PURE__*/_jsx(_Fragment, { children: items.map((itemValue_0, index_1) => { const day = set(index_1 + 1, 'dayWeek'); const itemValueFrom_0 = new OnesyDate(itemValue_0.from); const itemValueTo_0 = new OnesyDate(itemValue_0.to); return /*#__PURE__*/_jsxs(Line, { gap: 1, direction: "column", children: [/*#__PURE__*/_jsxs(Line, { gap: 1, direction: "row", align: "center", fullWidth: true, children: [/*#__PURE__*/_jsx(Line, { className: classes.palettePreview, style: { background: getColor(itemValue_0) } }), /*#__PURE__*/_jsxs(Line, { gap: 1, direction: "row", align: "center", children: [/*#__PURE__*/_jsx(Type, { version: "b2", weight: 400, children: format(day, 'dd', { l }) }), /*#__PURE__*/_jsxs(Type, { version: "b2", children: [format(itemValueFrom_0, formats.entire, { l }), " \u2014 ", format(itemValueTo_0, formats.entire, { l })] })] })] }), itemValue_0.description && /*#__PURE__*/_jsx(Type, { version: "b2", weight: 200, whiteSpace: "pre-wrap", className: classNames([classes.timeDescription, !refs.displayTime.current && 'onesy-work-day-time']), dangerouslySetInnerHTML: { __html: textToInnerHTML(itemValue_0.description) } })] }, index_1); }) }); }; const formattedDate = React.useMemo(() => { if (view === 'day') return format(date, `MMMM DD, YYYY`, { l }); if (['week', 'simple'].includes(view)) return `${format(startOf(date, 'week'), `MMM DD, YYYY`, { l })}${format(endOf(date, 'week'), `MMM DD, YYYY`, { l })}`; }, [view, date]); const legend = React.useMemo(() => { return /*#__PURE__*/_jsx(Line, { rowGap: 1.5, direction: "row", align: "center", className: classes.legend, children: optionsStatus.map((item_10, index_2) => /*#__PURE__*/_jsxs(Line, { gap: 1, direction: "row", align: "center", onClick: () => onStatusToggle(item_10.value), flexNo: true, className: classNames([classes.itemLegend, [undefined, true].includes(refs.statuses.current[item_10.value || 'working']) && classes.itemLegendActive]), children: [/*#__PURE__*/_jsx(Line, { className: classes.palettePreview, style: { background: getColor({ status: item_10.value }) } }), /*#__PURE__*/_jsx(Type, { version: "b2", children: item_10.name })] }, index_2)) }); }, [theme, statuses, optionsStatus]); return /*#__PURE__*/_jsxs(Component, _objectSpread(_objectSpread({ flex: true, fullWidth: true, className: classNames([staticClassName('CalendarAvailability', theme) && ['onesy-CalendarAvailability-root'], className, classes.root]) }, other), {}, { children: [/*#__PURE__*/_jsxs(Line, { gap: 1, fullWidth: true, className: classNames([staticClassName('CalendarAvailability', theme) && ['onesy-CalendarAvailability-header']]), children: [startHeader, /*#__PURE__*/_jsxs(Line, { gap: 2, direction: "row", wrap: "wrap", justify: "space-between", align: "center", fullWidth: true, children: [/*#__PURE__*/_jsxs(Line, { gap: 1.5, direction: "row", wrap: "wrap", align: "center", className: classes.aside, children: [startLeft, /*#__PURE__*/_jsx(Button, { color: "inherit", version: "outlined", size: "small", onClick: onToday, selected: now.days === date.days, children: l('Today') }), /*#__PURE__*/_jsxs(Line, { gap: 0, direction: "row", align: "center", children: [/*#__PURE__*/_jsx(Tooltip, { name: `${l('Previous')} ${l(view)}`, children: /*#__PURE__*/_jsx(IconButton, _objectSpread(_objectSpread({ onClick: onPrevious }, IconButtonProps), {}, { children: /*#__PURE__*/_jsx(IconPrevious, { size: "regular" }) })) }), /*#__PURE__*/_jsx(Tooltip, { name: `${l('Next')} ${l(view)}`, children: /*#__PURE__*/_jsx(IconButton, _objectSpread(_objectSpread({ onClick: onNext }, IconButtonProps), {}, { children: /*#__PURE__*/_jsx(IconNext, { size: "regular" }) })) })] }), /*#__PURE__*/_jsx(Type, { version: "h2", weight: 500, whiteSpace: "nowrap", children: formattedDate }), endLeft] }), /*#__PURE__*/_jsxs(Line, { gap: 1.5, direction: "row", align: "center", flexNo: true, className: classNames([classes.aside, classes.overflowX]), children: [startRight, ['week', 'day'].includes(view) && /*#__PURE__*/_jsxs(Label, { checked: displayTime, onChange: onChangeDisplayTime, children: [/*#__PURE__*/_jsx(Switch, {}), l('Display time')] }), /*#__PURE__*/_jsx(Select, { name: l('View'), value: view, onChange: onChangeView, options: viewOptions, size: "small", MenuProps: { portal: true, size: 'regular' } }), endRight] })] }), endHeader] }), /*#__PURE__*/_jsxs(Line, { ref: refs.calendar, gap: 2, flex: true, fullWidth: true, children: [view === 'simple' && /*#__PURE__*/_jsx(Line, { gap: 1, flex: true, fullWidth: true, children: /*#__PURE__*/_jsxs(Line, { gap: 4, fullWidth: true, className: classNames([staticClassName('CalendarAvailability', theme) && ['onesy-CalendarAvailability-simple'], classes.calendar]), children: [simpleTimesUI(), /*#__PURE__*/_jsxs(Line, { gap: 1.5, children: [/*#__PURE__*/_jsx(Type, { version: "t2", children: l('Exceptions this week') }), /*#__PURE__*/_jsx(Line, { gap: 1, children: simpleExceptionsUI() })] })] }) }), ['week', 'day'].includes(view) && /*#__PURE__*/_jsx(CalendarWeek, _objectSpread({ onOpen: onOpen, date: date, displayTime: displayTime, statuses: statuses, times: times, events: events, day: view === 'day' }, WeekProps)), legend] }), times && /*#__PURE__*/_jsxs(Modal, { open: !!modal?.open, onClose: onClose, minWidth: "lg", TransitionComponent: Slide, size: "small", children: [/*#__PURE__*/_jsxs(ModalHeader, { gap: 1, direction: "row", align: "center", justify: "space-between", fullWidth: true, children: [/*#__PURE__*/_jsxs(Line, { gap: 1, direction: "row", align: "center", children: [startLeftModal, /*#__PURE__*/_jsx(Line, { className: classes.palettePreview, style: { background: getColor(modal) } }), /*#__PURE__*/_jsx(Type, { version: "b2", weight: 100, children: cleanValue(itemToText(modal?.status), { capitalize: true }) }), endLeftModal] }), /*#__PURE__*/_jsxs(Line, { gap: 1, direction: "row", align: "center", children: [startRightModal, onUpdate && /*#__PURE__*/_jsx(Tooltip, { name: l('Update'), children: /*#__PURE__*/_jsx(IconButton, { onClick: () => { onUpdate(modal?.object); onClose(); }, children: /*#__PURE__*/_jsx(IconEdit, _objectSpread({}, IconProps)) }) }), onRemove && /*#__PURE__*/_jsx(Tooltip, { name: l('Remove'), children: /*#__PURE__*/_jsx(IconButton, { onClick: () => { onRemove(modal?.object); onClose(); }, children: /*#__PURE__*/_jsx(IconRemove, _objectSpread({}, IconProps)) }) }), /*#__PURE__*/_jsx(Tooltip, { name: l('Close'), children: /*#__PURE__*/_jsx(IconButton, { onClick: onClose, children: /*#__PURE__*/_jsx(IconClose, _objectSpread({}, IconProps)) }) }), endRightModal] })] }), /*#__PURE__*/_jsx(ModalMain, { align: "flex-start", className: classes.modalMain, children: /*#__PURE__*/_jsxs(Line, { gap: 1.5, fullWidth: true, children: [/*#__PURE__*/_jsxs(Type, { version: "l1", weight: 200, children: [format(modal?.day, 'dd', { l }), " ", format(new OnesyDate(modal?.from), modal?.weekly ? `hh:mm a` : formats.entire, { l }), " \u2014 ", format(new OnesyDate(modal?.to), modal?.weekly ? `hh:mm a` : formats.entire, { l })] }), modal?.description && /*#__PURE__*/_jsx(Type, { version: "b2", dangerouslySetInnerHTML: { __html: textToInnerHTML(modal.description) } })] }) })] })] })); }; CalendarAvailability.displayName = 'onesy-CalendarAvailability'; export default CalendarAvailability;