UNPKG

hwc

Version:

A react Headless Week Calendar toolkit to build calendars like Google Calendar.

696 lines (627 loc) 23.7 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var React = require('react'); var React__default = _interopDefault(React); var HwcContext = /*#__PURE__*/React__default.createContext(null); var HwcProvider = function HwcProvider(_ref) { var value = _ref.value, children = _ref.children; var Ctx = HwcContext; return React__default.createElement(Ctx.Provider, { value: value }, children); }; var useHwcContext = function useHwcContext() { var ctx = React__default.useContext(HwcContext); if (ctx === null) throw new Error('No WeekPicker Context found. Please Wrap Hwc components in <HwcProvider />'); return ctx; }; function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var MINUTE_IN_MILLIS = 60 * 1000; var HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS; var DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS; var posToDate = function posToDate(pos, width, startDay, hourHeight, daysCount) { var minuteHeight = 60 / hourHeight; var minutesDay = pos.y * minuteHeight; var dayInGrid = Math.trunc(pos.x / (width || 1) * daysCount); return new Date(startDay.getTime() + dayInGrid * DAY_IN_MILLIS + minutesDay * MINUTE_IN_MILLIS); }; var roundToQuarterHour = function roundToQuarterHour(date) { var rounded = startOfMinute(date); var minutes = rounded.getMinutes() + rounded.getHours() * 60; var roundedMinute = Math.round(minutes / 60 * 4) * 60 / 4; var d = startOfDay(date); d.setMinutes(roundedMinute); return d; }; var roundToSemiHour = function roundToSemiHour(date) { var rounded = startOfMinute(date); var minutes = rounded.getMinutes() + rounded.getHours() * 60; var roundedMinute = Math.round(minutes / 60 * 2) * 60 / 2; var d = startOfDay(date); d.setMinutes(roundedMinute); return d; }; var startOfDay = function startOfDay(input) { var d = new Date(input); d.setHours(0, 0, 0, 0); return d; }; var startOfMinute = function startOfMinute(input) { var d = new Date(input); d.setSeconds(0, 0); return d; }; var useMoveEvent = function useMoveEvent(date, updateEventHandler) { var _useState = React.useState(), moveState = _useState[0], setMoveState = _useState[1]; var setEventMoving = React.useCallback(function (event, evIndex) { return setMoveState(event !== undefined && evIndex !== undefined ? { startEvent: event, evIndex: evIndex, date: date, dragDelta: date.getTime() - event.startDate.getTime() } : undefined); }, [setMoveState, date]); React.useEffect(function () { if (!moveState) return; var delta = roundToQuarterHour(new Date(moveState.date.getTime() - date.getTime())).getTime(); updateEventHandler(moveState.evIndex, { startDate: new Date(moveState.startEvent.startDate.getTime() - delta), endDate: new Date(moveState.startEvent.endDate.getTime() - delta) }, moveState.startEvent); }, [moveState, date, updateEventHandler]); React.useEffect(function () { if (!moveState) return; var mouseUpHandler = function mouseUpHandler() { window.removeEventListener('mouseup', mouseUpHandler); setMoveState(undefined); }; window.addEventListener('mouseup', mouseUpHandler); }, [moveState, setMoveState]); return [setEventMoving]; }; var useResizeEvent = function useResizeEvent(date, updateEventHandler) { var _useState = React.useState(), resizeState = _useState[0], setResizeState = _useState[1]; var setEventResizing = React.useCallback(function (event, evIndex, grip) { return setResizeState(event !== undefined && evIndex !== undefined && grip !== undefined ? { event: event, evIndex: evIndex, date: date, grip: grip } : undefined); }, [setResizeState, date]); React.useEffect(function () { if (!resizeState) return; var delta = roundToQuarterHour(new Date(resizeState.date.getTime() - date.getTime())).getTime(); updateEventHandler(resizeState.evIndex, { startDate: new Date(resizeState.grip === 'start' ? resizeState.event.startDate.getTime() - delta : resizeState.event.startDate), endDate: new Date(resizeState.grip === 'end' ? resizeState.event.endDate.getTime() - delta : resizeState.event.endDate) }); }, [resizeState, date, updateEventHandler]); React.useEffect(function () { if (!resizeState) return; var mouseUpHandler = function mouseUpHandler() { window.removeEventListener('mouseup', mouseUpHandler); setResizeState(undefined); }; window.addEventListener('mouseup', mouseUpHandler); }, [resizeState, setResizeState]); return [setEventResizing]; }; var useShadowEvent = function useShadowEvent(date) { var _useState = React.useState(), startDragDate = _useState[0], setStartDragDate = _useState[1]; var shadowEvent = React.useMemo(function () { if (!startDragDate) return undefined; var dates = [roundToQuarterHour(startDragDate), roundToQuarterHour(date)].sort(function (a, b) { return a.getTime() - b.getTime(); }); return { startDate: dates[0], endDate: dates[1] }; }, [startDragDate, date]); return [shadowEvent, setStartDragDate]; }; var useHwc = function useHwc(props) { if (props === void 0) { props = {}; } var _props = props, _props$startDay = _props.startDay, startDay = _props$startDay === void 0 ? new Date() : _props$startDay, _props$cellHeight = _props.cellHeight, cellHeight = _props$cellHeight === void 0 ? 50 : _props$cellHeight, _props$daysCount = _props.daysCount, daysCount = _props$daysCount === void 0 ? 7 : _props$daysCount, _props$events = _props.events, events = _props$events === void 0 ? [] : _props$events, _props$onAddEvent = _props.onAddEvent, onAddEvent = _props$onAddEvent === void 0 ? function () {} : _props$onAddEvent, _props$onUpdateEvent = _props.onUpdateEvent, onUpdateEvent = _props$onUpdateEvent === void 0 ? function () {} : _props$onUpdateEvent; var normalizedStartDay = React.useMemo(function () { return startOfDay(startDay); }, [startDay]); // column witdh var _useState = React.useState(0), width = _useState[0], setWidth = _useState[1]; var cellWidth = React.useMemo(function () { return width / daysCount; }, [width, daysCount]); // mouse position and date var _useState2 = React.useState({ x: 0, y: 0 }), pos = _useState2[0], setPos = _useState2[1]; var date = React.useMemo(function () { return posToDate(pos, width, normalizedStartDay, cellHeight, daysCount); }, [pos, width, normalizedStartDay, cellHeight, daysCount]); // shadow event var _useShadowEvent = useShadowEvent(date), shadowEvent = _useShadowEvent[0], setStartDragDate = _useShadowEvent[1]; // move and resize var internalUpdateEventHandler = React.useCallback(function (evIndex, newEvent) { var ev = events[evIndex]; if (ev.startDate.getTime() !== newEvent.startDate.getTime() || ev.endDate.getTime() !== newEvent.endDate.getTime()) onUpdateEvent(evIndex, _extends({}, ev, newEvent), ev); }, [events, onUpdateEvent]); var _useMoveEvent = useMoveEvent(date, internalUpdateEventHandler), setEventMoving = _useMoveEvent[0]; var _useResizeEvent = useResizeEvent(date, internalUpdateEventHandler), setEventResizing = _useResizeEvent[0]; // handlers var addEventHandler = React.useCallback(function (ev) { return onAddEvent(ev); }, [onAddEvent]); var updateEventHandler = React.useCallback(function (eventIndex, newEvent, previousEvent) { return onUpdateEvent(eventIndex, newEvent, previousEvent); }, [onUpdateEvent]); var setEventMovingHandler = React.useCallback(function (evIndex) { setEventMoving(evIndex !== undefined ? events[evIndex] : undefined, evIndex); }, [setEventMoving, events]); var setEventResizingHandler = React.useCallback(function (evIndex, grip) { setEventResizing(evIndex !== undefined ? events[evIndex] : undefined, evIndex, evIndex !== undefined ? grip : undefined); }, [setEventResizing, events]); return { pos: pos, date: date, cellWidth: cellWidth, cellHeight: cellHeight, startDay: normalizedStartDay, daysCount: daysCount, events: events, setPos: setPos, setWidth: setWidth, setEventMoving: setEventMovingHandler, setEventResizing: setEventResizingHandler, shadowEvent: shadowEvent, setStartDragDate: setStartDragDate, addEvent: addEventHandler, updateEvent: updateEventHandler }; }; var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect; var _excluded = ["onMouseMove", "onMouseDown", "style", "children"]; var HwcDragPane = /*#__PURE__*/React__default.forwardRef(function (_ref, ref) { var _ref$onMouseMove = _ref.onMouseMove, onMouseMove = _ref$onMouseMove === void 0 ? function () {} : _ref$onMouseMove, _ref$onMouseDown = _ref.onMouseDown, onMouseDown = _ref$onMouseDown === void 0 ? function () {} : _ref$onMouseDown, style = _ref.style, children = _ref.children, props = _objectWithoutPropertiesLoose(_ref, _excluded); var _useHwcContext = useHwcContext(), setPos = _useHwcContext.setPos, date = _useHwcContext.date, setWidth = _useHwcContext.setWidth, setStartDragDate = _useHwcContext.setStartDragDate, addEvent = _useHwcContext.addEvent, shadowEvent = _useHwcContext.shadowEvent, cellHeight = _useHwcContext.cellHeight; var _useState = React.useState(null), element = _useState[0], setRef = _useState[1]; useIsomorphicLayoutEffect(function () { if (!element) return; var observer = new ResizeObserver(function (_ref2) { var entry = _ref2[0]; if (entry) setWidth(entry.contentRect.width); }); observer.observe(element); return function () { observer.disconnect(); }; }, [element, setWidth]); // shadow event var mouseDownDate = React.useRef(); var mouseDownHandler = React.useCallback(function (ev) { if (!(ev.target instanceof Element) || !ev.target.hasAttribute('week-picker-cell')) return; // prevent dnd ev.preventDefault(); mouseDownDate.current = date; onMouseDown(ev); }, [date, onMouseDown]); var mouseMoveHandler = React.useCallback(function (ev) { var offset = ev.currentTarget.getBoundingClientRect(); setPos({ x: ev.clientX - offset.left, y: ev.clientY - offset.top }); onMouseMove(ev); if (mouseDownDate.current && !shadowEvent && roundToQuarterHour(mouseDownDate.current).getTime() !== roundToQuarterHour(date).getTime()) { setStartDragDate(mouseDownDate.current); } }, [setPos, onMouseMove, shadowEvent, date, setStartDragDate]); useIsomorphicLayoutEffect(function () { var dragEndHandler = function dragEndHandler() { if (mouseDownDate.current) { if (shadowEvent) { addEvent(shadowEvent); } else { var roundedDate = roundToSemiHour(date); addEvent({ startDate: roundedDate, endDate: new Date(roundedDate.getTime() + 2 * HOUR_IN_MILLIS) }); } } mouseDownDate.current = undefined; setStartDragDate(undefined); }; window.addEventListener('mouseup', dragEndHandler); return function () { window.removeEventListener('mouseup', dragEndHandler); }; }, [setStartDragDate, addEvent, shadowEvent, date]); return React__default.createElement("div", Object.assign({}, props, { style: _extends({ position: 'relative', height: cellHeight * 24 + "px" }, style), onMouseMove: mouseMoveHandler, onDrag: function onDrag(ev) { return ev.preventDefault(); }, onMouseDown: mouseDownHandler, ref: ref || setRef }), children); }); var _excluded$1 = ["style", "colStyle", "renderCell"]; var defaultCellRenderer = function defaultCellRenderer(_ref) { var style = _ref.style; return React__default.createElement("div", { style: style }); }; var HwcGrid = /*#__PURE__*/React__default.forwardRef(function (_ref2, ref) { var style = _ref2.style, colStyle = _ref2.colStyle, _ref2$renderCell = _ref2.renderCell, renderCell = _ref2$renderCell === void 0 ? defaultCellRenderer : _ref2$renderCell, props = _objectWithoutPropertiesLoose(_ref2, _excluded$1); var _useHwcContext = useHwcContext(), startDay = _useHwcContext.startDay, cellHeight = _useHwcContext.cellHeight, cellWidth = _useHwcContext.cellWidth, daysCount = _useHwcContext.daysCount; var days = new Array(daysCount).fill(0).map(function (_, index) { return new Date(startDay.getTime() + index * DAY_IN_MILLIS); }); var lines = new Array(24).fill(0).map(function (_, index) { return index; }); return React__default.createElement("div", Object.assign({}, props, { ref: ref, style: _extends({ position: 'absolute', width: '100%', display: 'flex' }, style) }), days.map(function (d, dIndex) { return React__default.createElement("div", { key: dIndex, style: _extends({ flex: '0 0 1', width: '100%' }, colStyle) }, lines.map(function (l) { var x = dIndex === 0 ? 'start' : dIndex === days.length - 1 ? 'end' : undefined; var y = l === 0 ? 'start' : l === lines.length - 1 ? 'end' : undefined; return React__default.createElement(React.Fragment, { key: l }, React__default.Children.map(renderCell({ cellHeight: cellHeight, cellWidth: cellWidth, style: { height: cellHeight + "px", borderLeft: x === 'start' ? undefined : "#aaa solid 1px", borderTop: y === 'start' ? undefined : "#aaa solid 1px", boxSizing: 'border-box' }, x: x, y: y, day: d, date: new Date(d.getTime() + l * HOUR_IN_MILLIS) }), function (node) { return React__default.cloneElement(node, { 'week-picker-cell': '' }); })); })); })); }); var buildIsEventVisibleFilter = function buildIsEventVisibleFilter(startDay, daysCount) { var startMillis = startOfDay(startDay).getTime(); var endMillis = startMillis + daysCount * DAY_IN_MILLIS; return function (ev) { return ev.startDate.getTime() < endMillis && ev.endDate.getTime() > startMillis; }; }; var getCroppedEventsByDay = function getCroppedEventsByDay(ev) { var events = []; var evEndMillis = ev.endDate.getTime(); for (var i = 0; i < 20; i++) { var lastCroppedEvent = events[events.length - 1]; var nextDayMillis = lastCroppedEvent ? startOfDay(lastCroppedEvent.startDate).getTime() + 2 * DAY_IN_MILLIS : startOfDay(ev.startDate).getTime() + 1 * DAY_IN_MILLIS; var isOverflow = nextDayMillis < evEndMillis; events.push(_extends({}, ev, { startDate: new Date(lastCroppedEvent ? lastCroppedEvent.endDate : ev.startDate), endDate: new Date(isOverflow ? nextDayMillis : evEndMillis) })); if (!isOverflow) break; } return events; }; var useCards = function useCards() { var _useHwcContext = useHwcContext(), events = _useHwcContext.events, startDay = _useHwcContext.startDay, daysCount = _useHwcContext.daysCount, cellWidth = _useHwcContext.cellWidth, cellHeight = _useHwcContext.cellHeight; var eventsFilter = React.useMemo(function () { return buildIsEventVisibleFilter(startDay, daysCount); }, [startDay, daysCount]); return React.useMemo(function () { return events.map(function (event, index) { return { event: event, index: index }; }).filter(function (c) { return eventsFilter(c.event); }).map(function (c) { var evts = getCroppedEventsByDay(c.event); return evts.map(function (ev) { return _extends({}, c, { event: ev }); }); }).map(function (croppeds) { return croppeds.map(function (card, index) { return _extends({}, card, { isFirst: index === 0, isLast: index === croppeds.length - 1 }); }); }).flat().filter(function (c) { return eventsFilter(c.event); }).map(function (c) { var day = (startOfDay(c.event.startDate).getTime() - startOfDay(startDay).getTime()) / DAY_IN_MILLIS; var duration = c.event.endDate.getTime() - c.event.startDate.getTime(); var millisFromDayStart = c.event.startDate.getTime() - startOfDay(c.event.startDate).getTime(); var rect = { width: cellWidth, height: duration / HOUR_IN_MILLIS * cellHeight, left: day * cellWidth, top: millisFromDayStart / HOUR_IN_MILLIS * cellHeight }; return _extends({}, c, { rect: rect, style: { width: rect.width + "px", height: rect.height + "px" } }); }); }, [events, cellWidth, startDay, cellHeight, eventsFilter]); }; var useShadowCards = function useShadowCards() { var _useHwcContext = useHwcContext(), startDay = _useHwcContext.startDay, daysCount = _useHwcContext.daysCount, shadowEvent = _useHwcContext.shadowEvent, cellWidth = _useHwcContext.cellWidth, cellHeight = _useHwcContext.cellHeight; var eventsFilter = React.useMemo(function () { return buildIsEventVisibleFilter(startDay, daysCount); }, [startDay, daysCount]); return React.useMemo(function () { return (shadowEvent ? [{ event: shadowEvent }] : []).filter(function (c) { return eventsFilter(c.event); }).map(function (c) { var evts = getCroppedEventsByDay(c.event); return evts.map(function (ev) { return _extends({}, c, { event: ev }); }); }).map(function (croppeds) { return croppeds.map(function (card, index) { return _extends({}, card, { index: index, isFirst: index === 0, isLast: index === croppeds.length - 1 }); }); }).flat().filter(function (c) { return eventsFilter(c.event); }).map(function (c) { var day = (startOfDay(c.event.startDate).getTime() - startOfDay(startDay).getTime()) / DAY_IN_MILLIS; var duration = c.event.endDate.getTime() - c.event.startDate.getTime(); var millisFromDayStart = c.event.startDate.getTime() - startOfDay(c.event.startDate).getTime(); var rect = { width: cellWidth, height: duration / HOUR_IN_MILLIS * cellHeight, left: day * cellWidth, top: millisFromDayStart / HOUR_IN_MILLIS * cellHeight }; return _extends({}, c, { rect: rect, style: { width: rect.width + "px", height: rect.height + "px" } }); }); }, [cellWidth, startDay, shadowEvent, cellHeight, eventsFilter]); }; var GripRenderer = function GripRenderer(_ref) { var event = _ref.event, index = _ref.index, position = _ref.position, renderGrip = _ref.renderGrip; var _useHwcContext = useHwcContext(), setEventResizing = _useHwcContext.setEventResizing; var style = React.useMemo(function () { return { position: 'absolute', top: position === 'start' ? '-4px' : undefined, bottom: position === 'end' ? '-4px' : undefined, height: '9px', width: '100%', cursor: 'ns-resize' }; }, [position]); var onMouseDownHandler = React.useCallback(function (ev) { ev.preventDefault(); ev.stopPropagation(); setEventResizing(index, position); }, [setEventResizing, index, position]); var props = React.useMemo(function () { return { event: event, index: index, position: position, style: style, elProps: { style: style, onMouseDown: onMouseDownHandler } }; }, [event, index, position, style, onMouseDownHandler]); if (renderGrip) return React__default.createElement(React__default.Fragment, null, renderGrip(props)); return React__default.createElement("div", Object.assign({}, props.elProps)); }; var HwcEventsRenderer = function HwcEventsRenderer(_ref) { var renderCard = _ref.renderCard, renderShadowCard = _ref.renderShadowCard, renderGrip = _ref.renderGrip; var _useHwcContext = useHwcContext(), setEventMoving = _useHwcContext.setEventMoving; var cards = useCards(); var shadowCards = useShadowCards(); return React__default.createElement("div", { style: { position: 'relative' } }, cards.map(function (cardProps, index) { return React__default.createElement("div", { key: index, style: _extends({}, cardProps.style, { position: 'absolute', left: cardProps.rect.left + "px", top: cardProps.rect.top + "px" }), onMouseDown: function onMouseDown(ev) { ev.preventDefault(); setEventMoving(cardProps.index); } }, renderCard(cardProps), cardProps.isFirst && React__default.createElement(GripRenderer, { position: 'start', event: cardProps.event, index: cardProps.index, renderGrip: renderGrip }), cardProps.isLast && React__default.createElement(GripRenderer, { position: 'end', event: cardProps.event, index: cardProps.index, renderGrip: renderGrip })); }), shadowCards.map(function (shadowCardProps, index) { return React__default.createElement("div", { key: index, style: _extends({}, shadowCardProps.style, { position: 'absolute', left: shadowCardProps.rect.left + "px", top: shadowCardProps.rect.top + "px" }) }, renderShadowCard(shadowCardProps)); })); }; var HwcHeader = function HwcHeader(_ref) { var locale = _ref.locale, _ref$dateFormatOption = _ref.dateFormatOptions, dateFormatOptions = _ref$dateFormatOption === void 0 ? { day: '2-digit', weekday: 'short' } : _ref$dateFormatOption; var _useHwcContext = useHwcContext(), startDay = _useHwcContext.startDay, daysCount = _useHwcContext.daysCount; var days = new Array(daysCount).fill(0).map(function (_, index) { return new Date(startDay.getTime() + index * DAY_IN_MILLIS); }); return React__default.createElement("div", { style: { display: 'flex' } }, days.map(function (d, dIndex) { return React__default.createElement("div", { key: dIndex, style: { flex: '0 0 1', width: '100%' } }, d.toLocaleDateString(locale, dateFormatOptions)); })); }; exports.HwcContext = HwcContext; exports.HwcDragPane = HwcDragPane; exports.HwcEventsRenderer = HwcEventsRenderer; exports.HwcGrid = HwcGrid; exports.HwcHeader = HwcHeader; exports.HwcProvider = HwcProvider; exports.useHwc = useHwc; exports.useHwcContext = useHwcContext; //# sourceMappingURL=hwc.cjs.development.js.map