From 53ae8ecdbd0ccd5aa4368a9016bc9bdc1cf96e69 Mon Sep 17 00:00:00 2001 From: Tsu-ba-me Date: Tue, 28 Feb 2023 01:31:18 -0500 Subject: [PATCH] fix(striker-ui): make IconButton change based on state --- .../components/IconButton/IconButton.tsx | 121 ++++++++++++++---- striker-ui/types/IconButton.d.ts | 16 +++ 2 files changed, 109 insertions(+), 28 deletions(-) create mode 100644 striker-ui/types/IconButton.d.ts diff --git a/striker-ui/components/IconButton/IconButton.tsx b/striker-ui/components/IconButton/IconButton.tsx index fb22451a..2782db4a 100644 --- a/striker-ui/components/IconButton/IconButton.tsx +++ b/striker-ui/components/IconButton/IconButton.tsx @@ -1,9 +1,16 @@ -import { FC } from 'react'; +import { + Done as MUIDoneIcon, + Edit as MUIEditIcon, + Visibility as MUIVisibilityIcon, + VisibilityOff as MUIVisibilityOffIcon, +} from '@mui/icons-material'; import { IconButton as MUIIconButton, IconButtonProps as MUIIconButtonProps, inputClasses as muiInputClasses, + styled, } from '@mui/material'; +import { createElement, FC, ReactNode, useMemo } from 'react'; import { BLACK, @@ -13,35 +20,93 @@ import { TEXT, } from '../../lib/consts/DEFAULT_THEME'; -export type IconButtonProps = MUIIconButtonProps; +type IconButtonProps = IconButtonOptionalProps & MUIIconButtonProps; + +const ContainedIconButton = styled(MUIIconButton)({ + borderRadius: BORDER_RADIUS, + backgroundColor: GREY, + color: BLACK, + + '&:hover': { + backgroundColor: TEXT, + }, + + [`&.${muiInputClasses.disabled}`]: { + backgroundColor: DISABLED, + }, +}); + +const NormalIconButton = styled(MUIIconButton)({ + color: GREY, +}); + +const MAP_TO_VISIBILITY_ICON: IconButtonMapToStateIcon = { + false: MUIVisibilityIcon, + true: MUIVisibilityOffIcon, +}; + +const MAP_TO_EDIT_ICON: IconButtonMapToStateIcon = { + false: MUIEditIcon, + true: MUIDoneIcon, +}; + +const MAP_TO_MAP_PRESET: Record< + IconButtonPresetMapToStateIcon, + IconButtonMapToStateIcon +> = { + edit: MAP_TO_EDIT_ICON, + visibility: MAP_TO_VISIBILITY_ICON, +}; + +const MAP_TO_VARIANT: Record = { + contained: ContainedIconButton, + normal: NormalIconButton, +}; const IconButton: FC = ({ children, - sx, - ...iconButtonRestProps -}) => ( - - {children} - -); + defaultIcon, + iconProps, + mapPreset, + mapToIcon: externalMapToIcon, + state, + variant = 'contained', + ...restIconButtonProps +}) => { + const mapToIcon = useMemo( + () => externalMapToIcon ?? (mapPreset && MAP_TO_MAP_PRESET[mapPreset]), + [externalMapToIcon, mapPreset], + ); + + const iconButtonContent = useMemo(() => { + let result: ReactNode; + + if (mapToIcon) { + const iconElementType: CreatableComponent | undefined = state + ? mapToIcon[state] ?? defaultIcon + : defaultIcon; + + if (iconElementType) { + result = createElement(iconElementType, iconProps); + } + } else { + result = children; + } + + return result; + }, [children, mapToIcon, state, defaultIcon, iconProps]); + const iconButtonElementType = useMemo( + () => MAP_TO_VARIANT[variant], + [variant], + ); + + return createElement( + iconButtonElementType, + restIconButtonProps, + iconButtonContent, + ); +}; + +export type { IconButtonProps }; export default IconButton; diff --git a/striker-ui/types/IconButton.d.ts b/striker-ui/types/IconButton.d.ts new file mode 100644 index 00000000..af8ecf5a --- /dev/null +++ b/striker-ui/types/IconButton.d.ts @@ -0,0 +1,16 @@ +type CreatableComponent = Parameters[0]; + +type IconButtonPresetMapToStateIcon = 'edit' | 'visibility'; + +type IconButtonMapToStateIcon = Record; + +type IconButtonVariant = 'contained' | 'normal'; + +type IconButtonOptionalProps = { + defaultIcon?: CreatableComponent; + iconProps?: import('@mui/material').SvgIconProps; + mapPreset?: IconButtonPresetMapToStateIcon; + mapToIcon?: IconButtonMapToStateIcon; + state?: string; + variant?: IconButtonVariant; +};