@dfosco/storyboard-react 1.12.0 → 1.14.0
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 +1 -1
- package/src/Viewfinder.jsx +39 -25
- package/src/Viewfinder.module.css +39 -4
- package/src/context.jsx +5 -2
package/package.json
CHANGED
package/src/Viewfinder.jsx
CHANGED
|
@@ -73,6 +73,15 @@ function PlaceholderGraphic({ name }) {
|
|
|
73
73
|
)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Derive the current branch label from the base path.
|
|
78
|
+
* Branch deploy folders use the convention `branch--<name>/`.
|
|
79
|
+
*/
|
|
80
|
+
function getCurrentBranch(basePath) {
|
|
81
|
+
const match = (basePath || '').match(/\/branch--([^/]+)\/?$/)
|
|
82
|
+
return match ? match[1] : 'main'
|
|
83
|
+
}
|
|
84
|
+
|
|
76
85
|
/**
|
|
77
86
|
* Viewfinder — scene index and branch preview dashboard.
|
|
78
87
|
*
|
|
@@ -99,6 +108,8 @@ export default function Viewfinder({ scenes = {}, pageModules = {}, basePath, ti
|
|
|
99
108
|
return base.replace(/\/branch--[^/]*\/$/, '/')
|
|
100
109
|
}, [basePath])
|
|
101
110
|
|
|
111
|
+
const currentBranch = useMemo(() => getCurrentBranch(basePath), [basePath])
|
|
112
|
+
|
|
102
113
|
useEffect(() => {
|
|
103
114
|
const url = `${branchBasePath}branches.json`
|
|
104
115
|
fetch(url)
|
|
@@ -107,13 +118,39 @@ export default function Viewfinder({ scenes = {}, pageModules = {}, basePath, ti
|
|
|
107
118
|
.catch(() => setBranches([]))
|
|
108
119
|
}, [branchBasePath])
|
|
109
120
|
|
|
121
|
+
const handleBranchChange = (e) => {
|
|
122
|
+
const folder = e.target.value
|
|
123
|
+
if (folder) {
|
|
124
|
+
window.location.href = `${branchBasePath}${folder}/`
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
110
128
|
return (
|
|
111
129
|
<div className={styles.container}>
|
|
112
130
|
<header className={styles.header}>
|
|
113
|
-
<
|
|
131
|
+
<div className={styles.headerTop}>
|
|
132
|
+
<h1 className={styles.title}>{title}</h1>
|
|
133
|
+
{branches && branches.length > 0 && (
|
|
134
|
+
<div className={styles.branchDropdown}>
|
|
135
|
+
<label className={styles.branchLabel} htmlFor="branch-select">Branch</label>
|
|
136
|
+
<select
|
|
137
|
+
id="branch-select"
|
|
138
|
+
className={styles.branchSelect}
|
|
139
|
+
defaultValue=""
|
|
140
|
+
onChange={handleBranchChange}
|
|
141
|
+
>
|
|
142
|
+
<option value="" disabled>{currentBranch}</option>
|
|
143
|
+
{branches.map((b) => (
|
|
144
|
+
<option key={b.folder} value={b.folder}>
|
|
145
|
+
{b.branch}
|
|
146
|
+
</option>
|
|
147
|
+
))}
|
|
148
|
+
</select>
|
|
149
|
+
</div>
|
|
150
|
+
)}
|
|
151
|
+
</div>
|
|
114
152
|
<p className={styles.subtitle}>
|
|
115
153
|
{sceneNames.length} scene{sceneNames.length !== 1 ? 's' : ''}
|
|
116
|
-
{branches && branches.length > 0 ? ` · ${branches.length} branch preview${branches.length !== 1 ? 's' : ''}` : ''}
|
|
117
154
|
</p>
|
|
118
155
|
</header>
|
|
119
156
|
|
|
@@ -149,29 +186,6 @@ export default function Viewfinder({ scenes = {}, pageModules = {}, basePath, ti
|
|
|
149
186
|
</div>
|
|
150
187
|
</section>
|
|
151
188
|
)}
|
|
152
|
-
|
|
153
|
-
{branches && branches.length > 0 && (
|
|
154
|
-
<section className={styles.branchSection}>
|
|
155
|
-
<h2 className={styles.sectionTitle}>Branch Previews</h2>
|
|
156
|
-
<div className={styles.grid}>
|
|
157
|
-
{branches.map((b) => (
|
|
158
|
-
<a
|
|
159
|
-
key={b.folder}
|
|
160
|
-
href={`${branchBasePath}${b.folder}/`}
|
|
161
|
-
className={styles.card}
|
|
162
|
-
>
|
|
163
|
-
<div className={styles.thumbnail}>
|
|
164
|
-
<PlaceholderGraphic name={b.branch} />
|
|
165
|
-
</div>
|
|
166
|
-
<div className={styles.cardBody}>
|
|
167
|
-
<p className={styles.sceneName}>{b.branch}</p>
|
|
168
|
-
<p className={styles.branchMeta}>{b.folder}</p>
|
|
169
|
-
</div>
|
|
170
|
-
</a>
|
|
171
|
-
))}
|
|
172
|
-
</div>
|
|
173
|
-
</section>
|
|
174
|
-
)}
|
|
175
189
|
</div>
|
|
176
190
|
)
|
|
177
191
|
}
|
|
@@ -100,15 +100,50 @@
|
|
|
100
100
|
max-width: 960px;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
.
|
|
104
|
-
|
|
103
|
+
.headerTop {
|
|
104
|
+
display: flex;
|
|
105
|
+
align-items: baseline;
|
|
106
|
+
justify-content: space-between;
|
|
107
|
+
gap: 16px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.branchDropdown {
|
|
111
|
+
display: flex;
|
|
112
|
+
align-items: center;
|
|
113
|
+
gap: 8px;
|
|
114
|
+
flex-shrink: 0;
|
|
105
115
|
}
|
|
106
116
|
|
|
107
|
-
.
|
|
117
|
+
.branchLabel {
|
|
108
118
|
font-size: 12px;
|
|
119
|
+
font-weight: 500;
|
|
109
120
|
color: var(--fgColor-muted, #848d97);
|
|
110
|
-
|
|
121
|
+
white-space: nowrap;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.branchSelect {
|
|
125
|
+
appearance: none;
|
|
126
|
+
background-color: var(--bgColor-muted, #161b22);
|
|
127
|
+
color: var(--fgColor-default, #e6edf3);
|
|
128
|
+
border: 1px solid var(--borderColor-default, #30363d);
|
|
129
|
+
border-radius: 6px;
|
|
130
|
+
padding: 4px 28px 4px 10px;
|
|
131
|
+
font-size: 13px;
|
|
111
132
|
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%23848d97'%3E%3Cpath d='M6 8.5L1.5 4h9L6 8.5z'/%3E%3C/svg%3E");
|
|
135
|
+
background-repeat: no-repeat;
|
|
136
|
+
background-position: right 8px center;
|
|
137
|
+
min-width: 140px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.branchSelect:hover {
|
|
141
|
+
border-color: var(--borderColor-accent-emphasis, #1f6feb);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.branchSelect:focus-visible {
|
|
145
|
+
outline: 2px solid var(--borderColor-accent-emphasis, #1f6feb);
|
|
146
|
+
outline-offset: -1px;
|
|
112
147
|
}
|
|
113
148
|
|
|
114
149
|
.author {
|
package/src/context.jsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* eslint-disable react/prop-types */
|
|
2
|
-
import { useMemo } from 'react'
|
|
2
|
+
import { useEffect, useMemo } from 'react'
|
|
3
3
|
import { useParams, useLocation } from 'react-router-dom'
|
|
4
4
|
// Side-effect import: seeds the core data index via init()
|
|
5
5
|
import 'virtual:storyboard-data-index'
|
|
6
|
-
import { loadScene, sceneExists, findRecord, deepMerge, setSceneClass } from '@dfosco/storyboard-core'
|
|
6
|
+
import { loadScene, sceneExists, findRecord, deepMerge, setSceneClass, installBodyClassSync } from '@dfosco/storyboard-core'
|
|
7
7
|
import { StoryboardContext } from './StoryboardContext.js'
|
|
8
8
|
|
|
9
9
|
export { StoryboardContext }
|
|
@@ -34,6 +34,9 @@ export default function StoryboardProvider({ sceneName, recordName, recordParam,
|
|
|
34
34
|
const activeSceneName = sceneParam || sceneName || (sceneExists(pageScene) ? pageScene : 'default')
|
|
35
35
|
const params = useParams()
|
|
36
36
|
|
|
37
|
+
// Auto-install body class sync (sb-key--value classes on <body>)
|
|
38
|
+
useEffect(() => installBodyClassSync(), [])
|
|
39
|
+
|
|
37
40
|
const { data, error } = useMemo(() => {
|
|
38
41
|
try {
|
|
39
42
|
let sceneData = loadScene(activeSceneName)
|