diff --git a/src/components/TreeSelect/TreeSelect.tsx b/src/components/TreeSelect/TreeSelect.tsx index c5a56f57c3..7de1fce9d6 100644 --- a/src/components/TreeSelect/TreeSelect.tsx +++ b/src/components/TreeSelect/TreeSelect.tsx @@ -20,7 +20,7 @@ import type {CnMods} from '../utils/cn'; import {TreeSelectItem} from './TreeSelectItem'; import {TreeListContainer} from './components/TreeListContainer/TreeListContainer'; import {useTreeSelectSelection, useValue} from './hooks/useTreeSelectSelection'; -import type {RenderControlProps, TreeSelectProps} from './types'; +import type {TreeSelectProps, TreeSelectRenderControlProps} from './types'; import './TreeSelect.scss'; @@ -193,7 +193,7 @@ export const TreeSelect = React.forwardRef(function TreeSelect( const handleClose = React.useCallback(() => toggleOpen(false), [toggleOpen]); - const controlProps: RenderControlProps = { + const controlProps: TreeSelectRenderControlProps = { open, toggleOpen, clearValue: handleClearValue, @@ -282,13 +282,13 @@ export const TreeSelect = React.forwardRef(function TreeSelect( Boolean(multiple) && !renderState.context.groupState; if (renderItem) { - return renderItem( - renderState.data, - renderState.props, - renderState.context, + return renderItem({ + data: renderState.data, + props: renderState.props, + itemState: renderState.context, index, - renderContextProps, - ); + renderContext: renderContextProps, + }); } const itemData = listParsedState.itemsById[id]; diff --git a/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx b/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx index 5cfee9dddd..142ea642ae 100644 --- a/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx +++ b/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx @@ -18,10 +18,13 @@ export interface InfinityScrollExampleProps itemsCount?: number; } -export const InfinityScrollExample = ({itemsCount = 5, ...props}: InfinityScrollExampleProps) => { +export const InfinityScrollExample = ({ + itemsCount = 5, + ...storyProps +}: InfinityScrollExampleProps) => { const [value, setValue] = React.useState([]); const { - data = [], + data: items = [], onFetchMore, canFetchMore, isLoading, @@ -30,14 +33,14 @@ export const InfinityScrollExample = ({itemsCount = 5, ...props}: InfinityScroll return ( - {...props} - items={data} + {...storyProps} + items={items} value={value} - renderItem={(item, state, {isLastItem, groupState}) => { + renderItem={({data, props, itemState: {isLastItem, groupState}}) => { const node = ( {groupState.childrenIds.length} diff --git a/src/components/TreeSelect/__stories__/components/RenderVirtualizedContainer.tsx b/src/components/TreeSelect/__stories__/components/RenderVirtualizedContainer.tsx index c3b80e6f97..6df91efc98 100644 --- a/src/components/TreeSelect/__stories__/components/RenderVirtualizedContainer.tsx +++ b/src/components/TreeSelect/__stories__/components/RenderVirtualizedContainer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {ListContainerView, computeItemSize} from '../../../useList'; import {VirtualizedListContainer} from '../../../useList/__stories__/components/VirtualizedListContainer'; -import type {RenderContainerProps} from '../../types'; +import type {TreeSelectRenderContainerProps} from '../../types'; // custom container renderer example export const RenderVirtualizedContainer = ({ @@ -11,7 +11,7 @@ export const RenderVirtualizedContainer = ({ visibleFlattenIds, renderItem, size, -}: RenderContainerProps) => { +}: TreeSelectRenderContainerProps) => { return ( title, }).map(({data}, idx) => ({someRandomKey: data, id: String(idx)})); -export const WithDndListExample = (props: WithDndListExampleProps) => { +export const WithDndListExample = (storyProps: WithDndListExampleProps) => { const [items, setItems] = React.useState(randomItems); const [value, setValue] = React.useState([]); @@ -61,7 +61,7 @@ export const WithDndListExample = (props: WithDndListExampleProps) => { return ( { ); }} - renderItem={(item, state, _context, index, renderContextProps) => { + renderItem={({data, props, index, renderContext: renderContextProps}) => { const commonProps = { - ...state, + ...props, // selected: value.initem.uniqId, - title: item.someRandomKey, + title: data.someRandomKey, endSlot: , }; diff --git a/src/components/TreeSelect/__stories__/components/WithFiltrationAndControlsExample.tsx b/src/components/TreeSelect/__stories__/components/WithFiltrationAndControlsExample.tsx index 0353d4f837..4c09c00afd 100644 --- a/src/components/TreeSelect/__stories__/components/WithFiltrationAndControlsExample.tsx +++ b/src/components/TreeSelect/__stories__/components/WithFiltrationAndControlsExample.tsx @@ -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 {RenderContainerProps, TreeSelectProps} from '../../types'; +import type {TreeSelectProps, TreeSelectRenderContainerProps} from '../../types'; import {RenderVirtualizedContainer} from './RenderVirtualizedContainer'; @@ -21,11 +21,11 @@ export interface WithFiltrationAndControlsExampleProps export const WithFiltrationAndControlsExample = ({ itemsCount = 5, - ...props + ...treeSelectProps }: WithFiltrationAndControlsExampleProps) => { const {items, renderContainer} = React.useMemo(() => { const baseItems = createRandomizedData({num: itemsCount}); - const containerRenderer = (props: RenderContainerProps<{title: string}>) => { + const containerRenderer = (props: TreeSelectRenderContainerProps<{title: string}>) => { if (props.items.length === 0 && baseItems.length > 0) { return ( @@ -47,13 +47,12 @@ export const WithFiltrationAndControlsExample = ({ return ( { + itemState: {groupState}, + }) => { return ( } diff --git a/src/components/TreeSelect/__stories__/components/WithItemLinksAndActionsExample.tsx b/src/components/TreeSelect/__stories__/components/WithItemLinksAndActionsExample.tsx index 8b71822585..edb7920367 100644 --- a/src/components/TreeSelect/__stories__/components/WithItemLinksAndActionsExample.tsx +++ b/src/components/TreeSelect/__stories__/components/WithItemLinksAndActionsExample.tsx @@ -42,14 +42,14 @@ export const WithItemLinksAndActionsExample = (props: WithItemLinksAndActionsExa setOpen((x) => !x); }} expandedById={expandedById} - renderItem={( - item, - { + renderItem={({ + data, + props: { expanded, // don't use build in expand icon ListItemView behavior ...state }, - {groupState}, - ) => { + itemState: {groupState}, + }) => { return ( // eslint-disable-next-line jsx-a11y/anchor-is-valid ({ renderItem, className, idToFlattenIndex, -}: RenderContainerProps & {className?: string}) => { +}: TreeSelectRenderContainerProps & {className?: string}) => { return ( {items.map((itemSchema, index) => ( diff --git a/src/components/TreeSelect/index.ts b/src/components/TreeSelect/index.ts index 25188255a9..b6d9beb5f3 100644 --- a/src/components/TreeSelect/index.ts +++ b/src/components/TreeSelect/index.ts @@ -1,3 +1,3 @@ export {TreeSelect} from './TreeSelect'; export {TreeSelectItem, type TreeSelectItemProps} from './TreeSelectItem'; -export type {TreeSelectProps, RenderItem} from './types'; +export type {TreeSelectProps, TreeSelectRenderItem} from './types'; diff --git a/src/components/TreeSelect/types.ts b/src/components/TreeSelect/types.ts index 79c22ae5a7..56402d0098 100644 --- a/src/components/TreeSelect/types.ts +++ b/src/components/TreeSelect/types.ts @@ -13,7 +13,7 @@ import type { RenderItemState, } from '../useList'; -export type RenderControlProps = { +export type TreeSelectRenderControlProps = { open: boolean; toggleOpen(): void; clearValue(): void; @@ -24,17 +24,17 @@ export type RenderControlProps = { activeItemId?: ListItemId; }; -export type RenderItem = ( - item: T, +export type TreeSelectRenderItem = (props: { + data: T; // required item props to render - state: RenderItemState, + props: RenderItemState; // internal list context props - context: RenderItemContext, - index: number, - renderContextProps?: Object, -) => React.JSX.Element; + itemState: RenderItemContext; + index: number; + renderContext?: P; +}) => React.JSX.Element; -export type RenderContainerProps = ListParsedState & +export type TreeSelectRenderContainerProps = ListParsedState & ListState & { id: string; size: ListItemSize; @@ -43,7 +43,9 @@ export type RenderContainerProps = ListParsedState & className?: string; }; -export type RenderTreeListContainer = (props: RenderContainerProps) => React.JSX.Element; +export type TreeSelectRenderContainer = ( + props: TreeSelectRenderContainerProps, +) => React.JSX.Element; interface TreeSelectBaseProps extends QAProps, Partial> { value?: ListItemId[]; @@ -88,15 +90,15 @@ interface TreeSelectBaseProps extends QAProps, Partial; + renderItem?: TreeSelectRenderItem; onClose?(): void; onUpdate?(value: ListItemId[], selectedItems: T[]): void; onOpenChange?(open: boolean): void; - renderContainer?: RenderTreeListContainer; + renderContainer?: TreeSelectRenderContainer; /** * If you wont to disable default behavior pass `disabled` as a value; */ diff --git a/src/components/useList/types.ts b/src/components/useList/types.ts index ca98b78f86..723fe0a239 100644 --- a/src/components/useList/types.ts +++ b/src/components/useList/types.ts @@ -54,6 +54,11 @@ export interface OverrideItemContext { } export type RenderItemContext = { + /** + * optional, because ids may be skipped in the flatten order list, + * depending on the expanded state + */ + visibleFlattenIndex?: number; itemState: ItemState; /** * Exists if item is group diff --git a/src/components/useList/utils/getItemRenderState.tsx b/src/components/useList/utils/getItemRenderState.tsx index 5bd07bdc4d..7971b91c73 100644 --- a/src/components/useList/utils/getItemRenderState.tsx +++ b/src/components/useList/utils/getItemRenderState.tsx @@ -31,10 +31,12 @@ export const getItemRenderState = ( selectedById, activeItemId, id, + idToFlattenIndex, }: ItemRendererProps, {defaultExpanded = true}: {defaultExpanded?: boolean} = {}, ) => { const context: RenderItemContext = { + visibleFlattenIndex: idToFlattenIndex[id], itemState: itemsState[id], groupState: groupsState[id], isLastItem: id === visibleFlattenIds[visibleFlattenIds.length - 1],