UNPKG

@carbon/react

Version:

React components for the Carbon Design System

365 lines (352 loc) 13.9 kB
/** * Copyright IBM Corp. 2016, 2023 * * 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'; Object.defineProperty(exports, '__esModule', { value: true }); var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var PropTypes = require('prop-types'); var React = require('react'); var cx = require('classnames'); var useResizeObserver = require('../../internal/useResizeObserver.js'); var iconsReact = require('@carbon/icons-react'); var Copy = require('../Copy/Copy.js'); var Button = require('../Button/Button.js'); require('../Button/Button.Skeleton.js'); var CopyButton = require('../CopyButton/CopyButton.js'); var useId = require('../../internal/useId.js'); var copy = require('copy-to-clipboard'); var deprecate = require('../../prop-types/deprecate.js'); var usePrefix = require('../../internal/usePrefix.js'); var deprecateValuesWithin = require('../../prop-types/deprecateValuesWithin.js'); var mapPopoverAlign = require('../../tools/mapPopoverAlign.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes); var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx); var copy__default = /*#__PURE__*/_interopDefaultLegacy(copy); const rowHeightInPixels = 16; const defaultMaxCollapsedNumberOfRows = 15; const defaultMaxExpandedNumberOfRows = 0; const defaultMinCollapsedNumberOfRows = 3; const defaultMinExpandedNumberOfRows = 16; function CodeSnippet({ align = 'bottom', autoAlign = false, className, type = 'single', children, disabled, feedback, feedbackTimeout, onClick, ['aria-label']: ariaLabel = 'Copy to clipboard', ariaLabel: deprecatedAriaLabel, copyText, copyButtonDescription, light, showMoreText = 'Show more', showLessText = 'Show less', hideCopyButton, wrapText = false, maxCollapsedNumberOfRows = defaultMaxCollapsedNumberOfRows, maxExpandedNumberOfRows = defaultMaxExpandedNumberOfRows, minCollapsedNumberOfRows = defaultMinCollapsedNumberOfRows, minExpandedNumberOfRows = defaultMinExpandedNumberOfRows, ...rest }) { const [expandedCode, setExpandedCode] = React.useState(false); const [shouldShowMoreLessBtn, setShouldShowMoreLessBtn] = React.useState(false); const { current: uid } = React.useRef(useId.useId()); const codeContentRef = React.useRef(null); const codeContainerRef = React.useRef(null); const innerCodeRef = React.useRef(null); const [hasLeftOverflow, setHasLeftOverflow] = React.useState(false); const [hasRightOverflow, setHasRightOverflow] = React.useState(false); const getCodeRef = React.useCallback(() => { if (type === 'single') { return codeContainerRef; } if (type === 'multi') { return codeContentRef; } else { return innerCodeRef; } }, [type]); const prefix = usePrefix.usePrefix(); const getCodeRefDimensions = React.useCallback(() => { const { clientWidth: codeClientWidth = 0, scrollLeft: codeScrollLeft = 0, scrollWidth: codeScrollWidth = 0 } = getCodeRef().current || {}; return { horizontalOverflow: codeScrollWidth > codeClientWidth, codeClientWidth, codeScrollWidth, codeScrollLeft }; }, [getCodeRef]); const handleScroll = React.useCallback(() => { if (type === 'inline' || type === 'single' && !codeContainerRef?.current || type === 'multi' && !codeContentRef?.current) { return; } const { horizontalOverflow, codeClientWidth, codeScrollWidth, codeScrollLeft } = getCodeRefDimensions(); setHasLeftOverflow(horizontalOverflow && !!codeScrollLeft); setHasRightOverflow(horizontalOverflow && codeScrollLeft + codeClientWidth !== codeScrollWidth); }, [type, getCodeRefDimensions]); useResizeObserver.useResizeObserver({ ref: getCodeRef(), onResize: () => { if (codeContentRef?.current && type === 'multi') { const { height } = codeContentRef.current.getBoundingClientRect(); if (maxCollapsedNumberOfRows > 0 && (maxExpandedNumberOfRows <= 0 || maxExpandedNumberOfRows > maxCollapsedNumberOfRows) && height > maxCollapsedNumberOfRows * rowHeightInPixels) { setShouldShowMoreLessBtn(true); } else { setShouldShowMoreLessBtn(false); } if (expandedCode && minExpandedNumberOfRows > 0 && height <= minExpandedNumberOfRows * rowHeightInPixels) { setExpandedCode(false); } } if (codeContentRef?.current && type === 'multi' || codeContainerRef?.current && type === 'single') { handleScroll(); } } }); React.useEffect(() => { handleScroll(); }, [handleScroll]); const handleCopyClick = evt => { if (copyText || innerCodeRef?.current) { copy__default["default"](copyText ?? innerCodeRef?.current?.innerText ?? ''); } if (onClick) { onClick(evt); } }; const codeSnippetClasses = cx__default["default"](className, `${prefix}--snippet`, { [`${prefix}--snippet--${type}`]: type, [`${prefix}--snippet--disabled`]: type !== 'inline' && disabled, [`${prefix}--snippet--expand`]: expandedCode, [`${prefix}--snippet--light`]: light, [`${prefix}--snippet--no-copy`]: hideCopyButton, [`${prefix}--snippet--wraptext`]: wrapText, [`${prefix}--snippet--has-right-overflow`]: type == 'multi' && hasRightOverflow }); const expandCodeBtnText = expandedCode ? showLessText : showMoreText; if (type === 'inline') { if (hideCopyButton) { return /*#__PURE__*/React__default["default"].createElement("span", { className: codeSnippetClasses }, /*#__PURE__*/React__default["default"].createElement("code", { id: uid, ref: innerCodeRef }, children)); } return /*#__PURE__*/React__default["default"].createElement(Copy["default"], _rollupPluginBabelHelpers["extends"]({}, rest, { align: align, autoAlign: autoAlign, onClick: handleCopyClick, "aria-label": deprecatedAriaLabel || ariaLabel, "aria-describedby": uid, className: codeSnippetClasses, feedback: feedback, feedbackTimeout: feedbackTimeout }), /*#__PURE__*/React__default["default"].createElement("code", { id: uid, ref: innerCodeRef }, children)); } const containerStyle = {}; if (type === 'multi') { const styles = {}; if (expandedCode) { if (maxExpandedNumberOfRows > 0) { styles.maxHeight = maxExpandedNumberOfRows * rowHeightInPixels; } if (minExpandedNumberOfRows > 0) { styles.minHeight = minExpandedNumberOfRows * rowHeightInPixels; } } else { if (maxCollapsedNumberOfRows > 0) { styles.maxHeight = maxCollapsedNumberOfRows * rowHeightInPixels; } if (minCollapsedNumberOfRows > 0) { styles.minHeight = minCollapsedNumberOfRows * rowHeightInPixels; } } if (Object.keys(styles).length) { containerStyle.style = styles; } } return /*#__PURE__*/React__default["default"].createElement("div", _rollupPluginBabelHelpers["extends"]({}, rest, { className: codeSnippetClasses }), /*#__PURE__*/React__default["default"].createElement("div", _rollupPluginBabelHelpers["extends"]({ ref: codeContainerRef, role: type === 'single' || type === 'multi' ? 'textbox' : undefined, tabIndex: (type === 'single' || type === 'multi') && !disabled ? 0 : undefined, className: `${prefix}--snippet-container`, "aria-label": deprecatedAriaLabel || ariaLabel || 'code-snippet', "aria-readonly": type === 'single' || type === 'multi' ? true : undefined, "aria-multiline": type === 'multi' ? true : undefined, onScroll: type === 'single' && handleScroll || undefined }, containerStyle), /*#__PURE__*/React__default["default"].createElement("pre", { ref: codeContentRef, onScroll: type === 'multi' && handleScroll || undefined }, /*#__PURE__*/React__default["default"].createElement("code", { ref: innerCodeRef }, children))), hasLeftOverflow && /*#__PURE__*/React__default["default"].createElement("div", { className: `${prefix}--snippet__overflow-indicator--left` }), hasRightOverflow && type !== 'multi' && /*#__PURE__*/React__default["default"].createElement("div", { className: `${prefix}--snippet__overflow-indicator--right` }), !hideCopyButton && /*#__PURE__*/React__default["default"].createElement(CopyButton["default"], { align: align, autoAlign: autoAlign, size: type === 'multi' ? 'sm' : 'md', disabled: disabled, onClick: handleCopyClick, feedback: feedback, feedbackTimeout: feedbackTimeout, iconDescription: copyButtonDescription }), shouldShowMoreLessBtn && /*#__PURE__*/React__default["default"].createElement(Button["default"], { kind: "ghost", size: "sm", className: `${prefix}--snippet-btn--expand`, disabled: disabled, onClick: () => setExpandedCode(!expandedCode) }, /*#__PURE__*/React__default["default"].createElement("span", { className: `${prefix}--snippet-btn--text` }, expandCodeBtnText), /*#__PURE__*/React__default["default"].createElement(iconsReact.ChevronDown, { className: `${prefix}--icon-chevron--down ${prefix}--snippet__icon`, name: "chevron--down", role: "img" }))); } CodeSnippet.propTypes = { /** * Specify how the trigger should align with the tooltip */ align: deprecateValuesWithin["default"](PropTypes__default["default"].oneOf(['top', 'top-left', // deprecated use top-start instead 'top-right', // deprecated use top-end instead 'bottom', 'bottom-left', // deprecated use bottom-start instead 'bottom-right', // deprecated use bottom-end instead 'left', 'left-bottom', // deprecated use left-end instead 'left-top', // deprecated use left-start instead 'right', 'right-bottom', // deprecated use right-end instead 'right-top', // deprecated use right-start instead // new values to match floating-ui 'top-start', 'top-end', 'bottom-start', 'bottom-end', 'left-end', 'left-start', 'right-end', 'right-start']), ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end'], mapPopoverAlign.mapPopoverAlign), /** * Specify a label to be read by screen readers on the containing textbox * node */ ['aria-label']: PropTypes__default["default"].string, /** * Deprecated, please use `aria-label` instead. * Specify a label to be read by screen readers on the containing textbox * node */ ariaLabel: deprecate["default"](PropTypes__default["default"].string, 'This prop syntax has been deprecated. Please use the new `aria-label`.'), /** * **Experimental**: Will attempt to automatically align the tooltip */ autoAlign: PropTypes__default["default"].bool, /** * Provide the content of your CodeSnippet as a node or string */ children: PropTypes__default["default"].node, /** * Specify an optional className to be applied to the container node */ className: PropTypes__default["default"].string, /** * Specify the description for the Copy Button */ copyButtonDescription: PropTypes__default["default"].string, /** * Optional text to copy. If not specified, the `children` node's `innerText` * will be used as the copy value. */ copyText: PropTypes__default["default"].string, /** * Specify whether or not the CodeSnippet should be disabled */ disabled: PropTypes__default["default"].bool, /** * Specify the string displayed when the snippet is copied */ feedback: PropTypes__default["default"].string, /** * Specify the time it takes for the feedback message to timeout */ feedbackTimeout: PropTypes__default["default"].number, /** * Specify whether or not a copy button should be used/rendered. */ hideCopyButton: PropTypes__default["default"].bool, /** * Specify whether you are using the light variant of the Code Snippet, * typically used for inline snippet to display an alternate color */ light: deprecate["default"](PropTypes__default["default"].bool, 'The `light` prop for `CodeSnippet` has ' + 'been deprecated in favor of the new `Layer` component. It will be removed in the next major release.'), /** * Specify the maximum number of rows to be shown when in collapsed view */ maxCollapsedNumberOfRows: PropTypes__default["default"].number, /** * Specify the maximum number of rows to be shown when in expanded view */ maxExpandedNumberOfRows: PropTypes__default["default"].number, /** * Specify the minimum number of rows to be shown when in collapsed view */ minCollapsedNumberOfRows: PropTypes__default["default"].number, /** * Specify the minimum number of rows to be shown when in expanded view */ minExpandedNumberOfRows: PropTypes__default["default"].number, /** * An optional handler to listen to the `onClick` even fired by the Copy * Button */ onClick: PropTypes__default["default"].func, /** * Specify a string that is displayed when the Code Snippet has been * interacted with to show more lines */ showLessText: PropTypes__default["default"].string, /** * Specify a string that is displayed when the Code Snippet text is more * than 15 lines */ showMoreText: PropTypes__default["default"].string, /** * Provide the type of Code Snippet */ type: PropTypes__default["default"].oneOf(['single', 'inline', 'multi']), /** * Specify whether or not to wrap the text. */ wrapText: PropTypes__default["default"].bool }; exports["default"] = CodeSnippet;