Skip to content

Commit

Permalink
wip: update the component to use react-uploady config
Browse files Browse the repository at this point in the history
  • Loading branch information
DecampsRenan committed Jun 9, 2023
1 parent 1fa617e commit 534b8ec
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 141 deletions.
3 changes: 3 additions & 0 deletions .env.validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const envSchema = z.object({

NEXT_PUBLIC_DEV_ENV_NAME: z.string().optional(),
NEXT_PUBLIC_DEV_ENV_COLOR_SCHEME: z.string().optional(),

NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME: z.string().optional(),
NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET: z.string().optional(),
});

/**
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
"@formkit/auto-animate": "1.0.0-beta.6",
"@lukemorales/query-key-factory": "1.2.0",
"@prisma/client": "4.15.0",
"@rpldy/upload-drop-zone": "1.4.1",
"@rpldy/uploady": "1.4.1",
"@tanstack/react-query": "4.29.12",
"@tanstack/react-query-devtools": "4.29.12",
"axios": "1.4.0",
Expand Down
83 changes: 50 additions & 33 deletions src/components/FieldImageUpload/docs.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,50 @@ export default {
title: 'Fields/FieldImageUpload',
};

export const Default = () => (
<Formiz onSubmit={console.log} autoForm>
<Stack spacing={6}>
<FieldImageUpload
name="demo"
label="Profil picture"
helper="This is an helper"
required="Profil picture is required"
width="360px"
/>
<FieldImageUpload
name="demo-default-value"
label="Default value"
defaultValue="https://bit.ly/dan-abramov"
width="360px"
/>
<FieldImageUpload
name="demo-ratio"
label="Custom aspect ratio and size"
imageUploadProps={{ ratio: 1 }}
w="240px"
/>
<Box>
<Button type="submit">Submit</Button>
</Box>
</Stack>
</Formiz>
);
export const Default = () => {
const form = useForm({
onValidSubmit(formValues) {
console.log(formValues);
},
});

return (
<Formiz connect={form} autoForm>
<Stack spacing={6}>
<FieldImageUpload
name="demo"
label="Profil picture"
helper="This is an helper"
required="Profil picture is required"
width="360px"
/>
<FieldImageUpload
name="demo-default-value"
label="Default value"
defaultValue="https://bit.ly/dan-abramov"
width="360px"
/>
<FieldImageUpload
name="demo-ratio"
label="Custom aspect ratio and size"
imageUploadProps={{ ratio: 1 }}
w="240px"
/>
<Box>
<Button type="submit">Submit</Button>
</Box>
</Stack>
</Formiz>
);
};

export const CustomPlaceholder = () => {
const form = useForm({
onValidSubmit(formValues) {
console.log(formValues);
},
});

const PlaceholderComponent = () => (
<Center bgColor="gray.50" overflow="hidden">
<Stack textAlign="center" spacing={2}>
Expand All @@ -56,7 +70,7 @@ export const CustomPlaceholder = () => {
);

return (
<Formiz onSubmit={console.log} autoForm>
<Formiz connect={form} autoForm>
<Stack spacing={6}>
<FieldImageUpload
name="demo"
Expand All @@ -73,19 +87,22 @@ export const CustomPlaceholder = () => {
};

export const InvalidateFormWhileUploading = () => {
const form = useForm();
const form = useForm({
onValidSubmit(formValues) {
console.log(formValues);
},
});

return (
<Formiz connect={form} onSubmit={console.log} autoForm>
<Formiz connect={form} autoForm>
<Stack spacing={2}>
<FieldImageUpload
name="demo"
label="Invalidate during uploading"
w="360px"
imageUploadProps={{
onUploadStateChange: (isUploading) =>
isUploading &&
form.invalidateFields({ demo: 'Image is uploading' }),
isUploading && form.setErrors({ demo: 'Image is uploading' }),
}}
/>
<Box>
Expand Down
28 changes: 9 additions & 19 deletions src/components/FieldImageUpload/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import React, { useEffect, useState } from 'react';
import React, { FC, useEffect, useState } from 'react';

import { InputGroup } from '@chakra-ui/react';
import { FieldProps, useField } from '@formiz/core';

import { FormGroup, FormGroupProps } from '@/components/FormGroup';
import { ImageUpload, ImageUploadProps } from '@/components/ImageUpload';

export interface FieldImageUploadProps
extends FieldProps,
Omit<FormGroupProps, 'placeholder'> {
imageUploadProps?: Omit<ImageUploadProps, 'value' | 'onChange'>;
}
export type FieldImageUploadProps = FieldProps &
Omit<FormGroupProps, 'placeholder'> & {
imageUploadProps?: Omit<ImageUploadProps, 'value' | 'onChange'>;
};

export const FieldImageUpload = (props: FieldImageUploadProps) => {
export const FieldImageUpload: FC<FieldImageUploadProps> = (props) => {
const {
errorMessage,
id,
Expand All @@ -23,18 +22,9 @@ export const FieldImageUpload = (props: FieldImageUploadProps) => {
value,
otherProps,
} = useField(props);
const {
children,
label,
type,
placeholder,
helper,
leftIcon,
rightIcon,
color,
imageUploadProps,
...rest
} = otherProps;

const { children, label, helper, color, imageUploadProps, ...rest } =
otherProps;
const { required } = props;
const [isTouched, setIsTouched] = useState(false);
const showError = !isValid && (isTouched || isSubmitted);
Expand Down
9 changes: 9 additions & 0 deletions src/components/ImageUpload/ImageUpload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Input, InputProps, forwardRef } from '@chakra-ui/react';
import { useFileInput } from '@rpldy/uploady';

export const ImageUploadInput = forwardRef<InputProps, 'input'>(
(props, ref) => {
useFileInput(ref as ExplicitAny);
return <Input ref={ref} type="file" display="none" {...props} />;
}
);
28 changes: 0 additions & 28 deletions src/components/ImageUpload/cloudinary.service.ts

This file was deleted.

16 changes: 12 additions & 4 deletions src/components/ImageUpload/docs.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@ export const Default = () => {

return (
<Stack>
<ImageUpload value={imageUrl} onChange={setImageUrl} w="240px" />
<ImageUpload value={imageUrl} onChange={setImageUrl} w="360px" />
<ImageUpload
value={imageUrl}
onChange={setImageUrl}
onChange={(e) => setImageUrl(e.toString())}
w="240px"
/>
<ImageUpload
value={imageUrl}
onChange={(e) => setImageUrl(e.toString())}
w="360px"
/>
<ImageUpload
value={imageUrl}
onChange={(e) => setImageUrl(e.toString())}
w="480px"
ratio={1}
/>
Expand Down Expand Up @@ -50,7 +58,7 @@ export const CustomPlaceholder = () => {
<Stack>
<ImageUpload
value={imageUrl}
onChange={setImageUrl}
onChange={(e) => setImageUrl(e.toString())}
placeholder={<PlaceholderComponent />}
w="360px"
/>
Expand Down
107 changes: 50 additions & 57 deletions src/components/ImageUpload/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import {
IconButton,
Image,
ImageProps,
Input,
} from '@chakra-ui/react';
import Uploady from '@rpldy/uploady';
import { FiX } from 'react-icons/fi';

import { Loader } from '@/app/layout';
import { DefaultImagePlaceholder } from '@/components/ImageUpload/DefaultImagePlaceholder';
import { uploadFile } from '@/components/ImageUpload/cloudinary.service';
import { ImageUploadInput } from '@/components/ImageUpload/ImageUpload';
import { Loader } from '@/layout/Loader';

export type ImageUploadProps = Omit<BoxProps, 'onChange' | 'placeholder'> &
export type ImageUploadProps = BoxProps &
Pick<AspectRatioProps, 'ratio'> & {
value: string;
onChange: (url: string) => void;
Expand All @@ -35,25 +35,11 @@ export const ImageUpload: React.FC<ImageUploadProps> = ({
const fileInputRef = useRef<HTMLInputElement>(null);
const [isUploading, setIsUploading] = useState(false);

// TODO: I think the onChange should handle the upload, and the form value should contain
// the uploaded picture id (for future ref, like deleting on ccloudinary the uploaded image)
// and the unique url
const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) {
return;
}

try {
setIsUploading(true);
onUploadStateChange(true);

const imageUrl = await uploadFile(file);
console.log({ imageUrl });
onChange(imageUrl);
} catch (err) {
console.error(err);
} finally {
onUploadStateChange(false);
setIsUploading(false);
}
// TODO
};

const handleDelete = () => {
Expand All @@ -74,41 +60,48 @@ export const ImageUpload: React.FC<ImageUploadProps> = ({
};

return (
<Box
position="relative"
border="1px"
borderStyle="dashed"
borderColor="gray.200"
borderRadius="16px"
overflow="hidden"
cursor="pointer"
transition="border-color 150ms ease-in-out"
_hover={{ borderColor: 'gray.400' }}
{...rest}
<Uploady
destination={{
url: `https://api.cloudinary.com/v1_1/s${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/upload`,
params: {
upload_preset: process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET,
},
}}
>
<Input
ref={fileInputRef}
type="file"
display="none"
onChange={handleChange}
/>
<AspectRatio ratio={ratio} onClick={() => fileInputRef.current?.click()}>
<Image src={value} fallback={getFallback()} />
</AspectRatio>
{!!value && (
<IconButton
icon={<FiX />}
position="absolute"
top="0"
right="0"
size="lg"
variant="ghost"
aria-label="Remove photo"
onClick={handleDelete}
_active={{ bgColor: 'blackAlpha.600' }}
_hover={{ bgColor: 'blackAlpha.300' }}
/>
)}
</Box>
<Box
position="relative"
border="1px"
borderStyle="dashed"
borderColor="gray.200"
borderRadius="16px"
overflow="hidden"
cursor="pointer"
transition="border-color 150ms ease-in-out"
_hover={{ borderColor: 'gray.400' }}
{...rest}
>
<ImageUploadInput ref={fileInputRef} onChange={handleChange} />
<AspectRatio
ratio={ratio}
onClick={() => fileInputRef.current?.click()}
>
<Image src={value} fallback={getFallback()} />
</AspectRatio>
{!!value && (
<IconButton
icon={<FiX />}
position="absolute"
top="0"
right="0"
size="lg"
variant="ghost"
aria-label="Remove photo"
onClick={handleDelete}
_active={{ bgColor: 'blackAlpha.600' }}
_hover={{ bgColor: 'blackAlpha.300' }}
/>
)}
</Box>
</Uploady>
);
};
Loading

0 comments on commit 534b8ec

Please sign in to comment.