Skip to content

Commit

Permalink
chore: dndTreeSelect types
Browse files Browse the repository at this point in the history
  • Loading branch information
GermanVor committed Feb 7, 2024
1 parent 6320b7b commit aea9fe5
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 20 deletions.
61 changes: 44 additions & 17 deletions src/components/TreeSelect/DndTreeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,24 @@ import {reorderArray} from '../useList/__stories__/utils/reorderArray';

import {TreeSelect} from './TreeSelect';
import {TreeSelectItem} from './TreeSelectItem';
import type {RenderContainerProps, RenderItem, TreeSelectProps} from './types';
import type {RenderContainerType, RenderItem, TreeSelectProps} from './types';

const renderDndItem: RenderItem<string> = (item, state) => {
const renderDndItem: RenderItem<DndTreeSelectItemType> = (item, state, _, idx) => {
const commonProps = {
...state,
title: item,
title: item.content,
endSlot: <Icon data={Grip} size={16} />,
};

return (
<Draggable draggableId={state.id} index={Number(state.id)} key={`item-key-${state.id}`}>
<Draggable draggableId={state.id} index={Number(idx)} key={`item-key-${state.id}`}>
{(provided, snapshot) => {
// not expected offset appears, one way to fix - remove this offsets explicitly
if (snapshot.isDragging && provided.draggableProps.style) {
const style = provided.draggableProps.style as React.CSSProperties;
const style: React.CSSProperties = {
...provided.draggableProps.style,
};

// not expected offset appears, one way to fix - remove this offsets explicitly
if (snapshot.isDragging) {
style.left = undefined;
style.top = undefined;
}
Expand All @@ -37,6 +39,7 @@ const renderDndItem: RenderItem<string> = (item, state) => {
{...commonProps}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={style}
active={snapshot.isDragging}
/>
);
Expand All @@ -45,35 +48,57 @@ const renderDndItem: RenderItem<string> = (item, state) => {
);
};

export type DndTreeSelectItemType = {
id: string;
content: React.ReactNode;
};

export type RenderDndContainerType = (container: {
renderList: () => React.JSX.Element;
}) => React.ReactElement;

const DEFAULT_RENDER_CONTAINER: RenderDndContainerType = ({renderList}) => renderList();

let dndTreeSelectCount = 0;

export type DndTreeSelectProps<T> = Omit<
TreeSelectProps<T>,
'items' | 'renderContainer' | 'renderItem'
> & {
items: ListItemType<T>[];
setItems: (_: ListItemType<T>[]) => void;
renderContainer?: RenderDndContainerType;
initialDroppableId?: string;
};
export function DndTreeSelect({
items,
setItems,
initialDroppableId,
renderContainer: propsRenderContainer = DEFAULT_RENDER_CONTAINER,
...treeSelectNativeProps
}: DndTreeSelectProps<string>) {
const renderContainer = React.useCallback(
({renderItem, visibleFlattenIds, containerRef, id}: RenderContainerProps<string>) => {
}: DndTreeSelectProps<DndTreeSelectItemType>) {
const [droppableId] = React.useState(
() => initialDroppableId || `default-droppable-id-${dndTreeSelectCount++}`,
);

const renderContainer = React.useCallback<RenderContainerType<DndTreeSelectItemType>>(
({renderItem, visibleFlattenIds, items: _items, containerRef, id}) => {
const handleDrugEnd: OnDragEndResponder = ({destination, source}) => {
if (destination?.index && destination?.index !== source.index) {
if (destination?.index !== undefined && destination?.index !== source.index) {
const newItemsOrder = reorderArray(items, source.index, destination.index);

setItems(newItemsOrder);
}
};

const visibleFlattenItemList = visibleFlattenIds.map((visibleFlattenId) =>
renderItem(visibleFlattenId),
const visibleFlattenItemList = visibleFlattenIds.map((visibleFlattenId, idx) =>
renderItem(visibleFlattenId, idx),
);

return (
const renderList = () => (
<ListContainerView ref={containerRef} id={id}>
<DragDropContext onDragEnd={handleDrugEnd}>
<Droppable droppableId="droppable">
<Droppable droppableId={droppableId}>
{(droppableProvided) => {
return (
<div
Expand All @@ -89,12 +114,14 @@ export function DndTreeSelect({
</DragDropContext>
</ListContainerView>
);

return propsRenderContainer({renderList});
},
[items, setItems],
[items, setItems, propsRenderContainer],
);

return (
<TreeSelect
<TreeSelect<DndTreeSelectItemType>
{...treeSelectNativeProps}
items={items}
renderContainer={renderContainer}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@ import React from 'react';
import {Flex} from '../../../layout';
import {createRandomizedData} from '../../../useList/__stories__/utils/makeData';
import {DndTreeSelect} from '../../DndTreeSelect';
import type {DndTreeSelectItemType} from '../../DndTreeSelect';
import type {TreeSelectProps} from '../../types';

export interface WithDndListExampleProps
extends Omit<TreeSelectProps<string>, 'value' | 'onUpdate' | 'items' | 'getItemContent'> {}
extends Omit<
TreeSelectProps<DndTreeSelectItemType>,
'value' | 'onUpdate' | 'items' | 'getItemContent' | 'renderContainer'
> {}

let i = 0;

export const WithDndListExample = (props: WithDndListExampleProps) => {
const [items, setItems] = React.useState(() =>
createRandomizedData({num: 10, depth: 0, getData: (title) => title}),
createRandomizedData<DndTreeSelectItemType>({
num: 10,
depth: 0,
getData: (title) => ({id: `id-${i++}`, content: title}),
}),
);

return (
Expand Down
6 changes: 5 additions & 1 deletion src/components/TreeSelect/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type React from 'react';

import type {PopperPlacement} from '../../hooks/private';
import type {QAProps} from '../types';
import type {
KnownItemStructure,
Expand Down Expand Up @@ -42,6 +43,8 @@ export type RenderContainerProps<T> = ListParsedState<T> &
className?: string;
};

export type RenderContainerType<T> = (props: RenderContainerProps<T>) => React.JSX.Element;

interface TreeSelectBaseProps<T> extends QAProps, Partial<Omit<ListState, 'selectedById'>> {
value?: ListItemId[];
defaultOpen?: boolean;
Expand All @@ -50,6 +53,7 @@ interface TreeSelectBaseProps<T> extends QAProps, Partial<Omit<ListState, 'selec
id?: string | undefined;
popupClassName?: string;
popupWidth?: number;
placement?: PopperPlacement;
width?: 'auto' | 'max' | number;
className?: string;
popupDisablePortal?: boolean;
Expand Down Expand Up @@ -93,7 +97,7 @@ interface TreeSelectBaseProps<T> extends QAProps, Partial<Omit<ListState, 'selec
onClose?(): void;
onUpdate?(value: ListItemId[], selectedItems: T[]): void;
onOpenChange?(open: boolean): void;
renderContainer?(props: RenderContainerProps<T>): React.JSX.Element;
renderContainer?: RenderContainerType<T>;
/**
* If you wont to disable default behavior pass `disabled` as a value;
*/
Expand Down

0 comments on commit aea9fe5

Please sign in to comment.