Skip to content

Commit

Permalink
add react-dynamic example
Browse files Browse the repository at this point in the history
  • Loading branch information
TahaSh committed Sep 8, 2024
1 parent 1ae2f89 commit 55652df
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 0 deletions.
114 changes: 114 additions & 0 deletions examples/react-dynamic/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { useEffect } from 'react'
import './style.css'
import { createSwapy } from '../../src/index'
import { useState } from 'react'
import { SlotItemMap, Swapy } from '../../src/instance'
import { useMemo } from 'react'
import { useRef } from 'react'

function App() {
const swapyRef = useRef<Swapy | null>(null)

// Your items.
const [items, setItems] = useState([
{ id: '1', title: 'A' },
{ id: '2', title: 'B' },
{ id: '3', title: 'C' },
])

// Define a state for maintain the mapping between slots and items.
const [slotItemsMap, setSlotItemsMap] = useState<SlotItemMap>([...items.map(item => ({
slotId: item.id,
itemId: item.id
})),
// Defining an empty slot by setting itemId to null.
{ slotId: `${Math.round(Math.random() * 99999)}`, itemId: null }
])

// This is what you'll use to display your items.
const slottedItems = useMemo(() => slotItemsMap.map(({ slotId, itemId }) => ({
slotId,
itemId,
item: items.find(item => item.id === itemId)
})), [items, slotItemsMap])

useEffect(() => {
// Get the newly added items and convert them to slotItem objects
const newItems = items.filter(item => !slotItemsMap.some(slotItem => slotItem.itemId === item.id)).map(item => ({
slotId: item.id,
itemId: item.id
}))

// Remove items from slotItemsMap if they no longer exist in items
const withoutRemovedItems = slotItemsMap.filter(slotItem =>
items.some(item => item.id === slotItem.itemId) || !slotItem.itemId
)

/******* Below is how you would remove items and keep their slots empty ******/
// const withoutRemovedItems = slotItemsMap.map(slotItem => {
// if (!items.some(item => item.id === slotItem.itemId)) {
// return { slotId: slotItem.slotId, itemId: null }
// }
// return slotItem
// })

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

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

useEffect(() => {
const container = document.querySelector('.container')!
swapyRef.current = createSwapy(container, {
manualSwap: true
})

swapyRef.current.onSwap(({ data }) => {
// You need to call setData because it's a manualSwap instance
swapyRef.current?.setData({ array: data.array })
setSlotItemsMap(data.array)
})

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

return (
<div className='app'>

{/* ADD BUTTON */}
<button className='add' onClick={() => {
const id = `${Math.round(Math.random() * 99999)}`
const updatedItems = [...items, { id, title: id }]
setItems(updatedItems)
}}>Add</button>

<div className="container">
{slottedItems.map(({ itemId, slotId, item }) => (
<div className="slot" data-swapy-slot={slotId} key={slotId}>

{/* ITEM */}
{item ?
<div className="item" data-swapy-item={itemId} key={itemId}>
<div className="handle" data-swapy-handle></div>
<div>{item.title}</div>

{/* DELETE ITEM BUTTON */}
<button className='delete'
onClick={() => {
const updatedItems = items.filter(i => i.id !== item.id)
setItems(updatedItems)
}}>x</button>

</div>
: null}
</div>
))}
</div>
</div>
)
}

export default App
16 changes: 16 additions & 0 deletions examples/react-dynamic/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React Swapy Example</title>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.0.2/tailwind.min.css"
rel="stylesheet"
/>
</head>
<body>
<div id="app"></div>
<script src="./main.tsx" type="module"></script>
</body>
</html>
5 changes: 5 additions & 0 deletions examples/react-dynamic/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createRoot } from 'react-dom/client'
import App from './App'

const root = createRoot(document.getElementById('app')!)
root.render(<App />)
100 changes: 100 additions & 0 deletions examples/react-dynamic/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
* {
box-sizing: border-box;
}

:root {
background: #242424;
}

body,
#app {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
width: 100%;
min-height: 100dvh;
display: flex;
justify-content: center;
align-items: center;
padding: 0;
margin: 0;
}

.app {
width: 100%;
max-width: 600px;
}

.container {
display: flex;
flex-direction: column;
gap: 5px;
width: 100%;
max-width: 500px;
padding: 10px;
}

.second-row {
display: flex;
gap: 5px;
}

.slot {
background: #111;
flex: 1;
flex-basis: 80px;
height: 80px;
}

.item {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 40px;
user-select: none;
-webkit-user-select: none;
position: relative;
background: #508db9;
}

[data-swapy-highlighted] {
background: #444;
}

.handle {
position: absolute;
left: 0;
top: 0;
width: 40px;
height: 100%;
background: rgba(0, 0, 0, 0.5);
cursor: pointer;
}

.add {
position: fixed;
top: 10px;
left: 10px;
border-radius: 5px;
background: white;
width: 100px;
padding: 5px 0;
cursor: pointer;
}

.delete {
color: #222;
background: rgba(255, 255, 255, 0.5);
width: 30px;
height: 30px;
font-size: 18px;
border-radius: 50%;
position: absolute;
top: 10px;
right: 10px;
display: flex;
align-items: center;
justify-content: center;
}

0 comments on commit 55652df

Please sign in to comment.