@robotical/sensors-dashboard
Version:
A dashboard tool for depicting Marty data by Robotical
292 lines (289 loc) • 11.4 kB
JavaScript
;
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 : "")
})]
})
});
}