UNPKG

@carbon/react

Version:

React components for the Carbon Design System

118 lines (116 loc) 4.46 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 { PrefixContext } from "../../internal/usePrefix.js"; import { ArrowLeft, ArrowRight, Enter, Space } from "../../internal/keyboard/keys.js"; import { matches } from "../../internal/keyboard/match.js"; import { getNextIndex } from "../../internal/keyboard/navigation.js"; import { deprecate } from "../../prop-types/deprecate.js"; import { isComponentElement } from "../../internal/utils.js"; import { composeEventHandlers } from "../../tools/events.js"; import { LayoutConstraint } from "../Layout/index.js"; import IconSwitch from "../Switch/IconSwitch.js"; import classNames from "classnames"; import { Children, cloneElement, isValidElement, useContext, useEffect, useRef, useState } from "react"; import PropTypes from "prop-types"; import { jsx } from "react/jsx-runtime"; //#region src/components/ContentSwitcher/ContentSwitcher.tsx /** * 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. */ const ContentSwitcher = ({ children, className, light, lowContrast, selectedIndex: selectedIndexProp = 0, selectionMode = "automatic", size, onChange, ...other }) => { const prefix = useContext(PrefixContext); const [selectedIndex, setSelectedIndex] = useState(selectedIndexProp); const prevSelectedIndexRef = useRef(selectedIndexProp); const switchRefs = useRef([]); const childrenArray = Children.toArray(children); useEffect(() => { if (prevSelectedIndexRef.current !== selectedIndexProp) { setSelectedIndex(selectedIndexProp); prevSelectedIndexRef.current = selectedIndexProp; } }, [selectedIndexProp]); const handleItemRef = (index) => (ref) => { if (ref) switchRefs.current[index] = ref; }; const focusSwitch = (index) => { const ref = switchRefs.current[index]; if (ref) ref.focus(); }; const isKeyboardEvent = (event) => event && typeof event === "object" && "key" in event; const handleChildChange = (event) => { if (typeof event.index === "undefined") return; const { index } = event; if (isKeyboardEvent(event) && matches(event, [ArrowRight, ArrowLeft])) { const nextIndex = getNextIndex(event.key, index, childrenArray.length); if (typeof nextIndex !== "number") return; focusSwitch(nextIndex); if (selectionMode !== "manual") { const child = childrenArray[nextIndex]; setSelectedIndex(nextIndex); if (isValidElement(child)) onChange({ ...event, index: nextIndex, name: child.props.name, text: child.props.text }); } } else if (selectedIndex !== index && (isKeyboardEvent(event) ? matches(event, [Enter, Space]) : true)) { setSelectedIndex(index); focusSwitch(index); onChange(event); } }; const isIconOnly = Children.map(children, (child) => { return isComponentElement(child, IconSwitch); })?.every((val) => val === true); const classes = classNames(`${prefix}--content-switcher`, className, { [`${prefix}--content-switcher--light`]: light, [`${prefix}--content-switcher--${size}`]: size, [`${prefix}--layout--size-${size}`]: size, [`${prefix}--content-switcher--icon-only`]: isIconOnly, [`${prefix}--content-switcher--low-contrast`]: lowContrast }); return /* @__PURE__ */ jsx(LayoutConstraint, { size: { default: "md", min: "sm", max: "lg" }, ...other, className: classes, role: "tablist", onChange: void 0, children: children && Children.map(children, (child, index) => cloneElement(child, { index, onClick: composeEventHandlers([handleChildChange, child.props.onClick]), onKeyDown: composeEventHandlers([handleChildChange, child.props.onKeyDown]), selected: index === selectedIndex, ref: handleItemRef(index), size })) }); }; ContentSwitcher.displayName = "ContentSwitcher"; ContentSwitcher.propTypes = { children: PropTypes.node, className: PropTypes.string, light: deprecate(PropTypes.bool, "The `light` prop for `ContentSwitcher` is no longer needed and has been deprecated. It will be removed in the next major release."), lowContrast: PropTypes.bool, onChange: PropTypes.func.isRequired, selectedIndex: PropTypes.number, selectionMode: PropTypes.oneOf(["automatic", "manual"]), size: PropTypes.oneOf([ "sm", "md", "lg" ]) }; //#endregion export { ContentSwitcher };