fix(striker-ui): allow number in indicator in IconWithIndicator

main
Tsu-ba-me 2 years ago
parent 0593338b01
commit d6920acb4f
  1. 162
      striker-ui/components/IconWithIndicator.tsx

@ -4,15 +4,29 @@ import {
BoxProps as MUIBoxProps,
SvgIconProps,
} from '@mui/material';
import { createElement, FC } from 'react';
import {
createElement,
forwardRef,
ReactNode,
useCallback,
useImperativeHandle,
useMemo,
} from 'react';
import { BLACK, BLUE } from '../lib/consts/DEFAULT_THEME';
import FlexBox, { FlexBoxProps } from './FlexBox';
import { BodyText, BodyTextProps } from './Text';
import useProtect from '../hooks/useProtect';
import useProtectedState from '../hooks/useProtectedState';
type IndicatorValue = boolean | number;
type IconWithIndicatorOptionalPropsWithDefault = {
iconProps?: SvgIconProps;
indicatorProps?: FlexBoxProps;
indicatorTextProps?: BodyTextProps;
initialIndicatorValue?: IndicatorValue;
};
type IconWithIndicatorOptionalProps = IconWithIndicatorOptionalPropsWithDefault;
@ -22,13 +36,28 @@ type IconWithIndicatorProps = MUIBoxProps &
icon: SvgIconComponent;
};
type IconWithIndicatorForwardedRefContent = {
indicate?: (value: IndicatorValue) => void;
};
const CONTAINER_LENGTH = '1.7em';
const ICON_WITH_INDICATOR_DEFAULT_PROPS: Required<IconWithIndicatorOptionalPropsWithDefault> =
{
iconProps: {},
indicatorProps: {},
indicatorTextProps: {},
initialIndicatorValue: false,
};
const IconWithIndicator: FC<IconWithIndicatorProps> = ({
const INDICATOR_LENGTH = { small: '24%', medium: '50%' };
const INDICATOR_MAX = 99;
const INDICATOR_OFFSET = { small: '.1rem', medium: '0rem' };
const IconWithIndicator = forwardRef<
IconWithIndicatorForwardedRefContent,
IconWithIndicatorProps
>(
(
{
icon,
iconProps: {
sx: iconSx,
@ -40,37 +69,36 @@ const IconWithIndicator: FC<IconWithIndicatorProps> = ({
...restIndicatorProps
} = ICON_WITH_INDICATOR_DEFAULT_PROPS.indicatorProps,
sx,
}) => {
const containerLength = '1.7em';
const indicatorLength = '24%';
const indicatorOffset = '.1rem';
indicatorTextProps: {
sx: indicatorTextSx,
return (
<MUIBox
...restIndicatorTextProps
} = ICON_WITH_INDICATOR_DEFAULT_PROPS.indicatorTextProps,
initialIndicatorValue = ICON_WITH_INDICATOR_DEFAULT_PROPS.initialIndicatorValue,
sx,
},
ref,
) => {
const { protect } = useProtect();
const [indicatorValue, setIndicatorValue] = useProtectedState<
boolean | number
>(initialIndicatorValue, protect);
const buildIndicator = useCallback(
(
indicatorContent: ReactNode,
indicatorLength: number | string,
indicatorOffset: number | string,
) => (
<FlexBox
row
{...restIndicatorProps}
sx={{
height: containerLength,
width: containerLength,
position: 'relative',
...sx,
}}
>
{createElement(icon, {
...restIconProps,
sx: { height: '100%', width: '100%', ...iconSx },
})}
{createElement(FlexBox, {
row: true,
...restIndicatorProps,
sx: {
backgroundColor: BLUE,
borderColor: BLACK,
borderRadius: '50%',
borderStyle: 'solid',
borderWidth: '.2rem',
borderWidth: '.1em',
bottom: indicatorOffset,
boxSizing: 'content-box',
height: 0,
@ -81,12 +109,86 @@ const IconWithIndicator: FC<IconWithIndicatorProps> = ({
width: indicatorLength,
...indicatorSx,
},
}}
>
{indicatorContent}
</FlexBox>
),
[indicatorSx, restIndicatorProps],
);
const buildIndicatorText = useCallback(
(value: IndicatorValue) => (
<BodyText
{...restIndicatorTextProps}
sx={{
fontSize: '.7rem',
fontWeight: '700',
paddingTop: '100%',
...indicatorTextSx,
}}
>
{value > INDICATOR_MAX ? `${INDICATOR_MAX}+` : value}
</BodyText>
),
[indicatorTextSx, restIndicatorTextProps],
);
const indicator = useMemo(() => {
let result;
if (indicatorValue) {
let indicatorContent;
let indicatorLength = INDICATOR_LENGTH.small;
let indicatorOffset = INDICATOR_OFFSET.small;
if (Number.isFinite(indicatorValue)) {
indicatorContent = buildIndicatorText(indicatorValue);
indicatorLength = INDICATOR_LENGTH.medium;
indicatorOffset = INDICATOR_OFFSET.medium;
}
result = buildIndicator(
indicatorContent,
indicatorLength,
indicatorOffset,
);
}
return result;
}, [buildIndicator, buildIndicatorText, indicatorValue]);
useImperativeHandle(
ref,
() => ({
indicate: (value) => setIndicatorValue(value),
}),
[setIndicatorValue],
);
return (
<MUIBox
sx={{
height: CONTAINER_LENGTH,
width: CONTAINER_LENGTH,
position: 'relative',
...sx,
}}
>
{createElement(icon, {
...restIconProps,
sx: { height: '100%', width: '100%', ...iconSx },
})}
{indicator}
</MUIBox>
);
};
},
);
IconWithIndicator.defaultProps = ICON_WITH_INDICATOR_DEFAULT_PROPS;
IconWithIndicator.displayName = 'IconWithIndicator';
export type { IconWithIndicatorForwardedRefContent, IconWithIndicatorProps };
export default IconWithIndicator;

Loading…
Cancel
Save