Files
tom goriunovandGitHub 5e80806796 UI toolkit package (#2691)
* first version of the pack

* single entry point

* add custom components to the package

* move form components to toolkit

* add more utils and hooks to the package

* minor fixes

* add CI workflow

* update readme and package.json

* fix ts

* fix build of env-validator

* fix docker build

* fix icons size

* test fixes

* more test fixes
2025-04-21 19:44:22 +02:00

100 lines
2.7 KiB
TypeScript

import { Flex, type FlexProps } from '@chakra-ui/react';
import React from 'react';
import { scroller, Element } from 'react-scroll';
import { useUpdateEffect } from '../hooks/useUpdateEffect';
import type { LinkProps } from './link';
import { Link } from './link';
interface CollapsibleDetailsProps extends LinkProps {
children: React.ReactNode;
id?: string;
isExpanded?: boolean;
text?: [string, string];
noScroll?: boolean;
}
const SCROLL_CONFIG = {
duration: 500,
smooth: true,
};
const CUT_ID = 'CollapsibleDetails';
export const CollapsibleDetails = (props: CollapsibleDetailsProps) => {
const { children, id = CUT_ID, onClick, isExpanded: isExpandedProp = false, text: textProp, loading, noScroll, ...rest } = props;
const [ isExpanded, setIsExpanded ] = React.useState(isExpandedProp);
const handleClick = React.useCallback((event: React.MouseEvent<HTMLAnchorElement>) => {
setIsExpanded((flag) => !flag);
if (!noScroll) {
scroller.scrollTo(id, SCROLL_CONFIG);
}
onClick?.(event);
}, [ id, noScroll, onClick ]);
useUpdateEffect(() => {
setIsExpanded(isExpandedProp);
isExpandedProp && !noScroll && scroller.scrollTo(id, SCROLL_CONFIG);
}, [ isExpandedProp, id, noScroll ]);
const text = isExpanded ? (textProp?.[1] ?? 'Hide details') : (textProp?.[0] ?? 'View details');
return (
<>
<Link
textStyle="sm"
textDecorationLine="underline"
textDecorationStyle="dashed"
w="fit-content"
onClick={ handleClick }
loading={ loading }
{ ...rest }
>
<Element name={ id }>{ text }</Element>
</Link>
{ isExpanded && children }
</>
);
};
interface CollapsibleListProps<T> extends FlexProps {
items: Array<T>;
renderItem: (item: T, index: number) => React.ReactNode;
triggerProps?: LinkProps;
cutLength?: number;
}
export const CollapsibleList = <T,>(props: CollapsibleListProps<T>) => {
const CUT_LENGTH = 3;
const { items, renderItem, triggerProps, cutLength = CUT_LENGTH, ...rest } = props;
const [ isExpanded, setIsExpanded ] = React.useState(false);
const handleToggle = React.useCallback(() => {
setIsExpanded((flag) => !flag);
}, []);
return (
<Flex flexDir="column" w="100%" { ...rest }>
{ items.slice(0, isExpanded ? undefined : cutLength).map(renderItem) }
{ items.length > cutLength && (
<Link
textStyle="sm"
textDecorationLine="underline"
textDecorationStyle="dashed"
w="fit-content"
minW="auto"
onClick={ handleToggle }
{ ...triggerProps }
>
{ isExpanded ? 'Hide' : 'Show all' }
</Link>
) }
</Flex>
);
};