import * as React from "react"; import { cx } from "@emotion/css"; import { FieldListColumnProps, FieldListColumnSeparator, FieldListColumnWidthProps } from "./FieldListColumn"; import ResetButton from "../../button/components/ResetButton"; import { Icon } from "../../icon"; import { SystemIcons } from "../../icons/dist/system-icons-enum"; import { themeTextColorDisabled, themeTextColorPrimary } from "../../design-tokens/build/js/designTokens"; import { SpacingBox } from "../../styleUtils/modifiers"; import { Text } from "../../styleUtils/typography"; import { FieldListAddButton, FieldListColumn } from ".."; import { ButtonProps } from "../../button/components/ButtonBase"; import { findNestedPropertyInObject } from "../../utilities"; import { getFieldRowGrid, fieldListStack, invisibleColHeader, fieldWrapper } from "../style"; import { FieldListProvider, Context as FieldListContext } from "./FieldListContext"; import { iconSizes } from "../../shared/styles/styleUtils/layout/iconSizes"; const REMOVE_ICON_SIZE = "xs"; export interface FieldListProps { /** The data to populate the field values with */ data: Array>; /** An array of the indexes of disabled rows */ disabledRows?: number[]; /** The callback for when the remove button is clicked */ onRemoveItem?: (affectedRowIndex: number) => () => void; /** The callback for when a row is added */ onAddItem?: (addedRow: Record) => void; /** * The path to an object property who's value will be unique. * Typically, this will be some kind of ID. The value of this property * is used to set the key for each row in the field list. * * This key's value cannot be the field value because it will cause * problems when the field value is changed */ pathToUniqueKey?: string; children?: React.ReactNode | React.ReactNode[]; } type FieldListColumn = React.ReactElement< FieldListColumnProps & FieldListColumnWidthProps >; interface FieldListHeaderProps { columns: FieldListColumn[]; } interface FieldListRowProps extends Omit { data: Record; columns: FieldListColumn[]; isLastRow?: boolean; rowId: number | string; rowIndex: number; } const isRowDisabled = (rowIndex, disabledRows) => disabledRows && disabledRows.includes(rowIndex); const FieldListRow = ({ columns, data, rowIndex, disabledRows, isLastRow, pathToUniqueKey, rowId }: FieldListRowProps) => { const fieldListContext = React.useContext(FieldListContext); const addEmptyRow = (e: React.KeyboardEvent) => { const rowHasData = Object.keys(data || {}).filter( dataKey => Boolean(data[dataKey]) && dataKey !== pathToUniqueKey ).length; if (e.key === "Tab" && isLastRow && rowHasData) { fieldListContext?.addListItem(rowId); } }; const handleRowClick = () => { fieldListContext?.removeListItem(rowIndex); }; return (
{columns.map((col, i) => { return (
{col.type === FieldListColumn && col.props.children({ ...(col.props.onChange && col.props.pathToValue ? { onChange: col.props.onChange( rowIndex, col.props.pathToValue ) } : {}), fieldData: data, rowIndex, value: findNestedPropertyInObject(data, col.props.pathToValue), disabled: isRowDisabled(rowIndex, disabledRows), defaultProps: { key: `${col.props.pathToValue}-${rowIndex}`, id: `${col.props.pathToValue}-${rowIndex}`, inputLabel: `${col.props.header} ${rowIndex}` } })} {col.type === FieldListColumnSeparator && col.props.children}
); })} {/* this wrapper div is needed to fix a vertical centering bug in Firefox with
); }; const FieldListHeader = ({ columns }: FieldListHeaderProps) => ( {columns.map((col, i) => ( {col.type === FieldListColumn && col.props.header} {col.type === FieldListColumnSeparator && col.props.children} ))} ); const FieldList = ({ children, data, disabledRows, onAddItem, onRemoveItem, pathToUniqueKey = "id" }: FieldListProps) => { const columns = ( React.Children.toArray(children) as Array< React.ReactElement > ).filter( item => item.type === FieldListColumn || item.type === FieldListColumnSeparator ); const addButton = ( React.Children.toArray(children) as Array> ).find(child => child.type === FieldListAddButton); const [fieldListData, setFieldListData] = React.useState>>(data); React.useEffect(() => { setFieldListData(data); }, [data]); const getAddHandler = () => { if (!onAddItem && !addButton?.props.onClick) { return; } return (addedItem: Record) => { if (onAddItem && addedItem) { onAddItem(addedItem); } if (addButton?.props.onClick) { addButton?.props.onClick(); } }; }; return (
{fieldListData && fieldListData.length ? ( ) : null}
{fieldListData.map((_, i) => { const rowId = findNestedPropertyInObject( fieldListData[i], pathToUniqueKey ); const rowKey = `fieldList.row-${ (pathToUniqueKey && rowId) || rowId === 0 ? rowId : i }`; return ( ); })} {addButton}
); }; export default React.memo(FieldList);