UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

240 lines (230 loc) 7.55 kB
/** * Copyright IBM Corp. 2020, 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. */ 'use strict'; var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var React = require('react'); var uuidv4 = require('../../global/js/utils/uuidv4.js'); var index = require('../../_virtual/index.js'); var cx = require('classnames'); var devtools = require('../../global/js/utils/devtools.js'); var settings = require('../../settings.js'); var CoachmarkDragbar = require('./CoachmarkDragbar.js'); var CoachmarkHeader = require('./CoachmarkHeader.js'); var constants = require('./utils/constants.js'); var context = require('./utils/context.js'); var enums = require('./utils/enums.js'); var useIsomorphicEffect = require('../../global/js/hooks/useIsomorphicEffect.js'); // The block part of our conventional BEM class names (blockClass__E--M). const blockClass = `${settings.pkg.prefix}--coachmark-overlay`; const componentName = 'CoachmarkOverlay'; // NOTE: the component SCSS is not imported here: it is rolled up separately. const defaults = { kind: enums.COACHMARK_OVERLAY_KIND.FLOATING, theme: 'light' }; /** * DO NOT USE. This component is for the exclusive use * of other Onboarding components. */ exports.CoachmarkOverlay = /*#__PURE__*/React.forwardRef((_ref, ref) => { let { children, onClose, fixedIsVisible, className, kind = defaults.kind, theme = defaults.theme, ...rest } = _ref; const { winHeight, winWidth } = useWindowDimensions(); const [a11yDragMode, setA11yDragMode] = React.useState(false); const overlayRef = React.useRef(null); const coachmark = context.useCoachmark(); const isBeacon = kind === enums.COACHMARK_OVERLAY_KIND.TOOLTIP; const isDraggable = kind === enums.COACHMARK_OVERLAY_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; } } }; const styledTune = React.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 = constants.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.default(); useIsomorphicEffect.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", _rollupPluginBabelHelpers.extends({}, rest, { className: cx(blockClass, `${blockClass}--${kind}`, `${blockClass}__${theme}`, (isBeacon || isDraggable) && `${blockClass}--${coachmark.align}`, fixedIsVisible && `${blockClass}--is-visible`, a11yDragMode && `${blockClass}--is-dragmode`, className), ref: overlayRef, "aria-labelledby": contentId, tabIndex: -1 }, devtools.getDevtoolsProps(componentName)), isDraggable ? /*#__PURE__*/React.createElement(CoachmarkDragbar.CoachmarkDragbar, { a11yKeyboardHandler: handleKeyPress, onBlur: () => setA11yDragMode(false), onDrag: handleDrag, theme: theme, onClose: onClose }) : /*#__PURE__*/React.createElement(CoachmarkHeader.CoachmarkHeader, { onClose: onClose }), /*#__PURE__*/React.createElement("div", { className: `${blockClass}__body`, ref: ref, id: contentId }, React.Children.map(children, child => { return /*#__PURE__*/React.cloneElement(child, { isVisible }); }))); }); function getWindowDimensions() { const { innerWidth: winWidth, innerHeight: winHeight } = window; return { winWidth, winHeight }; } const useWindowDimensions = () => { const [windowDimensions, setWindowDimensions] = React.useState(getWindowDimensions()); React.useEffect(() => { /* istanbul ignore next */ function handleResize() { setWindowDimensions(getWindowDimensions()); } window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return windowDimensions; }; // Return a placeholder if not released and not enabled by feature flag exports.CoachmarkOverlay = settings.pkg.checkComponentEnabled(exports.CoachmarkOverlay, componentName); // The display name of the component, used by React. Note that displayName // is used in preference to relying on function.name. exports.CoachmarkOverlay.displayName = componentName; // The types and DocGen commentary for the component props, // in alphabetical order (for consistency). // See https://www.npmjs.com/package/prop-types#usage. exports.CoachmarkOverlay.propTypes = { /** * The CoachmarkOverlayElements child components. * Validation is handled in the parent Coachmark component. */ children: index.default.node.isRequired, /** * Optional class name for this component. */ className: index.default.string, /** * The visibility of CoachmarkOverlay is * managed in the parent Coachmark component. */ fixedIsVisible: index.default.bool.isRequired, /** * What kind or style of Coachmark to render. */ kind: index.default.oneOf(Object.values(enums.COACHMARK_OVERLAY_KIND)), /** * Function to call when the Coachmark closes. */ onClose: index.default.func.isRequired, /** * Determines the theme of the component. */ theme: index.default.oneOf(['light', 'dark']) };