dnd-block-tree 0.1.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/dist/index.d.mts +366 -0
- package/dist/index.d.ts +366 -0
- package/dist/index.js +871 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +848 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import * as _dnd_kit_core from '@dnd-kit/core';
|
|
4
|
+
import { UniqueIdentifier, CollisionDetection } from '@dnd-kit/core';
|
|
5
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Base block interface - extend this for your custom block types
|
|
9
|
+
*/
|
|
10
|
+
interface BaseBlock {
|
|
11
|
+
id: string;
|
|
12
|
+
type: string;
|
|
13
|
+
parentId: string | null;
|
|
14
|
+
order: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Normalized index structure for efficient tree operations
|
|
18
|
+
*/
|
|
19
|
+
interface BlockIndex<T extends BaseBlock = BaseBlock> {
|
|
20
|
+
byId: Map<string, T>;
|
|
21
|
+
byParent: Map<string | null, string[]>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Props passed to non-container block renderers
|
|
25
|
+
*/
|
|
26
|
+
interface BlockRendererProps<T extends BaseBlock = BaseBlock> {
|
|
27
|
+
block: T;
|
|
28
|
+
children?: ReactNode;
|
|
29
|
+
isDragging?: boolean;
|
|
30
|
+
isOver?: boolean;
|
|
31
|
+
depth: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Props passed to container block renderers (blocks that can have children)
|
|
35
|
+
*/
|
|
36
|
+
interface ContainerRendererProps<T extends BaseBlock = BaseBlock> extends BlockRendererProps<T> {
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
isExpanded: boolean;
|
|
39
|
+
onToggleExpand: () => void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get the appropriate props type for a renderer based on whether it's a container type
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* type SectionProps = RendererPropsFor<MyBlock, 'section', typeof CONTAINER_TYPES>
|
|
46
|
+
* // If CONTAINER_TYPES includes 'section', this is ContainerRendererProps
|
|
47
|
+
* // Otherwise, it's BlockRendererProps
|
|
48
|
+
*/
|
|
49
|
+
type RendererPropsFor<T extends BaseBlock, K extends T['type'], C extends readonly string[]> = K extends C[number] ? ContainerRendererProps<T & {
|
|
50
|
+
type: K;
|
|
51
|
+
}> : BlockRendererProps<T & {
|
|
52
|
+
type: K;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Map of block types to their renderers with automatic container detection
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const CONTAINER_TYPES = ['section'] as const
|
|
59
|
+
*
|
|
60
|
+
* const renderers: BlockRenderers<MyBlock, typeof CONTAINER_TYPES> = {
|
|
61
|
+
* section: (props) => <Section {...props} />, // props includes isExpanded, onToggleExpand
|
|
62
|
+
* task: (props) => <Task {...props} />, // props is BlockRendererProps
|
|
63
|
+
* }
|
|
64
|
+
*/
|
|
65
|
+
type BlockRenderers<T extends BaseBlock = BaseBlock, C extends readonly string[] = readonly string[]> = {
|
|
66
|
+
[K in T['type']]: (props: RendererPropsFor<T, K, C>) => ReactNode;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Internal renderer type used by TreeRenderer (less strict for flexibility)
|
|
70
|
+
*/
|
|
71
|
+
type InternalRenderers<T extends BaseBlock = BaseBlock> = {
|
|
72
|
+
[K in T['type']]: (props: BlockRendererProps<T> | ContainerRendererProps<T>) => ReactNode;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Block action types for the reducer
|
|
76
|
+
*/
|
|
77
|
+
type BlockAction<T extends BaseBlock> = {
|
|
78
|
+
type: 'ADD_ITEM';
|
|
79
|
+
payload: T;
|
|
80
|
+
} | {
|
|
81
|
+
type: 'DELETE_ITEM';
|
|
82
|
+
payload: {
|
|
83
|
+
id: string;
|
|
84
|
+
};
|
|
85
|
+
} | {
|
|
86
|
+
type: 'SET_ALL';
|
|
87
|
+
payload: T[];
|
|
88
|
+
} | {
|
|
89
|
+
type: 'MOVE_ITEM';
|
|
90
|
+
payload: {
|
|
91
|
+
activeId: UniqueIdentifier;
|
|
92
|
+
targetZone: string;
|
|
93
|
+
};
|
|
94
|
+
} | {
|
|
95
|
+
type: 'INSERT_ITEM';
|
|
96
|
+
payload: {
|
|
97
|
+
item: T;
|
|
98
|
+
parentId: string | null;
|
|
99
|
+
index: number;
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Drag overlay renderer
|
|
104
|
+
*/
|
|
105
|
+
interface DragOverlayProps$1<T extends BaseBlock = BaseBlock> {
|
|
106
|
+
block: T;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* BlockTree configuration options
|
|
110
|
+
*/
|
|
111
|
+
interface BlockTreeConfig {
|
|
112
|
+
activationDistance?: number;
|
|
113
|
+
previewDebounce?: number;
|
|
114
|
+
dropZoneHeight?: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Block state context value
|
|
118
|
+
*/
|
|
119
|
+
interface BlockStateContextValue<T extends BaseBlock = BaseBlock> {
|
|
120
|
+
blocks: T[];
|
|
121
|
+
blockMap: Map<string, T>;
|
|
122
|
+
childrenMap: Map<string | null, T[]>;
|
|
123
|
+
indexMap: Map<string, number>;
|
|
124
|
+
normalizedIndex: BlockIndex<T>;
|
|
125
|
+
createItem: (type: T['type'], parentId?: string | null) => T;
|
|
126
|
+
insertItem: (type: T['type'], referenceId: string, position: 'before' | 'after') => T;
|
|
127
|
+
deleteItem: (id: string) => void;
|
|
128
|
+
moveItem: (activeId: UniqueIdentifier, targetZone: string) => void;
|
|
129
|
+
setAll: (blocks: T[]) => void;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Tree state context value (UI state)
|
|
133
|
+
*/
|
|
134
|
+
interface TreeStateContextValue<T extends BaseBlock = BaseBlock> {
|
|
135
|
+
activeId: string | null;
|
|
136
|
+
activeBlock: T | null;
|
|
137
|
+
hoverZone: string | null;
|
|
138
|
+
expandedMap: Record<string, boolean>;
|
|
139
|
+
effectiveBlocks: T[];
|
|
140
|
+
blocksByParent: Map<string | null, T[]>;
|
|
141
|
+
setActiveId: (id: string | null) => void;
|
|
142
|
+
setHoverZone: (zone: string | null) => void;
|
|
143
|
+
toggleExpand: (id: string) => void;
|
|
144
|
+
setExpandAll: (expanded: boolean) => void;
|
|
145
|
+
handleHover: (zoneId: string, parentId: string | null) => void;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Drop zone types
|
|
149
|
+
*/
|
|
150
|
+
type DropZoneType = 'before' | 'after' | 'into';
|
|
151
|
+
/**
|
|
152
|
+
* Extract zone type from zone ID
|
|
153
|
+
*/
|
|
154
|
+
declare function getDropZoneType(zoneId: string): DropZoneType;
|
|
155
|
+
/**
|
|
156
|
+
* Extract block ID from zone ID
|
|
157
|
+
*/
|
|
158
|
+
declare function extractBlockId(zoneId: string): string;
|
|
159
|
+
/**
|
|
160
|
+
* Block state provider props
|
|
161
|
+
*/
|
|
162
|
+
interface BlockStateProviderProps<T extends BaseBlock = BaseBlock> {
|
|
163
|
+
children: ReactNode;
|
|
164
|
+
initialBlocks?: T[];
|
|
165
|
+
containerTypes?: readonly string[];
|
|
166
|
+
onChange?: (blocks: T[]) => void;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Tree state provider props
|
|
170
|
+
*/
|
|
171
|
+
interface TreeStateProviderProps<T extends BaseBlock = BaseBlock> {
|
|
172
|
+
children: ReactNode;
|
|
173
|
+
blocks: T[];
|
|
174
|
+
blockMap: Map<string, T>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Custom collision detection that scores drop zones by distance to nearest edge.
|
|
179
|
+
* Uses edge-distance scoring with a bottom bias for more natural drag behavior.
|
|
180
|
+
*
|
|
181
|
+
* Key features:
|
|
182
|
+
* - Scores by distance to nearest edge (top or bottom) of droppable
|
|
183
|
+
* - Applies -5px bias to elements below pointer midpoint (prefers dropping below)
|
|
184
|
+
* - Returns single winner (lowest score)
|
|
185
|
+
*/
|
|
186
|
+
declare const weightedVerticalCollision: CollisionDetection;
|
|
187
|
+
/**
|
|
188
|
+
* Simple closest center collision (fallback)
|
|
189
|
+
*/
|
|
190
|
+
declare const closestCenterCollision: CollisionDetection;
|
|
191
|
+
|
|
192
|
+
interface SensorConfig {
|
|
193
|
+
activationDistance?: number;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Create configured sensors with activation distance constraint.
|
|
197
|
+
* The activation distance prevents accidental drags while still allowing clicks.
|
|
198
|
+
*
|
|
199
|
+
* @param config - Sensor configuration
|
|
200
|
+
* @returns Configured sensors for DndContext
|
|
201
|
+
*/
|
|
202
|
+
declare function useConfiguredSensors(config?: SensorConfig): _dnd_kit_core.SensorDescriptor<_dnd_kit_core.SensorOptions>[];
|
|
203
|
+
/**
|
|
204
|
+
* Get sensor configuration for manual setup
|
|
205
|
+
*/
|
|
206
|
+
declare function getSensorConfig(activationDistance?: number): {
|
|
207
|
+
pointer: {
|
|
208
|
+
activationConstraint: {
|
|
209
|
+
distance: number;
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
touch: {
|
|
213
|
+
activationConstraint: {
|
|
214
|
+
distance: number;
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
interface BlockTreeProps<T extends BaseBlock, C extends readonly T['type'][] = readonly T['type'][]> {
|
|
220
|
+
/** Current blocks array */
|
|
221
|
+
blocks: T[];
|
|
222
|
+
/** Block renderers for each type */
|
|
223
|
+
renderers: BlockRenderers<T, C>;
|
|
224
|
+
/** Block types that can have children */
|
|
225
|
+
containerTypes?: C;
|
|
226
|
+
/** Called when blocks are reordered */
|
|
227
|
+
onChange?: (blocks: T[]) => void;
|
|
228
|
+
/** Custom drag overlay renderer */
|
|
229
|
+
dragOverlay?: (block: T) => ReactNode;
|
|
230
|
+
/** Activation distance in pixels (default: 8) */
|
|
231
|
+
activationDistance?: number;
|
|
232
|
+
/** Preview debounce in ms (default: 150) */
|
|
233
|
+
previewDebounce?: number;
|
|
234
|
+
/** Root container className */
|
|
235
|
+
className?: string;
|
|
236
|
+
/** Drop zone className */
|
|
237
|
+
dropZoneClassName?: string;
|
|
238
|
+
/** Active drop zone className */
|
|
239
|
+
dropZoneActiveClassName?: string;
|
|
240
|
+
/** Indent className for nested items */
|
|
241
|
+
indentClassName?: string;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Main BlockTree component
|
|
245
|
+
* Provides drag-and-drop functionality for hierarchical block structures
|
|
246
|
+
*/
|
|
247
|
+
declare function BlockTree<T extends BaseBlock, C extends readonly T['type'][] = readonly T['type'][]>({ blocks, renderers, containerTypes, onChange, dragOverlay, activationDistance, previewDebounce, className, dropZoneClassName, dropZoneActiveClassName, indentClassName, }: BlockTreeProps<T, C>): react_jsx_runtime.JSX.Element;
|
|
248
|
+
|
|
249
|
+
interface TreeRendererProps<T extends BaseBlock> {
|
|
250
|
+
blocks: T[];
|
|
251
|
+
blocksByParent: Map<string | null, T[]>;
|
|
252
|
+
parentId: string | null;
|
|
253
|
+
activeId: string | null;
|
|
254
|
+
expandedMap: Record<string, boolean>;
|
|
255
|
+
renderers: InternalRenderers<T>;
|
|
256
|
+
containerTypes: readonly string[];
|
|
257
|
+
onHover: (zoneId: string, parentId: string | null) => void;
|
|
258
|
+
onToggleExpand: (id: string) => void;
|
|
259
|
+
depth?: number;
|
|
260
|
+
dropZoneClassName?: string;
|
|
261
|
+
dropZoneActiveClassName?: string;
|
|
262
|
+
indentClassName?: string;
|
|
263
|
+
rootClassName?: string;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Recursive tree renderer with smart drop zones
|
|
267
|
+
*/
|
|
268
|
+
declare function TreeRenderer<T extends BaseBlock>({ blocks, blocksByParent, parentId, activeId, expandedMap, renderers, containerTypes, onHover, onToggleExpand, depth, dropZoneClassName, dropZoneActiveClassName, indentClassName, rootClassName, }: TreeRendererProps<T>): react_jsx_runtime.JSX.Element;
|
|
269
|
+
|
|
270
|
+
interface DropZoneProps {
|
|
271
|
+
id: string;
|
|
272
|
+
parentId: string | null;
|
|
273
|
+
onHover: (zoneId: string, parentId: string | null) => void;
|
|
274
|
+
activeId: string | null;
|
|
275
|
+
className?: string;
|
|
276
|
+
activeClassName?: string;
|
|
277
|
+
height?: number;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Drop zone indicator component
|
|
281
|
+
* Shows where blocks can be dropped
|
|
282
|
+
*/
|
|
283
|
+
declare function DropZoneComponent({ id, parentId, onHover, activeId, className, activeClassName, height, }: DropZoneProps): react_jsx_runtime.JSX.Element | null;
|
|
284
|
+
declare const DropZone: react.MemoExoticComponent<typeof DropZoneComponent>;
|
|
285
|
+
|
|
286
|
+
interface DragOverlayProps<T extends BaseBlock> {
|
|
287
|
+
activeBlock: T | null;
|
|
288
|
+
children?: (block: T) => ReactNode;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Default drag overlay component
|
|
292
|
+
* Shows a preview of the dragged item
|
|
293
|
+
*/
|
|
294
|
+
declare function DragOverlay<T extends BaseBlock>({ activeBlock, children, }: DragOverlayProps<T>): react_jsx_runtime.JSX.Element;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Create block state context and hooks
|
|
298
|
+
*/
|
|
299
|
+
declare function createBlockState<T extends BaseBlock>(): {
|
|
300
|
+
BlockStateProvider: ({ children, initialBlocks, containerTypes, onChange, }: BlockStateProviderProps<T>) => react_jsx_runtime.JSX.Element;
|
|
301
|
+
useBlockState: () => BlockStateContextValue<T>;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
interface CreateTreeStateOptions<T extends BaseBlock> {
|
|
305
|
+
previewDebounce?: number;
|
|
306
|
+
containerTypes?: string[];
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Create tree state context and hooks
|
|
310
|
+
* Handles UI state: active drag, hover zone, expand/collapse, virtual preview
|
|
311
|
+
*/
|
|
312
|
+
declare function createTreeState<T extends BaseBlock>(options?: CreateTreeStateOptions<T>): {
|
|
313
|
+
TreeStateProvider: ({ children, blocks, blockMap }: TreeStateProviderProps<T>) => react_jsx_runtime.JSX.Element;
|
|
314
|
+
useTreeState: () => TreeStateContextValue<T>;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Clone a Map
|
|
319
|
+
*/
|
|
320
|
+
declare function cloneMap<K, V>(map: Map<K, V>): Map<K, V>;
|
|
321
|
+
/**
|
|
322
|
+
* Clone a parent map with arrays
|
|
323
|
+
*/
|
|
324
|
+
declare function cloneParentMap(map: Map<string | null, string[]>): Map<string | null, string[]>;
|
|
325
|
+
/**
|
|
326
|
+
* Compute normalized index from flat block array
|
|
327
|
+
*/
|
|
328
|
+
declare function computeNormalizedIndex<T extends BaseBlock>(blocks: T[]): BlockIndex<T>;
|
|
329
|
+
/**
|
|
330
|
+
* Build ordered flat array from BlockIndex
|
|
331
|
+
*/
|
|
332
|
+
declare function buildOrderedBlocks<T extends BaseBlock>(index: BlockIndex<T>, containerTypes?: readonly string[]): T[];
|
|
333
|
+
/**
|
|
334
|
+
* Reparent a block based on drop zone ID
|
|
335
|
+
*
|
|
336
|
+
* @param state - Current block index
|
|
337
|
+
* @param activeId - ID of the dragged block
|
|
338
|
+
* @param targetZone - Drop zone ID (e.g., "after-uuid", "before-uuid", "into-uuid")
|
|
339
|
+
* @param containerTypes - Block types that can have children
|
|
340
|
+
*/
|
|
341
|
+
declare function reparentBlockIndex<T extends BaseBlock>(state: BlockIndex<T>, activeId: UniqueIdentifier, targetZone: string, containerTypes?: readonly string[]): BlockIndex<T>;
|
|
342
|
+
/**
|
|
343
|
+
* Get all descendant IDs of a block
|
|
344
|
+
*/
|
|
345
|
+
declare function getDescendantIds<T extends BaseBlock>(state: BlockIndex<T>, parentId: string): Set<string>;
|
|
346
|
+
/**
|
|
347
|
+
* Delete a block and all its descendants
|
|
348
|
+
*/
|
|
349
|
+
declare function deleteBlockAndDescendants<T extends BaseBlock>(state: BlockIndex<T>, id: string): BlockIndex<T>;
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Extract UUID from a zone ID by removing the prefix (before-, after-, into-)
|
|
353
|
+
*/
|
|
354
|
+
declare function extractUUID(id: string, pattern?: string): string;
|
|
355
|
+
/**
|
|
356
|
+
* Create a debounced function
|
|
357
|
+
*/
|
|
358
|
+
declare function debounce<Args extends unknown[]>(fn: (...args: Args) => void, delay: number): ((...args: Args) => void) & {
|
|
359
|
+
cancel: () => void;
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* Generate a unique ID (simple implementation)
|
|
363
|
+
*/
|
|
364
|
+
declare function generateId(): string;
|
|
365
|
+
|
|
366
|
+
export { type BaseBlock, type BlockAction, type BlockIndex, type BlockRendererProps, type BlockRenderers, type BlockStateContextValue, type BlockStateProviderProps, BlockTree, type BlockTreeConfig, type BlockTreeProps, type ContainerRendererProps, DragOverlay, type DragOverlayProps$1 as DragOverlayProps, DropZone, type DropZoneProps, type DropZoneType, type RendererPropsFor, TreeRenderer, type TreeRendererProps, type TreeStateContextValue, type TreeStateProviderProps, buildOrderedBlocks, cloneMap, cloneParentMap, closestCenterCollision, computeNormalizedIndex, createBlockState, createTreeState, debounce, deleteBlockAndDescendants, extractBlockId, extractUUID, generateId, getDescendantIds, getDropZoneType, getSensorConfig, reparentBlockIndex, useConfiguredSensors, weightedVerticalCollision };
|