UNPKG

@carbon/ibm-products

Version:
210 lines (208 loc) 6.74 kB
/** * Copyright IBM Corp. 2020, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { __toESM } from "../../_virtual/_rolldown/runtime.js"; import { require_classnames } from "../../node_modules/classnames/index.js"; import { pkg } from "../../settings.js"; import { useIsomorphicEffect } from "../../global/js/hooks/useIsomorphicEffect.js"; import { getDevtoolsProps } from "../../global/js/utils/devtools.js"; import uuidv4 from "../../global/js/utils/uuidv4.js"; import { COACHMARK_OVERLAY_KIND } from "./utils/enums.js"; import { useCoachmark } from "./utils/context.js"; import { CoachmarkDragbar } from "./CoachmarkDragbar.js"; import { CoachmarkHeader } from "./CoachmarkHeader.js"; import { getOffsetTune } from "./utils/constants.js"; import React, { forwardRef, useEffect, useMemo, useRef, useState } from "react"; import PropTypes from "prop-types"; //#region src/components/Coachmark/CoachmarkOverlay.tsx /** * Copyright IBM Corp. 2023, 2025 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ var import_classnames = /* @__PURE__ */ __toESM(require_classnames()); const blockClass = `${pkg.prefix}--coachmark-overlay`; const componentName = "CoachmarkOverlay"; const defaults = { kind: "floating", theme: "light" }; /** * DO NOT USE. This component is for the exclusive use * of other Onboarding components. * @deprecated This component is deprecated. */ const CoachmarkOverlay = forwardRef((props, ref) => { const { children, onClose, fixedIsVisible, className, kind = defaults.kind, theme = defaults.theme, ...rest } = props; const { winHeight, winWidth } = useWindowDimensions(); const [a11yDragMode, setA11yDragMode] = useState(false); const overlayRef = useRef(null); const coachmark = useCoachmark(); const isBeacon = kind === "tooltip"; const isDraggable = kind === "floating"; const isVisible = className?.includes("is-visible"); const handleKeyPress = (event) => { const { shiftKey, key } = event; /* istanbul ignore next */ if (key === "Enter" || key === " ") setA11yDragMode((prevVal) => !prevVal); else if (a11yDragMode) { const distanceToMove = shiftKey ? 128 : 32; switch (key) { case "ArrowLeft": handleDrag(distanceToMove * -1, 0); break; case "ArrowRight": handleDrag(distanceToMove, 0); break; case "ArrowUp": handleDrag(0, distanceToMove * -1); break; case "ArrowDown": handleDrag(0, distanceToMove); break; default: break; } } }; const styledTune = useMemo(() => { const style = {}; if (isBeacon || isDraggable) { if (coachmark?.targetRect) { style.left = coachmark.targetRect.x + window.scrollX; style.top = coachmark.targetRect.y + window.scrollY; } if (style.left && style.top) { if (isBeacon) { style.left = style.left + 16; style.top = style.top + 16; } if (isDraggable) { const offsetTune = getOffsetTune(coachmark, kind); style.left = style.left + offsetTune.left; style.top = style.top + offsetTune.top; } } } return style; }, [ isBeacon, isDraggable, coachmark, kind ]); /* istanbul ignore next */ function handleDragBounds(x, y) { let xRes = x; let yRes = y; const xMax = winWidth - 288; const yMax = winHeight - 150; if (xRes < 0) xRes = 0; else if (xRes > xMax) xRes = xMax; if (yRes < 0) yRes = 0; else if (yRes > yMax) yRes = yMax; return { targetX: xRes, targetY: yRes }; } function handleDrag(movementX, movementY) { const overlay = overlayRef.current; if (!overlay) return; const { x, y } = overlay.getBoundingClientRect(); const { targetX, targetY } = handleDragBounds(x + movementX, y + movementY); overlay.style.transform = "none"; overlay.style.position = "fixed"; overlay.style.left = `${targetX}px`; overlay.style.top = `${targetY}px`; overlay.style.bottom = "auto"; } const contentId = uuidv4(); useIsomorphicEffect(() => { if (overlayRef.current) { const currentStyle = overlayRef.current?.style; Object.keys(styledTune).forEach((key) => { const value = styledTune[key]; currentStyle.setProperty(key, `${value}px`); }); } }, [styledTune, overlayRef]); return /* @__PURE__ */ React.createElement("div", { ...rest, className: (0, import_classnames.default)(blockClass, `${blockClass}--${kind}`, `${blockClass}__${theme}`, (isBeacon || isDraggable) && coachmark?.align && `${blockClass}--${coachmark.align}`, fixedIsVisible && `${blockClass}--is-visible`, a11yDragMode && `${blockClass}--is-dragmode`, className), ref: overlayRef, "aria-labelledby": contentId, tabIndex: -1, ...getDevtoolsProps(componentName) }, isDraggable ? /* @__PURE__ */ React.createElement(CoachmarkDragbar, { a11yKeyboardHandler: handleKeyPress, onBlur: () => setA11yDragMode(false), onDrag: handleDrag, theme, onClose }) : /* @__PURE__ */ React.createElement(CoachmarkHeader, { onClose }), /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__body`, ref, id: contentId }, React.Children.map(children, (child) => { return React.cloneElement(child, { isVisible }); }))); }); function getWindowDimensions() { const { innerWidth: winWidth, innerHeight: winHeight } = window; return { winWidth, winHeight }; } const useWindowDimensions = () => { const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions()); useEffect(() => { /* istanbul ignore next */ function handleResize() { setWindowDimensions(getWindowDimensions()); } window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); return windowDimensions; }; /**@ts-ignore*/ CoachmarkOverlay.deprecated = { level: "warn", details: `${componentName} is deprecated.` }; CoachmarkOverlay.displayName = componentName; CoachmarkOverlay.propTypes = { /** * The CoachmarkOverlayElements child components. * Validation is handled in the parent Coachmark component. */ children: PropTypes.node.isRequired, /** * Optional class name for this component. */ className: PropTypes.string, /** * The visibility of CoachmarkOverlay is * managed in the parent Coachmark component. */ fixedIsVisible: PropTypes.bool.isRequired, /** * What kind or style of Coachmark to render. */ kind: PropTypes.oneOf(Object.values(COACHMARK_OVERLAY_KIND)), /** * Function to call when the Coachmark closes. */ onClose: PropTypes.func.isRequired, /** * Determines the theme of the component. */ theme: PropTypes.oneOf(["light", "dark"]) }; //#endregion export { CoachmarkOverlay };