Skip to content

Commit

Permalink
feat: add simple examples in doc, fix some review issues
Browse files Browse the repository at this point in the history
  • Loading branch information
IsaevAlexandr committed Jan 17, 2024
1 parent 432f036 commit b5f8772
Show file tree
Hide file tree
Showing 12 changed files with 199 additions and 41 deletions.
6 changes: 6 additions & 0 deletions src/components/TreeSelect/TreeSelect.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
$block: '.#{variables.$ns}tree-select';

#{$block} {
max-width: 100%;

&_width_max {
width: 100%;
}

&__popup {
overflow: hidden;
min-width: 300px;
Expand Down
24 changes: 21 additions & 3 deletions src/components/TreeSelect/TreeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
useListKeydown,
useListState,
} from '../useList';
import {block} from '../utils/cn';
import {type CnMods, block} from '../utils/cn';

import {TreeListContainer} from './components/TreeListContainer/TreeListContainer';
import {useTreeSelectSelection, useValue} from './hooks/useTreeSelectSelection';
Expand All @@ -37,6 +37,8 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(
size = 'm',
items,
defaultOpen,
className,
width,
popupClassName,
open: propsOpen,
multiple,
Expand Down Expand Up @@ -224,8 +226,24 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(
/>
);

const mods: CnMods = {
...(width === 'max' && {width}),
};

const inlineStyles: React.CSSProperties = {};

if (typeof width === 'number') {
inlineStyles.width = width;
}

return (
<Flex direction="column" gap="5" ref={controlWrapRef} className={b()}>
<Flex
direction="column"
gap="5"
ref={controlWrapRef}
className={b(mods, className)}
style={inlineStyles}
>
{togglerNode}
<SelectPopup
ref={controlWrapRef}
Expand Down Expand Up @@ -255,7 +273,7 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(
});

// assign components scope logic
state.selectable = context.groupState
state.hasSelectionIcon = context.groupState
? groupsBehavior === 'selectable'
: undefined;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
WithItemLinksAndActionsExampleProps,
} from './components/WithItemLinksAndActionsExample';

// TODO: пример с кастомной структурой данных
export default {
title: 'Unstable/TreeSelect',
component: TreeSelect,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {Flex, spacing} from '../../../layout';
import {useListFilter} from '../../../useList';
import {createRandomizedData} from '../../../useList/__stories__/utils/makeData';
import {TreeSelect} from '../../TreeSelect';
import type {TreeSelectProps} from '../../types';
import type {RenderContainerProps, TreeSelectProps} from '../../types';

import {RenderVirtualizedContainer} from './RenderVirtualizedContainer';

Expand All @@ -23,7 +23,23 @@ export const WithFiltrationAndControlsExample = ({
itemsCount = 5,
...props
}: WithFiltrationAndControlsExampleProps) => {
const items = React.useMemo(() => createRandomizedData({num: itemsCount}), [itemsCount]);
const {items, renderContainer} = React.useMemo(() => {
const baseItems = createRandomizedData({num: itemsCount});
const containerRenderer = (props: RenderContainerProps<{title: string}>) => {
if (props.items.length === 0 && baseItems.length > 0) {
return (
<Flex centerContent className={spacing({p: 2})} height="300px">
<Text variant="subheader-1">Nothing found</Text>
</Flex>
);
}

return <RenderVirtualizedContainer {...props} />;
};

return {items: baseItems, renderContainer: containerRenderer};
}, [itemsCount]);

const [open, onOpenChange] = React.useState(true);
const [value, setValue] = React.useState<string[]>([]);
const filterState = useListFilter({items});
Expand All @@ -48,17 +64,7 @@ export const WithFiltrationAndControlsExample = ({
ref={filterState.filterRef}
/>
}
renderContainer={(props) => {
if (props.items.length === 0 && items.length > 0) {
return (
<Flex centerContent className={spacing({p: 2})} height="300px">
<Text variant="subheader-1">Nothing found</Text>
</Flex>
);
}

return <RenderVirtualizedContainer {...props} />;
}}
renderContainer={renderContainer}
slotAfterListBody={
<Flex gap="2" className={spacing({p: 2})}>
<Button
Expand Down
2 changes: 2 additions & 0 deletions src/components/TreeSelect/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ interface TreeSelectBaseProps<T> extends QAProps, Partial<Omit<ListState, 'selec
id?: string | undefined;
popupClassName?: string;
popupWidth?: number;
width?: 'auto' | 'max' | number;
className?: string;
popupDisablePortal?: boolean;
multiple?: boolean;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const FlattenList = ({itemsCount, size}: FlattenListProps) => {
<ListItemView
{...state}
{...item}
selectable={!listContext.groupState}
hasSelectionIcon={!listContext.groupState}
/>
);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const PopupWithTogglerList = ({size, itemsCount}: PopupWithTogglerListPro
<ListItemView
{...state}
{...data}
selectable={!listContext.groupState}
hasSelectionIcon={!listContext.groupState}
/>
);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const RecursiveList = ({size, itemsCount}: RecursiveListProps) => {
<ListItemView
{...state}
{...data}
selectable={!listContext.groupState}
hasSelectionIcon={!listContext.groupState}
/>
);
}}
Expand Down
109 changes: 103 additions & 6 deletions src/components/useList/__stories__/useList.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,124 @@ The basic idea is that hooks take all the complex logic on themselves, and all y

`Storybook` provides complex examples how to use this components from this documentation.

### Hooks
### Hooks:

- [useList](#uselist-1);
- [useListKeydown](#uselistkeydown)
- [useListFilter](#uselistfilter);
- [useListState](#useliststate);

### Components (View only);
### Components (View only):

- [ListItemView](#listitemview);
- [ListContainerView](#listcontainerview);
- [ListRecursiveRenderer](#listrecursiverenderer);

### Utilitys
### Utilitys:

- [computeItemSize](#computeitemsize);
- [scrollToListItem](#scrolltolistitem);
- [getItemRenderState](#getitemrenderstate);
- [getListParsedState](#getlistparsedstate);

## Hooks
## Quick code snippets for beginners:

### flatten items:

```tsx
import {
ListContainerView,
type ListItemId,
type ListItemType,
ListItemView,
getItemRenderState,
useList,
useListKeydown,
useListState,
} from '@gravity-ui/uikit/unstable';

const items: ListItemType<string>[] = ['one', 'two', 'free', 'four', 'five'];

function List() {
const containerRef = React.useRef<HTMLDivElement>(null);

const listState = useListState();
const parsedListState = useList({
items,
expandedById: listState.expandedById,
});

useListKeydown({
onItemClick,
containerRef,
...parsedListState,
...listState,
});

return (
<ListContainerView ref={containerRef}>
{parsedListState.items.map((_, i) => {
const [itemData, computedItemProps, _listContext] = getItemRenderState({
id: String(i),
onItemClick,
...parsedListState,
...listState,
});

return <ListItemView key={i} {...computedItemProps} title={itemData} />;
})}
</ListContainerView>
);

// multiple selection
function onItemClick(id: ListItemId) {
listState.setSelected((prevState) => ({
...prevState,
[id]: !prevState[id],
}));

listState.setActiveItemId(id);
}
}
```

### tree item structure:

```tsx
const items: ListItemType<string>[] = [
{data: 'one'},
{data: 'two', children: [{data: 'tree', children: [{data: 'four'}, {data: 'five'}]}]},
];

function List() {
// same as prev example
return (
<ListContainerView ref={containerRef}>
{parsedListState.items.map((item, index) => (
<ListItemRecursiveRenderer
key={index}
itemSchema={item}
index={index}
expandedById={expandedById}
>
{(id) => {
const [itemData, computedItemProps, _listContext] = getItemRenderState({
id: String(i),
onItemClick,
...parsedListState,
...listState,
});

return <ListItemView {...computedItemProps} title={itemData} />;
}}
</ListItemRecursiveRenderer>
))}
</ListContainerView>
);
}
```

## Hooks:

### useList

Expand Down Expand Up @@ -216,7 +313,7 @@ const {
} = useListState();
```

## Components
## Components:

### ListItemView

Expand All @@ -235,7 +332,7 @@ Use it even if the functionality of the `useList` hook seems redundant to you
- `disabled` - The disabled state. It also prevents clicking on an element;
- `activeOnHover`- By default hovered elements has active styles. You can disable this behavior;
- `indentation` - Build in indentation component to render nested views structure;
- `selectable` - Show selected icon if selected and reserve space for this icon;
- `hasSelectionIcon` - Show selected icon if selected and reserve space for this icon;
- `onClick` - on item click callback. !Note: if passed this and `disabled` option is `true` click will not be appear;
- `style` - optional react `React.CSSProperties` object;
- `subtitle` - Slot under `title`. If passed string apply prefefined styles. Or you can pass custom `React.ReactNode` to use you own behaviour;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export interface ListItemViewProps extends QAProps, Omit<React.HTMLAttributes<'l
/**
* Show selected icon if selected and reserve space for this icon
*/
selectable?: boolean;
hasSelectionIcon?: boolean;
/**
* Note: if passed and `disabled` option is `true` click will not be appear
*/
Expand Down Expand Up @@ -90,7 +90,7 @@ export const ListItemView = React.forwardRef(
corners = true,
activeOnHover = true,
className,
selectable = true,
hasSelectionIcon = true,
indentation,
startSlot,
subtitle,
Expand All @@ -114,7 +114,7 @@ export const ListItemView = React.forwardRef(
className={b(
{
active,
selected,
selected: selected && !hasSelectionIcon,
activeOnHover,
clickable: Boolean(onClick),
},
Expand All @@ -133,7 +133,7 @@ export const ListItemView = React.forwardRef(
{...rest}
>
<Flex gap="2" alignItems="center">
{selectable && (
{hasSelectionIcon && (
<Slot>
{selected ? (
<Icon
Expand Down
Loading

0 comments on commit b5f8772

Please sign in to comment.