diff --git a/README.md b/README.md index 35f0b68..6907fcc 100644 --- a/README.md +++ b/README.md @@ -188,10 +188,10 @@ const GroupingExample = () => { ```tsx import type {ReorderingProviderProps} from '@gravity-ui/table'; -import {defaultDragHandleColumn, ReorderingProvider} from '@gravity-ui/table'; +import {dragHandleColumn, ReorderingProvider} from '@gravity-ui/table'; const columns: ColumnDef[] = [ - defaultDragHandleColumn, + dragHandleColumn, // ...other columns ]; diff --git a/package-lock.json b/package-lock.json index 3d6aab9..0a30426 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@gravity-ui/eslint-config": "^3.2.0", + "@gravity-ui/icons": "^2.11.0", "@gravity-ui/prettier-config": "^1.1.0", "@gravity-ui/stylelint-config": "^4.0.1", "@gravity-ui/tsconfig": "^1.0.0", @@ -65,6 +66,7 @@ "peerDependencies": { "@dnd-kit/core": "^6.0.0", "@dnd-kit/sortable": "^8.0.0", + "@gravity-ui/icons": "^2.0.0", "@gravity-ui/uikit": "^6.0.0", "react": "^17.0.0 || ^18.0.0", "react-dom": "^17.0.0 || ^18.0.0" @@ -3235,9 +3237,9 @@ "dev": true }, "node_modules/@gravity-ui/icons": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@gravity-ui/icons/-/icons-2.9.1.tgz", - "integrity": "sha512-0OH6YrVxFpHjk3x1f5GWlC+3Ek7LncNy95C9g94C13MhRtMRe0l/ZwSgbOmQ72lDxvQrmidt095Ft+mfID9XmQ==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@gravity-ui/icons/-/icons-2.11.0.tgz", + "integrity": "sha512-kRiemFztlFV4iUcsbdGRM/2ozK6XZYkzOV4hIzSmNDqtXpEoumm7LhzOLCiugN68YQrazzJLksq4Ho8lpjkVrg==", "dev": true, "peerDependencies": { "react": "*" diff --git a/package.json b/package.json index dc91da4..f397d44 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@gravity-ui/eslint-config": "^3.2.0", + "@gravity-ui/icons": "^2.11.0", "@gravity-ui/prettier-config": "^1.1.0", "@gravity-ui/stylelint-config": "^4.0.1", "@gravity-ui/tsconfig": "^1.0.0", @@ -98,6 +99,7 @@ "peerDependencies": { "@dnd-kit/core": "^6.0.0", "@dnd-kit/sortable": "^8.0.0", + "@gravity-ui/icons": "^2.0.0", "@gravity-ui/uikit": "^6.0.0", "react": "^17.0.0 || ^18.0.0", "react-dom": "^17.0.0 || ^18.0.0" diff --git a/src/components/BaseDragHandle/BaseDragHandle.tsx b/src/components/BaseDragHandle/BaseDragHandle.tsx deleted file mode 100644 index 6f0194e..0000000 --- a/src/components/BaseDragHandle/BaseDragHandle.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; - -import {useSortable} from '@dnd-kit/sortable'; -import type {Row, Table} from '@tanstack/react-table'; - -import {useDraggableRowDepth} from '../../hooks'; -import {BaseDraggableRowMarker} from '../BaseDraggableRowMarker'; -import {SortableListContext} from '../SortableListContext'; - -import {b} from './BaseDragHandle.classname'; - -import './BaseDragHandle.scss'; - -export interface BaseDragHandleProps { - row: Row; - table: Table; -} - -export const BaseDragHandle = ({row, table}: BaseDragHandleProps) => { - const {attributes, listeners, isDragging} = useSortable({ - id: row.id, - }); - - const {enableNesting} = React.useContext(SortableListContext) || {}; - - const {depth} = useDraggableRowDepth({row, table, isDragging}); - - return ( - - - - - - - {isDragging && enableNesting && } - - ); -}; - -BaseDragHandle.displayName = 'BaseDragHandle'; diff --git a/src/components/BaseDragHandle/index.ts b/src/components/BaseDragHandle/index.ts deleted file mode 100644 index ed78f25..0000000 --- a/src/components/BaseDragHandle/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './BaseDragHandle'; diff --git a/src/components/BaseDraggableRowMarker/BaseDraggableRowMarker.classname.ts b/src/components/BaseDraggableRowMarker/BaseDraggableRowMarker.classname.ts deleted file mode 100644 index 10fd04b..0000000 --- a/src/components/BaseDraggableRowMarker/BaseDraggableRowMarker.classname.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {block} from '../../utils'; - -export const b = block('draggable-row-marker'); diff --git a/src/components/BaseDraggableRowMarker/BaseDraggableRowMarker.scss b/src/components/BaseDraggableRowMarker/BaseDraggableRowMarker.scss deleted file mode 100644 index ad953ef..0000000 --- a/src/components/BaseDraggableRowMarker/BaseDraggableRowMarker.scss +++ /dev/null @@ -1,16 +0,0 @@ -@use '../variables'; - -$block: '.#{variables.$ns}draggable-row-marker'; - -#{$block} { - position: absolute; - inset-block-start: 0; - inset-inline-end: 0; - z-index: 2; - - height: 5px; - - background: #027bf3; - - margin-inline-start: calc(var(--gc-tree-view-padding) + var(--gc-draggable-row-marker-offset)); -} diff --git a/src/components/BaseDraggableRowMarker/BaseDraggableRowMarker.tsx b/src/components/BaseDraggableRowMarker/BaseDraggableRowMarker.tsx deleted file mode 100644 index 5362bc8..0000000 --- a/src/components/BaseDraggableRowMarker/BaseDraggableRowMarker.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import {b} from './BaseDraggableRowMarker.classname'; - -import './BaseDraggableRowMarker.scss'; - -export interface BaseDraggableRowMarkerProps { - left: number; -} - -export const BaseDraggableRowMarker = ({left}: BaseDraggableRowMarkerProps) => { - return ; -}; diff --git a/src/components/BaseDraggableRowMarker/index.ts b/src/components/BaseDraggableRowMarker/index.ts deleted file mode 100644 index 296dac5..0000000 --- a/src/components/BaseDraggableRowMarker/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './BaseDraggableRowMarker'; diff --git a/src/components/BaseTable/BaseTable.scss b/src/components/BaseTable/BaseTable.scss index 2301965..17ed209 100644 --- a/src/components/BaseTable/BaseTable.scss +++ b/src/components/BaseTable/BaseTable.scss @@ -3,9 +3,6 @@ $block: '.#{variables.$ns}table'; #{$block} { - --gc-tree-view-padding: 0px; - --gc-draggable-row-marker-offset: 20px; - border-spacing: 0; border-collapse: separate; @@ -88,7 +85,6 @@ $block: '.#{variables.$ns}table'; &__footer-row { display: flex; - width: 100%; height: auto; } @@ -97,7 +93,6 @@ $block: '.#{variables.$ns}table'; position: absolute; - width: 100%; height: auto; } } diff --git a/src/components/BaseTable/__stories__/stories/ReorderingStory.tsx b/src/components/BaseTable/__stories__/stories/ReorderingStory.tsx index 8f870f4..212f88e 100644 --- a/src/components/BaseTable/__stories__/stories/ReorderingStory.tsx +++ b/src/components/BaseTable/__stories__/stories/ReorderingStory.tsx @@ -2,7 +2,7 @@ import React from 'react'; import type {ColumnDef} from '@tanstack/react-table'; -import {defaultDragHandleColumn} from '../../../../constants'; +import {dragHandleColumn} from '../../../../constants'; import {useTable} from '../../../../hooks'; import {BaseTable} from '../../../BaseTable'; import {ReorderingProvider} from '../../../ReorderingProvider'; @@ -11,7 +11,7 @@ import {columns as originalColumns} from '../constants/columns'; import {data as originalData} from '../constants/data'; import type {Item} from '../types'; -const columns: ColumnDef[] = [defaultDragHandleColumn as ColumnDef, ...originalColumns]; +const columns: ColumnDef[] = [dragHandleColumn as ColumnDef, ...originalColumns]; export const ReorderingStory = () => { const [data, setData] = React.useState(originalData); diff --git a/src/components/BaseTable/__stories__/stories/ReorderingTreeStory.tsx b/src/components/BaseTable/__stories__/stories/ReorderingTreeStory.tsx index b01ba2b..4e97536 100644 --- a/src/components/BaseTable/__stories__/stories/ReorderingTreeStory.tsx +++ b/src/components/BaseTable/__stories__/stories/ReorderingTreeStory.tsx @@ -2,7 +2,7 @@ import React from 'react'; import type {ColumnDef, ExpandedState} from '@tanstack/react-table'; -import {defaultDragHandleColumn} from '../../../../constants'; +import {dragHandleColumn} from '../../../../constants'; import {useTable} from '../../../../hooks'; import {ReorderingProvider} from '../../../ReorderingProvider'; import {BaseTable} from '../../BaseTable'; @@ -11,7 +11,7 @@ import {draggableTreeColumns, data as originalData} from '../constants/tree'; import {useTreeDataReordering} from '../hooks/useTreeDataReordering'; const columns: ColumnDef[] = [ - defaultDragHandleColumn as ColumnDef, + dragHandleColumn as ColumnDef, ...draggableTreeColumns, ]; diff --git a/src/components/BaseTable/__stories__/stories/ReorderingWithVirtualizationStory.tsx b/src/components/BaseTable/__stories__/stories/ReorderingWithVirtualizationStory.tsx index da3de48..a2d1506 100644 --- a/src/components/BaseTable/__stories__/stories/ReorderingWithVirtualizationStory.tsx +++ b/src/components/BaseTable/__stories__/stories/ReorderingWithVirtualizationStory.tsx @@ -2,7 +2,7 @@ import React from 'react'; import type {ColumnDef} from '@tanstack/react-table'; -import {defaultDragHandleColumn} from '../../../../constants'; +import {dragHandleColumn} from '../../../../constants'; import {useTable, useWindowRowVirtualizer} from '../../../../hooks'; import {getVirtualRowRangeExtractor} from '../../../../utils'; import {BaseTable} from '../../../BaseTable'; @@ -16,7 +16,7 @@ import {cnVirtualizationStory} from './VirtualizationStory.classname'; import './VirtualizationStory.scss'; -const columns: ColumnDef[] = [defaultDragHandleColumn as ColumnDef, ...originalColumns]; +const columns: ColumnDef[] = [dragHandleColumn as ColumnDef, ...originalColumns]; export const ReorderingWithVirtualizationStory = () => { const tableRef = React.useRef(null); diff --git a/src/components/BaseDragHandle/BaseDragHandle.classname.ts b/src/components/DragHandle/DragHandle.classname.ts similarity index 100% rename from src/components/BaseDragHandle/BaseDragHandle.classname.ts rename to src/components/DragHandle/DragHandle.classname.ts diff --git a/src/components/BaseDragHandle/BaseDragHandle.scss b/src/components/DragHandle/DragHandle.scss similarity index 77% rename from src/components/BaseDragHandle/BaseDragHandle.scss rename to src/components/DragHandle/DragHandle.scss index 5579ef1..9753fde 100644 --- a/src/components/BaseDragHandle/BaseDragHandle.scss +++ b/src/components/DragHandle/DragHandle.scss @@ -5,6 +5,8 @@ $block: '.#{variables.$ns}drag-handle'; #{$block} { cursor: grab; + color: var(--g-color-text-secondary); + &[aria-pressed] { cursor: grabbing; } diff --git a/src/components/DragHandle/DragHandle.tsx b/src/components/DragHandle/DragHandle.tsx new file mode 100644 index 0000000..66f57ed --- /dev/null +++ b/src/components/DragHandle/DragHandle.tsx @@ -0,0 +1,28 @@ +import React from 'react'; + +import {useSortable} from '@dnd-kit/sortable'; +import {Grip as GripIcon} from '@gravity-ui/icons'; +import {Icon} from '@gravity-ui/uikit'; +import type {Row} from '@tanstack/react-table'; + +import {b} from './DragHandle.classname'; + +import './DragHandle.scss'; + +export interface DragHandleProps { + row: Row; +} + +export const DragHandle = ({row}: DragHandleProps) => { + const {attributes, listeners} = useSortable({ + id: row.id, + }); + + return ( + + + + ); +}; + +DragHandle.displayName = 'DragHandle'; diff --git a/src/components/DragHandle/index.ts b/src/components/DragHandle/index.ts new file mode 100644 index 0000000..21aa91a --- /dev/null +++ b/src/components/DragHandle/index.ts @@ -0,0 +1 @@ +export * from './DragHandle'; diff --git a/src/components/Table/__stories__/Table.stories.tsx b/src/components/Table/__stories__/Table.stories.tsx index d904737..173c78f 100644 --- a/src/components/Table/__stories__/Table.stories.tsx +++ b/src/components/Table/__stories__/Table.stories.tsx @@ -3,6 +3,8 @@ import type {Meta, StoryObj} from '@storybook/react'; import {Table} from '../index'; import {DefaultStory} from './stories/DefaultStory'; +import {ReorderingStory} from './stories/ReorderingStory'; +import {ReorderingWithVirtualizationStory} from './stories/ReorderingWithVirtualizationStory'; import {StickyHeaderStory} from './stories/StickyHeaderStory'; import {VirtualizationStory} from './stories/VirtualizationStory'; import {WindowVirtualizationStory} from './stories/WindowVirtualizationStory'; @@ -23,6 +25,10 @@ export const WithSelection: StoryObj = { render: WithSelectionStory, }; +export const Reordering: StoryObj = { + render: ReorderingStory, +}; + export const Virtualization: StoryObj = { render: VirtualizationStory, }; @@ -31,6 +37,10 @@ export const WindowVirtualization: StoryObj = render: WindowVirtualizationStory, }; +export const ReorderingWithVirtualization: StoryObj = { + render: ReorderingWithVirtualizationStory, +}; + export const StickyHeader: StoryObj = { render: StickyHeaderStory, }; diff --git a/src/components/Table/__stories__/stories/ReorderingStory.tsx b/src/components/Table/__stories__/stories/ReorderingStory.tsx new file mode 100644 index 0000000..e567b7a --- /dev/null +++ b/src/components/Table/__stories__/stories/ReorderingStory.tsx @@ -0,0 +1,53 @@ +import React from 'react'; + +import type {ColumnDef} from '@tanstack/react-table'; + +import {dragHandleColumn} from '../../../../constants'; +import {useTable} from '../../../../hooks'; +import {columns as originalColumns} from '../../../BaseTable/__stories__/constants/columns'; +import {data as originalData} from '../../../BaseTable/__stories__/constants/data'; +import type {Item} from '../../../BaseTable/__stories__/types'; +import type {ReorderingProviderProps} from '../../../ReorderingProvider'; +import {ReorderingProvider} from '../../../ReorderingProvider'; +import {Table} from '../../Table'; + +const columns: ColumnDef[] = [dragHandleColumn as ColumnDef, ...originalColumns]; + +export const ReorderingStory = () => { + const [data, setData] = React.useState(originalData); + + const table = useTable({ + columns, + data, + getRowId: (item) => item.id, + }); + + const handleReorder = React.useCallback< + NonNullable['onReorder']> + >(({draggedItemKey, baseItemKey}) => { + setData((prevData) => { + const dataClone = prevData.slice(); + + const index = dataClone.findIndex((item) => item.id === draggedItemKey); + + if (index >= 0) { + const dragged = dataClone.splice(index, 1)[0] as Item; + const insertIndex = dataClone.findIndex((item) => item.id === baseItemKey); + + if (insertIndex >= 0) { + dataClone.splice(insertIndex + 1, 0, dragged); + } else { + dataClone.unshift(dragged); + } + } + + return dataClone; + }); + }, []); + + return ( + + + + ); +}; diff --git a/src/components/Table/__stories__/stories/ReorderingWithVirtualizationStory.tsx b/src/components/Table/__stories__/stories/ReorderingWithVirtualizationStory.tsx new file mode 100644 index 0000000..913929f --- /dev/null +++ b/src/components/Table/__stories__/stories/ReorderingWithVirtualizationStory.tsx @@ -0,0 +1,62 @@ +import React from 'react'; + +import type {ColumnDef} from '@tanstack/react-table'; + +import {dragHandleColumn} from '../../../../constants'; +import {useTable, useWindowRowVirtualizer} from '../../../../hooks'; +import {getVirtualRowRangeExtractor} from '../../../../utils'; +import {columns as originalColumns} from '../../../BaseTable/__stories__/constants/columns'; +import type {Item} from '../../../BaseTable/__stories__/types'; +import {generateData} from '../../../BaseTable/__stories__/utils'; +import type {ReorderingProviderProps} from '../../../ReorderingProvider'; +import {ReorderingProvider} from '../../../ReorderingProvider'; +import {Table} from '../../Table'; + +const columns: ColumnDef[] = [dragHandleColumn as ColumnDef, ...originalColumns]; + +export const ReorderingWithVirtualizationStory = () => { + const tableRef = React.useRef(null); + const [data, setData] = React.useState(() => generateData(300)); + + const table = useTable({ + columns, + data, + getRowId: (item) => item.id, + }); + + const rowVirtualizer = useWindowRowVirtualizer({ + count: table.getRowModel().rows.length, + estimateSize: () => 20, + overscan: 5, + rangeExtractor: getVirtualRowRangeExtractor(tableRef.current), + }); + + const handleReorder = React.useCallback< + NonNullable['onReorder']> + >(({draggedItemKey, baseItemKey}) => { + setData((prevData) => { + const dataClone = prevData.slice(); + + const index = dataClone.findIndex((item) => item.id === draggedItemKey); + + if (index >= 0) { + const dragged = dataClone.splice(index, 1)[0] as Item; + const insertIndex = dataClone.findIndex((item) => item.id === baseItemKey); + + if (insertIndex >= 0) { + dataClone.splice(insertIndex + 1, 0, dragged); + } else { + dataClone.unshift(dragged); + } + } + + return dataClone; + }); + }, []); + + return ( + +
+ + ); +}; diff --git a/src/components/index.ts b/src/components/index.ts index b0dc63c..f079adb 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,7 +1,5 @@ export * from './BaseCell'; -export * from './BaseDragHandle'; export * from './BaseDraggableRow'; -export * from './BaseDraggableRowMarker'; export * from './BaseFooterCell'; export * from './BaseGroupHeader'; export * from './BaseHeaderCell'; @@ -11,6 +9,7 @@ export * from './BaseResizeHandle'; export * from './BaseRow'; export * from './BaseSortIndicator'; export * from './SelectionCheckbox'; +export * from './DragHandle'; export * from './SortableList'; export * from './SortableListContext'; export * from './SortableListDndContext'; diff --git a/src/constants/defaultDragHandleColumn.tsx b/src/constants/defaultDragHandleColumn.tsx deleted file mode 100644 index b56dfd5..0000000 --- a/src/constants/defaultDragHandleColumn.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import type {ColumnDef} from '@tanstack/react-table'; - -import {BaseDragHandle} from '../components'; - -export const defaultDragHandleColumn: ColumnDef = { - id: '_drag', - header: '', - cell: ({row, table}) => , - size: 14, - minSize: 14, -}; diff --git a/src/constants/dragHandleColumn.tsx b/src/constants/dragHandleColumn.tsx new file mode 100644 index 0000000..9b9a050 --- /dev/null +++ b/src/constants/dragHandleColumn.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import type {ColumnDef} from '@tanstack/react-table'; + +import {DragHandle} from '../components'; + +export const dragHandleColumn: ColumnDef = { + id: '_drag', + header: '', + cell: ({row}) => , + size: 32, + minSize: 32, +}; diff --git a/src/constants/index.ts b/src/constants/index.ts index a7a7a4c..ccfbf49 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,2 +1,2 @@ -export * from './defaultDragHandleColumn'; +export * from './dragHandleColumn'; export * from './selectionColumn'; diff --git a/src/index.ts b/src/index.ts index f2c876d..e03dac6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ export {BaseTable, ReorderingProvider, Table} from './components'; export type {BaseTableProps, ReorderingProviderProps, TableProps} from './components'; -export {defaultDragHandleColumn, selectionColumn} from './constants'; +export {dragHandleColumn, selectionColumn} from './constants'; export {useDraggableRowDepth, useRowVirtualizer, useTable, useWindowRowVirtualizer} from './hooks';