@dfosco/storyboard-react 1.11.3 → 1.13.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dfosco/storyboard-react",
3
- "version": "1.11.3",
3
+ "version": "1.13.0",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "@dfosco/storyboard-core": "*",
@@ -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
- <h1 className={styles.title}>{title}</h1>
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
- .branchSection {
104
- margin-top: 40px;
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
- .branchMeta {
117
+ .branchLabel {
108
118
  font-size: 12px;
119
+ font-weight: 500;
109
120
  color: var(--fgColor-muted, #848d97);
110
- margin: 4px 0 0;
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 {