@dfosco/storyboard-react 4.0.0-beta.41 → 4.0.0-beta.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/Icon.jsx +4 -3
- package/src/Viewfinder.jsx +38 -2
- package/src/Viewfinder.module.css +135 -573
- package/src/canvas/PageSelector.jsx +37 -13
- package/src/canvas/PageSelector.module.css +7 -0
- package/src/context.jsx +5 -1
|
@@ -9,14 +9,19 @@ import styles from './PageSelector.module.css'
|
|
|
9
9
|
*
|
|
10
10
|
* @param {{ currentName: string, pages: Array<{ name: string, route: string, title: string }>, isLocalDev?: boolean }} props
|
|
11
11
|
*/
|
|
12
|
-
export default function PageSelector({ currentName, pages, isLocalDev = false }) {
|
|
12
|
+
export default function PageSelector({ currentName, pages: initialPages, isLocalDev = false }) {
|
|
13
13
|
const [open, setOpen] = useState(false)
|
|
14
14
|
const [adding, setAdding] = useState(false)
|
|
15
15
|
const [newName, setNewName] = useState('')
|
|
16
16
|
const [creating, setCreating] = useState(false)
|
|
17
|
+
const [pages, setPages] = useState(initialPages)
|
|
18
|
+
const [successMsg, setSuccessMsg] = useState(null)
|
|
17
19
|
const containerRef = useRef(null)
|
|
18
20
|
const inputRef = useRef(null)
|
|
19
21
|
|
|
22
|
+
// Sync pages when prop changes (e.g. HMR reload)
|
|
23
|
+
useEffect(() => { setPages(initialPages) }, [initialPages])
|
|
24
|
+
|
|
20
25
|
const currentPage = pages.find((p) => p.name === currentName)
|
|
21
26
|
const currentLabel = currentPage?.title || currentName.split('/').pop()
|
|
22
27
|
const currentIndex = pages.findIndex((p) => p.name === currentName)
|
|
@@ -46,18 +51,32 @@ export default function PageSelector({ currentName, pages, isLocalDev = false })
|
|
|
46
51
|
setCreating(false)
|
|
47
52
|
return
|
|
48
53
|
}
|
|
49
|
-
|
|
50
|
-
const kebab = trimmed
|
|
51
|
-
.replace(/[^a-zA-Z0-9\s_-]/g, '')
|
|
52
|
-
.trim()
|
|
53
|
-
.replace(/[\s_]+/g, '-')
|
|
54
|
-
.toLowerCase()
|
|
55
|
-
.replace(/-+/g, '-')
|
|
56
|
-
.replace(/^-|-$/g, '')
|
|
57
|
-
const route = folder ? `/${folder}/${kebab}` : `/${kebab}`
|
|
54
|
+
const route = result.route
|
|
58
55
|
const base = (import.meta.env?.BASE_URL || '/').replace(/\/$/, '')
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
const targetUrl = base + route
|
|
57
|
+
|
|
58
|
+
// Optimistically add the new page to the bottom of the list
|
|
59
|
+
const pageName = result.name || trimmed
|
|
60
|
+
setPages(prev => [...prev, { name: pageName, route, title: trimmed }])
|
|
61
|
+
|
|
62
|
+
// Show success confirmation and reset form
|
|
63
|
+
setSuccessMsg(`"${trimmed}" created`)
|
|
64
|
+
setAdding(false)
|
|
65
|
+
setNewName('')
|
|
66
|
+
setCreating(false)
|
|
67
|
+
|
|
68
|
+
// Navigate to the new page after Vite picks up the new file
|
|
69
|
+
if (import.meta.hot) {
|
|
70
|
+
const timer = setTimeout(() => {
|
|
71
|
+
window.location.href = targetUrl
|
|
72
|
+
}, 3000)
|
|
73
|
+
import.meta.hot.on('vite:beforeFullReload', () => {
|
|
74
|
+
clearTimeout(timer)
|
|
75
|
+
sessionStorage.setItem('sb-pending-navigate', targetUrl)
|
|
76
|
+
})
|
|
77
|
+
} else {
|
|
78
|
+
setTimeout(() => { window.location.href = targetUrl }, 1000)
|
|
79
|
+
}
|
|
61
80
|
} catch (err) {
|
|
62
81
|
console.error('Failed to create canvas page:', err)
|
|
63
82
|
setCreating(false)
|
|
@@ -77,6 +96,7 @@ export default function PageSelector({ currentName, pages, isLocalDev = false })
|
|
|
77
96
|
setOpen(false)
|
|
78
97
|
setAdding(false)
|
|
79
98
|
setNewName('')
|
|
99
|
+
setSuccessMsg(null)
|
|
80
100
|
}
|
|
81
101
|
}
|
|
82
102
|
document.addEventListener('mousedown', handleClick)
|
|
@@ -100,7 +120,8 @@ export default function PageSelector({ currentName, pages, isLocalDev = false })
|
|
|
100
120
|
return () => document.removeEventListener('keydown', handleKey)
|
|
101
121
|
}, [open, adding])
|
|
102
122
|
|
|
103
|
-
|
|
123
|
+
// Show selector when there are multiple pages, or in dev mode (to allow adding pages)
|
|
124
|
+
if (!pages || (pages.length < 2 && !isLocalDev)) return null
|
|
104
125
|
|
|
105
126
|
return (
|
|
106
127
|
<nav ref={containerRef} className={styles.container} aria-label="Canvas pages">
|
|
@@ -189,6 +210,9 @@ export default function PageSelector({ currentName, pages, isLocalDev = false })
|
|
|
189
210
|
+ Add new page
|
|
190
211
|
</li>
|
|
191
212
|
)}
|
|
213
|
+
{successMsg && (
|
|
214
|
+
<li className={styles.successMsg}>✓ {successMsg}</li>
|
|
215
|
+
)}
|
|
192
216
|
</>
|
|
193
217
|
)}
|
|
194
218
|
</ul>
|
package/src/context.jsx
CHANGED
|
@@ -232,7 +232,11 @@ export default function StoryboardProvider({ flowName, sceneName, recordName, re
|
|
|
232
232
|
if (canvasId) {
|
|
233
233
|
const canvasData = canvases?.[canvasId]
|
|
234
234
|
const group = canvasData?._group
|
|
235
|
-
|
|
235
|
+
// Include the current canvas as a sibling even if it's the only page in its group,
|
|
236
|
+
// so the PageSelector can render and allow adding new pages.
|
|
237
|
+
const siblingPages = group
|
|
238
|
+
? canvasGroupMap.get(group) || []
|
|
239
|
+
: [{ name: canvasId, route: canvasData?._route || `/canvas/${canvasId}`, title: canvasData?.title || canvasId.split('/').pop() }]
|
|
236
240
|
const canvasMeta = canvasData?._canvasMeta || null
|
|
237
241
|
const canvasValue = {
|
|
238
242
|
data: null,
|