{"version":3,"sources":["../../../../src/elements/useDropZone/core.ts"],"sourcesContent":["import { observable, type Observable } from \"@legendapp/state\";\nimport { type DeepMaybeObservable } from \"@usels/core\";\nimport type { MaybeEventTarget } from \"../../types\";\nimport { createEventListener } from \"../../browser/useEventListener/core\";\nimport { defaultNavigator } from \"@shared/configurable\";\n\nexport interface UseDropZoneOptions {\n  dataTypes?: string[] | ((types: readonly string[]) => boolean);\n  checkValidity?: (items: DataTransferItemList) => boolean;\n  onDrop?: (files: File[] | null, event: DragEvent) => void;\n  onEnter?: (files: File[] | null, event: DragEvent) => void;\n  onLeave?: (files: File[] | null, event: DragEvent) => void;\n  onOver?: (files: File[] | null, event: DragEvent) => void;\n  multiple?: boolean;\n  preventDefaultForUnhandled?: boolean;\n}\n\nexport interface UseDropZoneReturn {\n  files$: Observable<File[] | null>;\n  isOverDropZone$: Observable<boolean>;\n}\n\n/**\n * Framework-agnostic file drop zone.\n *\n * Registers `dragenter` / `dragleave` / `dragover` / `drop` listeners on the\n * target via `createEventListener`, tracks drag-over state, and validates file\n * types before accepting drops. Must be called inside a `useScope` factory.\n */\nexport function createDropZone(\n  target: MaybeEventTarget,\n  options?: DeepMaybeObservable<UseDropZoneOptions>\n): UseDropZoneReturn {\n  const opts$ = observable(options);\n\n  const files$ = observable<File[] | null>(null);\n  const isOver$ = observable<boolean>(false);\n\n  // Drag counter tracks enter/leave pairs so nested children do not flip state.\n  let counter = 0;\n\n  const isValidDrop = (event: DragEvent): boolean => {\n    const items = event.dataTransfer?.items;\n    if (!items) return false;\n\n    // Safari detection: UA contains 'Safari' but not 'Chrome' (SSR guard required).\n    const isSafari =\n      defaultNavigator != null && /^((?!chrome|android).)*safari/i.test(defaultNavigator.userAgent);\n    if (isSafari) return true;\n\n    const o = opts$.peek();\n    const checkValidity = o?.checkValidity;\n    if (checkValidity) return checkValidity(items);\n\n    const dataTypes = o?.dataTypes;\n    if (dataTypes) {\n      const types = Array.from(items).map((i) => i.type);\n      if (typeof dataTypes === \"function\") return dataTypes(types);\n      return dataTypes.some((t) => types.includes(t));\n    }\n    return true;\n  };\n\n  const getFiles = (event: DragEvent): File[] | null => {\n    const fileList = Array.from(event.dataTransfer?.files ?? []);\n    if (!fileList.length) return null;\n    const multiple = opts$.peek()?.multiple;\n    return multiple === false ? [fileList[0]] : fileList;\n  };\n\n  const onDragEnter = (e: DragEvent) => {\n    e.preventDefault();\n    counter++;\n    const valid = isValidDrop(e);\n    if (valid) {\n      e.dataTransfer!.dropEffect = \"copy\";\n      isOver$.set(true);\n      opts$.peek()?.onEnter?.(getFiles(e), e);\n    } else {\n      e.dataTransfer!.dropEffect = \"none\";\n    }\n  };\n\n  const onDragLeave = (e: DragEvent) => {\n    counter = Math.max(0, counter - 1);\n    if (counter === 0) {\n      isOver$.set(false);\n      opts$.peek()?.onLeave?.(null, e);\n    }\n  };\n\n  const onDragOver = (e: DragEvent) => {\n    e.preventDefault();\n    if (isValidDrop(e)) {\n      e.dataTransfer!.dropEffect = \"copy\";\n      opts$.peek()?.onOver?.(getFiles(e), e);\n    } else {\n      e.dataTransfer!.dropEffect = \"none\";\n    }\n  };\n\n  const onDrop = (e: DragEvent) => {\n    e.preventDefault();\n    counter = 0;\n    isOver$.set(false);\n    if (isValidDrop(e)) {\n      const droppedFiles = getFiles(e);\n      files$.set(droppedFiles);\n      opts$.peek()?.onDrop?.(droppedFiles, e);\n    } else {\n      files$.set(null);\n      opts$.peek()?.onDrop?.(null, e);\n    }\n  };\n\n  createEventListener(target, \"dragenter\", onDragEnter);\n  createEventListener(target, \"dragleave\", onDragLeave);\n  createEventListener(target, \"dragover\", onDragOver);\n  createEventListener(target, \"drop\", onDrop);\n\n  return { files$, isOverDropZone$: isOver$ };\n}\n"],"mappings":"AAAA,SAAS,kBAAmC;AAG5C,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAyB1B,SAAS,eACd,QACA,SACmB;AACnB,QAAM,QAAQ,WAAW,OAAO;AAEhC,QAAM,SAAS,WAA0B,IAAI;AAC7C,QAAM,UAAU,WAAoB,KAAK;AAGzC,MAAI,UAAU;AAEd,QAAM,cAAc,CAAC,UAA8B;AACjD,UAAM,QAAQ,MAAM,cAAc;AAClC,QAAI,CAAC,MAAO,QAAO;AAGnB,UAAM,WACJ,oBAAoB,QAAQ,iCAAiC,KAAK,iBAAiB,SAAS;AAC9F,QAAI,SAAU,QAAO;AAErB,UAAM,IAAI,MAAM,KAAK;AACrB,UAAM,gBAAgB,GAAG;AACzB,QAAI,cAAe,QAAO,cAAc,KAAK;AAE7C,UAAM,YAAY,GAAG;AACrB,QAAI,WAAW;AACb,YAAM,QAAQ,MAAM,KAAK,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AACjD,UAAI,OAAO,cAAc,WAAY,QAAO,UAAU,KAAK;AAC3D,aAAO,UAAU,KAAK,CAAC,MAAM,MAAM,SAAS,CAAC,CAAC;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,CAAC,UAAoC;AACpD,UAAM,WAAW,MAAM,KAAK,MAAM,cAAc,SAAS,CAAC,CAAC;AAC3D,QAAI,CAAC,SAAS,OAAQ,QAAO;AAC7B,UAAM,WAAW,MAAM,KAAK,GAAG;AAC/B,WAAO,aAAa,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI;AAAA,EAC9C;AAEA,QAAM,cAAc,CAAC,MAAiB;AACpC,MAAE,eAAe;AACjB;AACA,UAAM,QAAQ,YAAY,CAAC;AAC3B,QAAI,OAAO;AACT,QAAE,aAAc,aAAa;AAC7B,cAAQ,IAAI,IAAI;AAChB,YAAM,KAAK,GAAG,UAAU,SAAS,CAAC,GAAG,CAAC;AAAA,IACxC,OAAO;AACL,QAAE,aAAc,aAAa;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,cAAc,CAAC,MAAiB;AACpC,cAAU,KAAK,IAAI,GAAG,UAAU,CAAC;AACjC,QAAI,YAAY,GAAG;AACjB,cAAQ,IAAI,KAAK;AACjB,YAAM,KAAK,GAAG,UAAU,MAAM,CAAC;AAAA,IACjC;AAAA,EACF;AAEA,QAAM,aAAa,CAAC,MAAiB;AACnC,MAAE,eAAe;AACjB,QAAI,YAAY,CAAC,GAAG;AAClB,QAAE,aAAc,aAAa;AAC7B,YAAM,KAAK,GAAG,SAAS,SAAS,CAAC,GAAG,CAAC;AAAA,IACvC,OAAO;AACL,QAAE,aAAc,aAAa;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,SAAS,CAAC,MAAiB;AAC/B,MAAE,eAAe;AACjB,cAAU;AACV,YAAQ,IAAI,KAAK;AACjB,QAAI,YAAY,CAAC,GAAG;AAClB,YAAM,eAAe,SAAS,CAAC;AAC/B,aAAO,IAAI,YAAY;AACvB,YAAM,KAAK,GAAG,SAAS,cAAc,CAAC;AAAA,IACxC,OAAO;AACL,aAAO,IAAI,IAAI;AACf,YAAM,KAAK,GAAG,SAAS,MAAM,CAAC;AAAA,IAChC;AAAA,EACF;AAEA,sBAAoB,QAAQ,aAAa,WAAW;AACpD,sBAAoB,QAAQ,aAAa,WAAW;AACpD,sBAAoB,QAAQ,YAAY,UAAU;AAClD,sBAAoB,QAAQ,QAAQ,MAAM;AAE1C,SAAO,EAAE,QAAQ,iBAAiB,QAAQ;AAC5C;","names":[]}