{"version":3,"sources":["../../../../src/elements/useDraggable/core.ts"],"sourcesContent":["import { batch, observable, type Observable } from \"@legendapp/state\";\nimport { peek, type DeepMaybeObservable } from \"@usels/core\";\nimport { resolveWindowSource, type ConfigurableWindow } from \"@shared/configurable\";\nimport type { MaybeEventTarget } from \"../../types\";\nimport { createEventListener } from \"../../browser/useEventListener/core\";\n\nexport interface Position {\n  x: number;\n  y: number;\n}\n\nexport interface UseDraggableOptions extends ConfigurableWindow {\n  /** Only start drag if pointerdown target exactly matches the element. Default: false */\n  exact?: boolean;\n  /** Call preventDefault on pointer events. Default: false */\n  preventDefault?: boolean;\n  /** Call stopPropagation on pointer events. Default: false */\n  stopPropagation?: boolean;\n  /** Use capture phase for pointerdown. Default: false */\n  capture?: boolean;\n  /** Restrict drag to a specific handle element */\n  handle?: MaybeEventTarget;\n  /** Clamp drag position inside this container element */\n  containerElement?: MaybeEventTarget;\n  /** Initial position. Read once at mount time. Default: { x: 0, y: 0 } */\n  initialValue?: Position;\n  /** Called on drag start. Return false to cancel. */\n  onStart?: (position: Position, event: PointerEvent) => void | false;\n  /** Called on each drag move */\n  onMove?: (position: Position, event: PointerEvent) => void;\n  /** Called on drag end */\n  onEnd?: (position: Position, event: PointerEvent) => void;\n  /** Restrict movement axis. Default: 'both' */\n  axis?: \"x\" | \"y\" | \"both\";\n  /** Disable dragging */\n  disabled?: boolean;\n  /** Filter by pointer type. Default: all types allowed */\n  pointerTypes?: Array<\"mouse\" | \"pen\" | \"touch\">;\n  /** Clamp drag position inside the viewport. Default: false */\n  restrictInView?: boolean;\n}\n\nexport interface UseDraggableReturn {\n  /** Current X position */\n  x$: Observable<number>;\n  /** Current Y position */\n  y$: Observable<number>;\n  /** Current position as { x, y } */\n  position$: Observable<Position>;\n  /** Whether currently dragging */\n  isDragging$: Observable<boolean>;\n  /** CSS position string: \"left: Xpx; top: Ypx;\" */\n  style$: Observable<string>;\n}\n\n/**\n * Framework-agnostic draggable Pointer-Events wrapper.\n *\n * Must be called inside a `useScope` factory — pointer listeners are\n * registered via `createEventListener` and cleaned up automatically when the\n * scope disposes. Public behavior matches the legacy `useDraggable` hook.\n *\n * @param target - Element to make draggable (`Ref$`, `Observable<Element|null>`, raw Element, etc.).\n * @param options - Configuration (supports `DeepMaybeObservable` — each field may be an Observable).\n */\nexport function createDraggable(\n  target: MaybeEventTarget,\n  options?: DeepMaybeObservable<UseDraggableOptions>\n): UseDraggableReturn {\n  const opts$ = observable(options) as unknown as Observable<UseDraggableOptions>;\n  const win$ = resolveWindowSource(opts$.window as unknown as Observable<unknown>);\n\n  // Mount-time-only: initial position.\n  const initial = opts$.peek()?.initialValue ?? { x: 0, y: 0 };\n\n  const x$ = observable<number>(initial.x);\n  const y$ = observable<number>(initial.y);\n  const isDragging$ = observable<boolean>(false);\n\n  const position$ = observable<Position>(() => ({ x: x$.get(), y: y$.get() }));\n  const style$ = observable<string>(() => `left: ${x$.get()}px; top: ${y$.get()}px;`);\n\n  // Offset between pointer and element top-left at drag start.\n  let pressedDelta: Position | null = null;\n\n  const onPointerDown = (e: PointerEvent) => {\n    const o = opts$.peek();\n    if (o?.disabled) return;\n\n    const el = peek(target) as HTMLElement | null;\n    if (!el) return;\n    if (o?.exact && e.target !== el) return;\n\n    const pointerTypes = o?.pointerTypes;\n    if (pointerTypes && !pointerTypes.includes(e.pointerType as \"mouse\" | \"pen\" | \"touch\")) return;\n\n    if (o?.preventDefault) e.preventDefault();\n    if (o?.stopPropagation) e.stopPropagation();\n\n    const rect = el.getBoundingClientRect();\n    const pos: Position = { x: rect.left, y: rect.top };\n\n    if (o?.onStart?.(pos, e) === false) return;\n\n    // Store (mouseX - cssLeft) so that onPointerMove gives: mouseX - delta = cssLeft + mouseDelta.\n    pressedDelta = {\n      x: e.clientX - x$.peek(),\n      y: e.clientY - y$.peek(),\n    };\n    isDragging$.set(true);\n  };\n\n  const onPointerMove = (e: PointerEvent) => {\n    if (!pressedDelta) return;\n    const o = opts$.peek();\n    if (o?.preventDefault) e.preventDefault();\n\n    let x = e.clientX - pressedDelta.x;\n    let y = e.clientY - pressedDelta.y;\n\n    // Axis restriction.\n    const axis = o?.axis;\n    if (axis === \"x\") y = y$.peek();\n    if (axis === \"y\") x = x$.peek();\n\n    // Container boundary clamping.\n    const container = o?.containerElement as unknown as HTMLElement | null | undefined;\n    if (container) {\n      const containerRect = container.getBoundingClientRect();\n      const elRect = (peek(target) as HTMLElement | null)?.getBoundingClientRect();\n      if (elRect) {\n        x = Math.max(containerRect.left, Math.min(x, containerRect.right - elRect.width));\n        y = Math.max(containerRect.top, Math.min(y, containerRect.bottom - elRect.height));\n      }\n    }\n\n    // Viewport restriction.\n    const win = win$.peek() as Window | null;\n    if (o?.restrictInView && win) {\n      const elRect = (peek(target) as HTMLElement | null)?.getBoundingClientRect();\n      if (elRect) {\n        x = Math.max(0, Math.min(x, win.innerWidth - elRect.width));\n        y = Math.max(0, Math.min(y, win.innerHeight - elRect.height));\n      }\n    }\n\n    batch(() => {\n      x$.set(x);\n      y$.set(y);\n    });\n    o?.onMove?.({ x, y }, e);\n  };\n\n  const onPointerUp = (e: PointerEvent) => {\n    if (!pressedDelta) return;\n    pressedDelta = null;\n    const pos: Position = { x: x$.peek(), y: y$.peek() };\n    isDragging$.set(false);\n    opts$.peek()?.onEnd?.(pos, e);\n  };\n\n  // Determine pointerdown target: handle if specified (mount-time), otherwise target.\n  // When handle is provided as a reactive source (Ref$ / Observable), `opts$.handle`\n  // tracks its changes so `createEventListener` re-binds automatically.\n  const handlePeek = opts$.peek()?.handle;\n  const pointerdownTarget: MaybeEventTarget =\n    handlePeek != null ? (opts$.handle as unknown as MaybeEventTarget) : target;\n\n  createEventListener(pointerdownTarget, \"pointerdown\", onPointerDown, {\n    capture: opts$.peek()?.capture ?? false,\n  });\n\n  createEventListener(win$ as unknown as Observable<unknown>, \"pointermove\", onPointerMove);\n  createEventListener(win$ as unknown as Observable<unknown>, \"pointerup\", onPointerUp);\n\n  return {\n    x$,\n    y$,\n    position$,\n    isDragging$,\n    style$,\n  };\n}\n"],"mappings":"AAAA,SAAS,OAAO,kBAAmC;AACnD,SAAS,YAAsC;AAC/C,SAAS,2BAAoD;AAE7D,SAAS,2BAA2B;AA6D7B,SAAS,gBACd,QACA,SACoB;AACpB,QAAM,QAAQ,WAAW,OAAO;AAChC,QAAM,OAAO,oBAAoB,MAAM,MAAwC;AAG/E,QAAM,UAAU,MAAM,KAAK,GAAG,gBAAgB,EAAE,GAAG,GAAG,GAAG,EAAE;AAE3D,QAAM,KAAK,WAAmB,QAAQ,CAAC;AACvC,QAAM,KAAK,WAAmB,QAAQ,CAAC;AACvC,QAAM,cAAc,WAAoB,KAAK;AAE7C,QAAM,YAAY,WAAqB,OAAO,EAAE,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,EAAE,EAAE;AAC3E,QAAM,SAAS,WAAmB,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK;AAGlF,MAAI,eAAgC;AAEpC,QAAM,gBAAgB,CAAC,MAAoB;AACzC,UAAM,IAAI,MAAM,KAAK;AACrB,QAAI,GAAG,SAAU;AAEjB,UAAM,KAAK,KAAK,MAAM;AACtB,QAAI,CAAC,GAAI;AACT,QAAI,GAAG,SAAS,EAAE,WAAW,GAAI;AAEjC,UAAM,eAAe,GAAG;AACxB,QAAI,gBAAgB,CAAC,aAAa,SAAS,EAAE,WAAwC,EAAG;AAExF,QAAI,GAAG,eAAgB,GAAE,eAAe;AACxC,QAAI,GAAG,gBAAiB,GAAE,gBAAgB;AAE1C,UAAM,OAAO,GAAG,sBAAsB;AACtC,UAAM,MAAgB,EAAE,GAAG,KAAK,MAAM,GAAG,KAAK,IAAI;AAElD,QAAI,GAAG,UAAU,KAAK,CAAC,MAAM,MAAO;AAGpC,mBAAe;AAAA,MACb,GAAG,EAAE,UAAU,GAAG,KAAK;AAAA,MACvB,GAAG,EAAE,UAAU,GAAG,KAAK;AAAA,IACzB;AACA,gBAAY,IAAI,IAAI;AAAA,EACtB;AAEA,QAAM,gBAAgB,CAAC,MAAoB;AACzC,QAAI,CAAC,aAAc;AACnB,UAAM,IAAI,MAAM,KAAK;AACrB,QAAI,GAAG,eAAgB,GAAE,eAAe;AAExC,QAAI,IAAI,EAAE,UAAU,aAAa;AACjC,QAAI,IAAI,EAAE,UAAU,aAAa;AAGjC,UAAM,OAAO,GAAG;AAChB,QAAI,SAAS,IAAK,KAAI,GAAG,KAAK;AAC9B,QAAI,SAAS,IAAK,KAAI,GAAG,KAAK;AAG9B,UAAM,YAAY,GAAG;AACrB,QAAI,WAAW;AACb,YAAM,gBAAgB,UAAU,sBAAsB;AACtD,YAAM,SAAU,KAAK,MAAM,GAA0B,sBAAsB;AAC3E,UAAI,QAAQ;AACV,YAAI,KAAK,IAAI,cAAc,MAAM,KAAK,IAAI,GAAG,cAAc,QAAQ,OAAO,KAAK,CAAC;AAChF,YAAI,KAAK,IAAI,cAAc,KAAK,KAAK,IAAI,GAAG,cAAc,SAAS,OAAO,MAAM,CAAC;AAAA,MACnF;AAAA,IACF;AAGA,UAAM,MAAM,KAAK,KAAK;AACtB,QAAI,GAAG,kBAAkB,KAAK;AAC5B,YAAM,SAAU,KAAK,MAAM,GAA0B,sBAAsB;AAC3E,UAAI,QAAQ;AACV,YAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,IAAI,aAAa,OAAO,KAAK,CAAC;AAC1D,YAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,IAAI,cAAc,OAAO,MAAM,CAAC;AAAA,MAC9D;AAAA,IACF;AAEA,UAAM,MAAM;AACV,SAAG,IAAI,CAAC;AACR,SAAG,IAAI,CAAC;AAAA,IACV,CAAC;AACD,OAAG,SAAS,EAAE,GAAG,EAAE,GAAG,CAAC;AAAA,EACzB;AAEA,QAAM,cAAc,CAAC,MAAoB;AACvC,QAAI,CAAC,aAAc;AACnB,mBAAe;AACf,UAAM,MAAgB,EAAE,GAAG,GAAG,KAAK,GAAG,GAAG,GAAG,KAAK,EAAE;AACnD,gBAAY,IAAI,KAAK;AACrB,UAAM,KAAK,GAAG,QAAQ,KAAK,CAAC;AAAA,EAC9B;AAKA,QAAM,aAAa,MAAM,KAAK,GAAG;AACjC,QAAM,oBACJ,cAAc,OAAQ,MAAM,SAAyC;AAEvE,sBAAoB,mBAAmB,eAAe,eAAe;AAAA,IACnE,SAAS,MAAM,KAAK,GAAG,WAAW;AAAA,EACpC,CAAC;AAED,sBAAoB,MAAwC,eAAe,aAAa;AACxF,sBAAoB,MAAwC,aAAa,WAAW;AAEpF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}