Back
Edit on GitHub
jam / tier-list

Presentation

Video

PresentEdit In Figma

1/--

No PDF file specified.
react
60 Min

Tier List w/ React

KeenanOH

Setup

Fork the starter code.

Optional: Create a StackBlitz account to save your work.

The Basics

Draggable - something that can be dragged within droppables

Droppable - something that can hold draggables

We are going to be using hello-pangea/dnd to help us with dragging and dropping since it provides a nice, easy-to-use abstraction.

Part 1: Creating a Draggable

First, we want to create our key components. The DataCard represents each object within the tier-list.

components/DataCard.tsx

import { Draggable } from '@hello-pangea/dnd';

interface DataCardProps {
    id: string;
    index: number;
    imageUrl: string;
}

export default function DataCard({ id, index, imageUrl }: DataCardProps) {
    return (
        <Draggable draggableId={id} index={index}>
            {(provided) => (
                <div
                    ref={provided.innerRef}
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                >
                    <img className="w-16" src={imageUrl} />
                </div>
            )}
        </Draggable>
    );
}

Part 2: Create a Droppable

Each tier has a droppable area where items can be moved around!

components/Tier.tsx

import { Droppable } from '@hello-pangea/dnd';
import DataCard from './DataCard';

export default function Tier({
    name,
    data,
    className = '',
}: {
    name: string;
    data: { id: string; imageUrl: string }[];
    className?: string;
}) {
    return (
        <div className="flex">
            <div className={'w-32 py-8 text-center ' + className}>{name}</div>

            <Droppable droppableId={name} direction="horizontal">
                {(provided) => (
                    <div
                        className="pl-4 flex flex-wrap items-center gap-4 border w-full select-none"
                        ref={provided.innerRef}
                        {...provided.droppableProps}
                    >
                        {data.map((data, index) => (
                            <DataCard
                                key={data.id}
                                id={data.id}
                                index={index}
                                imageUrl={data.imageUrl}
                            />
                        ))}
                        {provided.placeholder}
                    </div>
                )}
            </Droppable>
        </div>
    );
}

Part 3: Put it all together

App.tsx

import { DragDropContext, DropResult } from '@hello-pangea/dnd';
import Tier from './components/Tier';
import { useTierState } from './useTierState';
import { arrayMove, arrayTransfer } from './helpers';

export default function App() {
    const [tiers, setTiers] = useTierState();

    function onDragEnd(result: DropResult) {
        if (!result.destination) return;

        const oldIndex = result.source.index;
        const newIndex = result.destination.index;
        const oldTierName = result.source.droppableId;
        const newTierName = result.destination.droppableId;

        if (oldTierName === newTierName)
            setTiers((prevTiers) => {
                const tier = prevTiers.find(
                    (tier) => tier.name === oldTierName
                )!;

                arrayMove(tier.data, oldIndex, newIndex);

                return prevTiers;
            });
        else
            setTiers((prevTiers) => {
                const oldTier = prevTiers.find(
                    (tier) => tier.name === oldTierName
                )!;
                const newTier = prevTiers.find(
                    (tier) => tier.name === newTierName
                )!;

                arrayTransfer(oldTier.data, newTier.data, oldIndex, newIndex);

                return prevTiers;
            });
    }

    return (
        <DragDropContext onDragEnd={onDragEnd}>
            <div>
                {tiers.map((tier) => (
                    <Tier
                        key={tier.name}
                        className={tier.className}
                        name={tier.name}
                        data={tier.data}
                    />
                ))}
            </div>
        </DragDropContext>
    );
}

Final Code Example

https://stackblitz.com/edit/vitejs-vite-dpvrzw?file=README.md

You finished the Jam.
Congratulations! 🎉 🎉 🎉
Share your final project with the community
Project Name
Project URL

Author

KeenanOH
Message on Slack

Outline