Skip to content

Commit 6fbb6bc

Browse files
committed
feat: also remap to bottom-offset for items below open subtrees (#148)
1 parent 32e09f3 commit 6fbb6bc

File tree

4 files changed

+95
-26
lines changed

4 files changed

+95
-26
lines changed

next-release-notes.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
<!--
2-
### Breaking Changes
3-
41
### Features
52

6-
### Bug Fixes and Improvements
3+
- Make drop target depth in last-of-subtree locations dependent on x-coordinates of the mouse, allowing the user to
4+
drop in higher depths than the currently hovering item. This behavior is automatically used if `renderDepthOffset`
5+
on the renderers is set to a non-zero value. (#148)
6+
- This also applies when dropping at the top of an item where the item prior is of deeper depth, meaning that
7+
in this case the decision of dropping into the container above or directly above the hovering item will depend
8+
on the x-coordinate of the mouse.
9+
- Add `setDomFocus` argument to focus-item methods to provide an escape hatch to set the focus state of an item in RCT
10+
without updating the DOM focus. This defaults to true in all existing methods to maintain the current behavior if
11+
it is absent. (#336)
12+
- Allow customizing when a subtree is rendered or not with the new `shouldRenderChildren` prop. This can be used to
13+
create opening and closing animations on subtrees. See [Demo](https://rct.lukasbach.com/storybook/?path=/story/core-basic-examples--animated-expanding-and-collapsing)
14+
and [Demo Implementation](https://github.com/lukasbach/react-complex-tree/blob/main/packages/core/src/stories/BasicExamples.stories.tsx#L561) for details. (#333)
15+
16+
### Bug Fixes
717

8-
### Other Changes
9-
-->
18+
- Fix a bug where the `parentId` property in the `renderItemsContainer` render method was incorrectly set to the tree id
19+
for the root container.

packages/core/src/drag/DraggingPositionEvaluation.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -165,16 +165,33 @@ export class DraggingPositionEvaluation {
165165

166166
/**
167167
* Inside a folder, only drop at bottom offset to make it visually
168-
* consistent.
168+
* consistent. This also maps to bottom offset for items below open
169+
* subtrees, to keep the x-coordinate based dropping consistent (only
170+
* if indentation is defined).
169171
*/
170172
private maybeMapToBottomOffset() {
171173
const priorItem = this.env.linearItems[this.treeId][this.linearIndex - 1];
174+
175+
if (!priorItem || priorItem?.depth === undefined) return;
176+
177+
const depthDistanceToPrior = priorItem.depth - this.targetItem.depth;
172178
if (
173179
this.offset === 'top' &&
174180
this.targetItem.depth === (priorItem?.depth ?? -1)
175181
) {
182+
// map inside folder
183+
this.offset = 'bottom';
184+
this.linearIndex -= 1;
185+
this.targetItem = this.env.linearItems[this.treeId][this.linearIndex];
186+
} else if (
187+
this.offset === 'top' &&
188+
depthDistanceToPrior > 0 &&
189+
this.indentation !== undefined
190+
) {
191+
// map at bottom of folder, up inside folder contents
176192
this.offset = 'bottom';
177193
this.linearIndex -= 1;
194+
this.targetItem = this.env.linearItems[this.treeId][this.linearIndex];
178195
}
179196
}
180197

@@ -227,14 +244,18 @@ export class DraggingPositionEvaluation {
227244
return undefined;
228245
}
229246

247+
this.maybeRedirectInsideOpenFolder();
248+
this.maybeMapToBottomOffset();
249+
230250
const reparented = this.maybeReparentUpwards();
231251
if (reparented) {
232252
return reparented;
233253
}
234254

235-
this.maybeRedirectInsideOpenFolder();
255+
if (!this.canDropAtCurrentTarget()) {
256+
return undefined;
257+
}
236258

237-
// Must run before maybeMapToBottomOffset
238259
const { parent } = this.getParentOfLinearItem(
239260
this.linearIndex,
240261
this.treeId
@@ -243,14 +264,6 @@ export class DraggingPositionEvaluation {
243264
this.env.items[parent.item].children!.indexOf(this.targetItem.item) +
244265
(this.offset === 'top' ? 0 : 1);
245266

246-
this.maybeMapToBottomOffset();
247-
248-
if (!this.canDropAtCurrentTarget()) {
249-
return undefined;
250-
}
251-
252-
// used to be here: this.maybeMapToBottomOffset();.. moved up for better readability
253-
254267
if (this.offset) {
255268
return {
256269
targetType: 'between-items',

packages/core/src/drag/useDraggingPosition.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,9 @@ export const useDraggingPosition = () => {
8282

8383
const indentation = !env.renderDepthOffset
8484
? undefined
85-
: Math.min(
86-
Math.max(
87-
Math.floor((e.clientX - treeBb.left) / env.renderDepthOffset),
88-
0
89-
),
90-
targetLinearItem.depth
85+
: Math.max(
86+
Math.floor((e.clientX - treeBb.left) / env.renderDepthOffset),
87+
0
9188
);
9289

9390
let offset: 'top' | 'bottom' | undefined;

packages/core/test/dnd-basics.spec.tsx

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -434,10 +434,16 @@ describe('dnd basics', () => {
434434
it('reparents inner level', async () => {
435435
const test = await new TestUtil().renderOpenTree();
436436
await test.startDrag('bbb');
437-
await test.dragOver('add', 'bottom', 1);
437+
await test.dragOver('add', 'bottom', 2);
438438
await test.drop();
439-
await test.expectItemContentsUnchanged('ad', 'b');
440-
await test.expectItemContents('a', ['aa', 'ab', 'ac', 'ad', 'bbb']);
439+
await test.expectItemContentsUnchanged('a', 'b', 'root');
440+
await test.expectItemContents('ad', [
441+
'ada',
442+
'adb',
443+
'adc',
444+
'add',
445+
'bbb',
446+
]);
441447
});
442448

443449
it('reparents mid level', async () => {
@@ -465,6 +471,49 @@ describe('dnd basics', () => {
465471
'special',
466472
]);
467473
});
474+
475+
describe('reparent upwards when dragging at top of item below subtree', () => {
476+
it('reparents inner level', async () => {
477+
const test = await new TestUtil().renderOpenTree();
478+
await test.startDrag('bbb');
479+
await test.dragOver('b', 'top', 2);
480+
await test.drop();
481+
await test.expectItemContentsUnchanged('a', 'b', 'root');
482+
await test.expectItemContents('ad', [
483+
'ada',
484+
'adb',
485+
'adc',
486+
'add',
487+
'bbb',
488+
]);
489+
});
490+
491+
it('reparents mid level', async () => {
492+
const test = await new TestUtil().renderOpenTree();
493+
await test.startDrag('bbb');
494+
await test.dragOver('b', 'top', 1);
495+
await test.drop();
496+
await test.expectItemContentsUnchanged('ad', 'b', 'root');
497+
await test.expectItemContents('a', ['aa', 'ab', 'ac', 'ad', 'bbb']);
498+
});
499+
500+
it('reparents outer level', async () => {
501+
const test = await new TestUtil().renderOpenTree();
502+
await test.startDrag('bbb');
503+
await test.dragOver('b', 'top', 0);
504+
await test.drop();
505+
await test.expectItemContentsUnchanged('ad', 'a', 'b');
506+
await test.expectItemContents('root', [
507+
'target-parent',
508+
'a',
509+
'bbb',
510+
'b',
511+
'c',
512+
'deep1',
513+
'special',
514+
]);
515+
});
516+
});
468517
});
469518

470519
describe('redirects to parent', () => {

0 commit comments

Comments
 (0)