import { Add as MUIAddIcon, Delete as MUIDeleteIcon, Done as MUIDoneIcon, Edit as MUIEditIcon, } from '@mui/icons-material'; import { Box as MUIBox, List as MUIList, ListItem as MUIListItem, ListItemButton, ListItemIcon as MUIListItemIcon, SxProps, Theme, } from '@mui/material'; import { FC, ForwardedRef, forwardRef, useCallback, useImperativeHandle, useMemo, useState, } from 'react'; import { v4 as uuidv4 } from 'uuid'; import { BLUE, BORDER_RADIUS, GREY, RED } from '../lib/consts/DEFAULT_THEME'; import Checkbox from './Checkbox'; import Divider from './Divider'; import FlexBox from './FlexBox'; import IconButton from './IconButton'; import { BodyText } from './Text'; const List = forwardRef( ( { allowCheckAll: isAllowCheckAll = false, allowEdit: isAllowEdit = false, allowItemButton: isAllowItemButton = false, edit: isEdit = false, flexBoxProps, header, headerSpacing = '.3em', initialCheckAll = false, insertHeader: isInsertHeader = true, listEmpty, listItemIconMinWidth = '56px', listItemKeyPrefix = uuidv4(), listItemProps: { sx: listItemSx, ...restListItemProps } = {}, listItems, listProps: { sx: listSx, ...restListProps } = {}, onAdd, onDelete, onEdit, onAllCheckboxChange, onItemCheckboxChange, onItemClick, renderListItem = (key) => {key}, renderListItemCheckboxState, scroll: isScroll = false, // Input props that depend on other input props. allowAddItem: isAllowAddItem = isAllowEdit, allowCheckItem: isAllowCheckItem = isAllowEdit, allowDelete: isAllowDelete = isAllowEdit, allowEditItem: isAllowEditItem = isAllowEdit, }: ListProps, ref: ForwardedRef, ) => { const [isCheckAll, setIsCheckAll] = useState(initialCheckAll); const checkAllMinWidth = useMemo( () => `calc(${listItemIconMinWidth} - ${headerSpacing})`, [headerSpacing, listItemIconMinWidth], ); const addItemButton = useMemo( () => isAllowAddItem ? ( ) : undefined, [isAllowAddItem, onAdd], ); const deleteItemButton = useMemo( () => isEdit && isAllowDelete ? ( ) : undefined, [isAllowDelete, isEdit, onDelete], ); const editItemButton = useMemo(() => { if (isAllowEditItem) { return ( {isEdit ? : } ); } return undefined; }, [isAllowEditItem, isEdit, onEdit]); const checkAllElement = useMemo(() => { let element; if (isEdit && isAllowCheckItem) { element = isAllowCheckAll ? ( { const [, isChecked] = args; onAllCheckboxChange?.call(null, ...args); setIsCheckAll(isChecked); }} /> ) : ( ); } return element; }, [ checkAllMinWidth, isAllowCheckAll, isAllowCheckItem, isCheckAll, isEdit, onAllCheckboxChange, ]); const headerElement = useMemo(() => { const headerType = typeof header; return isInsertHeader && header ? ( {checkAllElement} {['boolean', 'string'].includes(headerType) ? ( <> {headerType === 'string' && {header}} ) : ( header )} {deleteItemButton} {editItemButton} {addItemButton} ) : ( header ); }, [ addItemButton, checkAllElement, deleteItemButton, editItemButton, header, headerSpacing, isInsertHeader, ]); const listEmptyElement = useMemo( () => typeof listEmpty === 'string' ? ( {listEmpty} ) : ( listEmpty ), [listEmpty], ); const listItemCheckbox = useCallback( (key: string, checked?: boolean) => isEdit && isAllowCheckItem ? ( onItemCheckboxChange?.call(null, key, ...args) } /> ) : undefined, [isAllowCheckItem, isEdit, listItemIconMinWidth, onItemCheckboxChange], ); const listItemElements = useMemo(() => { let result = listEmptyElement; if (listItems) { const entries = Object.entries(listItems); if (entries.length > 0) { result = entries.map(([key, value]) => { const listItem = renderListItem(key, value); return ( {listItemCheckbox( key, renderListItemCheckboxState?.call(null, key, value), )} {isAllowItemButton ? ( { onItemClick?.call(null, value, key, ...args); }} sx={{ borderRadius: BORDER_RADIUS }} > {listItem} ) : ( listItem )} ); }); } } return result; }, [ listEmptyElement, listItems, renderListItem, restListItemProps, listItemKeyPrefix, listItemSx, listItemCheckbox, renderListItemCheckboxState, isAllowItemButton, onItemClick, ]); const listScrollSx: SxProps | undefined = useMemo( () => (isScroll ? { maxHeight: '100%', overflowY: 'scroll' } : undefined), [isScroll], ); useImperativeHandle( ref, () => ({ setCheckAll: (value) => setIsCheckAll(value), }), [], ); return ( {headerElement} {listItemElements} ); }, ); List.displayName = 'List'; export default List as ( props: ListProps & { ref?: ForwardedRef }, ) => ReturnType>>;