UNPKG

@carbon/ibm-products

Version:
693 lines (691 loc) 26.3 kB
/** * 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 pconsole_default from "../../global/js/utils/pconsole.js"; import { pkg } from "../../settings.js"; import { usePreviousValue } from "../../global/js/hooks/usePreviousValue.js"; import { getSpecificElement, useFocus } from "../../global/js/hooks/useFocus.js"; import { usePresence } from "../../global/js/hooks/usePresence.js"; import { usePrefersReducedMotion } from "../../global/js/hooks/usePrefersReducedMotion.js"; import { getDevtoolsProps } from "../../global/js/utils/devtools.js"; import { allPropTypes } from "../../global/js/utils/props-helper.js"; import { useFeatureFlag as useFeatureFlag$1 } from "../FeatureFlags/index.js"; import { ActionSet } from "../ActionSet/ActionSet.js"; import { SIDE_PANEL_SIZES } from "./constants.js"; import React, { useCallback, useEffect, useRef, useState } from "react"; import PropTypes from "prop-types"; import { Button, Heading, IconButton, Layer, Section } from "@carbon/react"; import { ArrowLeft, Close } from "@carbon/react/icons"; import { Resizer } from "@carbon-labs/react-resizer"; import { moderate02 } from "@carbon/motion"; //#region src/components/SidePanel/SidePanel.tsx /** * 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. */ var import_classnames = /* @__PURE__ */ __toESM(require_classnames()); const blockClass = `${pkg.prefix}--side-panel`; const componentName = "SidePanel"; const defaults = { animateTitle: true, closeIconDescription: "Close", closeIconTooltipAlignment: "left", currentStep: 0, hideCloseButton: false, navigationBackIconDescription: "Back", placement: "right", size: "md" }; /** * Side panels keep users in-context of a page while performing tasks like navigating, editing, viewing details, or configuring something new. */ const SidePanel = React.forwardRef((props, ref) => { const { actionToolbarButtons, actions, aiLabel, animateTitle = defaults.animateTitle, children, className, closeIconDescription = defaults.closeIconDescription, closeIconTooltipAlignment = defaults.closeIconTooltipAlignment, condensedActions, currentStep = defaults.currentStep, decorator, hideCloseButton = defaults.hideCloseButton, id = blockClass, includeOverlay, labelText, navigationBackIconDescription = defaults.navigationBackIconDescription, onNavigationBack, onRequestClose, onUnmount, open, placement = defaults.placement, preventCloseOnClickOutside, selectorPageContent, selectorPrimaryFocus, size = defaults.size, slideIn, slug, subtitle, title, launcherButtonRef, ...rest } = props; const [animationComplete, setAnimationComplete] = useState(false); const localRef = useRef(null); const sidePanelRef = ref || localRef; const overlayRef = useRef(null); const innerContentRef = useRef(null); const closeRef = useRef(null); const animatedScrollRef = useRef(null); const headerRef = useRef(null); const titleRef = useRef(null); const labelTextRef = useRef(null); const subtitleRef = useRef(null); const previousState = usePreviousValue({ size, open, currentStep }); const [scrollAnimationDistance, setScrollAnimationDistance] = useState(-1); const [doAnimateTitle, setDoAnimateTitle] = useState(true); const { firstElement, keyDownListener } = useFocus(sidePanelRef); const panelRefValue = sidePanelRef.current; const previousOpen = usePreviousValue(open); const enableResizer = useFeatureFlag$1("enableSidepanelResizer"); const sidePanelWidth = useRef(void 0); const accumulatedDeltaRef = useRef(0); const shouldReduceMotion = usePrefersReducedMotion(); const { shouldRender } = usePresence(open, sidePanelRef, shouldReduceMotion ? "side-panel-exit-reduced" : placement === "right" ? "side-panel-exit-right" : "side-panel-exit-left"); const [labelTextHeight, setLabelTextHeight] = useState(0); const handleEscapeKey = useCallback((event) => { if (event.key === "Escape" && open) onRequestClose?.(); }, [onRequestClose, open]); useEffect(() => { if (open && !slideIn) { window.addEventListener("keydown", handleEscapeKey); return () => { window.removeEventListener("keydown", handleEscapeKey); }; } }, [ handleEscapeKey, open, slideIn ]); useEffect(() => { if (!enableResizer) return; const parentEl = sidePanelRef.current?.parentElement; if (parentEl) parentEl.style.removeProperty("--c4p-side-panel-modified-size"); }, [ size, enableResizer, sidePanelRef ]); useEffect(() => { if (!enableResizer) return; sidePanelWidth.current = sidePanelRef?.current?.clientWidth; }, [ sidePanelRef, sidePanelRef?.current?.clientWidth, enableResizer ]); useEffect(() => { if (open && !titleRef?.current) setDoAnimateTitle(false); else setDoAnimateTitle(animateTitle); }, [animateTitle, open]); const onResize = useCallback((event, delta) => { event.preventDefault(); event.stopPropagation(); const setWidth = (width) => { const parentEl = sidePanelRef.current?.parentElement; if (parentEl) parentEl.style.setProperty("--c4p-side-panel-modified-size", typeof width === "number" ? `${width}px` : width); }; if (event.type === "keydown") { switch (event.key) { case "Home": setWidth("75vw"); break; case "End": setWidth(SIDE_PANEL_SIZES["xs"]); break; case "ArrowLeft": case "ArrowRight": accumulatedDeltaRef.current += delta; setWidth((sidePanelWidth.current ?? 0) - (placement === "right" ? accumulatedDeltaRef.current : -accumulatedDeltaRef.current)); break; } return; } if (sidePanelRef.current?.style) sidePanelRef.current.style.transition = "none"; setWidth((sidePanelWidth.current ?? 0) - (placement === "right" ? delta : -delta)); }, [ placement, sidePanelRef, sidePanelWidth ]); const getPanelWidthPercent = useCallback((customWidth) => { if (customWidth) { const remInPixels = parseFloat(customWidth) * parseFloat(getComputedStyle(document.documentElement).fontSize); return Math.round(remInPixels / window.innerWidth * 100); } return Math.round((sidePanelRef.current?.clientWidth || 0) / window.innerWidth * 100); }, [sidePanelRef]); const onResizeEnd = useCallback((_, ref) => { accumulatedDeltaRef.current = 0; sidePanelRef.current?.style?.removeProperty("transition"); ref.current.setAttribute("aria-label", `side panel is covering ${getPanelWidthPercent()}% of screen`); ref.current.setAttribute("aria-valuenow", getPanelWidthPercent()); sidePanelWidth.current = sidePanelRef.current?.clientWidth; }, [sidePanelRef, getPanelWidthPercent]); const onDoubleClick = useCallback(() => { sidePanelWidth.current = Math.min(parseFloat(SIDE_PANEL_SIZES[size]) * 16, window.innerWidth * .75); const parentEl = sidePanelRef.current?.parentElement; if (parentEl) parentEl.style.removeProperty("--c4p-side-panel-modified-size"); }, [sidePanelRef, size]); const titleItemsStyles = useCallback((progress) => { if (subtitleRef?.current) { const subtitleEl = subtitleRef?.current; const calculatedMargin = subtitleEl?.clientHeight * progress; subtitleEl?.style?.setProperty("margin-top", `${-calculatedMargin}px`); } if (labelTextRef?.current) { const calculatedMargin = labelTextHeight * progress; labelTextRef?.current?.style?.setProperty("margin-top", `${-calculatedMargin}px`); } }, [labelTextHeight]); useEffect(() => { if (open && animateTitle && labelTextRef?.current) setLabelTextHeight(Number(labelTextRef?.current?.clientHeight || null)); }, [ animateTitle, labelTextRef, open ]); const handleScroll = useCallback(() => { if (doAnimateTitle && innerContentRef?.current) { const scrollTop = innerContentRef?.current?.scrollTop; const animationProgress = Math.min(Number(scrollTop), scrollAnimationDistance) / scrollAnimationDistance; panelRefValue?.style.setProperty(`--${blockClass}--scroll-animation-progress`, animationProgress.toString()); titleItemsStyles(animationProgress); } }, [ doAnimateTitle, panelRefValue?.style, scrollAnimationDistance, titleItemsStyles ]); useEffect(() => { if (sidePanelRef && panelRefValue) { const scrollableSection = animatedScrollRef.current ?? innerContentRef.current; if (previousState && previousState["currentStep"] !== currentStep && scrollableSection) scrollableSection.scrollTop = 0; if (previousState && previousState["size"] !== size && scrollableSection) scrollableSection.scrollTop = 0; } }, [ currentStep, sidePanelRef, size, previousState, id, panelRefValue ]); useEffect(() => { if (!title && labelText) console.warn(`${componentName}: The prop \`labelText\` was provided without a \`title\`. It is required to have a \`title\` when using the \`labelText\` prop.`); }, [labelText, title]); useEffect(() => { if (previousOpen && !open && launcherButtonRef) setTimeout(() => { launcherButtonRef?.current?.focus(); }, 0); }, [ launcherButtonRef, open, previousOpen ]); const checkSetDoAnimateTitle = () => { let canDoAnimateTitle = false; if (panelRefValue && open && animateTitle && animationComplete && titleRef?.current && title && title.length && !shouldReduceMotion) { const titleEl = titleRef.current; const labelHeight = labelTextRef?.current?.offsetHeight ?? 0; const subtitleHeight = subtitleRef?.current?.offsetHeight ?? 0; const titleVerticalBorder = actionToolbarButtons ? titleEl.offsetHeight - titleEl.clientHeight : 0; const scrollAnimationDistance = labelHeight + subtitleHeight + titleVerticalBorder; setScrollAnimationDistance(scrollAnimationDistance); panelRefValue?.style.setProperty(`--${blockClass}--scroll-animation-distance`, scrollAnimationDistance.toString()); const scrollEl = innerContentRef.current; if (scrollEl) { const innerComputed = window?.getComputedStyle(innerContentRef.current); const innerPaddingHeight = innerComputed ? parseFloat(innerComputed?.paddingTop) + parseFloat(innerComputed?.paddingBottom) : 0; canDoAnimateTitle = (!!labelText || !!actionToolbarButtons || !!subtitle) && scrollEl.scrollHeight - scrollEl.clientHeight >= scrollAnimationDistance + innerPaddingHeight; } } if (doAnimateTitle !== canDoAnimateTitle) setDoAnimateTitle(canDoAnimateTitle); }; useEffect(() => { if (!doAnimateTitle && sidePanelRef.current) panelRefValue?.style.setProperty(`--${blockClass}--scroll-animation-progress`, "0"); }, [ doAnimateTitle, handleScroll, sidePanelRef, innerContentRef, open, panelRefValue?.style ]); useEffect(() => { if (panelRefValue && open && animateTitle && animationComplete && titleRef?.current && title && title.length && !shouldReduceMotion) checkSetDoAnimateTitle(); }, [ open, doAnimateTitle, animationComplete, handleScroll, title, size, shouldReduceMotion, id ]); useEffect(() => { const handleOutsideClick = (event) => { if (panelRefValue && overlayRef.current && overlayRef.current.contains(event.target) && onRequestClose) onRequestClose(); }; const bodyElement = document.body; if (includeOverlay && open) bodyElement.style.overflow = "hidden"; else if (includeOverlay && !open) bodyElement.style.overflow = ""; if (includeOverlay && !preventCloseOnClickOutside) document.addEventListener("click", handleOutsideClick); return () => { const bodyElement = document.body; bodyElement.style.overflow = ""; document.removeEventListener("click", handleOutsideClick); }; }, [ includeOverlay, onRequestClose, open, preventCloseOnClickOutside, onUnmount, sidePanelRef, panelRefValue ]); const onAnimationEnd = () => { if (!open) onUnmount?.(); setAnimationComplete(!animationComplete); }; useEffect(() => { if (shouldReduceMotion) setAnimationComplete(true); }, [shouldReduceMotion]); const onAnimationStart = () => { setAnimationComplete(false); }; useEffect(() => { if (!open && slideIn) { const pageContentElement = selectorPageContent ? document.querySelector(selectorPageContent) : null; if (placement && placement === "right" && pageContentElement) pageContentElement.style.marginInlineEnd = "0"; else if (pageContentElement) pageContentElement.style.marginInlineStart = "0"; } }, [ open, placement, selectorPageContent, slideIn ]); useEffect(() => { if (!open && previousState && previousState["open"] && shouldReduceMotion) onUnmount?.(); }, [ open, onUnmount, shouldReduceMotion, previousState ]); useEffect(() => { if (open && slideIn) { const pageContentElement = selectorPageContent ? document.querySelector(selectorPageContent) : null; if (pageContentElement) pageContentElement.style.inlineSize = "auto"; else pconsole_default.warn("SidePanel prop `selectorPageContent` was not provided a selector that matches any element on your page. If an element is not found, the panel will render as a slide over."); if (placement && placement === "right" && pageContentElement) { pageContentElement.style.marginInlineEnd = "0"; pageContentElement.style.transition = !shouldReduceMotion ? `margin-inline-end ${moderate02}` : ""; pageContentElement.style.marginInlineEnd = SIDE_PANEL_SIZES[size]; } else if (pageContentElement) { pageContentElement.style.marginInlineStart = "0"; pageContentElement.style.transition = !shouldReduceMotion ? `margin-inline-start ${moderate02}` : ""; pageContentElement.style.marginInlineStart = SIDE_PANEL_SIZES[size]; } } }, [ slideIn, selectorPageContent, placement, size, shouldReduceMotion, open ]); useEffect(() => { if (open && animationComplete) { if (selectorPrimaryFocus && getSpecificElement(sidePanelRef?.current, selectorPrimaryFocus)) { const primeFocusEl = getSpecificElement(sidePanelRef?.current, selectorPrimaryFocus); if (primeFocusEl && window?.getComputedStyle(primeFocusEl)?.display !== "none") setTimeout(() => primeFocusEl?.focus(), 0); } else if (!slideIn) setTimeout(() => firstElement?.focus(), 0); } }, [ animationComplete, firstElement, open, selectorPrimaryFocus, sidePanelRef, slideIn ]); const primaryActionContainerClassNames = (0, import_classnames.default)([`${blockClass}__actions-container`, { [`${blockClass}__actions-container--condensed`]: condensedActions }]); const mainPanelClassNames = (0, import_classnames.default)([ blockClass, className, `${blockClass}`, `${blockClass}--${size}`, { [`${blockClass}--right-placement`]: placement === "right", [`${blockClass}--left-placement`]: placement === "left", [`${blockClass}--slide-in`]: slideIn, [`${blockClass}--enable-sidepanel-resizer`]: enableResizer && window.innerWidth > 768, [`${blockClass}--has-decorator`]: decorator, [`${blockClass}--has-slug`]: slug, [`${blockClass}--has-ai-label`]: aiLabel, [`${blockClass}--condensed-actions`]: condensedActions, [`${blockClass}--has-overlay`]: includeOverlay, [`${blockClass}--open`]: open, [`${blockClass}--closing`]: !open, [`${blockClass}--reduced-motion`]: shouldReduceMotion } ]); const renderTitle = () => /* @__PURE__ */ React.createElement("div", { className: (0, import_classnames.default)(`${blockClass}__title`, { [`${blockClass}__title--no-label`]: !labelTextRef.current }), ref: titleRef }, /* @__PURE__ */ React.createElement(Heading, { className: `${blockClass}__title-text`, "aria-hidden": false }, title), doAnimateTitle && !shouldReduceMotion && /* @__PURE__ */ React.createElement(Heading, { className: `${blockClass}__collapsed-title-text`, "aria-hidden": true }, title)); const renderHeader = () => { const closeSize = actions && actions.length && /l/.test(size) ? "md" : "sm"; let normalizedDecorator; /** * slug is deprecated * can remove this condition in future release */ if (slug && slug["type"]?.displayName === "AILabel") normalizedDecorator = React.cloneElement(slug, { size: "xs" }); if (aiLabel && aiLabel["type"]?.displayName === "AILabel") normalizedDecorator = React.cloneElement(aiLabel, { size: "xs" }); if (decorator?.["type"]?.displayName === "AILabel") normalizedDecorator = React.cloneElement(decorator, { size: "xs" }); return /* @__PURE__ */ React.createElement("div", { className: (0, import_classnames.default)(`${blockClass}__header`, { [`${blockClass}__header--on-detail-step`]: currentStep > 0, [`${blockClass}__header--no-title-animation`]: !animateTitle, [`${blockClass}__header--reduced-motion`]: shouldReduceMotion, [`${blockClass}__header--has-title`]: title }), ref: headerRef }, currentStep > 0 && /* @__PURE__ */ React.createElement(IconButton, { kind: "ghost", size: closeSize, align: "bottom", label: navigationBackIconDescription, className: `${blockClass}__navigation-back-button`, onClick: onNavigationBack }, /* @__PURE__ */ React.createElement(ArrowLeft, null)), title && title.length && labelText && labelText.length && /* @__PURE__ */ React.createElement("p", { className: `${blockClass}__label-text`, ref: labelTextRef }, labelText), title && title.length && renderTitle(), (normalizedDecorator || !hideCloseButton) && /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__decorator-and-close` }, normalizedDecorator, !hideCloseButton && /* @__PURE__ */ React.createElement(IconButton, { className: `${blockClass}__close-button`, label: closeIconDescription, onClick: onRequestClose, onKeyDown: slideIn ? void 0 : handleEscapeKey, ref: closeRef, align: closeIconTooltipAlignment }, /* @__PURE__ */ React.createElement(Close, { size: 16, "aria-hidden": "true", tabIndex: "-1", className: `${blockClass}--btn__icon` }))), subtitle && /* @__PURE__ */ React.createElement("p", { className: (0, import_classnames.default)(`${blockClass}__subtitle-text`, { [`${blockClass}__subtitle-text-no-animation-no-action-toolbar`]: !doAnimateTitle && (!actionToolbarButtons || !actionToolbarButtons.length) }), ref: subtitleRef }, subtitle), actionToolbarButtons && actionToolbarButtons.length && /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__action-toolbar` }, actionToolbarButtons.map(({ label, kind, hasIconOnly = false, icon, renderIcon, tooltipPosition, tooltipAlignment, leading, disabled, className, onClick, ...rest }) => /* @__PURE__ */ React.createElement(Button, { ...rest, key: label, kind: kind || "ghost", size: "sm", renderIcon: renderIcon || icon, iconDescription: label, ...hasIconOnly && { tooltipPosition: tooltipPosition || "bottom", tooltipAlignment: tooltipAlignment || "start" }, hasIconOnly, disabled, className: (0, import_classnames.default)([ `${blockClass}__action-toolbar-button`, className, { [`${blockClass}__action-toolbar-leading-button`]: leading } ]), onClick }, leading && label)))); }; const renderMain = () => { return /* @__PURE__ */ React.createElement("div", { ref: innerContentRef, onScroll: handleScroll, className: (0, import_classnames.default)(`${blockClass}__inner-content`, `${blockClass}--scrolls`, `${!doAnimateTitle && !animateTitle ? `${blockClass}__inner-content--no-animated-title` : ""}`) }, /* @__PURE__ */ React.createElement(Layer, null, children)); }; const handleKeyDown = (event) => { if (!slideIn) { handleEscapeKey(event); keyDownListener(event); } }; return shouldRender ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Section, { ...getDevtoolsProps(componentName), ...rest, id, className: mainPanelClassNames, ref: sidePanelRef, as: "aside", "aria-label": title || rest["aria-label"], onAnimationEnd, onAnimationStart, onKeyDown: handleKeyDown }, !slideIn && enableResizer && window.innerWidth > 768 && /* @__PURE__ */ React.createElement(Resizer, { className: `${blockClass}__resizer`, orientation: "vertical", "aria-valuemin": getPanelWidthPercent(SIDE_PANEL_SIZES["xs"]), "aria-valuemax": 75, "aria-valuenow": getPanelWidthPercent(), onResize, onResizeEnd, onDoubleClick }), renderHeader(), renderMain(), /* @__PURE__ */ React.createElement(ActionSet, { actions: actions ?? [], className: primaryActionContainerClassNames, size: size === "xs" ? "sm" : size })), includeOverlay && /* @__PURE__ */ React.createElement("div", { ref: overlayRef, className: (0, import_classnames.default)(`${blockClass}__overlay`, { [`${blockClass}__overlay--closing`]: !open }) })) : null; }); const deprecatedProps = { /** * **deprecated** * **Experimental:** Provide a `Slug` component to be rendered inside the `SidePanel` component */ slug: PropTypes.node, /** * **deprecated** * Please use the `decorator` instead * Optional prop that is intended for any scenario where something is being generated by AI to reinforce AI transparency, accountability, and explainability at the UI level. */ aiLabel: PropTypes.node }; SidePanel.propTypes = { /** * Sets the action toolbar buttons */ /**@ts-ignore */ actionToolbarButtons: PropTypes.arrayOf(PropTypes.shape({ label: PropTypes.string, leading: PropTypes.bool, icon: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), onClick: PropTypes.func, kind: PropTypes.oneOf([ "ghost", "tertiary", "secondary", "primary" ]), tooltipAlignment: PropTypes.oneOf([ "top", "right", "bottom", "left" ]), tooltipPosition: PropTypes.oneOf([ "start", "center", "end" ]) })), /** * The primary actions to be shown in the side panel. Each action is * specified as an object with optional fields: 'label' to supply the button * label, 'kind' to select the button kind (must be 'primary', 'secondary' or * 'ghost'), 'tooltipPosition' to select where the tooltip is placed around * the button (must be 'top', 'right', 'bottom', or 'left'), 'tooltipAlignment' * to select how the tooltip is aligned with the button (must be 'start', * 'center', or 'end', 'loading' to display a loading indicator, and 'onClick' to * receive notifications when the button is clicked. Additional fields in the * object will be passed to the Button component, and these can include * 'disabled', 'ref', 'className', and any other Button props. Any other * fields in the object will be passed through to the button element as HTML * attributes. * * See https://react.carbondesignsystem.com/?path=/docs/components-button--default#component-api */ actions: allPropTypes([PropTypes.arrayOf(PropTypes.shape({ ...Button.propTypes, kind: PropTypes.oneOf([ "ghost", "danger--ghost", "secondary", "danger", "primary" ]), tooltipPosition: PropTypes.oneOf([ "top", "right", "bottom", "left" ]), tooltipAlignment: PropTypes.oneOf([ "start", "center", "end" ]), label: PropTypes.string, loading: PropTypes.bool, /**@ts-ignore */ onClick: Button.propTypes.onClick }))]), /** * Determines if the title will animate on scroll */ animateTitle: PropTypes.bool, /** * Sets the body content of the side panel */ children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, /** * Sets an optional className to be added to the side panel outermost element */ className: PropTypes.string, /** * Sets the close button icon description */ closeIconDescription: PropTypes.string, /** * Sets the close button tooltip alignment */ closeIconTooltipAlignment: PropTypes.string, /** * Determines whether the side panel should render the condensed version (affects action buttons primarily) */ condensedActions: PropTypes.bool, /** * Sets the current step of the side panel */ currentStep: PropTypes.number, /** * Show/hide the "X" close button. */ hideCloseButton: PropTypes.bool, /** * Unique identifier */ id: PropTypes.string, /** * Determines whether the side panel should render with an overlay */ includeOverlay: PropTypes.bool, /** * Sets the label text which will display above the title text */ labelText: PropTypes.string, /** * Provide a ref to return focus to once the modal is closed. */ /**@ts-ignore */ launcherButtonRef: PropTypes.any, /** * Sets the icon description for the navigation back icon button */ navigationBackIconDescription: PropTypes.string, /** * Changes the current side panel page to the previous page */ onNavigationBack: PropTypes.func, /** * Specify a handler for closing the side panel. * This handler closes the modal, e.g. changing `open` prop. */ onRequestClose: PropTypes.func, /** * Optional function called when the side panel exit animation is complete. * This handler can be used for any state cleanup needed before the panel is removed from the DOM. */ onUnmount: PropTypes.func, /** * Determines whether the side panel should render or not */ open: PropTypes.bool.isRequired, /** * Determines if the side panel is on the right or left */ placement: PropTypes.oneOf(["left", "right"]), /** * Prevent closing on click outside of the panel */ preventCloseOnClickOutside: PropTypes.bool, /** * This is the selector to the element that contains all of the page content that will shrink if the panel is a slide in. * This prop is required when using the `slideIn` variant of the side panel. */ /**@ts-ignore*/ selectorPageContent: PropTypes.string, /** * Specify a CSS selector that matches the DOM element that should * be focused when the side panel opens */ /**@ts-ignore*/ selectorPrimaryFocus: PropTypes.string, /** * Sets the size of the side panel */ /**@ts-ignore*/ size: PropTypes.oneOf([ "xs", "sm", "md", "lg", "xl", "2xl" ]), /** * Determines if this panel slides in */ /**@ts-ignore*/ slideIn: PropTypes.bool, /** * Sets the subtitle text */ subtitle: PropTypes.node, /** * Sets the title text */ /**@ts-ignore*/ title: PropTypes.string, ...deprecatedProps }; SidePanel.displayName = componentName; //#endregion export { SidePanel };