zenoamaro / react-quill

A Quill component for React.
https://zenoamaro.github.io/react-quill
MIT License
6.69k stars 911 forks source link

Please help me, I want to build my project, but this is the error that comes out: node_modules\quill\dist\quill.js (7661:11) @ document ⨯ ReferenceError: document is not defined #968

Open KhasanshinRamis opened 5 months ago

KhasanshinRamis commented 5 months ago

Please help me, I want to build my project, but this is the error that comes out:

node_modules\quill\dist\quill.js (7661:11) @ document
 ⨯ ReferenceError: document is not defined
    at __webpack_require__ (C:\Users\Ramis\Desktop\React+Next.js\vip-app\.next\server\webpack-runtime.js:33:43)
    at __webpack_require__ (C:\Users\Ramis\Desktop\React+Next.js\vip-app\.next\server\webpack-runtime.js:33:43)
    at eval (./src/components/ui/textEditor.tsx:9:69)
    at (ssr)/./src/components/ui/textEditor.tsx (C:\Users\Ramis\Desktop\React+Next.js\vip-app\.next\server\app\(protected)\write\page.js:778:1)      
    at __webpack_require__ (C:\Users\Ramis\Desktop\React+Next.js\vip-app\.next\server\webpack-runtime.js:33:43)
    at eval (write/page.tsx:25:84)
    at (ssr)/./src/app/(protected)/write/page.tsx (C:\Users\Ramis\Desktop\React+Next.js\vip-app\.next\server\app\(protected)\write\page.js:536:1)    
    at __webpack_require__ (C:\Users\Ramis\Desktop\React+Next.js\vip-app\.next\server\webpack-runtime.js:33:43)
    at JSON.parse (<anonymous>)

My Components:

'use client';

import { Button } from '@/components/ui/button';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.bubble.css';
import { Input } from '@/components/ui/input';
import { Form, FormControl, FormItem, FormLabel, FormMessage, FormField } from '@/components/ui/form';
import { FormError } from '@/components/formError';
import { FormSuccess } from '@/components/formSuccess';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useForm } from 'react-hook-form';
import { FaFileImage } from "react-icons/fa";
import { zodResolver } from '@hookform/resolvers/zod';
import { NewPostSchema } from '@/schemas';
import {
    getStorage,
    ref,
    uploadBytesResumable,
    getDownloadURL,
} from "firebase/storage";
import * as z from 'zod';
import { useEffect, useState } from 'react';
import postService from '@/services/postService';
import { app } from '@/config/firebase';

export default function WritePage() {
    const queryClient = useQueryClient();

    const [media, setMedia] = useState<string>('');
    const [file, setFile] = useState<Blob | Uint8Array | ArrayBuffer>();
    const [disabledFromMedia, setDisabledFromMedia] = useState<boolean>(false);

    const [success, setSuccess] = useState<string | undefined>('');
    const [errorMessage, setErrorMessage] = useState<string | undefined>('');

    useEffect(() => {
        const storage = getStorage(app);
        const upload = () => {
            if (file) {

                const name = new Date().getTime().toString();
                const storageRef = ref(storage, name);

                const uploadTask = uploadBytesResumable(storageRef, file);

                uploadTask.on(
                    "state_changed",
                    (snapshot) => {
                        const progress =
                            (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
                        console.log("Upload is " + progress + "% done");
                        switch (snapshot.state) {
                            case "paused":
                                console.log("Upload is paused");
                                break;
                            case "running":
                                console.log("Upload is running");
                                break;
                        }
                    },
                    (error) => { console.log(error.message) },
                    () => {
                        getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
                            setMedia(downloadURL);
                            setDisabledFromMedia(true);
                            console.log('File available at', downloadURL);
                        });
                    }
                );
            }
        };

        file && upload();
    }, [file]);

    const form = useForm<z.infer<typeof NewPostSchema>>({
        resolver: zodResolver(NewPostSchema)
    });

    const mutation = useMutation({
        mutationKey: ['new-post'],
        mutationFn: (values: z.infer<typeof NewPostSchema>) => postService.create(values),
        onSuccess: (data: any) => {
            console.log('Success!', data);
            console.log(data.success);
            setSuccess('Новая запись создана!');
            queryClient.invalidateQueries({ queryKey: ['new-post'] });
        },
        onError: (error: any) => {
            console.log(error.message);
            setErrorMessage(error.response.data.error);
            queryClient.invalidateQueries({ queryKey: ['new-post'] });
        }
    });

    const slugify = (str: string) => str.toLowerCase().trim().replace(/\W_/g, "").replace(/\s/g, "-").replace(/^-+|-+$/g, "").replace(/а/g, "a").replace(/б/g, "b").replace(/в/g, "v").replace(/г/g, "g").replace(/д/g, "d").replace(/е/g, "e").replace(/ё/g, "yo").replace(/ж/g, "zh").replace(/з/g, "z").replace(/и/g, "i").replace(/й/g, "y").replace(/к/g, "k").replace(/л/g, "l").replace(/м/g, "m").replace(/н/g, "n").replace(/о/g, "o").replace(/п/g, "p").replace(/р/g, "r").replace(/с/g, "s").replace(/т/g, "t").replace(/у/g, "u").replace(/ф/g, "f").replace(/х/g, "kh").replace(/ц/g, "ts").replace(/ч/g, "ch").replace(/ш/g, "sh").replace(/щ/g, "shch").replace(/ъ/g, "").replace(/ы/g, "y").replace(/ь/g, "").replace(/э/g, "e").replace(/ю/g, "yu").replace(/я/g, "ya").slice(0, 30);

    const onSubmit = (values: z.infer<typeof NewPostSchema>) => {
        console.log(media);
        const updatedValues = {
            img: media,
            slug: slugify(values.title),
            ...values
        };
        console.log('image mutation', updatedValues.image);
        mutation.mutate(
            updatedValues
        );
    };

    console.log("media: ", media);
    console.log("file: ", file);

    return (
        <div className='min-h-screen'>
            <Form {...form}>
                <form
                    className='grid grid-rows-[64px_36px_32px_1fr_36px] gap-y-8 px-24 sm:px-4 py-6 h-full'
                    onSubmit={form.handleSubmit(onSubmit)}
                >
                    <FormField
                        control={form.control}
                        name='title'
                        render={({ field }) => (
                            <FormItem>
                                <FormControl>
                                    <Input
                                        {...field}
                                        placeholder='Заголовок'
                                        className='text-3xl md:text-xl pl-0 py-8 text-white placeholder:text-white focus:border-none focus:outline-none border-none bg-transparent outline-none shadow-none'
                                        disabled={mutation.isPending}
                                    />
                                </FormControl>
                                <FormMessage />
                            </FormItem>
                        )}
                    />
                    <FormField
                        control={form.control}
                        name='category'
                        render={({ field }) => (
                            <FormItem>
                                <Select
                                    disabled={mutation.isPending}
                                    onValueChange={field.onChange}
                                >
                                    <FormControl>
                                        <SelectTrigger>
                                            <SelectValue
                                                placeholder='Выберите категорию'

                                            />
                                        </SelectTrigger>
                                    </FormControl>
                                    <SelectContent>
                                        <SelectItem value='style'>
                                            Стиль
                                        </SelectItem>
                                        <SelectItem value='fashion'>
                                            Мода
                                        </SelectItem>
                                        <SelectItem value='food'>
                                            Еда
                                        </SelectItem>
                                        <SelectItem value='culture'>
                                            Культура
                                        </SelectItem>
                                        <SelectItem value='travel'>
                                            Путешествие
                                        </SelectItem>
                                        <SelectItem value='coding'>
                                            Программирование
                                        </SelectItem>
                                    </SelectContent>
                                </Select>
                                <FormMessage />
                            </FormItem>
                        )}
                    />
                    <FormField
                        control={form.control}
                        name='image'
                        render={({ field }) => (
                            <FormItem>
                                <FormLabel
                                    className='grid justify-center items-center bg-indigo-900 w-[32px] h-[32px] rounded-[50%]'
                                    htmlFor="image"
                                >
                                    <FaFileImage width={16} height={16} color='#F5EFEF' />
                                </FormLabel>
                                <FormControl>
                                    <Input
                                        {...field}
                                        type="file"
                                        id='image'
                                        disabled={mutation.isPending}
                                        onChange={(event) => {
                                            if (event.target.files && event.target.files.length > 0) {
                                                setFile(event.target.files[0]);
                                            }
                                        }}
                                        style={{ display: 'none' }}
                                    />
                                </FormControl>
                                <FormMessage />
                            </FormItem>
                        )}
                    />

                    <FormField
                        control={form.control}
                        name='description'
                        render={({ field }) => (
                            <FormItem>
                                <FormControl>
                                    <ReactQuill
                                        {...field}
                                        theme='bubble'
                                        placeholder='Расскажи свою историю...'
                                        className='[&>div>div>p]:text-base [&>div>div>p]:text-white [&>div>div>p]:placeholder:text-white placeholder:[&>div>div>p]:text-2xl md:placeholder:[&>div>div>p]:text-lg [&>div>div>h1]:text-white  [&>div>div>h1]:text-text-3xl md:placeholder:[&>div>div>h1]:text-xl [&>div>div>h2]:text-white [&>div>div>h2]:text-xl [&>div>div>ol>li]:text-base [&>div>div>ol>li]:text-white'
                                    />
                                </FormControl>
                                <FormMessage />
                            </FormItem>
                        )}
                    />

                    <FormError message={errorMessage} />
                    <FormSuccess message={success} />
                    <Button
                        type='submit'
                        className='grid items-end'
                        disabled={mutation.isPending && disabledFromMedia}
                    >
                        Создать новую запись
                    </Button>
                </form>
            </Form>
        </div>
    );

}
JardelCheung commented 5 months ago

U can use dynamic in next/dynamic to resolve this issue.

exa:

import dynamic from "next/dynamic";
const ReactQuill = dynamic(
    () => {
        return import("react-quill");
    },
    {ssr: false}
);

return(
<ReactQuill/>
)