@bquery/bquery 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +181 -25
- package/dist/{a11y-_9X-kt-_.js → a11y-DG2i4iZN.js} +3 -3
- package/dist/{a11y-_9X-kt-_.js.map → a11y-DG2i4iZN.js.map} +1 -1
- package/dist/a11y.es.mjs +1 -1
- package/dist/{component-L3-JfOFz.js → component-DRotf1hl.js} +19 -18
- package/dist/{component-L3-JfOFz.js.map → component-DRotf1hl.js.map} +1 -1
- package/dist/component.es.mjs +1 -1
- package/dist/concurrency/errors.d.ts +29 -0
- package/dist/concurrency/errors.d.ts.map +1 -0
- package/dist/concurrency/high-level.d.ts +85 -0
- package/dist/concurrency/high-level.d.ts.map +1 -0
- package/dist/concurrency/index.d.ts +19 -0
- package/dist/concurrency/index.d.ts.map +1 -0
- package/dist/concurrency/internal.d.ts +26 -0
- package/dist/concurrency/internal.d.ts.map +1 -0
- package/dist/concurrency/pipeline.d.ts +30 -0
- package/dist/concurrency/pipeline.d.ts.map +1 -0
- package/dist/concurrency/pool.d.ts +48 -0
- package/dist/concurrency/pool.d.ts.map +1 -0
- package/dist/concurrency/reactive.d.ts +107 -0
- package/dist/concurrency/reactive.d.ts.map +1 -0
- package/dist/concurrency/rpc.d.ts +46 -0
- package/dist/concurrency/rpc.d.ts.map +1 -0
- package/dist/concurrency/support.d.ts +23 -0
- package/dist/concurrency/support.d.ts.map +1 -0
- package/dist/concurrency/task.d.ts +31 -0
- package/dist/concurrency/task.d.ts.map +1 -0
- package/dist/concurrency/types.d.ts +343 -0
- package/dist/concurrency/types.d.ts.map +1 -0
- package/dist/concurrency-BU1wPEsZ.js +826 -0
- package/dist/concurrency-BU1wPEsZ.js.map +1 -0
- package/dist/concurrency.es.mjs +29 -0
- package/dist/{constraints-D5RHQLmP.js → constraints-CqjhmpZC.js} +1 -1
- package/dist/{constraints-D5RHQLmP.js.map → constraints-CqjhmpZC.js.map} +1 -1
- package/dist/core-CongXJuo.js +87 -0
- package/dist/core-CongXJuo.js.map +1 -0
- package/dist/{custom-directives-Dr4C5lVV.js → custom-directives-BjFzFhuf.js} +1 -1
- package/dist/{custom-directives-Dr4C5lVV.js.map → custom-directives-BjFzFhuf.js.map} +1 -1
- package/dist/{devtools-BhB2iDPT.js → devtools-C5FExMwv.js} +2 -2
- package/dist/{devtools-BhB2iDPT.js.map → devtools-C5FExMwv.js.map} +1 -1
- package/dist/devtools.es.mjs +1 -1
- package/dist/{dnd-NwZBYh4l.js → dnd-BAqzPlSo.js} +1 -1
- package/dist/{dnd-NwZBYh4l.js.map → dnd-BAqzPlSo.js.map} +1 -1
- package/dist/dnd.es.mjs +1 -1
- package/dist/effect-Cc51IH91.js +87 -0
- package/dist/effect-Cc51IH91.js.map +1 -0
- package/dist/{env-CTdvLaH2.js → env-PvwYHnJq.js} +1 -1
- package/dist/{env-CTdvLaH2.js.map → env-PvwYHnJq.js.map} +1 -1
- package/dist/{forms-UhAeJEoO.js → forms-Dx1Scvh0.js} +41 -40
- package/dist/{forms-UhAeJEoO.js.map → forms-Dx1Scvh0.js.map} +1 -1
- package/dist/forms.es.mjs +1 -1
- package/dist/full.d.ts +2 -0
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +243 -214
- package/dist/full.iife.js +117 -33
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +117 -33
- package/dist/full.umd.js.map +1 -1
- package/dist/{i18n-kuF6Ekj6.js → i18n-Cazyk9RD.js} +3 -3
- package/dist/{i18n-kuF6Ekj6.js.map → i18n-Cazyk9RD.js.map} +1 -1
- package/dist/i18n.es.mjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.mjs +270 -241
- package/dist/media/observers.d.ts.map +1 -1
- package/dist/{media-D4zLj9t-.js → media-dAKIGPk3.js} +3 -3
- package/dist/{media-D4zLj9t-.js.map → media-dAKIGPk3.js.map} +1 -1
- package/dist/media.es.mjs +1 -1
- package/dist/{motion-BJsAuULb.js → motion-BBMso9Ir.js} +1 -1
- package/dist/{motion-BJsAuULb.js.map → motion-BBMso9Ir.js.map} +1 -1
- package/dist/motion.es.mjs +1 -1
- package/dist/{mount-B-JvH6Y0.js → mount-C8O2vXkQ.js} +10 -9
- package/dist/{mount-B-JvH6Y0.js.map → mount-C8O2vXkQ.js.map} +1 -1
- package/dist/{platform-Dw2gE3zI.js → platform-BPHIXbw8.js} +17 -16
- package/dist/{platform-Dw2gE3zI.js.map → platform-BPHIXbw8.js.map} +1 -1
- package/dist/platform.es.mjs +1 -1
- package/dist/{plugin-C2WuC8SF.js → plugin-DjTqWg-P.js} +2 -2
- package/dist/{plugin-C2WuC8SF.js.map → plugin-DjTqWg-P.js.map} +1 -1
- package/dist/plugin.es.mjs +1 -1
- package/dist/reactive/watch.d.ts.map +1 -1
- package/dist/reactive/websocket.d.ts +6 -3
- package/dist/reactive/websocket.d.ts.map +1 -1
- package/dist/{reactive-BjpLkclt.js → reactive-BAd2hfl8.js} +436 -449
- package/dist/reactive-BAd2hfl8.js.map +1 -0
- package/dist/reactive.es.mjs +42 -40
- package/dist/readonly-C0ZwS1Tf.js +35 -0
- package/dist/readonly-C0ZwS1Tf.js.map +1 -0
- package/dist/{registry-B08iilIh.js → registry-Cr6VH8CR.js} +1 -1
- package/dist/{registry-B08iilIh.js.map → registry-Cr6VH8CR.js.map} +1 -1
- package/dist/{router-BieVwgci.js → router-CCepRMpC.js} +29 -28
- package/dist/{router-BieVwgci.js.map → router-CCepRMpC.js.map} +1 -1
- package/dist/router.es.mjs +1 -1
- package/dist/{ssr-CrGSJySz.js → ssr-D-1IPcfw.js} +4 -4
- package/dist/{ssr-CrGSJySz.js.map → ssr-D-1IPcfw.js.map} +1 -1
- package/dist/ssr.es.mjs +1 -1
- package/dist/{store-CY6sjTW3.js → store-CjmEeX9-.js} +6 -6
- package/dist/{store-CY6sjTW3.js.map → store-CjmEeX9-.js.map} +1 -1
- package/dist/store.es.mjs +2 -2
- package/dist/{testing-UjAtu9aQ.js → testing-TdfaL7VE.js} +7 -7
- package/dist/{testing-UjAtu9aQ.js.map → testing-TdfaL7VE.js.map} +1 -1
- package/dist/testing.es.mjs +1 -1
- package/dist/{untrack-D0fnO5k2.js → untrack-bjWDNdyE.js} +11 -10
- package/dist/{untrack-D0fnO5k2.js.map → untrack-bjWDNdyE.js.map} +1 -1
- package/dist/view.es.mjs +12 -11
- package/package.json +17 -13
- package/src/concurrency/errors.ts +57 -0
- package/src/concurrency/high-level.ts +387 -0
- package/src/concurrency/index.ts +63 -0
- package/src/concurrency/internal.ts +100 -0
- package/src/concurrency/pipeline.ts +133 -0
- package/src/concurrency/pool.ts +450 -0
- package/src/concurrency/reactive.ts +339 -0
- package/src/concurrency/rpc.ts +380 -0
- package/src/concurrency/support.ts +44 -0
- package/src/concurrency/task.ts +318 -0
- package/src/concurrency/types.ts +431 -0
- package/src/full.ts +65 -0
- package/src/index.ts +3 -0
- package/src/media/observers.ts +5 -8
- package/src/reactive/watch.ts +10 -9
- package/src/reactive/websocket.ts +31 -8
- package/dist/core-DdtZHzsS.js +0 -168
- package/dist/core-DdtZHzsS.js.map +0 -1
- package/dist/reactive-BjpLkclt.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dnd-NwZBYh4l.js","names":[],"sources":["../src/dnd/draggable.ts","../src/dnd/droppable.ts","../src/dnd/sortable.ts"],"sourcesContent":["/**\n * Make an element draggable using pointer events.\n *\n * Uses Pointer Events (not HTML5 Drag & Drop) for reliable\n * cross-platform behavior including touch support.\n *\n * @module bquery/dnd\n */\n\nimport type {\n BoundsRect,\n DragBounds,\n DragEventData,\n DragPosition,\n DraggableHandle,\n DraggableOptions,\n} from './types';\n\n/** Global registry of active draggable elements for drop zone detection. */\nconst activeDrags = new Map<HTMLElement, { element: HTMLElement; position: DragPosition }>();\n\n/**\n * Returns the currently active drag state, if any.\n * Used internally by `droppable()` to detect drag interactions.\n * @internal\n */\nexport const getActiveDrag = (): { element: HTMLElement; position: DragPosition } | undefined => {\n const entries = Array.from(activeDrags.values());\n return entries[entries.length - 1];\n};\n\n/**\n * Resolves a `DragBounds` value to an absolute `BoundsRect`.\n * @internal\n */\nconst resolveBounds = (el: HTMLElement, bounds: DragBounds): BoundsRect | null => {\n if (typeof bounds === 'object') {\n return bounds;\n }\n\n let target: HTMLElement | null = null;\n\n if (bounds === 'parent') {\n target = el.parentElement;\n } else {\n target = document.querySelector(bounds) as HTMLElement | null;\n }\n\n if (!target) return null;\n\n const rect = target.getBoundingClientRect();\n const elRect = el.getBoundingClientRect();\n const rawLeft = parseFloat(el.style.left || '0');\n const rawTop = parseFloat(el.style.top || '0');\n const leftOffset = Number.isNaN(rawLeft) ? 0 : rawLeft;\n const topOffset = Number.isNaN(rawTop) ? 0 : rawTop;\n\n return {\n left: rect.left - elRect.left + leftOffset,\n top: rect.top - elRect.top + topOffset,\n right: rect.right - elRect.right + leftOffset + (rect.width - elRect.width),\n bottom: rect.bottom - elRect.bottom + topOffset + (rect.height - elRect.height),\n };\n};\n\n/**\n * Clamp a position within bounds.\n * @internal\n */\nconst clampPosition = (pos: DragPosition, bounds: BoundsRect | null): DragPosition => {\n if (!bounds) return pos;\n return {\n x: Math.max(bounds.left, Math.min(bounds.right, pos.x)),\n y: Math.max(bounds.top, Math.min(bounds.bottom, pos.y)),\n };\n};\n\n/**\n * Makes an element draggable using pointer events.\n *\n * Features:\n * - Touch and mouse support via Pointer Events\n * - Axis locking (`x`, `y`, or `both`)\n * - Bounds constraint (parent, selector, or explicit rect)\n * - Optional drag handle\n * - Ghost/clone preview during drag\n * - Callbacks: `onDragStart`, `onDrag`, `onDragEnd`\n *\n * @param el - The element to make draggable\n * @param options - Configuration options\n * @returns A handle with `destroy()`, `disable()`, and `enable()` methods\n *\n * @example\n * ```ts\n * import { draggable } from '@bquery/bquery/dnd';\n *\n * const handle = draggable(document.querySelector('#box'), {\n * axis: 'both',\n * bounds: 'parent',\n * onDragEnd: ({ position }) => {\n * console.log('Dropped at', position.x, position.y);\n * },\n * });\n *\n * // Later:\n * handle.destroy();\n * ```\n */\nexport const draggable = (el: HTMLElement, options: DraggableOptions = {}): DraggableHandle => {\n const {\n axis = 'both',\n bounds,\n handle,\n ghost = false,\n ghostClass = 'bq-drag-ghost',\n draggingClass = 'bq-dragging',\n onDragStart,\n onDrag,\n onDragEnd,\n } = options;\n\n let enabled = !options.disabled;\n let isDragging = false;\n let startPointer: DragPosition = { x: 0, y: 0 };\n let currentPosition: DragPosition = { x: 0, y: 0 };\n let previousPosition: DragPosition = { x: 0, y: 0 };\n let ghostEl: HTMLElement | null = null;\n let ghostStartPosition: DragPosition | null = null;\n const previousTouchAction = el.style.touchAction;\n const previousUserSelect = el.style.userSelect;\n\n const createEventData = (event: PointerEvent): DragEventData => ({\n element: el,\n position: { ...currentPosition },\n delta: {\n x: currentPosition.x - previousPosition.x,\n y: currentPosition.y - previousPosition.y,\n },\n event,\n });\n\n const createGhost = (): HTMLElement => {\n const clone = el.cloneNode(true) as HTMLElement;\n const rect = el.getBoundingClientRect();\n clone.classList.add(ghostClass);\n clone.style.position = 'fixed';\n clone.style.left = `${rect.left}px`;\n clone.style.top = `${rect.top}px`;\n clone.style.width = `${rect.width}px`;\n clone.style.height = `${rect.height}px`;\n clone.style.pointerEvents = 'none';\n clone.style.zIndex = '999999';\n clone.style.opacity = '0.7';\n clone.style.margin = '0';\n document.body.appendChild(clone);\n return clone;\n };\n\n const removeGhost = (): void => {\n if (ghostEl) {\n ghostEl.remove();\n ghostEl = null;\n }\n ghostStartPosition = null;\n };\n\n const onPointerDown = (e: PointerEvent): void => {\n if (!enabled) return;\n\n // Check handle constraint\n if (handle) {\n const target = e.target as Element;\n if (!target.closest(handle)) return;\n }\n\n e.preventDefault();\n isDragging = true;\n startPointer = { x: e.clientX, y: e.clientY };\n previousPosition = { ...currentPosition };\n\n el.classList.add(draggingClass);\n el.setPointerCapture(e.pointerId);\n\n if (ghost) {\n const rect = el.getBoundingClientRect();\n ghostStartPosition = { x: rect.left, y: rect.top };\n ghostEl = createGhost();\n }\n\n // Register in global active drags\n activeDrags.set(el, { element: el, position: currentPosition });\n\n onDragStart?.(createEventData(e));\n };\n\n const onPointerMove = (e: PointerEvent): void => {\n if (!isDragging) return;\n\n e.preventDefault();\n previousPosition = { ...currentPosition };\n\n let newX = currentPosition.x + (e.clientX - startPointer.x);\n let newY = currentPosition.y + (e.clientY - startPointer.y);\n\n // Reset start pointer to current for delta calculation\n startPointer = { x: e.clientX, y: e.clientY };\n\n // Apply axis constraint\n if (axis === 'x') newY = currentPosition.y;\n if (axis === 'y') newX = currentPosition.x;\n\n let newPos: DragPosition = { x: newX, y: newY };\n\n // Apply bounds constraint\n if (bounds) {\n const resolvedBounds = resolveBounds(el, bounds);\n newPos = clampPosition(newPos, resolvedBounds);\n }\n\n currentPosition = newPos;\n\n // Update active drag position\n activeDrags.set(el, { element: el, position: currentPosition });\n\n // Apply the position\n if (ghost && ghostEl) {\n const start = ghostStartPosition ?? {\n x: el.getBoundingClientRect().left,\n y: el.getBoundingClientRect().top,\n };\n ghostEl.style.left = `${start.x + currentPosition.x}px`;\n ghostEl.style.top = `${start.y + currentPosition.y}px`;\n } else {\n el.style.transform = `translate(${currentPosition.x}px, ${currentPosition.y}px)`;\n }\n\n onDrag?.(createEventData(e));\n };\n\n const onPointerUp = (e: PointerEvent): void => {\n if (!isDragging) return;\n\n isDragging = false;\n el.classList.remove(draggingClass);\n try {\n if (\n typeof el.releasePointerCapture === 'function' &&\n (typeof el.hasPointerCapture !== 'function' || el.hasPointerCapture(e.pointerId))\n ) {\n el.releasePointerCapture(e.pointerId);\n }\n } catch {\n // Pointer capture may already be released in some interrupted drag flows.\n } finally {\n removeGhost();\n\n // Remove from active drags\n activeDrags.delete(el);\n\n onDragEnd?.(createEventData(e));\n }\n };\n\n // Attach listeners\n el.addEventListener('pointerdown', onPointerDown);\n el.addEventListener('pointermove', onPointerMove);\n el.addEventListener('pointerup', onPointerUp);\n el.addEventListener('pointercancel', onPointerUp);\n\n // Prevent default drag behavior\n el.style.touchAction = 'none';\n el.style.userSelect = 'none';\n\n return {\n destroy: () => {\n el.removeEventListener('pointerdown', onPointerDown);\n el.removeEventListener('pointermove', onPointerMove);\n el.removeEventListener('pointerup', onPointerUp);\n el.removeEventListener('pointercancel', onPointerUp);\n removeGhost();\n activeDrags.delete(el);\n el.style.touchAction = previousTouchAction;\n el.style.userSelect = previousUserSelect;\n el.classList.remove(draggingClass);\n },\n disable: () => {\n enabled = false;\n },\n enable: () => {\n enabled = true;\n },\n get enabled() {\n return enabled;\n },\n };\n};\n","/**\n * Define drop zones for draggable elements.\n *\n * Drop zones detect when draggable elements enter, move over,\n * leave, or are dropped onto them using pointer event hit-testing.\n *\n * @module bquery/dnd\n */\n\nimport { getActiveDrag } from './draggable';\nimport type { DropEventData, DroppableHandle, DroppableOptions } from './types';\n\ntype DroppableListener = {\n handlePointerMove: (event: PointerEvent) => void;\n handlePointerUp: (event: PointerEvent) => void;\n};\n\nconst passivePointerMoveListenerOptions = { passive: true } as const;\n\nconst droppableListeners = new Set<DroppableListener>();\nlet queuedPointerMove: PointerEvent | null = null;\nlet pointerMoveFrame: number | null = null;\n\nconst getDroppableListenersSnapshot = (): DroppableListener[] => Array.from(droppableListeners);\n\nconst hasDroppableEnvironment = (): boolean => {\n return (\n typeof document !== 'undefined' &&\n typeof document.addEventListener === 'function' &&\n typeof document.removeEventListener === 'function' &&\n typeof requestAnimationFrame === 'function' &&\n typeof cancelAnimationFrame === 'function'\n );\n};\n\nconst dispatchPointerMove = (event: PointerEvent): void => {\n for (const listener of getDroppableListenersSnapshot()) {\n listener.handlePointerMove(event);\n }\n};\n\nconst flushPointerMove = (): void => {\n pointerMoveFrame = null;\n const event = queuedPointerMove;\n queuedPointerMove = null;\n if (!event) return;\n dispatchPointerMove(event);\n};\n\nconst handleDocumentPointerMove = (event: PointerEvent): void => {\n queuedPointerMove = event;\n if (pointerMoveFrame === null) {\n pointerMoveFrame = requestAnimationFrame(flushPointerMove);\n }\n};\n\nconst handleDocumentPointerUp = (event: PointerEvent): void => {\n if (pointerMoveFrame !== null) {\n cancelAnimationFrame(pointerMoveFrame);\n pointerMoveFrame = null;\n queuedPointerMove = null;\n }\n\n for (const listener of getDroppableListenersSnapshot()) {\n listener.handlePointerUp(event);\n }\n};\n\nconst registerDroppableListener = (listener: DroppableListener): void => {\n if (droppableListeners.size === 0) {\n document.addEventListener(\n 'pointermove',\n handleDocumentPointerMove,\n passivePointerMoveListenerOptions\n );\n document.addEventListener('pointerup', handleDocumentPointerUp);\n }\n\n droppableListeners.add(listener);\n};\n\nconst unregisterDroppableListener = (listener: DroppableListener): void => {\n droppableListeners.delete(listener);\n\n if (droppableListeners.size !== 0) return;\n\n document.removeEventListener('pointermove', handleDocumentPointerMove);\n document.removeEventListener('pointerup', handleDocumentPointerUp);\n if (pointerMoveFrame !== null) {\n cancelAnimationFrame(pointerMoveFrame);\n pointerMoveFrame = null;\n }\n queuedPointerMove = null;\n};\n\n/**\n * Checks whether a dragged element is accepted by the drop zone.\n * @internal\n */\nconst isAccepted = (dragged: HTMLElement, accept: DroppableOptions['accept']): boolean => {\n if (!accept) return true;\n if (typeof accept === 'string') return dragged.matches(accept);\n return accept(dragged);\n};\n\n/**\n * Defines an element as a drop zone.\n *\n * Drop zones respond to draggable elements being moved over them\n * by firing callbacks and applying CSS classes. They work with\n * the `draggable()` function from this module.\n *\n * @param el - The drop zone element\n * @param options - Configuration options\n * @returns A handle with a `destroy()` method\n *\n * @example\n * ```ts\n * import { droppable } from '@bquery/bquery/dnd';\n *\n * const handle = droppable(document.querySelector('#dropzone'), {\n * accept: '.draggable-item',\n * overClass: 'drop-active',\n * onDrop: ({ dragged }) => {\n * console.log('Dropped:', dragged);\n * },\n * });\n *\n * // Later:\n * handle.destroy();\n * ```\n */\nexport const droppable = (el: HTMLElement, options: DroppableOptions = {}): DroppableHandle => {\n const {\n overClass = 'bq-drop-over',\n accept,\n onDragEnter,\n onDragOver,\n onDragLeave,\n onDrop,\n } = options;\n\n if (!hasDroppableEnvironment()) {\n return {\n destroy: () => {},\n };\n }\n\n let isOver = false;\n let currentDragged: HTMLElement | null = null;\n\n const createEventData = (dragged: HTMLElement, event: PointerEvent): DropEventData => ({\n zone: el,\n dragged,\n event,\n });\n\n const isPointerInside = (event: PointerEvent): boolean => {\n const rect = el.getBoundingClientRect();\n return (\n event.clientX >= rect.left &&\n event.clientX <= rect.right &&\n event.clientY >= rect.top &&\n event.clientY <= rect.bottom\n );\n };\n\n const resolveDraggedElement = (): HTMLElement | null => {\n return getActiveDrag()?.element ?? currentDragged;\n };\n\n const clearOverState = (event: PointerEvent, dragged = currentDragged): void => {\n if (!isOver) return;\n isOver = false;\n el.classList.remove(overClass);\n if (dragged) {\n onDragLeave?.(createEventData(dragged, event));\n }\n currentDragged = null;\n };\n\n const handlePointerMove = (e: PointerEvent): void => {\n const dragged = getActiveDrag()?.element ?? null;\n const isInside = isPointerInside(e);\n const acceptsDragged = dragged !== null && dragged !== el && isAccepted(dragged, accept);\n\n if (!acceptsDragged || !isInside) {\n clearOverState(e, dragged ?? currentDragged);\n return;\n }\n\n if (!isOver) {\n isOver = true;\n currentDragged = dragged;\n el.classList.add(overClass);\n onDragEnter?.(createEventData(dragged, e));\n } else {\n onDragOver?.(createEventData(dragged, e));\n }\n };\n\n const handlePointerUp = (e: PointerEvent): void => {\n const dragged = resolveDraggedElement();\n const isInside = isPointerInside(e);\n const acceptsDragged = dragged !== null && dragged !== el && isAccepted(dragged, accept);\n\n if (isInside && acceptsDragged && dragged) {\n onDrop?.(createEventData(dragged, e));\n }\n\n if (isOver) {\n isOver = false;\n el.classList.remove(overClass);\n }\n currentDragged = null;\n };\n\n const listener: DroppableListener = { handlePointerMove, handlePointerUp };\n registerDroppableListener(listener);\n\n return {\n destroy: () => {\n unregisterDroppableListener(listener);\n el.classList.remove(overClass);\n currentDragged = null;\n },\n };\n};\n","/**\n * Sortable list with animated reordering via pointer events.\n *\n * Makes children of a container sortable by dragging. Items are\n * rearranged in the DOM with optional CSS animation.\n *\n * @module bquery/dnd\n */\n\nimport type { SortEventData, SortableHandle, SortableOptions } from './types';\n\n/**\n * Gets the sortable items within a container.\n * @internal\n */\nconst getItems = (container: HTMLElement, selector: string): HTMLElement[] => {\n return Array.from(container.querySelectorAll(selector)) as HTMLElement[];\n};\n\n/**\n * Finds the closest sortable item to a given Y (or X) position.\n * @internal\n */\nconst getClosestItem = (\n items: HTMLElement[],\n clientPos: number,\n axis: 'x' | 'y',\n dragged: HTMLElement\n): { element: HTMLElement; index: number } | null => {\n let closest: { element: HTMLElement; index: number; distance: number } | null = null;\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (item === dragged) continue;\n\n const rect = item.getBoundingClientRect();\n const mid = axis === 'y' ? rect.top + rect.height / 2 : rect.left + rect.width / 2;\n const distance = clientPos - mid;\n\n if (\n closest === null ||\n (distance < 0 && distance > closest.distance) ||\n (closest.distance >= 0 && distance < 0 && Math.abs(distance) < Math.abs(closest.distance))\n ) {\n // Find the item we're just before\n if (distance < 0) {\n closest = { element: item, index: i, distance };\n }\n }\n }\n\n return closest ? { element: closest.element, index: closest.index } : null;\n};\n\n/**\n * Makes the children of a container sortable by dragging.\n *\n * Features:\n * - Pointer event based (touch + mouse)\n * - Animated reordering with configurable duration\n * - Axis constraint (vertical or horizontal)\n * - Optional drag handle\n * - Placeholder element during sort\n * - Callbacks: `onSortStart`, `onSortMove`, `onSortEnd`\n *\n * @param container - The container element whose children will be sortable\n * @param options - Configuration options\n * @returns A handle with `destroy()`, `disable()`, and `enable()` methods\n *\n * @example\n * ```ts\n * import { sortable } from '@bquery/bquery/dnd';\n *\n * const handle = sortable(document.querySelector('#list'), {\n * items: 'li',\n * axis: 'y',\n * animationDuration: 200,\n * onSortEnd: ({ oldIndex, newIndex }) => {\n * console.log(`Moved from ${oldIndex} to ${newIndex}`);\n * },\n * });\n *\n * // Later:\n * handle.destroy();\n * ```\n */\nexport const sortable = (container: HTMLElement, options: SortableOptions = {}): SortableHandle => {\n const {\n items: itemSelector = ':scope > *',\n axis = 'y',\n handle,\n placeholderClass = 'bq-sort-placeholder',\n sortingClass = 'bq-sorting',\n animationDuration = 200,\n onSortStart,\n onSortMove,\n onSortEnd,\n } = options;\n\n let enabled = !options.disabled;\n let isDragging = false;\n let dragItem: HTMLElement | null = null;\n let placeholder: HTMLElement | null = null;\n let startIndex = -1;\n let startPointerY = 0;\n let startPointerX = 0;\n let itemStartTop = 0;\n let itemStartLeft = 0;\n\n const createEventData = (item: HTMLElement, oldIdx: number, newIdx: number): SortEventData => ({\n container,\n item,\n oldIndex: oldIdx,\n newIndex: newIdx,\n });\n\n const onPointerDown = (e: PointerEvent): void => {\n if (!enabled) return;\n\n const target = e.target as HTMLElement;\n\n // Find the item being dragged\n const items = getItems(container, itemSelector);\n let item: HTMLElement | null = null;\n\n for (const it of items) {\n if (it.contains(target)) {\n item = it;\n break;\n }\n }\n\n if (!item) return;\n\n // Check handle constraint\n if (handle && !target.closest(handle)) return;\n\n e.preventDefault();\n\n isDragging = true;\n dragItem = item;\n startIndex = items.indexOf(item);\n startPointerY = e.clientY;\n startPointerX = e.clientX;\n\n const rect = item.getBoundingClientRect();\n itemStartTop = rect.top;\n itemStartLeft = rect.left;\n\n // Create placeholder\n placeholder = document.createElement('div');\n placeholder.classList.add(placeholderClass);\n placeholder.style.width = `${rect.width}px`;\n placeholder.style.height = `${rect.height}px`;\n placeholder.style.boxSizing = 'border-box';\n\n // Style the dragged item\n item.classList.add(sortingClass);\n item.style.position = 'fixed';\n item.style.width = `${rect.width}px`;\n item.style.height = `${rect.height}px`;\n item.style.left = `${rect.left}px`;\n item.style.top = `${rect.top}px`;\n item.style.zIndex = '999999';\n item.style.pointerEvents = 'none';\n item.style.margin = '0';\n\n // Insert placeholder where the item was\n item.parentNode?.insertBefore(placeholder, item);\n\n container.setPointerCapture(e.pointerId);\n\n onSortStart?.(createEventData(item, startIndex, startIndex));\n };\n\n const onPointerMove = (e: PointerEvent): void => {\n if (!isDragging || !dragItem || !placeholder) return;\n\n e.preventDefault();\n\n const deltaX = e.clientX - startPointerX;\n const deltaY = e.clientY - startPointerY;\n\n // Move the dragged item\n if (axis === 'y') {\n dragItem.style.top = `${itemStartTop + deltaY}px`;\n } else {\n dragItem.style.left = `${itemStartLeft + deltaX}px`;\n }\n\n // Find the closest item to determine insertion point\n const items = getItems(container, itemSelector);\n const clientPos = axis === 'y' ? e.clientY : e.clientX;\n const closest = getClosestItem(items, clientPos, axis, dragItem);\n\n if (closest) {\n // Move placeholder before the closest element\n container.insertBefore(placeholder, closest.element);\n } else {\n // Append to end\n container.appendChild(placeholder);\n }\n\n const currentIndex = Array.from(container.children).indexOf(placeholder);\n onSortMove?.(createEventData(dragItem, startIndex, currentIndex));\n };\n\n const onPointerUp = (e: PointerEvent): void => {\n if (!isDragging || !dragItem || !placeholder) return;\n\n isDragging = false;\n const draggedItem = dragItem;\n\n // Get final index\n const newIndex = Array.from(container.children).indexOf(placeholder);\n\n // Animate the item back to the placeholder position\n const placeholderRect = placeholder.getBoundingClientRect();\n const itemRect = draggedItem.getBoundingClientRect();\n\n if (animationDuration > 0) {\n const deltaX = placeholderRect.left - itemRect.left;\n const deltaY = placeholderRect.top - itemRect.top;\n\n draggedItem.style.transition = `transform ${animationDuration}ms ease`;\n draggedItem.style.transform = `translate(${deltaX}px, ${deltaY}px)`;\n\n let finalized = false;\n let timeoutId: number | null = null;\n const finalize = (): void => {\n if (finalized) return;\n finalized = true;\n if (timeoutId !== null) {\n window.clearTimeout(timeoutId);\n timeoutId = null;\n }\n resetDragItem();\n onSortEnd?.(createEventData(draggedItem, startIndex, newIndex));\n };\n timeoutId = window.setTimeout(() => {\n finalize();\n }, animationDuration + 50);\n\n draggedItem.addEventListener('transitionend', finalize, { once: true });\n } else {\n resetDragItem();\n onSortEnd?.(createEventData(draggedItem, startIndex, newIndex));\n }\n\n container.releasePointerCapture(e.pointerId);\n };\n\n const resetDragItem = (): void => {\n if (!dragItem || !placeholder) return;\n\n // Insert the real item where the placeholder is\n placeholder.parentNode?.insertBefore(dragItem, placeholder);\n placeholder.remove();\n placeholder = null;\n\n // Reset styles\n dragItem.classList.remove(sortingClass);\n dragItem.style.position = '';\n dragItem.style.width = '';\n dragItem.style.height = '';\n dragItem.style.left = '';\n dragItem.style.top = '';\n dragItem.style.zIndex = '';\n dragItem.style.pointerEvents = '';\n dragItem.style.margin = '';\n dragItem.style.transition = '';\n dragItem.style.transform = '';\n\n dragItem = null;\n };\n\n container.addEventListener('pointerdown', onPointerDown);\n container.addEventListener('pointermove', onPointerMove);\n container.addEventListener('pointerup', onPointerUp);\n container.addEventListener('pointercancel', onPointerUp);\n\n // Prevent default touch behavior on container\n container.style.touchAction = 'none';\n\n return {\n destroy: () => {\n container.removeEventListener('pointerdown', onPointerDown);\n container.removeEventListener('pointermove', onPointerMove);\n container.removeEventListener('pointerup', onPointerUp);\n container.removeEventListener('pointercancel', onPointerUp);\n container.style.touchAction = '';\n\n if (isDragging) {\n resetDragItem();\n }\n },\n disable: () => {\n enabled = false;\n },\n enable: () => {\n enabled = true;\n },\n get enabled() {\n return enabled;\n },\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dnd-BAqzPlSo.js","names":[],"sources":["../src/dnd/draggable.ts","../src/dnd/droppable.ts","../src/dnd/sortable.ts"],"sourcesContent":["/**\n * Make an element draggable using pointer events.\n *\n * Uses Pointer Events (not HTML5 Drag & Drop) for reliable\n * cross-platform behavior including touch support.\n *\n * @module bquery/dnd\n */\n\nimport type {\n BoundsRect,\n DragBounds,\n DragEventData,\n DragPosition,\n DraggableHandle,\n DraggableOptions,\n} from './types';\n\n/** Global registry of active draggable elements for drop zone detection. */\nconst activeDrags = new Map<HTMLElement, { element: HTMLElement; position: DragPosition }>();\n\n/**\n * Returns the currently active drag state, if any.\n * Used internally by `droppable()` to detect drag interactions.\n * @internal\n */\nexport const getActiveDrag = (): { element: HTMLElement; position: DragPosition } | undefined => {\n const entries = Array.from(activeDrags.values());\n return entries[entries.length - 1];\n};\n\n/**\n * Resolves a `DragBounds` value to an absolute `BoundsRect`.\n * @internal\n */\nconst resolveBounds = (el: HTMLElement, bounds: DragBounds): BoundsRect | null => {\n if (typeof bounds === 'object') {\n return bounds;\n }\n\n let target: HTMLElement | null = null;\n\n if (bounds === 'parent') {\n target = el.parentElement;\n } else {\n target = document.querySelector(bounds) as HTMLElement | null;\n }\n\n if (!target) return null;\n\n const rect = target.getBoundingClientRect();\n const elRect = el.getBoundingClientRect();\n const rawLeft = parseFloat(el.style.left || '0');\n const rawTop = parseFloat(el.style.top || '0');\n const leftOffset = Number.isNaN(rawLeft) ? 0 : rawLeft;\n const topOffset = Number.isNaN(rawTop) ? 0 : rawTop;\n\n return {\n left: rect.left - elRect.left + leftOffset,\n top: rect.top - elRect.top + topOffset,\n right: rect.right - elRect.right + leftOffset + (rect.width - elRect.width),\n bottom: rect.bottom - elRect.bottom + topOffset + (rect.height - elRect.height),\n };\n};\n\n/**\n * Clamp a position within bounds.\n * @internal\n */\nconst clampPosition = (pos: DragPosition, bounds: BoundsRect | null): DragPosition => {\n if (!bounds) return pos;\n return {\n x: Math.max(bounds.left, Math.min(bounds.right, pos.x)),\n y: Math.max(bounds.top, Math.min(bounds.bottom, pos.y)),\n };\n};\n\n/**\n * Makes an element draggable using pointer events.\n *\n * Features:\n * - Touch and mouse support via Pointer Events\n * - Axis locking (`x`, `y`, or `both`)\n * - Bounds constraint (parent, selector, or explicit rect)\n * - Optional drag handle\n * - Ghost/clone preview during drag\n * - Callbacks: `onDragStart`, `onDrag`, `onDragEnd`\n *\n * @param el - The element to make draggable\n * @param options - Configuration options\n * @returns A handle with `destroy()`, `disable()`, and `enable()` methods\n *\n * @example\n * ```ts\n * import { draggable } from '@bquery/bquery/dnd';\n *\n * const handle = draggable(document.querySelector('#box'), {\n * axis: 'both',\n * bounds: 'parent',\n * onDragEnd: ({ position }) => {\n * console.log('Dropped at', position.x, position.y);\n * },\n * });\n *\n * // Later:\n * handle.destroy();\n * ```\n */\nexport const draggable = (el: HTMLElement, options: DraggableOptions = {}): DraggableHandle => {\n const {\n axis = 'both',\n bounds,\n handle,\n ghost = false,\n ghostClass = 'bq-drag-ghost',\n draggingClass = 'bq-dragging',\n onDragStart,\n onDrag,\n onDragEnd,\n } = options;\n\n let enabled = !options.disabled;\n let isDragging = false;\n let startPointer: DragPosition = { x: 0, y: 0 };\n let currentPosition: DragPosition = { x: 0, y: 0 };\n let previousPosition: DragPosition = { x: 0, y: 0 };\n let ghostEl: HTMLElement | null = null;\n let ghostStartPosition: DragPosition | null = null;\n const previousTouchAction = el.style.touchAction;\n const previousUserSelect = el.style.userSelect;\n\n const createEventData = (event: PointerEvent): DragEventData => ({\n element: el,\n position: { ...currentPosition },\n delta: {\n x: currentPosition.x - previousPosition.x,\n y: currentPosition.y - previousPosition.y,\n },\n event,\n });\n\n const createGhost = (): HTMLElement => {\n const clone = el.cloneNode(true) as HTMLElement;\n const rect = el.getBoundingClientRect();\n clone.classList.add(ghostClass);\n clone.style.position = 'fixed';\n clone.style.left = `${rect.left}px`;\n clone.style.top = `${rect.top}px`;\n clone.style.width = `${rect.width}px`;\n clone.style.height = `${rect.height}px`;\n clone.style.pointerEvents = 'none';\n clone.style.zIndex = '999999';\n clone.style.opacity = '0.7';\n clone.style.margin = '0';\n document.body.appendChild(clone);\n return clone;\n };\n\n const removeGhost = (): void => {\n if (ghostEl) {\n ghostEl.remove();\n ghostEl = null;\n }\n ghostStartPosition = null;\n };\n\n const onPointerDown = (e: PointerEvent): void => {\n if (!enabled) return;\n\n // Check handle constraint\n if (handle) {\n const target = e.target as Element;\n if (!target.closest(handle)) return;\n }\n\n e.preventDefault();\n isDragging = true;\n startPointer = { x: e.clientX, y: e.clientY };\n previousPosition = { ...currentPosition };\n\n el.classList.add(draggingClass);\n el.setPointerCapture(e.pointerId);\n\n if (ghost) {\n const rect = el.getBoundingClientRect();\n ghostStartPosition = { x: rect.left, y: rect.top };\n ghostEl = createGhost();\n }\n\n // Register in global active drags\n activeDrags.set(el, { element: el, position: currentPosition });\n\n onDragStart?.(createEventData(e));\n };\n\n const onPointerMove = (e: PointerEvent): void => {\n if (!isDragging) return;\n\n e.preventDefault();\n previousPosition = { ...currentPosition };\n\n let newX = currentPosition.x + (e.clientX - startPointer.x);\n let newY = currentPosition.y + (e.clientY - startPointer.y);\n\n // Reset start pointer to current for delta calculation\n startPointer = { x: e.clientX, y: e.clientY };\n\n // Apply axis constraint\n if (axis === 'x') newY = currentPosition.y;\n if (axis === 'y') newX = currentPosition.x;\n\n let newPos: DragPosition = { x: newX, y: newY };\n\n // Apply bounds constraint\n if (bounds) {\n const resolvedBounds = resolveBounds(el, bounds);\n newPos = clampPosition(newPos, resolvedBounds);\n }\n\n currentPosition = newPos;\n\n // Update active drag position\n activeDrags.set(el, { element: el, position: currentPosition });\n\n // Apply the position\n if (ghost && ghostEl) {\n const start = ghostStartPosition ?? {\n x: el.getBoundingClientRect().left,\n y: el.getBoundingClientRect().top,\n };\n ghostEl.style.left = `${start.x + currentPosition.x}px`;\n ghostEl.style.top = `${start.y + currentPosition.y}px`;\n } else {\n el.style.transform = `translate(${currentPosition.x}px, ${currentPosition.y}px)`;\n }\n\n onDrag?.(createEventData(e));\n };\n\n const onPointerUp = (e: PointerEvent): void => {\n if (!isDragging) return;\n\n isDragging = false;\n el.classList.remove(draggingClass);\n try {\n if (\n typeof el.releasePointerCapture === 'function' &&\n (typeof el.hasPointerCapture !== 'function' || el.hasPointerCapture(e.pointerId))\n ) {\n el.releasePointerCapture(e.pointerId);\n }\n } catch {\n // Pointer capture may already be released in some interrupted drag flows.\n } finally {\n removeGhost();\n\n // Remove from active drags\n activeDrags.delete(el);\n\n onDragEnd?.(createEventData(e));\n }\n };\n\n // Attach listeners\n el.addEventListener('pointerdown', onPointerDown);\n el.addEventListener('pointermove', onPointerMove);\n el.addEventListener('pointerup', onPointerUp);\n el.addEventListener('pointercancel', onPointerUp);\n\n // Prevent default drag behavior\n el.style.touchAction = 'none';\n el.style.userSelect = 'none';\n\n return {\n destroy: () => {\n el.removeEventListener('pointerdown', onPointerDown);\n el.removeEventListener('pointermove', onPointerMove);\n el.removeEventListener('pointerup', onPointerUp);\n el.removeEventListener('pointercancel', onPointerUp);\n removeGhost();\n activeDrags.delete(el);\n el.style.touchAction = previousTouchAction;\n el.style.userSelect = previousUserSelect;\n el.classList.remove(draggingClass);\n },\n disable: () => {\n enabled = false;\n },\n enable: () => {\n enabled = true;\n },\n get enabled() {\n return enabled;\n },\n };\n};\n","/**\n * Define drop zones for draggable elements.\n *\n * Drop zones detect when draggable elements enter, move over,\n * leave, or are dropped onto them using pointer event hit-testing.\n *\n * @module bquery/dnd\n */\n\nimport { getActiveDrag } from './draggable';\nimport type { DropEventData, DroppableHandle, DroppableOptions } from './types';\n\ntype DroppableListener = {\n handlePointerMove: (event: PointerEvent) => void;\n handlePointerUp: (event: PointerEvent) => void;\n};\n\nconst passivePointerMoveListenerOptions = { passive: true } as const;\n\nconst droppableListeners = new Set<DroppableListener>();\nlet queuedPointerMove: PointerEvent | null = null;\nlet pointerMoveFrame: number | null = null;\n\nconst getDroppableListenersSnapshot = (): DroppableListener[] => Array.from(droppableListeners);\n\nconst hasDroppableEnvironment = (): boolean => {\n return (\n typeof document !== 'undefined' &&\n typeof document.addEventListener === 'function' &&\n typeof document.removeEventListener === 'function' &&\n typeof requestAnimationFrame === 'function' &&\n typeof cancelAnimationFrame === 'function'\n );\n};\n\nconst dispatchPointerMove = (event: PointerEvent): void => {\n for (const listener of getDroppableListenersSnapshot()) {\n listener.handlePointerMove(event);\n }\n};\n\nconst flushPointerMove = (): void => {\n pointerMoveFrame = null;\n const event = queuedPointerMove;\n queuedPointerMove = null;\n if (!event) return;\n dispatchPointerMove(event);\n};\n\nconst handleDocumentPointerMove = (event: PointerEvent): void => {\n queuedPointerMove = event;\n if (pointerMoveFrame === null) {\n pointerMoveFrame = requestAnimationFrame(flushPointerMove);\n }\n};\n\nconst handleDocumentPointerUp = (event: PointerEvent): void => {\n if (pointerMoveFrame !== null) {\n cancelAnimationFrame(pointerMoveFrame);\n pointerMoveFrame = null;\n queuedPointerMove = null;\n }\n\n for (const listener of getDroppableListenersSnapshot()) {\n listener.handlePointerUp(event);\n }\n};\n\nconst registerDroppableListener = (listener: DroppableListener): void => {\n if (droppableListeners.size === 0) {\n document.addEventListener(\n 'pointermove',\n handleDocumentPointerMove,\n passivePointerMoveListenerOptions\n );\n document.addEventListener('pointerup', handleDocumentPointerUp);\n }\n\n droppableListeners.add(listener);\n};\n\nconst unregisterDroppableListener = (listener: DroppableListener): void => {\n droppableListeners.delete(listener);\n\n if (droppableListeners.size !== 0) return;\n\n document.removeEventListener('pointermove', handleDocumentPointerMove);\n document.removeEventListener('pointerup', handleDocumentPointerUp);\n if (pointerMoveFrame !== null) {\n cancelAnimationFrame(pointerMoveFrame);\n pointerMoveFrame = null;\n }\n queuedPointerMove = null;\n};\n\n/**\n * Checks whether a dragged element is accepted by the drop zone.\n * @internal\n */\nconst isAccepted = (dragged: HTMLElement, accept: DroppableOptions['accept']): boolean => {\n if (!accept) return true;\n if (typeof accept === 'string') return dragged.matches(accept);\n return accept(dragged);\n};\n\n/**\n * Defines an element as a drop zone.\n *\n * Drop zones respond to draggable elements being moved over them\n * by firing callbacks and applying CSS classes. They work with\n * the `draggable()` function from this module.\n *\n * @param el - The drop zone element\n * @param options - Configuration options\n * @returns A handle with a `destroy()` method\n *\n * @example\n * ```ts\n * import { droppable } from '@bquery/bquery/dnd';\n *\n * const handle = droppable(document.querySelector('#dropzone'), {\n * accept: '.draggable-item',\n * overClass: 'drop-active',\n * onDrop: ({ dragged }) => {\n * console.log('Dropped:', dragged);\n * },\n * });\n *\n * // Later:\n * handle.destroy();\n * ```\n */\nexport const droppable = (el: HTMLElement, options: DroppableOptions = {}): DroppableHandle => {\n const {\n overClass = 'bq-drop-over',\n accept,\n onDragEnter,\n onDragOver,\n onDragLeave,\n onDrop,\n } = options;\n\n if (!hasDroppableEnvironment()) {\n return {\n destroy: () => {},\n };\n }\n\n let isOver = false;\n let currentDragged: HTMLElement | null = null;\n\n const createEventData = (dragged: HTMLElement, event: PointerEvent): DropEventData => ({\n zone: el,\n dragged,\n event,\n });\n\n const isPointerInside = (event: PointerEvent): boolean => {\n const rect = el.getBoundingClientRect();\n return (\n event.clientX >= rect.left &&\n event.clientX <= rect.right &&\n event.clientY >= rect.top &&\n event.clientY <= rect.bottom\n );\n };\n\n const resolveDraggedElement = (): HTMLElement | null => {\n return getActiveDrag()?.element ?? currentDragged;\n };\n\n const clearOverState = (event: PointerEvent, dragged = currentDragged): void => {\n if (!isOver) return;\n isOver = false;\n el.classList.remove(overClass);\n if (dragged) {\n onDragLeave?.(createEventData(dragged, event));\n }\n currentDragged = null;\n };\n\n const handlePointerMove = (e: PointerEvent): void => {\n const dragged = getActiveDrag()?.element ?? null;\n const isInside = isPointerInside(e);\n const acceptsDragged = dragged !== null && dragged !== el && isAccepted(dragged, accept);\n\n if (!acceptsDragged || !isInside) {\n clearOverState(e, dragged ?? currentDragged);\n return;\n }\n\n if (!isOver) {\n isOver = true;\n currentDragged = dragged;\n el.classList.add(overClass);\n onDragEnter?.(createEventData(dragged, e));\n } else {\n onDragOver?.(createEventData(dragged, e));\n }\n };\n\n const handlePointerUp = (e: PointerEvent): void => {\n const dragged = resolveDraggedElement();\n const isInside = isPointerInside(e);\n const acceptsDragged = dragged !== null && dragged !== el && isAccepted(dragged, accept);\n\n if (isInside && acceptsDragged && dragged) {\n onDrop?.(createEventData(dragged, e));\n }\n\n if (isOver) {\n isOver = false;\n el.classList.remove(overClass);\n }\n currentDragged = null;\n };\n\n const listener: DroppableListener = { handlePointerMove, handlePointerUp };\n registerDroppableListener(listener);\n\n return {\n destroy: () => {\n unregisterDroppableListener(listener);\n el.classList.remove(overClass);\n currentDragged = null;\n },\n };\n};\n","/**\n * Sortable list with animated reordering via pointer events.\n *\n * Makes children of a container sortable by dragging. Items are\n * rearranged in the DOM with optional CSS animation.\n *\n * @module bquery/dnd\n */\n\nimport type { SortEventData, SortableHandle, SortableOptions } from './types';\n\n/**\n * Gets the sortable items within a container.\n * @internal\n */\nconst getItems = (container: HTMLElement, selector: string): HTMLElement[] => {\n return Array.from(container.querySelectorAll(selector)) as HTMLElement[];\n};\n\n/**\n * Finds the closest sortable item to a given Y (or X) position.\n * @internal\n */\nconst getClosestItem = (\n items: HTMLElement[],\n clientPos: number,\n axis: 'x' | 'y',\n dragged: HTMLElement\n): { element: HTMLElement; index: number } | null => {\n let closest: { element: HTMLElement; index: number; distance: number } | null = null;\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (item === dragged) continue;\n\n const rect = item.getBoundingClientRect();\n const mid = axis === 'y' ? rect.top + rect.height / 2 : rect.left + rect.width / 2;\n const distance = clientPos - mid;\n\n if (\n closest === null ||\n (distance < 0 && distance > closest.distance) ||\n (closest.distance >= 0 && distance < 0 && Math.abs(distance) < Math.abs(closest.distance))\n ) {\n // Find the item we're just before\n if (distance < 0) {\n closest = { element: item, index: i, distance };\n }\n }\n }\n\n return closest ? { element: closest.element, index: closest.index } : null;\n};\n\n/**\n * Makes the children of a container sortable by dragging.\n *\n * Features:\n * - Pointer event based (touch + mouse)\n * - Animated reordering with configurable duration\n * - Axis constraint (vertical or horizontal)\n * - Optional drag handle\n * - Placeholder element during sort\n * - Callbacks: `onSortStart`, `onSortMove`, `onSortEnd`\n *\n * @param container - The container element whose children will be sortable\n * @param options - Configuration options\n * @returns A handle with `destroy()`, `disable()`, and `enable()` methods\n *\n * @example\n * ```ts\n * import { sortable } from '@bquery/bquery/dnd';\n *\n * const handle = sortable(document.querySelector('#list'), {\n * items: 'li',\n * axis: 'y',\n * animationDuration: 200,\n * onSortEnd: ({ oldIndex, newIndex }) => {\n * console.log(`Moved from ${oldIndex} to ${newIndex}`);\n * },\n * });\n *\n * // Later:\n * handle.destroy();\n * ```\n */\nexport const sortable = (container: HTMLElement, options: SortableOptions = {}): SortableHandle => {\n const {\n items: itemSelector = ':scope > *',\n axis = 'y',\n handle,\n placeholderClass = 'bq-sort-placeholder',\n sortingClass = 'bq-sorting',\n animationDuration = 200,\n onSortStart,\n onSortMove,\n onSortEnd,\n } = options;\n\n let enabled = !options.disabled;\n let isDragging = false;\n let dragItem: HTMLElement | null = null;\n let placeholder: HTMLElement | null = null;\n let startIndex = -1;\n let startPointerY = 0;\n let startPointerX = 0;\n let itemStartTop = 0;\n let itemStartLeft = 0;\n\n const createEventData = (item: HTMLElement, oldIdx: number, newIdx: number): SortEventData => ({\n container,\n item,\n oldIndex: oldIdx,\n newIndex: newIdx,\n });\n\n const onPointerDown = (e: PointerEvent): void => {\n if (!enabled) return;\n\n const target = e.target as HTMLElement;\n\n // Find the item being dragged\n const items = getItems(container, itemSelector);\n let item: HTMLElement | null = null;\n\n for (const it of items) {\n if (it.contains(target)) {\n item = it;\n break;\n }\n }\n\n if (!item) return;\n\n // Check handle constraint\n if (handle && !target.closest(handle)) return;\n\n e.preventDefault();\n\n isDragging = true;\n dragItem = item;\n startIndex = items.indexOf(item);\n startPointerY = e.clientY;\n startPointerX = e.clientX;\n\n const rect = item.getBoundingClientRect();\n itemStartTop = rect.top;\n itemStartLeft = rect.left;\n\n // Create placeholder\n placeholder = document.createElement('div');\n placeholder.classList.add(placeholderClass);\n placeholder.style.width = `${rect.width}px`;\n placeholder.style.height = `${rect.height}px`;\n placeholder.style.boxSizing = 'border-box';\n\n // Style the dragged item\n item.classList.add(sortingClass);\n item.style.position = 'fixed';\n item.style.width = `${rect.width}px`;\n item.style.height = `${rect.height}px`;\n item.style.left = `${rect.left}px`;\n item.style.top = `${rect.top}px`;\n item.style.zIndex = '999999';\n item.style.pointerEvents = 'none';\n item.style.margin = '0';\n\n // Insert placeholder where the item was\n item.parentNode?.insertBefore(placeholder, item);\n\n container.setPointerCapture(e.pointerId);\n\n onSortStart?.(createEventData(item, startIndex, startIndex));\n };\n\n const onPointerMove = (e: PointerEvent): void => {\n if (!isDragging || !dragItem || !placeholder) return;\n\n e.preventDefault();\n\n const deltaX = e.clientX - startPointerX;\n const deltaY = e.clientY - startPointerY;\n\n // Move the dragged item\n if (axis === 'y') {\n dragItem.style.top = `${itemStartTop + deltaY}px`;\n } else {\n dragItem.style.left = `${itemStartLeft + deltaX}px`;\n }\n\n // Find the closest item to determine insertion point\n const items = getItems(container, itemSelector);\n const clientPos = axis === 'y' ? e.clientY : e.clientX;\n const closest = getClosestItem(items, clientPos, axis, dragItem);\n\n if (closest) {\n // Move placeholder before the closest element\n container.insertBefore(placeholder, closest.element);\n } else {\n // Append to end\n container.appendChild(placeholder);\n }\n\n const currentIndex = Array.from(container.children).indexOf(placeholder);\n onSortMove?.(createEventData(dragItem, startIndex, currentIndex));\n };\n\n const onPointerUp = (e: PointerEvent): void => {\n if (!isDragging || !dragItem || !placeholder) return;\n\n isDragging = false;\n const draggedItem = dragItem;\n\n // Get final index\n const newIndex = Array.from(container.children).indexOf(placeholder);\n\n // Animate the item back to the placeholder position\n const placeholderRect = placeholder.getBoundingClientRect();\n const itemRect = draggedItem.getBoundingClientRect();\n\n if (animationDuration > 0) {\n const deltaX = placeholderRect.left - itemRect.left;\n const deltaY = placeholderRect.top - itemRect.top;\n\n draggedItem.style.transition = `transform ${animationDuration}ms ease`;\n draggedItem.style.transform = `translate(${deltaX}px, ${deltaY}px)`;\n\n let finalized = false;\n let timeoutId: number | null = null;\n const finalize = (): void => {\n if (finalized) return;\n finalized = true;\n if (timeoutId !== null) {\n window.clearTimeout(timeoutId);\n timeoutId = null;\n }\n resetDragItem();\n onSortEnd?.(createEventData(draggedItem, startIndex, newIndex));\n };\n timeoutId = window.setTimeout(() => {\n finalize();\n }, animationDuration + 50);\n\n draggedItem.addEventListener('transitionend', finalize, { once: true });\n } else {\n resetDragItem();\n onSortEnd?.(createEventData(draggedItem, startIndex, newIndex));\n }\n\n container.releasePointerCapture(e.pointerId);\n };\n\n const resetDragItem = (): void => {\n if (!dragItem || !placeholder) return;\n\n // Insert the real item where the placeholder is\n placeholder.parentNode?.insertBefore(dragItem, placeholder);\n placeholder.remove();\n placeholder = null;\n\n // Reset styles\n dragItem.classList.remove(sortingClass);\n dragItem.style.position = '';\n dragItem.style.width = '';\n dragItem.style.height = '';\n dragItem.style.left = '';\n dragItem.style.top = '';\n dragItem.style.zIndex = '';\n dragItem.style.pointerEvents = '';\n dragItem.style.margin = '';\n dragItem.style.transition = '';\n dragItem.style.transform = '';\n\n dragItem = null;\n };\n\n container.addEventListener('pointerdown', onPointerDown);\n container.addEventListener('pointermove', onPointerMove);\n container.addEventListener('pointerup', onPointerUp);\n container.addEventListener('pointercancel', onPointerUp);\n\n // Prevent default touch behavior on container\n container.style.touchAction = 'none';\n\n return {\n destroy: () => {\n container.removeEventListener('pointerdown', onPointerDown);\n container.removeEventListener('pointermove', onPointerMove);\n container.removeEventListener('pointerup', onPointerUp);\n container.removeEventListener('pointercancel', onPointerUp);\n container.style.touchAction = '';\n\n if (isDragging) {\n resetDragItem();\n }\n },\n disable: () => {\n enabled = false;\n },\n enable: () => {\n enabled = true;\n },\n get enabled() {\n return enabled;\n },\n };\n};\n"],"mappings":"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"}
|
package/dist/dnd.es.mjs
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { i as l, l as h } from "./core-CongXJuo.js";
|
|
2
|
+
var t = [], n = (e) => typeof e == "object" && e !== null && "_addDisposer" in e, y = (e) => (typeof e == "object" || typeof e == "function") && e !== null && typeof e.then == "function", v = (e) => {
|
|
3
|
+
const r = typeof e.constructor == "function" ? e.constructor.name : void 0;
|
|
4
|
+
return typeof e == "function" && (Symbol.toStringTag in e && e[Symbol.toStringTag] === "AsyncFunction" || r === "AsyncFunction");
|
|
5
|
+
}, c = () => {
|
|
6
|
+
for (let e = t.length - 1; e >= 0; e--) if (t[e].active) return t[e];
|
|
7
|
+
}, d = class {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.disposers = [], this._active = !0;
|
|
10
|
+
}
|
|
11
|
+
get active() {
|
|
12
|
+
return this._active;
|
|
13
|
+
}
|
|
14
|
+
_addDisposer(e) {
|
|
15
|
+
this._active && this.disposers.push(e);
|
|
16
|
+
}
|
|
17
|
+
run(e) {
|
|
18
|
+
if (!this._active) throw new Error("bQuery reactive: Cannot run in a stopped effectScope");
|
|
19
|
+
if (v(e)) throw new Error("bQuery reactive: effectScope.run() only supports synchronous callbacks");
|
|
20
|
+
t.push(this);
|
|
21
|
+
try {
|
|
22
|
+
const r = e();
|
|
23
|
+
if (y(r))
|
|
24
|
+
throw this.stop(), new Error("bQuery reactive: effectScope.run() only supports synchronous callbacks");
|
|
25
|
+
return r;
|
|
26
|
+
} finally {
|
|
27
|
+
t.pop();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
stop() {
|
|
31
|
+
if (this._active) {
|
|
32
|
+
this._active = !1;
|
|
33
|
+
for (let e = this.disposers.length - 1; e >= 0; e--) try {
|
|
34
|
+
this.disposers[e]();
|
|
35
|
+
} catch (r) {
|
|
36
|
+
console.error("bQuery reactive: Error in scope cleanup", r);
|
|
37
|
+
}
|
|
38
|
+
this.disposers.length = 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}, b = () => {
|
|
42
|
+
const e = new d(), r = c();
|
|
43
|
+
return n(r) && r._addDisposer(() => e.stop()), e;
|
|
44
|
+
}, _ = () => c(), g = (e) => {
|
|
45
|
+
const r = c();
|
|
46
|
+
if (!r || !r.active || !n(r)) throw new Error("bQuery reactive: onScopeDispose() must be called inside an active effectScope");
|
|
47
|
+
r._addDisposer(e);
|
|
48
|
+
}, D = (e) => {
|
|
49
|
+
let r, o = !1;
|
|
50
|
+
const a = c(), p = () => {
|
|
51
|
+
if (r) {
|
|
52
|
+
try {
|
|
53
|
+
r();
|
|
54
|
+
} catch (i) {
|
|
55
|
+
console.error("bQuery reactive: Error in effect cleanup", i);
|
|
56
|
+
}
|
|
57
|
+
r = void 0;
|
|
58
|
+
}
|
|
59
|
+
}, f = () => {
|
|
60
|
+
p(), l(s);
|
|
61
|
+
}, u = () => {
|
|
62
|
+
o || (o = !0, f());
|
|
63
|
+
};
|
|
64
|
+
n(a) && a._addDisposer(u);
|
|
65
|
+
const s = () => {
|
|
66
|
+
if (!o) {
|
|
67
|
+
p(), l(s);
|
|
68
|
+
try {
|
|
69
|
+
r = h(s, e);
|
|
70
|
+
} catch (i) {
|
|
71
|
+
console.error("bQuery reactive: Error in effect", i);
|
|
72
|
+
}
|
|
73
|
+
o && f();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return s(), u;
|
|
77
|
+
};
|
|
78
|
+
export {
|
|
79
|
+
n as a,
|
|
80
|
+
_ as i,
|
|
81
|
+
b as n,
|
|
82
|
+
g as o,
|
|
83
|
+
c as r,
|
|
84
|
+
D as t
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=effect-Cc51IH91.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect-Cc51IH91.js","names":[],"sources":["../src/reactive/scope.ts","../src/reactive/effect.ts"],"sourcesContent":["/**\n * Reactive effect scopes for grouped disposal.\n *\n * An `EffectScope` collects all effects, computed values, and watches created\n * inside its `run()` callback so they can be disposed together with a single\n * `stop()` call. Scopes nest — an inner scope is collected by its parent.\n *\n * @module bquery/reactive\n */\n\nimport type { CleanupFn } from './internals';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A scope that collects reactive resources for grouped disposal.\n *\n * @example\n * ```ts\n * import { effectScope, signal, effect, computed } from '@bquery/bquery/reactive';\n *\n * const scope = effectScope();\n *\n * scope.run(() => {\n * const count = signal(0);\n * effect(() => console.log(count.value));\n * const doubled = computed(() => count.value * 2);\n * });\n *\n * scope.stop(); // All effects and computed values disposed\n * ```\n */\nexport interface EffectScope {\n /** Whether the scope has not yet been stopped. */\n readonly active: boolean;\n\n /**\n * Executes `fn` inside this scope, collecting any reactive resources\n * (effects, computed values, watches, nested scopes) created during the call.\n *\n * `run()` is synchronous-only. Do not pass an async function or a function\n * that returns a Promise — resources created after an `await` cannot be\n * collected reliably.\n *\n * @template T - Return type of the provided function\n * @param fn - Function to run inside the scope\n * @returns The return value of `fn`\n * @throws {Error} If the scope has already been stopped\n */\n run<T>(fn: () => T): T;\n\n /**\n * Disposes all collected resources and marks the scope as inactive.\n * Calling `stop()` on an already-stopped scope is a safe no-op.\n */\n stop(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal scope stack\n// ---------------------------------------------------------------------------\n\n/** @internal */\ninterface ScopeInternal extends EffectScope {\n /** @internal – Register a cleanup callback to run when the scope stops. */\n _addDisposer(fn: CleanupFn): void;\n}\n\nconst scopeStack: ScopeInternal[] = [];\n\n/** @internal */\nexport const hasScopeDisposer = (\n scope: EffectScope | undefined\n): scope is EffectScope & { _addDisposer(fn: CleanupFn): void } =>\n typeof scope === 'object' && scope !== null && '_addDisposer' in scope;\n\nconst isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>\n (typeof value === 'object' || typeof value === 'function') &&\n value !== null &&\n typeof (value as { then?: unknown }).then === 'function';\n\n/**\n * Best-effort detection for native async functions so `run()` can reject them\n * before invocation. Transpiled async functions may not preserve this shape, so\n * promise-like return values are still checked after execution as a fallback.\n * @internal\n */\nconst isAsyncFunction = (value: unknown): value is (...args: never[]) => Promise<unknown> => {\n const constructorName =\n typeof (value as { constructor?: unknown }).constructor === 'function'\n ? (value as { constructor: { name?: unknown } }).constructor.name\n : undefined;\n\n return (\n typeof value === 'function' &&\n ((Symbol.toStringTag in value &&\n (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag] === 'AsyncFunction') ||\n constructorName === 'AsyncFunction')\n );\n};\n\n/**\n * Returns the currently active scope, or `undefined` if none.\n * @internal\n */\nexport const getActiveScope = (): EffectScope | undefined => {\n for (let i = scopeStack.length - 1; i >= 0; i--) {\n if (scopeStack[i].active) {\n return scopeStack[i];\n }\n }\n\n return undefined;\n};\n\n// ---------------------------------------------------------------------------\n// EffectScope implementation\n// ---------------------------------------------------------------------------\n\nclass EffectScopeImpl implements ScopeInternal {\n private disposers: CleanupFn[] = [];\n private _active = true;\n\n get active(): boolean {\n return this._active;\n }\n\n /** @internal */\n _addDisposer(fn: CleanupFn): void {\n if (this._active) {\n this.disposers.push(fn);\n }\n }\n\n run<T>(fn: () => T): T {\n if (!this._active) {\n throw new Error('bQuery reactive: Cannot run in a stopped effectScope');\n }\n if (isAsyncFunction(fn)) {\n throw new Error('bQuery reactive: effectScope.run() only supports synchronous callbacks');\n }\n\n scopeStack.push(this);\n try {\n const result = fn();\n if (isPromiseLike(result)) {\n this.stop();\n throw new Error('bQuery reactive: effectScope.run() only supports synchronous callbacks');\n }\n return result;\n } finally {\n scopeStack.pop();\n }\n }\n\n stop(): void {\n if (!this._active) return;\n this._active = false;\n\n // Dispose in reverse order (LIFO) to mirror creation order\n for (let i = this.disposers.length - 1; i >= 0; i--) {\n try {\n this.disposers[i]();\n } catch (error) {\n console.error('bQuery reactive: Error in scope cleanup', error);\n }\n }\n this.disposers.length = 0;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a new effect scope for grouped disposal of reactive resources.\n *\n * All `effect()`, `computed()`, `watch()`, and nested `effectScope()` calls\n * made inside `scope.run(fn)` are automatically collected. Calling\n * `scope.stop()` disposes them all at once.\n *\n * `run()` is synchronous-only. Create the scope outside async flows when\n * needed, but keep the callback itself synchronous so cleanup registration\n * stays deterministic.\n *\n * @returns A new {@link EffectScope}\n *\n * @example\n * ```ts\n * import { effectScope, signal, effect, onScopeDispose } from '@bquery/bquery/reactive';\n *\n * const scope = effectScope();\n *\n * scope.run(() => {\n * const count = signal(0);\n *\n * effect(() => console.log(count.value));\n *\n * onScopeDispose(() => {\n * console.log('Custom cleanup');\n * });\n * });\n *\n * scope.stop(); // logs \"Custom cleanup\", all effects stopped\n * ```\n */\nexport const effectScope = (): EffectScope => {\n const scope = new EffectScopeImpl();\n\n // If created inside another scope, auto-collect as a nested scope\n const parent = getActiveScope();\n if (hasScopeDisposer(parent)) {\n parent._addDisposer(() => scope.stop());\n }\n\n return scope;\n};\n\n/**\n * Returns the currently active {@link EffectScope}, or `undefined` if\n * code is not running inside any scope's `run()` callback.\n *\n * @returns The active scope, or `undefined`\n *\n * @example\n * ```ts\n * import { effectScope, getCurrentScope } from '@bquery/bquery/reactive';\n *\n * const scope = effectScope();\n * scope.run(() => {\n * console.log(getCurrentScope() !== undefined); // true\n * });\n *\n * console.log(getCurrentScope()); // undefined\n * ```\n */\nexport const getCurrentScope = (): EffectScope | undefined => getActiveScope();\n\n/**\n * Registers a cleanup callback on the currently active scope.\n *\n * The callback runs when the scope is stopped. This is useful for\n * registering arbitrary cleanup (e.g. event listeners, timers)\n * alongside effects and computed values.\n *\n * @param fn - Cleanup function to run when the scope stops\n * @throws {Error} If called outside an active scope\n *\n * @example\n * ```ts\n * import { effectScope, onScopeDispose } from '@bquery/bquery/reactive';\n *\n * const scope = effectScope();\n *\n * scope.run(() => {\n * const controller = new AbortController();\n * fetch('/api/data', { signal: controller.signal });\n *\n * onScopeDispose(() => controller.abort());\n * });\n *\n * scope.stop(); // abort() is called\n * ```\n */\nexport const onScopeDispose = (fn: CleanupFn): void => {\n const scope = getActiveScope();\n if (!scope || !scope.active || !hasScopeDisposer(scope)) {\n throw new Error(\n 'bQuery reactive: onScopeDispose() must be called inside an active effectScope'\n );\n }\n scope._addDisposer(fn);\n};\n","/**\n * Reactive effects.\n */\n\nimport { CleanupFn, Observer, track, clearDependencies } from './internals';\nimport { getActiveScope, hasScopeDisposer } from './scope';\n\n/**\n * Creates a side effect that automatically re-runs when dependencies change.\n *\n * The effect runs immediately upon creation and then re-runs whenever\n * any signal or computed value read inside it changes.\n *\n * If created inside an {@link effectScope}, the effect is automatically\n * collected and will be disposed when the scope stops.\n *\n * @param fn - The effect function to run\n * @returns A cleanup function to stop the effect\n */\nexport const effect = (fn: () => void | CleanupFn): CleanupFn => {\n let cleanupFn: CleanupFn | void;\n let isDisposed = false;\n const scope = getActiveScope();\n\n const runCleanup = (): void => {\n if (cleanupFn) {\n try {\n cleanupFn();\n } catch (error) {\n console.error('bQuery reactive: Error in effect cleanup', error);\n }\n cleanupFn = undefined;\n }\n };\n\n const clearEffectState = (): void => {\n runCleanup();\n // Clean up all dependencies when effect is disposed\n clearDependencies(observer);\n };\n\n const dispose: CleanupFn = () => {\n if (isDisposed) {\n return;\n }\n\n isDisposed = true;\n clearEffectState();\n };\n\n if (hasScopeDisposer(scope)) {\n scope._addDisposer(dispose);\n }\n\n const observer: Observer = () => {\n if (isDisposed) return;\n\n runCleanup();\n\n // Clear old dependencies before running to avoid stale subscriptions\n clearDependencies(observer);\n\n try {\n cleanupFn = track(observer, fn);\n } catch (error) {\n console.error('bQuery reactive: Error in effect', error);\n }\n\n if (isDisposed) {\n clearEffectState();\n }\n };\n\n observer();\n\n return dispose;\n};\n"],"mappings":";AAsEA,IAAM,IAA8B,CAAA,GAGvB,IAAA,CACX,MAEA,OAAO,KAAU,YAAY,MAAU,QAAQ,kBAAkB,GAE7D,IAAA,CAAiB,OACpB,OAAO,KAAU,YAAY,OAAO,KAAU,eAC/C,MAAU,QACV,OAAQ,EAA6B,QAAS,YAQ1C,IAAA,CAAmB,MAAoE;AAC3F,QAAM,IACJ,OAAQ,EAAoC,eAAgB,aACvD,EAA8C,YAAY,OAC3D;AAEN,SACE,OAAO,KAAU,eACf,OAAO,eAAe,KACrB,EAA6C,OAAO,WAAA,MAAiB,mBACtE,MAAoB;GAQb,IAAA,MAAgD;AAC3D,WAAS,IAAI,EAAW,SAAS,GAAG,KAAK,GAAG,IAC1C,KAAI,EAAW,CAAA,EAAG,OAChB,QAAO,EAAW,CAAA;GAWlB,IAAN,MAA+C;AAAA;qBACZ,CAAA,kBACf;AAAA;EAElB,IAAI,SAAkB;AACpB,WAAO,KAAK;AAAA;EAId,aAAa,GAAqB;AAChC,IAAI,KAAK,WACP,KAAK,UAAU,KAAK,CAAA;AAAA;EAIxB,IAAO,GAAgB;AACrB,QAAI,CAAC,KAAK,QACR,OAAM,IAAI,MAAM,sDAAA;AAElB,QAAI,EAAgB,CAAA,EAClB,OAAM,IAAI,MAAM,wEAAA;AAGlB,IAAA,EAAW,KAAK,IAAA;AAChB,QAAI;AACF,YAAM,IAAS,EAAA;AACf,UAAI,EAAc,CAAA;AAChB,mBAAK,KAAA,GACC,IAAI,MAAM,wEAAA;AAElB,aAAO;AAAA;AAEP,MAAA,EAAW,IAAA;AAAA;;EAIf,OAAa;AACX,QAAK,KAAK,SACV;AAAA,WAAK,UAAU;AAGf,eAAS,IAAI,KAAK,UAAU,SAAS,GAAG,KAAK,GAAG,IAC9C,KAAI;AACF,aAAK,UAAU,CAAA,EAAA;AAAA,eACR,GAAO;AACd,gBAAQ,MAAM,2CAA2C,CAAA;AAAA;AAG7D,WAAK,UAAU,SAAS;AAAA;AAAA;GAwCf,IAAA,MAAiC;AAC5C,QAAM,IAAQ,IAAI,EAAA,GAGZ,IAAS,EAAA;AACf,SAAI,EAAiB,CAAA,KACnB,EAAO,aAAA,MAAmB,EAAM,KAAA,CAAM,GAGjC;GAqBI,IAAA,MAAiD,EAAA,GA4BjD,IAAA,CAAkB,MAAwB;AACrD,QAAM,IAAQ,EAAA;AACd,MAAI,CAAC,KAAS,CAAC,EAAM,UAAU,CAAC,EAAiB,CAAA,EAC/C,OAAM,IAAI,MACR,+EAAA;AAGJ,EAAA,EAAM,aAAa,CAAA;GC/PR,IAAA,CAAU,MAA0C;AAC/D,MAAI,GACA,IAAa;AACjB,QAAM,IAAQ,EAAA,GAER,IAAA,MAAyB;AAC7B,QAAI,GAAW;AACb,UAAI;AACF,QAAA,EAAA;AAAA,eACO,GAAO;AACd,gBAAQ,MAAM,4CAA4C,CAAA;AAAA;AAE5D,MAAA,IAAY;AAAA;KAIV,IAAA,MAA+B;AACnC,IAAA,EAAA,GAEA,EAAkB,CAAA;AAAA,KAGd,IAAA,MAA2B;AAC/B,IAAI,MAIJ,IAAa,IACb,EAAA;AAAA;AAGF,EAAI,EAAiB,CAAA,KACnB,EAAM,aAAa,CAAA;AAGrB,QAAM,IAAA,MAA2B;AAC/B,QAAI,CAAA,GAEJ;AAAA,MAAA,EAAA,GAGA,EAAkB,CAAA;AAElB,UAAI;AACF,QAAA,IAAY,EAAM,GAAU,CAAA;AAAA,eACrB,GAAO;AACd,gBAAQ,MAAM,oCAAoC,CAAA;AAAA;AAGpD,MAAI,KACF,EAAA;AAAA;AAAA;AAIJ,SAAA,EAAA,GAEO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-
|
|
1
|
+
{"version":3,"file":"env-PvwYHnJq.js","names":[],"sources":["../src/core/env.ts"],"sourcesContent":["/**\n * Shared environment detection helpers.\n *\n * @internal\n */\n\ntype BQueryEnvGlobal = typeof globalThis & {\n __BQUERY_DEV__?: boolean;\n process?: {\n env?: {\n NODE_ENV?: string;\n };\n release?: {\n name?: string;\n };\n versions?: {\n node?: string;\n };\n };\n};\n\n/**\n * Returns whether development-only diagnostics should be enabled.\n *\n * Priority:\n * 1. Explicit global override via `globalThis.__BQUERY_DEV__`\n * 2. `process.env.NODE_ENV`\n * 3. Actual Node-like runtimes without `NODE_ENV` default to development\n * 4. Production-safe fallback (`false`)\n *\n * @internal\n */\nexport const detectDevEnvironment = (): boolean => {\n try {\n const globalObject = globalThis as BQueryEnvGlobal;\n\n if (typeof globalObject.__BQUERY_DEV__ === 'boolean') {\n return globalObject.__BQUERY_DEV__;\n }\n\n const nodeEnv = globalObject.process?.env?.NODE_ENV;\n if (typeof nodeEnv === 'string') {\n return nodeEnv !== 'production';\n }\n\n const nodeVersion = globalObject.process?.versions?.node;\n if (typeof nodeVersion === 'string' && nodeVersion.length > 0) {\n return true;\n }\n\n const releaseName = globalObject.process?.release?.name;\n if (releaseName === 'node' || releaseName === 'io.js') {\n return true;\n }\n\n return false;\n } catch {\n return false;\n }\n};\n"],"mappings":"AAgCA,IAAa,IAAA,MAAsC;AACjD,MAAI;AACF,UAAM,IAAe;AAErB,QAAI,OAAO,EAAa,kBAAmB,UACzC,QAAO,EAAa;AAGtB,UAAM,IAAU,EAAa,SAAS,KAAK;AAC3C,QAAI,OAAO,KAAY,SACrB,QAAO,MAAY;AAGrB,UAAM,IAAc,EAAa,SAAS,UAAU;AACpD,QAAI,OAAO,KAAgB,YAAY,EAAY,SAAS,EAC1D,QAAO;AAGT,UAAM,IAAc,EAAa,SAAS,SAAS;AACnD,WAAI,MAAgB,UAAU,MAAgB;AAAA,UAKxC;AACN,WAAO;AAAA"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { i as k } from "./object-BCk-1c8T.js";
|
|
2
2
|
import { t as P } from "./function-Cybd57JV.js";
|
|
3
3
|
import { l as M } from "./type-guards-BMX2c0LP.js";
|
|
4
|
-
import { n as f,
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { n as f, t as D } from "./core-CongXJuo.js";
|
|
5
|
+
import { t as $ } from "./readonly-C0ZwS1Tf.js";
|
|
6
|
+
import { t as x } from "./effect-Cc51IH91.js";
|
|
7
|
+
import { n as R, r as m } from "./untrack-bjWDNdyE.js";
|
|
7
8
|
var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
8
9
|
const t = r(e), s = M(t) ? await t : t;
|
|
9
10
|
return L(s) ? void 0 : s;
|
|
@@ -31,7 +32,7 @@ var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
|
31
32
|
return r.error.value = s, s;
|
|
32
33
|
}
|
|
33
34
|
return r.error.value = "", "";
|
|
34
|
-
},
|
|
35
|
+
}, Y = (r) => {
|
|
35
36
|
const e = Object.entries(r.fields), t = {}, s = {};
|
|
36
37
|
for (const [n, a] of e) {
|
|
37
38
|
const i = A(a);
|
|
@@ -53,13 +54,13 @@ var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
|
53
54
|
await F(u, i.validators) && (n = !0);
|
|
54
55
|
}
|
|
55
56
|
if (r.crossValidators && r.crossValidators.length > 0) {
|
|
56
|
-
const a =
|
|
57
|
+
const a = p();
|
|
57
58
|
for (const i of r.crossValidators) {
|
|
58
59
|
const u = await i(a);
|
|
59
60
|
if (u) {
|
|
60
61
|
for (const [V, v] of Object.entries(u)) if (v) {
|
|
61
|
-
const
|
|
62
|
-
|
|
62
|
+
const g = t[V];
|
|
63
|
+
g && (g.error.value === "" && (g.error.value = v), n = !0);
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
}
|
|
@@ -70,14 +71,14 @@ var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
|
70
71
|
o.value = !0;
|
|
71
72
|
try {
|
|
72
73
|
if (!await h()) return;
|
|
73
|
-
r.onSubmit && await r.onSubmit(
|
|
74
|
+
r.onSubmit && await r.onSubmit(p());
|
|
74
75
|
} finally {
|
|
75
76
|
o.value = !1;
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
}, y = () => {
|
|
79
80
|
for (const n of Object.keys(t)) t[n].reset();
|
|
80
|
-
},
|
|
81
|
+
}, p = () => {
|
|
81
82
|
const n = {};
|
|
82
83
|
for (const a of Object.keys(t)) n[a] = t[a].value.value;
|
|
83
84
|
return n;
|
|
@@ -92,7 +93,7 @@ var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
|
92
93
|
validateField: b,
|
|
93
94
|
validate: h,
|
|
94
95
|
reset: y,
|
|
95
|
-
getValues:
|
|
96
|
+
getValues: p,
|
|
96
97
|
setValues: (n) => {
|
|
97
98
|
for (const [a, i] of Object.entries(n)) {
|
|
98
99
|
if (k(a) || !Object.prototype.hasOwnProperty.call(t, a)) continue;
|
|
@@ -111,10 +112,10 @@ var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
|
111
112
|
}, T = (r) => r === !0 || r === void 0, q = async (r, e) => {
|
|
112
113
|
const t = r(e), s = M(t) ? await t : t;
|
|
113
114
|
return T(s) ? void 0 : s;
|
|
114
|
-
},
|
|
115
|
+
}, Z = (r, e = {}) => {
|
|
115
116
|
let t;
|
|
116
|
-
U(r) ? t = r : t = f(
|
|
117
|
-
const s = t.peek(), o = f(e.initialError ?? ""), l = f(!1), c = f(!1), b = m(() => !Object.is(t.value, s)), h = m(() => !b.value), j = m(() => o.value === ""), y = e.validateOn ?? "manual",
|
|
117
|
+
U(r) ? t = r : t = f($(r) || z(r) ? r.peek() : r);
|
|
118
|
+
const s = t.peek(), o = f(e.initialError ?? ""), l = f(!1), c = f(!1), b = m(() => !Object.is(t.value, s)), h = m(() => !b.value), j = m(() => o.value === ""), y = e.validateOn ?? "manual", p = Math.max(0, e.debounceMs ?? 0);
|
|
118
119
|
let d = 0, w = !1, n = !1, a = !1, i;
|
|
119
120
|
const u = (O) => {
|
|
120
121
|
console.error("bQuery forms: Error in scheduled field validation", O);
|
|
@@ -137,16 +138,16 @@ var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
|
137
138
|
}
|
|
138
139
|
}, v = P(() => {
|
|
139
140
|
V().catch(u);
|
|
140
|
-
},
|
|
141
|
+
}, p), g = () => {
|
|
141
142
|
if (!a) {
|
|
142
|
-
if (
|
|
143
|
+
if (p > 0) {
|
|
143
144
|
v();
|
|
144
145
|
return;
|
|
145
146
|
}
|
|
146
147
|
V().catch(u);
|
|
147
148
|
}
|
|
148
149
|
};
|
|
149
|
-
return (y === "change" || y === "both") && (i =
|
|
150
|
+
return (y === "change" || y === "both") && (i = x(() => {
|
|
150
151
|
if (t.value, !w) {
|
|
151
152
|
w = !0;
|
|
152
153
|
return;
|
|
@@ -155,7 +156,7 @@ var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
|
155
156
|
n = !1;
|
|
156
157
|
return;
|
|
157
158
|
}
|
|
158
|
-
|
|
159
|
+
g();
|
|
159
160
|
})), {
|
|
160
161
|
value: t,
|
|
161
162
|
error: o,
|
|
@@ -165,7 +166,7 @@ var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
|
165
166
|
isValid: j,
|
|
166
167
|
isValidating: c,
|
|
167
168
|
touch: () => {
|
|
168
|
-
l.value = !0, (y === "blur" || y === "both") &&
|
|
169
|
+
l.value = !0, (y === "blur" || y === "both") && g();
|
|
169
170
|
},
|
|
170
171
|
reset: () => {
|
|
171
172
|
d += 1, v.cancel(), Object.is(t.peek(), s) || (n = !0), t.value = s, o.value = e.initialError ?? "", l.value = !1, c.value = !1;
|
|
@@ -175,25 +176,25 @@ var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
|
175
176
|
a || (a = !0, d += 1, v.cancel(), i?.(), i = void 0, b.dispose(), h.dispose(), j.dispose(), c.value = !1);
|
|
176
177
|
}
|
|
177
178
|
};
|
|
178
|
-
}, U = (r) => r instanceof
|
|
179
|
+
}, U = (r) => r instanceof D, z = (r) => r instanceof R, _ = (r = "This field is required") => (e) => e == null || typeof e == "string" && e.trim() === "" || Array.isArray(e) && e.length === 0 ? r : !0, rr = (r, e) => {
|
|
179
180
|
const t = e ?? `Must be at least ${r} characters`;
|
|
180
181
|
return (s) => (typeof s == "string" ? s : String(s ?? "")).length >= r ? !0 : t;
|
|
181
|
-
},
|
|
182
|
+
}, tr = (r, e) => {
|
|
182
183
|
const t = e ?? `Must be at most ${r} characters`;
|
|
183
184
|
return (s) => (typeof s == "string" ? s : String(s ?? "")).length <= r ? !0 : t;
|
|
184
|
-
},
|
|
185
|
+
}, er = (r, e = "Invalid format") => {
|
|
185
186
|
const t = r.global || r.sticky ? new RegExp(r.source, r.flags.replace(/[gy]/g, "")) : r;
|
|
186
187
|
return (s) => {
|
|
187
188
|
const o = typeof s == "string" ? s : String(s ?? "");
|
|
188
189
|
return t.lastIndex = 0, t.test(o) ? !0 : e;
|
|
189
190
|
};
|
|
190
|
-
},
|
|
191
|
+
}, sr = (r = "Invalid email address") => {
|
|
191
192
|
const e = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
192
193
|
return (t) => {
|
|
193
194
|
const s = typeof t == "string" ? t : String(t ?? "");
|
|
194
195
|
return s === "" || e.test(s) ? !0 : r;
|
|
195
196
|
};
|
|
196
|
-
},
|
|
197
|
+
}, nr = (r = "Invalid URL") => (e) => {
|
|
197
198
|
const t = typeof e == "string" ? e : String(e ?? "");
|
|
198
199
|
if (t === "") return !0;
|
|
199
200
|
try {
|
|
@@ -201,27 +202,27 @@ var L = (r) => r === !0 || r === void 0, N = async (r, e) => {
|
|
|
201
202
|
} catch {
|
|
202
203
|
return r;
|
|
203
204
|
}
|
|
204
|
-
},
|
|
205
|
+
}, ar = (r, e) => {
|
|
205
206
|
const t = e ?? `Must be at least ${r}`;
|
|
206
207
|
return (s) => s == null || typeof s == "string" && s.trim() === "" || (typeof s == "number" ? s : Number(s)) >= r ? !0 : t;
|
|
207
|
-
},
|
|
208
|
+
}, or = (r, e) => {
|
|
208
209
|
const t = e ?? `Must be at most ${r}`;
|
|
209
210
|
return (s) => s == null || typeof s == "string" && s.trim() === "" || (typeof s == "number" ? s : Number(s)) <= r ? !0 : t;
|
|
210
|
-
},
|
|
211
|
+
}, ir = (r, e) => (t) => r(t) ? !0 : e, ur = (r, e) => async (t) => await r(t) ? !0 : e, lr = (r, e = "Fields do not match") => (t) => Object.is(t, r.value) ? !0 : e;
|
|
211
212
|
export {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
213
|
+
or as a,
|
|
214
|
+
rr as c,
|
|
215
|
+
nr as d,
|
|
216
|
+
Z as f,
|
|
217
|
+
lr as i,
|
|
218
|
+
er as l,
|
|
219
|
+
ur as n,
|
|
220
|
+
tr as o,
|
|
221
|
+
Y as p,
|
|
222
|
+
sr as r,
|
|
223
|
+
ar as s,
|
|
224
|
+
ir as t,
|
|
225
|
+
_ as u
|
|
225
226
|
};
|
|
226
227
|
|
|
227
|
-
//# sourceMappingURL=forms-
|
|
228
|
+
//# sourceMappingURL=forms-Dx1Scvh0.js.map
|