@dfosco/storyboard-react 4.2.2 → 4.2.3
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/AuthModal/AuthModal.jsx +1 -1
- package/src/BranchBar/BranchBar.jsx +18 -1
- package/src/BranchBar/BranchBar.module.css +9 -1
- package/src/CommandPalette/CommandPalette.jsx +1 -1
- package/src/canvas/CanvasPage.jsx +5 -5
- package/src/canvas/widgets/ComponentSetWidget.jsx +14 -5
- package/src/context.jsx +1 -1
- package/src/index.js +1 -1
- package/src/story/ComponentSetPage.jsx +14 -2
- package/src/story/ComponentSetPage.module.css +20 -12
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dfosco/storyboard-react",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@base-ui/react": "^1.4.0",
|
|
7
|
-
"@dfosco/storyboard-core": "4.2.
|
|
8
|
-
"@dfosco/tiny-canvas": "4.2.
|
|
7
|
+
"@dfosco/storyboard-core": "4.2.3",
|
|
8
|
+
"@dfosco/tiny-canvas": "4.2.3",
|
|
9
9
|
"@neodrag/react": "^2.3.1",
|
|
10
10
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
11
11
|
"@radix-ui/react-visually-hidden": "^1.2.4",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AuthModal — Global PAT entry dialog for comments authentication.
|
|
3
3
|
* Mounted at app root, triggered by:
|
|
4
|
-
* -
|
|
4
|
+
* - CoreUIBar (comments tool / "C" shortcut) via 'storyboard:open-auth-modal' event
|
|
5
5
|
* - ViewfinderNew sidebar login button via same event
|
|
6
6
|
*/
|
|
7
7
|
import { useState, useEffect, useCallback } from 'react'
|
|
@@ -15,6 +15,15 @@ function checkLocalDev() {
|
|
|
15
15
|
return window.__SB_LOCAL_DEV__ === true
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/** Read the dev domain color injected by the server plugin, validated via CSS.supports. */
|
|
19
|
+
function getDevDomainColor() {
|
|
20
|
+
if (typeof window === 'undefined') return null
|
|
21
|
+
const color = window.__SB_DEV_DOMAIN_COLOR__
|
|
22
|
+
if (!color) return null
|
|
23
|
+
if (typeof CSS !== 'undefined' && CSS.supports && !CSS.supports('color', color)) return null
|
|
24
|
+
return color
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
export default function BranchBar({ basePath }) {
|
|
19
28
|
const [hidden, setHidden] = useState(
|
|
20
29
|
() => typeof document !== 'undefined' && document.documentElement.classList.contains('storyboard-chrome-hidden')
|
|
@@ -33,6 +42,7 @@ export default function BranchBar({ basePath }) {
|
|
|
33
42
|
|
|
34
43
|
const isLocalDev = checkLocalDev()
|
|
35
44
|
const isOnBranch = currentBranch !== 'main'
|
|
45
|
+
const domainColor = isLocalDev ? getDevDomainColor() : null
|
|
36
46
|
|
|
37
47
|
useEffect(() => {
|
|
38
48
|
const observer = new MutationObserver(() => {
|
|
@@ -52,8 +62,15 @@ export default function BranchBar({ basePath }) {
|
|
|
52
62
|
|
|
53
63
|
return (
|
|
54
64
|
<div className={css.bar} data-branch-bar>
|
|
55
|
-
<div
|
|
65
|
+
<div
|
|
66
|
+
className={`${css.barInner}${isLocalDev ? '' : ` ${css.barProd}`}`}
|
|
67
|
+
style={domainColor ? { '--sb-branch-bar-bg': domainColor } : undefined}
|
|
68
|
+
>
|
|
56
69
|
<span className={css.barLabel}>
|
|
70
|
+
{isLocalDev && window.__SB_DEV_DOMAIN__ && <>
|
|
71
|
+
<span className={css.barDomainName}>⌘ {window.__SB_DEV_DOMAIN__}</span>
|
|
72
|
+
<span className={css.barSeparator}>·</span>
|
|
73
|
+
</>}
|
|
57
74
|
<GitBranchIcon size={12} />
|
|
58
75
|
<span className={css.barBranchName}>{currentBranch}</span>
|
|
59
76
|
{isLocalDev && <>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
justify-content: center;
|
|
17
17
|
gap: 8px;
|
|
18
18
|
height: 32px;
|
|
19
|
-
background: hsl(212, 92%, 45%);
|
|
19
|
+
background: var(--sb-branch-bar-bg, hsl(212, 92%, 45%));
|
|
20
20
|
color: #fff;
|
|
21
21
|
padding: 4px 12px;
|
|
22
22
|
position: relative;
|
|
@@ -72,6 +72,14 @@
|
|
|
72
72
|
white-space: nowrap;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
.barDomainName {
|
|
76
|
+
font-weight: 400;
|
|
77
|
+
max-width: 200px;
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
text-overflow: ellipsis;
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
.barSeparator {
|
|
76
84
|
opacity: 0.6;
|
|
77
85
|
margin: 0 2px;
|
|
@@ -880,7 +880,7 @@ function buildPaletteItems(basePath, onCreateAction, onNavigateToPage) {
|
|
|
880
880
|
|
|
881
881
|
/**
|
|
882
882
|
* StoryboardCommandPalette — React command palette using react-cmdk.
|
|
883
|
-
* Mounted at app root, listens for custom events from
|
|
883
|
+
* Mounted at app root, listens for custom events from CoreUIBar.
|
|
884
884
|
*/
|
|
885
885
|
export default function StoryboardCommandPalette({ basePath }) {
|
|
886
886
|
const [open, setOpen] = useState(false)
|
|
@@ -2075,7 +2075,7 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
|
|
|
2075
2075
|
return () => document.removeEventListener('storyboard:canvas:toggle-snap', handleSnapToggle)
|
|
2076
2076
|
}, [canvasId])
|
|
2077
2077
|
|
|
2078
|
-
// Broadcast snap state to
|
|
2078
|
+
// Broadcast snap state to toolbar
|
|
2079
2079
|
useEffect(() => {
|
|
2080
2080
|
document.dispatchEvent(new CustomEvent('storyboard:canvas:snap-state', {
|
|
2081
2081
|
detail: { snapEnabled }
|
|
@@ -2083,7 +2083,7 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
|
|
|
2083
2083
|
snapEnabledRef.current = snapEnabled
|
|
2084
2084
|
}, [snapEnabled])
|
|
2085
2085
|
|
|
2086
|
-
// Respond to snap-state requests from
|
|
2086
|
+
// Respond to snap-state requests from toolbar (handles mount-order race)
|
|
2087
2087
|
useEffect(() => {
|
|
2088
2088
|
function handleRequest() {
|
|
2089
2089
|
document.dispatchEvent(new CustomEvent('storyboard:canvas:snap-state', {
|
|
@@ -2094,7 +2094,7 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
|
|
|
2094
2094
|
return () => document.removeEventListener('storyboard:canvas:snap-state-request', handleRequest)
|
|
2095
2095
|
}, [])
|
|
2096
2096
|
|
|
2097
|
-
// Listen for gridSize from
|
|
2097
|
+
// Listen for gridSize from toolbar config
|
|
2098
2098
|
useEffect(() => {
|
|
2099
2099
|
function handleGridSize(e) {
|
|
2100
2100
|
const size = e.detail?.gridSize
|
|
@@ -2638,7 +2638,7 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
|
|
|
2638
2638
|
return () => document.removeEventListener('keydown', handleKeyDown)
|
|
2639
2639
|
}, [handleUndo, handleRedo, handleDuplicateSelected, handleDuplicateWithConnectors, handleSelectAll])
|
|
2640
2640
|
|
|
2641
|
-
// Listen for undo/redo from CoreUIBar
|
|
2641
|
+
// Listen for undo/redo from CoreUIBar
|
|
2642
2642
|
useEffect(() => {
|
|
2643
2643
|
function handleUndoEvent() { handleUndo() }
|
|
2644
2644
|
function handleRedoEvent() { handleRedo() }
|
|
@@ -2650,7 +2650,7 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
|
|
|
2650
2650
|
}
|
|
2651
2651
|
}, [handleUndo, handleRedo])
|
|
2652
2652
|
|
|
2653
|
-
// Broadcast undo/redo availability to
|
|
2653
|
+
// Broadcast undo/redo availability to toolbar
|
|
2654
2654
|
useEffect(() => {
|
|
2655
2655
|
document.dispatchEvent(new CustomEvent('storyboard:canvas:undo-redo-state', {
|
|
2656
2656
|
detail: { canUndo: undoRedo.canUndo, canRedo: undoRedo.canRedo }
|
|
@@ -73,15 +73,24 @@ export default forwardRef(function ComponentSetWidget({ id: widgetId, props, onU
|
|
|
73
73
|
useEffect(() => {
|
|
74
74
|
function handleMessage(e) {
|
|
75
75
|
if (e.source !== iframeRef.current?.contentWindow) return
|
|
76
|
-
if (e.data?.type
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
if (e.data?.type === 'storyboard:component-set:select') {
|
|
77
|
+
const newSelected = e.data.exportName || ''
|
|
78
|
+
if (newSelected !== selected) {
|
|
79
|
+
onUpdate?.({ selected: newSelected })
|
|
80
|
+
}
|
|
81
|
+
} else if (e.data?.type === 'storyboard:component-set:resize') {
|
|
82
|
+
// Auto-size widget to fit the grid content (+ header height)
|
|
83
|
+
const headerH = 32
|
|
84
|
+
const newW = Math.max(200, Math.ceil(e.data.width))
|
|
85
|
+
const newH = Math.max(60, Math.ceil(e.data.height) + headerH)
|
|
86
|
+
if (newW !== width || newH !== height) {
|
|
87
|
+
onUpdate?.({ width: newW, height: newH })
|
|
88
|
+
}
|
|
80
89
|
}
|
|
81
90
|
}
|
|
82
91
|
window.addEventListener('message', handleMessage)
|
|
83
92
|
return () => window.removeEventListener('message', handleMessage)
|
|
84
|
-
}, [selected, onUpdate])
|
|
93
|
+
}, [selected, width, height, onUpdate])
|
|
85
94
|
|
|
86
95
|
const handleResize = useCallback((w, h) => {
|
|
87
96
|
onUpdate?.({ width: w, height: h })
|
package/src/context.jsx
CHANGED
|
@@ -263,7 +263,7 @@ function StoryboardProviderInner({ flowName, sceneName, recordName, recordParam,
|
|
|
263
263
|
cleanup = mountDesignModes()
|
|
264
264
|
})
|
|
265
265
|
.catch(() => {
|
|
266
|
-
//
|
|
266
|
+
// UI not available — degrade gracefully
|
|
267
267
|
})
|
|
268
268
|
|
|
269
269
|
return () => cleanup?.()
|
package/src/index.js
CHANGED
|
@@ -34,7 +34,7 @@ export { installHashPreserver } from './hashPreserver.js'
|
|
|
34
34
|
export { FormContext } from './context/FormContext.js'
|
|
35
35
|
|
|
36
36
|
// Design mode hook (keep — React apps may still read mode state)
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
|
|
39
39
|
// Workspace dashboard
|
|
40
40
|
export { default as Workspace } from './Workspace.jsx'
|
|
@@ -84,7 +84,8 @@ export default function ComponentSetPage({ name }) {
|
|
|
84
84
|
|
|
85
85
|
const gridRef = useRef(null)
|
|
86
86
|
|
|
87
|
-
// Measure all cell content elements and snap cells to the largest
|
|
87
|
+
// Measure all cell content elements and snap cells to the largest.
|
|
88
|
+
// Posts the total grid size to the parent widget so it can auto-size.
|
|
88
89
|
useLayoutEffect(() => {
|
|
89
90
|
const grid = gridRef.current
|
|
90
91
|
if (!grid || !exports) return
|
|
@@ -101,6 +102,17 @@ export default function ComponentSetPage({ name }) {
|
|
|
101
102
|
}
|
|
102
103
|
grid.style.setProperty('--cell-snap-w', `${maxW}px`)
|
|
103
104
|
grid.style.setProperty('--cell-snap-h', `${maxH}px`)
|
|
105
|
+
|
|
106
|
+
// Post total grid size to parent widget
|
|
107
|
+
if (isEmbed && window.parent !== window) {
|
|
108
|
+
requestAnimationFrame(() => {
|
|
109
|
+
window.parent.postMessage({
|
|
110
|
+
type: 'storyboard:component-set:resize',
|
|
111
|
+
width: grid.scrollWidth,
|
|
112
|
+
height: grid.scrollHeight,
|
|
113
|
+
}, '*')
|
|
114
|
+
})
|
|
115
|
+
}
|
|
104
116
|
}
|
|
105
117
|
|
|
106
118
|
// Measure after fonts load and initial paint
|
|
@@ -110,7 +122,7 @@ export default function ComponentSetPage({ name }) {
|
|
|
110
122
|
const ro = new ResizeObserver(measure)
|
|
111
123
|
for (const el of cells) ro.observe(el)
|
|
112
124
|
return () => ro.disconnect()
|
|
113
|
-
}, [exports, layout])
|
|
125
|
+
}, [exports, layout, isEmbed])
|
|
114
126
|
|
|
115
127
|
const handleSelect = useCallback((exportName) => {
|
|
116
128
|
const params = new URLSearchParams(location.search)
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/* ComponentSetPage — grid layout for all exports of a story */
|
|
2
2
|
|
|
3
3
|
.grid {
|
|
4
|
-
background-color: var(--bgColor-
|
|
4
|
+
background-color: var(--bgColor-default, #ffffff);
|
|
5
5
|
display: flex;
|
|
6
6
|
flex-wrap: nowrap;
|
|
7
|
-
gap:
|
|
8
|
-
padding:
|
|
9
|
-
min-height: 100vh;
|
|
7
|
+
gap: 0;
|
|
8
|
+
padding: 0;
|
|
10
9
|
width: max-content;
|
|
11
10
|
min-width: 100%;
|
|
12
11
|
box-sizing: border-box;
|
|
@@ -24,20 +23,30 @@
|
|
|
24
23
|
flex: 0 0 auto;
|
|
25
24
|
display: flex;
|
|
26
25
|
flex-direction: column;
|
|
27
|
-
border:
|
|
28
|
-
border-radius:
|
|
26
|
+
border: 1px solid var(--borderColor-muted, #d8dee4);
|
|
27
|
+
border-radius: 0;
|
|
29
28
|
overflow: hidden;
|
|
30
29
|
transition: border-color 120ms ease, box-shadow 120ms ease;
|
|
31
30
|
position: relative;
|
|
32
|
-
background: var(--bgColor-
|
|
31
|
+
background: var(--bgColor-muted, #f6f8fa);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Collapse double borders between adjacent cells */
|
|
35
|
+
.grid[data-layout="horizontal"] .cell + .cell {
|
|
36
|
+
border-left: none;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
.grid[data-layout="vertical"] .cell + .cell {
|
|
40
|
+
border-top: none;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* In horizontal layout, cells snap to widest and tallest component */
|
|
36
44
|
.grid[data-layout="horizontal"] .cell {
|
|
37
45
|
min-width: var(--cell-snap-w, 200px);
|
|
46
|
+
min-height: var(--cell-snap-h, 60px);
|
|
38
47
|
}
|
|
39
48
|
|
|
40
|
-
/* In vertical layout,
|
|
49
|
+
/* In vertical layout, cells snap to widest and tallest component */
|
|
41
50
|
.grid[data-layout="vertical"] .cell {
|
|
42
51
|
min-height: var(--cell-snap-h, 120px);
|
|
43
52
|
min-width: 100%;
|
|
@@ -58,14 +67,12 @@
|
|
|
58
67
|
font-size: 11px;
|
|
59
68
|
font-weight: 600;
|
|
60
69
|
color: var(--fgColor-muted, #656d76);
|
|
61
|
-
background: var(--bgColor-
|
|
70
|
+
background: var(--bgColor-default, #ffffff);
|
|
62
71
|
border-bottom: 1px solid var(--borderColor-muted, #d8dee4);
|
|
63
72
|
cursor: pointer;
|
|
64
73
|
user-select: none;
|
|
65
74
|
transition: background 100ms ease, color 100ms ease;
|
|
66
75
|
flex-shrink: 0;
|
|
67
|
-
/* Round top corners to match cell */
|
|
68
|
-
border-radius: 6px 6px 0 0;
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
.cellLabel:hover {
|
|
@@ -99,6 +106,7 @@
|
|
|
99
106
|
flex: 1;
|
|
100
107
|
overflow: visible;
|
|
101
108
|
position: relative;
|
|
109
|
+
padding: 12px;
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
.error {
|