@carbon/react
Version:
React components for the Carbon Design System
365 lines (352 loc) • 13.9 kB
JavaScript
/**
* 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;