UNPKG

@amaui/ui-react

Version:
393 lines (392 loc) 23.5 kB
"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importDefault(require("react")); const utils_1 = require("@amaui/utils"); const style_react_1 = require("@amaui/style-react"); const date_1 = require("@amaui/date"); const Line_1 = __importDefault(require("../Line")); const Type_1 = __importDefault(require("../Type")); const Tooltip_1 = __importDefault(require("../Tooltip")); const utils_2 = require("../utils"); const useStyle = (0, style_react_1.style)(theme => ({ root: { color: theme.methods.palette.color.value('primary', 10), background: theme.palette.background.default.primary }, weekDay: { width: '44px', height: '44px', borderRadius: '50%' }, today: { background: theme.palette.color.primary[40], color: '#fff' }, days: { overflow: 'auto hidden' }, day: { minWidth: '184px', height: '134vh', width: 'calc((100% / 7) - 106px)' }, dayBody: { position: 'relative', borderLeft: `1px solid ${theme.palette.light ? '#dadada' : '#575757'}` }, dayHeader: { height: '78px', padding: '4px' }, hours: { position: 'sticky', top: '0px', left: '0px', width: '44px', height: '134vh', flex: '0 0 auto', marginRight: '8px', background: theme.palette.background.default.primary, zIndex: 14 }, hourWrapper: { position: 'relative' }, hour: { width: '50px', alignSelf: 'flex-start', left: '0px', position: 'absolute', top: '0', marginTop: '-8px', zIndex: '40', borderRadius: theme.methods.shape.radius.value(0.5) }, guide: { position: 'absolute', left: '-9px', height: '1px', width: 'calc(100% + 9px)', background: theme.palette.light ? '#dadada' : '#575757' }, guidelineHour: { position: 'absolute', left: '0px', height: '2px', transform: 'translateY(-50%)', background: theme.palette.color.tertiary[50], zIndex: 14, '&::before': { position: 'absolute', content: "''", width: '12px', height: '12px', borderRadius: '50%', background: theme.palette.color.tertiary[50], top: '-5px', left: '-6.5px' } }, range: { position: 'absolute', left: 0, right: 0, cursor: 'pointer', zIndex: 1, transition: theme.methods.transitions.make('box-shadow'), overflow: 'hidden', '& > *': { whiteSpace: 'pre-wrap', transition: theme.methods.transitions.make('opacity') }, '& > .amaui-work-day-time': { opacity: 0 }, '&:hover': { zIndex: 14, boxShadow: theme.shadows.values.default[2], '& > .amaui-work-day-time': { opacity: 1 } } }, time: { position: 'relative', zIndex: 1, padding: '0px 2px', borderRadius: 2 } }), { name: 'amaui-CalendarWeek' }); const CalendarWeek = react_1.default.forwardRef((props_, ref) => { const theme = (0, style_react_1.useAmauiTheme)(); const props = react_1.default.useMemo(() => { var _a, _b, _c, _d, _e, _f, _g, _h; return (Object.assign(Object.assign(Object.assign({}, (_d = (_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _a === void 0 ? void 0 : _a.elements) === null || _b === void 0 ? void 0 : _b.all) === null || _c === void 0 ? void 0 : _c.props) === null || _d === void 0 ? void 0 : _d.default), (_h = (_g = (_f = (_e = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _e === void 0 ? void 0 : _e.elements) === null || _f === void 0 ? void 0 : _f.amauiCalendarWeek) === null || _g === void 0 ? void 0 : _g.props) === null || _h === void 0 ? void 0 : _h.default), props_)); }, [props_]); const Line = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Line) || Line_1.default; }, [theme]); const Type = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Type) || Type_1.default; }, [theme]); const Tooltip = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Tooltip) || Tooltip_1.default; }, [theme]); const { date, times: timesProps, events, onOpen, onTimeClick, render, statuses = {}, displayTime = true, day: dayProp, className, children } = props, other = __rest(props, ["date", "times", "events", "onOpen", "onTimeClick", "render", "statuses", "displayTime", "day", "className", "children"]); const { classes } = useStyle(); const [now, setNow] = react_1.default.useState(new date_1.AmauiDate()); const refs = { date: react_1.default.useRef(date), displayTime: react_1.default.useRef(displayTime), interval: react_1.default.useRef(undefined), days: react_1.default.useRef({}), overlaping: react_1.default.useRef({}), statuses: react_1.default.useRef(statuses) }; refs.date.current = date; refs.displayTime.current = displayTime; refs.statuses.current = statuses; const times = react_1.default.useMemo(() => { if (events) { return [ { dates: { active: true, values: ((0, utils_1.is)('array', events) ? events : [events]).filter(Boolean) } } ]; } return ((0, utils_1.is)('array', timesProps) ? timesProps : [timesProps]).filter(Boolean); }, [events, timesProps]); const rangeShade = theme.palette.light ? 60 : 40; react_1.default.useEffect(() => { // 1 minute refs.interval.current = setInterval(() => { setNow(new date_1.AmauiDate()); }, 60 * 1e3); return () => { clearInterval(refs.interval.current); }; }, []); const getDates = react_1.default.useCallback((available) => { var _a; const values = ((_a = available.dates) === null || _a === void 0 ? void 0 : _a.values) || []; return values.map((item) => { if (item.entire) { if (item.from) { let from = new date_1.AmauiDate(item.from); let to; if (['day', 'week', 'month', 'year'].includes(item.entire)) from = (0, date_1.startOf)(from, 'day'); if (item.entire === 'minute') from = (0, date_1.startOf)(from, 'minute'); if (item.entire === 'hour') from = (0, date_1.startOf)(from, 'hour'); to = (0, date_1.endOf)(from, item.entire); item.from = from.milliseconds; item.to = to.milliseconds; } } return item; }); }, [times]); const getColor = react_1.default.useCallback((item) => { let palette = theme.palette.color.neutral; if ((item === null || item === void 0 ? void 0 : item.status) === 'working') palette = theme.palette.color.success; if ((item === null || item === void 0 ? void 0 : item.status) === 'not-working') palette = theme.palette.color.info; if ((item === null || item === void 0 ? void 0 : item.status) === 'break') palette = theme.palette.color.warning; if ((item === null || item === void 0 ? void 0 : item.status) === 'pending') palette = theme.methods.color(style_react_1.colors.yellow[50]); if ((item === null || item === void 0 ? void 0 : item.status) === 'rescheduled') palette = theme.methods.color(style_react_1.colors.purple[50]); if ((item === null || item === void 0 ? void 0 : item.status) === 'canceled') palette = theme.palette.color.error; if ((item === null || item === void 0 ? void 0 : item.status) === 'other') palette = theme.palette.color.neutral; if (item.color) palette = theme.methods.color(item.color); return palette[rangeShade]; }, [rangeShade, style_react_1.colors, theme]); const itemToText = react_1.default.useCallback((item) => { if (item === 'pending') return 'scheduled'; if (item === 'not-count-workout-session') return `don't count workout session`; return item; }, []); const renderTimes = (day, valuesAll, weekly = true, itemDay) => { if (itemDay !== undefined && !(itemDay === null || itemDay === void 0 ? void 0 : itemDay.active)) return null; if (weekly) { const ends_at = (itemDay === null || itemDay === void 0 ? void 0 : itemDay.ends_at) ? new date_1.AmauiDate(itemDay.ends_at) : undefined; if (ends_at) { const day_StartDay = (0, date_1.startOf)(day, 'day'); const ends_at_StartDay = (0, date_1.startOf)(ends_at, 'day'); if (day_StartDay.milliseconds >= ends_at_StartDay.milliseconds) return null; } } const dayStartOfDay = (0, date_1.startOf)(day, 'day'); const dayDate = (0, date_1.format)(day, utils_2.formats.date); const values = valuesAll === null || valuesAll === void 0 ? void 0 : valuesAll.filter((item) => { const from = new date_1.AmauiDate(item.from); const fromStartOfDay = (0, date_1.startOf)(from, 'day'); const to = new date_1.AmauiDate(item.to); const toStartOfDay = (0, date_1.startOf)(to, 'day'); return [undefined, true].includes(refs.statuses.current[item.status || 'working']) && (weekly ? weekly : ( // from ((dayDate === (0, date_1.format)(from, utils_2.formats.date)) || (dayStartOfDay.milliseconds > fromStartOfDay.milliseconds)) && // to ((dayDate === (0, date_1.format)(to, utils_2.formats.date)) || (dayStartOfDay.milliseconds < toStartOfDay.milliseconds)))); }); const elements = []; const renderTo = (valueNew) => { if (valueNew === '23:59') return '24:00'; return valueNew; }; values === null || values === void 0 ? void 0 : values.forEach((item, index) => { var _a, _b; if (!(item.from && item.to)) return; let from = new date_1.AmauiDate(item.from); let to = new date_1.AmauiDate(item.to); if (!weekly) { const fromStartOfDay = (0, date_1.startOf)(from, 'day'); const toStartOfDay = (0, date_1.startOf)(to, 'day'); if (((dayStartOfDay === null || dayStartOfDay === void 0 ? void 0 : dayStartOfDay.milliseconds) > (fromStartOfDay === null || fromStartOfDay === void 0 ? void 0 : fromStartOfDay.milliseconds)) && ((dayDate === (0, date_1.format)(to, utils_2.formats.date)) || (dayStartOfDay.milliseconds < toStartOfDay.milliseconds))) item.from = (0, date_1.startOf)(from, 'day').milliseconds; if (((dayDate === (0, date_1.format)(from, utils_2.formats.date)) || (dayStartOfDay.milliseconds > fromStartOfDay.milliseconds)) && ((dayStartOfDay === null || dayStartOfDay === void 0 ? void 0 : dayStartOfDay.milliseconds) < (toStartOfDay === null || toStartOfDay === void 0 ? void 0 : toStartOfDay.milliseconds))) item.to = (0, date_1.endOf)(to, 'day').milliseconds; } from = new date_1.AmauiDate(item.from); to = new date_1.AmauiDate(item.to); const itemDate = (0, date_1.format)(day, utils_2.formats.date); const top = +(100 * (((from.hour * 60) + from.minute) / (24 * 60))).toFixed(4); const bottom = +((100 - (100 * (((to.hour * 60) + (to.minute === 59 ? 60 : to.minute)) / (24 * 60))))).toFixed(4); if (!refs.days.current[itemDate]) refs.days.current[itemDate] = []; if (!refs.overlaping.current[itemDate]) refs.overlaping.current[itemDate] = []; const bottom_ = 100 - bottom; // intersections const overlaps = refs.days.current[itemDate].filter(([itemTop, itemBottom]) => { return !(top >= itemBottom || bottom_ <= itemTop); }); let level = 0; if (overlaps.length) { level = refs.overlaping.current[itemDate].filter(([itemTop, itemBottom]) => { return !(top >= itemBottom || bottom_ <= itemTop); }).length + 1; refs.overlaping.current[itemDate].push([top, bottom_]); } refs.days.current[itemDate].push([top, bottom_]); const minimal = ((100 - bottom) - top) < 5; const background = getColor(item); const WrapperElement = item.status ? Tooltip : react_1.default.Fragment; const WrapperElementProps = item.status ? { name: (0, utils_1.cleanValue)(itemToText(item.status), { capitalize: true }), color: getColor(item) } : undefined; const itemProps = { onClicl: (event) => onOpen(Object.assign(Object.assign({}, item), { day, weekly }), event), className: classes.range }; const style = Object.assign({ top: `${top}%`, bottom: `${bottom}%`, color: theme.methods.palette.color.text(background), background, left: `calc(0px + ${level * 10}px)` }, (top === 0 && bottom === 0 && { border: 'none' })); const elementRendered = (0, utils_1.is)('function', render) ? render(item, dayProp ? 'day' : 'week') : ((0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0.5, align: 'center', justify: 'center' }, itemProps, { children: [!minimal && refs.displayTime.current && ((0, jsx_runtime_1.jsxs)(Type, Object.assign({ version: 'l2', weight: 300, align: 'center', className: classes.time, style: { background: getColor(item) } }, { children: [(0, date_1.format)(from, 'hh:mm a'), " - ", renderTo((0, date_1.format)(to, 'hh:mm a'))] }))), item.description && ((0, jsx_runtime_1.jsx)(Type, { version: 'b2', dangerouslySetInnerHTML: { __html: (0, utils_1.textToInnerHTML)(item.description) }, style: { maxHeight: 24, maxWidth: '90%' } }))] }), index)); elements.push((0, jsx_runtime_1.jsx)(WrapperElement, Object.assign({}, WrapperElementProps, { children: react_1.default.cloneElement(elementRendered, Object.assign(Object.assign({}, itemProps), { className: (0, style_react_1.classNames)([ itemProps.className, (_a = elementRendered.props) === null || _a === void 0 ? void 0 : _a.className ]), style: Object.assign(Object.assign({}, style), (_b = elementRendered.props) === null || _b === void 0 ? void 0 : _b.style) })) }))); }); return elements; }; const onTimeClickMethod = react_1.default.useCallback((itemDay, event) => { const rect = event.currentTarget.getBoundingClientRect(); const relativeY = event.clientY - rect.top; const relativePercentage = (relativeY / rect.height) * 100; let timeDate = (0, date_1.set)(Math.floor(24 * (relativePercentage / 100)), 'hour', itemDay); // start of the hour timeDate = (0, date_1.startOf)(timeDate, 'hour'); onTimeClick === null || onTimeClick === void 0 ? void 0 : onTimeClick(timeDate, dayProp ? 'day' : 'week', event); }, []); const timesUI = react_1.default.useCallback((dayDate) => { // clean up refs.days.current = {}; refs.overlaping.current = {}; const day = dayDate.dayWeek === 0 ? 7 : dayDate.dayWeek; return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: times.map(item => { var _a; return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [item.weekly && renderTimes(dayDate, (_a = item.weekly.days[day]) === null || _a === void 0 ? void 0 : _a.values, true, item.weekly.days[day]), item.dates && renderTimes(dayDate, getDates(item), false)] }); }) }); }, [theme, times, date]); const hours = react_1.default.useMemo(() => { return Array.from({ length: 24 }).map((item, index) => (0, date_1.set)(index, 'hour', date)); }, [date]); const days = react_1.default.useMemo(() => { const weekStartDate = (0, date_1.set)(4, 'hour', (0, date_1.startOf)(date, 'week')); return Array.from({ length: 7 }).map((_, index) => (0, date_1.add)(index, 'day', weekStartDate)); }, [date]); return ((0, jsx_runtime_1.jsx)(Line, Object.assign({ ref: ref, gap: 1.5, flex: true, fullWidth: true, className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('CalendarWeek', theme) && [ 'amaui-CalendarWeek-root', dayProp && 'amaui-CalendarWeek-prop-day' ], className, classes.root ]) }, other, { children: (0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0, direction: 'row', align: 'stretch', flex: true, fullWidth: true, className: classes.days }, { children: [(0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0, align: 'center', fullWidth: true, className: (0, style_react_1.classNames)([ 'amaui-hours', classes.hours ]) }, { children: [(0, jsx_runtime_1.jsx)(Line, { className: classes.dayHeader }), (0, jsx_runtime_1.jsx)(Line, Object.assign({ gap: 0, flex: true, fullWidth: true, className: classes.hourWrapper }, { children: hours.map((itemHour, index) => ((0, jsx_runtime_1.jsx)(Line, Object.assign({ gap: 1, align: 'unset', className: classes.hour, style: { top: `${(100 / 24) * index}%` } }, { children: (0, jsx_runtime_1.jsx)(Type, Object.assign({ version: 'b3', whiteSpace: 'nowrap' }, { children: (0, date_1.format)(itemHour, 'h A') })) }), index))) }))] })), dayProp && ((0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0, direction: 'column', align: 'center', flex: true, className: (0, style_react_1.classNames)([ 'amaui-day', classes.day ]) }, { children: [(0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0, align: 'center', fullWidth: true, className: classes.dayHeader }, { children: [(0, jsx_runtime_1.jsx)(Type, Object.assign({ version: 'l1', weight: 200 }, { children: (0, date_1.format)(date, 'd') })), (0, jsx_runtime_1.jsx)(Line, Object.assign({ align: 'center', justify: 'center', className: (0, style_react_1.classNames)([ classes.weekDay, date.year === now.year && date.dayYear === now.dayYear && classes.today ]) }, { children: (0, jsx_runtime_1.jsx)(Type, Object.assign({ version: 'h3', weight: 400, align: 'center' }, { children: (0, date_1.format)(date, 'D') })) }))] })), (0, jsx_runtime_1.jsxs)(Line, Object.assign({ className: classes.dayBody, flex: true, fullWidth: true, onClick: (event) => onTimeClickMethod(date, event) }, { children: [timesUI(date), hours.map((itemGuide, indexGuide) => ((0, jsx_runtime_1.jsx)(Line, { className: classes.guide, fullWidth: true, style: { top: `${(100 / 24) * indexGuide}%` } }, indexGuide))), ((0, date_1.format)(now, utils_2.formats.date) === (0, date_1.format)(date, utils_2.formats.date)) && ((0, jsx_runtime_1.jsx)(Line, { className: classes.guidelineHour, fullWidth: true, style: { top: `${(((now.hour * 60) + now.minute) / (24 * 60)) * 100}%` } }))] }))] }))), !dayProp && days.map((itemDay, index) => { return ((0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0, direction: 'column', align: 'center', flex: true, className: (0, style_react_1.classNames)([ 'amaui-day', classes.day ]) }, { children: [(0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0, align: 'center', fullWidth: true, className: classes.dayHeader }, { children: [(0, jsx_runtime_1.jsx)(Type, Object.assign({ version: 'l1', weight: 200 }, { children: (0, date_1.format)(itemDay, 'd') })), (0, jsx_runtime_1.jsx)(Line, Object.assign({ align: 'center', justify: 'center', className: (0, style_react_1.classNames)([ classes.weekDay, itemDay.year === now.year && itemDay.dayYear === now.dayYear && classes.today ]) }, { children: (0, jsx_runtime_1.jsx)(Type, Object.assign({ version: 'h3', weight: 400, align: 'center' }, { children: (0, date_1.format)(itemDay, 'D') })) }))] })), (0, jsx_runtime_1.jsxs)(Line, Object.assign({ className: classes.dayBody, flex: true, fullWidth: true, onClick: (event) => onTimeClickMethod(itemDay, event) }, { children: [timesUI(itemDay), hours.map((itemGuide, indexGuide) => ((0, jsx_runtime_1.jsx)(Line, { className: classes.guide, fullWidth: true, style: { top: `${(100 / 24) * indexGuide}%` } }, indexGuide))), ((0, date_1.format)(now, utils_2.formats.date) === (0, date_1.format)(itemDay, utils_2.formats.date)) && ((0, jsx_runtime_1.jsx)(Line, { className: classes.guidelineHour, fullWidth: true, style: { top: `${(((now.hour * 60) + now.minute) / (24 * 60)) * 100}%` } }))] }))] }), index)); })] })) }))); }); CalendarWeek.displayName = 'amaui-CalendarWeek'; exports.default = CalendarWeek;