@keenmate/svelte-treeview 4.5.0 → 4.7.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.
@@ -4,102 +4,73 @@
4
4
  import {getContext, onDestroy, type Snippet} from "svelte"
5
5
  import type {Ltree, DropPosition, DropOperation} from "../ltree/types.js"
6
6
  import type {RenderCoordinator} from "./RenderCoordinator.svelte.js"
7
+ import type {NodeCallbacks, NodeConfig} from "./Tree.svelte"
7
8
  import { uiLogger } from "../logger.js"
8
9
 
9
10
  // Define component props interface
11
+ // Callbacks and config come from context, drag state comes as props
10
12
  interface Props {
11
13
  node: LTreeNode<T>;
12
14
  children?: Snippet<[T]>; // Keep the general children slot for backward compatibility
13
- onNodeClicked?: (node: LTreeNode<T>) => void;
14
- onNodeRightClicked?: (node: LTreeNode<T>, event: MouseEvent) => void;
15
- onNodeDragStart?: (node: LTreeNode<T>, event: DragEvent) => void;
16
- onNodeDragOver?: (node: LTreeNode<T>, event: DragEvent) => void;
17
- onNodeDragLeave?: (node: LTreeNode<T>, event: DragEvent) => void;
18
- onNodeDrop?: (node: LTreeNode<T>, event: DragEvent) => void;
19
- onZoneDrop?: (node: LTreeNode<T>, position: DropPosition, event: DragEvent) => void;
20
-
21
- // Touch drag handlers for mobile support
22
- onTouchDragStart?: (node: LTreeNode<T>, event: TouchEvent) => void;
23
- onTouchDragMove?: (node: LTreeNode<T>, event: TouchEvent) => void;
24
- onTouchDragEnd?: (node: LTreeNode<T>, event: TouchEvent) => void;
25
-
26
- // BEHAVIOUR
27
- shouldToggleOnNodeClick?: boolean | null | undefined;
28
15
 
29
16
  // Progressive rendering
30
17
  progressiveRender?: boolean;
31
18
  renderBatchSize?: number;
32
19
 
33
- // VISUALS
34
- expandIconClass?: string | null | undefined;
35
- collapseIconClass?: string | null | undefined;
36
- leafIconClass?: string | null | undefined;
37
- selectedNodeClass?: string | null | undefined;
38
- dragOverNodeClass?: string | null | undefined;
39
- isDraggedNode?: boolean | null | undefined;
40
-
41
- // Drag position indicators
20
+ // Drag state (passed as props for efficient Svelte diffing)
21
+ isDraggedNode?: boolean;
42
22
  isDragInProgress?: boolean;
43
- hoveredNodeForDropPath?: string | null; // Path of node being hovered for drop
23
+ hoveredNodeForDropPath?: string | null;
44
24
  activeDropPosition?: DropPosition | null;
25
+ dropOperation?: DropOperation;
45
26
 
46
- // Drop zone configuration
47
- dropZoneMode?: 'floating' | 'glow'; // 'floating' = original floating zones, 'glow' = border glow indicators
48
- dropZoneLayout?: 'around' | 'above' | 'below' | 'wave' | 'wave2';
49
- dropZoneStart?: number | string; // number = percentage (0-100), string = any CSS value ("33%", "50px", "3rem")
50
- dropZoneMaxWidth?: number; // max width in pixels for wave layouts
51
- dropOperation?: DropOperation; // Current drag operation ('move' or 'copy')
52
- allowCopy?: boolean; // Whether copy operation is allowed (Ctrl+drag)
27
+ // Flat rendering mode
28
+ flatMode?: boolean; // When true, don't render children (Tree handles flat rendering)
29
+ flatIndentSize?: string; // CSS value for per-level indentation in flat mode
53
30
  }
54
31
 
55
32
  // Destructure props using Svelte 5 syntax
56
33
  let {
57
34
  node,
58
35
  children = undefined,
59
- onNodeClicked,
60
- onNodeRightClicked,
61
- onNodeDragStart,
62
- onNodeDragOver,
63
- onNodeDragLeave,
64
- onNodeDrop,
65
- onZoneDrop,
66
-
67
- // Touch drag handlers for mobile support
68
- onTouchDragStart,
69
- onTouchDragMove,
70
- onTouchDragEnd,
71
-
72
- // BEHAVIOUR
73
- shouldToggleOnNodeClick = true,
74
36
 
75
37
  // Progressive rendering
76
38
  progressiveRender = false,
77
39
  renderBatchSize = 50,
78
40
 
79
- // VISUALS
80
- expandIconClass = "ltree-icon-expand",
81
- collapseIconClass = "ltree-icon-collapse",
82
- leafIconClass = "ltree-icon-leaf",
83
- selectedNodeClass,
84
- dragOverNodeClass,
41
+ // Drag state
85
42
  isDraggedNode = false,
86
-
87
- // Drag position indicators
88
43
  isDragInProgress = false,
89
44
  hoveredNodeForDropPath = null,
90
45
  activeDropPosition = null,
91
-
92
- // Drop zone configuration
93
- dropZoneMode = 'glow',
94
- dropZoneLayout = 'around',
95
- dropZoneStart = 33,
96
- dropZoneMaxWidth = 120,
97
46
  dropOperation = 'move',
98
- allowCopy = false,
47
+
48
+ // Flat rendering mode
49
+ flatMode = false,
50
+ flatIndentSize = '1.5rem',
99
51
  }: Props = $props()
100
52
 
53
+ // Get stable references from context (avoids prop drilling and re-renders from inline functions)
54
+ const callbacks = getContext<NodeCallbacks<T>>('NodeCallbacks');
55
+ const config = getContext<NodeConfig>('NodeConfig');
56
+
57
+ // Destructure config for convenience (these are stable references)
58
+ const {
59
+ shouldToggleOnNodeClick,
60
+ expandIconClass,
61
+ collapseIconClass,
62
+ leafIconClass,
63
+ selectedNodeClass,
64
+ dragOverNodeClass,
65
+ dropZoneMode,
66
+ dropZoneLayout,
67
+ dropZoneStart,
68
+ dropZoneMaxWidth,
69
+ allowCopy,
70
+ } = config;
71
+
101
72
  // Compute if THIS node is the one being hovered for drop
102
- const isHoveredForDrop = $derived(hoveredNodeForDropPath === node.path)
73
+ const isHoveredForDrop = $derived(hoveredNodeForDropPath === node.path);
103
74
 
104
75
  // Format dropZoneStart - number = percentage, string = as-is
105
76
  const formattedDropZoneStart = $derived(
@@ -118,53 +89,96 @@
118
89
  // Track glow position for glow mode
119
90
  let glowPosition = $state<'above' | 'below' | 'child' | null>(null);
120
91
 
92
+ // Get allowed drop positions for this node (empty/undefined = all allowed)
93
+ // Uses tree.getNodeAllowedDropPositions which checks callback > member > node property
94
+ const allowedPositions = $derived(tree.getNodeAllowedDropPositions(node));
95
+
96
+ // Check if a position is allowed for this node
97
+ function isPositionAllowed(position: DropPosition): boolean {
98
+ if (!allowedPositions || allowedPositions.length === 0) {
99
+ return true; // All positions allowed by default
100
+ }
101
+ return allowedPositions.includes(position);
102
+ }
103
+
121
104
  // Calculate glow position based on mouse position in the node row
122
- function calculateGlowPosition(event: DragEvent, element: HTMLElement): 'above' | 'below' | 'child' {
105
+ // Respects allowedDropPositions - snaps to nearest allowed position
106
+ function calculateGlowPosition(event: DragEvent, element: HTMLElement): 'above' | 'below' | 'child' | null {
123
107
  const rect = element.getBoundingClientRect();
124
108
  const x = event.clientX - rect.left;
125
109
  const y = event.clientY - rect.top;
126
110
  const width = rect.width;
127
111
  const height = rect.height;
128
112
 
129
- // Right half = child
113
+ // Calculate the ideal position based on mouse position
114
+ let idealPosition: DropPosition;
130
115
  if (x > width / 2) {
131
- return 'child';
116
+ idealPosition = 'child';
117
+ } else if (y < height / 2) {
118
+ idealPosition = 'above';
119
+ } else {
120
+ idealPosition = 'below';
121
+ }
122
+
123
+ // If no restrictions, return the ideal position
124
+ if (!allowedPositions || allowedPositions.length === 0) {
125
+ return idealPosition;
132
126
  }
133
- // Left half, top 50% = above
134
- if (y < height / 2) {
135
- return 'above';
127
+
128
+ // If the ideal position is allowed, use it
129
+ if (allowedPositions.includes(idealPosition)) {
130
+ return idealPosition;
136
131
  }
137
- // Left half, bottom 50% = below
138
- return 'below';
132
+
133
+ // Otherwise, snap to the nearest allowed position
134
+ // Priority: if only one position allowed, use that
135
+ if (allowedPositions.length === 1) {
136
+ return allowedPositions[0];
137
+ }
138
+
139
+ // Multiple positions allowed but not the ideal one
140
+ // For above/below: pick based on Y position
141
+ // For child: pick based on what's available
142
+ if (allowedPositions.includes('above') && allowedPositions.includes('below')) {
143
+ // Both above and below allowed, pick based on Y
144
+ return y < height / 2 ? 'above' : 'below';
145
+ }
146
+
147
+ // Return the first allowed position
148
+ return allowedPositions[0];
139
149
  }
140
150
 
141
151
  // Convert reactive statements to derived values
142
- const childrenArray = $derived(Object.values(node?.children || []))
143
- const childrenWithData = $derived(Object.values(node?.children || []))
152
+ // In flat mode, children rendering is handled by Tree.svelte, so we skip these computations
153
+ const childrenArray = $derived(!flatMode ? Object.values(node?.children || []) : [])
144
154
  const hasChildren = $derived(node?.hasChildren || false)
145
155
  const indentStyle = $derived(
146
- `margin-left: var(--tree-node-indent-per-level, 0.5rem)`,
156
+ flatMode
157
+ ? `margin-left: calc(${(node?.level || 1) - 1} * ${flatIndentSize})`
158
+ : `margin-left: var(--tree-node-indent-per-level, 0.5rem)`,
147
159
  )
148
160
 
149
- // Progressive rendering state
161
+ // Progressive rendering state - only used in recursive mode
150
162
  let renderedCount = $state(0);
151
163
  let unregisterFromCoordinator: (() => void) | null = null;
152
164
  let lastExpandedState = false;
153
165
  let lastChildrenLength = 0;
154
166
 
155
- // Get the children to render (all or progressive slice)
167
+ // Get the children to render (all or progressive slice) - only used in recursive mode
156
168
  const childrenToRender = $derived(
157
- progressiveRender && renderCoordinator
169
+ !flatMode && progressiveRender && renderCoordinator
158
170
  ? childrenArray.slice(0, renderedCount)
159
171
  : childrenArray
160
172
  );
161
173
  const hasMoreToRender = $derived(
162
- progressiveRender && renderCoordinator && renderedCount < childrenArray.length
174
+ !flatMode && progressiveRender && renderCoordinator && renderedCount < childrenArray.length
163
175
  );
164
176
 
165
177
  // Handle expansion state changes - use coordinator for progressive rendering
166
- // Only react to isExpanded changes, not renderedCount changes
178
+ // Skip entirely in flat mode since Tree.svelte handles children rendering
167
179
  $effect(() => {
180
+ if (flatMode) return; // Skip in flat mode - children handled by Tree
181
+
168
182
  const isExpanded = node?.isExpanded ?? false;
169
183
  const childCount = childrenArray.length;
170
184
  const shouldRenderProgressively = progressiveRender && renderCoordinator && childCount > 0;
@@ -232,7 +246,7 @@
232
246
 
233
247
  function _onNodeClicked() {
234
248
  uiLogger.debug(`Node clicked: ${node.path}`, { id: node.id, hasChildren: node.hasChildren })
235
- onNodeClicked?.(node)
249
+ callbacks.onNodeClicked(node)
236
250
  if (shouldToggleOnNodeClick) {
237
251
  toggleExpanded()
238
252
  }
@@ -269,9 +283,9 @@
269
283
  class:ltree-clickable={node.isSelectable}
270
284
  class:ltree-dragged={isDraggedNode}
271
285
  class:ltree-draggable={node?.isDraggable}
272
- class:ltree-glow-above={dropZoneMode === 'glow' && isDragInProgress && isHoveredForDrop && glowPosition === 'above'}
273
- class:ltree-glow-below={dropZoneMode === 'glow' && isDragInProgress && isHoveredForDrop && glowPosition === 'below'}
274
- class:ltree-glow-child={dropZoneMode === 'glow' && isDragInProgress && isHoveredForDrop && glowPosition === 'child'}
286
+ class:ltree-glow-above={dropZoneMode === 'glow' && isDragInProgress && isHoveredForDrop && glowPosition === 'above' && isPositionAllowed('above')}
287
+ class:ltree-glow-below={dropZoneMode === 'glow' && isDragInProgress && isHoveredForDrop && glowPosition === 'below' && isPositionAllowed('below')}
288
+ class:ltree-glow-child={dropZoneMode === 'glow' && isDragInProgress && isHoveredForDrop && glowPosition === 'child' && isPositionAllowed('child')}
275
289
  class:ltree-drop-copy={isDragInProgress && isHoveredForDrop && dropOperation === 'copy'}
276
290
  draggable={node?.isDraggable}
277
291
  onclick={(e) => {
@@ -280,7 +294,7 @@
280
294
  }}
281
295
  oncontextmenu={(e) => {
282
296
  e.stopPropagation();
283
- onNodeRightClicked?.(node, e);
297
+ callbacks.onNodeRightClicked(node, e);
284
298
  }}
285
299
  ondragstart={(e) => {
286
300
  if (node?.isDraggable && e.dataTransfer) {
@@ -289,7 +303,7 @@
289
303
  "application/svelte-treeview",
290
304
  JSON.stringify(node),
291
305
  );
292
- onNodeDragStart?.(node, e);
306
+ callbacks.onNodeDragStart(node, e);
293
307
  }
294
308
  }}
295
309
  ondragover={(e) => {
@@ -305,7 +319,7 @@
305
319
  glowPosition = calculateGlowPosition(e, e.currentTarget as HTMLElement);
306
320
  }
307
321
  }
308
- onNodeDragOver?.(node, e);
322
+ callbacks.onNodeDragOver(node, e);
309
323
  }}
310
324
  ondragleave={(e) => {
311
325
  const rect = e.currentTarget.getBoundingClientRect();
@@ -315,7 +329,7 @@
315
329
  if (x < rect.left || x >= rect.right || y < rect.top || y >= rect.bottom) {
316
330
  isDraggedOver = false;
317
331
  glowPosition = null;
318
- onNodeDragLeave?.(node, e);
332
+ callbacks.onNodeDragLeave(node, e);
319
333
  }
320
334
  }}
321
335
  ondrop={(e) => {
@@ -327,15 +341,15 @@
327
341
  isDraggedOver = false;
328
342
  // In glow mode, use the calculated glowPosition for the drop
329
343
  if (dropZoneMode === 'glow' && glowPosition) {
330
- onZoneDrop?.(node, glowPosition, e);
344
+ callbacks.onZoneDrop(node, glowPosition, e);
331
345
  } else {
332
- onNodeDrop?.(node, e);
346
+ callbacks.onNodeDrop(node, e);
333
347
  }
334
348
  glowPosition = null;
335
349
  }}
336
- ontouchstart={(e) => onTouchDragStart?.(node, e)}
337
- ontouchmove={(e) => onTouchDragMove?.(node, e)}
338
- ontouchend={(e) => onTouchDragEnd?.(node, e)}
350
+ ontouchstart={(e) => callbacks.onTouchDragStart(node, e)}
351
+ ontouchmove={(e) => callbacks.onTouchDragMove(node, e)}
352
+ ontouchend={(e) => callbacks.onTouchDragEnd(node, e)}
339
353
  >
340
354
  {#if children}
341
355
  {@render children(node)}
@@ -345,71 +359,57 @@
345
359
  </div>
346
360
 
347
361
  <!-- Drop zones: positioned relative to .ltree-node-row (outside content to avoid padding issues) -->
348
- <!-- Only render floating drop zones when in 'floating' mode -->
362
+ <!-- Only render floating drop zones when in 'floating' mode, filtered by allowedDropPositions -->
349
363
  {#if dropZoneMode === 'floating' && isDragInProgress && isHoveredForDrop}
350
364
  <div
351
365
  class="ltree-drop-zones ltree-drop-zones-{dropZoneLayout}"
352
366
  style="--drop-zone-start: {formattedDropZoneStart}; --drop-zone-max-width: {dropZoneMaxWidth}px;"
353
367
  >
354
- <div
355
- class="ltree-drop-zone ltree-drop-above"
356
- class:ltree-drop-zone-active={hoveredZone === 'above'}
357
- ondragover={(e) => { e.preventDefault(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = 'above'; onNodeDragOver?.(node, e); }}
358
- ondragleave={() => { hoveredZone = null; }}
359
- ondrop={(e) => { e.stopPropagation(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = null; onZoneDrop?.(node, 'above', e); }}
360
- >↑ Above</div>
361
- <div
362
- class="ltree-drop-zone ltree-drop-below"
363
- class:ltree-drop-zone-active={hoveredZone === 'below'}
364
- ondragover={(e) => { e.preventDefault(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = 'below'; onNodeDragOver?.(node, e); }}
365
- ondragleave={() => { hoveredZone = null; }}
366
- ondrop={(e) => { e.stopPropagation(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = null; onZoneDrop?.(node, 'below', e); }}
367
- >↓ Below</div>
368
- <div
369
- class="ltree-drop-zone ltree-drop-child"
370
- class:ltree-drop-zone-active={hoveredZone === 'child'}
371
- ondragover={(e) => { e.preventDefault(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = 'child'; onNodeDragOver?.(node, e); }}
372
- ondragleave={() => { hoveredZone = null; }}
373
- ondrop={(e) => { e.stopPropagation(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = null; onZoneDrop?.(node, 'child', e); }}
374
- >→ Child</div>
368
+ {#if isPositionAllowed('above')}
369
+ <div
370
+ class="ltree-drop-zone ltree-drop-above"
371
+ class:ltree-drop-zone-active={hoveredZone === 'above'}
372
+ ondragover={(e) => { e.preventDefault(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = 'above'; callbacks.onNodeDragOver(node, e); }}
373
+ ondragleave={() => { hoveredZone = null; }}
374
+ ondrop={(e) => { e.stopPropagation(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = null; callbacks.onZoneDrop(node, 'above', e); }}
375
+ >↑ Above</div>
376
+ {/if}
377
+ {#if isPositionAllowed('below')}
378
+ <div
379
+ class="ltree-drop-zone ltree-drop-below"
380
+ class:ltree-drop-zone-active={hoveredZone === 'below'}
381
+ ondragover={(e) => { e.preventDefault(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = 'below'; callbacks.onNodeDragOver(node, e); }}
382
+ ondragleave={() => { hoveredZone = null; }}
383
+ ondrop={(e) => { e.stopPropagation(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = null; callbacks.onZoneDrop(node, 'below', e); }}
384
+ >↓ Below</div>
385
+ {/if}
386
+ {#if isPositionAllowed('child')}
387
+ <div
388
+ class="ltree-drop-zone ltree-drop-child"
389
+ class:ltree-drop-zone-active={hoveredZone === 'child'}
390
+ ondragover={(e) => { e.preventDefault(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = 'child'; callbacks.onNodeDragOver(node, e); }}
391
+ ondragleave={() => { hoveredZone = null; }}
392
+ ondrop={(e) => { e.stopPropagation(); if (e.dataTransfer) e.dataTransfer.dropEffect = (allowCopy && e.ctrlKey) ? 'copy' : 'move'; hoveredZone = null; callbacks.onZoneDrop(node, 'child', e); }}
393
+ >→ Child</div>
394
+ {/if}
375
395
  </div>
376
396
  {/if}
377
397
  </div>
378
398
 
379
- {#if node?.isExpanded && node?.hasChildren}
399
+ <!-- In flat mode, children are rendered by Tree.svelte, not recursively here -->
400
+ {#if !flatMode && node?.isExpanded && node?.hasChildren}
380
401
  <div class="ltree-children">
381
402
  {#each childrenToRender as item (item.id)}
382
403
  <Node
383
404
  node={item}
384
405
  {children}
385
- {shouldToggleOnNodeClick}
386
- {onNodeClicked}
387
- {onNodeRightClicked}
388
- {onNodeDragStart}
389
- {onNodeDragOver}
390
- {onNodeDragLeave}
391
- {onNodeDrop}
392
- {onZoneDrop}
393
- {onTouchDragStart}
394
- {onTouchDragMove}
395
- {onTouchDragEnd}
396
406
  {progressiveRender}
397
407
  {renderBatchSize}
398
- {expandIconClass}
399
- {collapseIconClass}
400
- {leafIconClass}
401
- {selectedNodeClass}
402
- {dragOverNodeClass}
403
408
  {isDraggedNode}
404
409
  {isDragInProgress}
405
410
  {hoveredNodeForDropPath}
406
411
  {activeDropPosition}
407
- {dropZoneMode}
408
- {dropZoneLayout}
409
- {dropZoneStart}
410
- {dropZoneMaxWidth}
411
412
  {dropOperation}
412
- {allowCopy}
413
413
  />
414
414
  {/each}
415
415
  {#if hasMoreToRender}
@@ -6,34 +6,15 @@ declare function $$render<T>(): {
6
6
  props: {
7
7
  node: LTreeNode<T>;
8
8
  children?: Snippet<[T]>;
9
- onNodeClicked?: (node: LTreeNode<T>) => void;
10
- onNodeRightClicked?: (node: LTreeNode<T>, event: MouseEvent) => void;
11
- onNodeDragStart?: (node: LTreeNode<T>, event: DragEvent) => void;
12
- onNodeDragOver?: (node: LTreeNode<T>, event: DragEvent) => void;
13
- onNodeDragLeave?: (node: LTreeNode<T>, event: DragEvent) => void;
14
- onNodeDrop?: (node: LTreeNode<T>, event: DragEvent) => void;
15
- onZoneDrop?: (node: LTreeNode<T>, position: DropPosition, event: DragEvent) => void;
16
- onTouchDragStart?: (node: LTreeNode<T>, event: TouchEvent) => void;
17
- onTouchDragMove?: (node: LTreeNode<T>, event: TouchEvent) => void;
18
- onTouchDragEnd?: (node: LTreeNode<T>, event: TouchEvent) => void;
19
- shouldToggleOnNodeClick?: boolean | null | undefined;
20
9
  progressiveRender?: boolean;
21
10
  renderBatchSize?: number;
22
- expandIconClass?: string | null | undefined;
23
- collapseIconClass?: string | null | undefined;
24
- leafIconClass?: string | null | undefined;
25
- selectedNodeClass?: string | null | undefined;
26
- dragOverNodeClass?: string | null | undefined;
27
- isDraggedNode?: boolean | null | undefined;
11
+ isDraggedNode?: boolean;
28
12
  isDragInProgress?: boolean;
29
13
  hoveredNodeForDropPath?: string | null;
30
14
  activeDropPosition?: DropPosition | null;
31
- dropZoneMode?: "floating" | "glow";
32
- dropZoneLayout?: "around" | "above" | "below" | "wave" | "wave2";
33
- dropZoneStart?: number | string;
34
- dropZoneMaxWidth?: number;
35
15
  dropOperation?: DropOperation;
36
- allowCopy?: boolean;
16
+ flatMode?: boolean;
17
+ flatIndentSize?: string;
37
18
  };
38
19
  exports: {};
39
20
  bindings: "";