UNPKG

@react-querybuilder/dnd

Version:

Drag-and-drop-enabled version of react-querybuilder (DnD-library-agnostic)

1 lines 7.48 kB
{"version":3,"file":"shadowQuery-Z2qhxCGM.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":"8GAmCA,MAAM,MAAmB,CAAC,EAGb,EACX,EAAuC,CACrC,iBAAkB,KAClB,sBAAuB,EACvB,WAAY,EACZ,WAAY,CACd,CAAC,EC5BU,GACX,EACA,EACA,IACS,CACT,GAAI,IAAe,YAEjB,MAAO,CAAC,GAAG,EAAY,CAAC,EAI1B,IAAM,EAAa,EAAc,CAAU,EACrC,EAAc,EAAW,GAAG,EAAE,EAOpC,OALI,IAAa,QAER,CAAC,GAAG,EAAY,CAAW,EAG7B,CAAC,GAAG,EAAY,EAAc,CAAC,CACxC,EAMM,GAAU,EAAmB,IAAmC,CACpE,GAAI,EAAY,SAAW,EAAgB,OAAQ,MAAO,GAE1D,IAAM,EAAgB,EAAc,CAAW,EACzC,EAAa,EAAc,CAAe,EAIhD,GAAI,EAAc,SAAW,EAAW,OAAQ,MAAO,GACvD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAc,OAAQ,IACxC,GAAI,EAAc,KAAO,EAAW,GAAI,MAAO,GAGjD,IAAM,EAAU,EAAY,GAAG,EAAE,EAC3B,EAAU,EAAgB,GAAG,EAAE,EAGrC,OAAO,IAAY,GAAW,IAAY,EAAU,CACtD,EAYa,GAAsB,CACjC,gBAEA,cACA,aACA,aACA,WACA,aACA,gBAUiE,CACjE,IAAM,EAAkB,EAA+B,EAAY,EAAY,CAAQ,EACjF,EAAU,IAAe,OAE/B,GAAI,EAEF,GAAI,CAKF,MAAO,CAAE,YAJW,EAAM,EAAe,EAAa,EAAY,CAAE,MAAO,CAAQ,CAIhE,EAAG,YAAa,CAAW,CAChD,MAAQ,CAEN,OAAO,IACT,CAIF,GAAI,CAAC,GAAW,EAAO,EAAa,CAAe,EACjD,OAAO,KAGT,GAAI,CACF,IAAM,EAAc,EAAK,EAAe,EAAa,EAAiB,CAAE,MAAO,CAAQ,CAAC,EAKpF,EAAc,EAClB,GAAI,CAAC,EAAS,CACZ,IAAM,EAAgB,EAAc,CAAW,EACzC,EAAa,EAAc,CAAe,EAIhD,GAFE,EAAc,SAAW,EAAW,QACpC,EAAc,OAAO,EAAG,IAAM,IAAM,EAAW,EAAE,EACnC,CACd,IAAM,EAAU,EAAY,GAAG,EAAE,EAC3B,EAAU,EAAgB,GAAG,EAAE,EACjC,EAAU,IACZ,EAAc,CAAC,GAAG,EAAY,EAAU,CAAC,EAE7C,CACF,CACA,MAAO,CAAE,cAAa,aAAY,CACpC,MAAQ,CAEN,OAAO,IACT,CACF"}