@amaui/ui-react
Version:
UI for React
393 lines (392 loc) • 23.5 kB
JavaScript
"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;