TahaSh / swapy

✨ A framework-agnostic tool that converts any layout into a drag-to-swap one with just a few lines of code https://swapy.tahazsh.com/
MIT License
6.73k stars 138 forks source link

TypeError: Cannot read properties of undefined (reading 'element') #84

Open th11n opened 1 month ago

th11n commented 1 month ago

Hey, Im trying to use Swapy in my nextjs app and i encounter this error:

swapy.js:2888 Uncaught TypeError: Cannot read properties of undefined (reading 'element')
    at ae.eval [as setup] (swapy.js:2888:88)
    at ae.init (swapy.js:344:50)
    at eval (swapy.js:2535:9)
    at Array.forEach (<anonymous>)
    at Tt.update (swapy.js:2534:74)
    at Tt.tick (swapy.js:2506:122)

Here is my code:

"use client"

import useAuth from "@/hooks/use-auth";
import { useRouter } from "next/navigation";
import { Spinner } from "@nextui-org/spinner";
import { useState, useEffect, useRef, useMemo } from "react";
import {
    Sidebar,
    SidebarSection,
    SidebarToggleButton,
    NavItem,
} from '@saas-ui/react'
import { FiHome, FiUsers, FiSettings, FiChevronsRight } from 'react-icons/fi'
import { BiDoorOpen } from "react-icons/bi";
import { TiDocumentAdd } from "react-icons/ti";
import { Spacer } from '@chakra-ui/react'
import { createSwapy, SlotItemMap, Swapy } from 'swapy'

interface Table {
    id: number;
    name: string;
    user_id: number;
    lists: string[];
}

interface List {
    id: number;
    name: string;
    table_id: number;
    cards: string[];
}

export default function Table({ params }: { params: { slug: string } }) {
    const router = useRouter();
    const { user, isSignedIn, isLoading } = useAuth();
    const [errorMessage, setErrorMessage] = useState<string>('');
    const [lists, setLists] = useState<List[]>([]);

    const swapyRef = useRef<Swapy | null>(null)
    const container = useRef<HTMLDivElement | null>(null);
    const [slotItemsMap, setSlotItemsMap] = useState<SlotItemMap>([...lists.map(list => ({
        slotId: list.id.toString(),
        itemId: list.id.toString()
    })),
    // Defining an empty slot by setting itemId to null.
    { slotId: `${Math.round(Math.random() * 99999)}`, itemId: null }
    ])

    const slottedItems = useMemo(() => slotItemsMap.map(({ slotId, itemId }) => ({
        slotId,
        itemId,
        item: lists.find(list => list.id.toString() === itemId)
    })), [lists, slotItemsMap])

    useEffect(() => {
        const newItems = lists.filter(list => !slotItemsMap.some(slotItem => slotItem.itemId === list.id.toString())).map(list => ({
            slotId: list.id.toString(),
            itemId: list.id.toString()
        }))

        const withoutRemovedItems = slotItemsMap.filter(slotItem =>
            lists.some(list => list.id.toString() === slotItem.itemId) || !slotItem.itemId
        )

        const updatedSlotItemsMap = [...withoutRemovedItems, ...newItems]

        setSlotItemsMap(updatedSlotItemsMap)
        swapyRef.current?.setData({ array: updatedSlotItemsMap })
    }, [lists])

    useEffect(() => {
        console.log(1, container, container?.current)
        if (!container || !container.current) {
            return
        }
        console.log(2)

        swapyRef.current = createSwapy(container.current, {
            manualSwap: true,
            swapMode: 'hover',
            autoScrollOnDrag: true
        })

        swapyRef.current.onSwap(({ data }) => {
            swapyRef.current?.setData({ array: data.array })
            setSlotItemsMap(data.array)
        })

        swapyRef.current.enable(true)

        return () => {
            swapyRef.current?.destroy()
            swapyRef.current?.enable(false)
        }
    }, [container, container.current])

    useEffect(() => {
        if (isSignedIn && !isLoading) {
            fetchTable();
        }
    }, [isSignedIn]);

    if (!isSignedIn && !isLoading) {
        router.push('/');
        return null;
    }

    if (isLoading) {
        return <div className="h-screen w-full flex items-center justify-center"><Spinner size="lg" /></div>;
    }

    if (!user) {
        return <div>No user data available.</div>;
    }

    async function fetchTable() {
        try {
            const response = await fetch(`http://localhost:8000/tables/${params.slug}`, {
                method: 'GET',
                headers: {
                    'Accept': 'application/json',
                },
                credentials: 'include'
            });

            if (response.ok) {
                const tables: Table[] = await response.json();
                setLists(tables.lists);
            } else if (response.status === 401) {
                setErrorMessage('Unauthorized access. Please log in again.');
            } else {
                setErrorMessage(`Error: ${response.statusText}`);
            }
        } catch (error) {
            console.error('Something went wrong! Error:', error);
            setErrorMessage('Something went wrong! Try again later.');
        }
    }

    return (
        <div className="flex flex-row h-screen">
            <Sidebar variant="default" width="64px" toggleBreakpoint="sm">
                <SidebarToggleButton />
                <SidebarSection alignItems="center" direction="row">
                    <Logo />
                    <p className="font-bold text-inherit">Noteflow</p>
                </SidebarSection>
                <SidebarSection>
                    <NavItem icon={<FiHome size="1.2em" />} size="md" isActive>
                        Home
                    </NavItem>
                    <NavItem icon={<FiUsers size="1.2em" />} size="md">
                        Users
                    </NavItem>
                    <NavItem icon={<FiSettings size="1.2em" />} size="md">
                        Settings
                    </NavItem>
                </SidebarSection>
                <Spacer />
                <SidebarSection>
                    <NavItem icon={<BiDoorOpen size="1.2em" color="red.300" />} color="red.300" size="md">
                        Log out
                    </NavItem>
                </SidebarSection>
            </Sidebar>
            <div className="flex flex-col w-full h-full py-3 px-14 sm:px-10 sm:py-4">
                <h1 className="font-bold text-xl mb-6">Dashboard</h1>
                {errorMessage && <div className="text-red-500">{errorMessage}</div>}
                <div className="grid grid-cols-1 lg:grid-cols-6 grid-rows-5 gap-4 h-full w-full">
                    <div className="border-[#f5f5f7] border-2 rounded-xl flex items-center justify-center h-32">
                        <span className="p-2 text-black">
                            <TiDocumentAdd size={50} />
                        </span>
                    </div>
                    <div className="container" ref={container}>
                        {slottedItems.map(({ itemId, slotId, item }) => ((
                            <div className="slot" data-swapy-slot={slotId} key={slotId}>
                                {
                                    item ? (
                                        <div className="item a" data-swapy-item={itemId} key={itemId}>
                                            <div data-swapy-handle   key={item.id} className="bg-[#f5f5f7] border-2 border-[#f5f5f7] p-4 rounded-xl flex flex-col justify-between relative overflow-hidden h-32">
                                                <h2 className="text-xl font-bold z-[1]">{item.name}</h2>
                                            </div>
                                        </div>
                                    )
                                        : null
                                }
                            </div>
                        )))}
                </div>
            </div>
        </div>
        </div >
    )
}

const Logo = () => (
    <svg fill="none" height="36" viewBox="0 0 32 32" width="36">
        <path
            clipRule="evenodd"
            d="M17.6482 10.1305L15.8785 7.02583L7.02979 22.5499H10.5278L17.6482 10.1305ZM19.8798 14.0457L18.11 17.1983L19.394 19.4511H16.8453L15.1056 22.5499H24.7272L19.8798 14.0457Z"
            fill="currentColor"
            fillRule="evenodd"
        />
    </svg>
);

What do i do? Thank you

nirzon47 commented 1 month ago

I am facing the same problem after building, works fine in dev.

Did you figure out a solution?