fix(striker-ui): revise List to add more flexibility

main
Tsu-ba-me 2 years ago
parent 81974ef626
commit 0bf25ba693
  1. 153
      striker-ui/components/List.tsx
  2. 2
      striker-ui/components/StrikerConfig/ManageChangedSSHKeysForm.tsx
  3. 42
      striker-ui/types/List.d.ts

@ -9,8 +9,6 @@ import {
List as MUIList,
ListItem as MUIListItem,
ListItemIcon as MUIListItemIcon,
ListItemProps as MUIListItemProps,
ListProps as MUIListProps,
SxProps,
Theme,
} from '@mui/material';
@ -18,7 +16,6 @@ import {
FC,
ForwardedRef,
forwardRef,
ReactNode,
useCallback,
useImperativeHandle,
useMemo,
@ -28,127 +25,52 @@ import { v4 as uuidv4 } from 'uuid';
import { BLUE, GREY, RED } from '../lib/consts/DEFAULT_THEME';
import Checkbox, { CheckboxProps } from './Checkbox';
import Checkbox from './Checkbox';
import Divider from './Divider';
import FlexBox, { FlexBoxProps } from './FlexBox';
import IconButton, { IconButtonProps } from './IconButton';
import FlexBox from './FlexBox';
import IconButton from './IconButton';
import { BodyText } from './Text';
type OnCheckboxChange = Exclude<CheckboxProps['onChange'], undefined>;
type ListOptionalPropsWithDefaults<T extends unknown = unknown> = {
allowCheckAll?: boolean;
allowEdit?: boolean;
edit?: boolean;
initialCheckAll?: boolean;
insertHeader?: boolean;
listItemKeyPrefix?: string;
listItemProps?: MUIListItemProps;
listProps?: MUIListProps;
renderListItem?: (key: string, value: T) => ReactNode;
scroll?: boolean;
};
type ListOptionalPropsWithoutDefaults<T extends unknown = unknown> = {
allowAddItem?: boolean;
allowCheckItem?: boolean;
allowDelete?: boolean;
allowEditItem?: boolean;
header?: ReactNode;
listEmpty?: ReactNode;
onAdd?: IconButtonProps['onClick'];
onDelete?: IconButtonProps['onClick'];
onEdit?: IconButtonProps['onClick'];
onAllCheckboxChange?: CheckboxProps['onChange'];
onItemCheckboxChange?: (
key: string,
...onChangeParams: Parameters<OnCheckboxChange>
) => ReturnType<OnCheckboxChange>;
renderListItemCheckboxState?: (key: string, value: T) => boolean;
};
type ListOptionalProps<T extends unknown = unknown> =
ListOptionalPropsWithDefaults<T> & ListOptionalPropsWithoutDefaults<T>;
type ListProps<T extends unknown = unknown> = FlexBoxProps &
ListOptionalProps<T> & {
listItems: Record<string, T>;
};
type ListForwardedRefContent = {
setCheckAll?: (value: boolean) => void;
};
const HEADER_SPACING = '.3em';
const LIST_DEFAULT_PROPS: Required<ListOptionalPropsWithDefaults> &
ListOptionalPropsWithoutDefaults = {
header: undefined,
allowAddItem: undefined,
allowCheckAll: false,
allowCheckItem: undefined,
allowDelete: undefined,
allowEdit: false,
allowEditItem: undefined,
edit: false,
initialCheckAll: false,
insertHeader: true,
listEmpty: undefined,
listItemKeyPrefix: uuidv4(),
listItemProps: {},
listProps: {},
onAdd: undefined,
onDelete: undefined,
onEdit: undefined,
onAllCheckboxChange: undefined,
onItemCheckboxChange: undefined,
renderListItem: (key) => <BodyText>{key}</BodyText>,
renderListItemCheckboxState: undefined,
scroll: false,
};
const LIST_ICON_MIN_WIDTH = '56px';
const CHECK_ALL_MIN_WIDTH = `calc(${LIST_ICON_MIN_WIDTH} - ${HEADER_SPACING})`;
const List = forwardRef(
<T,>(
{
allowCheckAll: isAllowCheckAll = false,
allowEdit: isAllowEdit = false,
edit: isEdit = false,
flexBoxProps,
header,
allowCheckAll: isAllowCheckAll = LIST_DEFAULT_PROPS.allowCheckAll,
allowEdit: isAllowEdit = LIST_DEFAULT_PROPS.allowEdit,
edit: isEdit = LIST_DEFAULT_PROPS.edit,
initialCheckAll = LIST_DEFAULT_PROPS.initialCheckAll,
insertHeader: isInsertHeader = LIST_DEFAULT_PROPS.insertHeader,
listEmpty = LIST_DEFAULT_PROPS.listEmpty,
listItemKeyPrefix = LIST_DEFAULT_PROPS.listItemKeyPrefix,
listItemProps: {
sx: listItemSx,
...restListItemProps
} = LIST_DEFAULT_PROPS.listItemProps,
headerSpacing = '.3em',
initialCheckAll = false,
insertHeader: isInsertHeader = true,
listEmpty,
listItemIconMinWidth = '56px',
listItemKeyPrefix = uuidv4(),
listItemProps: { sx: listItemSx, ...restListItemProps } = {},
listItems,
listProps: {
sx: listSx,
...restListProps
} = LIST_DEFAULT_PROPS.listProps,
listProps: { sx: listSx, ...restListProps } = {},
onAdd,
onDelete,
onEdit,
onAllCheckboxChange,
onItemCheckboxChange,
renderListItem = LIST_DEFAULT_PROPS.renderListItem,
renderListItem = (key) => <BodyText>{key}</BodyText>,
renderListItemCheckboxState,
scroll: isScroll = LIST_DEFAULT_PROPS.scroll,
scroll: isScroll = false,
// Input props that depend on other input props.
allowAddItem: isAllowAddItem = isAllowEdit,
allowCheckItem: isAllowCheckItem = isAllowEdit,
allowDelete: isAllowDelete = isAllowEdit,
allowEditItem: isAllowEditItem = isAllowEdit,
...rootProps
}: ListProps<T>,
ref: ForwardedRef<ListForwardedRefContent>,
) => {
const [isCheckAll, setIsCheckAll] = useState<boolean>(initialCheckAll);
const checkAllMinWidth = useMemo(
() => `calc(${listItemIconMinWidth} - ${headerSpacing})`,
[headerSpacing, listItemIconMinWidth],
);
const addItemButton = useMemo(
() =>
isAllowAddItem ? (
@ -192,7 +114,7 @@ const List = forwardRef(
if (isEdit && isAllowCheckItem) {
element = isAllowCheckAll ? (
<MUIBox sx={{ minWidth: CHECK_ALL_MIN_WIDTH }}>
<MUIBox sx={{ minWidth: checkAllMinWidth }}>
<Checkbox
checked={isCheckAll}
edge="start"
@ -205,12 +127,13 @@ const List = forwardRef(
/>
</MUIBox>
) : (
<Divider sx={{ minWidth: CHECK_ALL_MIN_WIDTH }} />
<Divider sx={{ minWidth: checkAllMinWidth }} />
);
}
return element;
}, [
checkAllMinWidth,
isAllowCheckAll,
isAllowCheckItem,
isCheckAll,
@ -220,7 +143,7 @@ const List = forwardRef(
const headerElement = useMemo(
() =>
isInsertHeader && header ? (
<FlexBox row spacing={HEADER_SPACING} sx={{ height: '2.4em' }}>
<FlexBox row spacing={headerSpacing} sx={{ height: '2.4em' }}>
{checkAllElement}
{typeof header === 'string' ? (
<>
@ -243,6 +166,7 @@ const List = forwardRef(
deleteItemButton,
editItemButton,
header,
headerSpacing,
isInsertHeader,
],
);
@ -259,7 +183,7 @@ const List = forwardRef(
const listItemCheckbox = useCallback(
(key: string, checked?: boolean) =>
isEdit && isAllowCheckItem ? (
<MUIListItemIcon sx={{ minWidth: LIST_ICON_MIN_WIDTH }}>
<MUIListItemIcon sx={{ minWidth: listItemIconMinWidth }}>
<Checkbox
checked={checked}
edge="start"
@ -269,14 +193,17 @@ const List = forwardRef(
/>
</MUIListItemIcon>
) : undefined,
[isAllowCheckItem, isEdit, onItemCheckboxChange],
[isAllowCheckItem, isEdit, listItemIconMinWidth, onItemCheckboxChange],
);
const listItemElements = useMemo(() => {
let result = listEmptyElement;
if (listItems) {
const entries = Object.entries(listItems);
return entries.length > 0
? entries.map(([key, value]) => (
if (entries.length > 0) {
result = entries.map(([key, value]) => (
<MUIListItem
{...restListItemProps}
key={`${listItemKeyPrefix}-${key}`}
@ -288,8 +215,11 @@ const List = forwardRef(
)}
{renderListItem(key, value)}
</MUIListItem>
))
: listEmptyElement;
));
}
}
return result;
}, [
listEmptyElement,
listItemCheckbox,
@ -314,7 +244,7 @@ const List = forwardRef(
);
return (
<FlexBox spacing={0} {...rootProps}>
<FlexBox spacing={0} {...flexBoxProps}>
{headerElement}
<MUIList
{...restListProps}
@ -327,11 +257,8 @@ const List = forwardRef(
},
);
List.defaultProps = LIST_DEFAULT_PROPS;
List.displayName = 'List';
export type { ListForwardedRefContent, ListProps };
export default List as <T>(
props: ListProps<T> & { ref?: ForwardedRef<ListForwardedRefContent> },
) => ReturnType<FC<ListProps<T>>>;

@ -2,7 +2,7 @@ import { FC, useMemo, useRef } from 'react';
import Divider from '../Divider';
import FlexBox from '../FlexBox';
import List, { ListForwardedRefContent } from '../List';
import List from '../List';
import MessageBox from '../MessageBox';
import { ExpandablePanel } from '../Panels';
import { BodyText } from '../Text';

@ -0,0 +1,42 @@
type OnCheckboxChange = Exclude<
import('../components/Checkbox').CheckboxProps['onChange'],
undefined
>;
type ListOptionalProps<T extends unknown = unknown> = {
allowCheckAll?: boolean;
allowAddItem?: boolean;
allowCheckItem?: boolean;
allowEdit?: boolean;
allowDelete?: boolean;
allowEditItem?: boolean;
edit?: boolean;
flexBoxProps?: import('../components/FlexBox').FlexBoxProps;
header?: import('react').ReactNode;
headerSpacing?: number | string;
initialCheckAll?: boolean;
insertHeader?: boolean;
listEmpty?: import('react').ReactNode;
listItemIconMinWidth?: number | string;
listItemKeyPrefix?: string;
listItemProps?: import('@mui/material').ListItemProps;
listItems?: Record<string, T>;
listProps?: import('@mui/material').ListProps;
onAdd?: import('../components/IconButton').IconButtonProps['onClick'];
onDelete?: import('../components/IconButton').IconButtonProps['onClick'];
onEdit?: import('../components/IconButton').IconButtonProps['onClick'];
onAllCheckboxChange?: OnCheckboxChange;
onItemCheckboxChange?: (
key: string,
...onChangeParams: Parameters<OnCheckboxChange>
) => ReturnType<OnCheckboxChange>;
renderListItem?: (key: string, value: T) => import('react').ReactNode;
renderListItemCheckboxState?: (key: string, value: T) => boolean;
scroll?: boolean;
};
type ListProps<T extends unknown = unknown> = ListOptionalProps<T>;
type ListForwardedRefContent = {
setCheckAll?: (value: boolean) => void;
};
Loading…
Cancel
Save