@onesy/ui-react
Version:
UI for React
718 lines (717 loc) • 26.3 kB
JavaScript
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;