uplofile 2.2.0 → 2.2.1

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
@@ -7,11 +7,11 @@ Accessible, unstyled primitives for building your own upload UI — with drag‑
7
7
 
8
8
  ## Features
9
9
 
10
- - React 16+ compatible
11
- - Drag‑and‑drop or click‑to‑upload
12
- - Upload progress, plus cancel/retry/remove actions
10
+ - React 16+ compatible
11
+ - Drag‑and‑drop or click‑to‑upload
12
+ - Upload progress, plus cancel/retry/remove actions
13
13
  - **Custom Validation:** Use `beforeUpload` to validate files before they start uploading
14
- - Hidden input for form submissions
14
+ - Hidden input for form submissions
15
15
  - Unstyled — bring your own design
16
16
 
17
17
  ---
@@ -36,25 +36,25 @@ Import and use the components in your React component:
36
36
  "use client";
37
37
 
38
38
  import {
39
- UplofileDropzone,
40
- UplofilePreview,
41
- UplofileRoot,
42
- UplofileTrigger,
39
+ UplofileDropzone,
40
+ UplofilePreview,
41
+ UplofileRoot,
42
+ UplofileTrigger,
43
43
  } from "uplofile";
44
44
 
45
45
  export default function Basic() {
46
46
  return (
47
- <UplofileRoot onRemove={onRemove} upload={upload} removeMode={"strict"}>
48
- <UplofileDropzone className={"border p-2 rounded"}>
49
- <span>Drop your files here or</span>{" "}
50
- <UplofileTrigger className={"underline text-blue-500"}>
51
- Select file
52
- </UplofileTrigger>
53
- <div className={"border-t my-6 py-6"}>
54
- <UplofilePreview />
55
- </div>
56
- </UplofileDropzone>
57
- </UplofileRoot>
47
+ <UplofileRoot onRemove={onRemove} upload={upload} removeMode={"strict"}>
48
+ <UplofileDropzone className={"border p-2 rounded"}>
49
+ <span>Drop your files here or</span>{" "}
50
+ <UplofileTrigger className={"underline text-blue-500"}>
51
+ Select file
52
+ </UplofileTrigger>
53
+ <div className={"border-t my-6 py-6"}>
54
+ <UplofilePreview />
55
+ </div>
56
+ </UplofileDropzone>
57
+ </UplofileRoot>
58
58
  );
59
59
  }
60
60
  ```
package/dist/index.d.ts CHANGED
@@ -29,6 +29,7 @@ type UploadResult<TMeta = any> = {
29
29
  type UplofileRootRef<TMeta = any> = {
30
30
  setItems: (items: UploadFileItem<TMeta>[] | ((prev: UploadFileItem<TMeta>[]) => UploadFileItem<TMeta>[])) => void;
31
31
  getItems: () => UploadFileItem<TMeta>[];
32
+ isLoading: boolean;
32
33
  onDrop: (e: DragEvent) => void;
33
34
  onDragOver: (e: DragEvent) => void;
34
35
  openFileDialog: () => void;
@@ -66,6 +67,7 @@ type ItemActions = {
66
67
  type ImageUploaderContextValue<TMeta = any> = {
67
68
  items: UploadFileItem<TMeta>[];
68
69
  setItems: (items: UploadFileItem<TMeta>[]) => void;
70
+ isLoading: boolean;
69
71
  disabled?: boolean;
70
72
  multiple: boolean;
71
73
  accept: string;
@@ -92,6 +94,7 @@ type ImageUploaderContextValue<TMeta = any> = {
92
94
  };
93
95
  type TriggerRenderProps<TMeta = any> = {
94
96
  items: UploadFileItem<TMeta>[];
97
+ isLoading: boolean;
95
98
  isUploading: boolean;
96
99
  uploadingCount: number;
97
100
  doneCount: number;
@@ -101,6 +104,7 @@ type TriggerRenderProps<TMeta = any> = {
101
104
  };
102
105
  type PreviewRenderProps<TMeta = any> = {
103
106
  items: UploadFileItem<TMeta>[];
107
+ isLoading: boolean;
104
108
  setItems: (items: UploadFileItem<TMeta>[]) => void;
105
109
  actions: ItemActions;
106
110
  };
package/dist/index.mjs CHANGED
@@ -117,6 +117,7 @@ const acceptsFile = (file, accept)=>{
117
117
  const UploaderCtx = /*#__PURE__*/ createContext(null);
118
118
  const Root = /*#__PURE__*/ forwardRef(({ multiple = true, initial = [], onChange, upload, removeMode = "optimistic", onRemove, accept = "image/*", beforeUpload, name = "image", maxCount, disabled, children }, ref)=>{
119
119
  const [items, setItems] = useState([]);
120
+ const [isLoading, setIsLoading] = useState(Array.isArray(initial) ? initial.length > 0 : !!initial);
120
121
  const controllers = useRef(new Map());
121
122
  const removeControllers = useRef(new Map());
122
123
  const inputRef = useRef(null);
@@ -125,21 +126,25 @@ const Root = /*#__PURE__*/ forwardRef(({ multiple = true, initial = [], onChange
125
126
  useEffect(()=>{
126
127
  if (hasHydratedInitialRef.current) return;
127
128
  const hydrate = async ()=>{
128
- const arr = await initial;
129
- if (!Array.isArray(arr) || arr.length === 0) return;
130
- const mapped = arr.map((it)=>{
131
- return {
132
- uid: it.uid || it.id,
133
- id: it.id,
134
- name: it.name,
135
- url: it.url,
136
- status: "done",
137
- meta: it.meta
138
- };
139
- });
140
- // Only hydrate if the user hasn't already added/modified items locally
141
- setItems((prev)=>prev.length === 0 ? mapped : prev);
142
- hasHydratedInitialRef.current = true;
129
+ try {
130
+ const arr = await initial;
131
+ if (!Array.isArray(arr) || arr.length === 0) return;
132
+ const mapped = arr.map((it)=>{
133
+ return {
134
+ uid: it.uid || it.id,
135
+ id: it.id,
136
+ name: it.name,
137
+ url: it.url,
138
+ status: "done",
139
+ meta: it.meta
140
+ };
141
+ });
142
+ // Only hydrate if the user hasn't already added/modified items locally
143
+ setItems((prev)=>prev.length === 0 ? mapped : prev);
144
+ hasHydratedInitialRef.current = true;
145
+ } finally{
146
+ setIsLoading(false);
147
+ }
143
148
  };
144
149
  void hydrate();
145
150
  }, [
@@ -377,6 +382,7 @@ const Root = /*#__PURE__*/ forwardRef(({ multiple = true, initial = [], onChange
377
382
  }, []);
378
383
  const ctx = {
379
384
  items,
385
+ isLoading,
380
386
  disabled,
381
387
  multiple,
382
388
  accept,
@@ -409,6 +415,7 @@ const Root = /*#__PURE__*/ forwardRef(({ multiple = true, initial = [], onChange
409
415
  useImperativeHandle(ref, ()=>({
410
416
  setItems: emitChange,
411
417
  getItems: ()=>items,
418
+ isLoading,
412
419
  onDrop,
413
420
  onDragOver,
414
421
  openFileDialog: ()=>inputRef.current?.click(),
@@ -416,6 +423,7 @@ const Root = /*#__PURE__*/ forwardRef(({ multiple = true, initial = [], onChange
416
423
  }), [
417
424
  emitChange,
418
425
  items,
426
+ isLoading,
419
427
  onDrop,
420
428
  onDragOver,
421
429
  actions
@@ -453,11 +461,12 @@ const Dropzone = ({ asChild, ...rest })=>{
453
461
  };
454
462
 
455
463
  const Preview = ({ render })=>{
456
- const { items, actions, setItems } = useUplofile();
464
+ const { items, actions, setItems, isLoading } = useUplofile();
457
465
  if (render && typeof render === "function") return render({
458
466
  items,
459
467
  setItems,
460
- actions
468
+ actions,
469
+ isLoading
461
470
  });
462
471
  if (items.length === 0) return null;
463
472
  return /*#__PURE__*/ jsx("div", {
@@ -765,7 +774,7 @@ const Remove = ({ uid, asChild, ...rest })=>{
765
774
  };
766
775
 
767
776
  const Trigger = ({ asChild, children, render, ...rest })=>{
768
- const { openFileDialog, disabled, items } = useUplofile();
777
+ const { openFileDialog, disabled, items, isLoading } = useUplofile();
769
778
  const Comp = asChild ? Slot : "button";
770
779
  const uploading = items.filter((i)=>i.status === "uploading");
771
780
  const uploadingCount = uploading.length;
@@ -774,6 +783,7 @@ const Trigger = ({ asChild, children, render, ...rest })=>{
774
783
  const totalProgress = uploadingCount ? Math.round(uploading.reduce((acc, it)=>acc + (typeof it.progress === "number" ? it.progress : 0), 0) / uploadingCount) : undefined;
775
784
  const api = {
776
785
  items,
786
+ isLoading,
777
787
  isUploading: uploadingCount > 0,
778
788
  uploadingCount,
779
789
  doneCount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uplofile",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Composable file‑upload components for React.",
5
5
  "license": "MIT",
6
6
  "author": "Chris Josh <KristofaJosh>",
@@ -41,7 +41,8 @@
41
41
  "clean": "rm -rf dist",
42
42
  "prepare": "pnpm run build",
43
43
  "prepublishOnly": "pnpm run build",
44
- "test": "vitest"
44
+ "test": "vitest",
45
+ "format": "prettier --write ."
45
46
  },
46
47
  "dependencies": {
47
48
  "@radix-ui/react-slot": "1.1.0"