UNPKG

@robotical/sensors-dashboard

Version:

A dashboard tool for depicting Marty data by Robotical

292 lines (289 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = LandingPage; require("core-js/modules/web.dom-collections.iterator.js"); require("core-js/modules/esnext.iterator.constructor.js"); require("core-js/modules/esnext.iterator.for-each.js"); var _Header = _interopRequireDefault(require("../../components/Header")); var _MainContent = _interopRequireDefault(require("../../components/MainContent")); var _stylesModule = _interopRequireDefault(require("./styles.module.css")); var _react = require("react"); var _ModalState = _interopRequireDefault(require("../../state-observables/modal/ModalState")); var _Modal = _interopRequireDefault(require("../../components/Modal")); var _fa = require("react-icons/fa"); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const isWindowTarget = target => target === window; const getScrollTop = target => isWindowTarget(target) ? window.scrollY || document.documentElement.scrollTop || 0 : target.scrollTop; const getClientHeight = target => isWindowTarget(target) ? window.innerHeight : target.clientHeight; const getScrollHeight = target => isWindowTarget(target) ? Math.max(document.body.scrollHeight, document.documentElement.scrollHeight) : target.scrollHeight; const isScrollableElement = element => { const style = window.getComputedStyle(element); const hasScrollableOverflow = style.overflowY === "auto" || style.overflowY === "scroll" || style.overflowY === "overlay"; return hasScrollableOverflow && element.scrollHeight > element.clientHeight + 1; }; const canScrollInDirection = (target, deltaY) => { const maxScrollTop = getScrollHeight(target) - getClientHeight(target); const scrollTop = getScrollTop(target); if (deltaY > 0) { return scrollTop < maxScrollTop - 1; } if (deltaY < 0) { return scrollTop > 1; } return false; }; const normaliseWheelDeltaY = (event, target) => { if (event.deltaMode === 1) { return event.deltaY * 16; } if (event.deltaMode === 2) { return event.deltaY * getClientHeight(target); } return event.deltaY; }; const getEventElement = target => { if (target instanceof HTMLElement) { return target; } if (target instanceof Element) { return target.parentElement; } return null; }; const hasNestedScrollableTarget = (target, boundary, deltaY) => { let currentElement = getEventElement(target); while (currentElement && currentElement !== boundary) { if (isScrollableElement(currentElement) && canScrollInDirection(currentElement, deltaY)) { return true; } currentElement = currentElement.parentElement; } return false; }; function LandingPage(_ref) { var _modalData$modalTitle, _modalData$withLogo; let { isInModal } = _ref; const [modalData, setModalData] = (0, _react.useState)(null); const [shouldCloseModal, setShouldCloseModal] = (0, _react.useState)(false); const mainRef = (0, _react.useRef)(null); const isModalOpen = Boolean(modalData === null || modalData === void 0 ? void 0 : modalData.modalContent); // Create subscription to the modal state (0, _react.useEffect)(() => { const subscriptionHelper = { notify(eventTopic, eventData) { switch (eventTopic) { case "SetModal": if (eventData) { setModalData(eventData); } break; case "CloseModal": setShouldCloseModal(true); break; } } }; // Subscribe _ModalState.default.subscribe(subscriptionHelper, ["SetModal", "CloseModal"]); // Return unsubscribe function return () => { _ModalState.default.unsubscribe(subscriptionHelper); }; }, []); const resetModal = () => { setShouldCloseModal(false); setModalData(null); }; (0, _react.useEffect)(() => { const mainElement = mainRef.current; if (!mainElement) { return undefined; } if (isModalOpen) { mainElement.setAttribute("inert", ""); } else { mainElement.removeAttribute("inert"); } return () => { mainElement.removeAttribute("inert"); }; }, [isModalOpen]); return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [isModalOpen && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Modal.default, { title: (_modalData$modalTitle = modalData === null || modalData === void 0 ? void 0 : modalData.modalTitle) !== null && _modalData$modalTitle !== void 0 ? _modalData$modalTitle : "", modalResetter: resetModal, shouldCloseModal: shouldCloseModal, withLogo: (_modalData$withLogo = modalData === null || modalData === void 0 ? void 0 : modalData.withLogo) !== null && _modalData$withLogo !== void 0 ? _modalData$withLogo : true, children: modalData === null || modalData === void 0 ? void 0 : modalData.modalContent }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { id: "sensors-dashboard-modal-main-container" }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("main", { "aria-label": "Sensor Insights Hub", "aria-hidden": isModalOpen || undefined, className: "".concat(_stylesModule.default.mainContainer, " ").concat(isInModal ? _stylesModule.default.mainContainerInModal : ""), ref: mainRef, tabIndex: 0, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(ScrollCue, { mainRef: mainRef }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Header.default, { isInModal: isInModal }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_MainContent.default, { mainRef: mainRef })] })] }); } function ScrollCue(_ref2) { let { mainRef } = _ref2; const scrollTargetRef = (0, _react.useRef)(null); const [scrollCueState, setScrollCueState] = (0, _react.useState)({ canScrollDown: false, canScrollUp: false, hasMeasured: false, isScrollable: false, scrollProgress: 0 }); const getScrollTarget = (0, _react.useCallback)(() => { let currentElement = mainRef.current; while (currentElement) { if (isScrollableElement(currentElement)) { return currentElement; } currentElement = currentElement.parentElement; } return window; }, [mainRef]); const updateScrollCue = (0, _react.useCallback)(() => { const target = getScrollTarget(); const maxScrollTop = getScrollHeight(target) - getClientHeight(target); const scrollTop = getScrollTop(target); const scrollProgress = maxScrollTop > 1 ? scrollTop / maxScrollTop : 0; const nextState = { canScrollDown: scrollTop < maxScrollTop - 1, canScrollUp: scrollTop > 1, hasMeasured: true, isScrollable: maxScrollTop > 1, scrollProgress: Math.min(Math.max(scrollProgress, 0), 1) }; scrollTargetRef.current = target; setScrollCueState(currentState => currentState.canScrollDown === nextState.canScrollDown && currentState.canScrollUp === nextState.canScrollUp && currentState.hasMeasured === nextState.hasMeasured && currentState.isScrollable === nextState.isScrollable && Math.abs(currentState.scrollProgress - nextState.scrollProgress) < 0.01 ? currentState : nextState); }, [getScrollTarget]); (0, _react.useEffect)(() => { const watchedTargets = []; let currentElement = mainRef.current; let mutationAnimationFrame = null; let secondAnimationFrame = null; while (currentElement) { watchedTargets.push(currentElement); currentElement = currentElement.parentElement; } watchedTargets.push(window); const scheduleScrollCueUpdate = () => { if (mutationAnimationFrame !== null) { return; } mutationAnimationFrame = requestAnimationFrame(() => { mutationAnimationFrame = null; updateScrollCue(); }); }; const resizeObserver = new ResizeObserver(updateScrollCue); const mutationObserver = new MutationObserver(scheduleScrollCueUpdate); watchedTargets.forEach(target => { target.addEventListener("scroll", updateScrollCue, { passive: true }); if (!isWindowTarget(target)) { resizeObserver.observe(target); } }); if (mainRef.current) { mutationObserver.observe(mainRef.current, { attributeFilter: ["class", "style"], attributes: true, childList: true, subtree: true }); } const timeoutIds = [80, 220, 500, 1000].map(delay => window.setTimeout(updateScrollCue, delay)); const animationFrame = requestAnimationFrame(() => { updateScrollCue(); secondAnimationFrame = requestAnimationFrame(updateScrollCue); }); window.addEventListener("resize", updateScrollCue); return () => { cancelAnimationFrame(animationFrame); if (secondAnimationFrame !== null) { cancelAnimationFrame(secondAnimationFrame); } if (mutationAnimationFrame !== null) { cancelAnimationFrame(mutationAnimationFrame); } timeoutIds.forEach(timeoutId => window.clearTimeout(timeoutId)); window.removeEventListener("resize", updateScrollCue); mutationObserver.disconnect(); resizeObserver.disconnect(); watchedTargets.forEach(target => { target.removeEventListener("scroll", updateScrollCue); }); }; }, [mainRef, updateScrollCue]); (0, _react.useEffect)(() => { const mainElement = mainRef.current; if (!mainElement) { return undefined; } const handleWheel = event => { const target = scrollTargetRef.current || getScrollTarget(); const deltaY = normaliseWheelDeltaY(event, target); if (event.defaultPrevented || event.ctrlKey || Math.abs(deltaY) < 1 || Math.abs(deltaY) <= Math.abs(event.deltaX) || hasNestedScrollableTarget(event.target, mainElement, deltaY) || !canScrollInDirection(target, deltaY)) { return; } event.preventDefault(); target.scrollBy({ top: deltaY, behavior: "auto" }); requestAnimationFrame(updateScrollCue); }; mainElement.addEventListener("wheel", handleWheel, { capture: true, passive: false }); return () => { mainElement.removeEventListener("wheel", handleWheel, { capture: true }); }; }, [getScrollTarget, mainRef, updateScrollCue]); if (scrollCueState.hasMeasured && !scrollCueState.isScrollable) { return null; } return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { "aria-hidden": "true", className: _stylesModule.default.scrollCue, style: { "--scroll-cue-progress": scrollCueState.scrollProgress }, children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { className: _stylesModule.default.scrollCueControls, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_fa.FaChevronUp, { className: "".concat(_stylesModule.default.scrollCueIcon, " ").concat(!scrollCueState.canScrollUp ? _stylesModule.default.scrollCueIconMuted : "") }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { className: _stylesModule.default.scrollCueTrack, children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { className: _stylesModule.default.scrollCueThumb }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_fa.FaChevronDown, { className: "".concat(_stylesModule.default.scrollCueIcon, " ").concat(!scrollCueState.canScrollDown ? _stylesModule.default.scrollCueIconMuted : "") })] }) }); }