Skip to content

Commit 66d84d4

Browse files
AmitMYclaude
andcommitted
Add drag and drop reordering and GitHub Pages deployment
- Implement drag and drop functionality to reorder signs - Add visual feedback during drag (opacity and border indicators) - Configure base path for GitHub Pages deployment - Add GitHub Actions workflow for automatic deployment 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 64b4c86 commit 66d84d4

File tree

3 files changed

+136
-3
lines changed

3 files changed

+136
-3
lines changed

.github/workflows/deploy.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
concurrency:
14+
group: "pages"
15+
cancel-in-progress: false
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Setup Node
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: 20
28+
cache: 'npm'
29+
30+
- name: Install dependencies
31+
run: npm ci
32+
33+
- name: Build
34+
run: npm run build
35+
36+
- name: Setup Pages
37+
uses: actions/configure-pages@v4
38+
39+
- name: Upload artifact
40+
uses: actions/upload-pages-artifact@v3
41+
with:
42+
path: './dist'
43+
44+
deploy:
45+
environment:
46+
name: github-pages
47+
url: ${{ steps.deployment.outputs.page_url }}
48+
runs-on: ubuntu-latest
49+
needs: build
50+
steps:
51+
- name: Deploy to GitHub Pages
52+
id: deployment
53+
uses: actions/deploy-pages@v4

src/App.jsx

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ function App() {
3838
const [insertAfterIndex, setInsertAfterIndex] = useState(null)
3939
const [selectedSignIndex, setSelectedSignIndex] = useState(null)
4040
const [contextMenu, setContextMenu] = useState(null)
41+
const [draggedIndex, setDraggedIndex] = useState(null)
42+
const [dragOverIndex, setDragOverIndex] = useState(null)
4143
const fileInputRef = useRef(null)
4244

4345
const vpRef = useRef(null)
@@ -169,16 +171,79 @@ function App() {
169171
}
170172
}
171173

174+
const handleDragStart = (e) => {
175+
const path = e.composedPath()
176+
const index = getIndexFromSign(path)
177+
if (index !== -1) {
178+
setDraggedIndex(index)
179+
e.dataTransfer.effectAllowed = 'move'
180+
}
181+
}
182+
183+
const handleDragOver = (e) => {
184+
e.preventDefault()
185+
e.dataTransfer.dropEffect = 'move'
186+
187+
const path = e.composedPath()
188+
const index = getIndexFromSign(path)
189+
if (index !== -1 && index !== draggedIndex) {
190+
setDragOverIndex(index)
191+
}
192+
}
193+
194+
const handleDragEnd = () => {
195+
setDraggedIndex(null)
196+
setDragOverIndex(null)
197+
}
198+
199+
const handleDrop = (e) => {
200+
e.preventDefault()
201+
202+
if (draggedIndex === null) return
203+
204+
const path = e.composedPath()
205+
const dropIndex = getIndexFromSign(path)
206+
207+
if (dropIndex !== -1 && dropIndex !== draggedIndex) {
208+
const newSigns = [...signs]
209+
const [draggedSign] = newSigns.splice(draggedIndex, 1)
210+
newSigns.splice(dropIndex, 0, draggedSign)
211+
updateSignsWithHistory(newSigns)
212+
213+
// Update selected index if needed
214+
if (selectedSignIndex === draggedIndex) {
215+
setSelectedSignIndex(dropIndex)
216+
} else if (selectedSignIndex !== null) {
217+
if (draggedIndex < selectedSignIndex && dropIndex >= selectedSignIndex) {
218+
setSelectedSignIndex(selectedSignIndex - 1)
219+
} else if (draggedIndex > selectedSignIndex && dropIndex <= selectedSignIndex) {
220+
setSelectedSignIndex(selectedSignIndex + 1)
221+
}
222+
}
223+
}
224+
225+
setDraggedIndex(null)
226+
setDragOverIndex(null)
227+
}
228+
172229
vpElement.addEventListener('click', handleClick)
173230
vpElement.addEventListener('dblclick', handleDoubleClick)
174231
vpElement.addEventListener('contextmenu', handleContextMenu)
232+
vpElement.addEventListener('dragstart', handleDragStart)
233+
vpElement.addEventListener('dragover', handleDragOver)
234+
vpElement.addEventListener('dragend', handleDragEnd)
235+
vpElement.addEventListener('drop', handleDrop)
175236

176237
return () => {
177238
vpElement.removeEventListener('click', handleClick)
178239
vpElement.removeEventListener('dblclick', handleDoubleClick)
179240
vpElement.removeEventListener('contextmenu', handleContextMenu)
241+
vpElement.removeEventListener('dragstart', handleDragStart)
242+
vpElement.removeEventListener('dragover', handleDragOver)
243+
vpElement.removeEventListener('dragend', handleDragEnd)
244+
vpElement.removeEventListener('drop', handleDrop)
180245
}
181-
}, [signs])
246+
}, [signs, draggedIndex])
182247

183248
// Close context menu and clear selection when clicking anywhere outside
184249
useEffect(() => {
@@ -196,7 +261,7 @@ function App() {
196261
return () => document.removeEventListener('click', handleClick)
197262
}, [])
198263

199-
// Update selected styles on signs in shadow DOM
264+
// Update selected styles and draggable attribute on signs in shadow DOM
200265
useEffect(() => {
201266
const vpElement = vpRef.current
202267
if (!vpElement) return
@@ -208,6 +273,10 @@ function App() {
208273
const allSigns = Array.from(vpElement.shadowRoot.querySelectorAll('sgnw-sign, sgnw-symbol'))
209274

210275
allSigns.forEach((sign, index) => {
276+
// Make draggable
277+
sign.setAttribute('draggable', 'true')
278+
279+
// Apply selection styles
211280
if (index === selectedSignIndex) {
212281
sign.style.outline = '3px solid #ef4444'
213282
sign.style.outlineOffset = '2px'
@@ -219,11 +288,21 @@ function App() {
219288
sign.style.backgroundColor = ''
220289
sign.style.borderRadius = ''
221290
}
291+
292+
// Apply drag styles
293+
if (index === draggedIndex) {
294+
sign.style.opacity = '0.5'
295+
} else if (index === dragOverIndex) {
296+
sign.style.borderLeft = '3px solid #3b82f6'
297+
} else {
298+
sign.style.opacity = ''
299+
sign.style.borderLeft = ''
300+
}
222301
})
223302
}, 10)
224303

225304
return () => clearTimeout(timeout)
226-
}, [selectedSignIndex, signs])
305+
}, [selectedSignIndex, signs, draggedIndex, dragOverIndex])
227306

228307
// Keyboard shortcuts for undo/redo and selection
229308
useEffect(() => {

vite.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { VitePWA } from 'vite-plugin-pwa'
44

55
// https://vite.dev/config/
66
export default defineConfig({
7+
base: '/signwriting-word-processor/',
78
plugins: [
89
react(),
910
VitePWA({

0 commit comments

Comments
 (0)