Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README-ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,28 @@ const ReorderingExample = () => {
};
```

### Переупорядочивание без drag handle

Передайте `dragWithoutHandle`, чтобы вся строка стала зоной начала перетаскивания, и не добавляйте
`dragHandleColumn` в определения столбцов:

```tsx
const columns: ColumnDef<Person>[] = [
{accessorKey: 'name', header: 'Name'},
{accessorKey: 'age', header: 'Age'},
];

return (
<ReorderingProvider table={table} dragWithoutHandle onReorder={handleReorder}>
<Table table={table} />
</ReorderingProvider>
);
```

Перетаскивание начинается после движения указателя на 8 пикселей, поэтому обычные клики по строке
и контролам продолжают работать. Чтобы исключить пользовательскую область строки из зоны начала
перетаскивания, вызовите `preventDefault()` в её обработчике `onPointerDown`.

### Переупорядочивание столбцов

Оберните таблицу в `ColumnReorderingProvider`, чтобы включить перетаскивание столбцов за их заголовки.
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,28 @@ const ReorderingExample = () => {
};
```

#### Reordering without a drag handle

Set `dragWithoutHandle` to use the whole row as the drag activator and omit
`dragHandleColumn` from the column definitions:

```tsx
const columns: ColumnDef<Person>[] = [
{accessorKey: 'name', header: 'Name'},
{accessorKey: 'age', header: 'Age'},
];

return (
<ReorderingProvider table={table} dragWithoutHandle onReorder={handleReorder}>
<Table table={table} />
</ReorderingProvider>
);
```

The pointer must move by 8 pixels before dragging starts, so regular row and control clicks keep
working. To exclude a custom part of a row from starting a drag, call `preventDefault()` in its
`onPointerDown` handler.

#### Column reordering

Wrap the table with `ColumnReorderingProvider` to enable drag-and-drop reordering of columns by their headers.
Expand Down
26 changes: 25 additions & 1 deletion src/components/BaseDraggableRow/BaseDraggableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const BaseDraggableRow = React.forwardRef(
isChildMode,
activeItemKey,
targetItemIndex = -1,
dragWithoutHandle,
enableNesting,
// The `useSortable` hook is provided by `@dnd-kit/sortable` library and imported via `SortableListContext`.
// This is a temporary solution to prevent importing the entire `@dnd-kit` library
Expand All @@ -40,6 +41,7 @@ export const BaseDraggableRow = React.forwardRef(
transform = null,
transition,
isDragging = false,
listeners,
} = useSortable?.({
id: row.id,
}) || {};
Expand Down Expand Up @@ -72,17 +74,39 @@ export const BaseDraggableRow = React.forwardRef(
? attributesProp(draggableRow)
: attributesProp;

const handlePointerDown = (event: React.PointerEvent<HTMLTableRowElement>) => {
attributes?.onPointerDown?.(event);

if (event.defaultPrevented) {
return;
}

listeners?.onPointerDown?.(event);
};

return {
...attributes,
onPointerDown: dragWithoutHandle
? handlePointerDown
: attributes?.onPointerDown,
'data-key': draggableRow.id,
'data-depth': depth,
'data-draggable': true,
'data-drag-without-handle': dragWithoutHandle || undefined,
'data-dragging': isDragging,
'data-drag-active': isDragActive,
'data-expanded': isDragActive && isParent,
};
},
[attributesProp, depth, isDragging, isDragActive, isParent],
[
attributesProp,
depth,
dragWithoutHandle,
isDragging,
isDragActive,
isParent,
listeners,
],
);

return (
Expand Down
7 changes: 7 additions & 0 deletions src/components/BaseTable/__stories__/BaseTable.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ export const Reordering: StoryObj<typeof ReorderingStory> = {
render: ReorderingStory,
};

export const ReorderingWithoutDragHandle: StoryObj<typeof ReorderingStory> = {
render: ReorderingStory,
args: {
dragWithoutHandle: true,
},
};

export const ReorderingTree: StoryObj<typeof ReorderingTreeStory> = {
render: ReorderingTreeStory,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ import type {Item} from '../types';

const columns: ColumnDef<Item>[] = [dragHandleColumn as ColumnDef<Item>, ...originalColumns];

export const ReorderingStory = () => {
interface ReorderingStoryProps {
dragWithoutHandle?: boolean;
}

export const ReorderingStory = ({dragWithoutHandle = false}: ReorderingStoryProps) => {
const [data, setData] = React.useState(originalData);

const table = useTable({
columns,
columns: dragWithoutHandle ? originalColumns : columns,
data,
getRowId: (item) => item.id,
});
Expand Down Expand Up @@ -45,7 +49,11 @@ export const ReorderingStory = () => {
}, []);

return (
<ReorderingProvider table={table} onReorder={handleReorder}>
<ReorderingProvider
table={table}
dragWithoutHandle={dragWithoutHandle}
onReorder={handleReorder}
>
<BaseTable table={table} />
</ReorderingProvider>
);
Expand Down
8 changes: 8 additions & 0 deletions src/components/ReorderingProvider/ReorderingProvider.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ $dragHandleBlock: '.#{variables.$ns}drag-handle';
position: relative;

&[data-draggable='true'] {
&[data-drag-without-handle='true'] {
cursor: grab;

&[data-dragging='true'] {
cursor: grabbing;
}
}

&:not([data-drag-active='true']):hover {
#{$dragHandleBlock} {
z-index: 1;
Expand Down
4 changes: 4 additions & 0 deletions src/components/ReorderingProvider/ReorderingProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export interface ReorderingProviderProps<TData> {
table: Table<TData>;
/** Children */
children?: React.ReactNode;
/** Enables dragging a row by any part of it instead of requiring a drag handle. Default: `false` */
dragWithoutHandle?: SortableListProps['dragWithoutHandle'];
/** A list of the dnd-kit modifiers */
dndModifiers?: SortableListDndContextProps['modifiers'];
/** Determines whether elements can be nested using drag-and-drop */
Expand All @@ -24,6 +26,7 @@ export interface ReorderingProviderProps<TData> {
export const ReorderingProvider = <TData,>({
table,
children,
dragWithoutHandle = false,
dndModifiers,
enableNesting,
onReorder,
Expand All @@ -36,6 +39,7 @@ export const ReorderingProvider = <TData,>({
items={rowIds}
onDragEnd={onReorder}
enableNesting={enableNesting}
dragWithoutHandle={dragWithoutHandle}
dndModifiers={dndModifiers}
>
{children}
Expand Down
18 changes: 16 additions & 2 deletions src/components/SortableList/SortableList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
toRowSortableId,
} from '../TableDndRoot';

import {ROW_DRAG_ACTIVATION_DISTANCE} from './constants';

const useRowSortable: typeof useSortable = (args) =>
useSortable({
...args,
Expand All @@ -23,6 +25,7 @@ const useRowSortable: typeof useSortable = (args) =>

export interface SortableListProps extends UseSortableListParams {
children?: React.ReactNode;
dragWithoutHandle?: boolean;
dndModifiers?: import('@dnd-kit/core').Modifier[];
}

Expand All @@ -32,6 +35,7 @@ export const SortableList = ({
onDragStart,
onDragEnd,
enableNesting,
dragWithoutHandle = false,
dndModifiers,
}: SortableListProps) => {
const registry = React.useContext(TableDndRegistryContext);
Expand All @@ -56,11 +60,12 @@ export const SortableList = ({
const scopeConfig = React.useMemo(
() => ({
type: 'row' as const,
activationDistance: dragWithoutHandle ? ROW_DRAG_ACTIVATION_DISTANCE : undefined,
modifiers: dndModifiers,
autoScroll: true,
handlers,
}),
[dndModifiers, handlers],
[dndModifiers, dragWithoutHandle, handlers],
);

const contextValue = React.useMemo(
Expand All @@ -73,10 +78,19 @@ export const SortableList = ({
isParentMode,
targetItemIndex,
enableNesting,
dragWithoutHandle,
useSortable: useRowSortable,
}) satisfies SortableListContextValue,
// eslint-disable-next-line react-hooks/exhaustive-deps
[activeItemKey, isChildMode, isNextChildMode, isParentMode, targetItemIndex, enableNesting],
[
activeItemKey,
dragWithoutHandle,
enableNesting,
isChildMode,
isNextChildMode,
isParentMode,
targetItemIndex,
],
);

const content = (
Expand Down
1 change: 1 addition & 0 deletions src/components/SortableList/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const ROW_DRAG_ACTIVATION_DISTANCE = 8;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {useSortableList} from '../../hooks';

export interface SortableListContextValue
extends Omit<ReturnType<typeof useSortableList>, 'handlers'> {
dragWithoutHandle?: boolean;
enableNesting?: boolean;
useSortable?: typeof useSortable;
}
Expand Down
7 changes: 7 additions & 0 deletions src/components/Table/__stories__/Table.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ export const Reordering: StoryObj<typeof ReorderingStory> = {
render: ReorderingStory,
};

export const ReorderingWithoutDragHandle: StoryObj<typeof ReorderingStory> = {
render: ReorderingStory,
args: {
dragWithoutHandle: true,
},
};

export const ColumnReordering: StoryObj<typeof ColumnReorderingStory> = {
render: ColumnReorderingStory,
};
Expand Down
14 changes: 11 additions & 3 deletions src/components/Table/__stories__/stories/ReorderingStory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ import {Table} from '../../Table';

const columns: ColumnDef<Item>[] = [dragHandleColumn as ColumnDef<Item>, ...originalColumns];

export const ReorderingStory = () => {
interface ReorderingStoryProps {
dragWithoutHandle?: boolean;
}

export const ReorderingStory = ({dragWithoutHandle = false}: ReorderingStoryProps) => {
const [data, setData] = React.useState(originalData);

const table = useTable({
columns,
columns: dragWithoutHandle ? originalColumns : columns,
data,
getRowId: (item) => item.id,
});
Expand Down Expand Up @@ -45,7 +49,11 @@ export const ReorderingStory = () => {
}, []);

return (
<ReorderingProvider table={table} onReorder={handleReorder}>
<ReorderingProvider
table={table}
dragWithoutHandle={dragWithoutHandle}
onReorder={handleReorder}
>
<Table table={table} />
</ReorderingProvider>
);
Expand Down
3 changes: 2 additions & 1 deletion src/components/TableDndRoot/TableDndRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export const TableDndRoot = ({scopes, children}: TableDndRootProps) => {
const scopeList = React.useMemo(() => Object.values(scopes), [scopes]);

const columnScope = scopeList.find((scope) => scope.type === 'column');
const activationDistance = columnScope?.activationDistance;
const rowScope = scopeList.find((scope) => scope.type === 'row');
const activationDistance = columnScope?.activationDistance ?? rowScope?.activationDistance;

const pointerSensor = useSensor(
PointerSensor,
Expand Down
Loading