@uploadcn/react 0.0.0-stage → 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/config.tsx","../src/use-upload-selector.ts","../src/context.ts","../src/messages.ts","../src/render.ts","../src/use-dropzone.ts","../src/use-file-preview.ts","../src/use-uploader.ts","../src/primitives.tsx","../src/use-upload.ts","../src/use-window-drop.ts","../src/use-paste-files.ts","../src/use-file-validation.ts","../src/use-image.ts","../src/use-network.ts","../src/index.ts"],"sourcesContent":["import type { UploadAdapter, UploaderOptions } from \"@uploadcn/core\"\nimport * as React from \"react\"\n\nimport type { UploadMessages } from \"./messages\"\n\n/**\n * App-wide defaults for every uploader below the provider: where files go\n * (`adapter`), limits, retries and messages. Props on a component win.\n */\nexport interface UploadConfig extends Partial<\n Omit<UploaderOptions<unknown>, \"adapter\">\n> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- results are app-defined\n adapter?: UploadAdapter<any>\n messages?: Partial<UploadMessages>\n}\n\nconst UploadConfigContext = React.createContext<UploadConfig>({})\n\n/**\n * Configure uploads once — usually in your root layout — and every Upload\n * component and block uses it. Nest providers to override per section.\n *\n * @example\n * <UploadConfigProvider adapter={cloudinaryAdapter({ cloudName, uploadPreset })}>\n * {children}\n * </UploadConfigProvider>\n */\nexport function UploadConfigProvider({\n children,\n ...config\n}: UploadConfig & { children?: React.ReactNode }) {\n const parent = React.useContext(UploadConfigContext)\n const value = { ...parent, ...stripUndefined(config) }\n return (\n <UploadConfigContext.Provider value={value}>\n {children}\n </UploadConfigContext.Provider>\n )\n}\n\n/** The merged config from the nearest `UploadConfigProvider`s. */\nexport function useUploadConfig(): UploadConfig {\n return React.useContext(UploadConfigContext)\n}\n\nexport function stripUndefined<T extends object>(value: T): Partial<T> {\n return Object.fromEntries(\n Object.entries(value).filter(([, entry]) => entry !== undefined)\n ) as Partial<T>\n}\n","import type { Uploader, UploaderState } from \"@uploadcn/core\"\nimport * as React from \"react\"\n\n/**\n * Subscribes to a slice of uploader state. The component re-renders only\n * when the selected value changes (by `isEqual`, default `Object.is`).\n */\nexport function useUploadSelector<TResult, TSelected>(\n uploader: Uploader<TResult>,\n selector: (state: UploaderState<TResult>) => TSelected,\n isEqual: (a: TSelected, b: TSelected) => boolean = Object.is\n): TSelected {\n const cache = React.useRef<{\n state: UploaderState<TResult>\n selector: (state: UploaderState<TResult>) => TSelected\n selected: TSelected\n } | null>(null)\n\n const getSnapshot = () => {\n const state = uploader.getState()\n const previous = cache.current\n if (\n previous &&\n previous.state === state &&\n previous.selector === selector\n ) {\n return previous.selected\n }\n const selected = selector(state)\n // Keep the previous reference when equal so consumers skip re-rendering.\n const stable =\n previous && isEqual(previous.selected, selected)\n ? previous.selected\n : selected\n cache.current = { state, selector, selected: stable }\n return stable\n }\n\n return React.useSyncExternalStore(\n uploader.subscribe,\n getSnapshot,\n getSnapshot\n )\n}\n\n/** Shallow array equality, for selectors that return lists. */\nexport function shallowArrayEqual<T>(a: readonly T[], b: readonly T[]) {\n return (\n a.length === b.length &&\n a.every((value, index) => Object.is(value, b[index]))\n )\n}\n","import type { UploadItem, Uploader, ValidationOptions } from \"@uploadcn/core\"\nimport * as React from \"react\"\n\nimport type { UploadMessages } from \"./messages\"\nimport { useUploadSelector } from \"./use-upload-selector\"\n\nexport interface UploadContextValue {\n uploader: Uploader<unknown>\n /** Opens the native file picker. */\n openFileDialog: () => void\n /** Adds files through the same path as the picker and drops. */\n addFiles: (files: File[]) => void\n accept: ValidationOptions[\"accept\"]\n multiple: boolean\n disabled: boolean\n messages: UploadMessages\n inputId: string\n}\n\nexport const UploadContext = React.createContext<UploadContextValue | null>(\n null\n)\n\nexport function useUploadContext(): UploadContextValue {\n const context = React.useContext(UploadContext)\n if (!context) {\n throw new Error(\n \"Upload components must be rendered inside <Upload> (Upload.Root)\"\n )\n }\n return context\n}\n\nexport const UploadItemContext = React.createContext<UploadItem | null>(null)\n\n/**\n * The current item. Inside `<UploadItem>` it reads from context; pass an id\n * to subscribe to any item from within `<Upload>`.\n */\nexport function useUploadItem<TResult = unknown>(\n id?: string\n): UploadItem<TResult> {\n const fromContext = React.useContext(UploadItemContext)\n const upload = React.useContext(UploadContext)\n const byId = useOptionalItem(upload?.uploader ?? null, id)\n const item = id ? byId : fromContext\n if (!item) {\n throw new Error(\n id\n ? `No upload item with id \"${id}\"`\n : \"useUploadItem() must be used inside <UploadItem> or given an id\"\n )\n }\n return item as UploadItem<TResult>\n}\n\nconst emptyState = { items: [], online: true } as const\nconst noop = () => () => {}\nconst nullUploader = {\n getState: () => emptyState,\n subscribe: noop,\n getItem: () => undefined,\n} as unknown as Uploader<unknown>\n\nfunction useOptionalItem(\n uploader: Uploader<unknown> | null,\n id: string | undefined\n) {\n const source = uploader ?? nullUploader\n return useUploadSelector(source, () => (id ? source.getItem(id) : undefined))\n}\n","import type { UploadItem, UploadStatus } from \"@uploadcn/core\"\n\n/** Every user-facing string, so primitives can be translated. */\nexport interface UploadMessages {\n status: Record<UploadStatus, string>\n retrying: string\n waitingForNetwork: string\n /** Live-region announcements. */\n added: (count: number) => string\n uploaded: (name: string) => string\n failed: (name: string, reason: string) => string\n rejected: (name: string, reason: string) => string\n cancelled: (name: string) => string\n completed: (count: number) => string\n /** Accessible labels for item actions. */\n remove: (name: string) => string\n cancel: (name: string) => string\n retry: (name: string) => string\n pause: (name: string) => string\n resume: (name: string) => string\n progress: (name: string) => string\n totalProgress: string\n}\n\nexport const defaultMessages: UploadMessages = {\n status: {\n idle: \"Ready\",\n validating: \"Checking\",\n queued: \"Queued\",\n uploading: \"Uploading\",\n paused: \"Paused\",\n processing: \"Processing\",\n scanning: \"Scanning\",\n success: \"Uploaded\",\n error: \"Failed\",\n cancelled: \"Cancelled\",\n rejected: \"Rejected\",\n },\n retrying: \"Retrying\",\n waitingForNetwork: \"Waiting for network\",\n added: (count) => `${count} file${count === 1 ? \"\" : \"s\"} added`,\n uploaded: (name) => `${name} uploaded`,\n failed: (name, reason) => `${name} failed: ${reason}`,\n rejected: (name, reason) => `${name} rejected: ${reason}`,\n cancelled: (name) => `${name} cancelled`,\n completed: (count) => `${count} upload${count === 1 ? \"\" : \"s\"} finished`,\n remove: (name) => `Remove ${name}`,\n cancel: (name) => `Cancel upload of ${name}`,\n retry: (name) => `Retry upload of ${name}`,\n pause: (name) => `Pause upload of ${name}`,\n resume: (name) => `Resume upload of ${name}`,\n progress: (name) => `Upload progress for ${name}`,\n totalProgress: \"Total upload progress\",\n}\n\n/** A status label that also explains automatic retries and offline pauses. */\nexport function getStatusLabel(\n item: Pick<UploadItem, \"status\" | \"retryAt\" | \"pauseReason\">,\n messages: UploadMessages = defaultMessages\n) {\n if (item.status === \"queued\" && item.retryAt) return messages.retrying\n if (item.status === \"paused\" && item.pauseReason === \"offline\") {\n return messages.waitingForNetwork\n }\n return messages.status[item.status]\n}\n","import * as React from \"react\"\n\n/**\n * Replace a primitive's element, like Base UI's `render` prop:\n * pass an element to clone (`render={<Button />}`) or a function that\n * receives the merged props and the component state.\n */\nexport type RenderProp<TState> =\n | React.ReactElement<Record<string, unknown>>\n | ((props: Record<string, unknown>, state: TState) => React.ReactElement)\n\ntype AnyProps = Record<string, unknown>\n\nfunction mergeRefs(...refs: unknown[]) {\n const live = refs.filter(Boolean)\n if (live.length <= 1) return live[0]\n return (value: unknown) => {\n const cleanups: (() => void)[] = []\n for (const ref of live) {\n if (typeof ref === \"function\") {\n const cleanup = ref(value)\n if (typeof cleanup === \"function\") cleanups.push(cleanup)\n } else if (ref && typeof ref === \"object\" && \"current\" in ref) {\n ;(ref as { current: unknown }).current = value\n }\n }\n return () => {\n for (const cleanup of cleanups) cleanup()\n for (const ref of live) {\n if (ref && typeof ref === \"object\" && \"current\" in ref) {\n ;(ref as { current: unknown }).current = null\n }\n }\n }\n }\n}\n\n/**\n * Merges two prop objects: event handlers run in sequence (unless the first\n * calls `preventDefault`), classNames concatenate, styles shallow-merge,\n * refs combine, and everything else from `overrides` wins.\n */\nexport function mergeProps(base: AnyProps, overrides: AnyProps): AnyProps {\n const merged: AnyProps = { ...base }\n for (const [key, value] of Object.entries(overrides)) {\n const current = merged[key]\n if (\n /^on[A-Z]/.test(key) &&\n typeof current === \"function\" &&\n typeof value === \"function\"\n ) {\n merged[key] = (...args: unknown[]) => {\n ;(value as (...a: unknown[]) => void)(...args)\n const event = args[0] as { defaultPrevented?: boolean } | undefined\n if (!event?.defaultPrevented) {\n ;(current as (...a: unknown[]) => void)(...args)\n }\n }\n } else if (key === \"className\" && current && value) {\n merged[key] = `${current as string} ${value as string}`\n } else if (key === \"style\" && current && value) {\n merged[key] = { ...(current as object), ...(value as object) }\n } else if (key === \"ref\") {\n merged[key] = mergeRefs(current, value)\n } else if (value !== undefined) {\n merged[key] = value\n }\n }\n return merged\n}\n\n/** Renders `tag`, or the user's `render` element/function with merged props. */\nexport function renderElement<TState>(\n tag: keyof React.JSX.IntrinsicElements,\n props: AnyProps,\n render: RenderProp<TState> | undefined,\n state: TState\n): React.ReactElement {\n if (!render) return React.createElement(tag, props)\n if (typeof render === \"function\") return render(props, state)\n return React.cloneElement(render, mergeProps(props, render.props))\n}\n\n/** Converts truthy booleans to `\"\"` data attributes and drops falsy ones. */\nexport function dataAttributes(\n attributes: Record<string, string | number | boolean | null | undefined>\n) {\n const result: Record<string, string | number> = {}\n for (const [key, value] of Object.entries(attributes)) {\n if (value === true) result[`data-${key}`] = \"\"\n else if (value !== false && value != null) result[`data-${key}`] = value\n }\n return result\n}\n","import {\n type ValidationOptions,\n getDraggedTypes,\n getDroppedFiles,\n isFileDrag,\n matchesAccept,\n} from \"@uploadcn/core\"\nimport * as React from \"react\"\n\nexport interface UseDropzoneOptions {\n /** Receives every dropped file, with folders expanded. */\n onDrop: (files: File[]) => void\n /** Used to flag drags of unsupported types before they are dropped. */\n accept?: ValidationOptions[\"accept\"]\n /** Reject drags carrying more than one file. */\n multiple?: boolean\n disabled?: boolean\n}\n\nexport interface DropzoneState {\n /** Files are being dragged over the zone. */\n isDragging: boolean\n /** The dragged files don't match `accept` (or `multiple`). */\n isDragReject: boolean\n}\n\nexport interface DropzoneHandlers {\n onDragEnter: React.DragEventHandler\n onDragOver: React.DragEventHandler\n onDragLeave: React.DragEventHandler\n onDrop: React.DragEventHandler\n}\n\n/**\n * Drag-and-drop state and handlers for any element. Uses an enter/leave\n * counter so moving over child elements doesn't flicker the drag state.\n */\nexport function useDropzone(options: UseDropzoneOptions) {\n const { onDrop, accept, multiple = true, disabled = false } = options\n const [state, setState] = React.useState<DropzoneState>({\n isDragging: false,\n isDragReject: false,\n })\n const depth = React.useRef(0)\n\n const evaluate = React.useCallback(\n (dataTransfer: DataTransfer) => {\n const types = getDraggedTypes(dataTransfer)\n const tooMany = !multiple && types.length > 1\n // Some browsers report empty types while dragging; don't guess.\n const badType = types.some(\n (type) => type !== \"\" && !matchesAccept({ name: \"\", type }, accept)\n )\n return tooMany || badType\n },\n [accept, multiple]\n )\n\n const handlers = React.useMemo<DropzoneHandlers>(\n () => ({\n onDragEnter(event) {\n if (disabled || !isFileDrag(event.dataTransfer)) return\n event.preventDefault()\n depth.current++\n setState({\n isDragging: true,\n isDragReject: evaluate(event.dataTransfer),\n })\n },\n onDragOver(event) {\n if (disabled || !isFileDrag(event.dataTransfer)) return\n event.preventDefault()\n event.dataTransfer.dropEffect = \"copy\"\n },\n onDragLeave(event) {\n if (disabled || !isFileDrag(event.dataTransfer)) return\n depth.current = Math.max(0, depth.current - 1)\n if (depth.current === 0)\n setState({ isDragging: false, isDragReject: false })\n },\n onDrop(event) {\n if (disabled || !isFileDrag(event.dataTransfer)) return\n // Mark as handled so window-level drop zones ignore it.\n event.preventDefault()\n depth.current = 0\n setState({ isDragging: false, isDragReject: false })\n void getDroppedFiles(event.dataTransfer).then((files) => {\n if (files.length) onDrop(multiple ? files : files.slice(0, 1))\n })\n },\n }),\n [disabled, evaluate, multiple, onDrop]\n )\n\n return { ...state, handlers }\n}\n","import { isPreviewableImage } from \"@uploadcn/core\"\nimport * as React from \"react\"\n\nexport type PreviewKind = \"image\" | \"video\" | \"audio\" | \"pdf\" | \"file\"\n\nexport function getPreviewKind(file: {\n type: string\n name: string\n}): PreviewKind {\n if (isPreviewableImage(file)) return \"image\"\n if (file.type.startsWith(\"video/\")) return \"video\"\n if (file.type.startsWith(\"audio/\")) return \"audio\"\n if (\n file.type === \"application/pdf\" ||\n file.name.toLowerCase().endsWith(\".pdf\")\n ) {\n return \"pdf\"\n }\n return \"file\"\n}\n\nexport interface UseFilePreviewOptions {\n /**\n * For images, render a downscaled thumbnail no larger than this many\n * pixels instead of decoding the full image — keeps large galleries fast.\n */\n thumbnailSize?: number\n}\n\nasync function createThumbnail(file: Blob, size: number): Promise<Blob | null> {\n if (\n typeof createImageBitmap !== \"function\" ||\n file.type === \"image/svg+xml\"\n ) {\n return null\n }\n const bitmap = await createImageBitmap(file, {\n imageOrientation: \"from-image\",\n })\n try {\n const scale = Math.min(1, size / Math.max(bitmap.width, bitmap.height))\n if (scale === 1) return null\n const width = Math.round(bitmap.width * scale)\n const height = Math.round(bitmap.height * scale)\n const canvas =\n typeof OffscreenCanvas !== \"undefined\"\n ? new OffscreenCanvas(width, height)\n : Object.assign(document.createElement(\"canvas\"), { width, height })\n const context = canvas.getContext(\"2d\") as\n OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null\n if (!context) return null\n context.drawImage(bitmap, 0, 0, width, height)\n if (\"convertToBlob\" in canvas) {\n return canvas.convertToBlob({ type: \"image/webp\", quality: 0.8 })\n }\n return new Promise((resolve) => canvas.toBlob(resolve, \"image/webp\", 0.8))\n } finally {\n bitmap.close()\n }\n}\n\n/**\n * An object URL for previewing a file, revoked automatically when the file\n * changes or the component unmounts.\n */\nexport function useFilePreview(\n file: File | Blob | null | undefined,\n { thumbnailSize }: UseFilePreviewOptions = {}\n): string | null {\n const [preview, setPreview] = React.useState<{\n source: Blob\n url: string\n } | null>(null)\n\n React.useEffect(() => {\n if (!file) return\n let cancelled = false\n let objectUrl: string | null = null\n const source = file\n\n const publish = (blob: Blob) => {\n if (cancelled) return\n objectUrl = URL.createObjectURL(blob)\n setPreview({ source, url: objectUrl })\n }\n const thumbnail =\n thumbnailSize && file.type.startsWith(\"image/\")\n ? createThumbnail(file, thumbnailSize).catch(() => null)\n : Promise.resolve(null)\n void thumbnail.then((blob) => publish(blob ?? source))\n\n return () => {\n cancelled = true\n if (objectUrl) URL.revokeObjectURL(objectUrl)\n }\n }, [file, thumbnailSize])\n\n // A stale URL (for a previous file) is never returned.\n return preview && preview.source === file ? preview.url : null\n}\n","import {\n type UploadAdapter,\n type UploadError,\n type UploadItem,\n type Uploader,\n type UploaderOptions,\n createUploader,\n} from \"@uploadcn/core\"\nimport * as React from \"react\"\n\nimport { stripUndefined, useUploadConfig } from \"./config\"\n\nexport interface UploaderCallbacks<TResult> {\n onAdd?: (items: UploadItem<TResult>[]) => void\n onReject?: (item: UploadItem<TResult>) => void\n onSuccess?: (item: UploadItem<TResult>) => void\n onError?: (item: UploadItem<TResult>, error: UploadError) => void\n /** Called when the queue drains after uploads finished. */\n onComplete?: (items: UploadItem<TResult>[]) => void\n}\n\nexport interface UseUploaderOptions<TResult>\n extends\n Omit<UploaderOptions<TResult>, \"adapter\">,\n UploaderCallbacks<TResult> {\n /**\n * Where files go. Defaults to the adapter from the nearest\n * `UploadConfigProvider`.\n */\n adapter?: UploadAdapter<TResult>\n /** Restore persisted uploads on mount (requires `persistence`). */\n restore?: boolean\n}\n\nfunction splitOptions<TResult>(options: UseUploaderOptions<TResult>) {\n const {\n onAdd,\n onReject,\n onSuccess,\n onError,\n onComplete,\n restore,\n ...engine\n } = options\n return {\n callbacks: { onAdd, onReject, onSuccess, onError, onComplete },\n restore,\n engine,\n }\n}\n\nconst NO_ADAPTER =\n \"UploadCN: no adapter. Pass `adapter` to the component or useUploader, \" +\n \"or wrap your app in <UploadConfigProvider adapter={…}>. \" +\n \"See https://uploadcn.dev/docs/storage\"\n\n/**\n * Creates an upload engine bound to the component's lifetime. The instance\n * is stable; option changes (including a new adapter) apply on the next\n * render without recreating it.\n */\nexport function useUploader<TResult = unknown>(\n options: UseUploaderOptions<TResult> = {}\n): Uploader<TResult> {\n const { messages: _messages, ...defaults } = useUploadConfig()\n const { callbacks, restore, engine: own } = splitOptions(options)\n const engine = {\n ...defaults,\n ...stripUndefined(own),\n } as UploaderOptions<TResult>\n if (!engine.adapter) throw new Error(NO_ADAPTER)\n const [uploader] = React.useState(() => createUploader<TResult>(engine))\n\n const callbacksRef = React.useRef(callbacks)\n React.useEffect(() => {\n callbacksRef.current = callbacks\n uploader.setOptions(engine)\n })\n\n React.useEffect(() => {\n const unsubscribers = [\n uploader.on(\"add\", ({ items }) => callbacksRef.current.onAdd?.(items)),\n uploader.on(\"reject\", ({ item }) =>\n callbacksRef.current.onReject?.(item)\n ),\n uploader.on(\"success\", ({ item }) =>\n callbacksRef.current.onSuccess?.(item)\n ),\n uploader.on(\"error\", ({ item, error }) =>\n callbacksRef.current.onError?.(item, error)\n ),\n uploader.on(\"complete\", ({ items }) =>\n callbacksRef.current.onComplete?.(items)\n ),\n ]\n const unmount = uploader.mount()\n return () => {\n for (const unsubscribe of unsubscribers) unsubscribe()\n unmount()\n }\n }, [uploader])\n\n React.useEffect(() => {\n if (restore) void uploader.restore()\n }, [uploader, restore])\n\n return uploader\n}\n","import {\n type UploadItem,\n type Uploader,\n formatBytes,\n formatDuration,\n formatSpeed,\n getUploadSummary,\n isActiveStatus,\n toAcceptAttribute,\n} from \"@uploadcn/core\"\nimport * as React from \"react\"\n\nimport { useUploadConfig } from \"./config\"\n\nimport {\n UploadContext,\n type UploadContextValue,\n UploadItemContext,\n useUploadContext,\n useUploadItem,\n} from \"./context\"\nimport {\n type UploadMessages,\n defaultMessages,\n getStatusLabel,\n} from \"./messages\"\nimport {\n type RenderProp,\n dataAttributes,\n mergeProps,\n renderElement,\n} from \"./render\"\nimport { useDropzone } from \"./use-dropzone\"\nimport { getPreviewKind, useFilePreview } from \"./use-file-preview\"\nimport { shallowArrayEqual, useUploadSelector } from \"./use-upload-selector\"\nimport { type UseUploaderOptions, useUploader } from \"./use-uploader\"\n\ntype ElementProps<\n TTag extends keyof React.JSX.IntrinsicElements,\n TState,\n> = Omit<React.ComponentPropsWithRef<TTag>, \"children\"> & {\n render?: RenderProp<TState>\n children?: React.ReactNode\n}\n\nconst visuallyHidden: React.CSSProperties = {\n position: \"absolute\",\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: \"hidden\",\n clip: \"rect(0, 0, 0, 0)\",\n whiteSpace: \"nowrap\",\n border: 0,\n}\n\n// ── Root ────────────────────────────────────────────────────────────────────\n\nconst OPTION_KEYS = [\n \"adapter\",\n \"autoUpload\",\n \"concurrency\",\n \"retry\",\n \"duplicates\",\n \"transform\",\n \"process\",\n \"persistence\",\n \"network\",\n \"progressInterval\",\n \"accept\",\n \"maxSize\",\n \"minSize\",\n \"maxFiles\",\n \"minFiles\",\n \"image\",\n \"media\",\n \"validate\",\n \"restore\",\n \"onAdd\",\n \"onReject\",\n \"onSuccess\",\n \"onError\",\n \"onComplete\",\n] as const satisfies readonly (keyof UseUploaderOptions<unknown>)[]\n\nexport interface UploadRootState {\n disabled: boolean\n isUploading: boolean\n}\n\ninterface UploadRootOwnProps<TResult> {\n /** Allow selecting several files. Default `true` (unless `maxFiles` is 1). */\n multiple?: boolean\n disabled?: boolean\n /**\n * Renders hidden inputs with the values of successful uploads, so the\n * component works in plain `<form>`s and server actions.\n */\n name?: string\n /** Value submitted per successful upload. Defaults to `result.key`, `result.url`, or the file name. */\n getFormValue?: (item: UploadItem<TResult>) => string\n /**\n * What the form submits under `name`: `\"value\"` (default) submits a value\n * per successful upload; `\"file\"` submits the selected files themselves,\n * so the component works as a plain file input (no upload needed).\n */\n submit?: \"value\" | \"file\"\n /** Opens the camera on mobile (`<input capture>`). */\n capture?: \"user\" | \"environment\"\n /** Let users pick whole folders. */\n directory?: boolean\n /** Translate or customize every string. */\n messages?: Partial<UploadMessages>\n render?: RenderProp<UploadRootState>\n children?: React.ReactNode\n}\n\ntype DivProps = Omit<React.ComponentPropsWithRef<\"div\">, \"onError\" | \"children\">\n\nexport type UploadRootProps<TResult = unknown> = DivProps &\n UploadRootOwnProps<TResult> &\n (\n | ({ uploader: Uploader<TResult> } & {\n [K in (typeof OPTION_KEYS)[number]]?: never\n })\n | ({ uploader?: undefined } & UseUploaderOptions<TResult>)\n )\n\nconst SUBMITTED = new Set([\"idle\", \"queued\", \"uploading\", \"paused\", \"success\"])\n\n/** A hidden file input that mirrors the queue, for native form submission. */\nfunction FormFiles({\n name,\n items,\n}: {\n name: string\n items: readonly UploadItem[]\n}) {\n const ref = React.useRef<HTMLInputElement>(null)\n React.useEffect(() => {\n const input = ref.current\n if (!input || typeof DataTransfer === \"undefined\") return\n const transfer = new DataTransfer()\n for (const item of items) {\n if (SUBMITTED.has(item.status)) transfer.items.add(item.file)\n }\n input.files = transfer.files\n }, [items])\n return (\n <input\n ref={ref}\n type=\"file\"\n name={name}\n multiple\n hidden\n tabIndex={-1}\n aria-hidden\n />\n )\n}\n\nfunction defaultFormValue(item: UploadItem): string {\n const result = item.result as\n { key?: unknown; url?: unknown } | null | undefined\n if (result && typeof result.key === \"string\") return result.key\n if (result && typeof result.url === \"string\") return result.url\n return item.name\n}\n\n/**\n * The root of an upload UI. Pass uploader options directly\n * (`<Upload adapter={…} maxFiles={5}>`) or an `uploader` from `useUploader`\n * to control it from outside.\n */\nexport function UploadRoot<TResult = unknown>(props: UploadRootProps<TResult>) {\n if (props.uploader) {\n return <UploadRootView {...(props as UploadRootViewProps<TResult>)} />\n }\n return <UploadRootWithUploader {...props} />\n}\n\nfunction UploadRootWithUploader<TResult>(props: UploadRootProps<TResult>) {\n const options: Record<string, unknown> = {}\n const rest: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(props)) {\n if ((OPTION_KEYS as readonly string[]).includes(key)) options[key] = value\n else rest[key] = value\n }\n const uploader = useUploader(\n options as unknown as UseUploaderOptions<TResult>\n )\n return (\n <UploadRootView\n {...(rest as Omit<UploadRootViewProps<TResult>, \"uploader\">)}\n uploader={uploader}\n />\n )\n}\n\ntype UploadRootViewProps<TResult> = DivProps &\n UploadRootOwnProps<TResult> & { uploader: Uploader<TResult> }\n\nfunction UploadRootView<TResult>({\n uploader: typedUploader,\n multiple: multipleProp,\n disabled = false,\n name,\n getFormValue = defaultFormValue as (item: UploadItem<TResult>) => string,\n submit = \"value\",\n capture,\n directory,\n messages: messagesProp,\n render,\n children,\n ...props\n}: UploadRootViewProps<TResult>) {\n const uploader = typedUploader as unknown as Uploader<unknown>\n const inputRef = React.useRef<HTMLInputElement>(null)\n const inputId = React.useId()\n const { accept, maxFiles } = uploader.getOptions()\n const multiple = multipleProp ?? maxFiles !== 1\n\n const configMessages = useUploadConfig().messages\n const messages = React.useMemo(\n () => ({\n ...defaultMessages,\n ...configMessages,\n ...messagesProp,\n status: {\n ...defaultMessages.status,\n ...configMessages?.status,\n ...messagesProp?.status,\n },\n }),\n [configMessages, messagesProp]\n )\n\n const items = useUploadSelector(uploader, (state) => state.items)\n const isUploading = React.useMemo(\n () => getUploadSummary(items).isUploading,\n [items]\n )\n\n const addFiles = React.useCallback(\n (files: File[]) => {\n if (disabled || files.length === 0) return\n if (!multiple) {\n // Single-file mode replaces the current file (avatars, documents).\n uploader.remove()\n void uploader.add(files.slice(0, 1))\n return\n }\n void uploader.add(files)\n },\n [disabled, multiple, uploader]\n )\n\n const openFileDialog = React.useCallback(() => {\n if (!disabled) inputRef.current?.click()\n }, [disabled])\n\n const context = React.useMemo<UploadContextValue>(\n () => ({\n uploader,\n openFileDialog,\n addFiles,\n accept,\n multiple,\n disabled,\n messages,\n inputId,\n }),\n [\n uploader,\n openFileDialog,\n addFiles,\n accept,\n multiple,\n disabled,\n messages,\n inputId,\n ]\n )\n\n const state: UploadRootState = { disabled, isUploading }\n const rootProps = mergeProps(\n {\n ...dataAttributes({ disabled, uploading: isUploading }),\n \"aria-busy\": isUploading || undefined,\n },\n props\n )\n\n const directoryProps = directory ? { webkitdirectory: \"\", directory: \"\" } : {}\n\n return (\n <UploadContext.Provider value={context}>\n {renderElement(\n \"div\",\n {\n ...rootProps,\n children: (\n <>\n {children}\n <input\n ref={inputRef}\n id={inputId}\n type=\"file\"\n tabIndex={-1}\n aria-hidden\n style={visuallyHidden}\n accept={toAcceptAttribute(accept)}\n multiple={multiple}\n disabled={disabled}\n capture={capture}\n {...directoryProps}\n onChange={(event) => {\n addFiles(Array.from(event.currentTarget.files ?? []))\n // Reset so selecting the same file again fires `change`.\n event.currentTarget.value = \"\"\n }}\n />\n {name && submit === \"file\" ? (\n <FormFiles name={name} items={items} />\n ) : null}\n {name && submit === \"value\"\n ? items\n .filter((item) => item.status === \"success\")\n .map((item) => (\n <input\n key={item.id}\n type=\"hidden\"\n name={name}\n value={getFormValue(item as UploadItem<TResult>)}\n />\n ))\n : null}\n <UploadAnnouncer uploader={uploader} messages={messages} />\n </>\n ),\n },\n render,\n state\n )}\n </UploadContext.Provider>\n )\n}\n\nfunction UploadAnnouncer({\n uploader,\n messages,\n}: {\n uploader: Uploader<unknown>\n messages: UploadMessages\n}) {\n const [message, setMessage] = React.useState(\"\")\n const messagesRef = React.useRef(messages)\n React.useEffect(() => {\n messagesRef.current = messages\n })\n\n React.useEffect(() => {\n const m = () => messagesRef.current\n const unsubscribers = [\n uploader.on(\"add\", ({ items }) => setMessage(m().added(items.length))),\n uploader.on(\"success\", ({ item }) => setMessage(m().uploaded(item.name))),\n uploader.on(\"error\", ({ item, error }) =>\n setMessage(m().failed(item.name, error.message))\n ),\n uploader.on(\"reject\", ({ item }) =>\n setMessage(m().rejected(item.name, item.issues[0]?.message ?? \"\"))\n ),\n uploader.on(\"cancel\", ({ item }) => setMessage(m().cancelled(item.name))),\n ]\n return () => {\n for (const unsubscribe of unsubscribers) unsubscribe()\n }\n }, [uploader])\n\n return (\n <div role=\"status\" aria-live=\"polite\" aria-atomic style={visuallyHidden}>\n {message}\n </div>\n )\n}\n\n// ── Dropzone & trigger ──────────────────────────────────────────────────────\n\nexport interface UploadDropzoneState {\n isDragging: boolean\n isDragReject: boolean\n disabled: boolean\n}\n\nexport type UploadDropzoneProps = ElementProps<\"div\", UploadDropzoneState> & {\n /**\n * Clicking or pressing Enter/Space opens the file picker. Set `false` when\n * the dropzone contains its own `UploadTrigger`. Default `true`.\n */\n clickable?: boolean\n}\n\nconst INTERACTIVE =\n \"button, a, input, select, textarea, [role=button], [role=link]\"\n\nexport function UploadDropzone({\n render,\n clickable = true,\n ...props\n}: UploadDropzoneProps) {\n const { addFiles, accept, multiple, disabled, openFileDialog } =\n useUploadContext()\n const { isDragging, isDragReject, handlers } = useDropzone({\n onDrop: addFiles,\n accept,\n multiple,\n disabled,\n })\n const state = { isDragging, isDragReject, disabled }\n\n const interaction: Record<string, unknown> = clickable\n ? {\n role: \"button\",\n tabIndex: disabled ? -1 : 0,\n \"aria-disabled\": disabled || undefined,\n onClick(event: React.MouseEvent<HTMLElement>) {\n // Ignore clicks on nested controls (they handle themselves).\n const target = (event.target as HTMLElement).closest(INTERACTIVE)\n if (target && target !== event.currentTarget) return\n openFileDialog()\n },\n onKeyDown(event: React.KeyboardEvent<HTMLElement>) {\n if (event.target !== event.currentTarget) return\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault()\n openFileDialog()\n }\n },\n }\n : {}\n\n return renderElement(\n \"div\",\n mergeProps(\n {\n ...handlers,\n ...interaction,\n ...dataAttributes({\n dragging: isDragging,\n \"drag-reject\": isDragReject,\n disabled,\n }),\n },\n props\n ),\n render,\n state\n )\n}\n\nexport type UploadTriggerProps = ElementProps<\"button\", { disabled: boolean }>\n\n/** A button that opens the file picker. */\nexport function UploadTrigger({ render, ...props }: UploadTriggerProps) {\n const { openFileDialog, disabled, inputId } = useUploadContext()\n return renderElement(\n \"button\",\n mergeProps(\n {\n type: \"button\",\n disabled,\n \"aria-controls\": inputId,\n onClick: openFileDialog,\n },\n props\n ),\n render,\n { disabled }\n )\n}\n\n// ── List & items ────────────────────────────────────────────────────────────\n\nexport type UploadListProps = Omit<\n ElementProps<\"ul\", { count: number }>,\n \"children\"\n> & {\n /**\n * A template rendered once per item (`<UploadItem>…</UploadItem>`), or a\n * function of the item for full control.\n */\n children?: React.ReactNode | ((item: UploadItem) => React.ReactNode)\n /** Show only matching items (e.g. hide rejected ones). */\n filter?: (item: UploadItem) => boolean\n}\n\nconst ItemSlot = React.memo(function ItemSlot({\n id,\n template,\n}: {\n id: string\n template: React.ReactNode | ((item: UploadItem) => React.ReactNode)\n}) {\n const item = useUploadItem(id)\n return (\n <UploadItemContext.Provider value={item}>\n {typeof template === \"function\" ? template(item) : template}\n </UploadItemContext.Provider>\n )\n})\n\n/**\n * A list of uploads. Children are rendered once per item, with the item in\n * context; each item re-renders on its own progress without re-rendering\n * its siblings.\n */\nexport function UploadList({\n render,\n children,\n filter,\n ...props\n}: UploadListProps) {\n const { uploader } = useUploadContext()\n const ids = useUploadSelector(\n uploader,\n (state) =>\n (filter ? state.items.filter(filter) : state.items).map(\n (item) => item.id\n ),\n shallowArrayEqual\n )\n const content = ids.map((id) => (\n <ItemSlot key={id} id={id} template={children} />\n ))\n return renderElement(\n \"ul\",\n mergeProps(\n { role: \"list\", ...dataAttributes({ empty: ids.length === 0 }) },\n {\n ...props,\n children: content,\n }\n ),\n render,\n { count: ids.length }\n )\n}\n\nexport type UploadItemProps = ElementProps<\"li\", UploadItem> & {\n /** The item to render. Optional inside `UploadList`, which provides it. */\n item?: UploadItem\n}\n\n/** Provides an item to the item-level primitives inside it. */\nexport function UploadItemRoot({\n item: itemProp,\n render,\n ...props\n}: UploadItemProps) {\n const contextItem = React.useContext(UploadItemContext)\n const item = itemProp ?? contextItem\n if (!item) {\n throw new Error(\n \"UploadItem needs an `item` prop or must be rendered inside UploadList\"\n )\n }\n return (\n <UploadItemContext.Provider value={item}>\n {renderElement(\n \"li\",\n mergeProps(\n {\n ...dataAttributes({ status: item.status, restored: item.restored }),\n \"aria-busy\": isActiveStatus(item.status) || undefined,\n },\n props\n ),\n render,\n item\n )}\n </UploadItemContext.Provider>\n )\n}\n\nexport type UploadPreviewProps = Omit<\n ElementProps<\"img\", UploadItem>,\n \"src\"\n> & {\n /** Shown for files that can't be previewed as images. */\n fallback?: React.ReactNode\n /** Max thumbnail dimension in px. Default 256. */\n thumbnailSize?: number\n}\n\n/** An image thumbnail of the item, or `fallback` for other file types. */\nexport function UploadPreview({\n render,\n fallback = null,\n thumbnailSize = 256,\n alt = \"\",\n ...props\n}: UploadPreviewProps) {\n const item = useUploadItem()\n const isImage = getPreviewKind(item.file) === \"image\"\n const url = useFilePreview(isImage ? item.originalFile : null, {\n thumbnailSize,\n })\n if (!isImage) return <>{fallback}</>\n if (!url) return null\n return renderElement(\n \"img\",\n mergeProps({ src: url, alt, decoding: \"async\", draggable: false }, props),\n render,\n item\n )\n}\n\nexport function UploadName({\n render,\n ...props\n}: ElementProps<\"span\", UploadItem>) {\n const item = useUploadItem()\n return renderElement(\n \"span\",\n mergeProps({ title: item.name, children: item.name }, props),\n render,\n item\n )\n}\n\nexport type UploadSizeProps = ElementProps<\"span\", UploadItem> & {\n /** `\"total\"` shows the size; `\"progress\"` shows \"3 MB / 10 MB\" while uploading. */\n format?: \"total\" | \"progress\"\n}\n\nexport function UploadSize({\n render,\n format = \"progress\",\n ...props\n}: UploadSizeProps) {\n const item = useUploadItem()\n const showProgress =\n format === \"progress\" &&\n (item.status === \"uploading\" || item.status === \"paused\")\n const text = showProgress\n ? `${formatBytes(item.progress.loaded)} / ${formatBytes(item.size)}`\n : formatBytes(item.size)\n return renderElement(\n \"span\",\n mergeProps({ children: text }, props),\n render,\n item\n )\n}\n\n/** Upload speed and time remaining, while uploading. */\nexport function UploadSpeed({\n render,\n ...props\n}: ElementProps<\"span\", UploadItem>) {\n const item = useUploadItem()\n if (item.status !== \"uploading\" || item.progress.speed == null) return null\n const eta = formatDuration(item.progress.eta)\n const text = `${formatSpeed(item.progress.speed)}${eta ? ` · ${eta} left` : \"\"}`\n return renderElement(\n \"span\",\n mergeProps({ children: text }, props),\n render,\n item\n )\n}\n\nexport function UploadStatus({\n render,\n ...props\n}: ElementProps<\"span\", UploadItem>) {\n const item = useUploadItem()\n const { messages } = useUploadContext()\n return renderElement(\n \"span\",\n mergeProps(\n {\n ...dataAttributes({ status: item.status }),\n children: getStatusLabel(item, messages),\n },\n props\n ),\n render,\n item\n )\n}\n\n/** The item's error or validation message, when it has one. */\nexport function UploadError({\n render,\n ...props\n}: ElementProps<\"p\", UploadItem>) {\n const item = useUploadItem()\n if (!item.error) return null\n if (item.status !== \"error\" && item.status !== \"rejected\") return null\n const message = item.issues.length\n ? item.issues.map((issue) => issue.message).join(\". \")\n : item.error.message\n return renderElement(\n \"p\",\n mergeProps(\n { ...dataAttributes({ status: item.status }), children: message },\n props\n ),\n render,\n item\n )\n}\n\n/** Renders its children only once the item uploaded successfully. */\nexport function UploadSuccess({ children }: { children?: React.ReactNode }) {\n const item = useUploadItem()\n return item.status === \"success\" ? <>{children}</> : null\n}\n\n// ── Progress ────────────────────────────────────────────────────────────────\n\nexport interface UploadProgressState {\n percent: number\n indeterminate: boolean\n complete: boolean\n}\n\nconst ProgressContext = React.createContext<UploadProgressState | null>(null)\n\nexport type UploadProgressProps = ElementProps<\"div\", UploadProgressState>\n\n/**\n * An accessible progress bar. Inside `<UploadItem>` it tracks that item;\n * anywhere else it tracks the whole queue.\n */\nexport function UploadProgress(props: UploadProgressProps) {\n const item = React.useContext(UploadItemContext)\n return item ? (\n <ItemProgress item={item} {...props} />\n ) : (\n <QueueProgress {...props} />\n )\n}\n\nfunction ItemProgress({\n item,\n ...props\n}: UploadProgressProps & { item: UploadItem }) {\n const { messages } = useUploadContext()\n const done =\n item.status === \"success\" ||\n item.status === \"processing\" ||\n item.status === \"scanning\"\n const indeterminate =\n item.status === \"validating\" ||\n item.status === \"processing\" ||\n item.status === \"scanning\" ||\n (item.status === \"uploading\" && item.size === 0)\n return (\n <ProgressView\n {...props}\n percent={done ? 100 : item.progress.percent}\n indeterminate={indeterminate}\n complete={item.status === \"success\"}\n label={messages.progress(item.name)}\n status={item.status}\n />\n )\n}\n\nfunction QueueProgress(props: UploadProgressProps) {\n const { uploader, messages } = useUploadContext()\n const items = useUploadSelector(uploader, (state) => state.items)\n const summary = React.useMemo(() => getUploadSummary(items), [items])\n return (\n <ProgressView\n {...props}\n percent={summary.percent}\n indeterminate={false}\n complete={summary.isComplete}\n label={messages.totalProgress}\n />\n )\n}\n\nfunction ProgressView({\n render,\n percent,\n indeterminate,\n complete,\n label,\n status,\n ...props\n}: UploadProgressProps &\n UploadProgressState & { label: string; status?: string }) {\n const state = { percent, indeterminate, complete }\n return (\n <ProgressContext.Provider value={state}>\n {renderElement(\n \"div\",\n mergeProps(\n {\n role: \"progressbar\",\n \"aria-label\": label,\n \"aria-valuemin\": 0,\n \"aria-valuemax\": 100,\n \"aria-valuenow\": indeterminate ? undefined : Math.round(percent),\n \"aria-valuetext\": indeterminate\n ? undefined\n : `${Math.round(percent)}%`,\n style: {\n \"--upload-progress\": `${percent}%`,\n } as React.CSSProperties,\n ...dataAttributes({ indeterminate, complete, status }),\n },\n props\n ),\n render,\n state\n )}\n </ProgressContext.Provider>\n )\n}\n\n/** The filled part of `UploadProgress`. */\nexport function UploadProgressIndicator({\n render,\n ...props\n}: ElementProps<\"div\", UploadProgressState>) {\n const state = React.useContext(ProgressContext)\n if (!state)\n throw new Error(\"UploadProgressIndicator must be inside UploadProgress\")\n return renderElement(\n \"div\",\n mergeProps(\n {\n style: state.indeterminate\n ? undefined\n : { transform: `translateX(-${100 - state.percent}%)` },\n ...dataAttributes({\n indeterminate: state.indeterminate,\n complete: state.complete,\n }),\n },\n props\n ),\n render,\n state\n )\n}\n\n// ── Item actions ────────────────────────────────────────────────────────────\n\ntype ActionProps = ElementProps<\"button\", UploadItem> & {\n /** Render (disabled) even when the action doesn't apply. */\n forceMount?: boolean\n}\n\nfunction createItemAction(\n name: string,\n isAvailable: (item: UploadItem, uploader: Uploader<unknown>) => boolean,\n perform: (item: UploadItem, uploader: Uploader<unknown>) => void,\n label: (messages: UploadMessages, name: string) => string\n) {\n function Action({ render, forceMount = false, ...props }: ActionProps) {\n const item = useUploadItem()\n const { uploader, messages } = useUploadContext()\n const available = isAvailable(item, uploader)\n if (!available && !forceMount) return null\n return renderElement(\n \"button\",\n mergeProps(\n {\n type: \"button\",\n disabled: !available,\n \"aria-label\": label(messages, item.name),\n onClick: () => perform(item, uploader),\n },\n props\n ),\n render,\n item\n )\n }\n Action.displayName = name\n return Action\n}\n\nexport const UploadRemove = createItemAction(\n \"UploadRemove\",\n () => true,\n (item, uploader) => uploader.remove(item.id),\n (m, name) => m.remove(name)\n)\n\nexport const UploadCancel = createItemAction(\n \"UploadCancel\",\n (item) => isActiveStatus(item.status) || item.status === \"paused\",\n (item, uploader) => uploader.cancel(item.id),\n (m, name) => m.cancel(name)\n)\n\nexport const UploadRetry = createItemAction(\n \"UploadRetry\",\n (item) => item.status === \"error\" || item.status === \"cancelled\",\n (item, uploader) => uploader.retry(item.id),\n (m, name) => m.retry(name)\n)\n\nexport const UploadPause = createItemAction(\n \"UploadPause\",\n (item, uploader) => uploader.canPause(item),\n (item, uploader) => uploader.pause(item.id),\n (m, name) => m.pause(name)\n)\n\nexport const UploadResume = createItemAction(\n \"UploadResume\",\n (item) => item.status === \"paused\",\n (item, uploader) => uploader.resume(item.id),\n (m, name) => m.resume(name)\n)\n\n// ── Queue-level ─────────────────────────────────────────────────────────────\n\n/** Renders its children only while the queue is empty. */\nexport function UploadEmpty({ children }: { children?: React.ReactNode }) {\n const { uploader } = useUploadContext()\n const empty = useUploadSelector(uploader, (state) => state.items.length === 0)\n return empty ? <>{children}</> : null\n}\n\ntype QueueActionProps = ElementProps<\"button\", { count: number }> & {\n forceMount?: boolean\n}\n\n/** Starts idle uploads (when `autoUpload` is off). Hidden when there are none. */\nexport function UploadStart({\n render,\n forceMount = false,\n ...props\n}: QueueActionProps) {\n const { uploader, disabled } = useUploadContext()\n const count = useUploadSelector(\n uploader,\n (state) => state.items.filter((item) => item.status === \"idle\").length\n )\n if (count === 0 && !forceMount) return null\n return renderElement(\n \"button\",\n mergeProps(\n {\n type: \"button\",\n disabled: disabled || count === 0,\n onClick: () => uploader.start(),\n },\n props\n ),\n render,\n { count }\n )\n}\n\n/** Removes finished items (or every item with `all`). Hidden when there's nothing to clear. */\nexport function UploadClear({\n render,\n forceMount = false,\n all = false,\n ...props\n}: QueueActionProps & { all?: boolean }) {\n const { uploader } = useUploadContext()\n const count = useUploadSelector(uploader, (state) =>\n all\n ? state.items.length\n : state.items.filter((item) =>\n [\"success\", \"cancelled\", \"rejected\"].includes(item.status)\n ).length\n )\n if (count === 0 && !forceMount) return null\n return renderElement(\n \"button\",\n mergeProps(\n {\n type: \"button\",\n disabled: count === 0,\n onClick: () => (all ? uploader.remove() : uploader.clearCompleted()),\n },\n props\n ),\n render,\n { count }\n )\n}\n","import {\n type UploadItem,\n type UploadSummary,\n type Uploader,\n getUploadSummary,\n} from \"@uploadcn/core\"\nimport * as React from \"react\"\n\nimport { useUploadSelector } from \"./use-upload-selector\"\nimport { type UseUploaderOptions, useUploader } from \"./use-uploader\"\n\nexport interface UseUploadReturn<TResult> {\n uploader: Uploader<TResult>\n items: readonly UploadItem<TResult>[]\n summary: UploadSummary\n online: boolean\n add: Uploader<TResult>[\"add\"]\n start: Uploader<TResult>[\"start\"]\n pause: Uploader<TResult>[\"pause\"]\n resume: Uploader<TResult>[\"resume\"]\n cancel: Uploader<TResult>[\"cancel\"]\n retry: Uploader<TResult>[\"retry\"]\n remove: Uploader<TResult>[\"remove\"]\n clearCompleted: Uploader<TResult>[\"clearCompleted\"]\n update: Uploader<TResult>[\"update\"]\n}\n\n/**\n * The all-in-one hook: creates an uploader and returns its items, an\n * aggregate summary and bound actions.\n *\n * @example\n * const { items, add, summary } = useUpload({ adapter: s3Adapter({ endpoint: \"/api/upload\" }) })\n */\nexport function useUpload<TResult = unknown>(\n options: UseUploaderOptions<TResult>\n): UseUploadReturn<TResult> {\n const uploader = useUploader(options)\n return useUploadState(uploader)\n}\n\n/** Like `useUpload`, for an uploader created elsewhere (e.g. from context). */\nexport function useUploadState<TResult>(\n uploader: Uploader<TResult>\n): UseUploadReturn<TResult> {\n const state = useUploadSelector(uploader, (s) => s)\n const summary = React.useMemo(\n () => getUploadSummary(state.items),\n [state.items]\n )\n return React.useMemo(\n () => ({\n uploader,\n items: state.items,\n summary,\n online: state.online,\n add: uploader.add,\n start: uploader.start,\n pause: uploader.pause,\n resume: uploader.resume,\n cancel: uploader.cancel,\n retry: uploader.retry,\n remove: uploader.remove,\n clearCompleted: uploader.clearCompleted,\n update: uploader.update,\n }),\n [uploader, state, summary]\n )\n}\n\n/** Aggregate progress, speed, ETA and counts for the whole queue. */\nexport function useUploadProgress<TResult>(\n uploader: Uploader<TResult>\n): UploadSummary {\n const items = useUploadSelector(uploader, (state) => state.items)\n return React.useMemo(() => getUploadSummary(items), [items])\n}\n\n/**\n * Derives form values from successful uploads — e.g. object keys for a\n * server action or react-hook-form field. Pass a stable `getValue` (defined\n * outside the component or memoized) to keep the returned array stable.\n */\nexport function useUploadValue<TResult, TValue>(\n uploader: Uploader<TResult>,\n getValue: (item: UploadItem<TResult>) => TValue\n): TValue[] {\n const items = useUploadSelector(uploader, (state) => state.items)\n return React.useMemo(\n () =>\n items\n .filter((item) => item.status === \"success\")\n .map((item) => getValue(item)),\n [items, getValue]\n )\n}\n","import { getDroppedFiles, isFileDrag } from \"@uploadcn/core\"\nimport * as React from \"react\"\n\nexport interface UseWindowDropOptions {\n /** Called with files dropped anywhere on the page. */\n onDrop: (files: File[]) => void\n /** Turn detection off without unmounting. Default `true`. */\n enabled?: boolean\n}\n\n/**\n * Detects files dragged over the browser window — for \"drop anywhere\"\n * overlays. Drops already handled by an inner dropzone (which calls\n * `preventDefault`) are ignored, so the two can coexist.\n */\nexport function useWindowDrop({\n onDrop,\n enabled = true,\n}: UseWindowDropOptions) {\n const [isDragging, setIsDragging] = React.useState(false)\n const onDropRef = React.useRef(onDrop)\n React.useEffect(() => {\n onDropRef.current = onDrop\n })\n\n React.useEffect(() => {\n if (!enabled) return\n let depth = 0\n\n function onDragEnter(event: DragEvent) {\n if (!isFileDrag(event.dataTransfer)) return\n depth++\n setIsDragging(true)\n }\n function onDragOver(event: DragEvent) {\n if (!isFileDrag(event.dataTransfer)) return\n // Required for the drop event to fire.\n event.preventDefault()\n }\n function onDragLeave(event: DragEvent) {\n if (!isFileDrag(event.dataTransfer)) return\n depth = Math.max(0, depth - 1)\n // relatedTarget is null when the pointer leaves the window.\n if (depth === 0 || event.relatedTarget === null) {\n depth = 0\n setIsDragging(false)\n }\n }\n function onDropEvent(event: DragEvent) {\n depth = 0\n setIsDragging(false)\n if (!event.dataTransfer || !isFileDrag(event.dataTransfer)) return\n const handledByDropzone = event.defaultPrevented\n // Stop the browser from navigating to the file.\n event.preventDefault()\n if (handledByDropzone) return\n void getDroppedFiles(event.dataTransfer).then((files) => {\n if (files.length) onDropRef.current(files)\n })\n }\n function onBlur() {\n depth = 0\n setIsDragging(false)\n }\n\n window.addEventListener(\"dragenter\", onDragEnter)\n window.addEventListener(\"dragover\", onDragOver)\n window.addEventListener(\"dragleave\", onDragLeave)\n window.addEventListener(\"drop\", onDropEvent)\n window.addEventListener(\"blur\", onBlur)\n return () => {\n window.removeEventListener(\"dragenter\", onDragEnter)\n window.removeEventListener(\"dragover\", onDragOver)\n window.removeEventListener(\"dragleave\", onDragLeave)\n window.removeEventListener(\"drop\", onDropEvent)\n window.removeEventListener(\"blur\", onBlur)\n setIsDragging(false)\n }\n }, [enabled])\n\n return { isDragging: enabled && isDragging }\n}\n","import { getClipboardFiles } from \"@uploadcn/core\"\nimport * as React from \"react\"\n\nexport interface UsePasteFilesOptions {\n onPaste: (files: File[]) => void\n /**\n * Element to listen on. Defaults to the whole document, which is what chat\n * inputs usually want (paste a screenshot anywhere).\n */\n target?: React.RefObject<HTMLElement | null>\n enabled?: boolean\n}\n\n/**\n * Uploads files and screenshots pasted from the clipboard. Text pastes are\n * left alone so inputs keep working normally.\n */\nexport function usePasteFiles({\n onPaste,\n target,\n enabled = true,\n}: UsePasteFilesOptions) {\n const onPasteRef = React.useRef(onPaste)\n React.useEffect(() => {\n onPasteRef.current = onPaste\n })\n\n React.useEffect(() => {\n if (!enabled) return\n const element: HTMLElement | Document = target?.current ?? document\n function handlePaste(event: Event) {\n const files = getClipboardFiles((event as ClipboardEvent).clipboardData)\n if (files.length === 0) return\n event.preventDefault()\n onPasteRef.current(files)\n }\n element.addEventListener(\"paste\", handlePaste)\n return () => element.removeEventListener(\"paste\", handlePaste)\n }, [enabled, target])\n}\n","import {\n type ValidateFilesResult,\n type ValidationIssue,\n type ValidationOptions,\n validateFileCount,\n validateFiles,\n} from \"@uploadcn/core\"\nimport * as React from \"react\"\n\n/**\n * Standalone validation for flows that don't upload immediately — e.g.\n * collecting files in a form and submitting them later. Uses the same rules\n * as the uploader, including async and custom validators.\n */\nexport function useFileValidation(options: ValidationOptions) {\n const [isValidating, setIsValidating] = React.useState(false)\n const optionsRef = React.useRef(options)\n React.useEffect(() => {\n optionsRef.current = options\n })\n const pending = React.useRef<AbortController | null>(null)\n\n React.useEffect(() => () => pending.current?.abort(), [])\n\n const validate = React.useCallback(\n async (\n files: readonly File[],\n context: { existingCount?: number } = {}\n ): Promise<ValidateFilesResult> => {\n pending.current?.abort()\n const controller = new AbortController()\n pending.current = controller\n setIsValidating(true)\n try {\n return await validateFiles(files, optionsRef.current, {\n ...context,\n signal: controller.signal,\n })\n } finally {\n if (pending.current === controller) {\n pending.current = null\n setIsValidating(false)\n }\n }\n },\n []\n )\n\n const validateCount = React.useCallback(\n (count: number): ValidationIssue | null =>\n validateFileCount(count, optionsRef.current),\n []\n )\n\n return { validate, validateCount, isValidating }\n}\n","import {\n type CompressImageOptions,\n type CropArea,\n type ImageTransformOptions,\n compressImage,\n transformImage,\n} from \"@uploadcn/core/image\"\nimport * as React from \"react\"\n\nexport interface CompressionStats {\n before: number\n after: number\n /** Fraction saved, 0–1. */\n saved: number\n}\n\n/** Compresses images on demand and reports how much was saved. */\nexport function useImageCompression(options: CompressImageOptions = {}) {\n const [isCompressing, setIsCompressing] = React.useState(false)\n const [stats, setStats] = React.useState<CompressionStats | null>(null)\n const [error, setError] = React.useState<Error | null>(null)\n const optionsRef = React.useRef(options)\n React.useEffect(() => {\n optionsRef.current = options\n })\n\n const compress = React.useCallback(async (file: File) => {\n setIsCompressing(true)\n setError(null)\n try {\n const result = await compressImage(file, optionsRef.current)\n setStats({\n before: file.size,\n after: result.size,\n saved: file.size === 0 ? 0 : 1 - result.size / file.size,\n })\n return result\n } catch (cause) {\n const failure = cause instanceof Error ? cause : new Error(String(cause))\n setError(failure)\n throw failure\n } finally {\n setIsCompressing(false)\n }\n }, [])\n\n return { compress, isCompressing, stats, error }\n}\n\nexport interface ImageCropState {\n /** Pan offset, in the units of your crop UI. */\n position: { x: number; y: number }\n zoom: number\n /** Degrees, clockwise. */\n rotation: number\n flipHorizontal: boolean\n flipVertical: boolean\n /** Crop rectangle in source-image pixels, reported by the crop UI. */\n area: CropArea | null\n}\n\nconst INITIAL_CROP: ImageCropState = {\n position: { x: 0, y: 0 },\n zoom: 1,\n rotation: 0,\n flipHorizontal: false,\n flipVertical: false,\n area: null,\n}\n\nexport interface UseImageCropOptions {\n minZoom?: number\n maxZoom?: number\n}\n\n/**\n * UI-agnostic crop/rotate/zoom state with an `apply` that renders the\n * result. Pair it with any crop UI (react-easy-crop, a canvas, sliders).\n */\nexport function useImageCrop({\n minZoom = 1,\n maxZoom = 4,\n}: UseImageCropOptions = {}) {\n const [state, setState] = React.useState<ImageCropState>(INITIAL_CROP)\n\n const actions = React.useMemo(\n () => ({\n setPosition: (position: { x: number; y: number }) =>\n setState((s) => ({ ...s, position })),\n setZoom: (zoom: number) =>\n setState((s) => ({\n ...s,\n zoom: Math.min(maxZoom, Math.max(minZoom, zoom)),\n })),\n setRotation: (rotation: number) =>\n setState((s) => ({ ...s, rotation: ((rotation % 360) + 360) % 360 })),\n rotateBy: (degrees: number) =>\n setState((s) => ({\n ...s,\n rotation: (((s.rotation + degrees) % 360) + 360) % 360,\n })),\n flip: (axis: \"horizontal\" | \"vertical\") =>\n setState((s) =>\n axis === \"horizontal\"\n ? { ...s, flipHorizontal: !s.flipHorizontal }\n : { ...s, flipVertical: !s.flipVertical }\n ),\n setArea: (area: CropArea | null) => setState((s) => ({ ...s, area })),\n reset: () => setState(INITIAL_CROP),\n }),\n [minZoom, maxZoom]\n )\n\n const apply = React.useCallback(\n (\n file: File,\n options: Omit<ImageTransformOptions, \"crop\" | \"rotate\"> = {}\n ) =>\n transformImage(file, {\n ...options,\n crop: state.area ?? undefined,\n rotate: state.rotation,\n flipHorizontal: state.flipHorizontal,\n flipVertical: state.flipVertical,\n }),\n [state]\n )\n\n return { ...state, ...actions, minZoom, maxZoom, apply }\n}\n","import { type Uploader, getUploadSummary } from \"@uploadcn/core\"\nimport * as React from \"react\"\n\nimport { useUploadSelector } from \"./use-upload-selector\"\n\nfunction subscribe(callback: () => void) {\n window.addEventListener(\"online\", callback)\n window.addEventListener(\"offline\", callback)\n return () => {\n window.removeEventListener(\"online\", callback)\n window.removeEventListener(\"offline\", callback)\n }\n}\n\n/**\n * `navigator.onLine`, kept in sync with `online`/`offline` events. `true`\n * on the server. Note that \"online\" only means a network interface is up.\n */\nexport function useNetworkStatus(): boolean {\n return React.useSyncExternalStore(\n subscribe,\n () => navigator.onLine,\n () => true\n )\n}\n\nexport interface UseUploadGuardOptions {\n /** Default `true`. */\n enabled?: boolean\n}\n\n/**\n * Warns before closing or reloading the tab while uploads are in progress.\n * Browsers show their own generic message; custom text is not supported.\n */\nexport function useUploadGuard<TResult>(\n uploader: Uploader<TResult>,\n { enabled = true }: UseUploadGuardOptions = {}\n) {\n const isUploading = useUploadSelector(\n uploader,\n (state) => getUploadSummary(state.items).isUploading\n )\n\n React.useEffect(() => {\n if (!enabled || !isUploading) return\n function onBeforeUnload(event: BeforeUnloadEvent) {\n event.preventDefault()\n }\n window.addEventListener(\"beforeunload\", onBeforeUnload)\n return () => window.removeEventListener(\"beforeunload\", onBeforeUnload)\n }, [enabled, isUploading])\n\n return isUploading\n}\n","import {\n UploadCancel,\n UploadClear,\n UploadDropzone,\n UploadEmpty,\n UploadError,\n UploadItemRoot,\n UploadList,\n UploadName,\n UploadPause,\n UploadPreview,\n UploadProgress,\n UploadProgressIndicator,\n UploadRemove,\n UploadResume,\n UploadRetry,\n UploadRoot,\n UploadSize,\n UploadSpeed,\n UploadStart,\n UploadStatus,\n UploadSuccess,\n UploadTrigger,\n} from \"./primitives\"\n\n/**\n * Headless, accessible upload primitives. Style them directly or use the\n * shadcn-styled versions from the UploadCN registry.\n */\nexport const Upload = {\n Root: UploadRoot,\n Dropzone: UploadDropzone,\n Trigger: UploadTrigger,\n List: UploadList,\n Item: UploadItemRoot,\n Preview: UploadPreview,\n Name: UploadName,\n Size: UploadSize,\n Speed: UploadSpeed,\n Status: UploadStatus,\n Error: UploadError,\n Success: UploadSuccess,\n Progress: UploadProgress,\n ProgressIndicator: UploadProgressIndicator,\n Remove: UploadRemove,\n Cancel: UploadCancel,\n Retry: UploadRetry,\n Pause: UploadPause,\n Resume: UploadResume,\n Empty: UploadEmpty,\n Start: UploadStart,\n Clear: UploadClear,\n}\n\nexport {\n UploadCancel,\n UploadClear,\n UploadDropzone,\n UploadEmpty,\n UploadError,\n UploadItemRoot,\n UploadList,\n UploadName,\n UploadPause,\n UploadPreview,\n UploadProgress,\n UploadProgressIndicator,\n UploadRemove,\n UploadResume,\n UploadRetry,\n UploadRoot,\n UploadSize,\n UploadSpeed,\n UploadStart,\n UploadStatus,\n UploadSuccess,\n UploadTrigger,\n}\nexport type {\n UploadDropzoneProps,\n UploadDropzoneState,\n UploadItemProps,\n UploadListProps,\n UploadPreviewProps,\n UploadProgressProps,\n UploadProgressState,\n UploadRootProps,\n UploadRootState,\n UploadSizeProps,\n UploadTriggerProps,\n} from \"./primitives\"\n\nexport {\n UploadContext,\n UploadItemContext,\n useUploadContext,\n useUploadItem,\n type UploadContextValue,\n} from \"./context\"\nexport {\n defaultMessages,\n getStatusLabel,\n type UploadMessages,\n} from \"./messages\"\nexport { mergeProps, type RenderProp } from \"./render\"\n\nexport {\n useUploader,\n type UploaderCallbacks,\n type UseUploaderOptions,\n} from \"./use-uploader\"\nexport {\n useUpload,\n useUploadProgress,\n useUploadState,\n useUploadValue,\n type UseUploadReturn,\n} from \"./use-upload\"\nexport { shallowArrayEqual, useUploadSelector } from \"./use-upload-selector\"\nexport {\n useDropzone,\n type DropzoneHandlers,\n type DropzoneState,\n type UseDropzoneOptions,\n} from \"./use-dropzone\"\nexport { useWindowDrop, type UseWindowDropOptions } from \"./use-window-drop\"\nexport { usePasteFiles, type UsePasteFilesOptions } from \"./use-paste-files\"\nexport { useFileValidation } from \"./use-file-validation\"\nexport {\n useImageCompression,\n useImageCrop,\n type CompressionStats,\n type ImageCropState,\n type UseImageCropOptions,\n} from \"./use-image\"\nexport {\n getPreviewKind,\n useFilePreview,\n type PreviewKind,\n type UseFilePreviewOptions,\n} from \"./use-file-preview\"\nexport {\n useNetworkStatus,\n useUploadGuard,\n type UseUploadGuardOptions,\n} from \"./use-network\"\nexport {\n UploadConfigProvider,\n useUploadConfig,\n type UploadConfig,\n} from \"./config\"\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
2
  "name": "@uploadcn/react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "React hooks and headless, accessible primitives for UploadCN.",
5
+ "keywords": [
6
+ "react",
7
+ "upload",
8
+ "file-upload",
9
+ "dropzone",
10
+ "hooks",
11
+ "headless",
12
+ "shadcn"
13
+ ],
14
+ "license": "MIT",
15
+ "type": "module",
16
+ "sideEffects": false,
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/uploadcn/uploadcn.git",
20
+ "directory": "packages/react"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "exports": {
26
+ ".": "./dist/index.js",
27
+ "./package.json": "./package.json"
28
+ },
29
+ "types": "./dist/index.d.ts",
30
+ "dependencies": {
31
+ "@uploadcn/core": "^0.1.0"
32
+ },
33
+ "peerDependencies": {
34
+ "react": "^19.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@testing-library/dom": "^10.4.2",
38
+ "@testing-library/react": "^16.3.3",
39
+ "@types/react": "^19.3.0",
40
+ "@types/react-dom": "^19.3.0",
41
+ "eslint": "^10.12.0",
42
+ "happy-dom": "^20.14.5",
43
+ "react": "19.3.0",
44
+ "react-dom": "19.3.0",
45
+ "tsdown": "^0.23.0",
46
+ "typescript": "^5.9.3",
47
+ "vitest": "^5.0.3",
48
+ "@workspace/typescript-config": "0.0.0",
49
+ "@workspace/eslint-config": "0.0.0"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "scripts": {
55
+ "build": "tsdown",
56
+ "dev": "tsdown --watch",
57
+ "lint": "eslint .",
58
+ "typecheck": "tsc --noEmit",
59
+ "test": "vitest run",
60
+ "test:watch": "vitest"
61
+ }
6
62
  }