@react-querybuilder/dnd
Version:
Drag-and-drop-enabled version of react-querybuilder (DnD-library-agnostic)
1 lines • 7.59 kB
Source Map (JSON)
{"version":3,"file":"shadowQuery-XxKzMrJJ.mjs","names":[],"sources":["../src/DragPreviewContext.ts","../src/shadowQuery.ts"],"sourcesContent":["import type { Context } from 'react';\nimport { createContext } from 'react';\nimport type { DndDropTargetType, Path } from 'react-querybuilder';\nimport type { DragPreviewState } from './types';\n\n/**\n * Context value for the drag preview state during update-while-dragging.\n *\n * @group DnD\n */\nexport interface DragPreviewContextValue {\n /** Current drag preview state, or `null` when not actively dragging. */\n dragPreviewState: DragPreviewState | null;\n /**\n * Update the preview position. Called by adapter hooks when the cursor\n * enters a new quadrant of a rule or group.\n */\n updatePreviewPosition: (\n targetPath: Path,\n targetType: DndDropTargetType,\n quadrant: 'upper' | 'lower'\n ) => void;\n /**\n * Commit the current shadow query as the real query and clear preview state.\n * Called on drop.\n */\n commitDrag: () => void;\n /**\n * Discard the shadow query and revert to the original query.\n * Called on cancel.\n */\n cancelDrag: () => void;\n}\n\n// v8 ignore next\nconst noop = (): void => {};\n\n/** @group Components */\nexport const DragPreviewContext: Context<DragPreviewContextValue> =\n createContext<DragPreviewContextValue>({\n dragPreviewState: null,\n updatePreviewPosition: noop,\n commitDrag: noop,\n cancelDrag: noop,\n });\n","import type {\n DndDropTargetType,\n DraggedItem,\n DropEffect,\n Path,\n RuleGroupTypeAny,\n} from 'react-querybuilder';\nimport { getParentPath, group, move } from 'react-querybuilder';\n\n/**\n * Computes the destination path for a quadrant-based drag target.\n *\n * - **Rule, upper quadrant**: Insert before the target rule.\n * - **Rule, lower quadrant**: Insert after the target rule.\n * - **RuleGroup header**: Insert as first child of the group (position 0).\n */\nexport const computeDestinationFromQuadrant = (\n targetPath: Path,\n targetType: DndDropTargetType,\n quadrant: 'upper' | 'lower'\n): Path => {\n if (targetType === 'ruleGroup') {\n // Group header → insert as first child\n return [...targetPath, 0];\n }\n\n // Rule target\n const parentPath = getParentPath(targetPath);\n const targetIndex = targetPath.at(-1)!;\n\n if (quadrant === 'upper') {\n // Insert before the target\n return [...parentPath, targetIndex];\n }\n // Insert after the target\n return [...parentPath, targetIndex + 1];\n};\n\n/**\n * Checks whether the dragged item is already at the computed destination,\n * meaning no visual change would occur.\n */\nconst isNoOp = (draggedPath: Path, destinationPath: Path): boolean => {\n if (draggedPath.length !== destinationPath.length) return false;\n\n const parentDragged = getParentPath(draggedPath);\n const parentDest = getParentPath(destinationPath);\n\n // Different parents → not a no-op\n // v8 ignore next -- unreachable: paths of equal length always have equal-length parents\n if (parentDragged.length !== parentDest.length) return false;\n for (let i = 0; i < parentDragged.length; i++) {\n if (parentDragged[i] !== parentDest[i]) return false;\n }\n\n const dragIdx = draggedPath.at(-1)!;\n const destIdx = destinationPath.at(-1)!;\n\n // Moving to same position or position+1 (which resolves to same after removal)\n return destIdx === dragIdx || destIdx === dragIdx + 1;\n};\n\n/**\n * Computes a shadow query given the current drag state and target position.\n *\n * Uses the existing `move()` and `group()` utilities from `@react-querybuilder/core`\n * to produce an immutable preview of the query with the dragged item at its\n * prospective position.\n *\n * @returns The shadow query and the path where the dragged item now lives,\n * or `null` if the move would be a no-op.\n */\nexport const computeShadowQuery = ({\n originalQuery,\n // draggedItem,\n draggedPath,\n targetPath,\n targetType,\n quadrant,\n dropEffect,\n groupItems,\n}: {\n originalQuery: RuleGroupTypeAny;\n draggedItem: DraggedItem;\n draggedPath: Path;\n targetPath: Path;\n targetType: DndDropTargetType;\n quadrant: 'upper' | 'lower';\n dropEffect: DropEffect;\n groupItems: boolean;\n}): { shadowQuery: RuleGroupTypeAny; previewPath: Path } | null => {\n const destinationPath = computeDestinationFromQuadrant(targetPath, targetType, quadrant);\n const isClone = dropEffect === 'copy';\n\n if (groupItems) {\n // Group mode: create a new group containing both items\n try {\n const shadowQuery = group(originalQuery, draggedPath, targetPath, { clone: isClone });\n // After grouping, the dragged item is inside the new group.\n // The new group is at targetPath, and the source item is at index 0 or 1\n // depending on relative positions. For preview purposes, use targetPath.\n return { shadowQuery, previewPath: targetPath };\n } catch {\n // v8 ignore next\n return null;\n }\n }\n\n // Normal move/copy\n if (!isClone && isNoOp(draggedPath, destinationPath)) {\n return null;\n }\n\n try {\n const shadowQuery = move(originalQuery, draggedPath, destinationPath, { clone: isClone });\n // Compute the preview path: where the item ended up after the move.\n // After a move, the destination path may shift if the source was before it.\n // For preview purposes, we approximate: if source was before dest in same parent,\n // the effective dest shifts back by 1.\n let previewPath = destinationPath;\n if (!isClone) {\n const parentDragged = getParentPath(draggedPath);\n const parentDest = getParentPath(destinationPath);\n const sameParent =\n parentDragged.length === parentDest.length &&\n parentDragged.every((v, i) => v === parentDest[i]);\n if (sameParent) {\n const dragIdx = draggedPath.at(-1)!;\n const destIdx = destinationPath.at(-1)!;\n if (dragIdx < destIdx) {\n previewPath = [...parentDest, destIdx - 1];\n }\n }\n }\n return { shadowQuery, previewPath };\n } catch {\n // v8 ignore next\n return null;\n }\n};\n"],"mappings":";;;;AAmCA,MAAM,aAAmB,CAAC;;AAG1B,MAAa,qBACX,cAAuC;CACrC,kBAAkB;CAClB,uBAAuB;CACvB,YAAY;CACZ,YAAY;AACd,CAAC;;;;;;;;;;AC5BH,MAAa,kCACX,YACA,YACA,aACS;CACT,IAAI,eAAe,aAEjB,OAAO,CAAC,GAAG,YAAY,CAAC;CAI1B,MAAM,aAAa,cAAc,UAAU;CAC3C,MAAM,cAAc,WAAW,GAAG,EAAE;CAEpC,IAAI,aAAa,SAEf,OAAO,CAAC,GAAG,YAAY,WAAW;CAGpC,OAAO,CAAC,GAAG,YAAY,cAAc,CAAC;AACxC;;;;;AAMA,MAAM,UAAU,aAAmB,oBAAmC;CACpE,IAAI,YAAY,WAAW,gBAAgB,QAAQ,OAAO;CAE1D,MAAM,gBAAgB,cAAc,WAAW;CAC/C,MAAM,aAAa,cAAc,eAAe;;CAIhD,IAAI,cAAc,WAAW,WAAW,QAAQ,OAAO;CACvD,KAAK,IAAI,IAAI,GAAG,IAAI,cAAc,QAAQ,KACxC,IAAI,cAAc,OAAO,WAAW,IAAI,OAAO;CAGjD,MAAM,UAAU,YAAY,GAAG,EAAE;CACjC,MAAM,UAAU,gBAAgB,GAAG,EAAE;CAGrC,OAAO,YAAY,WAAW,YAAY,UAAU;AACtD;;;;;;;;;;;AAYA,MAAa,sBAAsB,EACjC,eAEA,aACA,YACA,YACA,UACA,YACA,iBAUiE;CACjE,MAAM,kBAAkB,+BAA+B,YAAY,YAAY,QAAQ;CACvF,MAAM,UAAU,eAAe;CAE/B,IAAI,YAEF,IAAI;EAKF,OAAO;GAAE,aAJW,MAAM,eAAe,aAAa,YAAY,EAAE,OAAO,QAAQ,CAIhE;GAAG,aAAa;EAAW;CAChD,QAAQ;;EAEN,OAAO;CACT;CAIF,IAAI,CAAC,WAAW,OAAO,aAAa,eAAe,GACjD,OAAO;CAGT,IAAI;EACF,MAAM,cAAc,KAAK,eAAe,aAAa,iBAAiB,EAAE,OAAO,QAAQ,CAAC;EAKxF,IAAI,cAAc;EAClB,IAAI,CAAC,SAAS;GACZ,MAAM,gBAAgB,cAAc,WAAW;GAC/C,MAAM,aAAa,cAAc,eAAe;GAIhD,IAFE,cAAc,WAAW,WAAW,UACpC,cAAc,OAAO,GAAG,MAAM,MAAM,WAAW,EAAE,GACnC;IACd,MAAM,UAAU,YAAY,GAAG,EAAE;IACjC,MAAM,UAAU,gBAAgB,GAAG,EAAE;IACrC,IAAI,UAAU,SACZ,cAAc,CAAC,GAAG,YAAY,UAAU,CAAC;GAE7C;EACF;EACA,OAAO;GAAE;GAAa;EAAY;CACpC,QAAQ;;EAEN,OAAO;CACT;AACF"}