@pyreon/dnd 0.11.5 → 0.11.6

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 CHANGED
@@ -84,6 +84,7 @@ const { containerRef, itemRef, activeId, overId, overEdge } = useSortable({
84
84
  ```
85
85
 
86
86
  Features:
87
+
87
88
  - Auto-scroll when dragging near container edges
88
89
  - `overEdge` signal shows "top"/"bottom" (vertical) or "left"/"right" (horizontal)
89
90
  - Keyboard reordering with Alt+Arrow keys
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/use-drag-monitor.ts","../src/use-draggable.ts","../src/use-droppable.ts","../src/use-file-drop.ts","../src/use-sortable.ts"],"sourcesContent":["import { monitorForElements } from \"@atlaskit/pragmatic-drag-and-drop/element/adapter\"\nimport { onCleanup, signal } from \"@pyreon/reactivity\"\nimport type { DragData } from \"./types\"\n\nexport interface UseDragMonitorOptions {\n /** Called on any drag start in the page. */\n onDragStart?: (data: DragData) => void\n /** Called on any drop in the page. */\n onDrop?: (sourceData: DragData, targetData: DragData) => void\n /** Filter which drags to monitor. */\n canMonitor?: (data: DragData) => boolean\n}\n\nexport interface UseDragMonitorResult {\n /** Whether any element is currently being dragged. */\n isDragging: () => boolean\n /** Data of the currently dragging element (null if not dragging). */\n dragData: () => DragData | null\n}\n\n/**\n * Monitor all drag operations on the page.\n * Useful for global drag indicators, analytics, or coordination between\n * multiple drag-and-drop areas.\n *\n * @example\n * ```tsx\n * const { isDragging, dragData } = useDragMonitor({\n * canMonitor: (data) => data.type === \"card\",\n * onDrop: (source, target) => logDrop(source, target),\n * })\n *\n * <Show when={isDragging()}>\n * <div class=\"global-drag-overlay\">\n * Dragging: {() => dragData()?.name}\n * </div>\n * </Show>\n * ```\n */\nexport function useDragMonitor(options?: UseDragMonitorOptions): UseDragMonitorResult {\n const isDragging = signal(false)\n const dragData = signal<DragData | null>(null)\n\n const canMonitorFn = options?.canMonitor\n ? ({ source }: { source: { data: Record<string, unknown> } }) =>\n options.canMonitor?.(source.data as DragData) ?? true\n : null\n\n const cleanup = monitorForElements({\n ...(canMonitorFn ? { canMonitor: canMonitorFn } : {}),\n onDragStart: ({ source }) => {\n isDragging.set(true)\n dragData.set(source.data as DragData)\n options?.onDragStart?.(source.data as DragData)\n },\n onDrop: ({ source, location }) => {\n isDragging.set(false)\n dragData.set(null)\n const targetData = location.current.dropTargets[0]?.data ?? {}\n options?.onDrop?.(source.data as DragData, targetData as DragData)\n },\n })\n\n onCleanup(cleanup)\n\n return { isDragging, dragData }\n}\n","import { draggable } from \"@atlaskit/pragmatic-drag-and-drop/element/adapter\"\nimport { onCleanup, signal } from \"@pyreon/reactivity\"\nimport type { DragData, UseDraggableOptions, UseDraggableResult } from \"./types\"\n\n/**\n * Make an element draggable with signal-driven state.\n *\n * @example\n * ```tsx\n * let cardEl: HTMLElement | null = null\n *\n * const { isDragging } = useDraggable({\n * element: () => cardEl,\n * data: { id: card.id, type: \"card\" },\n * })\n *\n * <div ref={(el) => cardEl = el} class={isDragging() ? \"opacity-50\" : \"\"}>\n * {card.title}\n * </div>\n * ```\n */\nexport function useDraggable<T extends DragData = DragData>(\n options: UseDraggableOptions<T>,\n): UseDraggableResult {\n const isDragging = signal(false)\n let cleanup: (() => void) | undefined\n\n function setup() {\n if (cleanup) cleanup()\n\n const el = options.element()\n if (!el) return\n\n const resolveData = () =>\n typeof options.data === \"function\" ? (options.data as () => T)() : options.data\n\n const handle = options.handle?.()\n cleanup = draggable({\n element: el,\n ...(handle ? { dragHandle: handle } : {}),\n getInitialData: resolveData,\n canDrag: () => {\n const disabled = options.disabled\n if (typeof disabled === \"function\") return !disabled()\n return !disabled\n },\n onDragStart: () => {\n isDragging.set(true)\n options.onDragStart?.()\n },\n onDrop: () => {\n isDragging.set(false)\n options.onDragEnd?.()\n },\n })\n }\n\n // Defer setup to next microtask so refs are populated\n queueMicrotask(setup)\n\n onCleanup(() => {\n if (cleanup) cleanup()\n })\n\n return { isDragging }\n}\n","import { dropTargetForElements } from \"@atlaskit/pragmatic-drag-and-drop/element/adapter\"\nimport { onCleanup, signal } from \"@pyreon/reactivity\"\nimport type { DragData, UseDroppableOptions, UseDroppableResult } from \"./types\"\n\n/**\n * Make an element a drop target with signal-driven state.\n *\n * @example\n * ```tsx\n * let zoneEl: HTMLElement | null = null\n *\n * const { isOver } = useDroppable({\n * element: () => zoneEl,\n * onDrop: (data) => handleDrop(data),\n * canDrop: (data) => data.type === \"card\",\n * })\n *\n * <div ref={(el) => zoneEl = el} class={isOver() ? \"bg-blue-50\" : \"\"}>\n * Drop here\n * </div>\n * ```\n */\nexport function useDroppable<T extends DragData = DragData>(\n options: UseDroppableOptions<T>,\n): UseDroppableResult {\n const isOver = signal(false)\n let cleanup: (() => void) | undefined\n\n function setup() {\n if (cleanup) cleanup()\n\n const el = options.element()\n if (!el) return\n\n cleanup = dropTargetForElements({\n element: el,\n getData: () => {\n if (!options.data) return {}\n return typeof options.data === \"function\" ? (options.data as () => T)() : options.data\n },\n canDrop: ({ source }) => {\n if (!options.canDrop) return true\n return options.canDrop(source.data as DragData)\n },\n onDragEnter: ({ source }) => {\n isOver.set(true)\n options.onDragEnter?.(source.data as DragData)\n },\n onDragLeave: () => {\n isOver.set(false)\n options.onDragLeave?.()\n },\n onDrop: ({ source }) => {\n isOver.set(false)\n options.onDrop?.(source.data as DragData)\n },\n })\n }\n\n queueMicrotask(setup)\n\n onCleanup(() => {\n if (cleanup) cleanup()\n })\n\n return { isOver }\n}\n","import {\n dropTargetForExternal,\n monitorForExternal,\n} from \"@atlaskit/pragmatic-drag-and-drop/external/adapter\"\nimport { containsFiles, getFiles } from \"@atlaskit/pragmatic-drag-and-drop/external/file\"\nimport { onCleanup, signal } from \"@pyreon/reactivity\"\n\nexport interface UseFileDropOptions {\n /** Element getter for the drop zone. */\n element: () => HTMLElement | null\n /** Called when files are dropped. */\n onDrop: (files: File[]) => void\n /** Filter accepted file types (e.g. [\"image/*\", \".pdf\"]). */\n accept?: string[]\n /** Maximum number of files. */\n maxFiles?: number\n /** Whether drop is disabled. */\n disabled?: boolean | (() => boolean)\n}\n\nexport interface UseFileDropResult {\n /** Whether files are being dragged over the drop zone. */\n isOver: () => boolean\n /** Whether files are being dragged anywhere on the page. */\n isDraggingFiles: () => boolean\n}\n\n/**\n * File drop zone with signal-driven state.\n * Uses the native file drag events via pragmatic-drag-and-drop.\n *\n * @example\n * ```tsx\n * let dropZone: HTMLElement | null = null\n *\n * const { isOver, isDraggingFiles } = useFileDrop({\n * element: () => dropZone,\n * accept: [\"image/*\", \".pdf\"],\n * maxFiles: 5,\n * onDrop: (files) => upload(files),\n * })\n *\n * <div\n * ref={(el) => dropZone = el}\n * class={isOver() ? \"drop-active\" : isDraggingFiles() ? \"drop-ready\" : \"\"}\n * >\n * Drop files here\n * </div>\n * ```\n */\nexport function useFileDrop(options: UseFileDropOptions): UseFileDropResult {\n const isOver = signal(false)\n const isDraggingFiles = signal(false)\n let cleanup: (() => void) | undefined\n\n function matchesAccept(file: File, accept: string[]): boolean {\n return accept.some((pattern) => {\n if (pattern.startsWith(\".\")) {\n return file.name.toLowerCase().endsWith(pattern.toLowerCase())\n }\n if (pattern.endsWith(\"/*\")) {\n return file.type.startsWith(pattern.slice(0, -1))\n }\n return file.type === pattern\n })\n }\n\n function setup() {\n if (cleanup) cleanup()\n\n const el = options.element()\n if (!el) return\n\n const cleanups: (() => void)[] = []\n\n // Monitor for file drags anywhere on the page\n cleanups.push(\n monitorForExternal({\n canMonitor: ({ source }) => containsFiles({ source }),\n onDragStart: () => isDraggingFiles.set(true),\n onDrop: () => isDraggingFiles.set(false),\n }),\n )\n\n // Drop target on the specific element\n cleanups.push(\n dropTargetForExternal({\n element: el,\n canDrop: ({ source }) => {\n const disabled = options.disabled\n if (typeof disabled === \"function\" ? disabled() : disabled) return false\n return containsFiles({ source })\n },\n onDragEnter: () => isOver.set(true),\n onDragLeave: () => isOver.set(false),\n onDrop: ({ source }) => {\n isOver.set(false)\n isDraggingFiles.set(false)\n\n let files = getFiles({ source })\n\n // Filter by accept\n if (options.accept && options.accept.length > 0) {\n files = files.filter((f) => matchesAccept(f, options.accept as string[]))\n }\n\n // Limit count\n if (options.maxFiles && files.length > options.maxFiles) {\n files = files.slice(0, options.maxFiles)\n }\n\n if (files.length > 0) {\n options.onDrop(files)\n }\n },\n }),\n )\n\n cleanup = () => {\n for (const fn of cleanups) fn()\n }\n }\n\n queueMicrotask(setup)\n\n onCleanup(() => {\n if (cleanup) cleanup()\n })\n\n return { isOver, isDraggingFiles }\n}\n","import { combine } from \"@atlaskit/pragmatic-drag-and-drop/combine\"\nimport { draggable, dropTargetForElements } from \"@atlaskit/pragmatic-drag-and-drop/element/adapter\"\nimport { autoScrollForElements } from \"@atlaskit/pragmatic-drag-and-drop-auto-scroll/element\"\nimport {\n attachClosestEdge,\n type Edge,\n extractClosestEdge,\n} from \"@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge\"\nimport { onCleanup, signal } from \"@pyreon/reactivity\"\nimport type { DropEdge, UseSortableOptions, UseSortableResult } from \"./types\"\n\nconst SORT_KEY = \"__pyreon_sortable_key\"\nconst SORT_ID = \"__pyreon_sortable_id\"\n\nlet _sortableCounter = 0\n\n/**\n * Sortable list with signal-driven state, auto-scroll, and edge detection.\n *\n * Features:\n * - Keyed drag items matching `<For by={...}>` pattern\n * - Auto-scroll when dragging near container edges\n * - Closest-edge detection (drop above/below or left/right)\n * - Axis constraint (vertical/horizontal)\n * - Keyboard reordering (Alt+Arrow keys)\n *\n * @example\n * ```tsx\n * const items = signal([\n * { id: \"1\", name: \"Alice\" },\n * { id: \"2\", name: \"Bob\" },\n * { id: \"3\", name: \"Charlie\" },\n * ])\n *\n * const { containerRef, itemRef, activeId, overId, overEdge } = useSortable({\n * items,\n * by: (item) => item.id,\n * onReorder: (newItems) => items.set(newItems),\n * })\n *\n * <ul ref={containerRef}>\n * <For each={items()} by={item => item.id}>\n * {(item) => (\n * <li\n * ref={itemRef(item.id)}\n * class={activeId() === item.id ? \"dragging\" : \"\"}\n * style={overId() === item.id ? `border-${overEdge()}: 2px solid blue` : \"\"}\n * >\n * {item.name}\n * </li>\n * )}\n * </For>\n * </ul>\n * ```\n */\nexport function useSortable<T>(options: UseSortableOptions<T>): UseSortableResult {\n const sortableId = `sortable-${++_sortableCounter}`\n const activeId = signal<string | number | null>(null)\n const overId = signal<string | number | null>(null)\n const overEdge = signal<DropEdge | null>(null)\n const axis = options.axis ?? \"vertical\"\n\n const cleanups: (() => void)[] = []\n\n /** Perform the reorder based on current active/over/edge state. */\n function performReorder() {\n const dragId = activeId.peek()\n const dropId = overId.peek()\n const edge = overEdge.peek()\n if (dragId == null || dropId == null || dragId === dropId) return\n\n const currentItems = options.items()\n const dragIndex = currentItems.findIndex((item) => options.by(item) === dragId)\n const dropIndex = currentItems.findIndex((item) => options.by(item) === dropId)\n if (dragIndex === -1 || dropIndex === -1) return\n\n const reordered = [...currentItems]\n const [moved] = reordered.splice(dragIndex, 1)\n if (!moved) return\n\n // Determine insert position based on closest edge\n const rawInsert =\n edge === \"bottom\" || edge === \"right\"\n ? dropIndex >= dragIndex\n ? dropIndex\n : dropIndex + 1\n : dropIndex <= dragIndex\n ? dropIndex\n : dropIndex - 1\n const insertAt = Math.max(0, Math.min(rawInsert, reordered.length))\n\n reordered.splice(insertAt, 0, moved)\n options.onReorder(reordered)\n }\n\n function containerRef(el: HTMLElement) {\n // Auto-scroll when dragging near container edges\n cleanups.push(\n autoScrollForElements({\n element: el,\n canScroll: ({ source }) => source.data[SORT_ID] === sortableId,\n }),\n )\n\n // Container is a drop target for reorder finalization\n cleanups.push(\n dropTargetForElements({\n element: el,\n getData: () => ({ [SORT_ID]: sortableId }),\n canDrop: ({ source }) => source.data[SORT_ID] === sortableId,\n onDrop: () => {\n performReorder()\n activeId.set(null)\n overId.set(null)\n overEdge.set(null)\n },\n }),\n )\n\n // Keyboard reordering: Alt+Arrow keys\n const keyHandler = (e: KeyboardEvent) => {\n if (!e.altKey) return\n\n const isUp = axis === \"vertical\" ? e.key === \"ArrowUp\" : e.key === \"ArrowLeft\"\n const isDown = axis === \"vertical\" ? e.key === \"ArrowDown\" : e.key === \"ArrowRight\"\n if (!isUp && !isDown) return\n\n const focused = document.activeElement as HTMLElement | null\n if (!focused || !el.contains(focused)) return\n\n const focusedKey = focused.dataset.pyreonSortKey\n if (!focusedKey) return\n\n e.preventDefault()\n\n const currentItems = options.items()\n const currentIndex = currentItems.findIndex((item) => String(options.by(item)) === focusedKey)\n if (currentIndex === -1) return\n\n const targetIndex = isUp ? currentIndex - 1 : currentIndex + 1\n if (targetIndex < 0 || targetIndex >= currentItems.length) return\n\n const reordered = [...currentItems]\n const temp = reordered[currentIndex]\n reordered[currentIndex] = reordered[targetIndex] as T\n reordered[targetIndex] = temp as T\n options.onReorder(reordered)\n\n // Restore focus after DOM update\n requestAnimationFrame(() => {\n const items = el.querySelectorAll(\"[data-pyreon-sort-key]\")\n for (const item of items) {\n if ((item as HTMLElement).dataset.pyreonSortKey === focusedKey) {\n ;(item as HTMLElement).focus()\n break\n }\n }\n })\n }\n\n el.addEventListener(\"keydown\", keyHandler)\n cleanups.push(() => el.removeEventListener(\"keydown\", keyHandler))\n }\n\n function itemRef(key: string | number): (el: HTMLElement) => void {\n return (el: HTMLElement) => {\n el.dataset.pyreonSortKey = String(key)\n if (!el.hasAttribute(\"tabindex\")) el.setAttribute(\"tabindex\", \"0\")\n el.setAttribute(\"role\", \"listitem\")\n el.setAttribute(\"aria-roledescription\", \"sortable item\")\n\n const allowedEdges: Edge[] = axis === \"vertical\" ? [\"top\", \"bottom\"] : [\"left\", \"right\"]\n\n const cleanup = combine(\n draggable({\n element: el,\n getInitialData: () => ({\n [SORT_KEY]: key,\n [SORT_ID]: sortableId,\n }),\n onDragStart: () => activeId.set(key),\n onDrop: () => {\n queueMicrotask(() => {\n activeId.set(null)\n overId.set(null)\n overEdge.set(null)\n })\n },\n }),\n dropTargetForElements({\n element: el,\n getData: ({ input, element }) =>\n attachClosestEdge(\n { [SORT_KEY]: key, [SORT_ID]: sortableId },\n { input, element, allowedEdges },\n ),\n canDrop: ({ source }) => source.data[SORT_ID] === sortableId,\n onDragEnter: ({ self }) => {\n overId.set(key)\n overEdge.set(extractClosestEdge(self.data) as DropEdge | null)\n },\n onDrag: ({ self }) => {\n overEdge.set(extractClosestEdge(self.data) as DropEdge | null)\n },\n onDragLeave: () => {\n if (overId.peek() === key) {\n overId.set(null)\n overEdge.set(null)\n }\n },\n }),\n )\n\n cleanups.push(cleanup)\n }\n }\n\n onCleanup(() => {\n for (const cleanup of cleanups) cleanup()\n cleanups.length = 0\n activeId.set(null)\n overId.set(null)\n overEdge.set(null)\n })\n\n return { containerRef, itemRef, activeId, overId, overEdge }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/use-drag-monitor.ts","../src/use-draggable.ts","../src/use-droppable.ts","../src/use-file-drop.ts","../src/use-sortable.ts"],"sourcesContent":["import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'\nimport { onCleanup, signal } from '@pyreon/reactivity'\nimport type { DragData } from './types'\n\nexport interface UseDragMonitorOptions {\n /** Called on any drag start in the page. */\n onDragStart?: (data: DragData) => void\n /** Called on any drop in the page. */\n onDrop?: (sourceData: DragData, targetData: DragData) => void\n /** Filter which drags to monitor. */\n canMonitor?: (data: DragData) => boolean\n}\n\nexport interface UseDragMonitorResult {\n /** Whether any element is currently being dragged. */\n isDragging: () => boolean\n /** Data of the currently dragging element (null if not dragging). */\n dragData: () => DragData | null\n}\n\n/**\n * Monitor all drag operations on the page.\n * Useful for global drag indicators, analytics, or coordination between\n * multiple drag-and-drop areas.\n *\n * @example\n * ```tsx\n * const { isDragging, dragData } = useDragMonitor({\n * canMonitor: (data) => data.type === \"card\",\n * onDrop: (source, target) => logDrop(source, target),\n * })\n *\n * <Show when={isDragging()}>\n * <div class=\"global-drag-overlay\">\n * Dragging: {() => dragData()?.name}\n * </div>\n * </Show>\n * ```\n */\nexport function useDragMonitor(options?: UseDragMonitorOptions): UseDragMonitorResult {\n const isDragging = signal(false)\n const dragData = signal<DragData | null>(null)\n\n const canMonitorFn = options?.canMonitor\n ? ({ source }: { source: { data: Record<string, unknown> } }) =>\n options.canMonitor?.(source.data as DragData) ?? true\n : null\n\n const cleanup = monitorForElements({\n ...(canMonitorFn ? { canMonitor: canMonitorFn } : {}),\n onDragStart: ({ source }) => {\n isDragging.set(true)\n dragData.set(source.data as DragData)\n options?.onDragStart?.(source.data as DragData)\n },\n onDrop: ({ source, location }) => {\n isDragging.set(false)\n dragData.set(null)\n const targetData = location.current.dropTargets[0]?.data ?? {}\n options?.onDrop?.(source.data as DragData, targetData as DragData)\n },\n })\n\n onCleanup(cleanup)\n\n return { isDragging, dragData }\n}\n","import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'\nimport { onCleanup, signal } from '@pyreon/reactivity'\nimport type { DragData, UseDraggableOptions, UseDraggableResult } from './types'\n\n/**\n * Make an element draggable with signal-driven state.\n *\n * @example\n * ```tsx\n * let cardEl: HTMLElement | null = null\n *\n * const { isDragging } = useDraggable({\n * element: () => cardEl,\n * data: { id: card.id, type: \"card\" },\n * })\n *\n * <div ref={(el) => cardEl = el} class={isDragging() ? \"opacity-50\" : \"\"}>\n * {card.title}\n * </div>\n * ```\n */\nexport function useDraggable<T extends DragData = DragData>(\n options: UseDraggableOptions<T>,\n): UseDraggableResult {\n const isDragging = signal(false)\n let cleanup: (() => void) | undefined\n\n function setup() {\n if (cleanup) cleanup()\n\n const el = options.element()\n if (!el) return\n\n const resolveData = () =>\n typeof options.data === 'function' ? (options.data as () => T)() : options.data\n\n const handle = options.handle?.()\n cleanup = draggable({\n element: el,\n ...(handle ? { dragHandle: handle } : {}),\n getInitialData: resolveData,\n canDrag: () => {\n const disabled = options.disabled\n if (typeof disabled === 'function') return !disabled()\n return !disabled\n },\n onDragStart: () => {\n isDragging.set(true)\n options.onDragStart?.()\n },\n onDrop: () => {\n isDragging.set(false)\n options.onDragEnd?.()\n },\n })\n }\n\n // Defer setup to next microtask so refs are populated\n queueMicrotask(setup)\n\n onCleanup(() => {\n if (cleanup) cleanup()\n })\n\n return { isDragging }\n}\n","import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'\nimport { onCleanup, signal } from '@pyreon/reactivity'\nimport type { DragData, UseDroppableOptions, UseDroppableResult } from './types'\n\n/**\n * Make an element a drop target with signal-driven state.\n *\n * @example\n * ```tsx\n * let zoneEl: HTMLElement | null = null\n *\n * const { isOver } = useDroppable({\n * element: () => zoneEl,\n * onDrop: (data) => handleDrop(data),\n * canDrop: (data) => data.type === \"card\",\n * })\n *\n * <div ref={(el) => zoneEl = el} class={isOver() ? \"bg-blue-50\" : \"\"}>\n * Drop here\n * </div>\n * ```\n */\nexport function useDroppable<T extends DragData = DragData>(\n options: UseDroppableOptions<T>,\n): UseDroppableResult {\n const isOver = signal(false)\n let cleanup: (() => void) | undefined\n\n function setup() {\n if (cleanup) cleanup()\n\n const el = options.element()\n if (!el) return\n\n cleanup = dropTargetForElements({\n element: el,\n getData: () => {\n if (!options.data) return {}\n return typeof options.data === 'function' ? (options.data as () => T)() : options.data\n },\n canDrop: ({ source }) => {\n if (!options.canDrop) return true\n return options.canDrop(source.data as DragData)\n },\n onDragEnter: ({ source }) => {\n isOver.set(true)\n options.onDragEnter?.(source.data as DragData)\n },\n onDragLeave: () => {\n isOver.set(false)\n options.onDragLeave?.()\n },\n onDrop: ({ source }) => {\n isOver.set(false)\n options.onDrop?.(source.data as DragData)\n },\n })\n }\n\n queueMicrotask(setup)\n\n onCleanup(() => {\n if (cleanup) cleanup()\n })\n\n return { isOver }\n}\n","import {\n dropTargetForExternal,\n monitorForExternal,\n} from '@atlaskit/pragmatic-drag-and-drop/external/adapter'\nimport { containsFiles, getFiles } from '@atlaskit/pragmatic-drag-and-drop/external/file'\nimport { onCleanup, signal } from '@pyreon/reactivity'\n\nexport interface UseFileDropOptions {\n /** Element getter for the drop zone. */\n element: () => HTMLElement | null\n /** Called when files are dropped. */\n onDrop: (files: File[]) => void\n /** Filter accepted file types (e.g. [\"image/*\", \".pdf\"]). */\n accept?: string[]\n /** Maximum number of files. */\n maxFiles?: number\n /** Whether drop is disabled. */\n disabled?: boolean | (() => boolean)\n}\n\nexport interface UseFileDropResult {\n /** Whether files are being dragged over the drop zone. */\n isOver: () => boolean\n /** Whether files are being dragged anywhere on the page. */\n isDraggingFiles: () => boolean\n}\n\n/**\n * File drop zone with signal-driven state.\n * Uses the native file drag events via pragmatic-drag-and-drop.\n *\n * @example\n * ```tsx\n * let dropZone: HTMLElement | null = null\n *\n * const { isOver, isDraggingFiles } = useFileDrop({\n * element: () => dropZone,\n * accept: [\"image/*\", \".pdf\"],\n * maxFiles: 5,\n * onDrop: (files) => upload(files),\n * })\n *\n * <div\n * ref={(el) => dropZone = el}\n * class={isOver() ? \"drop-active\" : isDraggingFiles() ? \"drop-ready\" : \"\"}\n * >\n * Drop files here\n * </div>\n * ```\n */\nexport function useFileDrop(options: UseFileDropOptions): UseFileDropResult {\n const isOver = signal(false)\n const isDraggingFiles = signal(false)\n let cleanup: (() => void) | undefined\n\n function matchesAccept(file: File, accept: string[]): boolean {\n return accept.some((pattern) => {\n if (pattern.startsWith('.')) {\n return file.name.toLowerCase().endsWith(pattern.toLowerCase())\n }\n if (pattern.endsWith('/*')) {\n return file.type.startsWith(pattern.slice(0, -1))\n }\n return file.type === pattern\n })\n }\n\n function setup() {\n if (cleanup) cleanup()\n\n const el = options.element()\n if (!el) return\n\n const cleanups: (() => void)[] = []\n\n // Monitor for file drags anywhere on the page\n cleanups.push(\n monitorForExternal({\n canMonitor: ({ source }) => containsFiles({ source }),\n onDragStart: () => isDraggingFiles.set(true),\n onDrop: () => isDraggingFiles.set(false),\n }),\n )\n\n // Drop target on the specific element\n cleanups.push(\n dropTargetForExternal({\n element: el,\n canDrop: ({ source }) => {\n const disabled = options.disabled\n if (typeof disabled === 'function' ? disabled() : disabled) return false\n return containsFiles({ source })\n },\n onDragEnter: () => isOver.set(true),\n onDragLeave: () => isOver.set(false),\n onDrop: ({ source }) => {\n isOver.set(false)\n isDraggingFiles.set(false)\n\n let files = getFiles({ source })\n\n // Filter by accept\n if (options.accept && options.accept.length > 0) {\n files = files.filter((f) => matchesAccept(f, options.accept as string[]))\n }\n\n // Limit count\n if (options.maxFiles && files.length > options.maxFiles) {\n files = files.slice(0, options.maxFiles)\n }\n\n if (files.length > 0) {\n options.onDrop(files)\n }\n },\n }),\n )\n\n cleanup = () => {\n for (const fn of cleanups) fn()\n }\n }\n\n queueMicrotask(setup)\n\n onCleanup(() => {\n if (cleanup) cleanup()\n })\n\n return { isOver, isDraggingFiles }\n}\n","import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine'\nimport { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'\nimport { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-scroll/element'\nimport {\n attachClosestEdge,\n type Edge,\n extractClosestEdge,\n} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge'\nimport { onCleanup, signal } from '@pyreon/reactivity'\nimport type { DropEdge, UseSortableOptions, UseSortableResult } from './types'\n\nconst SORT_KEY = '__pyreon_sortable_key'\nconst SORT_ID = '__pyreon_sortable_id'\n\nlet _sortableCounter = 0\n\n/**\n * Sortable list with signal-driven state, auto-scroll, and edge detection.\n *\n * Features:\n * - Keyed drag items matching `<For by={...}>` pattern\n * - Auto-scroll when dragging near container edges\n * - Closest-edge detection (drop above/below or left/right)\n * - Axis constraint (vertical/horizontal)\n * - Keyboard reordering (Alt+Arrow keys)\n *\n * @example\n * ```tsx\n * const items = signal([\n * { id: \"1\", name: \"Alice\" },\n * { id: \"2\", name: \"Bob\" },\n * { id: \"3\", name: \"Charlie\" },\n * ])\n *\n * const { containerRef, itemRef, activeId, overId, overEdge } = useSortable({\n * items,\n * by: (item) => item.id,\n * onReorder: (newItems) => items.set(newItems),\n * })\n *\n * <ul ref={containerRef}>\n * <For each={items()} by={item => item.id}>\n * {(item) => (\n * <li\n * ref={itemRef(item.id)}\n * class={activeId() === item.id ? \"dragging\" : \"\"}\n * style={overId() === item.id ? `border-${overEdge()}: 2px solid blue` : \"\"}\n * >\n * {item.name}\n * </li>\n * )}\n * </For>\n * </ul>\n * ```\n */\nexport function useSortable<T>(options: UseSortableOptions<T>): UseSortableResult {\n const sortableId = `sortable-${++_sortableCounter}`\n const activeId = signal<string | number | null>(null)\n const overId = signal<string | number | null>(null)\n const overEdge = signal<DropEdge | null>(null)\n const axis = options.axis ?? 'vertical'\n\n const cleanups: (() => void)[] = []\n\n /** Perform the reorder based on current active/over/edge state. */\n function performReorder() {\n const dragId = activeId.peek()\n const dropId = overId.peek()\n const edge = overEdge.peek()\n if (dragId == null || dropId == null || dragId === dropId) return\n\n const currentItems = options.items()\n const dragIndex = currentItems.findIndex((item) => options.by(item) === dragId)\n const dropIndex = currentItems.findIndex((item) => options.by(item) === dropId)\n if (dragIndex === -1 || dropIndex === -1) return\n\n const reordered = [...currentItems]\n const [moved] = reordered.splice(dragIndex, 1)\n if (!moved) return\n\n // Determine insert position based on closest edge\n const rawInsert =\n edge === 'bottom' || edge === 'right'\n ? dropIndex >= dragIndex\n ? dropIndex\n : dropIndex + 1\n : dropIndex <= dragIndex\n ? dropIndex\n : dropIndex - 1\n const insertAt = Math.max(0, Math.min(rawInsert, reordered.length))\n\n reordered.splice(insertAt, 0, moved)\n options.onReorder(reordered)\n }\n\n function containerRef(el: HTMLElement) {\n // Auto-scroll when dragging near container edges\n cleanups.push(\n autoScrollForElements({\n element: el,\n canScroll: ({ source }) => source.data[SORT_ID] === sortableId,\n }),\n )\n\n // Container is a drop target for reorder finalization\n cleanups.push(\n dropTargetForElements({\n element: el,\n getData: () => ({ [SORT_ID]: sortableId }),\n canDrop: ({ source }) => source.data[SORT_ID] === sortableId,\n onDrop: () => {\n performReorder()\n activeId.set(null)\n overId.set(null)\n overEdge.set(null)\n },\n }),\n )\n\n // Keyboard reordering: Alt+Arrow keys\n const keyHandler = (e: KeyboardEvent) => {\n if (!e.altKey) return\n\n const isUp = axis === 'vertical' ? e.key === 'ArrowUp' : e.key === 'ArrowLeft'\n const isDown = axis === 'vertical' ? e.key === 'ArrowDown' : e.key === 'ArrowRight'\n if (!isUp && !isDown) return\n\n const focused = document.activeElement as HTMLElement | null\n if (!focused || !el.contains(focused)) return\n\n const focusedKey = focused.dataset.pyreonSortKey\n if (!focusedKey) return\n\n e.preventDefault()\n\n const currentItems = options.items()\n const currentIndex = currentItems.findIndex((item) => String(options.by(item)) === focusedKey)\n if (currentIndex === -1) return\n\n const targetIndex = isUp ? currentIndex - 1 : currentIndex + 1\n if (targetIndex < 0 || targetIndex >= currentItems.length) return\n\n const reordered = [...currentItems]\n const temp = reordered[currentIndex]\n reordered[currentIndex] = reordered[targetIndex] as T\n reordered[targetIndex] = temp as T\n options.onReorder(reordered)\n\n // Restore focus after DOM update\n requestAnimationFrame(() => {\n const items = el.querySelectorAll('[data-pyreon-sort-key]')\n for (const item of items) {\n if ((item as HTMLElement).dataset.pyreonSortKey === focusedKey) {\n ;(item as HTMLElement).focus()\n break\n }\n }\n })\n }\n\n el.addEventListener('keydown', keyHandler)\n cleanups.push(() => el.removeEventListener('keydown', keyHandler))\n }\n\n function itemRef(key: string | number): (el: HTMLElement) => void {\n return (el: HTMLElement) => {\n el.dataset.pyreonSortKey = String(key)\n if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0')\n el.setAttribute('role', 'listitem')\n el.setAttribute('aria-roledescription', 'sortable item')\n\n const allowedEdges: Edge[] = axis === 'vertical' ? ['top', 'bottom'] : ['left', 'right']\n\n const cleanup = combine(\n draggable({\n element: el,\n getInitialData: () => ({\n [SORT_KEY]: key,\n [SORT_ID]: sortableId,\n }),\n onDragStart: () => activeId.set(key),\n onDrop: () => {\n queueMicrotask(() => {\n activeId.set(null)\n overId.set(null)\n overEdge.set(null)\n })\n },\n }),\n dropTargetForElements({\n element: el,\n getData: ({ input, element }) =>\n attachClosestEdge(\n { [SORT_KEY]: key, [SORT_ID]: sortableId },\n { input, element, allowedEdges },\n ),\n canDrop: ({ source }) => source.data[SORT_ID] === sortableId,\n onDragEnter: ({ self }) => {\n overId.set(key)\n overEdge.set(extractClosestEdge(self.data) as DropEdge | null)\n },\n onDrag: ({ self }) => {\n overEdge.set(extractClosestEdge(self.data) as DropEdge | null)\n },\n onDragLeave: () => {\n if (overId.peek() === key) {\n overId.set(null)\n overEdge.set(null)\n }\n },\n }),\n )\n\n cleanups.push(cleanup)\n }\n }\n\n onCleanup(() => {\n for (const cleanup of cleanups) cleanup()\n cleanups.length = 0\n activeId.set(null)\n overId.set(null)\n overEdge.set(null)\n })\n\n return { containerRef, itemRef, activeId, overId, overEdge }\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  /** Data attached to a draggable item. */
3
3
  type DragData = Record<string, unknown>;
4
4
  /** Position of a drop relative to the target element. */
5
- type DropEdge = "top" | "bottom" | "left" | "right";
5
+ type DropEdge = 'top' | 'bottom' | 'left' | 'right';
6
6
  /** Drop location information. */
7
7
  interface DropLocation {
8
8
  /** The edge closest to the drop point. */
@@ -54,7 +54,7 @@ interface UseSortableOptions<T> {
54
54
  /** Called with the reordered items after a drop. */
55
55
  onReorder: (items: T[]) => void;
56
56
  /** Sort axis. Default: "vertical". */
57
- axis?: "vertical" | "horizontal";
57
+ axis?: 'vertical' | 'horizontal';
58
58
  }
59
59
  interface UseSortableResult {
60
60
  /** Attach to the scroll container. */
package/package.json CHANGED
@@ -1,19 +1,16 @@
1
1
  {
2
2
  "name": "@pyreon/dnd",
3
- "version": "0.11.5",
3
+ "version": "0.11.6",
4
4
  "description": "Signal-driven drag and drop for Pyreon — wraps @atlaskit/pragmatic-drag-and-drop",
5
+ "bugs": {
6
+ "url": "https://github.com/pyreon/pyreon/issues"
7
+ },
5
8
  "license": "MIT",
6
9
  "repository": {
7
10
  "type": "git",
8
11
  "url": "https://github.com/pyreon/pyreon.git",
9
12
  "directory": "packages/fundamentals/dnd"
10
13
  },
11
- "bugs": {
12
- "url": "https://github.com/pyreon/pyreon/issues"
13
- },
14
- "publishConfig": {
15
- "access": "public"
16
- },
17
14
  "files": [
18
15
  "lib",
19
16
  "src",
@@ -29,23 +26,26 @@
29
26
  "types": "./lib/types/index.d.ts"
30
27
  }
31
28
  },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
32
  "scripts": {
33
33
  "build": "vl_rolldown_build",
34
34
  "dev": "vl_rolldown_build-watch",
35
35
  "test": "vitest run",
36
36
  "typecheck": "tsc --noEmit",
37
- "lint": "biome check ."
37
+ "lint": "oxlint ."
38
38
  },
39
39
  "dependencies": {
40
40
  "@atlaskit/pragmatic-drag-and-drop": "^1.7.0",
41
41
  "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^2.0.0",
42
42
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.0"
43
43
  },
44
- "peerDependencies": {
45
- "@pyreon/core": "^0.11.5",
46
- "@pyreon/reactivity": "^0.11.5"
47
- },
48
44
  "devDependencies": {
49
- "@pyreon/typescript": "^0.11.5"
45
+ "@pyreon/typescript": "^0.11.6"
46
+ },
47
+ "peerDependencies": {
48
+ "@pyreon/core": "^0.11.6",
49
+ "@pyreon/reactivity": "^0.11.6"
50
50
  }
51
51
  }
package/src/index.ts CHANGED
@@ -8,13 +8,13 @@ export type {
8
8
  UseDroppableResult,
9
9
  UseSortableOptions,
10
10
  UseSortableResult,
11
- } from "./types"
11
+ } from './types'
12
12
 
13
- export type { UseDragMonitorOptions, UseDragMonitorResult } from "./use-drag-monitor"
14
- export { useDragMonitor } from "./use-drag-monitor"
13
+ export type { UseDragMonitorOptions, UseDragMonitorResult } from './use-drag-monitor'
14
+ export { useDragMonitor } from './use-drag-monitor'
15
15
 
16
- export { useDraggable } from "./use-draggable"
17
- export { useDroppable } from "./use-droppable"
18
- export type { UseFileDropOptions, UseFileDropResult } from "./use-file-drop"
19
- export { useFileDrop } from "./use-file-drop"
20
- export { useSortable } from "./use-sortable"
16
+ export { useDraggable } from './use-draggable'
17
+ export { useDroppable } from './use-droppable'
18
+ export type { UseFileDropOptions, UseFileDropResult } from './use-file-drop'
19
+ export { useFileDrop } from './use-file-drop'
20
+ export { useSortable } from './use-sortable'