@carbon/react
Version:
React components for the Carbon Design System
209 lines (207 loc) • 7.63 kB
JavaScript
/**
* 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 { Text } from "../Text/Text.js";
import { useId } from "../../internal/useId.js";
import { deprecate } from "../../prop-types/deprecate.js";
import { useOutsideClick } from "../../internal/useOutsideClick.js";
import classNames from "classnames";
import React, { useRef, useState } from "react";
import PropTypes from "prop-types";
import { jsx, jsxs } from "react/jsx-runtime";
import { RadioButton, RadioButtonChecked } from "@carbon/icons-react";
//#region src/components/StructuredList/StructuredList.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 GridSelectedRowStateContext = React.createContext(null);
const GridSelectedRowDispatchContext = React.createContext(null);
function StructuredListWrapper(props) {
const { children, selection, className, ["aria-label"]: ariaLabel = "Structured list section", ariaLabel: deprecatedAriaLabel, isCondensed, isFlush, selectedInitialRow, ...other } = props;
const prefix = usePrefix();
const classes = classNames(`${prefix}--structured-list`, {
[`${prefix}--structured-list--selection`]: selection,
[`${prefix}--structured-list--condensed`]: isCondensed,
[`${prefix}--structured-list--flush`]: isFlush && !selection
}, className);
const [selectedRow, setSelectedRow] = React.useState(selectedInitialRow ?? null);
return /* @__PURE__ */ jsx(GridSelectedRowStateContext.Provider, {
value: selectedRow,
children: /* @__PURE__ */ jsx(GridSelectedRowDispatchContext.Provider, {
value: setSelectedRow,
children: /* @__PURE__ */ jsx("div", {
role: "table",
className: classes,
...other,
"aria-label": deprecatedAriaLabel || ariaLabel,
children
})
})
});
}
StructuredListWrapper.propTypes = {
["aria-label"]: PropTypes.string,
ariaLabel: deprecate(PropTypes.string, "This prop syntax has been deprecated. Please use the new `aria-label`."),
children: PropTypes.node,
className: PropTypes.string,
isCondensed: PropTypes.bool,
isFlush: PropTypes.bool,
selection: PropTypes.bool,
selectedInitialRow: PropTypes.string
};
function StructuredListHead(props) {
const { children, className, ...other } = props;
return /* @__PURE__ */ jsx("div", {
role: "rowgroup",
className: classNames(`${usePrefix()}--structured-list-thead`, className),
...other,
children
});
}
StructuredListHead.propTypes = {
children: PropTypes.node,
className: PropTypes.string
};
function StructuredListBody(props) {
const { children, className, ...other } = props;
return /* @__PURE__ */ jsx("div", {
className: classNames(`${usePrefix()}--structured-list-tbody`, className),
role: "rowgroup",
...other,
children
});
}
StructuredListBody.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
head: PropTypes.bool,
onKeyDown: PropTypes.func
};
const GridRowContext = React.createContext(null);
function StructuredListRow(props) {
const { onKeyDown, children, className, head, onClick, selection, id, ...other } = props;
const [hasFocusWithin, setHasFocusWithin] = useState(false);
const generatedRowId = useId("grid-input");
const rowId = id ?? generatedRowId;
const selectedRow = React.useContext(GridSelectedRowStateContext);
const setSelectedRow = React.useContext(GridSelectedRowDispatchContext);
const prefix = usePrefix();
const value = { id: rowId };
const classes = classNames(`${prefix}--structured-list-row`, {
[`${prefix}--structured-list-row--header-row`]: head,
[`${prefix}--structured-list-row--focused-within`]: hasFocusWithin && !selection || hasFocusWithin && selection && (selectedRow === rowId || selectedRow === null),
[`${prefix}--structured-list-row--selected`]: selectedRow === rowId
}, className);
const itemRef = useRef(null);
const handleClick = () => {
setHasFocusWithin(false);
};
useOutsideClick(itemRef, handleClick);
return head ? /* @__PURE__ */ jsxs("div", {
role: "row",
...other,
className: classes,
children: [selection && /* @__PURE__ */ jsx(StructuredListCell, { head: true }), children]
}) : /* @__PURE__ */ jsx("div", {
...other,
role: "row",
className: classes,
ref: itemRef,
onClick: (event) => {
setSelectedRow?.(rowId);
onClick?.(event);
if (selection) setHasFocusWithin(true);
},
onFocus: (event) => {
if (selection || event.currentTarget === event.target) setHasFocusWithin(true);
},
onBlur: () => {
setHasFocusWithin(false);
},
onKeyDown,
children: /* @__PURE__ */ jsxs(GridRowContext.Provider, {
value,
children: [selection && /* @__PURE__ */ jsx(StructuredListCell, { children: selectedRow === rowId ? /* @__PURE__ */ jsx(RadioButtonChecked, { className: `${prefix}--structured-list__icon` }) : /* @__PURE__ */ jsx(RadioButton, { className: `${prefix}--structured-list__icon` }) }), children]
})
});
}
StructuredListRow.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
head: PropTypes.bool,
label: deprecate(PropTypes.bool, `\nThe \`label\` prop is no longer needed and will be removed in the next major version of Carbon.`),
onClick: PropTypes.func,
onKeyDown: PropTypes.func,
selection: PropTypes.bool,
id: PropTypes.string
};
function StructuredListInput(props) {
const defaultId = useId("structureListInput");
const { className, name = `structured-list-input-${defaultId}`, title, id, onChange, ...other } = props;
const prefix = usePrefix();
const classes = classNames(`${prefix}--structured-list-input`, `${prefix}--visually-hidden`, className);
const row = React.useContext(GridRowContext);
const selectedRow = React.useContext(GridSelectedRowStateContext);
const setSelectedRow = React.useContext(GridSelectedRowDispatchContext);
return /* @__PURE__ */ jsx("input", {
...other,
type: "radio",
tabIndex: 0,
checked: !!row && row.id === selectedRow,
value: row?.id ?? "",
onChange: (event) => {
setSelectedRow?.(event.target.value);
onChange?.(event);
},
id: id ?? defaultId,
className: classes,
name,
title
});
}
StructuredListInput.propTypes = {
className: PropTypes.string,
defaultChecked: deprecate(PropTypes.bool, `\nThe prop \`defaultChecked\` is no longer needed and will be removed in the next major version of Carbon.`),
id: PropTypes.string,
name: PropTypes.string,
onChange: PropTypes.func,
title: PropTypes.string,
value: deprecate(PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, `\nThe prop \`value\` will be removed in the next major version of Carbon.`)
};
function StructuredListCell(props) {
const { children, className, head, noWrap, ...other } = props;
const prefix = usePrefix();
const classes = classNames({
[`${prefix}--structured-list-th`]: head,
[`${prefix}--structured-list-td`]: !head,
[`${prefix}--structured-list-content--nowrap`]: noWrap
}, className);
if (head) return /* @__PURE__ */ jsx(Text, {
className: classes,
role: "columnheader",
...other,
children
});
return /* @__PURE__ */ jsx(Text, {
as: "div",
className: classes,
role: "cell",
...other,
children
});
}
StructuredListCell.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
head: PropTypes.bool,
noWrap: PropTypes.bool
};
//#endregion
export { StructuredListBody, StructuredListCell, StructuredListHead, StructuredListInput, StructuredListRow, StructuredListWrapper };