@carbon/ibm-products
Version:
Carbon for IBM Products
159 lines (157 loc) • 6.12 kB
JavaScript
/**
* 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 { usePrefersReducedMotion } from "../../global/js/hooks/usePrefersReducedMotion.js";
import { getDevtoolsProps } from "../../global/js/utils/devtools.js";
import { useWebTerminal } from "./hooks/index.js";
import React, { useEffect, useMemo, useRef, useState } from "react";
import PropTypes from "prop-types";
import { Button, OverflowMenu, OverflowMenuItem, Theme } from "@carbon/react";
import { Close, Help } from "@carbon/react/icons";
//#region src/components/WebTerminal/WebTerminal.tsx
/**
* Copyright IBM Corp. 2020, 2022
*
* 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 componentName = "WebTerminal";
const blockClass = `${pkg.prefix}--web-terminal`;
const defaults = {
actions: Object.freeze([]),
documentationLinks: Object.freeze([]),
documentationLinksIconDescription: "Show documentation links",
isInitiallyOpen: false,
webTerminalAriaLabel: "Web terminal header"
};
/**
* The `WebTerminal` is prompted by the user and is persistent until dismissed. The purpose of a web terminal is to provide users with the ability to type commands manually instead of using the GUI.
*/
const WebTerminal = React.forwardRef(({ actions = defaults.actions, children, className, closeIconDescription, documentationLinks = defaults.documentationLinks, documentationLinksIconDescription = defaults.documentationLinksIconDescription, isInitiallyOpen = defaults.isInitiallyOpen, webTerminalAriaLabel = defaults.webTerminalAriaLabel, ...rest }, ref) => {
const localRef = useRef(null);
const webTerminalRef = ref ?? localRef;
const { open, closeWebTerminal, openWebTerminal } = useWebTerminal();
const [shouldRender, setRender] = useState(open);
const shouldReduceMotion = usePrefersReducedMotion();
const showDocumentationLinks = useMemo(() => documentationLinks.length > 0, [documentationLinks]);
useEffect(() => {
if (open) setRender(true);
}, [open]);
/**
On render, check if user want's the web terminal to be open by default
*/
useEffect(() => {
if (isInitiallyOpen) openWebTerminal?.();
}, []);
/**
When the web terminal slide in animation is complete, sets render to false.
*/
const onAnimationEnd = () => {
if (!open) setRender(false);
};
const handleCloseTerminal = () => {
/**
If the user prefers reduced motion, we have to manually set render to false
because onAnimationEnd will never be called.
*/
if (shouldReduceMotion) setRender(false);
closeWebTerminal?.();
};
return shouldRender ? /* @__PURE__ */ React.createElement("div", {
...rest,
...getDevtoolsProps(componentName),
ref: webTerminalRef,
className: (0, import_classnames.default)([
blockClass,
className,
{
[`${blockClass}--open`]: open,
[`${blockClass}--closed`]: !open
}
]),
onAnimationEnd
}, /* @__PURE__ */ React.createElement("header", {
"aria-label": webTerminalAriaLabel,
className: `${blockClass}__bar`
}, /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__actions` }, showDocumentationLinks && /* @__PURE__ */ React.createElement(OverflowMenu, {
renderIcon: (props) => /* @__PURE__ */ React.createElement(Help, {
size: 16,
...props
}),
iconDescription: documentationLinksIconDescription,
"aria-label": documentationLinksIconDescription,
menuOptionsClass: `${blockClass}__documentation-overflow`,
size: "lg"
}, documentationLinks.map(({ ...rest }, i) => /* @__PURE__ */ React.createElement(OverflowMenuItem, {
key: i,
...rest
}))), actions.map(({ renderIcon, onClick, iconDescription }) => /* @__PURE__ */ React.createElement(Button, {
key: iconDescription,
hasIconOnly: true,
renderIcon,
onClick,
iconDescription,
kind: "ghost",
"aria-label": iconDescription
}))), /* @__PURE__ */ React.createElement(Button, {
hasIconOnly: true,
renderIcon: (props) => /* @__PURE__ */ React.createElement(Close, {
size: 16,
...props
}),
kind: "ghost",
iconDescription: closeIconDescription,
onClick: handleCloseTerminal,
onAnimationEnd: (event) => event.stopPropagation()
})), /* @__PURE__ */ React.createElement(Theme, { theme: "g100" }, /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__body` }, children))) : null;
});
WebTerminal.displayName = componentName;
WebTerminal.propTypes = {
/**
* An array of actions to be displayed in the web terminal header bar
*/
/**@ts-ignore */
actions: PropTypes.arrayOf(PropTypes.shape({
renderIcon: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired,
iconDescription: PropTypes.string.isRequired
})),
/**
* Provide your own terminal component as children to show up in the web terminal
*/
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
/**
* Custom classname for additional styling of the web terminal
*/
className: PropTypes.string,
/**
* Icon description for the close button
*/
closeIconDescription: PropTypes.string.isRequired,
/**
* Array of objects for each documentation link. Each documentation link uses the prop types of OverflowMenuItems. See more: https://react.carbondesignsystem.com/?path=/docs/components-overflowmenu--default
*/
/**@ts-ignore */
documentationLinks: PropTypes.arrayOf(PropTypes.shape({ ...OverflowMenuItem.propTypes })),
/**
* Description for the documentation link overflow menu tooltip
*/
documentationLinksIconDescription: PropTypes.string,
/**
* Optionally pass if the web terminal should be open by default
*/
isInitiallyOpen: PropTypes.bool,
/**
* Specifies aria label for Web terminal
*/
webTerminalAriaLabel: PropTypes.string
};
//#endregion
export { WebTerminal };