UNPKG

@carbon/react

Version:

React components for the Carbon Design System

225 lines (223 loc) 8.04 kB
/** * Copyright IBM Corp. 2016, 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 { usePrefix } from "../../internal/usePrefix.js"; import { useId } from "../../internal/useId.js"; import { deprecate } from "../../prop-types/deprecate.js"; import { deprecateValuesWithin } from "../../prop-types/deprecateValuesWithin.js"; import { mapPopoverAlign } from "../../tools/mapPopoverAlign.js"; import Button_default from "../Button/index.js"; import { useResizeObserver } from "../../internal/useResizeObserver.js"; import Copy_default from "../Copy/index.js"; import CopyButton_default from "../CopyButton/index.js"; import classNames from "classnames"; import { useCallback, useRef, useState } from "react"; import PropTypes from "prop-types"; import { jsx, jsxs } from "react/jsx-runtime"; import { ChevronDown } from "@carbon/icons-react"; import copy from "copy-to-clipboard"; //#region src/components/CodeSnippet/CodeSnippet.tsx /** * Copyright IBM Corp. 2016, 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. */ 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] = useState(false); const [shouldShowMoreLessBtn, setShouldShowMoreLessBtn] = useState(false); const { current: uid } = useRef(useId()); const codeContentRef = useRef(null); const codeContainerRef = useRef(null); const innerCodeRef = useRef(null); const getCodeRef = useCallback(() => { if (type === "single") return codeContainerRef; if (type === "multi") return codeContentRef; else return innerCodeRef; }, [type]); const prefix = usePrefix(); useResizeObserver({ ref: getCodeRef(), onResize: () => { if (innerCodeRef?.current && type === "multi") { const { height } = innerCodeRef.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); } } }); const handleCopyClick = (evt) => { if (copyText || innerCodeRef?.current) copy(copyText ?? innerCodeRef?.current?.innerText ?? ""); if (onClick) onClick(evt); }; const codeSnippetClasses = classNames(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 }); const expandCodeBtnText = expandedCode ? showLessText : showMoreText; if (type === "inline") { if (hideCopyButton) return /* @__PURE__ */ jsx("span", { className: codeSnippetClasses, children: /* @__PURE__ */ jsx("code", { id: uid, ref: innerCodeRef, children }) }); return /* @__PURE__ */ jsx(Copy_default, { ...rest, align, autoAlign, onClick: handleCopyClick, "aria-label": deprecatedAriaLabel || ariaLabel, "aria-describedby": uid, className: codeSnippetClasses, disabled, feedback, feedbackTimeout, children: /* @__PURE__ */ jsx("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__ */ jsxs("div", { ...rest, className: codeSnippetClasses, children: [ /* @__PURE__ */ jsx("div", { ref: codeContainerRef, role: type === "single" || type === "multi" ? "textbox" : void 0, tabIndex: (type === "single" || type === "multi") && !disabled ? 0 : void 0, className: `${prefix}--snippet-container`, "aria-label": deprecatedAriaLabel || ariaLabel || "code-snippet", "aria-readonly": type === "single" || type === "multi" ? true : void 0, "aria-multiline": type === "multi" ? true : void 0, ...containerStyle, children: /* @__PURE__ */ jsx("pre", { ref: codeContentRef, ...containerStyle, children: /* @__PURE__ */ jsx("code", { ref: innerCodeRef, children }) }) }), !hideCopyButton && /* @__PURE__ */ jsx(CopyButton_default, { align, autoAlign, size: type === "multi" ? "sm" : "md", disabled, onClick: handleCopyClick, feedback, feedbackTimeout, iconDescription: copyButtonDescription }), shouldShowMoreLessBtn && /* @__PURE__ */ jsxs(Button_default, { kind: "ghost", size: "sm", className: `${prefix}--snippet-btn--expand`, disabled, onClick: () => setExpandedCode(!expandedCode), children: [/* @__PURE__ */ jsx("span", { className: `${prefix}--snippet-btn--text`, children: expandCodeBtnText }), /* @__PURE__ */ jsx(ChevronDown, { className: `${prefix}--icon-chevron--down ${prefix}--snippet__icon`, name: "chevron--down", role: "img" })] }) ] }); } CodeSnippet.propTypes = { align: deprecateValuesWithin(PropTypes.oneOf([ "top", "top-left", "top-right", "bottom", "bottom-left", "bottom-right", "left", "left-bottom", "left-top", "right", "right-bottom", "right-top", "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), ["aria-label"]: PropTypes.string, ariaLabel: deprecate(PropTypes.string, "This prop syntax has been deprecated. Please use the new `aria-label`."), autoAlign: PropTypes.bool, children: PropTypes.node, className: PropTypes.string, copyButtonDescription: PropTypes.string, copyText: PropTypes.string, disabled: PropTypes.bool, feedback: PropTypes.string, feedbackTimeout: PropTypes.number, hideCopyButton: PropTypes.bool, light: deprecate(PropTypes.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."), maxCollapsedNumberOfRows: PropTypes.number, maxExpandedNumberOfRows: PropTypes.number, minCollapsedNumberOfRows: PropTypes.number, minExpandedNumberOfRows: PropTypes.number, onClick: PropTypes.func, showLessText: PropTypes.string, showMoreText: PropTypes.string, type: PropTypes.oneOf([ "single", "inline", "multi" ]), wrapText: PropTypes.bool }; //#endregion export { CodeSnippet as default };