Las aplicaciones que usan drag and drop son muy comunes hoy en día, son excelentes para la experiencia de usuario dentro de un app. Y probablemente te gustaría implementarlo en tu próximo proyecto.
En esta ocasión, te enseñare como realizar una aplicación que tenga la funcionalidad de drag & drop, pero sin usar alguna librería externa, solamente con React JS.
🚨 Nota: Este post requiere que sepas las bases de React con TypeScript (hooks básicos y custom hooks).
Cualquier tipo de Feedback es bienvenido, gracias y espero disfrutes el articulo.🤗
Tabla de contenido.
📌 Tecnologías a utilizar.
📌 Creando el proyecto.
📌 Primeros pasos.
📌 Creando nuestras tarjetas.
📌 Creando los contenedores para nuestras tarjetas.📌 Definiendo el tipo e interfaz para la información de las tarjetas.
📌 Creando el componente DragAndDrop.tsx
📌 Agregando algunos datos para crear tarjetas.
📌 Mostrando algunas tarjetas.
📌 Realizando la funcional de Drag.
📌 Realizando la funcional de Drop.📌 Creando el estado para mantener las tarjetas.
📌 Realizando las funciones para hacer el drop en los contenedores.📌 Opcional. Refactorización del código en
DragAndDrop.tsx
📌 Conclusión.
👉 Tecnologías a utilizar.
- ▶️ React JS (version 18)
- ▶️ Vite JS
- ▶️ TypeScript
- ▶️ CSS vanilla (Los estilos los encuentras en el repositorio al final de este post)
👉 Creando el proyecto.
Al proyecto le colocaremos el nombre de: dnd-app
(opcional, tu le puedes poner el nombre que gustes).
npm init vite@latest
Creamos el proyecto con Vite JS y seleccionamos React con TypeScript.
Luego ejecutamos el siguiente comando para navegar al directorio que se acaba de crear.
cd dnd-app
Luego instalamos las dependencias.
npm install
Después abrimos el proyecto en un editor de código (en mi caso VS code).
code .
Luego con este comando levantaremos el servidor de desarrollo, y finalmente vamos a un navegador y accedemos a http://localhost:5173
(en vite version 2 el puerto era localhost:3000
, pero en la nueva version el puerto es localhost:5173
)
npm run dev
👉 Primeros pasos.
De una vez, creamos la carpeta src/components
y agregamos el archivo Title.tsx
y dentro agregamos:
export const Title = () => {
return (
<div className="title flex">
<h1>Creating basic Drag & Drop 👆 </h1>
<span>( without external libraries )</span>
</div>
)
}
Ahora, dentro del archivo src/App.tsx
borramos todo el contenido del archivo y colocamos un componente funcional que muestre el titulo que acabamos de crear.
import { Title } from "./components/Title"
const App = () => {
return (
<div className="container-main flex">
<Title />
</div>
)
}
export default App
Debería de verse así 👀:
👉 Creando nuestras tarjetas.
Dentro de la carpeta src/components
agregamos el archivo CardItem.tsx
Por el momento no recibirá ninguna prop, lo hará después.
export const CardItem = () => {
return (
<div className='card-container'>
<p>content</p>
</div>
)
}
Aun NO usaremos el componente Card en un archivo, pero si quieres puedes importarlo en el archivo src/App.tsx
para que puedas darle algunos estilos y verlos en pantalla.
👉 Creando los contenedores para nuestras tarjetas.
Ahora vamos a crear nuestro contenedor para las tarjetas.
Dentro de la carpeta src/components
agregamos el archivo ContainerCards.tsx
y agregamos lo siguiente:
Este componente por el momento recibe como parámetro el estado ( puedes ver de que tipo es el Status)
import { Status } from '../interfaces'
interface Props {
status: Status
}
export const ContainerCards = ({ status }: Props) => {
return (
<div className="layout-cards" >
<p>{status} hero</p>
{/* Cards */}
</div>
)
}
🟠 Definiendo el tipo e interfaz para la información de las tarjetas.
El type Status es el siguiente:
export type Status = 'good' | 'bad' | 'normal'
Este tipo esta en dentro de la carpeta src/interfaces
dentro de un archivo index.ts
(el cual deben ir creando, ya que el type Status lo usaremos en varios archivos )
Aprovechando que se están creando el index.ts
en src/interfaces
también agreguen la siguiente interfaz.
Asi es como lucirán los datos de las tarjetas.
export interface Data {
id: number
content: string
status: Status
}
👉 Creando el componente DragAndDrop.tsx
Bueno, hasta aquí ya hemos creado el componente que contendrá las tarjetas, pero necesitamos 3 contenedores de tarjetas:
- Uno para lo heroes buenos.
- Uno para lo heroes normales.
- Uno para lo heroes malos.
Dentro de la carpeta src/components
agregamos el archivo DragAndDrop.tsx
y agregamos lo siguiente:
import { Status } from "../interfaces"
import { ContainerCards } from "./ContainerCards"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
return (
<div className="grid">
{
typesHero.map( container => (
<ContainerCards
status={container}
key={container}
/>
))
}
</div>
)
}
Este componente debemos agregarlo al src/App.tsx
import { DragAndDrop} from "./components/DragAndDrop"
import { Title } from "./components/Title"
const App = () => {
return (
<div className="container-main flex">
<Title />
<DragAndDrop />
</div>
)
}
export default App
Debería de verse algo como esto por el momento 👀...
Listo ya tenemos los contenedores donde se podrán soltar las tarjetas y clasificarlas. 👋
Ahora necesitamos crear algunas tarjetas.
👉 Agregando algunos datos para crear tarjetas.
Ahora creamos una carpeta src/assets
y dentro un archivo index.ts
el cual contendrá un listado con datos para rellenar las tarjetas.
import { Data } from "../interfaces";
export const data: Data[] = [
{
id: 1,
content: 'Aqua-man',
status: 'good'
},
{
id: 2,
content: 'Flash',
status: 'normal'
},
{
id: 3,
content: 'Green Lantern',
status: 'good'
},
{
id: 4,
content: 'Batman',
status: 'bad'
},
]
Ahora, devuelta en src/componentes/DragAndDrop.tsx
en componente ContainerCards le pasamos una nueva prop llamada items a dicha prop le pasamos como valor la data que hemos creado en la carpeta src/assets
import { ContainerCards } from "./ContainerCards"
import { Status } from "../interfaces"
import { data } from "../assets"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
return (
<div className="grid">
{
typesHero.map( container => (
<ContainerCards
status={container}
key={container}
items={data}
/>
))
}
</div>
)
}
Esto nos marcara error ya que items no es una propiedad que ContainerCards este esperando. 😥
Pero eso lo arreglamos en la siguiente sección. 👇
👉 Mostrando algunas tarjetas.
Para mostrar algunas tarjetas, necesitamos hacer unos cambios en los parámetros de cada componente.
1 - Primero el componente src/components/CardItem.tsx
Recibirá como props la data que es de tipo Data, la que habíamos definido con anterioridad .
De una vez mostramos la propiedad content dentro de data.
import { Data } from "../interfaces"
interface Props {
data: Data
}
export const CardItem = ({ data, handleDragging }: Props) => {
return (
<div className='card-container'>
<p>{data.content}</p>
</div>
)
}
2 - En el componente src/components/ContainerCards.tsx
cambiamos las interfaz de Props agregando la propiedad items que es un listado de Data y la desestructuramos en el componente
import { Data, Status } from "../interfaces"
interface Props {
items: Data[]
status: Status
}
export const ContainerCards = ({ items = [], status }: Props) => {
return (
<div className="layout-cards">
<p>{status} hero</p>
</div>
)
}
Luego debajo de la etiqueta p
realizamos una iteración a los items.
Y retornamos la el CardItem.tsx
mandando el item
a la propiedad de data
del CardItem
import { Data, Status } from "../interfaces"
import { CardItem } from "./CardItem"
interface Props {
items: Data[]
status: Status
}
export const ContainerCards = ({ items = [], status}: Props) => {
return (
<div className="layout-cards">
<p>{status} hero</p>
{
items.map(item => (
<CardItem
data={item}
key={item.id}
/>
))
}
</div>
)
}
Esto te dará una advertencia de que las key se repiten 😥
Esto es debido a que estamos renderizando 3 veces el ContainerCards.
Pero espera la única propiedad que hará la diferencia entre estos 3 componentes es el status
Por lo que haremos la siguiente condición:
- Si el estado que recibe el componente ContainerCards es igual al estado del item (o sea del super héroe) entonces renderizalo, de lo contrario retorna falso.
import { Data, Status } from "../interfaces"
import { CardItem } from "./CardItem"
interface Props {
items: Data[]
status: Status
}
export const ContainerCards = ({ items = [], status }: Props) => {
return (
<div className="layout-cards">
<p>{status} hero</p>
{
items.map(item => (
status === item.status
&& <CardItem
data={item}
key={item.id}
/>
))
}
</div>
)
}
Y asi evitamos el conflicto con las llaves y se clasificaran las tarjetas de la siguiente manera 👀...
👉 Realizando la funcional de Drag.
Para realizar la funcionalidad de drag, primero vamos a definir un estado y una función en src/components/DragAndDrop.tsx
-
El estado nos ayudara a saber si si esta haciendo drag, y asi cambiar los estilos de.
- Y por defecto sera false, ya que al inicio de la aplicación no se estará haciendo haciendo drag.
- Solo sera true cuando se arrastre alguna tarjeta.
La función, la cual recibe un valor booleano, nos ayudara a cambiar al valor al estado, esto lo hado para no pasar el setter setIsDragging como prop.
Pasamos como prop al componente ContainerCards:
- isDragging, tendrá el valor del estado
- handleDragging, sera la función que creamos para actualizar el estado.
import { ContainerCards } from "./ContainerCards"
import { data } from "../assets"
import { Status } from "../interfaces"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
const [isDragging, setIsDragging] = useState(false)
const handleDragging = (dragging: boolean) => setIsDragging(dragging)
return (
<div className="grid">
{
typesHero.map(container => (
<ContainerCards
items={data}
status={container}
key={container}
isDragging={isDragging}
handleDragging={handleDragging}
/>
))
}
</div>
)
}
Esto marcara error porque ContainerCards no espera esas propiedades.
Asi que vamos a tener que cambiar la interfaz de ContainerCards
El el archivo src/components/ContainerCards.tsx
interface Props {
items: Data[]
status: Status
isDragging: boolean
handleDragging: (dragging: boolean) => void
}
Y de una vez obtenemos esas props.
Nota que en el className del div colocamos una condición, donde si isDragging es verdadero entonces agregamos la clase
layout-dragging
. Esta clase solo cambiara el color de fondo y el borde del contenedor, cuando se arrastre una tarjeta.Nota, que también pasamos una nueva prop al CardItem la cual es handleDragging, esto es porque la tarjeta es el componente que va actualizar el estado que creamos con anterioridad.
import { CardItem } from "./CardItem"
import { Data, Status } from "../interfaces"
interface Props {
items: Data[]
status: Status
isDragging: boolean
handleDragging: (dragging: boolean) => void
}
export const ContainerCards = ({ items = [], status, isDragging, handleDragging }: Props) => {
return (
<div
className={`layout-cards ${isDragging ? 'layout-dragging' : ''}`}
>
<p>{status} hero</p>
{
items.map(item => (
status === item.status
&& <CardItem
data={item}
key={item.id}
handleDragging={handleDragging}
/>
))
}
</div>
)
}
El CardItem nos marcara error ya que no espera la propiedad handleDragging, por lo que debemos modificar su interfaz.
Ahora en el archivo src/components/CardItem.tsx
modificamos la interfaz
interface Props {
data: Data,
handleDragging: (dragging: boolean) => void
}
Y ahora si, empezamos a agregar la funcionalidad de drag en este componente.
Primero al div
que es el toda la tarjeta, le agregamos el atributo draggable para indicar que este componente se puede arrastrar.
import { Data } from "../interfaces"
interface Props {
data: Data,
handleDragging: (dragging: boolean) => void
}
export const CardItem = ({ data, handleDragging }: Props) => {
return (
<div
className='card-container'
draggable
>
<p>{data.content}</p>
</div>
)
}
Luego agregamos el atributo onDragEnd que va a ejecutar la función handleDragEnd.
Dicha función lo único que hará es colocar el valor del estado isDragging en false, porque cuando se ejecute onDragEnd ya se habrá dejado de arrastrar la tarjeta por lo que tenemos que quitar los estilos de cuando se hace drag, o sea volver todos los estilos como al inicio.
import { Data } from "../interfaces"
interface Props {
data: Data,
handleDragging: (dragging: boolean) => void
}
export const CardItem = ({ data, handleDragging }: Props) => {
const handleDragEnd = () => handleDragging(false)
return (
<div
className='card-container'
draggable
onDragEnd={handleDragEnd}
>
<p>{data.content}</p>
</div>
)
}
Luego agregamos el atributo onDragStart (se ejecuta cuando se empieza arrastrar el componente, si no le colocáramos el atributo draggable, entonces onDragStart no se ejecutaría).
onDragStart va ejecutar la función handleDragStart.
Esta función recibe el evento y dentro del evento hay una propiedad que nos interesa que es la de dataTransfer.
La propiedad dataTransfer nos permite contener u obtener datos cuando se esta arrastrando un elemento.
La propiedad setData dentro de dataTransfer, establece los datos que queremos contener al momento de arrastrar un elemento, y recibe dos parámetros:
format: es el formato de la data a mantener, el cual es "text"
data: es la información que queremos contener mientras se hace el arrastre del elemento. Solo acepta un string. En este caso, almacenaremos el id de la tarjeta.
NOTA: también existe una propiedad dentro de dataTransfer llamada clearData que limpia el cache de los datos que almacenamos. En este caso no es necesario ejecutarlo, ya que vamos a estar sobrescribiendo el mismo identificador 'text'.
Después de contener la data, ejecutamos handleDragging mandando el valor de true para indicar al usuario que estamos arrastrando un elemento.
import { Data } from "../interfaces"
interface Props {
data: Data,
handleDragging: (dragging: boolean) => void
}
export const CardItem = ({ data, handleDragging }: Props) => {
const handleDragStart = (e: React.DragEvent<HTMLDivElement>) => {
e.dataTransfer.setData('text', `${data.id}`)
handleDragging(true)
}
const handleDragEnd = () => handleDragging(false)
return (
<div
className='card-container'
draggable
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
>
<p>{data.content}</p>
</div>
)
}
Y asi tendríamos la parte de arrastrar un elemento, ya tendríamos la información contenida lista para obtenerla cuando se suelte en otro contenedor.
Asi se vería cuando arrastramos una tarjeta, cambia el diseño de los contenedores indicando que son los lugares donde puedes soltar la tarjeta.
👉 Realizando la funcional de Drop.
Antes de hacer la parte de soltar el elemento, debemos realizar otras cosas antes.
🟠 Creando el estado para mantener las tarjetas.
Primero establecer la lista de heroes en un estado y poder actualizarla cuando se suelte la tarjeta en otro contenedor,en ese momento actualizaríamos la propiedad status del héroe, lo que provocara que se vuelva a renderizar de nuevo el listado organizando las tarjetas que cambiaron.
Para eso vamos a src/components/DragAndDrop.tsx
y creamos un nuevo estado.
Su valor inicial va a ser la data que hemos definido previamente en src/assets
.
import { data } from "../assets"
const [listItems, setListItems] = useState<Data[]>(data)
Y ahora, al momento de renderizar el componente ContainerCards, en vez de pasar el valor de data a la prop de items, le mandaremos el valor del estado listItems.
import { ContainerCards } from "./ContainerCards"
import { data } from "../assets"
import { Status } from "../interfaces"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
const [isDragging, setIsDragging] = useState(false)
const [listItems, setListItems] = useState<Data[]>(data)
const handleDragging = (dragging: boolean) => setIsDragging(dragging)
return (
<div className="grid">
{
typesHero.map(container => (
<ContainerCards
items={listItems}
status={container}
key={container}
isDragging={isDragging}
handleDragging={handleDragging}
/>
))
}
</div>
)
}
Después crearemos una función para actualizar el estado de la listItems.
La llamaremos handleUpdateList, y recibirá dos parámetros:
- id: el identificador de la tarjeta, sera de tipo número.
- status: el nuevo estado de la tarjeta, sera de tipo Status.
Dentro de la función ...
1 - Primero buscaremos el elemento en el valor del estado listItems, mediante el ID.
2 - Evaluaremos si los datos existen y si el status que nos pasan es diferente al status que ya tiene, entonces haremos los cambios en el estado.
3 - Dentro de la condición, accedemos a la tarjeta encontrada y actualizaremos su propiedad status asignándole el nuevo status que nos llega por parámetro en la función.
4 - Llamamos al setListItems para actualizar el estado, colocando:
La tarjeta con su propiedad status actualizada.
Un nuevo arreglo, filtrando los elementos para quitar el tarjeta que estamos actualizando y evitar que se duplique la información.
Ahora, al componente ContainerCards le agregamos una nueva propiedad llamada handleUpdateList y le mandamos la función que acabamos de crear handleUpdateList.
import { ContainerCards } from "./ContainerCards"
import { data } from "../assets"
import { Status } from "../interfaces"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
const [isDragging, setIsDragging] = useState(false)
const [listItems, setListItems] = useState<Data[]>(data)
const handleDragging = (dragging: boolean) => setIsDragging(dragging)
const handleUpdateList = (id: number, status: Status) => {
let card = listItems.find(item => item.id === id)
if (card && card.status !== status) {
card.status = status
setListItems( prev => ([
card!,
...prev.filter(item => item.id !== id)
]))
}
}
return (
<div className="grid">
{
typesHero.map(container => (
<ContainerCards
items={listItems}
status={container}
key={container}
isDragging={isDragging}
handleDragging={handleDragging}
handleUpdateList={handleUpdateList}
/>
))
}
</div>
)
}
Esto nos marcara error, porque el componente ContainerCards no espera la propiedad handleUpdateList, asi que debemos actualizar la interfaz de ContainerCards.
En src/components/ContainerCards.tsx
:
interface Props {
items: Data[]
status: Status
isDragging: boolean
handleDragging: (dragging: boolean) => void
handleUpdateList: (id: number, status: Status) => void
}
👉 Realizando las funciones para hacer el drop en los contenedores.
Estamos en src/components/ContainerCards.tsx
.
Dentro del componente vamos a establecer dos propiedades nuevas al elemento div.
onDragOver: se produce cuando un elemento que es arrastrable se arrastra sobre un objetivo de soltar valido. Le pasamos la función handleDragOver, que crearemos en un instante.
onDrop: se produce cuando el elemento arrastrado se deja caer. Le pasamos la función handleDrop, que crearemos en un instante.
<div
className={`layout-cards ${isDragging ? 'layout-dragging' : ''}`}
onDragOver={handleDragOver}
onDrop={handleDrop}
>
<p>{status} hero</p>
{
items.map(item => (
status === item.status
&& <CardItem
data={item}
key={item.id}
handleDragging={handleDragging}
/>
))
}
</div>
La función handleDragOver solo hará esto.
Primero, recibirá el evento que emite onDragOver.
Ya que por defecto los datos no pueden ser soltados en otros elementos y para permitir soltarlos debemos evitar el comportamiento por defecto.
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault()
}
Ahora la función handleDrop
Primero, recibirá el evento que emite onDrop.
Dentro de la función, evitamos el comportamiento por defecto, el cual se nota mas con imágenes (cuando soltamos una imagen en un lugar de nuestra app, abre la imagen, sacándonos de la app).
-
Entonces, del evento, obtenemos la propiedad dataTransfer y mediante la propiedad de getData de dataTransfer, la ejecutamos mandando el identificador del cual obtendremos el ID de la tarjeta.
- El signo de
+
al inicio dee.dataTransfer.getData('text')
es para convertir el valor a un número.
- El signo de
-
Luego llamaremos la función handleUpdateList que el componente nos pasa por props, (hay que desestructurarlo del componente).
- Le pasamos primero el id que obtuvimos de la propiedad getData de dataTransfer ya convertido en número.
- Después le pasamos el status que recibimos por props en el componente.
Finalmente llamamos handleDragging mandando el valor de false para indicar al usuario que ya no estamos arrastrando nada.
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault()
const id = +e.dataTransfer.getData('text')
handleUpdateList(id, status)
handleDragging(false)
}
Así se vería el código de src/components/ContainerCards.tsx
import { Data, Status } from "../interfaces"
import { CardItem } from "./CardItem"
interface Props {
items: Data[]
status: Status
isDragging: boolean
handleUpdateList: (id: number, status: Status) => void
handleDragging: (dragging: boolean) => void
}
export const ContainerCards = ({ items = [], status, isDragging, handleDragging, handleUpdateList }: Props) => {
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault()
handleUpdateList(+e.dataTransfer.getData('text'), status)
handleDragging(false)
}
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => e.preventDefault()
return (
<div
className={`layout-cards ${isDragging ? 'layout-dragging' : ''}`}
onDrop={handleDrop}
onDragOver={handleDragOver}
>
<p>{status} hero</p>
{
items.map(item => (
status === item.status
&& <CardItem
data={item}
key={item.id}
handleDragging={handleDragging}
/>
))
}
</div>
)
}
El resultado final debería verse de esta manera 🥳!
👉 Opcional. Refactorización del código en DragAndDrop.tsx
Tenemos bastante lógica en nuestro componente, por lo cual seria una buena opción crear un custom hook para administrar esa lógica.
Creamos una carpeta de src/hooks
y dentro un archivo llamado useDragAndDrop.ts
Primero definimos la función, la cual recibirá un estado inicial que sera de tipo arreglo de Data
export const useDragAndDrop = (initialState: Data[]) => {}
Del componente DragAndDrop.tsx recortamos toda la lógica y la colocamos en el custom hook.
El valor inicial del estado de listItems sera que nos pasen por parámetro del hook.
Y por ultimo retornamos como un objeto:
- isDragging.
- listItems.
- handleUpdateList.
- handleDragging.
import { useState } from "react"
import { Data, Status } from "../interfaces"
export const useDragAndDrop = (initialState: Data[]) => {
const [isDragging, setIsDragging] = useState(false)
const [listItems, setListItems] = useState<Data[]>(initialState)
const handleUpdateList = (id: number, status: Status) => {
let card = listItems.find(item => item.id === id)
if (card && card.status !== status) {
card.status = status
setListItems( prev => ([
card!,
...prev.filter(item => item.id !== id)
]))
}
}
const handleDragging = (dragging: boolean) => setIsDragging(dragging)
return {
isDragging,
listItems,
handleUpdateList,
handleDragging,
}
}
Ahora en el componente src/components/DragAndDrop.tsx
llamamos a nuestro custom hook.
Le mandamos la data a nuestro hook, por parámetro y solo desestructuramos las propiedades y listo!.
import { ContainerCards } from "./ContainerCards"
import { useDragAndDrop } from "../hooks/useDragAndDrop"
import { Status } from "../interfaces"
import { data } from "../assets"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
const { isDragging, listItems, handleDragging, handleUpdateList } = useDragAndDrop(data)
return (
<div className="grid">
{
typesHero.map(container => (
<ContainerCards
items={listItems}
status={container}
key={container}
isDragging={isDragging}
handleDragging={handleDragging}
handleUpdateList={handleUpdateList}
/>
))
}
</div>
)
}
Así quedara mas legible tu componente. 🎉
👉 Conclusión.
Este proceso es una de las formas de construir una aplicación con funcionalidad de Drag & Drop sin usar librerías externas.
Una forma de mejorar esta aplicación seria usando un administrador de estado para evitar estar pasando demasiadas props a los componentes.
Si quieres algo mas elaborado y expandir las funcionalidades, puedes optar por un paquete de terceros que te recomiendo bastante, y es
react-beautiful-dnd
, una librería muy buena y popular.
Espero haberte ayudado a entender como realizar este ejercicio,muchas gracias por llegar hasta aquí! 🤗❤️
Te invito a que comentes si es que este articulo te resulta útil o interesante, o si es que conoces alguna otra forma distinta o mejor de como hacer un drag & drop. 🙌
🟠 Demostración en vivo.
https://drag-and-drop-react-app.netlify.app
🟠 Código fuente.
Franklin361 / drag-and-drop-react
Creating an application using Drag & Drop with React JS 🤏
Creating an app using Drag and Drop with React without libraries 👆!
This time, we are going to implement the functionality to do a Drag & Drop with React JS and without any other external package or library!
Features ⚙️
- Card dragging.
- Dropping cards into a container.
- Sorting cards.
Technologies 🧪
- React JS
- TypeScript
- Vite JS
- Vanilla CSS 3
Installation 🧰
- Clone the repository (you need to have Git installed).
git clone https://github.com/Franklin361/drag-and-drop-react
- Install dependencies of the project.
npm install
- Run the project.
npm run dev
Top comments (2)
Generalmente publico cada viernes, pero a partir de hoy tomare un descanso de dos semanas ✈️ por lo que no publicare nada en ese lapso de tiempo 😔, buen día! ❤️
Gracias por tu ayuda Franklin es lo que estaba buscando entender