@vielzeug/dnd 2.1.0 → 3.0.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 CHANGED
@@ -1,23 +1,6 @@
1
1
  # @vielzeug/dnd
2
2
 
3
- > Framework-agnostic drag-and-drop. Drop zones with MIME filtering, sortable lists with drag handles, and explicit connected scopes — zero dependencies.
4
-
5
- [![npm version](https://img.shields.io/npm/v/@vielzeug/dnd)](https://www.npmjs.com/package/@vielzeug/dnd) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
-
7
- <details>
8
- <summary>Quick Reference</summary>
9
-
10
- **Package:** `@vielzeug/dnd` &nbsp;·&nbsp; **Category:** UI Interaction
11
-
12
- **Key exports:** `createDropZone`, `createSortable`, `createSortableScope`, `applyReorder`, `matchesAccept`
13
-
14
- **When to use:** File drop zones with MIME filtering and async validation, or sortable lists with keyboard, FLIP animation, and optimistic-update support — zero dependencies.
15
-
16
- **Related:** [@vielzeug/ore](https://vielzeug.dev/ore/) · [@vielzeug/scroll](https://vielzeug.dev/scroll/) · [@vielzeug/refine](https://vielzeug.dev/refine/)
17
-
18
- </details>
19
-
20
- `@vielzeug/dnd` is part of Vielzeug and ships as a zero-dependency TypeScript package with ESM+CJS output.
3
+ > Drag-and-drop — drop zones and sortable lists
21
4
 
22
5
  ## Installation
23
6
 
@@ -30,7 +13,8 @@ yarn add @vielzeug/dnd
30
13
  ## Quick Start
31
14
 
32
15
  ```ts
33
- import { createDropZone, createSortable, createSortableScope } from '@vielzeug/dnd';
16
+ import { createDropZone } from '@vielzeug/dnd/drop';
17
+ import { createSortable, createSortableScope } from '@vielzeug/dnd/sortable';
34
18
 
35
19
  // Drop zone — with async validation and clipboard paste support
36
20
  using zone = createDropZone({
@@ -58,8 +42,8 @@ using sortable = createSortable({
58
42
  onBeforeReorder: (from, to) => {
59
43
  // snapshot element positions here for FLIP animations
60
44
  },
61
- onReorder: ({ ids }) => {
62
- setOrder(ids);
45
+ onReorder: ({ after }) => {
46
+ setOrder(after);
63
47
  },
64
48
  scope,
65
49
  });
@@ -1 +1 @@
1
- {"version":3,"file":"_shared.cjs","names":[],"sources":["../src/_shared.ts"],"sourcesContent":["import type { Disposable } from './types';\n\n// ─── Disposable ──────────────────────────────────────────────────────────────\n\n/**\n * Create a `Disposable` backed by an `AbortController`.\n * `dispose()` is idempotent — the signal aborts and `cleanup` runs at most once.\n * Pass the returned `disposalSignal` to `addEventListener` calls so removal happens\n * automatically on dispose, instead of pairing manual `removeEventListener` calls.\n */\nexport function createDisposable(cleanup?: () => void): Disposable {\n let disposed = false;\n const abortController = new AbortController();\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n abortController.abort();\n cleanup?.();\n };\n\n return {\n get disposalSignal() {\n return abortController.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n [Symbol.dispose]: dispose,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nexport function resolveDisabled(disabled: boolean | undefined): boolean {\n return disabled === true;\n}\n"],"mappings":"AAUA,SAAgB,EAAiB,EAAkC,CACjE,IAAI,EAAW,GACT,EAAkB,IAAI,gBAEtB,MAAsB,CACtB,IAEJ,EAAW,GACX,EAAgB,MAAM,EACtB,IAAU,EACZ,EAEA,MAAO,CACL,IAAI,gBAAiB,CACnB,OAAO,EAAgB,MACzB,EACA,UACA,IAAI,UAAW,CACb,OAAO,CACT,GACC,OAAO,SAAU,CACpB,CACF,CAIA,SAAgB,EAAgB,EAAwC,CACtE,OAAO,IAAa,EACtB"}
1
+ {"version":3,"file":"_shared.cjs","names":[],"sources":["../src/_shared.ts"],"sourcesContent":["import type { Disposable } from './types.js';\n\n// ─── Disposable ──────────────────────────────────────────────────────────────\n\n/**\n * Create a `Disposable` backed by an `AbortController`.\n * `dispose()` is idempotent — the signal aborts and `cleanup` runs at most once.\n * Pass the returned `disposalSignal` to `addEventListener` calls so removal happens\n * automatically on dispose, instead of pairing manual `removeEventListener` calls.\n */\nexport function createDisposable(cleanup?: () => void): Disposable {\n let disposed = false;\n const abortController = new AbortController();\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n abortController.abort();\n cleanup?.();\n };\n\n return {\n get disposalSignal() {\n return abortController.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n [Symbol.dispose]: dispose,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nexport function resolveDisabled(disabled: boolean | undefined): boolean {\n return disabled === true;\n}\n"],"mappings":"AAUA,SAAgB,EAAiB,EAAkC,CACjE,IAAI,EAAW,GACT,EAAkB,IAAI,gBAEtB,MAAsB,CACtB,IAEJ,EAAW,GACX,EAAgB,MAAM,EACtB,IAAU,EACZ,EAEA,MAAO,CACL,IAAI,gBAAiB,CACnB,OAAO,EAAgB,MACzB,EACA,UACA,IAAI,UAAW,CACb,OAAO,CACT,GACC,OAAO,SAAU,CACpB,CACF,CAIA,SAAgB,EAAgB,EAAwC,CACtE,OAAO,IAAa,EACtB"}
package/dist/_shared.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Disposable } from './types';
1
+ import type { Disposable } from './types.js';
2
2
  /**
3
3
  * Create a `Disposable` backed by an `AbortController`.
4
4
  * `dispose()` is idempotent — the signal aborts and `cleanup` runs at most once.
@@ -1 +1 @@
1
- {"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../src/_shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI1C;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,UAAU,CAsBjE;AAID,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAEtE"}
1
+ {"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../src/_shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,UAAU,CAsBjE;AAID,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAEtE"}
@@ -1 +1 @@
1
- {"version":3,"file":"_shared.js","names":[],"sources":["../src/_shared.ts"],"sourcesContent":["import type { Disposable } from './types';\n\n// ─── Disposable ──────────────────────────────────────────────────────────────\n\n/**\n * Create a `Disposable` backed by an `AbortController`.\n * `dispose()` is idempotent — the signal aborts and `cleanup` runs at most once.\n * Pass the returned `disposalSignal` to `addEventListener` calls so removal happens\n * automatically on dispose, instead of pairing manual `removeEventListener` calls.\n */\nexport function createDisposable(cleanup?: () => void): Disposable {\n let disposed = false;\n const abortController = new AbortController();\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n abortController.abort();\n cleanup?.();\n };\n\n return {\n get disposalSignal() {\n return abortController.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n [Symbol.dispose]: dispose,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nexport function resolveDisabled(disabled: boolean | undefined): boolean {\n return disabled === true;\n}\n"],"mappings":";AAUA,SAAgB,EAAiB,GAAkC;CACjE,IAAI,IAAW,IACT,IAAkB,IAAI,gBAAgB,GAEtC,UAAsB;EACtB,MAEJ,IAAW,IACX,EAAgB,MAAM,GACtB,IAAU;CACZ;CAEA,OAAO;EACL,IAAI,iBAAiB;GACnB,OAAO,EAAgB;EACzB;EACA;EACA,IAAI,WAAW;GACb,OAAO;EACT;GACC,OAAO,UAAU;CACpB;AACF;AAIA,SAAgB,EAAgB,GAAwC;CACtE,OAAO,MAAa;AACtB"}
1
+ {"version":3,"file":"_shared.js","names":[],"sources":["../src/_shared.ts"],"sourcesContent":["import type { Disposable } from './types.js';\n\n// ─── Disposable ──────────────────────────────────────────────────────────────\n\n/**\n * Create a `Disposable` backed by an `AbortController`.\n * `dispose()` is idempotent — the signal aborts and `cleanup` runs at most once.\n * Pass the returned `disposalSignal` to `addEventListener` calls so removal happens\n * automatically on dispose, instead of pairing manual `removeEventListener` calls.\n */\nexport function createDisposable(cleanup?: () => void): Disposable {\n let disposed = false;\n const abortController = new AbortController();\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n abortController.abort();\n cleanup?.();\n };\n\n return {\n get disposalSignal() {\n return abortController.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n [Symbol.dispose]: dispose,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nexport function resolveDisabled(disabled: boolean | undefined): boolean {\n return disabled === true;\n}\n"],"mappings":";AAUA,SAAgB,EAAiB,GAAkC;CACjE,IAAI,IAAW,IACT,IAAkB,IAAI,gBAAgB,GAEtC,UAAsB;EACtB,MAEJ,IAAW,IACX,EAAgB,MAAM,GACtB,IAAU;CACZ;CAEA,OAAO;EACL,IAAI,iBAAiB;GACnB,OAAO,EAAgB;EACzB;EACA;EACA,IAAI,WAAW;GACb,OAAO;EACT;GACC,OAAO,UAAU;CACpB;AACF;AAIA,SAAgB,EAAgB,GAAwC;CACtE,OAAO,MAAa;AACtB"}
package/dist/_touch.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./_shared.cjs");require("./_dev.cjs");var t=2147483647;function n(){return{dropEffect:`move`,effectAllowed:`move`,getData:()=>``,setData(){},setDragImage(){}}}function r(e){let n=e.getBoundingClientRect(),r=document.createElement(`div`);return r.setAttribute(`aria-hidden`,`true`),r.setAttribute(`data-dnd-touch-preview`,``),r.setAttribute(`inert`,``),Object.assign(r.style,{background:`transparent`,border:`2px solid currentColor`,boxSizing:`border-box`,height:`${n.height}px`,left:`${n.left}px`,opacity:`0.7`,pointerEvents:`none`,position:`fixed`,top:`${n.top}px`,transition:`none`,width:`${n.width}px`,zIndex:String(t)}),r}function i(i,a){let o=n(),s={state:`idle`};function c(e,t){return Array.from(e).find(e=>e.identifier===t)}function l(){s.state===`dragging`&&s.preview?.remove(),s={state:`idle`}}function u(e,t,n){let r=e.source.style.display,i=e.preview?.style.display??``;e.source.style.display=`none`,e.preview&&(e.preview.style.display=`none`);try{return document.elementFromPoint(t,n)}finally{e.source.style.display=r,e.preview&&(e.preview.style.display=i)}}let d=e.createDisposable(l);function f(e,t,n,r,i=!1){let a=new Event(t,{bubbles:!0,cancelable:!0});return Object.defineProperty(a,"__dndTouch",{configurable:!0,value:!0}),Object.defineProperty(a,"__dndTouchPreview",{configurable:!0,value:i}),Object.defineProperty(a,"clientX",{configurable:!0,value:n}),Object.defineProperty(a,"clientY",{configurable:!0,value:r}),Object.defineProperty(a,"dataTransfer",{configurable:!0,value:o}),e.dispatchEvent(a),a.defaultPrevented}function p(e){if(i.preview===!1)return null;let n;try{let t=i.preview?.(e);n=i.preview?t?.cloneNode(!0)??null:r(e)}catch(e){`${String(e)}`,n=null}return n?(n.setAttribute(`aria-hidden`,`true`),n.setAttribute(`data-dnd-touch-preview`,``),n.setAttribute(`inert`,``),n.removeAttribute(`id`),n.querySelectorAll(`[id]`).forEach(e=>{e.removeAttribute(`id`)}),n.style.pointerEvents=`none`,n.style.position=`fixed`,n.style.zIndex=String(t),document.body.appendChild(n),n):null}document.addEventListener(`touchstart`,e=>{if(s.state!==`idle`)return;let t=e.changedTouches[0];if(!t)return;let n=document.elementFromPoint(t.clientX,t.clientY),r=n?a(n):null;r&&(s={identifier:t.identifier,source:r,start:{clientX:t.clientX,clientY:t.clientY},state:`pending`})},{passive:!1,signal:d.disposalSignal}),document.addEventListener(`touchmove`,e=>{if(s.state===`idle`)return;let t=c(e.changedTouches,s.identifier);if(!t)return;if(s.state===`pending`){if(Math.hypot(t.clientX-s.start.clientX,t.clientY-s.start.clientY)<6)return;let e={clientX:t.clientX,clientY:t.clientY},n=p(s.source),r={current:e,identifier:s.identifier,lastTarget:s.source,preview:n,previewOrigin:n?e:null,source:s.source,state:`dragging`};if(s=r,f(r.source,`dragstart`,t.clientX,t.clientY,n!==null),s!==r)return}if(s.current={clientX:t.clientX,clientY:t.clientY},s.preview&&s.previewOrigin){let e=t.clientX-s.previewOrigin.clientX,n=t.clientY-s.previewOrigin.clientY;s.preview.style.transform=`translate3d(${e}px, ${n}px, 0)`}let n=u(s,t.clientX,t.clientY);n&&n!==s.lastTarget&&(s.lastTarget&&f(s.lastTarget,`dragleave`,t.clientX,t.clientY),s.lastTarget=n),n&&f(n,`dragover`,t.clientX,t.clientY),e.preventDefault()},{passive:!1,signal:d.disposalSignal});function m(e,t){if(s.state===`idle`)return;let n=c(e.changedTouches,s.identifier);if(!n){let n=c(e.touches,s.identifier);if(!t||n)return}if(s.state===`pending`){l();return}let r=s,i=n??r.current;try{let e=!1;if(!t){let t=u(r,i.clientX,i.clientY);t&&(e=f(t,`drop`,i.clientX,i.clientY))}o.dropEffect=e?`move`:`none`,f(r.source,`dragend`,i.clientX,i.clientY,r.preview!==null)}finally{o.dropEffect=`move`,l()}}return document.addEventListener(`touchend`,e=>m(e,!1),{passive:!0,signal:d.disposalSignal}),document.addEventListener(`touchcancel`,e=>m(e,!0),{passive:!0,signal:d.disposalSignal}),d}exports.createScopeTouchController=i;
1
+ require("./_dev.cjs");const e=require("./_shared.cjs"),t=require("./errors.cjs");let n=require("@vielzeug/gesture");var r=2147483647;function i(){return{dropEffect:`move`,effectAllowed:`move`,getData:()=>``,setData(){},setDragImage(){}}}function a(e){let t=e.getBoundingClientRect(),n=e.ownerDocument.createElement(`div`);return n.setAttribute(`aria-hidden`,`true`),n.setAttribute(`data-dnd-touch-preview`,``),n.setAttribute(`inert`,``),Object.assign(n.style,{background:`transparent`,border:`2px solid currentColor`,boxSizing:`border-box`,height:`${t.height}px`,left:`${t.left}px`,opacity:`0.7`,pointerEvents:`none`,position:`fixed`,top:`${t.top}px`,transition:`none`,width:`${t.width}px`,zIndex:String(r)}),n}function o(o,s){let{activationDistance:c=6,preview:l}=o;if(!Number.isFinite(c)||c<0)throw new t.DndError(`touch.activationDistance must be a non-negative finite number`);let u=i(),d=new Map,f=null,p=null,m=()=>{p?.preview?.remove(),f=null,p=null},h=e.createDisposable(()=>{m(),d.clear()}),g=e=>{let t=e.composedPath().find(e=>e.nodeType===1)??e.target;return t&&t.nodeType===1?t:null},_=(e,t)=>{let n=e.source.style.display,r=e.preview?.style.display??``;e.source.style.display=`none`,e.preview&&(e.preview.style.display=`none`);try{return e.source.ownerDocument.elementFromPoint(t.x,t.y)}finally{e.source.style.display=n,e.preview&&(e.preview.style.display=r)}},v=(e,t,n,r=!1)=>{let i=new((e.ownerDocument.defaultView?.Event)??Event)(t,{bubbles:!0,cancelable:!0});return Object.defineProperties(i,{__dndTouch:{configurable:!0,value:!0},__dndTouchPreview:{configurable:!0,value:r},clientX:{configurable:!0,value:n.x},clientY:{configurable:!0,value:n.y},dataTransfer:{configurable:!0,value:u}}),e.dispatchEvent(i),i.defaultPrevented},y=e=>{if(l===!1)return null;let t;try{let n=l?.(e);t=l?n?.cloneNode(!0)??null:a(e)}catch(e){`${String(e)}`,t=null}return t?(t.setAttribute(`aria-hidden`,`true`),t.setAttribute(`data-dnd-touch-preview`,``),t.setAttribute(`inert`,``),t.removeAttribute(`id`),t.querySelectorAll(`[id]`).forEach(e=>{e.removeAttribute(`id`)}),t.style.pointerEvents=`none`,t.style.position=`fixed`,t.style.zIndex=String(r),e.ownerDocument.body.appendChild(t),t):null},b=e=>{let t=f;if(f=null,!t)return;let n=y(t);p={current:e.current,lastTarget:t,preview:n,previewOrigin:n?e.current:null,source:t},v(t,`dragstart`,e.current,n!==null)},x=e=>{if(!p)return;if(p.current=e.current,p.preview&&p.previewOrigin){let t=e.current.x-p.previewOrigin.x,n=e.current.y-p.previewOrigin.y;p.preview.style.transform=`translate3d(${t}px, ${n}px, 0)`}let t=_(p,e.current);t&&t!==p.lastTarget&&(p.lastTarget&&v(p.lastTarget,`dragleave`,e.current),p.lastTarget=t),t&&v(t,`dragover`,e.current),e.event.preventDefault()},S=e=>{if(f=null,!p)return;let t=p;try{let n=!1;if(e.reason===`release`){let r=_(t,e.current);r&&(n=v(r,`drop`,e.current))}u.dropEffect=n?`move`:`none`,v(t.source,`dragend`,e.current,t.preview!==null)}finally{u.dropEffect=`move`,m()}};return Object.assign(h,{cancel(){for(let e of d.values())if(e.cancel())return!0;return!1},register(e){let t=(0,n.createDragGesture)(e,{activationDistance:c,onEnd:S,onMove:x,onStart:b,pointerCapture:!1,shouldStart:e=>{if(e.pointerType!==`touch`||p)return!1;let t=g(e);return f=t?s(t):null,f!==null},signal:h.disposalSignal});return d.set(e,t),()=>{t.dispose(),d.delete(e),f&&e.contains(f)&&(f=null),p&&e.contains(p.source)&&m()}}})}exports.createScopeTouchController=o;
2
2
  //# sourceMappingURL=_touch.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_touch.cjs","names":[],"sources":["../src/_touch.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { createDisposable } from './_shared';\nimport type { Disposable } from './types';\n\nexport interface TouchInputOptions {\n /**\n * Renders visual feedback for a touch drag. Return `null` to keep the source item visible\n * instead of rendering a preview. Dnd clones the returned element, preserving caller-owned DOM.\n * The default is an inert outline sized to the source item.\n */\n preview?: false | ((item: HTMLElement) => HTMLElement | null);\n}\n\nexport type ScopeTouchController = Disposable;\n\nconst PREVIEW_Z_INDEX = 2147483647;\n\ntype TouchPoint = {\n clientX: number;\n clientY: number;\n};\n\ntype PendingTouchSession = {\n identifier: number;\n source: HTMLElement;\n start: TouchPoint;\n state: 'pending';\n};\n\ntype DraggingTouchSession = {\n current: TouchPoint;\n identifier: number;\n lastTarget: Element | null;\n preview: HTMLElement | null;\n previewOrigin: TouchPoint | null;\n source: HTMLElement;\n state: 'dragging';\n};\n\ntype TouchSession = { state: 'idle' } | DraggingTouchSession | PendingTouchSession;\n\nfunction makeDataTransfer(): DataTransfer {\n return {\n dropEffect: 'move',\n effectAllowed: 'move',\n getData: () => '',\n setData() {},\n setDragImage() {},\n } as unknown as DataTransfer;\n}\n\nfunction createDefaultPreview(source: HTMLElement): HTMLElement {\n const rect = source.getBoundingClientRect();\n const preview = document.createElement('div');\n\n preview.setAttribute('aria-hidden', 'true');\n preview.setAttribute('data-dnd-touch-preview', '');\n preview.setAttribute('inert', '');\n Object.assign(preview.style, {\n background: 'transparent',\n border: '2px solid currentColor',\n boxSizing: 'border-box',\n height: `${rect.height}px`,\n left: `${rect.left}px`,\n opacity: '0.7',\n pointerEvents: 'none',\n position: 'fixed',\n top: `${rect.top}px`,\n transition: 'none',\n width: `${rect.width}px`,\n zIndex: String(PREVIEW_Z_INDEX),\n });\n\n return preview;\n}\n\nexport function createScopeTouchController(\n options: TouchInputOptions,\n resolveDragTarget: (target: Element) => HTMLElement | null,\n): ScopeTouchController {\n const dt = makeDataTransfer();\n const dragStartDistancePx = 6;\n let session: TouchSession = { state: 'idle' };\n\n function findTouch(touches: TouchList, identifier: number): Touch | undefined {\n return Array.from(touches).find((touch) => touch.identifier === identifier);\n }\n\n function resetSession(): void {\n if (session.state === 'dragging') session.preview?.remove();\n\n session = { state: 'idle' };\n }\n\n function elementBelow(active: DraggingTouchSession, clientX: number, clientY: number): Element | null {\n const previousSourceDisplay = active.source.style.display;\n const previousPreviewDisplay = active.preview?.style.display ?? '';\n\n active.source.style.display = 'none';\n if (active.preview) active.preview.style.display = 'none';\n\n try {\n return document.elementFromPoint(clientX, clientY);\n } finally {\n active.source.style.display = previousSourceDisplay;\n\n if (active.preview) active.preview.style.display = previousPreviewDisplay;\n }\n }\n\n const disposable = createDisposable(resetSession);\n\n function dispatch(element: Element, type: string, clientX: number, clientY: number, hasPreview = false): boolean {\n const event = new Event(type, { bubbles: true, cancelable: true });\n\n Object.defineProperty(event, '__dndTouch', { configurable: true, value: true });\n Object.defineProperty(event, '__dndTouchPreview', { configurable: true, value: hasPreview });\n Object.defineProperty(event, 'clientX', { configurable: true, value: clientX });\n Object.defineProperty(event, 'clientY', { configurable: true, value: clientY });\n Object.defineProperty(event, 'dataTransfer', { configurable: true, value: dt });\n element.dispatchEvent(event);\n\n return event.defaultPrevented;\n }\n\n function renderPreview(source: HTMLElement): HTMLElement | null {\n if (options.preview === false) return null;\n\n let previewEl: HTMLElement | null;\n\n try {\n const preview = options.preview?.(source);\n\n previewEl = options.preview\n ? ((preview?.cloneNode(true) as HTMLElement | undefined) ?? null)\n : createDefaultPreview(source);\n } catch (error) {\n warn(`touch drag preview failed to render, continuing without one: ${String(error)}`);\n previewEl = null;\n }\n\n if (!previewEl) return null;\n\n previewEl.setAttribute('aria-hidden', 'true');\n previewEl.setAttribute('data-dnd-touch-preview', '');\n previewEl.setAttribute('inert', '');\n previewEl.removeAttribute('id');\n previewEl.querySelectorAll('[id]').forEach((element) => {\n element.removeAttribute('id');\n });\n previewEl.style.pointerEvents = 'none';\n previewEl.style.position = 'fixed';\n previewEl.style.zIndex = String(PREVIEW_Z_INDEX);\n document.body.appendChild(previewEl);\n\n return previewEl;\n }\n\n document.addEventListener(\n 'touchstart',\n (event: TouchEvent) => {\n if (session.state !== 'idle') return;\n\n const touch = event.changedTouches[0];\n\n if (!touch) return;\n\n const target = document.elementFromPoint(touch.clientX, touch.clientY);\n const draggable = target ? resolveDragTarget(target) : null;\n\n if (!draggable) return;\n\n session = {\n identifier: touch.identifier,\n source: draggable,\n start: { clientX: touch.clientX, clientY: touch.clientY },\n state: 'pending',\n };\n },\n { passive: false, signal: disposable.disposalSignal },\n );\n\n document.addEventListener(\n 'touchmove',\n (event: TouchEvent) => {\n if (session.state === 'idle') return;\n\n const touch = findTouch(event.changedTouches, session.identifier);\n\n if (!touch) return;\n\n if (session.state === 'pending') {\n const distance = Math.hypot(touch.clientX - session.start.clientX, touch.clientY - session.start.clientY);\n\n if (distance < dragStartDistancePx) return;\n\n const point = { clientX: touch.clientX, clientY: touch.clientY };\n const preview = renderPreview(session.source);\n const active: DraggingTouchSession = {\n current: point,\n identifier: session.identifier,\n lastTarget: session.source,\n preview,\n previewOrigin: preview ? point : null,\n source: session.source,\n state: 'dragging',\n };\n\n session = active;\n dispatch(active.source, 'dragstart', touch.clientX, touch.clientY, preview !== null);\n\n if (session !== active) return;\n }\n\n session.current = { clientX: touch.clientX, clientY: touch.clientY };\n\n if (session.preview && session.previewOrigin) {\n const dx = touch.clientX - session.previewOrigin.clientX;\n const dy = touch.clientY - session.previewOrigin.clientY;\n\n session.preview.style.transform = `translate3d(${dx}px, ${dy}px, 0)`;\n }\n\n const below = elementBelow(session, touch.clientX, touch.clientY);\n\n if (below && below !== session.lastTarget) {\n if (session.lastTarget) dispatch(session.lastTarget, 'dragleave', touch.clientX, touch.clientY);\n\n session.lastTarget = below;\n }\n\n if (below) dispatch(below, 'dragover', touch.clientX, touch.clientY);\n\n event.preventDefault();\n },\n { passive: false, signal: disposable.disposalSignal },\n );\n\n function finish(event: TouchEvent, cancelled: boolean): void {\n if (session.state === 'idle') return;\n\n const touch = findTouch(event.changedTouches, session.identifier);\n\n if (!touch) {\n const stillActive = findTouch(event.touches, session.identifier);\n\n if (!cancelled || stillActive) return;\n }\n\n if (session.state === 'pending') {\n resetSession();\n\n return;\n }\n\n const active = session;\n const point = touch ?? active.current;\n\n try {\n let dropAccepted = false;\n\n if (!cancelled) {\n const below = elementBelow(active, point.clientX, point.clientY);\n\n if (below) dropAccepted = dispatch(below, 'drop', point.clientX, point.clientY);\n }\n\n dt.dropEffect = dropAccepted ? 'move' : 'none';\n dispatch(active.source, 'dragend', point.clientX, point.clientY, active.preview !== null);\n } finally {\n dt.dropEffect = 'move';\n resetSession();\n }\n }\n\n document.addEventListener('touchend', (event: TouchEvent) => finish(event, false), {\n passive: true,\n signal: disposable.disposalSignal,\n });\n document.addEventListener('touchcancel', (event: TouchEvent) => finish(event, true), {\n passive: true,\n signal: disposable.disposalSignal,\n });\n\n return disposable;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_touch.cjs","names":[],"sources":["../src/_touch.ts"],"sourcesContent":["import { createDragGesture, type DragGesture, type DragGestureDetail } from '@vielzeug/gesture';\n\nimport { warn } from './_dev.js';\nimport { createDisposable } from './_shared.js';\nimport { DndError } from './errors.js';\nimport type { Disposable } from './types.js';\n\nexport type TouchInputOptions = Readonly<{\n /** Finger movement in pixels required before sorting starts. @default 6 */\n activationDistance?: number;\n /**\n * Renders visual feedback for a touch drag. Return `null` to keep the source item visible\n * instead of rendering a preview. Dnd clones the returned element, preserving caller-owned DOM.\n * The default is an inert outline sized to the source item.\n */\n preview?: false | ((item: HTMLElement) => HTMLElement | null);\n}>;\n\nexport interface ScopeTouchController extends Disposable {\n cancel(): boolean;\n register(element: HTMLElement): () => void;\n}\n\nconst PREVIEW_Z_INDEX = 2147483647;\n\ntype TouchDragSession = {\n current: DragGestureDetail['current'];\n lastTarget: Element | null;\n preview: HTMLElement | null;\n previewOrigin: DragGestureDetail['current'] | null;\n source: HTMLElement;\n};\n\nfunction makeDataTransfer(): DataTransfer {\n return {\n dropEffect: 'move',\n effectAllowed: 'move',\n getData: () => '',\n setData() {},\n setDragImage() {},\n } as unknown as DataTransfer;\n}\n\nfunction createDefaultPreview(source: HTMLElement): HTMLElement {\n const rect = source.getBoundingClientRect();\n const preview = source.ownerDocument.createElement('div');\n preview.setAttribute('aria-hidden', 'true');\n preview.setAttribute('data-dnd-touch-preview', '');\n preview.setAttribute('inert', '');\n Object.assign(preview.style, {\n background: 'transparent',\n border: '2px solid currentColor',\n boxSizing: 'border-box',\n height: `${rect.height}px`,\n left: `${rect.left}px`,\n opacity: '0.7',\n pointerEvents: 'none',\n position: 'fixed',\n top: `${rect.top}px`,\n transition: 'none',\n width: `${rect.width}px`,\n zIndex: String(PREVIEW_Z_INDEX),\n });\n return preview;\n}\n\nexport function createScopeTouchController(\n options: TouchInputOptions,\n resolveDragTarget: (target: Element) => HTMLElement | null,\n): ScopeTouchController {\n const { activationDistance = 6, preview: previewOption } = options;\n if (!Number.isFinite(activationDistance) || activationDistance < 0) {\n throw new DndError('touch.activationDistance must be a non-negative finite number');\n }\n\n const dataTransfer = makeDataTransfer();\n const gestures = new Map<HTMLElement, DragGesture>();\n let pendingSource: HTMLElement | null = null;\n let session: TouchDragSession | null = null;\n\n const resetSession = (): void => {\n session?.preview?.remove();\n pendingSource = null;\n session = null;\n };\n const disposable = createDisposable(() => {\n resetSession();\n gestures.clear();\n });\n\n const eventTarget = (event: PointerEvent): Element | null => {\n const target = event.composedPath().find((node) => (node as Node).nodeType === 1) ?? event.target;\n return target && (target as Node).nodeType === 1 ? (target as Element) : null;\n };\n\n const elementBelow = (active: TouchDragSession, point: DragGestureDetail['current']): Element | null => {\n const previousSourceDisplay = active.source.style.display;\n const previousPreviewDisplay = active.preview?.style.display ?? '';\n active.source.style.display = 'none';\n if (active.preview) active.preview.style.display = 'none';\n try {\n return active.source.ownerDocument.elementFromPoint(point.x, point.y);\n } finally {\n active.source.style.display = previousSourceDisplay;\n if (active.preview) active.preview.style.display = previousPreviewDisplay;\n }\n };\n\n const dispatch = (\n element: Element,\n type: string,\n point: DragGestureDetail['current'],\n hasPreview = false,\n ): boolean => {\n const EventType = element.ownerDocument.defaultView?.Event ?? Event;\n const event = new EventType(type, { bubbles: true, cancelable: true });\n Object.defineProperties(event, {\n __dndTouch: { configurable: true, value: true },\n __dndTouchPreview: { configurable: true, value: hasPreview },\n clientX: { configurable: true, value: point.x },\n clientY: { configurable: true, value: point.y },\n dataTransfer: { configurable: true, value: dataTransfer },\n });\n element.dispatchEvent(event);\n return event.defaultPrevented;\n };\n\n const renderPreview = (source: HTMLElement): HTMLElement | null => {\n if (previewOption === false) return null;\n let previewElement: HTMLElement | null;\n try {\n const preview = previewOption?.(source);\n previewElement = previewOption\n ? ((preview?.cloneNode(true) as HTMLElement | undefined) ?? null)\n : createDefaultPreview(source);\n } catch (error) {\n warn(`touch drag preview failed to render, continuing without one: ${String(error)}`);\n previewElement = null;\n }\n if (!previewElement) return null;\n\n previewElement.setAttribute('aria-hidden', 'true');\n previewElement.setAttribute('data-dnd-touch-preview', '');\n previewElement.setAttribute('inert', '');\n previewElement.removeAttribute('id');\n previewElement.querySelectorAll('[id]').forEach((element) => {\n element.removeAttribute('id');\n });\n previewElement.style.pointerEvents = 'none';\n previewElement.style.position = 'fixed';\n previewElement.style.zIndex = String(PREVIEW_Z_INDEX);\n source.ownerDocument.body.appendChild(previewElement);\n return previewElement;\n };\n\n const start = (detail: DragGestureDetail): void => {\n const source = pendingSource;\n pendingSource = null;\n if (!source) return;\n\n const preview = renderPreview(source);\n const active: TouchDragSession = {\n current: detail.current,\n lastTarget: source,\n preview,\n previewOrigin: preview ? detail.current : null,\n source,\n };\n session = active;\n dispatch(source, 'dragstart', detail.current, preview !== null);\n };\n\n const move = (detail: DragGestureDetail): void => {\n if (!session) return;\n session.current = detail.current;\n if (session.preview && session.previewOrigin) {\n const x = detail.current.x - session.previewOrigin.x;\n const y = detail.current.y - session.previewOrigin.y;\n session.preview.style.transform = `translate3d(${x}px, ${y}px, 0)`;\n }\n\n const below = elementBelow(session, detail.current);\n if (below && below !== session.lastTarget) {\n if (session.lastTarget) dispatch(session.lastTarget, 'dragleave', detail.current);\n session.lastTarget = below;\n }\n if (below) dispatch(below, 'dragover', detail.current);\n detail.event.preventDefault();\n };\n\n const end = (detail: DragGestureDetail & Readonly<{ reason: 'cancel' | 'release' }>): void => {\n pendingSource = null;\n if (!session) return;\n const active = session;\n try {\n let dropAccepted = false;\n if (detail.reason === 'release') {\n const below = elementBelow(active, detail.current);\n if (below) dropAccepted = dispatch(below, 'drop', detail.current);\n }\n dataTransfer.dropEffect = dropAccepted ? 'move' : 'none';\n dispatch(active.source, 'dragend', detail.current, active.preview !== null);\n } finally {\n dataTransfer.dropEffect = 'move';\n resetSession();\n }\n };\n\n return Object.assign(disposable, {\n cancel(): boolean {\n for (const gesture of gestures.values()) {\n if (gesture.cancel()) return true;\n }\n return false;\n },\n register(element: HTMLElement): () => void {\n const gesture = createDragGesture(element, {\n activationDistance,\n onEnd: end,\n onMove: move,\n onStart: start,\n pointerCapture: false,\n shouldStart: (event) => {\n if (event.pointerType !== 'touch' || session) return false;\n const target = eventTarget(event);\n pendingSource = target ? resolveDragTarget(target) : null;\n return pendingSource !== null;\n },\n signal: disposable.disposalSignal,\n });\n gestures.set(element, gesture);\n\n return () => {\n gesture.dispose();\n gestures.delete(element);\n if (pendingSource && element.contains(pendingSource)) pendingSource = null;\n if (session && element.contains(session.source)) resetSession();\n };\n },\n });\n}\n"],"mappings":"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"}
package/dist/_touch.d.ts CHANGED
@@ -1,12 +1,17 @@
1
- import type { Disposable } from './types';
2
- export interface TouchInputOptions {
1
+ import type { Disposable } from './types.js';
2
+ export type TouchInputOptions = Readonly<{
3
+ /** Finger movement in pixels required before sorting starts. @default 6 */
4
+ activationDistance?: number;
3
5
  /**
4
6
  * Renders visual feedback for a touch drag. Return `null` to keep the source item visible
5
7
  * instead of rendering a preview. Dnd clones the returned element, preserving caller-owned DOM.
6
8
  * The default is an inert outline sized to the source item.
7
9
  */
8
10
  preview?: false | ((item: HTMLElement) => HTMLElement | null);
11
+ }>;
12
+ export interface ScopeTouchController extends Disposable {
13
+ cancel(): boolean;
14
+ register(element: HTMLElement): () => void;
9
15
  }
10
- export type ScopeTouchController = Disposable;
11
16
  export declare function createScopeTouchController(options: TouchInputOptions, resolveDragTarget: (target: Element) => HTMLElement | null): ScopeTouchController;
12
17
  //# sourceMappingURL=_touch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"_touch.d.ts","sourceRoot":"","sources":["../src/_touch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,WAAW,KAAK,WAAW,GAAG,IAAI,CAAC,CAAC;CAC/D;AAED,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC;AA+D9C,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iBAAiB,EAC1B,iBAAiB,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,IAAI,GACzD,oBAAoB,CA8MtB"}
1
+ {"version":3,"file":"_touch.d.ts","sourceRoot":"","sources":["../src/_touch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,WAAW,KAAK,WAAW,GAAG,IAAI,CAAC,CAAC;CAC/D,CAAC,CAAC;AAEH,MAAM,WAAW,oBAAqB,SAAQ,UAAU;IACtD,MAAM,IAAI,OAAO,CAAC;IAClB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,IAAI,CAAC;CAC5C;AA6CD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iBAAiB,EAC1B,iBAAiB,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,IAAI,GACzD,oBAAoB,CA2KtB"}
package/dist/_touch.js CHANGED
@@ -1,8 +1,10 @@
1
- import { createDisposable as e } from "./_shared.js";
2
1
  import "./_dev.js";
2
+ import { createDisposable as e } from "./_shared.js";
3
+ import { DndError as t } from "./errors.js";
4
+ import { createDragGesture as n } from "@vielzeug/gesture";
3
5
  //#region src/_touch.ts
4
- var t = 2147483647;
5
- function n() {
6
+ var r = 2147483647;
7
+ function i() {
6
8
  return {
7
9
  dropEffect: "move",
8
10
  effectAllowed: "move",
@@ -11,158 +13,139 @@ function n() {
11
13
  setDragImage() {}
12
14
  };
13
15
  }
14
- function r(e) {
15
- let n = e.getBoundingClientRect(), r = document.createElement("div");
16
- return r.setAttribute("aria-hidden", "true"), r.setAttribute("data-dnd-touch-preview", ""), r.setAttribute("inert", ""), Object.assign(r.style, {
16
+ function a(e) {
17
+ let t = e.getBoundingClientRect(), n = e.ownerDocument.createElement("div");
18
+ return n.setAttribute("aria-hidden", "true"), n.setAttribute("data-dnd-touch-preview", ""), n.setAttribute("inert", ""), Object.assign(n.style, {
17
19
  background: "transparent",
18
20
  border: "2px solid currentColor",
19
21
  boxSizing: "border-box",
20
- height: `${n.height}px`,
21
- left: `${n.left}px`,
22
+ height: `${t.height}px`,
23
+ left: `${t.left}px`,
22
24
  opacity: "0.7",
23
25
  pointerEvents: "none",
24
26
  position: "fixed",
25
- top: `${n.top}px`,
27
+ top: `${t.top}px`,
26
28
  transition: "none",
27
- width: `${n.width}px`,
28
- zIndex: String(t)
29
- }), r;
29
+ width: `${t.width}px`,
30
+ zIndex: String(r)
31
+ }), n;
30
32
  }
31
- function i(i, a) {
32
- let o = n(), s = { state: "idle" };
33
- function c(e, t) {
34
- return Array.from(e).find((e) => e.identifier === t);
35
- }
36
- function l() {
37
- s.state === "dragging" && s.preview?.remove(), s = { state: "idle" };
38
- }
39
- function u(e, t, n) {
40
- let r = e.source.style.display, i = e.preview?.style.display ?? "";
33
+ function o(o, s) {
34
+ let { activationDistance: c = 6, preview: l } = o;
35
+ if (!Number.isFinite(c) || c < 0) throw new t("touch.activationDistance must be a non-negative finite number");
36
+ let u = i(), d = /* @__PURE__ */ new Map(), f = null, p = null, m = () => {
37
+ p?.preview?.remove(), f = null, p = null;
38
+ }, h = e(() => {
39
+ m(), d.clear();
40
+ }), g = (e) => {
41
+ let t = e.composedPath().find((e) => e.nodeType === 1) ?? e.target;
42
+ return t && t.nodeType === 1 ? t : null;
43
+ }, _ = (e, t) => {
44
+ let n = e.source.style.display, r = e.preview?.style.display ?? "";
41
45
  e.source.style.display = "none", e.preview && (e.preview.style.display = "none");
42
46
  try {
43
- return document.elementFromPoint(t, n);
47
+ return e.source.ownerDocument.elementFromPoint(t.x, t.y);
44
48
  } finally {
45
- e.source.style.display = r, e.preview && (e.preview.style.display = i);
49
+ e.source.style.display = n, e.preview && (e.preview.style.display = r);
46
50
  }
47
- }
48
- let d = e(l);
49
- function f(e, t, n, r, i = !1) {
50
- let a = new Event(t, {
51
+ }, v = (e, t, n, r = !1) => {
52
+ let i = new ((e.ownerDocument.defaultView?.Event) ?? Event)(t, {
51
53
  bubbles: !0,
52
54
  cancelable: !0
53
55
  });
54
- return Object.defineProperty(a, "__dndTouch", {
55
- configurable: !0,
56
- value: !0
57
- }), Object.defineProperty(a, "__dndTouchPreview", {
58
- configurable: !0,
59
- value: i
60
- }), Object.defineProperty(a, "clientX", {
61
- configurable: !0,
62
- value: n
63
- }), Object.defineProperty(a, "clientY", {
64
- configurable: !0,
65
- value: r
66
- }), Object.defineProperty(a, "dataTransfer", {
67
- configurable: !0,
68
- value: o
69
- }), e.dispatchEvent(a), a.defaultPrevented;
70
- }
71
- function p(e) {
72
- if (i.preview === !1) return null;
73
- let n;
56
+ return Object.defineProperties(i, {
57
+ __dndTouch: {
58
+ configurable: !0,
59
+ value: !0
60
+ },
61
+ __dndTouchPreview: {
62
+ configurable: !0,
63
+ value: r
64
+ },
65
+ clientX: {
66
+ configurable: !0,
67
+ value: n.x
68
+ },
69
+ clientY: {
70
+ configurable: !0,
71
+ value: n.y
72
+ },
73
+ dataTransfer: {
74
+ configurable: !0,
75
+ value: u
76
+ }
77
+ }), e.dispatchEvent(i), i.defaultPrevented;
78
+ }, y = (e) => {
79
+ if (l === !1) return null;
80
+ let t;
74
81
  try {
75
- let t = i.preview?.(e);
76
- n = i.preview ? t?.cloneNode(!0) ?? null : r(e);
82
+ let n = l?.(e);
83
+ t = l ? n?.cloneNode(!0) ?? null : a(e);
77
84
  } catch (e) {
78
- `${String(e)}`, n = null;
85
+ `${String(e)}`, t = null;
79
86
  }
80
- return n ? (n.setAttribute("aria-hidden", "true"), n.setAttribute("data-dnd-touch-preview", ""), n.setAttribute("inert", ""), n.removeAttribute("id"), n.querySelectorAll("[id]").forEach((e) => {
87
+ return t ? (t.setAttribute("aria-hidden", "true"), t.setAttribute("data-dnd-touch-preview", ""), t.setAttribute("inert", ""), t.removeAttribute("id"), t.querySelectorAll("[id]").forEach((e) => {
81
88
  e.removeAttribute("id");
82
- }), n.style.pointerEvents = "none", n.style.position = "fixed", n.style.zIndex = String(t), document.body.appendChild(n), n) : null;
83
- }
84
- document.addEventListener("touchstart", (e) => {
85
- if (s.state !== "idle") return;
86
- let t = e.changedTouches[0];
87
- if (!t) return;
88
- let n = document.elementFromPoint(t.clientX, t.clientY), r = n ? a(n) : null;
89
- r && (s = {
90
- identifier: t.identifier,
91
- source: r,
92
- start: {
93
- clientX: t.clientX,
94
- clientY: t.clientY
95
- },
96
- state: "pending"
97
- });
98
- }, {
99
- passive: !1,
100
- signal: d.disposalSignal
101
- }), document.addEventListener("touchmove", (e) => {
102
- if (s.state === "idle") return;
103
- let t = c(e.changedTouches, s.identifier);
104
- if (!t) return;
105
- if (s.state === "pending") {
106
- if (Math.hypot(t.clientX - s.start.clientX, t.clientY - s.start.clientY) < 6) return;
107
- let e = {
108
- clientX: t.clientX,
109
- clientY: t.clientY
110
- }, n = p(s.source), r = {
111
- current: e,
112
- identifier: s.identifier,
113
- lastTarget: s.source,
114
- preview: n,
115
- previewOrigin: n ? e : null,
116
- source: s.source,
117
- state: "dragging"
118
- };
119
- if (s = r, f(r.source, "dragstart", t.clientX, t.clientY, n !== null), s !== r) return;
120
- }
121
- if (s.current = {
122
- clientX: t.clientX,
123
- clientY: t.clientY
124
- }, s.preview && s.previewOrigin) {
125
- let e = t.clientX - s.previewOrigin.clientX, n = t.clientY - s.previewOrigin.clientY;
126
- s.preview.style.transform = `translate3d(${e}px, ${n}px, 0)`;
127
- }
128
- let n = u(s, t.clientX, t.clientY);
129
- n && n !== s.lastTarget && (s.lastTarget && f(s.lastTarget, "dragleave", t.clientX, t.clientY), s.lastTarget = n), n && f(n, "dragover", t.clientX, t.clientY), e.preventDefault();
130
- }, {
131
- passive: !1,
132
- signal: d.disposalSignal
133
- });
134
- function m(e, t) {
135
- if (s.state === "idle") return;
136
- let n = c(e.changedTouches, s.identifier);
137
- if (!n) {
138
- let n = c(e.touches, s.identifier);
139
- if (!t || n) return;
140
- }
141
- if (s.state === "pending") {
142
- l();
143
- return;
89
+ }), t.style.pointerEvents = "none", t.style.position = "fixed", t.style.zIndex = String(r), e.ownerDocument.body.appendChild(t), t) : null;
90
+ }, b = (e) => {
91
+ let t = f;
92
+ if (f = null, !t) return;
93
+ let n = y(t);
94
+ p = {
95
+ current: e.current,
96
+ lastTarget: t,
97
+ preview: n,
98
+ previewOrigin: n ? e.current : null,
99
+ source: t
100
+ }, v(t, "dragstart", e.current, n !== null);
101
+ }, x = (e) => {
102
+ if (!p) return;
103
+ if (p.current = e.current, p.preview && p.previewOrigin) {
104
+ let t = e.current.x - p.previewOrigin.x, n = e.current.y - p.previewOrigin.y;
105
+ p.preview.style.transform = `translate3d(${t}px, ${n}px, 0)`;
144
106
  }
145
- let r = s, i = n ?? r.current;
107
+ let t = _(p, e.current);
108
+ t && t !== p.lastTarget && (p.lastTarget && v(p.lastTarget, "dragleave", e.current), p.lastTarget = t), t && v(t, "dragover", e.current), e.event.preventDefault();
109
+ }, S = (e) => {
110
+ if (f = null, !p) return;
111
+ let t = p;
146
112
  try {
147
- let e = !1;
148
- if (!t) {
149
- let t = u(r, i.clientX, i.clientY);
150
- t && (e = f(t, "drop", i.clientX, i.clientY));
113
+ let n = !1;
114
+ if (e.reason === "release") {
115
+ let r = _(t, e.current);
116
+ r && (n = v(r, "drop", e.current));
151
117
  }
152
- o.dropEffect = e ? "move" : "none", f(r.source, "dragend", i.clientX, i.clientY, r.preview !== null);
118
+ u.dropEffect = n ? "move" : "none", v(t.source, "dragend", e.current, t.preview !== null);
153
119
  } finally {
154
- o.dropEffect = "move", l();
120
+ u.dropEffect = "move", m();
155
121
  }
156
- }
157
- return document.addEventListener("touchend", (e) => m(e, !1), {
158
- passive: !0,
159
- signal: d.disposalSignal
160
- }), document.addEventListener("touchcancel", (e) => m(e, !0), {
161
- passive: !0,
162
- signal: d.disposalSignal
163
- }), d;
122
+ };
123
+ return Object.assign(h, {
124
+ cancel() {
125
+ for (let e of d.values()) if (e.cancel()) return !0;
126
+ return !1;
127
+ },
128
+ register(e) {
129
+ let t = n(e, {
130
+ activationDistance: c,
131
+ onEnd: S,
132
+ onMove: x,
133
+ onStart: b,
134
+ pointerCapture: !1,
135
+ shouldStart: (e) => {
136
+ if (e.pointerType !== "touch" || p) return !1;
137
+ let t = g(e);
138
+ return f = t ? s(t) : null, f !== null;
139
+ },
140
+ signal: h.disposalSignal
141
+ });
142
+ return d.set(e, t), () => {
143
+ t.dispose(), d.delete(e), f && e.contains(f) && (f = null), p && e.contains(p.source) && m();
144
+ };
145
+ }
146
+ });
164
147
  }
165
148
  //#endregion
166
- export { i as createScopeTouchController };
149
+ export { o as createScopeTouchController };
167
150
 
168
151
  //# sourceMappingURL=_touch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_touch.js","names":[],"sources":["../src/_touch.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { createDisposable } from './_shared';\nimport type { Disposable } from './types';\n\nexport interface TouchInputOptions {\n /**\n * Renders visual feedback for a touch drag. Return `null` to keep the source item visible\n * instead of rendering a preview. Dnd clones the returned element, preserving caller-owned DOM.\n * The default is an inert outline sized to the source item.\n */\n preview?: false | ((item: HTMLElement) => HTMLElement | null);\n}\n\nexport type ScopeTouchController = Disposable;\n\nconst PREVIEW_Z_INDEX = 2147483647;\n\ntype TouchPoint = {\n clientX: number;\n clientY: number;\n};\n\ntype PendingTouchSession = {\n identifier: number;\n source: HTMLElement;\n start: TouchPoint;\n state: 'pending';\n};\n\ntype DraggingTouchSession = {\n current: TouchPoint;\n identifier: number;\n lastTarget: Element | null;\n preview: HTMLElement | null;\n previewOrigin: TouchPoint | null;\n source: HTMLElement;\n state: 'dragging';\n};\n\ntype TouchSession = { state: 'idle' } | DraggingTouchSession | PendingTouchSession;\n\nfunction makeDataTransfer(): DataTransfer {\n return {\n dropEffect: 'move',\n effectAllowed: 'move',\n getData: () => '',\n setData() {},\n setDragImage() {},\n } as unknown as DataTransfer;\n}\n\nfunction createDefaultPreview(source: HTMLElement): HTMLElement {\n const rect = source.getBoundingClientRect();\n const preview = document.createElement('div');\n\n preview.setAttribute('aria-hidden', 'true');\n preview.setAttribute('data-dnd-touch-preview', '');\n preview.setAttribute('inert', '');\n Object.assign(preview.style, {\n background: 'transparent',\n border: '2px solid currentColor',\n boxSizing: 'border-box',\n height: `${rect.height}px`,\n left: `${rect.left}px`,\n opacity: '0.7',\n pointerEvents: 'none',\n position: 'fixed',\n top: `${rect.top}px`,\n transition: 'none',\n width: `${rect.width}px`,\n zIndex: String(PREVIEW_Z_INDEX),\n });\n\n return preview;\n}\n\nexport function createScopeTouchController(\n options: TouchInputOptions,\n resolveDragTarget: (target: Element) => HTMLElement | null,\n): ScopeTouchController {\n const dt = makeDataTransfer();\n const dragStartDistancePx = 6;\n let session: TouchSession = { state: 'idle' };\n\n function findTouch(touches: TouchList, identifier: number): Touch | undefined {\n return Array.from(touches).find((touch) => touch.identifier === identifier);\n }\n\n function resetSession(): void {\n if (session.state === 'dragging') session.preview?.remove();\n\n session = { state: 'idle' };\n }\n\n function elementBelow(active: DraggingTouchSession, clientX: number, clientY: number): Element | null {\n const previousSourceDisplay = active.source.style.display;\n const previousPreviewDisplay = active.preview?.style.display ?? '';\n\n active.source.style.display = 'none';\n if (active.preview) active.preview.style.display = 'none';\n\n try {\n return document.elementFromPoint(clientX, clientY);\n } finally {\n active.source.style.display = previousSourceDisplay;\n\n if (active.preview) active.preview.style.display = previousPreviewDisplay;\n }\n }\n\n const disposable = createDisposable(resetSession);\n\n function dispatch(element: Element, type: string, clientX: number, clientY: number, hasPreview = false): boolean {\n const event = new Event(type, { bubbles: true, cancelable: true });\n\n Object.defineProperty(event, '__dndTouch', { configurable: true, value: true });\n Object.defineProperty(event, '__dndTouchPreview', { configurable: true, value: hasPreview });\n Object.defineProperty(event, 'clientX', { configurable: true, value: clientX });\n Object.defineProperty(event, 'clientY', { configurable: true, value: clientY });\n Object.defineProperty(event, 'dataTransfer', { configurable: true, value: dt });\n element.dispatchEvent(event);\n\n return event.defaultPrevented;\n }\n\n function renderPreview(source: HTMLElement): HTMLElement | null {\n if (options.preview === false) return null;\n\n let previewEl: HTMLElement | null;\n\n try {\n const preview = options.preview?.(source);\n\n previewEl = options.preview\n ? ((preview?.cloneNode(true) as HTMLElement | undefined) ?? null)\n : createDefaultPreview(source);\n } catch (error) {\n warn(`touch drag preview failed to render, continuing without one: ${String(error)}`);\n previewEl = null;\n }\n\n if (!previewEl) return null;\n\n previewEl.setAttribute('aria-hidden', 'true');\n previewEl.setAttribute('data-dnd-touch-preview', '');\n previewEl.setAttribute('inert', '');\n previewEl.removeAttribute('id');\n previewEl.querySelectorAll('[id]').forEach((element) => {\n element.removeAttribute('id');\n });\n previewEl.style.pointerEvents = 'none';\n previewEl.style.position = 'fixed';\n previewEl.style.zIndex = String(PREVIEW_Z_INDEX);\n document.body.appendChild(previewEl);\n\n return previewEl;\n }\n\n document.addEventListener(\n 'touchstart',\n (event: TouchEvent) => {\n if (session.state !== 'idle') return;\n\n const touch = event.changedTouches[0];\n\n if (!touch) return;\n\n const target = document.elementFromPoint(touch.clientX, touch.clientY);\n const draggable = target ? resolveDragTarget(target) : null;\n\n if (!draggable) return;\n\n session = {\n identifier: touch.identifier,\n source: draggable,\n start: { clientX: touch.clientX, clientY: touch.clientY },\n state: 'pending',\n };\n },\n { passive: false, signal: disposable.disposalSignal },\n );\n\n document.addEventListener(\n 'touchmove',\n (event: TouchEvent) => {\n if (session.state === 'idle') return;\n\n const touch = findTouch(event.changedTouches, session.identifier);\n\n if (!touch) return;\n\n if (session.state === 'pending') {\n const distance = Math.hypot(touch.clientX - session.start.clientX, touch.clientY - session.start.clientY);\n\n if (distance < dragStartDistancePx) return;\n\n const point = { clientX: touch.clientX, clientY: touch.clientY };\n const preview = renderPreview(session.source);\n const active: DraggingTouchSession = {\n current: point,\n identifier: session.identifier,\n lastTarget: session.source,\n preview,\n previewOrigin: preview ? point : null,\n source: session.source,\n state: 'dragging',\n };\n\n session = active;\n dispatch(active.source, 'dragstart', touch.clientX, touch.clientY, preview !== null);\n\n if (session !== active) return;\n }\n\n session.current = { clientX: touch.clientX, clientY: touch.clientY };\n\n if (session.preview && session.previewOrigin) {\n const dx = touch.clientX - session.previewOrigin.clientX;\n const dy = touch.clientY - session.previewOrigin.clientY;\n\n session.preview.style.transform = `translate3d(${dx}px, ${dy}px, 0)`;\n }\n\n const below = elementBelow(session, touch.clientX, touch.clientY);\n\n if (below && below !== session.lastTarget) {\n if (session.lastTarget) dispatch(session.lastTarget, 'dragleave', touch.clientX, touch.clientY);\n\n session.lastTarget = below;\n }\n\n if (below) dispatch(below, 'dragover', touch.clientX, touch.clientY);\n\n event.preventDefault();\n },\n { passive: false, signal: disposable.disposalSignal },\n );\n\n function finish(event: TouchEvent, cancelled: boolean): void {\n if (session.state === 'idle') return;\n\n const touch = findTouch(event.changedTouches, session.identifier);\n\n if (!touch) {\n const stillActive = findTouch(event.touches, session.identifier);\n\n if (!cancelled || stillActive) return;\n }\n\n if (session.state === 'pending') {\n resetSession();\n\n return;\n }\n\n const active = session;\n const point = touch ?? active.current;\n\n try {\n let dropAccepted = false;\n\n if (!cancelled) {\n const below = elementBelow(active, point.clientX, point.clientY);\n\n if (below) dropAccepted = dispatch(below, 'drop', point.clientX, point.clientY);\n }\n\n dt.dropEffect = dropAccepted ? 'move' : 'none';\n dispatch(active.source, 'dragend', point.clientX, point.clientY, active.preview !== null);\n } finally {\n dt.dropEffect = 'move';\n resetSession();\n }\n }\n\n document.addEventListener('touchend', (event: TouchEvent) => finish(event, false), {\n passive: true,\n signal: disposable.disposalSignal,\n });\n document.addEventListener('touchcancel', (event: TouchEvent) => finish(event, true), {\n passive: true,\n signal: disposable.disposalSignal,\n });\n\n return disposable;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_touch.js","names":[],"sources":["../src/_touch.ts"],"sourcesContent":["import { createDragGesture, type DragGesture, type DragGestureDetail } from '@vielzeug/gesture';\n\nimport { warn } from './_dev.js';\nimport { createDisposable } from './_shared.js';\nimport { DndError } from './errors.js';\nimport type { Disposable } from './types.js';\n\nexport type TouchInputOptions = Readonly<{\n /** Finger movement in pixels required before sorting starts. @default 6 */\n activationDistance?: number;\n /**\n * Renders visual feedback for a touch drag. Return `null` to keep the source item visible\n * instead of rendering a preview. Dnd clones the returned element, preserving caller-owned DOM.\n * The default is an inert outline sized to the source item.\n */\n preview?: false | ((item: HTMLElement) => HTMLElement | null);\n}>;\n\nexport interface ScopeTouchController extends Disposable {\n cancel(): boolean;\n register(element: HTMLElement): () => void;\n}\n\nconst PREVIEW_Z_INDEX = 2147483647;\n\ntype TouchDragSession = {\n current: DragGestureDetail['current'];\n lastTarget: Element | null;\n preview: HTMLElement | null;\n previewOrigin: DragGestureDetail['current'] | null;\n source: HTMLElement;\n};\n\nfunction makeDataTransfer(): DataTransfer {\n return {\n dropEffect: 'move',\n effectAllowed: 'move',\n getData: () => '',\n setData() {},\n setDragImage() {},\n } as unknown as DataTransfer;\n}\n\nfunction createDefaultPreview(source: HTMLElement): HTMLElement {\n const rect = source.getBoundingClientRect();\n const preview = source.ownerDocument.createElement('div');\n preview.setAttribute('aria-hidden', 'true');\n preview.setAttribute('data-dnd-touch-preview', '');\n preview.setAttribute('inert', '');\n Object.assign(preview.style, {\n background: 'transparent',\n border: '2px solid currentColor',\n boxSizing: 'border-box',\n height: `${rect.height}px`,\n left: `${rect.left}px`,\n opacity: '0.7',\n pointerEvents: 'none',\n position: 'fixed',\n top: `${rect.top}px`,\n transition: 'none',\n width: `${rect.width}px`,\n zIndex: String(PREVIEW_Z_INDEX),\n });\n return preview;\n}\n\nexport function createScopeTouchController(\n options: TouchInputOptions,\n resolveDragTarget: (target: Element) => HTMLElement | null,\n): ScopeTouchController {\n const { activationDistance = 6, preview: previewOption } = options;\n if (!Number.isFinite(activationDistance) || activationDistance < 0) {\n throw new DndError('touch.activationDistance must be a non-negative finite number');\n }\n\n const dataTransfer = makeDataTransfer();\n const gestures = new Map<HTMLElement, DragGesture>();\n let pendingSource: HTMLElement | null = null;\n let session: TouchDragSession | null = null;\n\n const resetSession = (): void => {\n session?.preview?.remove();\n pendingSource = null;\n session = null;\n };\n const disposable = createDisposable(() => {\n resetSession();\n gestures.clear();\n });\n\n const eventTarget = (event: PointerEvent): Element | null => {\n const target = event.composedPath().find((node) => (node as Node).nodeType === 1) ?? event.target;\n return target && (target as Node).nodeType === 1 ? (target as Element) : null;\n };\n\n const elementBelow = (active: TouchDragSession, point: DragGestureDetail['current']): Element | null => {\n const previousSourceDisplay = active.source.style.display;\n const previousPreviewDisplay = active.preview?.style.display ?? '';\n active.source.style.display = 'none';\n if (active.preview) active.preview.style.display = 'none';\n try {\n return active.source.ownerDocument.elementFromPoint(point.x, point.y);\n } finally {\n active.source.style.display = previousSourceDisplay;\n if (active.preview) active.preview.style.display = previousPreviewDisplay;\n }\n };\n\n const dispatch = (\n element: Element,\n type: string,\n point: DragGestureDetail['current'],\n hasPreview = false,\n ): boolean => {\n const EventType = element.ownerDocument.defaultView?.Event ?? Event;\n const event = new EventType(type, { bubbles: true, cancelable: true });\n Object.defineProperties(event, {\n __dndTouch: { configurable: true, value: true },\n __dndTouchPreview: { configurable: true, value: hasPreview },\n clientX: { configurable: true, value: point.x },\n clientY: { configurable: true, value: point.y },\n dataTransfer: { configurable: true, value: dataTransfer },\n });\n element.dispatchEvent(event);\n return event.defaultPrevented;\n };\n\n const renderPreview = (source: HTMLElement): HTMLElement | null => {\n if (previewOption === false) return null;\n let previewElement: HTMLElement | null;\n try {\n const preview = previewOption?.(source);\n previewElement = previewOption\n ? ((preview?.cloneNode(true) as HTMLElement | undefined) ?? null)\n : createDefaultPreview(source);\n } catch (error) {\n warn(`touch drag preview failed to render, continuing without one: ${String(error)}`);\n previewElement = null;\n }\n if (!previewElement) return null;\n\n previewElement.setAttribute('aria-hidden', 'true');\n previewElement.setAttribute('data-dnd-touch-preview', '');\n previewElement.setAttribute('inert', '');\n previewElement.removeAttribute('id');\n previewElement.querySelectorAll('[id]').forEach((element) => {\n element.removeAttribute('id');\n });\n previewElement.style.pointerEvents = 'none';\n previewElement.style.position = 'fixed';\n previewElement.style.zIndex = String(PREVIEW_Z_INDEX);\n source.ownerDocument.body.appendChild(previewElement);\n return previewElement;\n };\n\n const start = (detail: DragGestureDetail): void => {\n const source = pendingSource;\n pendingSource = null;\n if (!source) return;\n\n const preview = renderPreview(source);\n const active: TouchDragSession = {\n current: detail.current,\n lastTarget: source,\n preview,\n previewOrigin: preview ? detail.current : null,\n source,\n };\n session = active;\n dispatch(source, 'dragstart', detail.current, preview !== null);\n };\n\n const move = (detail: DragGestureDetail): void => {\n if (!session) return;\n session.current = detail.current;\n if (session.preview && session.previewOrigin) {\n const x = detail.current.x - session.previewOrigin.x;\n const y = detail.current.y - session.previewOrigin.y;\n session.preview.style.transform = `translate3d(${x}px, ${y}px, 0)`;\n }\n\n const below = elementBelow(session, detail.current);\n if (below && below !== session.lastTarget) {\n if (session.lastTarget) dispatch(session.lastTarget, 'dragleave', detail.current);\n session.lastTarget = below;\n }\n if (below) dispatch(below, 'dragover', detail.current);\n detail.event.preventDefault();\n };\n\n const end = (detail: DragGestureDetail & Readonly<{ reason: 'cancel' | 'release' }>): void => {\n pendingSource = null;\n if (!session) return;\n const active = session;\n try {\n let dropAccepted = false;\n if (detail.reason === 'release') {\n const below = elementBelow(active, detail.current);\n if (below) dropAccepted = dispatch(below, 'drop', detail.current);\n }\n dataTransfer.dropEffect = dropAccepted ? 'move' : 'none';\n dispatch(active.source, 'dragend', detail.current, active.preview !== null);\n } finally {\n dataTransfer.dropEffect = 'move';\n resetSession();\n }\n };\n\n return Object.assign(disposable, {\n cancel(): boolean {\n for (const gesture of gestures.values()) {\n if (gesture.cancel()) return true;\n }\n return false;\n },\n register(element: HTMLElement): () => void {\n const gesture = createDragGesture(element, {\n activationDistance,\n onEnd: end,\n onMove: move,\n onStart: start,\n pointerCapture: false,\n shouldStart: (event) => {\n if (event.pointerType !== 'touch' || session) return false;\n const target = eventTarget(event);\n pendingSource = target ? resolveDragTarget(target) : null;\n return pendingSource !== null;\n },\n signal: disposable.disposalSignal,\n });\n gestures.set(element, gesture);\n\n return () => {\n gesture.dispose();\n gestures.delete(element);\n if (pendingSource && element.contains(pendingSource)) pendingSource = null;\n if (session && element.contains(session.source)) resetSession();\n };\n },\n });\n}\n"],"mappings":"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"}