react-table-dnd 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +256 -0
- package/dist/Components/BodyRow.d.ts +9 -0
- package/dist/Components/ColumnCell.d.ts +9 -0
- package/dist/Components/DragHandle.d.ts +8 -0
- package/dist/Components/Draggable.d.ts +12 -0
- package/dist/Components/RowCell.d.ts +11 -0
- package/dist/Components/TableBody.d.ts +8 -0
- package/dist/Components/TableContainer/index.d.ts +15 -0
- package/dist/Components/TableContainer/store.d.ts +7 -0
- package/dist/Components/TableContainer/styles.d.ts +7 -0
- package/dist/Components/TableContainer/useTable.d.ts +9 -0
- package/dist/Components/TableHeader.d.ts +8 -0
- package/dist/Components/index.d.ts +11 -0
- package/dist/Components/utils.d.ts +5 -0
- package/dist/hooks/types.d.ts +125 -0
- package/dist/hooks/useAutoScroll.d.ts +15 -0
- package/dist/hooks/useDragContextEvents.d.ts +7 -0
- package/dist/hooks/useDropTarget.d.ts +13 -0
- package/dist/hooks/useIndexMaps.d.ts +15 -0
- package/dist/hooks/useLongPress.d.ts +14 -0
- package/dist/hooks/useShiftTransforms.d.ts +16 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.es.js +1236 -0
- package/dist/react-table-dnd.css +2 -0
- package/package.json +93 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Sami Odeh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# react-table-dnd
|
|
4
|
+
|
|
5
|
+
<p>
|
|
6
|
+
<img src="./docs/desktop.gif" alt="react-table-dnd — drag rows and columns" width="680" />
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p><strong>Drag-and-drop row & column reordering for React tables.</strong></p>
|
|
10
|
+
|
|
11
|
+
<p>60fps animations · Auto-scroll · Mobile long-press · Virtual scrolling · Zero UI deps</p>
|
|
12
|
+
|
|
13
|
+
<p>
|
|
14
|
+
<a href="https://www.npmjs.com/package/react-table-dnd"><img src="https://img.shields.io/npm/v/react-table-dnd?color=6366f1&label=npm" alt="npm" /></a>
|
|
15
|
+
<a href="https://bundlephobia.com/package/react-table-dnd"><img src="https://img.shields.io/bundlephobia/minzip/react-table-dnd?color=6366f1&label=size" alt="bundle size" /></a>
|
|
16
|
+
<a href="https://www.npmjs.com/package/react-table-dnd"><img src="https://img.shields.io/npm/dm/react-table-dnd?color=6366f1" alt="downloads" /></a>
|
|
17
|
+
<a href="https://github.com/samiodeh1337/react-table-dnd/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/react-table-dnd?color=6366f1" alt="license" /></a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<p>
|
|
21
|
+
<a href="https://samiodeh1337.github.io/react-table-dnd/"><strong>Live Demos & Docs</strong></a>
|
|
22
|
+
·
|
|
23
|
+
<a href="#quick-start">Quick Start</a>
|
|
24
|
+
·
|
|
25
|
+
<a href="#api">API</a>
|
|
26
|
+
·
|
|
27
|
+
<a href="https://github.com/samiodeh1337/react-table-dnd">GitHub</a>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Why react-table-dnd?
|
|
35
|
+
|
|
36
|
+
- **Rows & columns** — reorder both independently, automatic direction detection
|
|
37
|
+
- **60fps** — direct DOM transforms during drag, no React re-renders until drop
|
|
38
|
+
- **Mobile** — long-press to drag on touch devices, optimized for Chrome Android & Safari iOS
|
|
39
|
+
- **Auto-scroll** — accelerates when dragging near container edges
|
|
40
|
+
- **100k+ rows** — works with `@tanstack/react-virtual`
|
|
41
|
+
- **Drag handles** — restrict drag to a grip icon with `<DragHandle>`
|
|
42
|
+
- **Constraints** — lock specific rows or columns via drag range options
|
|
43
|
+
- **Drop animation** — clone smoothly flies to the drop target
|
|
44
|
+
- **Fully styleable** — `className` + `style` on every component — Tailwind, styled-components, CSS modules
|
|
45
|
+
- **TypeScript** — full type definitions out of the box
|
|
46
|
+
- **Tiny** — only peer dependency is React
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install react-table-dnd
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> Requires `react` and `react-dom` >= 17.0.0
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```jsx
|
|
59
|
+
import {
|
|
60
|
+
TableContainer, TableHeader, ColumnCell,
|
|
61
|
+
TableBody, BodyRow, RowCell,
|
|
62
|
+
} from "react-table-dnd";
|
|
63
|
+
|
|
64
|
+
function arrayMove(arr, from, to) {
|
|
65
|
+
const next = [...arr];
|
|
66
|
+
const [item] = next.splice(from, 1);
|
|
67
|
+
next.splice(to, 0, item);
|
|
68
|
+
return next;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default function App() {
|
|
72
|
+
const [cols, setCols] = useState([
|
|
73
|
+
{ id: "name", label: "Name", width: 150 },
|
|
74
|
+
{ id: "age", label: "Age", width: 100 },
|
|
75
|
+
{ id: "city", label: "City", width: 160 },
|
|
76
|
+
]);
|
|
77
|
+
const [rows, setRows] = useState([
|
|
78
|
+
{ id: "1", name: "Alice", age: 28, city: "NYC" },
|
|
79
|
+
{ id: "2", name: "Bob", age: 34, city: "LA" },
|
|
80
|
+
{ id: "3", name: "Carol", age: 22, city: "SF" },
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<TableContainer
|
|
85
|
+
onDragEnd={({ sourceIndex, targetIndex, dragType }) => {
|
|
86
|
+
if (dragType === "column") setCols(arrayMove(cols, sourceIndex, targetIndex));
|
|
87
|
+
else setRows(arrayMove(rows, sourceIndex, targetIndex));
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
<TableHeader>
|
|
91
|
+
{cols.map((col, i) => (
|
|
92
|
+
<ColumnCell key={col.id} id={col.id} index={i} style={{ width: col.width }}>
|
|
93
|
+
{col.label}
|
|
94
|
+
</ColumnCell>
|
|
95
|
+
))}
|
|
96
|
+
</TableHeader>
|
|
97
|
+
<TableBody>
|
|
98
|
+
{rows.map((row, ri) => (
|
|
99
|
+
<BodyRow key={row.id} id={row.id} index={ri}>
|
|
100
|
+
{cols.map((col, ci) => (
|
|
101
|
+
<RowCell key={col.id} index={ci}>
|
|
102
|
+
{row[col.id]}
|
|
103
|
+
</RowCell>
|
|
104
|
+
))}
|
|
105
|
+
</BodyRow>
|
|
106
|
+
))}
|
|
107
|
+
</TableBody>
|
|
108
|
+
</TableContainer>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## API
|
|
114
|
+
|
|
115
|
+
### Components
|
|
116
|
+
|
|
117
|
+
| Component | Props | Description |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| **`TableContainer`** | `onDragEnd`, `options`, `renderPlaceholder`, `className`, `style` | Root wrapper — provides drag context |
|
|
120
|
+
| **`TableHeader`** | `className`, `style` | Header row container |
|
|
121
|
+
| **`ColumnCell`** | **`id`**, **`index`**, `className`, `style` | Draggable column header cell |
|
|
122
|
+
| **`TableBody`** | `className`, `style` | Scrollable body — pass `ref` for virtual scrolling |
|
|
123
|
+
| **`BodyRow`** | **`id`**, **`index`**, `className`, `style` | Draggable row |
|
|
124
|
+
| **`RowCell`** | **`index`**, `className`, `style` | Cell within a row |
|
|
125
|
+
| **`DragHandle`** | `className`, `style` | Wrap inside BodyRow/ColumnCell to restrict drag to this element |
|
|
126
|
+
|
|
127
|
+
Bold props are required.
|
|
128
|
+
|
|
129
|
+
### Column Width
|
|
130
|
+
|
|
131
|
+
Pass `width` inside the `style` prop on `ColumnCell`. Columns grow proportionally by default to fill available space. To fix a column at exactly its pixel size, also pass `flex`:
|
|
132
|
+
|
|
133
|
+
```jsx
|
|
134
|
+
{/* Flex — grows proportionally to fill container (default) */}
|
|
135
|
+
<ColumnCell style={{ width: 150 }}>Name</ColumnCell>
|
|
136
|
+
|
|
137
|
+
{/* Fixed — stays exactly 150px regardless of container width */}
|
|
138
|
+
<ColumnCell style={{ width: 150, flex: "0 0 150px" }}>Name</ColumnCell>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Types
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
interface DragEndResult {
|
|
145
|
+
sourceIndex: number;
|
|
146
|
+
targetIndex: number;
|
|
147
|
+
dragType: "row" | "column";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
interface DragRange {
|
|
151
|
+
start?: number; // first draggable index
|
|
152
|
+
end?: number; // last draggable index (exclusive)
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Options
|
|
157
|
+
|
|
158
|
+
```jsx
|
|
159
|
+
<TableContainer
|
|
160
|
+
options={{
|
|
161
|
+
rowDragRange: { start: 1 }, // lock first row
|
|
162
|
+
columnDragRange: { start: 1, end: 5 }, // lock first col, only 1-4 draggable
|
|
163
|
+
}}
|
|
164
|
+
/>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Drag Handle
|
|
168
|
+
|
|
169
|
+
```jsx
|
|
170
|
+
import { DragHandle } from "react-table-dnd";
|
|
171
|
+
|
|
172
|
+
<BodyRow id="1" index={0}>
|
|
173
|
+
<RowCell index={0}>
|
|
174
|
+
<DragHandle><GripIcon /></DragHandle>
|
|
175
|
+
Content here
|
|
176
|
+
</RowCell>
|
|
177
|
+
</BodyRow>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Custom Placeholder
|
|
181
|
+
|
|
182
|
+
```jsx
|
|
183
|
+
<TableContainer
|
|
184
|
+
renderPlaceholder={() => (
|
|
185
|
+
<div style={{
|
|
186
|
+
background: "#6366f122",
|
|
187
|
+
border: "2px dashed #6366f1",
|
|
188
|
+
height: "100%",
|
|
189
|
+
}} />
|
|
190
|
+
)}
|
|
191
|
+
/>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Styling
|
|
195
|
+
|
|
196
|
+
Every component accepts `className` and `style`. No opinionated styles on cells.
|
|
197
|
+
|
|
198
|
+
<table>
|
|
199
|
+
<tr>
|
|
200
|
+
<td><strong>Inline</strong></td>
|
|
201
|
+
<td><strong>Tailwind</strong></td>
|
|
202
|
+
<td><strong>styled-components</strong></td>
|
|
203
|
+
</tr>
|
|
204
|
+
<tr>
|
|
205
|
+
<td>
|
|
206
|
+
|
|
207
|
+
```jsx
|
|
208
|
+
<ColumnCell style={{
|
|
209
|
+
padding: "0 16px",
|
|
210
|
+
fontWeight: 700,
|
|
211
|
+
}} />
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
</td>
|
|
215
|
+
<td>
|
|
216
|
+
|
|
217
|
+
```jsx
|
|
218
|
+
<ColumnCell className="px-4
|
|
219
|
+
font-bold text-sm" />
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
</td>
|
|
223
|
+
<td>
|
|
224
|
+
|
|
225
|
+
```jsx
|
|
226
|
+
const Col = styled(ColumnCell)`
|
|
227
|
+
padding: 0 16px;
|
|
228
|
+
font-weight: 700;
|
|
229
|
+
`;
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
</td>
|
|
233
|
+
</tr>
|
|
234
|
+
</table>
|
|
235
|
+
|
|
236
|
+
## Browser Support
|
|
237
|
+
|
|
238
|
+
| | Chrome | Firefox | Safari | Edge |
|
|
239
|
+
|---|---|---|---|---|
|
|
240
|
+
| **Desktop** | ✅ | ✅ | ✅ | ✅ |
|
|
241
|
+
| **Mobile** | ✅ | ✅ | ✅ | ✅ |
|
|
242
|
+
|
|
243
|
+
Mobile uses long-press to initiate drag.
|
|
244
|
+
|
|
245
|
+
## Contributing
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
git clone https://github.com/samiodeh1337/react-table-dnd.git
|
|
249
|
+
cd react-table-dnd
|
|
250
|
+
npm install
|
|
251
|
+
npm run dev # docs site at localhost:5173
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## License
|
|
255
|
+
|
|
256
|
+
[MIT](LICENSE) © [Sami Odeh](https://github.com/samiodeh1337)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { default as React, CSSProperties, ReactNode } from 'react';
|
|
2
|
+
interface BodyRowProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
style?: CSSProperties;
|
|
5
|
+
className?: string;
|
|
6
|
+
[propName: string]: any;
|
|
7
|
+
}
|
|
8
|
+
declare const BodyRow: React.FC<BodyRowProps>;
|
|
9
|
+
export default BodyRow;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { default as React, ReactNode } from 'react';
|
|
2
|
+
interface ColumnCellProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
style?: React.CSSProperties;
|
|
5
|
+
className?: string;
|
|
6
|
+
[propName: string]: any;
|
|
7
|
+
}
|
|
8
|
+
declare const ColumnCell: React.FC<ColumnCellProps>;
|
|
9
|
+
export default ColumnCell;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { default as React, CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import { DragType } from '../hooks/types';
|
|
3
|
+
export interface DraggableProps {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
id: number | string;
|
|
6
|
+
index: number;
|
|
7
|
+
type: DragType;
|
|
8
|
+
styles: CSSProperties;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
declare const Draggable: React.FC<DraggableProps>;
|
|
12
|
+
export default Draggable;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
interface RowCellProps {
|
|
3
|
+
children?: React.ReactNode;
|
|
4
|
+
width?: number;
|
|
5
|
+
index: number;
|
|
6
|
+
style?: React.CSSProperties;
|
|
7
|
+
className?: string;
|
|
8
|
+
[propName: string]: any;
|
|
9
|
+
}
|
|
10
|
+
declare const RowCell: React.FC<RowCellProps>;
|
|
11
|
+
export default RowCell;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { default as React, ReactNode } from 'react';
|
|
2
|
+
interface TableBodyProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
style?: React.CSSProperties;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
declare const TableBody: React.ForwardRefExoticComponent<TableBodyProps & React.RefAttributes<HTMLDivElement>>;
|
|
8
|
+
export default TableBody;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { default as React, ReactNode } from 'react';
|
|
2
|
+
import { DragEndResult, DragRange } from '../../hooks/types';
|
|
3
|
+
interface TableProviderProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
style?: React.CSSProperties;
|
|
7
|
+
onDragEnd?: (result: DragEndResult) => void;
|
|
8
|
+
renderPlaceholder?: () => ReactNode;
|
|
9
|
+
options?: {
|
|
10
|
+
columnDragRange: DragRange;
|
|
11
|
+
rowDragRange: DragRange;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
declare const TableProvider: React.ForwardRefExoticComponent<TableProviderProps & React.RefAttributes<HTMLDivElement>>;
|
|
15
|
+
export default TableProvider;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TableState, TableAction } from '../../hooks/types';
|
|
2
|
+
export interface TableStore {
|
|
3
|
+
getState: () => TableState;
|
|
4
|
+
dispatch: (action: TableAction) => void;
|
|
5
|
+
subscribe: (listener: () => void) => () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function createTableStore(reducer: (state: TableState, action: TableAction) => TableState, initialState: TableState): TableStore;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TableStore } from './store';
|
|
2
|
+
import { TableState, TableAction } from '../../hooks/types';
|
|
3
|
+
export declare const StoreContext: import('react').Context<TableStore | null>;
|
|
4
|
+
export declare const useTableStore: <T>(selector: (state: TableState) => T) => T;
|
|
5
|
+
export declare const useTableDispatch: () => ((action: TableAction) => void);
|
|
6
|
+
export declare const useTable: () => {
|
|
7
|
+
state: TableState;
|
|
8
|
+
dispatch: (action: TableAction) => void;
|
|
9
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { default as React, ReactNode } from 'react';
|
|
2
|
+
interface TableHeaderProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
style?: React.CSSProperties;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
declare const TableHeader: React.ForwardRefExoticComponent<TableHeaderProps & React.RefAttributes<HTMLDivElement>>;
|
|
8
|
+
export default TableHeader;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { default as TableContainer } from './TableContainer';
|
|
2
|
+
import { default as TableHeader } from './TableHeader';
|
|
3
|
+
import { default as ColumnCell } from './ColumnCell';
|
|
4
|
+
import { default as TableBody } from './TableBody';
|
|
5
|
+
import { default as BodyRow } from './BodyRow';
|
|
6
|
+
import { default as RowCell } from './RowCell';
|
|
7
|
+
import { default as DragHandle } from './DragHandle';
|
|
8
|
+
export { useTable, useTableStore, useTableDispatch } from './TableContainer/useTable';
|
|
9
|
+
export { TableContainer, TableHeader, ColumnCell, TableBody, BodyRow, RowCell, DragHandle };
|
|
10
|
+
export type { DragEndResult, DragRange, DragType } from '../hooks/types';
|
|
11
|
+
export type { DraggableProps } from './Draggable';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ColumnItem, RowItem } from '../hooks/types';
|
|
2
|
+
export declare const binarySearchDropIndex: (mouseY: number, items: RowItem[]) => number;
|
|
3
|
+
export declare const binarySearchDropIndexHeader: (mouseX: number, items: ColumnItem[]) => number;
|
|
4
|
+
export declare function isScrollbarClick(clientX: number, clientY: number, target: HTMLElement): boolean;
|
|
5
|
+
export declare const isIndexOutOfRange: (index: string | number, start?: number, end?: number) => boolean;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { MutableRefObject, RefObject } from 'react';
|
|
2
|
+
export type DragType = 'row' | 'column';
|
|
3
|
+
export interface DragEndResult {
|
|
4
|
+
sourceIndex: number;
|
|
5
|
+
targetIndex: number;
|
|
6
|
+
dragType: DragType;
|
|
7
|
+
}
|
|
8
|
+
export interface DraggedState {
|
|
9
|
+
initial: {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
};
|
|
13
|
+
translate: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
};
|
|
17
|
+
isDragging: boolean;
|
|
18
|
+
draggedID: string | null;
|
|
19
|
+
targetIndex: number | null;
|
|
20
|
+
sourceIndex: number | null;
|
|
21
|
+
}
|
|
22
|
+
export interface RectState {
|
|
23
|
+
draggedItemWidth: number;
|
|
24
|
+
draggedItemHeight: number;
|
|
25
|
+
}
|
|
26
|
+
export interface TableDimensions {
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
}
|
|
30
|
+
export interface DragRange {
|
|
31
|
+
start?: number;
|
|
32
|
+
end?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface Options {
|
|
35
|
+
columnDragRange: DragRange;
|
|
36
|
+
rowDragRange: DragRange;
|
|
37
|
+
defaultSizing: number;
|
|
38
|
+
}
|
|
39
|
+
export interface HookRefs {
|
|
40
|
+
tableRef: RefObject<HTMLDivElement | null>;
|
|
41
|
+
bodyRef: RefObject<HTMLDivElement | null>;
|
|
42
|
+
headerRef: RefObject<HTMLDivElement | null>;
|
|
43
|
+
cloneRef: RefObject<HTMLDivElement | null>;
|
|
44
|
+
placeholderRef: RefObject<HTMLDivElement | null>;
|
|
45
|
+
}
|
|
46
|
+
export interface TableState {
|
|
47
|
+
dragged: DraggedState;
|
|
48
|
+
dragType: DragType | null;
|
|
49
|
+
rect: RectState;
|
|
50
|
+
tableDimensions: TableDimensions;
|
|
51
|
+
refs: HookRefs;
|
|
52
|
+
bodyScrollBarWidth: number;
|
|
53
|
+
options: Options;
|
|
54
|
+
widths: number[];
|
|
55
|
+
columnIds: string[];
|
|
56
|
+
}
|
|
57
|
+
export type TableAction = {
|
|
58
|
+
type: 'setDragged';
|
|
59
|
+
value: Partial<DraggedState>;
|
|
60
|
+
} | {
|
|
61
|
+
type: 'setDragType';
|
|
62
|
+
value: DragType | null;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'setTableDimensions';
|
|
65
|
+
value: TableDimensions;
|
|
66
|
+
} | {
|
|
67
|
+
type: 'setRef';
|
|
68
|
+
refName: keyof HookRefs;
|
|
69
|
+
value: MutableRefObject<HTMLDivElement | null> | null;
|
|
70
|
+
} | {
|
|
71
|
+
type: 'setBodyScrollBarWidth';
|
|
72
|
+
value: number;
|
|
73
|
+
} | {
|
|
74
|
+
type: 'setWidths';
|
|
75
|
+
value: number[];
|
|
76
|
+
} | {
|
|
77
|
+
type: 'setColumnIds';
|
|
78
|
+
value: string[];
|
|
79
|
+
} | {
|
|
80
|
+
type: 'setOptions';
|
|
81
|
+
value: Partial<Options>;
|
|
82
|
+
} | {
|
|
83
|
+
type: 'dragStart';
|
|
84
|
+
value: {
|
|
85
|
+
rect: RectState;
|
|
86
|
+
dragged: {
|
|
87
|
+
initial: {
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
};
|
|
91
|
+
translate: {
|
|
92
|
+
x: number;
|
|
93
|
+
y: number;
|
|
94
|
+
};
|
|
95
|
+
draggedID: string | null;
|
|
96
|
+
isDragging: boolean;
|
|
97
|
+
sourceIndex: number;
|
|
98
|
+
};
|
|
99
|
+
dragType: DragType | null;
|
|
100
|
+
tableDimensions: TableDimensions;
|
|
101
|
+
};
|
|
102
|
+
} | {
|
|
103
|
+
type: 'dragEnd';
|
|
104
|
+
value: {
|
|
105
|
+
targetIndex: number | null;
|
|
106
|
+
sourceIndex: number | null;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
export interface RowItem {
|
|
110
|
+
height: number;
|
|
111
|
+
itemTop: number;
|
|
112
|
+
itemBottom: number;
|
|
113
|
+
index: string;
|
|
114
|
+
}
|
|
115
|
+
export interface ColumnItem {
|
|
116
|
+
left: number;
|
|
117
|
+
width: number;
|
|
118
|
+
itemLeft: number;
|
|
119
|
+
itemRight: number;
|
|
120
|
+
index: string;
|
|
121
|
+
}
|
|
122
|
+
export interface Point {
|
|
123
|
+
x: number;
|
|
124
|
+
y: number;
|
|
125
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { HookRefs } from './types';
|
|
2
|
+
declare const useAutoScroll: (refs: HookRefs) => {
|
|
3
|
+
startAutoScroll: (speed: number, ref: HTMLDivElement, dir: "horizontal" | "vertical") => void;
|
|
4
|
+
stopAutoScroll: () => void;
|
|
5
|
+
setContainerRect: (rect: DOMRect) => void;
|
|
6
|
+
isAutoScrollingVertical: import('react').RefObject<boolean>;
|
|
7
|
+
isAutoScrollingHorizontal: import('react').RefObject<boolean>;
|
|
8
|
+
pointerRef: import('react').RefObject<{
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
}>;
|
|
12
|
+
BodyScrollHandle: import('react').UIEventHandler<HTMLDivElement>;
|
|
13
|
+
HeaderScrollHandle: import('react').UIEventHandler<HTMLDivElement>;
|
|
14
|
+
};
|
|
15
|
+
export default useAutoScroll;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Dispatch } from 'react';
|
|
2
|
+
import { HookRefs, DraggedState, DragType, Options, DragEndResult, TableAction } from './types';
|
|
3
|
+
declare const useDragContextEvents: (refs: HookRefs, dragged: DraggedState, dispatch: Dispatch<TableAction>, dragType: DragType | null, options: Options, onDragEnd: ((result: DragEndResult) => void) | undefined) => {
|
|
4
|
+
dragStart: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
5
|
+
touchStart: (e: React.TouchEvent<HTMLDivElement>) => void;
|
|
6
|
+
};
|
|
7
|
+
export default useDragContextEvents;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HookRefs, Options, RowItem, ColumnItem, DragType, Point } from './types';
|
|
2
|
+
interface DropTargetResult {
|
|
3
|
+
computeRowItems: () => RowItem[] | null;
|
|
4
|
+
computeColumnItems: () => ColumnItem[] | null;
|
|
5
|
+
resolveDropIndex: (clientX: number, clientY: number, dtype: DragType | null, rect: DOMRect, bodyScrollTop: number, initial: Point, size: {
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}) => number;
|
|
9
|
+
cachedItemsRef: React.RefObject<RowItem[] | ColumnItem[] | null>;
|
|
10
|
+
cachedContainerRef: React.RefObject<DOMRect | null>;
|
|
11
|
+
}
|
|
12
|
+
declare const useDropTarget: (refs: HookRefs, options: Options) => DropTargetResult;
|
|
13
|
+
export default useDropTarget;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { HookRefs } from './types';
|
|
2
|
+
import { IndexMap } from './useShiftTransforms';
|
|
3
|
+
interface IndexMapsResult {
|
|
4
|
+
rowIndexMapRef: React.RefObject<IndexMap>;
|
|
5
|
+
colIndexMapRef: React.RefObject<IndexMap>;
|
|
6
|
+
cellIndexMapRef: React.RefObject<Map<number, HTMLElement[]>>;
|
|
7
|
+
mapStaleRef: React.RefObject<boolean>;
|
|
8
|
+
buildMaps: (dtype: string | null | undefined, body: HTMLElement | null) => void;
|
|
9
|
+
rebuildRowMap: (container: HTMLElement) => void;
|
|
10
|
+
rebuildColumnMaps: (container: HTMLElement, header: HTMLElement | null) => void;
|
|
11
|
+
checkStaleness: () => void;
|
|
12
|
+
clearMaps: () => void;
|
|
13
|
+
}
|
|
14
|
+
declare const useIndexMaps: (refs: HookRefs) => IndexMapsResult;
|
|
15
|
+
export default useIndexMaps;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HookRefs } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Mobile long-press-to-drag.
|
|
4
|
+
* this hook implements JS-based scrolling with momentum/inertia when
|
|
5
|
+
* the long press is cancelled (user is scrolling, not dragging).
|
|
6
|
+
*
|
|
7
|
+
* preventDefault() is called on touchmove to stop any residual
|
|
8
|
+
* browser behavior during the 300ms detection window.
|
|
9
|
+
*/
|
|
10
|
+
export default function useLongPress(refs: HookRefs, beginDrag: (e: React.TouchEvent<HTMLDivElement>, clientX: number, clientY: number) => void, dragEnd: () => void, onDragMove: (clientX: number, clientY: number) => void): {
|
|
11
|
+
touchStart: (e: React.TouchEvent<HTMLDivElement>) => void;
|
|
12
|
+
cancelLongPress: () => void;
|
|
13
|
+
isTouchActiveRef: import('react').RefObject<boolean>;
|
|
14
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { HookRefs, DragType } from './types';
|
|
2
|
+
export type IndexMap = Map<number, {
|
|
3
|
+
outer: HTMLElement;
|
|
4
|
+
inner: HTMLElement;
|
|
5
|
+
}>;
|
|
6
|
+
interface ShiftTransformsResult {
|
|
7
|
+
applyShiftTransforms: (sourceIndex: number | null, targetIndex: number | null, dtype: DragType | null) => void;
|
|
8
|
+
clearShiftTransforms: () => void;
|
|
9
|
+
prevTargetIndexRef: React.RefObject<number | null>;
|
|
10
|
+
draggedSizeRef: React.RefObject<{
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
declare const useShiftTransforms: (refs: HookRefs, rowIndexMapRef: React.RefObject<IndexMap>, colIndexMapRef: React.RefObject<IndexMap>, cellIndexMapRef: React.RefObject<Map<number, HTMLElement[]>>) => ShiftTransformsResult;
|
|
16
|
+
export default useShiftTransforms;
|