diff --git a/packages/react-arborist/package.json b/packages/react-arborist/package.json index f60dde68..3719caf2 100644 --- a/packages/react-arborist/package.json +++ b/packages/react-arborist/package.json @@ -1,6 +1,6 @@ { - "name": "react-arborist", - "version": "3.4.0", + "name": "@deepdub/react-arborist", + "version": "3.4.2", "license": "MIT", "source": "src/index.ts", "main": "dist/main/index.js", @@ -8,6 +8,8 @@ "types": "dist/module/index.d.ts", "sideEffects": false, "scripts": { + "preyalc": "npm version prerelease --preid=\"yalc\" --no-git-tag-version", + "yalc": "yalc publish", "build:cjs": "tsc --outDir dist/main", "build:es": "tsc --outDir dist/module --module es2022 --moduleResolution node", "build": "npm-run-all clean -p 'build:**'", diff --git a/packages/react-arborist/src/dnd/compute-drop.ts b/packages/react-arborist/src/dnd/compute-drop.ts index 4f4a69bd..4ee28852 100644 --- a/packages/react-arborist/src/dnd/compute-drop.ts +++ b/packages/react-arborist/src/dnd/compute-drop.ts @@ -9,17 +9,36 @@ import { } from "../utils"; import { DropResult } from "./drop-hook"; -function measureHover(el: HTMLElement, offset: XYCoord) { +function measureHover( + el: HTMLElement, + offset: XYCoord, + hitAreaHeight?: number, + disableReorder?: boolean +) { const rect = el.getBoundingClientRect(); const x = offset.x - Math.round(rect.x); const y = offset.y - Math.round(rect.y); const height = rect.height; + const pad = + hitAreaHeight === undefined ? height / 4 : (height - hitAreaHeight) / 2; + + if (disableReorder) { + return { + x, + inTopHalf: false, + inBottomHalf: false, + inMiddle: true, + atTop: false, + atBottom: false, + }; + } + const inTopHalf = y < height / 2; const inBottomHalf = !inTopHalf; - const pad = height / 4; - const inMiddle = y > pad && y < height - pad; + const inMiddle = y >= pad && y <= height - pad; const atTop = !inMiddle && inTopHalf; const atBottom = !inMiddle && inBottomHalf; + return { x, inTopHalf, inBottomHalf, inMiddle, atTop, atBottom }; } @@ -57,6 +76,8 @@ type Args = { element: HTMLElement; offset: XYCoord; indent: number; + hitAreaHeight?: number; + disableReorder?: boolean; node: NodeApi | null; prevNode: NodeApi | null; nextNode: NodeApi | null; @@ -114,7 +135,12 @@ export type Cursor = LineCursor | NoCursor | HighlightCursor; * This is the most complex, tricky function in the whole repo. */ export function computeDrop(args: Args): ComputedDrop { - const hover = measureHover(args.element, args.offset); + const hover = measureHover( + args.element, + args.offset, + args.hitAreaHeight, + args.disableReorder + ); const indent = args.indent; const hoverLevel = Math.round(Math.max(0, hover.x - indent) / indent); const { node, nextNode, prevNode } = args; diff --git a/packages/react-arborist/src/dnd/drag-hook.ts b/packages/react-arborist/src/dnd/drag-hook.ts index 22ac564e..ebd0063e 100644 --- a/packages/react-arborist/src/dnd/drag-hook.ts +++ b/packages/react-arborist/src/dnd/drag-hook.ts @@ -22,8 +22,26 @@ export function useDragHook(node: NodeApi): ConnectDragSource { tree.dispatch(dnd.dragStart(node.id, dragIds)); return { id: node.id }; }, - end: () => { + end: (_, monitor) => { tree.hideCursor(); + + if (tree.props.ignoreDropsOutside) { + const coords = monitor.getClientOffset(); + const bounds = tree.listEl.current?.getBoundingClientRect(); + + if ( + coords && + bounds && + (coords.y < bounds.top || + coords.y > bounds.bottom || + coords.x < bounds.left || + coords.x > bounds.right) + ) { + tree.dispatch(dnd.dragEnd()); + return; + } + } + let { parentId, index, dragIds } = tree.state.dnd; // If they held down meta, we need to create a copy // if (drop.dropEffect === "copy") diff --git a/packages/react-arborist/src/dnd/drop-hook.ts b/packages/react-arborist/src/dnd/drop-hook.ts index 6be259de..3b7dca1e 100644 --- a/packages/react-arborist/src/dnd/drop-hook.ts +++ b/packages/react-arborist/src/dnd/drop-hook.ts @@ -27,6 +27,8 @@ export function useDropHook( element: el.current, offset: offset, indent: tree.indent, + hitAreaHeight: tree.props.rowHitAreaHeight, + disableReorder: tree.props.disableReorder, node: node, prevNode: node.prev, nextNode: node.next, diff --git a/packages/react-arborist/src/dnd/outer-drop-hook.ts b/packages/react-arborist/src/dnd/outer-drop-hook.ts index b150f1ab..a1b7e2a9 100644 --- a/packages/react-arborist/src/dnd/outer-drop-hook.ts +++ b/packages/react-arborist/src/dnd/outer-drop-hook.ts @@ -24,6 +24,8 @@ export function useOuterDrop() { element: tree.listEl.current, offset: offset, indent: tree.indent, + hitAreaHeight: tree.props.rowHitAreaHeight, + disableReorder: tree.props.disableReorder, node: null, prevNode: tree.visibleNodes[tree.visibleNodes.length - 1], nextNode: null, diff --git a/packages/react-arborist/src/types/tree-props.ts b/packages/react-arborist/src/types/tree-props.ts index 440bcc40..3c6daf71 100644 --- a/packages/react-arborist/src/types/tree-props.ts +++ b/packages/react-arborist/src/types/tree-props.ts @@ -27,6 +27,8 @@ export interface TreeProps { /* Sizes */ rowHeight?: number; + rowHitAreaHeight?: number; + disableReorder?: boolean; overscanCount?: number; width?: number | string; height?: number; @@ -51,6 +53,7 @@ export interface TreeProps { dragNodes: NodeApi[]; index: number; }) => boolean); + ignoreDropsOutside?: boolean; /* Event Handlers */ onActivate?: (node: NodeApi) => void; diff --git a/packages/showcase/package.json b/packages/showcase/package.json index 99cc1e2a..bcd592ab 100644 --- a/packages/showcase/package.json +++ b/packages/showcase/package.json @@ -10,11 +10,11 @@ "clean": "rimraf .next out" }, "dependencies": { + "@deepdub/react-arborist": "workspace:*", "clsx": "^2.0.0", "nanoid": "^5.0.4", "next": "^14.0.4", "react": "^18.2.0", - "react-arborist": "workspace:*", "react-dom": "^18.2.0", "react-icons": "^4.12.0", "tree-model-improved": "^2.0.1", diff --git a/yalc.lock b/yalc.lock new file mode 100644 index 00000000..0e846a9f --- /dev/null +++ b/yalc.lock @@ -0,0 +1,9 @@ +{ + "version": "v1", + "packages": { + "@deepdub/react-arborist": { + "signature": "d2a829b67d4af9008b1cd9a8140e8912", + "file": true + } + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index aa34bce3..63d1b961 100644 --- a/yarn.lock +++ b/yarn.lock @@ -617,6 +617,30 @@ __metadata: languageName: node linkType: hard +"@deepdub/react-arborist@workspace:*, @deepdub/react-arborist@workspace:packages/react-arborist": + version: 0.0.0-use.local + resolution: "@deepdub/react-arborist@workspace:packages/react-arborist" + dependencies: + "@types/jest": "npm:^29.5.11" + "@types/react": "npm:^18.2.43" + "@types/react-window": "npm:^1.8.8" + "@types/use-sync-external-store": "npm:^0.0.6" + jest: "npm:^29.7.0" + npm-run-all: "npm:^4.1.5" + react-dnd: "npm:^14.0.3" + react-dnd-html5-backend: "npm:^14.0.3" + react-window: "npm:^1.8.10" + redux: "npm:^5.0.0" + rimraf: "npm:^5.0.5" + ts-jest: "npm:^29.1.1" + typescript: "npm:^5.3.3" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + react: ">= 16.14" + react-dom: ">= 16.14" + languageName: unknown + linkType: soft + "@eslint-community/eslint-utils@npm:^4.2.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -6878,30 +6902,6 @@ __metadata: languageName: unknown linkType: soft -"react-arborist@workspace:*, react-arborist@workspace:packages/react-arborist": - version: 0.0.0-use.local - resolution: "react-arborist@workspace:packages/react-arborist" - dependencies: - "@types/jest": "npm:^29.5.11" - "@types/react": "npm:^18.2.43" - "@types/react-window": "npm:^1.8.8" - "@types/use-sync-external-store": "npm:^0.0.6" - jest: "npm:^29.7.0" - npm-run-all: "npm:^4.1.5" - react-dnd: "npm:^14.0.3" - react-dnd-html5-backend: "npm:^14.0.3" - react-window: "npm:^1.8.10" - redux: "npm:^5.0.0" - rimraf: "npm:^5.0.5" - ts-jest: "npm:^29.1.1" - typescript: "npm:^5.3.3" - use-sync-external-store: "npm:^1.2.0" - peerDependencies: - react: ">= 16.14" - react-dom: ">= 16.14" - languageName: unknown - linkType: soft - "react-dnd-html5-backend@npm:^14.0.3": version: 14.1.0 resolution: "react-dnd-html5-backend@npm:14.1.0" @@ -7537,6 +7537,7 @@ __metadata: version: 0.0.0-use.local resolution: "showcase@workspace:packages/showcase" dependencies: + "@deepdub/react-arborist": "workspace:*" "@types/node": "npm:20.10.4" "@types/react": "npm:18.2.43" "@types/react-dom": "npm:18.2.17" @@ -7547,7 +7548,6 @@ __metadata: next: "npm:^14.0.4" npm-run-all: "npm:^4.1.5" react: "npm:^18.2.0" - react-arborist: "workspace:*" react-dom: "npm:^18.2.0" react-icons: "npm:^4.12.0" rimraf: "npm:^5.0.5"