UNPKG

@josmangarsal/pragmatic-scheduler

Version:
317 lines (316 loc) 19.1 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TimelineView = void 0; var jsx_runtime_1 = require("@emotion/react/jsx-runtime"); var react_1 = require("react"); var Scheduler_1 = require("../components/Scheduler"); var HeaderRow_1 = require("../components/HeaderRow"); var material_1 = require("@mui/material"); var ResourceCell_1 = require("../layout/ResourceCell"); var ResourceHeader_1 = require("../layout/ResourceHeader"); var GridCell_1 = require("../layout/GridCell"); var react_grid_layout_1 = __importDefault(require("react-grid-layout")); var EventTile_1 = require("../components/EventTile"); var UnassignedHeader_1 = require("../components/UnassignedHeader"); var useCalcEventPosition_1 = require("../hooks/useCalcEventPosition"); var useLayoutToCalEvent_1 = require("../hooks/useLayoutToCalEvent"); var useCalcGridPositions_1 = require("../hooks/useCalcGridPositions"); var date_fns_1 = require("date-fns"); var datePositionHelper_1 = require("../helpers/datePositionHelper"); require("overlayscrollbars/overlayscrollbars.css"); var overlayscrollbars_react_1 = require("overlayscrollbars-react"); var useCreatingEvent_1 = require("../hooks/useCreatingEvent"); var useScrollLoading_1 = require("../hooks/useScrollLoading"); var TimelineView = function () { var _a = (0, react_1.useContext)(Scheduler_1.SchedulerContext), resources = _a.resources, events = _a.events, days = _a.days, config = _a.config, activeDate = _a.activeDate, _b = _a.calendarBounds, totalDivisions = _b.totalDivisions, start = _b.start, end = _b.end, onEventChange = _a.onEventChange, setResizingEvent = _a.setResizingEvent, extendWithScroll = _a.extendWithScroll; var ref = (0, react_1.useRef)(null); var scrollRefResources = (0, react_1.useRef)(null); var scrollRefEvents = (0, react_1.useRef)(null); var calcEventPosition = (0, useCalcEventPosition_1.useCalcEventPosition)(); var layoutToCalEvent = (0, useLayoutToCalEvent_1.useLayoutToCalEvent)(); var gridLayouts = (0, useCalcGridPositions_1.useCalcGridPositions)(); var _c = __read((0, react_1.useState)(false), 2), isDraggingOver = _c[0], setIsDraggingOver = _c[1]; var _d = __read((0, react_1.useState)(false), 2), loaded = _d[0], setLoaded = _d[1]; var _e = __read((0, react_1.useState)(), 2), dragCalEvent = _e[0], setDragCalEvent = _e[1]; var _f = __read((0, react_1.useState)({ i: 'droppedItem', w: 4, h: 1, x: 0, y: 0 }), 2), droppingItem = _f[0], setDroppingItem = _f[1]; var _g = __read((0, react_1.useState)(gridLayouts), 2), layouts = _g[0], setLayouts = _g[1]; var updateLayout = (0, react_1.useCallback)(function (layout) { var gridIds = gridLayouts.map(function (l) { return l.i; }); var eventLayouts = layout.filter(function (l) { return !gridIds.includes(l.i); }); setLayouts(__spreadArray(__spreadArray([], __read(eventLayouts), false), __read(gridLayouts), false)); }, [gridLayouts]); var prevActiveDate = (0, react_1.useRef)(); var prevStartDate = (0, react_1.useRef)(); var prevEndDate = (0, react_1.useRef)(); (0, react_1.useEffect)(function () { var _a, _b, _c, _d, _e, _f, _g; if (extendWithScroll) return; // Scroll to start on expand end date if (prevStartDate.current && ((_a = prevStartDate.current) === null || _a === void 0 ? void 0 : _a.getTime()) !== start.getTime()) { prevStartDate.current = start; (_b = ref.current) === null || _b === void 0 ? void 0 : _b.scrollTo({ left: 0, behavior: 'smooth', }); return; } // Scroll to end on expand end date if (prevEndDate.current && ((_c = prevEndDate.current) === null || _c === void 0 ? void 0 : _c.getTime()) !== end.getTime()) { prevEndDate.current = end; (_d = ref.current) === null || _d === void 0 ? void 0 : _d.scrollTo({ left: ref.current.scrollWidth, behavior: 'smooth', }); return; } setTimeout(function () { prevStartDate.current = start; prevEndDate.current = end; }, 100); // Don't scroll if activeDate has not changed if (((_e = prevActiveDate.current) === null || _e === void 0 ? void 0 : _e.getTime()) === activeDate.getTime()) return; prevActiveDate.current = activeDate; if ((0, date_fns_1.isBefore)(start, activeDate) && (0, date_fns_1.isBefore)(activeDate, end)) { // scroll to activeDate, that must be inside the current date interval (_f = ref.current) === null || _f === void 0 ? void 0 : _f.scrollTo({ left: (0, datePositionHelper_1.dateToPosition)(activeDate, start, end, ref.current.scrollWidth), behavior: 'smooth', }); } else { // scroll to the current date (there is 1 day in the past) (_g = ref.current) === null || _g === void 0 ? void 0 : _g.scrollTo({ left: ref.current.scrollWidth * (1 / (config.daysToDisplay + 1)), behavior: 'smooth', }); } }, [activeDate, config.daysToDisplay, end, extendWithScroll, start]); // Lock vertical scroll (0, react_1.useEffect)(function () { var preventVerticalScroll = function (e) { if (e.deltaY !== 0) { e.preventDefault(); } }; window.addEventListener('wheel', preventVerticalScroll, { passive: false }); return function () { window.removeEventListener('wheel', preventVerticalScroll); }; }, [loaded]); // Use wheel to scroll horizontally in timeline (0, react_1.useEffect)(function () { var el = ref.current; if (el) { var onWheel_1 = function (e) { if (e.deltaY === 0) return; if (!(el.scrollLeft === 0 && e.deltaY < 0) && !(el.scrollWidth - el.clientWidth - Math.round(el.scrollLeft) === 0 && e.deltaY > 0)) { e.preventDefault(); } el.scrollLeft += e.deltaY * 0.25; }; el.addEventListener('wheel', onWheel_1); return function () { return el.removeEventListener('wheel', onWheel_1); }; } }, []); // Observe scroll and update vertical scrollbar position (0, react_1.useEffect)(function () { var el = ref.current; if (!el) return; var handleScroll = function () { var _a, _b, _c, _d; var scrollRight = el.scrollWidth - el.clientWidth - el.scrollLeft; var verticalScrollbar = (_d = (_c = (_b = (_a = scrollRefEvents.current) === null || _a === void 0 ? void 0 : _a.osInstance()) === null || _b === void 0 ? void 0 : _b.elements()) === null || _c === void 0 ? void 0 : _c.scrollbarVertical) === null || _d === void 0 ? void 0 : _d.scrollbar; if (verticalScrollbar) { verticalScrollbar.style.transform = "translateX(-".concat(scrollRight, "px)"); } }; el.addEventListener('scroll', handleScroll); var resizeObserver = new ResizeObserver(handleScroll); resizeObserver.observe(el); return function () { el.removeEventListener('scroll', handleScroll); resizeObserver.disconnect(); }; }, []); // Prevent scrollbar wheel event (0, react_1.useEffect)(function () { var _a, _b, _c; var scrollbarEventElement = (_c = (_b = (_a = scrollRefEvents.current) === null || _a === void 0 ? void 0 : _a.osInstance()) === null || _b === void 0 ? void 0 : _b.elements()) === null || _c === void 0 ? void 0 : _c.scrollEventElement; var onWheel = function () { if (scrollbarEventElement instanceof HTMLElement) { scrollbarEventElement.scrollTop = scrollbarEventElement.scrollTop + 0; } }; if (scrollbarEventElement) { scrollbarEventElement.addEventListener('wheel', onWheel, { passive: false }); } return function () { if (scrollbarEventElement) { scrollbarEventElement.removeEventListener('wheel', onWheel); } }; }, [loaded, scrollRefEvents]); (0, react_1.useEffect)(function () { var _a, _b; if (!loaded) { setTimeout(function () { setLoaded(true); }, 1000); return; } var eventsInstance = (_a = scrollRefEvents.current) === null || _a === void 0 ? void 0 : _a.osInstance(); var resourcesInstance = (_b = scrollRefResources.current) === null || _b === void 0 ? void 0 : _b.osInstance(); if (!eventsInstance || !resourcesInstance) return; // Handler to sync scroll var handleScroll = function () { var scrollTop = eventsInstance.elements().viewport.scrollTop; resourcesInstance.elements().viewport.scrollTop = scrollTop; }; eventsInstance.on('scroll', handleScroll); return function () { eventsInstance.off('scroll', handleScroll); }; }, [loaded]); var cols = (0, react_1.useMemo)(function () { return totalDivisions * config.divisionParts; }, [config.divisionParts, totalDivisions]); var handleDrop = (0, react_1.useCallback)(function (_layout, layoutItem) { if (dragCalEvent && layoutItem) { var updatedEvent = layoutToCalEvent(layoutItem); onEventChange === null || onEventChange === void 0 ? void 0 : onEventChange(updatedEvent); setIsDraggingOver(false); } }, [dragCalEvent, layoutToCalEvent, onEventChange]); var handleDropDragOver = (0, react_1.useCallback)(function () { setIsDraggingOver(true); return undefined; }, [setIsDraggingOver]); var handleUnassignedDragStart = (0, react_1.useCallback)(function (event) { setDragCalEvent(event); var layout = calcEventPosition(event); setDroppingItem(layout); }, [calcEventPosition]); var handleDragResizeStop = (0, react_1.useCallback)(function (layout, _oldItem, newItem) { if (newItem.i === 'creatingEvent') { return; } var updatedEvent = layoutToCalEvent(newItem); onEventChange === null || onEventChange === void 0 ? void 0 : onEventChange(updatedEvent); // be sure to unset this so that the handleLayoutChange updates the layout setIsDraggingOver(false); setTimeout(function () { // note this is required as the handleLayoutChange is not always triggered // see: https://github.com/react-grid-layout/react-grid-layout/issues/1606 // the setTimeout is a hack as sometime it ges into a loop depending on how quickly the event is dropped updateLayout(layout); }); setResizingEvent(null); }, [layoutToCalEvent, onEventChange, setResizingEvent, updateLayout]); var handleLayoutChange = (0, react_1.useCallback)(function (layout) { // note we have to check if we are dragging over as the handleLayoutChange can remove the droppingItem // which causes the handleDrop be called with an undefined layoutItem, and the "pink box" is not shown if (!isDraggingOver) { updateLayout(layout); } }, [isDraggingOver, updateLayout]); var handleOnResize = (0, react_1.useCallback)(function (layout, oldItem, newItem) { if (!newItem) return; if (newItem.i === 'droppedItem' || newItem.i === 'creatingEvent') { return; } var resizedEvent = layoutToCalEvent(newItem); setResizingEvent(resizedEvent); }, [layoutToCalEvent, setResizingEvent]); var maxHeight = (0, react_1.useMemo)(function () { return (config.rowsToDisplay ? config.rowsToDisplay * config.rowHeight : '100%'); }, [config.rowsToDisplay, config.rowHeight]); var gridWidth = (0, react_1.useMemo)(function () { return (cols * config.divisionWidth) / config.divisionParts; }, [cols, config.divisionParts, config.divisionWidth]); // Observe events container to recalculate grid width to fit screen (0, react_1.useEffect)(function () { var el = ref.current; if (!el) return; var recalcGridWidth = function () { if (gridWidth < el.clientWidth) { var calculatedDivisionWidth = el.clientWidth / totalDivisions; // Custom event to link with useSchedulerViewConfig window.dispatchEvent(new CustomEvent('timelineview:resize:calculatedDivisionWidth', { detail: { calculatedDivisionWidth: calculatedDivisionWidth } })); } }; var resizeObserver = new ResizeObserver(recalcGridWidth); resizeObserver.observe(el); recalcGridWidth(); return function () { resizeObserver.disconnect(); }; }, [cols, config.divisionParts, gridWidth, totalDivisions]); var renderCreatingEvent = (0, useCreatingEvent_1.useCreatingEvent)({ ref: ref, gridWidth: gridWidth, cols: cols, rows: config.rowsToDisplay ? config.rowsToDisplay : resources.length, }).renderCreatingEvent; var ScrollReboundLoader = (0, useScrollLoading_1.useScrollLoading)({ scrollRef: ref }).ScrollReboundLoader; return ((0, jsx_runtime_1.jsxs)(material_1.Box, { children: [config.unAssignedRows ? (0, jsx_runtime_1.jsx)(UnassignedHeader_1.UnassignedHeader, {}) : null, (0, jsx_runtime_1.jsxs)(material_1.Box, __assign({ display: "flex" }, { children: [(0, jsx_runtime_1.jsxs)(material_1.Box, __assign({ marginTop: config.unAssignedRows ? "".concat(config.rowHeight * 2, "px") : 0, maxWidth: config.resourceColumnWidth, minWidth: config.resourceColumnWidth }, { children: [(0, jsx_runtime_1.jsx)(ResourceHeader_1.ResourceHeader, {}), (0, jsx_runtime_1.jsx)(overlayscrollbars_react_1.OverlayScrollbarsComponent, __assign({ ref: scrollRefResources, options: { overflow: { x: 'hidden', y: 'hidden' } }, style: { maxHeight: maxHeight } }, { children: resources.map(function (resource) { return ((0, jsx_runtime_1.jsx)(ResourceCell_1.ResourceCell, { resource: resource }, resource.id)); }) }))] })), (0, jsx_runtime_1.jsxs)(material_1.Box, __assign({ position: "relative", flex: 1, overflow: "auto", ref: ref }, { children: [config.unAssignedRows ? (0, jsx_runtime_1.jsx)(HeaderRow_1.UnAssignedEvents, { onDragStart: handleUnassignedDragStart }) : null, (0, jsx_runtime_1.jsx)(HeaderRow_1.HeaderRow, { days: days, eventsBoxElement: ref.current }), (0, jsx_runtime_1.jsx)(overlayscrollbars_react_1.OverlayScrollbarsComponent, __assign({ ref: scrollRefEvents, options: { overflow: { x: 'hidden', y: 'scroll' } }, style: { maxHeight: maxHeight, width: gridWidth } }, { children: (0, jsx_runtime_1.jsxs)(react_grid_layout_1.default, __assign({ className: "layout", margin: [0, 0], compactType: null, allowOverlap: true, cols: cols, rowHeight: config.rowHeight, width: gridWidth, isBounded: true, isDroppable: true, onDrop: handleDrop, onDropDragOver: handleDropDragOver, droppingItem: droppingItem, onDragStop: handleDragResizeStop, onResizeStop: handleDragResizeStop, layout: layouts, onLayoutChange: handleLayoutChange, draggableCancel: ".not-draggable", onResize: handleOnResize, onDrag: handleOnResize }, { children: [gridLayouts.map(function (layout) { return ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(material_1.Box, __assign({ width: config.divisionWidth, height: config.rowHeight * layout.h }, { children: (0, jsx_runtime_1.jsx)(GridCell_1.GridCell, { layout: layout, ScrollReboundLoader: ScrollReboundLoader }) })) }, layout.i)); }), events .filter(function (e) { return e.resourceId; }) .sort(function (a, b) { return (a.allowOverlap ? 0 : 1) - (b.allowOverlap ? 0 : 1); }) .map(function (event) { var dataGridProps = calcEventPosition(event); return ((0, jsx_runtime_1.jsx)("div", __assign({ "data-grid": dataGridProps }, { children: (0, jsx_runtime_1.jsx)(EventTile_1.EventTile, { event: event, infoFlowData: { scrollRef: ref.current, dataGridProps: dataGridProps, config: config, }, overflowData: { leftOverflow: dataGridProps.leftOverflow, middleOverflow: dataGridProps.middleOverflow, rightOverflow: dataGridProps.rightOverflow, } }, event.id) }), event.id)); }), renderCreatingEvent] })) }))] }))] }))] })); }; exports.TimelineView = TimelineView;