UNPKG

@react-querybuilder/dnd

Version:

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

1 lines 36.8 kB
{"version":3,"file":"react-querybuilder_dnd.production.mjs","names":[],"sources":["../src/adapter.ts","../src/flipAnimation.ts","../src/QueryBuilderDndContext.ts","../src/InlineCombinatorDnD.tsx","../src/RuleDnD.tsx","../src/RuleGroupDnD.tsx","../src/QueryBuilderDnD.tsx","../src/useShadowQuery.ts"],"sourcesContent":["import type { Ref } from 'react';\nimport type {\n DropEffect,\n Path,\n QueryActions,\n RuleGroupTypeAny,\n RuleType,\n Schema,\n} from 'react-querybuilder';\nimport type { CustomCanDropParams, OnRuleDropCallback } from './types';\n\n/**\n * Parameters for the adapter's `useRuleDnD` hook.\n */\nexport interface DndAdapterRuleDnDParams {\n path: Path;\n disabled: boolean;\n // oxlint-disable-next-line typescript/no-explicit-any\n schema: Schema<any, any>;\n actions: QueryActions;\n rule: RuleType;\n canDrop?: (params: CustomCanDropParams) => boolean;\n copyModeModifierKey: string;\n copyModeAfterHoverMs?: number;\n groupModeModifierKey: string;\n groupModeAfterHoverMs?: number;\n hideDefaultDragPreview?: boolean;\n onRuleDrop?: OnRuleDropCallback;\n}\n\n/**\n * Parameters for the adapter's `useRuleGroupDnD` hook.\n */\nexport interface DndAdapterRuleGroupDnDParams {\n path: Path;\n disabled: boolean;\n // oxlint-disable-next-line typescript/no-explicit-any\n schema: Schema<any, any>;\n actions: QueryActions;\n ruleGroup: RuleGroupTypeAny;\n canDrop?: (params: CustomCanDropParams) => boolean;\n copyModeModifierKey: string;\n copyModeAfterHoverMs?: number;\n groupModeModifierKey: string;\n groupModeAfterHoverMs?: number;\n hideDefaultDragPreview?: boolean;\n onRuleDrop?: OnRuleDropCallback;\n}\n\n/**\n * Parameters for the adapter's `useInlineCombinatorDnD` hook.\n */\nexport interface DndAdapterInlineCombinatorDnDParams {\n path: Path;\n // oxlint-disable-next-line typescript/no-explicit-any\n schema: Schema<any, any>;\n rules?: (RuleType | RuleGroupTypeAny | string)[];\n canDrop?: (params: CustomCanDropParams) => boolean;\n copyModeModifierKey: string;\n copyModeAfterHoverMs?: number;\n groupModeModifierKey: string;\n groupModeAfterHoverMs?: number;\n}\n\n/**\n * Return type for the adapter's `useRuleDnD` hook. Matches the existing\n * `UseRuleDnD` interface from `react-querybuilder`.\n */\nexport interface AdapterUseRuleDnDResult {\n isDragging: boolean;\n dragMonitorId: string | symbol;\n isOver: boolean;\n dropMonitorId: string | symbol;\n dragRef: Ref<HTMLSpanElement>;\n dndRef: Ref<HTMLDivElement>;\n dropEffect?: DropEffect;\n groupItems?: boolean;\n dropNotAllowed?: boolean;\n}\n\n/**\n * Return type for the adapter's `useRuleGroupDnD` hook. Matches the existing\n * `UseRuleGroupDnD` interface from `react-querybuilder`.\n */\nexport interface AdapterUseRuleGroupDnDResult {\n isDragging: boolean;\n dragMonitorId: string | symbol;\n isOver: boolean;\n dropMonitorId: string | symbol;\n previewRef: Ref<HTMLDivElement>;\n dragRef: Ref<HTMLSpanElement>;\n dropRef: Ref<HTMLDivElement>;\n dropEffect?: DropEffect;\n groupItems?: boolean;\n dropNotAllowed?: boolean;\n}\n\n/**\n * Return type for the adapter's `useInlineCombinatorDnD` hook.\n */\nexport interface AdapterUseInlineCombinatorDnDResult {\n isOver: boolean;\n dropMonitorId: string | symbol | null;\n dropRef: Ref<HTMLDivElement>;\n dropEffect?: DropEffect;\n groupItems?: boolean;\n dropNotAllowed?: boolean;\n}\n\n/**\n * Props for the adapter's DnD context provider component.\n */\nexport interface DndAdapterProviderProps {\n debugMode?: boolean;\n /**\n * When `true`, the adapter should enable update-while-dragging behavior:\n * computing a shadow query on hover and committing it on drop instead of\n * using the standard drop-indicator approach.\n */\n updateWhileDragging?: boolean;\n /**\n * Milliseconds after hovering a drop target before the drop effect\n * automatically switches to \"copy\".\n */\n copyModeAfterHoverMs?: number;\n /**\n * Milliseconds after hovering a drop target before the drop will\n * automatically create a new group.\n */\n groupModeAfterHoverMs?: number;\n children: React.ReactNode;\n}\n\n/**\n * The DnD adapter interface. Implementations of this interface provide\n * drag-and-drop functionality for react-querybuilder using a specific\n * DnD library.\n *\n * Built-in adapters: {@link createReactDnDAdapter} (for `react-dnd`)\n * and {@link createDndKitAdapter} (for `@dnd-kit/core`).\n *\n * @group DnD\n */\nexport interface DndAdapter {\n /**\n * Provider component that wraps the query builder tree with the\n * DnD library's context.\n */\n DndProvider: React.ComponentType<DndAdapterProviderProps>;\n\n /**\n * Hook providing drag-and-drop behavior for a rule component.\n * Returns refs and state to attach to the rule's DOM elements.\n */\n useRuleDnD: (params: DndAdapterRuleDnDParams) => AdapterUseRuleDnDResult;\n\n /**\n * Hook providing drag-and-drop behavior for a rule group component.\n * Returns refs and state to attach to the group's DOM elements.\n */\n useRuleGroupDnD: (params: DndAdapterRuleGroupDnDParams) => AdapterUseRuleGroupDnDResult;\n\n /**\n * Hook providing drop-target behavior for an inline combinator.\n * Returns a drop ref and hover state.\n */\n useInlineCombinatorDnD: (\n params: DndAdapterInlineCombinatorDnDParams\n ) => AdapterUseInlineCombinatorDnDResult;\n}\n\n/**\n * Type guard to check if a value is a {@link DndAdapter}.\n */\nexport const isDndAdapter = (value: unknown): value is DndAdapter =>\n typeof value === 'object' &&\n value !== null &&\n 'DndProvider' in value &&\n 'useRuleDnD' in value &&\n 'useRuleGroupDnD' in value &&\n 'useInlineCombinatorDnD' in value;\n","/**\n * FLIP (First, Last, Invert, Play) animation utility.\n *\n * Captures the positions of elements before a DOM update, then after the\n * update animates them from their old positions to their new ones using\n * CSS transforms, producing a smooth layout transition.\n *\n * @example\n * ```ts\n * const flip = createFlipAnimator('.rule, .ruleGroup');\n * flip.captureFirst(containerEl);\n * // ... React re-render happens ...\n * useLayoutEffect(() => { flip.playLast(containerEl); }, [shadowQuery]);\n * ```\n */\n\nconst FLIP_DURATION_MS = 150;\nconst FLIP_EASING = 'ease';\n\nexport interface FlipAnimator {\n /** Capture the \"first\" positions of all matching elements. */\n captureFirst: (container: HTMLElement) => void;\n /**\n * Measure \"last\" positions, compute deltas, and animate.\n * Call this in `useLayoutEffect` after React has committed the new DOM.\n */\n playLast: (container: HTMLElement) => void;\n}\n\nconst getElementKey = (el: Element): string | null =>\n el.getAttribute('data-rule-id') ?? el.getAttribute('data-testid') ?? null;\n\n/**\n * Creates a FLIP animator that tracks elements matching the given CSS selector\n * within a container. Elements are identified by their `data-rule-id` or `data-testid`\n * attribute.\n */\nexport const createFlipAnimator = (selector: string): FlipAnimator => {\n let firstPositions = new Map<string, DOMRect>();\n\n const captureFirst = (container: HTMLElement): void => {\n firstPositions = new Map();\n const elements = container.querySelectorAll(selector);\n for (const el of elements) {\n const key = getElementKey(el);\n if (key) {\n firstPositions.set(key, el.getBoundingClientRect());\n }\n }\n };\n\n const playLast = (container: HTMLElement): void => {\n const elements = container.querySelectorAll(selector);\n for (const el of elements) {\n const key = getElementKey(el);\n if (!key) continue;\n\n const firstRect = firstPositions.get(key);\n if (!firstRect) continue;\n\n const lastRect = el.getBoundingClientRect();\n const deltaX = firstRect.left - lastRect.left;\n const deltaY = firstRect.top - lastRect.top;\n\n if (deltaX === 0 && deltaY === 0) continue;\n\n // Invert: place element at its old position\n const htmlEl = el as HTMLElement;\n htmlEl.style.transform = `translate(${deltaX}px, ${deltaY}px)`;\n htmlEl.style.transition = 'none';\n\n // Play: animate to new position\n requestAnimationFrame(() => {\n htmlEl.style.transition = `transform ${FLIP_DURATION_MS}ms ${FLIP_EASING}`;\n htmlEl.style.transform = '';\n\n const handleTransitionEnd = (): void => {\n htmlEl.style.transition = '';\n htmlEl.removeEventListener('transitionend', handleTransitionEnd);\n };\n htmlEl.addEventListener('transitionend', handleTransitionEnd);\n });\n }\n\n firstPositions = new Map();\n };\n\n return { captureFirst, playLast };\n};\n","import type { Context } from 'react';\nimport { createContext } from 'react';\nimport { defaultControlElements } from 'react-querybuilder';\nimport type { QueryBuilderDndContextProps } from './types';\n\nconst { rule, ruleGroup, combinatorSelector } = defaultControlElements;\n\n/**\n * @group Components\n */\nexport const QueryBuilderDndContext: Context<QueryBuilderDndContextProps> =\n createContext<QueryBuilderDndContextProps>({\n baseControls: { rule, ruleGroup, combinatorSelector },\n });\n","import * as React from 'react';\nimport { useContext } from 'react';\nimport type { InlineCombinatorProps } from 'react-querybuilder';\nimport { standardClassnames, TestID } from 'react-querybuilder';\nimport { DragPreviewContext } from './DragPreviewContext';\nimport { QueryBuilderDndContext } from './QueryBuilderDndContext';\n\n/**\n * The drag-and-drop-enabled inline combinator component.\n *\n * @group Components\n */\nexport const InlineCombinatorDnD = ({\n component: CombinatorSelectorComponent,\n ...props\n}: InlineCombinatorProps): React.JSX.Element => {\n const {\n adapter,\n canDrop,\n copyModeModifierKey,\n copyModeAfterHoverMs,\n groupModeModifierKey,\n groupModeAfterHoverMs,\n } = useContext(QueryBuilderDndContext);\n const { dragPreviewState } = useContext(DragPreviewContext);\n\n // When updateWhileDragging is active, suppress drop indicator\n const isUpdateWhileDragging = dragPreviewState !== null;\n\n const { dropRef, dropMonitorId, isOver } = adapter!.useInlineCombinatorDnD({\n path: props.path,\n schema: props.schema,\n rules: props.rules,\n canDrop,\n copyModeModifierKey: copyModeModifierKey ?? 'alt',\n copyModeAfterHoverMs,\n groupModeModifierKey: groupModeModifierKey ?? 'ctrl',\n groupModeAfterHoverMs,\n });\n\n // Suppress isOver when updateWhileDragging is active\n // v8 ignore next -- same pattern as RuleDnD/RuleGroupDnD; IC only used in specific mode\n const effectiveIsOver = isUpdateWhileDragging ? false : isOver;\n\n const wrapperClassName = [\n props.schema.suppressStandardClassnames || standardClassnames.betweenRules,\n (effectiveIsOver && !props.schema.classNames.dndOver) || false,\n (effectiveIsOver && !props.schema.suppressStandardClassnames && standardClassnames.dndOver) ||\n false,\n ]\n .filter(c => typeof c === 'string')\n .join(' ');\n\n return (\n <div\n key=\"dnd\"\n ref={dropRef}\n className={wrapperClassName}\n data-dropmonitorid={dropMonitorId}\n data-testid={TestID.inlineCombinator}>\n <CombinatorSelectorComponent {...props} testID={TestID.combinators} />\n </div>\n );\n};\n\n/**\n * @group Hooks\n * @deprecated Access via the adapter instead: `adapter.useInlineCombinatorDnD(params)`.\n */\nexport { type AdapterUseInlineCombinatorDnDResult as UseInlineCombinatorDnDResult } from './adapter';\n","import * as React from 'react';\nimport { useContext } from 'react';\nimport type { RuleProps } from 'react-querybuilder';\nimport { DragPreviewContext } from './DragPreviewContext';\nimport { QueryBuilderDndContext } from './QueryBuilderDndContext';\n\n/**\n * Rule component for drag-and-drop. Renders the provided rule component\n * ({@link react-querybuilder!Rule Rule} by default), but forwards the\n * drag-and-drop context.\n *\n * @group Components\n */\nexport const RuleDnD = (props: RuleProps): React.JSX.Element => {\n const rqbDndContext = useContext(QueryBuilderDndContext);\n const { dragPreviewState } = useContext(DragPreviewContext);\n\n const {\n adapter,\n canDrop,\n copyModeModifierKey,\n copyModeAfterHoverMs,\n groupModeModifierKey,\n groupModeAfterHoverMs,\n hideDefaultDragPreview,\n onRuleDrop,\n } = rqbDndContext;\n\n const disabled = !!props.parentDisabled || !!props.disabled;\n\n const dndRefs = adapter!.useRuleDnD({\n path: props.path,\n disabled,\n schema: props.schema,\n actions: props.actions,\n rule: props.rule,\n canDrop,\n copyModeModifierKey: copyModeModifierKey ?? 'alt',\n copyModeAfterHoverMs,\n groupModeModifierKey: groupModeModifierKey ?? 'ctrl',\n groupModeAfterHoverMs,\n hideDefaultDragPreview,\n onRuleDrop,\n });\n\n // When updateWhileDragging is active, suppress isDragging and isOver\n // indicators — the visual feedback is the item moving in the tree.\n const overriddenDndRefs = dragPreviewState\n ? { ...dndRefs, isDragging: false, isOver: false, dropNotAllowed: false }\n : dndRefs;\n\n const { rule: BaseRuleComponent } = rqbDndContext.baseControls;\n\n return (\n <QueryBuilderDndContext.Provider value={rqbDndContext}>\n <BaseRuleComponent {...props} {...overriddenDndRefs} />\n </QueryBuilderDndContext.Provider>\n );\n};\n\n/**\n * @group Hooks\n * @deprecated Access via the adapter instead: `adapter.useRuleDnD(params)`.\n */\nexport { type AdapterUseRuleDnDResult as UseRuleDnDResult } from './adapter';\n","import * as React from 'react';\nimport { useContext, useMemo } from 'react';\nimport type { RuleGroupProps } from 'react-querybuilder';\nimport { DragPreviewContext } from './DragPreviewContext';\nimport { QueryBuilderDndContext } from './QueryBuilderDndContext';\n\n/**\n * Rule group component for drag-and-drop. Renders the provided rule group component\n * ({@link react-querybuilder!RuleGroup RuleGroup} by default), but forwards the drag-and-drop\n * context so that child rules and groups will render within the appropriate drag-and-drop wrappers.\n *\n * @group Components\n */\nexport const RuleGroupDnD = (props: RuleGroupProps): React.JSX.Element => {\n const rqbDndContext = useContext(QueryBuilderDndContext);\n const { dragPreviewState } = useContext(DragPreviewContext);\n\n const {\n adapter,\n canDrop,\n baseControls: { ruleGroup: BaseRuleGroupComponent },\n copyModeModifierKey,\n copyModeAfterHoverMs,\n groupModeModifierKey,\n groupModeAfterHoverMs,\n hideDefaultDragPreview,\n onRuleDrop,\n } = rqbDndContext;\n\n // When updateWhileDragging is active and this is the root group,\n // swap ruleGroup/rules with the shadow query so the tree re-renders\n // with the dragged item at its preview position.\n const effectiveProps = useMemo(() => {\n if (props.path.length === 0 && dragPreviewState) {\n const sq = dragPreviewState.shadowQuery;\n return {\n ...props,\n ruleGroup: sq,\n rules: sq.rules,\n };\n }\n return props;\n }, [props, dragPreviewState]);\n\n const dndRefs = adapter!.useRuleGroupDnD({\n disabled: !!effectiveProps.parentDisabled || !!effectiveProps.disabled,\n path: effectiveProps.path,\n schema: effectiveProps.schema,\n actions: effectiveProps.actions,\n ruleGroup: effectiveProps.ruleGroup,\n canDrop,\n copyModeModifierKey: copyModeModifierKey ?? 'alt',\n copyModeAfterHoverMs,\n groupModeModifierKey: groupModeModifierKey ?? 'ctrl',\n groupModeAfterHoverMs,\n hideDefaultDragPreview,\n onRuleDrop,\n });\n\n // When updateWhileDragging is active, suppress isDragging and isOver\n // indicators — the visual feedback is the item moving in the tree.\n const overriddenDndRefs = dragPreviewState\n ? { ...dndRefs, isDragging: false, isOver: false, dropNotAllowed: false }\n : dndRefs;\n\n return <BaseRuleGroupComponent {...effectiveProps} {...overriddenDndRefs} />;\n};\n\n/**\n * @group Hooks\n * @deprecated Access via the adapter instead: `adapter.useRuleGroupDnD(params)`.\n */\nexport { type AdapterUseRuleGroupDnDResult as UseRuleGroupDnDResult } from './adapter';\n","import * as React from 'react';\nimport { useContext, useEffect, useMemo, useState } from 'react';\nimport type { QueryBuilderContextProps } from 'react-querybuilder';\nimport {\n messages,\n preferAnyProp,\n preferProp,\n QueryBuilderContext,\n useMergedContext,\n} from 'react-querybuilder';\nimport type { DndAdapter } from './adapter';\nimport { isDndAdapter } from './adapter';\nimport { createReactDnDAdapter } from './adapters/react-dnd';\nimport { InlineCombinatorDnD } from './InlineCombinatorDnD';\nimport { isTouchDevice } from './isTouchDevice';\nimport { QueryBuilderDndContext } from './QueryBuilderDndContext';\nimport { RuleDnD } from './RuleDnD';\nimport { RuleGroupDnD } from './RuleGroupDnD';\nimport type {\n DndProp,\n QueryBuilderDndContextProps,\n QueryBuilderDndProps,\n UseReactDnD,\n} from './types';\n\n/**\n * Context provider to enable drag-and-drop. If the application already implements\n * `react-dnd`, use {@link QueryBuilderDndWithoutProvider} instead.\n *\n * @group Components\n */\nexport const QueryBuilderDnD = (props: QueryBuilderDndProps): React.JSX.Element => {\n const {\n controlClassnames,\n controlElements,\n debugMode,\n enableDragAndDrop: enableDragAndDropProp,\n enableMountQueryChange,\n translations,\n } = props;\n\n const rqbContext = useMergedContext({\n controlClassnames,\n controlElements,\n debugMode,\n enableDragAndDrop: enableDragAndDropProp ?? true,\n enableMountQueryChange,\n translations: translations ?? {},\n });\n const { enableDragAndDrop } = rqbContext;\n\n // Resolve the adapter: either directly provided, wrapped from legacy DndProp, or async-loaded\n const adapter = useResolvedAdapter(props.dnd);\n const key = enableDragAndDrop && adapter ? 'dnd' : 'no-dnd';\n\n const contextWithoutDnD = useMemo(\n () => ({ ...rqbContext, enableDragAndDrop: false, debugMode }),\n [rqbContext, debugMode]\n );\n const contextWithDnD = useMemo(\n () => ({ ...rqbContext, enableDragAndDrop, debugMode }),\n [rqbContext, debugMode, enableDragAndDrop]\n );\n\n if (!enableDragAndDrop || !adapter) {\n return (\n <QueryBuilderContext.Provider key={key} value={contextWithoutDnD}>\n {props.children}\n </QueryBuilderContext.Provider>\n );\n }\n\n const { DndProvider } = adapter;\n\n return (\n <DndProvider\n key={key}\n debugMode={debugMode}\n updateWhileDragging={props.updateWhileDragging}\n copyModeAfterHoverMs={props.copyModeAfterHoverMs}\n groupModeAfterHoverMs={props.groupModeAfterHoverMs}>\n <QueryBuilderContext.Provider key={key} value={contextWithDnD}>\n <QueryBuilderDndWithoutProvider\n dnd={adapter}\n canDrop={props.canDrop}\n copyModeModifierKey={props.copyModeModifierKey}\n copyModeAfterHoverMs={props.copyModeAfterHoverMs}\n groupModeModifierKey={props.groupModeModifierKey}\n groupModeAfterHoverMs={props.groupModeAfterHoverMs}\n hideDefaultDragPreview={props.hideDefaultDragPreview}\n updateWhileDragging={props.updateWhileDragging}\n onDragMove={props.onDragMove}\n onRuleDrop={props.onRuleDrop}>\n {props.children}\n </QueryBuilderDndWithoutProvider>\n </QueryBuilderContext.Provider>\n </DndProvider>\n );\n};\n\n/**\n * Context provider to enable drag-and-drop. Only use this provider if the application\n * already implements `react-dnd`, otherwise use {@link QueryBuilderDnD}.\n *\n * @group Components\n */\nexport const QueryBuilderDndWithoutProvider = (props: QueryBuilderDndProps): React.JSX.Element => {\n const rqbContext = useContext(QueryBuilderContext);\n const rqbDndContext = useContext(QueryBuilderDndContext);\n const adapter = useResolvedAdapter(props.dnd) ?? rqbDndContext.adapter;\n const copyModeModifierKey = preferAnyProp(\n undefined,\n props.copyModeModifierKey,\n rqbDndContext.copyModeModifierKey\n );\n const groupModeModifierKey = preferAnyProp(\n undefined,\n props.groupModeModifierKey,\n rqbDndContext.groupModeModifierKey\n );\n const copyModeAfterHoverMs = preferAnyProp(\n undefined,\n props.copyModeAfterHoverMs,\n rqbDndContext.copyModeAfterHoverMs\n );\n const groupModeAfterHoverMs = preferAnyProp(\n undefined,\n props.groupModeAfterHoverMs,\n rqbDndContext.groupModeAfterHoverMs\n );\n const enableDragAndDrop = preferProp(true, props.enableDragAndDrop, rqbContext.enableDragAndDrop);\n const debugMode = preferProp(false, props.debugMode, rqbContext.debugMode);\n const hideDefaultDragPreview = preferProp(\n false,\n props.hideDefaultDragPreview,\n rqbDndContext.hideDefaultDragPreview\n );\n const canDrop = preferAnyProp(undefined, props.canDrop, rqbDndContext.canDrop);\n const updateWhileDragging = preferProp(\n false,\n props.updateWhileDragging,\n rqbDndContext.updateWhileDragging\n );\n const onDragMove = preferAnyProp(undefined, props.onDragMove, rqbDndContext.onDragMove);\n const onRuleDrop = preferAnyProp(undefined, props.onRuleDrop, rqbDndContext.onRuleDrop);\n const key = enableDragAndDrop && adapter ? 'dnd' : 'no-dnd';\n\n const baseControls = useMemo(\n () => ({\n rule:\n props.controlElements?.rule ??\n rqbContext.controlElements?.rule ??\n rqbDndContext.baseControls.rule,\n ruleGroup:\n props.controlElements?.ruleGroup ??\n rqbContext.controlElements?.ruleGroup ??\n rqbDndContext.baseControls.ruleGroup,\n combinatorSelector:\n props.controlElements?.combinatorSelector ??\n rqbContext.controlElements?.combinatorSelector ??\n rqbDndContext.baseControls.combinatorSelector,\n }),\n [\n props.controlElements?.combinatorSelector,\n props.controlElements?.rule,\n props.controlElements?.ruleGroup,\n rqbContext.controlElements?.combinatorSelector,\n rqbContext.controlElements?.rule,\n rqbContext.controlElements?.ruleGroup,\n rqbDndContext.baseControls.combinatorSelector,\n rqbDndContext.baseControls.rule,\n rqbDndContext.baseControls.ruleGroup,\n ]\n );\n\n const newContext: QueryBuilderContextProps = useMemo(\n () => ({\n ...rqbContext,\n enableDragAndDrop,\n debugMode,\n controlElements: {\n ...rqbContext.controlElements,\n ruleGroup: RuleGroupDnD,\n rule: RuleDnD,\n inlineCombinator: InlineCombinatorDnD,\n },\n }),\n [debugMode, enableDragAndDrop, rqbContext]\n );\n\n const dndContextValue: QueryBuilderDndContextProps = useMemo(\n () => ({\n baseControls,\n canDrop,\n copyModeModifierKey,\n copyModeAfterHoverMs,\n groupModeModifierKey,\n groupModeAfterHoverMs,\n hideDefaultDragPreview,\n updateWhileDragging,\n onDragMove,\n onRuleDrop,\n adapter,\n }),\n [\n baseControls,\n canDrop,\n copyModeModifierKey,\n copyModeAfterHoverMs,\n groupModeModifierKey,\n groupModeAfterHoverMs,\n hideDefaultDragPreview,\n updateWhileDragging,\n onDragMove,\n onRuleDrop,\n adapter,\n ]\n );\n\n const contextWithoutDnD = useMemo(\n () => ({ ...rqbContext, enableDragAndDrop: false, debugMode }),\n [rqbContext, debugMode]\n );\n\n if (!enableDragAndDrop || !adapter) {\n return (\n <QueryBuilderContext.Provider key={key} value={contextWithoutDnD}>\n {props.children}\n </QueryBuilderContext.Provider>\n );\n }\n\n return (\n <QueryBuilderContext.Provider key={key} value={newContext}>\n <QueryBuilderDndContext.Provider value={dndContextValue}>\n {props.children}\n </QueryBuilderDndContext.Provider>\n </QueryBuilderContext.Provider>\n );\n};\n\nlet didWarnEnabledDndWithoutReactDnD = false;\n\n/**\n * Resolves a `dnd` prop (which may be a {@link DndAdapter}, a legacy {@link DndProp},\n * or `undefined`) into a {@link DndAdapter} or `null`.\n *\n * Hooks are always called in the same order regardless of the `dndParam` value\n * to satisfy the Rules of Hooks.\n */\nconst useResolvedAdapter = (dndParam?: DndAdapter | DndProp): DndAdapter | null => {\n const directAdapter = dndParam && isDndAdapter(dndParam) ? dndParam : null;\n\n // Always call useMemo (returns null when not applicable)\n const legacyAdapter = useMemo(\n () => (dndParam && !isDndAdapter(dndParam) ? createReactDnDAdapter(dndParam) : null),\n [dndParam]\n );\n\n // Always call the async hook, but skip loading when an explicit adapter exists\n const asyncAdapter = useAsyncReactDnDAdapter(directAdapter !== null || legacyAdapter !== null);\n\n return directAdapter ?? legacyAdapter ?? asyncAdapter;\n};\n\nconst useAsyncReactDnDAdapter = (skip: boolean): DndAdapter | null => {\n const [adapter, setAdapter] = useState<DndAdapter | null>(null);\n\n useEffect(() => {\n if (skip) return undefined;\n\n let didCancel = false;\n\n const loadDnD = async () => {\n const [reactDnD, reactDndHTML5Be, reactDndTouchBe] = await Promise.all(\n ['', '-html5-backend', '-touch-backend'].map(pn =>\n import(/* @vite-ignore */ `react-dnd${pn}`).catch(\n /* v8 ignore next -- @preserve */ () => null\n )\n )\n );\n\n // v8 ignore else\n if (!didCancel) {\n // v8 ignore else -- react-dnd is always importable in the test environment\n if (reactDnD) {\n let dndExports: DndProp;\n // v8 ignore next\n if (reactDndHTML5Be && (!reactDndTouchBe || (reactDndTouchBe && !isTouchDevice()))) {\n dndExports = {\n ...reactDnD,\n ...reactDndHTML5Be,\n ...reactDndTouchBe,\n ReactDndBackend: reactDndHTML5Be.HTML5Backend,\n };\n } else if (reactDndTouchBe) {\n dndExports = {\n ...reactDnD,\n ...reactDndTouchBe,\n ...reactDndHTML5Be,\n ReactDndBackend: reactDndTouchBe.TouchBackend,\n };\n } else {\n return;\n }\n setAdapter(() => createReactDnDAdapter(dndExports));\n } else {\n if (process.env.NODE_ENV !== 'production' && !didWarnEnabledDndWithoutReactDnD) {\n console.error(messages.errorEnabledDndWithoutReactDnD);\n didWarnEnabledDndWithoutReactDnD = true;\n }\n }\n }\n };\n\n if (!adapter) {\n loadDnD();\n }\n\n return () => {\n didCancel = true;\n };\n }, [adapter, skip]);\n\n return skip ? null : adapter;\n};\n\n/**\n * @group Hooks\n * @deprecated Use `createReactDnDAdapter` instead. This hook is kept for backward compatibility.\n */\nexport const useReactDnD = (dndParam?: DndProp): UseReactDnD | null => {\n const [dnd, setDnd] = useState(dndParam ?? null);\n\n useEffect(() => {\n let didCancel = false;\n\n const getDnD = async () => {\n const [reactDnD, reactDndHTML5Be, reactDndTouchBe] = await Promise.all(\n ['', '-html5-backend', '-touch-backend'].map(pn =>\n import(/* @vite-ignore */ `react-dnd${pn}`).catch(() => null)\n )\n );\n\n // v8 ignore else\n if (!didCancel) {\n if (reactDnD) {\n // Only prefer HTML5 backend if not touch device or we don't have the touch backend\n // (Can't test this since jsdom unconditionally defines `window.ontouchstart`.)\n // v8 ignore next\n if (reactDndHTML5Be && (!reactDndTouchBe || (reactDndTouchBe && !isTouchDevice()))) {\n setDnd(() => ({\n ...reactDnD,\n ...reactDndHTML5Be,\n ...reactDndTouchBe,\n ReactDndBackend: reactDndHTML5Be.HTML5Backend,\n }));\n } else if (reactDndTouchBe) {\n setDnd(() => ({\n ...reactDnD,\n ...reactDndTouchBe,\n ...reactDndHTML5Be,\n ReactDndBackend: reactDndTouchBe.TouchBackend,\n }));\n }\n } else {\n // v8 ignore else\n if (process.env.NODE_ENV !== 'production' && !didWarnEnabledDndWithoutReactDnD) {\n console.error(messages.errorEnabledDndWithoutReactDnD);\n didWarnEnabledDndWithoutReactDnD = true;\n }\n }\n }\n };\n\n if (!dnd) {\n getDnD();\n }\n\n return () => {\n didCancel = true;\n };\n }, [dnd]);\n\n // v8 ignore next\n if (dnd && !dnd.ReactDndBackend) {\n // Prefer touch backend if this is a touch device\n dnd.ReactDndBackend = isTouchDevice()\n ? (dnd.TouchBackend ?? dnd.HTML5Backend)\n : (dnd.HTML5Backend ?? dnd.TouchBackend);\n }\n\n return dnd as UseReactDnD;\n};\n","import { useContext } from 'react';\nimport type { RuleGroupTypeAny } from 'react-querybuilder';\nimport { DragPreviewContext } from './DragPreviewContext';\n\n/**\n * Hook for consuming the shadow query during an active drag with\n * `updateWhileDragging` enabled.\n *\n * @returns The shadow query if a drag is in progress, otherwise `undefined`.\n *\n * @group Hooks\n */\nexport const useShadowQuery = (): RuleGroupTypeAny | undefined => {\n const { dragPreviewState } = useContext(DragPreviewContext);\n return dragPreviewState?.shadowQuery;\n};\n"],"mappings":"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"}