11import React , {
22 forwardRef ,
33 useCallback ,
4+ useEffect ,
45 useImperativeHandle ,
6+ useRef ,
57 useState ,
68} from "react" ;
79import { DropzoneOptions } from "react-dropzone" ;
@@ -13,6 +15,7 @@ import "react-photo-view/dist/react-photo-view.css";
1315import "./style.css" ;
1416import { PhotoProviderProps } from "react-photo-view/dist/PhotoProvider" ;
1517import { Dropzone } from "./Dropzone" ;
18+ import { use } from "motion/react-client" ;
1619
1720const RemoveIcon = ( ) => (
1821 < svg
@@ -49,13 +52,8 @@ const PreviewIcon = () => (
4952 </ svg >
5053) ;
5154
52- type ValueItem = {
53- url : string ;
54- name ?: string ;
55- } ;
56-
5755type ImageItem = {
58- id : string ;
56+ id ? : string ;
5957 url ?: string ;
6058 name ?: string ;
6159 file ?: File ;
@@ -69,7 +67,7 @@ export type ImageUploadProps = {
6967 height ?: number ;
7068 dropzoneOptions ?: DropzoneOptions ;
7169 photoProviderProps ?: Omit < PhotoProviderProps , "children" > ;
72- value ?: string | ValueItem | ( string | ValueItem ) [ ] ;
70+ value ?: string | ImageItem | ( string | ImageItem ) [ ] ;
7371 onChange ?: ( value : ImageItem [ ] ) => void ;
7472 max ?: number ;
7573 onUpload ?: ( file : File ) => string | Promise < string > ;
@@ -80,12 +78,7 @@ export type ImageUploadProps = {
8078 children ?: React . ReactNode ;
8179} ;
8280
83- export type ImageUploadRef = {
84- reset : ( value ?: string | ValueItem | ( string | ValueItem ) [ ] ) => void ;
85- value : ImageItem [ ] ;
86- } ;
87-
88- export const ImageUpload = forwardRef < ImageUploadRef , ImageUploadProps > (
81+ export const ImageUpload = forwardRef < HTMLElement , ImageUploadProps > (
8982 ( props , ref ) => {
9083 const {
9184 width = 100 ,
@@ -103,76 +96,69 @@ export const ImageUpload = forwardRef<ImageUploadRef, ImageUploadProps>(
10396 children,
10497 } = props ;
10598
106- const getImages = useCallback (
107- ( value : string | ValueItem | ( string | ValueItem ) [ ] ) => {
108- if ( ! Array . isArray ( value ) ) {
109- value = [ value ] ;
110- }
111-
112- return value
113- . map < ImageItem > ( ( item ) => {
114- if ( typeof item === "string" ) {
115- item = { url : item } ;
116- }
117- return {
118- id : uuidv7 ( ) ,
119- ...item ,
120- } ;
121- } )
122- . slice ( 0 , max ) ;
123- } ,
124- [ max ]
125- ) ;
99+ const dropzoneRef = useRef < HTMLElement > ( null ) ;
126100
127- const [ images , setImages ] = useState < ImageItem [ ] > ( ( ) => getImages ( value ) ) ;
128-
129- useImperativeHandle (
130- ref ,
131- ( ) => ( {
132- reset : ( val = value ) => {
133- setImages ( getImages ( val ) ) ;
134- } ,
135- get value ( ) {
136- return images ;
137- } ,
138- } ) ,
139- [ value , images ]
140- ) ;
101+ useImperativeHandle ( ref , ( ) => dropzoneRef . current ) ;
102+
103+ const getImages = useCallback ( ( ) => {
104+ let innerValue = value ;
105+ if ( ! Array . isArray ( innerValue ) ) {
106+ innerValue = [ innerValue ] ;
107+ }
108+
109+ return innerValue
110+ . map < ImageItem > ( ( item ) => {
111+ if ( typeof item === "string" ) {
112+ item = { url : item } ;
113+ }
114+
115+ return {
116+ id : uuidv7 ( ) ,
117+ ...item ,
118+ } ;
119+ } )
120+ . slice ( 0 , max ) ;
121+ } , [ value , max ] ) ;
122+
123+ const [ images , setImages ] = useState < ImageItem [ ] > ( ( ) => getImages ( ) ) ;
124+
125+ useEffect ( ( ) => {
126+ setImages ( getImages ( ) ) ;
127+ } , [ getImages ] ) ;
141128
142129 const onDropAccepted = useCallback (
143130 async ( acceptedFiles : File [ ] ) => {
144- const newImages = acceptedFiles
131+ const addImages = acceptedFiles
145132 . slice ( 0 , max - images . length )
146133 . map < ImageItem > ( ( file ) => ( {
147134 id : uuidv7 ( ) ,
148135 name : file . name ,
149136 loading : true ,
150137 file,
151138 } ) ) ;
152- setImages ( ( images ) => {
153- images = images . concat ( newImages ) ;
154- Promise . all (
155- newImages . map ( async ( item ) => {
156- item . url = await onUpload ?.( item . file ) ;
157- item . loading = false ;
158- setImages ( ( images ) => {
159- return [ ...images ] ;
160- } ) ;
161- } )
162- ) . then ( ( ) => onChange ?.( images ) ) ;
163- return images ;
164- } ) ;
139+ const newImages = images . concat ( addImages ) ;
140+ setImages ( newImages ) ;
141+ Promise . all (
142+ newImages . map ( async ( item ) => {
143+ item . url = await onUpload ?.( item . file ) ;
144+ item . loading = false ;
145+ setImages ( ( images ) => {
146+ return [ ...images ] ;
147+ } ) ;
148+ } )
149+ ) . then ( ( ) => onChange ?.( newImages ) ) ;
165150 } ,
166- [ images . length , max ]
151+ [ images , max , onChange ]
167152 ) ;
168153
169- const onRemoveImage = useCallback ( ( idx : number ) => {
170- setImages ( ( images ) => {
171- images = images . filter ( ( _ , index ) => index != idx ) ;
172- onChange ?.( images ) ;
173- return images ;
174- } ) ;
175- } , [ ] ) ;
154+ const onRemoveImage = useCallback (
155+ ( idx : number ) => {
156+ const newImages = images . filter ( ( _ , index ) => index != idx ) ;
157+ onChange ?.( newImages ) ;
158+ setImages ( newImages ) ;
159+ } ,
160+ [ images , onChange ]
161+ ) ;
176162
177163 return (
178164 < PhotoProvider { ...photoProviderProps } >
@@ -232,6 +218,7 @@ export const ImageUpload = forwardRef<ImageUploadRef, ImageUploadProps>(
232218 className = { dropzoneClassName }
233219 width = { width }
234220 height = { height }
221+ ref = { dropzoneRef }
235222 >
236223 { children }
237224 </ Dropzone >
0 commit comments