bedrock-flows 0.7.1
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/auth-schema.sql +8 -0
- package/bin/bedrock-flows.mjs +127 -0
- package/lib/setup.mjs +262 -0
- package/package.json +11 -0
- package/template/.storybook/main.js +46 -0
- package/template/.storybook/manager-head.html +963 -0
- package/template/.storybook/preview-head.html +35 -0
- package/template/.storybook/preview.js +23 -0
- package/template/CHANGELOG.md +236 -0
- package/template/README.md +26 -0
- package/template/apps/dashboard/index.html +15 -0
- package/template/apps/dashboard/package.json +22 -0
- package/template/apps/dashboard/src/App.module.css +1318 -0
- package/template/apps/dashboard/src/App.tsx +2716 -0
- package/template/apps/dashboard/src/auth-client.ts +17 -0
- package/template/apps/dashboard/src/changelog.tsx +92 -0
- package/template/apps/dashboard/src/index.css +86 -0
- package/template/apps/dashboard/src/main.tsx +15 -0
- package/template/apps/dashboard/src/theme.ts +31 -0
- package/template/apps/dashboard/src/vite-env.d.ts +4 -0
- package/template/apps/dashboard/vite.config.ts +48 -0
- package/template/apps/worker/.dev.vars.example +50 -0
- package/template/apps/worker/package.json +19 -0
- package/template/apps/worker/src/index.ts +295 -0
- package/template/apps/worker/tsconfig.json +11 -0
- package/template/apps/worker/wrangler.jsonc +29 -0
- package/template/bedrock.config.ts +16 -0
- package/template/design-system/README.md +97 -0
- package/template/design-system/starter-v1/components/button/component.css +42 -0
- package/template/design-system/starter-v1/components/button/danger.html +21 -0
- package/template/design-system/starter-v1/components/button/default.html +21 -0
- package/template/design-system/starter-v1/components/button/disabled.html +21 -0
- package/template/design-system/starter-v1/components/button/ghost.html +21 -0
- package/template/design-system/starter-v1/components/button/macro.njk +14 -0
- package/template/design-system/starter-v1/components/button/primary.html +21 -0
- package/template/design-system/starter-v1/components/button/variants.json +30 -0
- package/template/design-system/starter-v1/ds.json +3 -0
- package/template/design-system/starter-v1/global.css +52 -0
- package/template/design-system/starter-v1/style.css +107 -0
- package/template/gitignore +8 -0
- package/template/package.json +41 -0
- package/template/prototypes/F-001-hello/1-welcome.njk +30 -0
- package/template/prototypes/F-001-hello/2-form.njk +46 -0
- package/template/prototypes/F-001-hello/3-done.njk +29 -0
- package/template/prototypes/F-001-hello/meta.json +6 -0
- package/template/prototypes/_shared/_auth-gate.njk +54 -0
- package/template/prototypes/_shared/delivery.njk +43 -0
- package/template/prototypes/_shared/layout.njk +15 -0
- package/template/prototypes/_shared/screen.njk +1818 -0
- package/template/prototypes/_shared/wireflow.njk +4731 -0
- package/template/public/auth-gate.css +150 -0
- package/template/public/bedrock/color-inspector.js +284 -0
- package/template/public/bedrock/component-overlay.js +219 -0
- package/template/public/bedrock/data/bedrock-config.js +45 -0
- package/template/public/bedrock/font-size-overlay.js +590 -0
- package/template/public/bedrock/grid-overlay.js +379 -0
- package/template/public/bedrock/prototype-navigation.js +974 -0
- package/template/public/cmdk.js +146 -0
- package/template/public/ds-xray.css +112 -0
- package/template/public/ds-xray.js +271 -0
- package/template/public/favicon.svg +4 -0
- package/template/public/icons/bolt-fill.svg +3 -0
- package/template/public/icons/bolt.svg +3 -0
- package/template/public/icons/caret-down-fill.svg +3 -0
- package/template/public/icons/check-double.svg +4 -0
- package/template/public/icons/check.svg +3 -0
- package/template/public/icons/chevron-left.svg +3 -0
- package/template/public/icons/chevron-right.svg +3 -0
- package/template/public/icons/circle-info.svg +6 -0
- package/template/public/icons/grid.svg +6 -0
- package/template/public/icons/message-square-1.svg +3 -0
- package/template/public/icons/message-square.svg +3 -0
- package/template/public/icons/messages.svg +4 -0
- package/template/public/icons/options-horizontal.svg +5 -0
- package/template/public/icons/swatches.svg +6 -0
- package/template/public/icons/workflow.svg +6 -0
- package/template/public/lightbox.js +87 -0
- package/template/public/proto-chrome.css +596 -0
- package/template/public/screen-comments.css +723 -0
- package/template/public/wireflow-client.js +26 -0
- package/template/scripts/build-storybooks.mjs +8 -0
- package/template/scripts/dev-setup.mjs +15 -0
- package/template/scripts/generate-stories.mjs +12 -0
- package/template/scripts/generate-variants.mjs +22 -0
- package/template/tsconfig.base.json +19 -0
|
@@ -0,0 +1,974 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prototype Navigation (Bedrock 3)
|
|
3
|
+
*
|
|
4
|
+
* Toggleable sidebar showing all pages in the prototype.
|
|
5
|
+
* Toggle with Ctrl+M (or Cmd+M on Mac).
|
|
6
|
+
* State is persisted in localStorage.
|
|
7
|
+
*
|
|
8
|
+
* Page data is auto-generated by bedrock/generate-pages.js and loaded
|
|
9
|
+
* via bedrock/data/pages.js (sets window.PAGE_DATA).
|
|
10
|
+
*
|
|
11
|
+
* Uses a flex-based layout that pushes content instead of overlaying.
|
|
12
|
+
* Styles are injected and don't affect the global style.css.
|
|
13
|
+
*
|
|
14
|
+
* Viewport preview uses an iframe to properly trigger CSS media queries.
|
|
15
|
+
*
|
|
16
|
+
* Pages with double dashes (e.g., contact--error.html) are page states
|
|
17
|
+
* and only shown in the "Page States" section when viewing that page.
|
|
18
|
+
*
|
|
19
|
+
* Includes integrated dev tools:
|
|
20
|
+
* - Viewport / breakpoint preview
|
|
21
|
+
* - BEM component overlay (Ctrl+Shift+B / Ctrl+Shift+A)
|
|
22
|
+
* - Grid overlay (Ctrl+Shift+G / Ctrl+Shift+H)
|
|
23
|
+
* - Typography inspector (Ctrl+Shift+F / Ctrl+Shift+D)
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// Don't run inside iframe (prevents recursion in viewport preview)
|
|
27
|
+
if (window.self !== window.top) {
|
|
28
|
+
// We're in an iframe, don't inject prototype navigation
|
|
29
|
+
document.documentElement.classList.add('is-iframe-preview');
|
|
30
|
+
} else {
|
|
31
|
+
|
|
32
|
+
const STORAGE_KEY = 'protoNavOpen';
|
|
33
|
+
const VIEWPORT_STORAGE_KEY = 'protoNavViewport';
|
|
34
|
+
const isLocalDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
|
|
35
|
+
|
|
36
|
+
// Read config from generated bedrock-config.js (with defaults)
|
|
37
|
+
const BEDROCK_CONFIG = window.BEDROCK_CONFIG || {};
|
|
38
|
+
const PLUGINS = BEDROCK_CONFIG.plugins || {};
|
|
39
|
+
const BREAKPOINTS = BEDROCK_CONFIG.breakpoints || [
|
|
40
|
+
{ id: 'full', label: 'Full width', width: null },
|
|
41
|
+
{ id: 'desktop', label: 'Desktop', width: 1280 },
|
|
42
|
+
{ id: 'tablet-lg', label: 'Large Tablet', width: 960 },
|
|
43
|
+
{ id: 'tablet', label: 'Tablet', width: 720 },
|
|
44
|
+
{ id: 'mobile', label: 'Mobile', width: 375 }
|
|
45
|
+
];
|
|
46
|
+
const STORYBOOK_PORTS = BEDROCK_CONFIG.storybookPorts || [6006, 6007, 6008];
|
|
47
|
+
|
|
48
|
+
// Use auto-generated PAGE_DATA from bedrock/data/pages.js
|
|
49
|
+
const PAGE_DATA = window.PAGE_DATA || {};
|
|
50
|
+
|
|
51
|
+
// Resolve the page-states list for a given pathname. Returns the array
|
|
52
|
+
// (with a leading "Default" entry) or null when the page has no states.
|
|
53
|
+
function findPageStatesFor(path) {
|
|
54
|
+
for (const section of Object.values(PAGE_DATA)) {
|
|
55
|
+
for (const page of section) {
|
|
56
|
+
if (isCurrentPage(page.href, path)) {
|
|
57
|
+
if (page.states && page.states.length > 0) {
|
|
58
|
+
return [{ href: page.href, label: 'Default' }, ...page.states];
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (page.states) {
|
|
63
|
+
for (const state of page.states) {
|
|
64
|
+
if (isCurrentPage(state.href, path)) {
|
|
65
|
+
return [{ href: page.href, label: 'Default' }, ...page.states];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Render the inner block (without the wrapping .proto-nav__states div) for a given list.
|
|
75
|
+
function renderPageStatesInner(pageStates, currentPath) {
|
|
76
|
+
return `
|
|
77
|
+
<div class="proto-nav__section proto-nav__section--states">
|
|
78
|
+
<div class="proto-nav__section-title">Page States</div>
|
|
79
|
+
${pageStates.map(state => {
|
|
80
|
+
const isCurrent = isCurrentPage(state.href, currentPath);
|
|
81
|
+
return `<a href="${state.href}"${isCurrent ? ' class="is-current"' : ''}>${state.label}</a>`;
|
|
82
|
+
}).join('')}
|
|
83
|
+
</div>
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
88
|
+
const currentPath = window.location.pathname;
|
|
89
|
+
const pageStates = findPageStatesFor(currentPath);
|
|
90
|
+
|
|
91
|
+
// Build sections HTML
|
|
92
|
+
let sectionsHTML = '';
|
|
93
|
+
for (const [sectionName, pages] of Object.entries(PAGE_DATA)) {
|
|
94
|
+
sectionsHTML += `
|
|
95
|
+
<div class="proto-nav__section">
|
|
96
|
+
<div class="proto-nav__section-title">${sectionName}</div>
|
|
97
|
+
${pages.map(page => {
|
|
98
|
+
const isCurrent = isCurrentPage(page.href, currentPath);
|
|
99
|
+
return `<a href="${page.href}"${isCurrent ? ' class="is-current"' : ''}>${page.label}</a>`;
|
|
100
|
+
}).join('')}
|
|
101
|
+
</div>
|
|
102
|
+
`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Add page states section if applicable
|
|
106
|
+
const pageStatesHTML = pageStates ? renderPageStatesInner(pageStates, currentPath) : '';
|
|
107
|
+
|
|
108
|
+
// Icons per viewport id — inline SVGs for the always-visible icon bar
|
|
109
|
+
const VIEWPORT_ICONS = {
|
|
110
|
+
'full': '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 6V2h4M14 6V2h-4M2 10v4h4M14 10v4h-4"/></svg>',
|
|
111
|
+
'desktop': '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="1.5" y="2.5" width="13" height="8.5" rx="1"/><path d="M6 14h4M8 11v3"/></svg>',
|
|
112
|
+
'tablet-lg': '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="1.5" y="2.5" width="13" height="11" rx="1.25"/><path d="M7 12h2"/></svg>',
|
|
113
|
+
'tablet': '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="1.5" width="10" height="13" rx="1.25"/><path d="M7 12.5h2"/></svg>',
|
|
114
|
+
'mobile': '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="4.5" y="1.5" width="7" height="13" rx="1.25"/><path d="M7.25 12.5h1.5"/></svg>'
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Build breakpoint buttons HTML (only if viewport plugin enabled)
|
|
118
|
+
const breakpointButtonsHTML = PLUGINS.viewport !== false ? BREAKPOINTS.map(bp =>
|
|
119
|
+
`<button class="proto-nav__viewport-btn" data-viewport="${bp.id}" data-width="${bp.width || ''}" title="${bp.label}${bp.width ? ' (' + bp.width + 'px)' : ''}" aria-label="${bp.label}">${VIEWPORT_ICONS[bp.id] || bp.label}</button>`
|
|
120
|
+
).join('') : '';
|
|
121
|
+
|
|
122
|
+
// Create the sidebar HTML
|
|
123
|
+
const sidebarHTML = `
|
|
124
|
+
<div class="proto-nav" id="protoNav" aria-hidden="true">
|
|
125
|
+
<div class="proto-nav__inner">
|
|
126
|
+
<div class="proto-nav__header">
|
|
127
|
+
<span class="proto-nav__title">Site Index <span class="proto-nav__shortcut"><kbd>Ctrl</kbd>+<kbd>M</kbd></span></span>
|
|
128
|
+
<button class="proto-nav__close" aria-label="Close navigation">×</button>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="proto-nav__content">
|
|
131
|
+
${sectionsHTML}
|
|
132
|
+
</div>
|
|
133
|
+
<div class="proto-nav__states" id="protoNavStates">${pageStatesHTML}</div>
|
|
134
|
+
${PLUGINS.viewport !== false ? `
|
|
135
|
+
<div class="proto-nav__viewport">
|
|
136
|
+
<div class="proto-nav__section-title">Viewport</div>
|
|
137
|
+
<div class="proto-nav__viewport-buttons">
|
|
138
|
+
${breakpointButtonsHTML}
|
|
139
|
+
</div>
|
|
140
|
+
<div class="proto-nav__viewport-indicator" id="viewportIndicator"></div>
|
|
141
|
+
</div>
|
|
142
|
+
` : ''}
|
|
143
|
+
${(PLUGINS.componentOverlay !== false || PLUGINS.gridOverlay !== false || PLUGINS.typographyInspector !== false || PLUGINS.colorInspector !== false) ? `
|
|
144
|
+
<details class="proto-nav__tools-details">
|
|
145
|
+
<summary class="proto-nav__tools-summary">Tools</summary>
|
|
146
|
+
<div class="proto-nav__tools-content">
|
|
147
|
+
${PLUGINS.componentOverlay !== false ? `
|
|
148
|
+
<label class="proto-nav__checkbox-label">
|
|
149
|
+
<input type="checkbox" id="bemOverlayTopLevel" class="proto-nav__checkbox">
|
|
150
|
+
<span>Show component names</span>
|
|
151
|
+
</label>
|
|
152
|
+
<label class="proto-nav__checkbox-label">
|
|
153
|
+
<input type="checkbox" id="bemOverlayAtoms" class="proto-nav__checkbox">
|
|
154
|
+
<span>Show small components</span>
|
|
155
|
+
</label>
|
|
156
|
+
` : ''}
|
|
157
|
+
${PLUGINS.gridOverlay !== false ? `
|
|
158
|
+
<div class="proto-nav__section-title" style="padding-top: 12px;">Grid</div>
|
|
159
|
+
<label class="proto-nav__checkbox-label">
|
|
160
|
+
<input type="checkbox" id="gridOverlayCol" class="proto-nav__checkbox">
|
|
161
|
+
<span>Column grid</span>
|
|
162
|
+
</label>
|
|
163
|
+
<label class="proto-nav__checkbox-label">
|
|
164
|
+
<input type="checkbox" id="gridOverlaySpacing" class="proto-nav__checkbox">
|
|
165
|
+
<span>Section spacing</span>
|
|
166
|
+
</label>
|
|
167
|
+
` : ''}
|
|
168
|
+
${(PLUGINS.typographyInspector !== false || PLUGINS.colorInspector !== false) ? `
|
|
169
|
+
<div class="proto-nav__section-title" style="padding-top: 12px;">Inspector</div>
|
|
170
|
+
<div class="proto-nav__segmented" id="inspectorSegmented">
|
|
171
|
+
<button class="proto-nav__segmented-btn is-active" data-mode="none">None</button>
|
|
172
|
+
${PLUGINS.typographyInspector !== false ? `
|
|
173
|
+
<button class="proto-nav__segmented-btn" data-mode="font">Font</button>
|
|
174
|
+
<button class="proto-nav__segmented-btn" data-mode="spacing">Spacing</button>
|
|
175
|
+
` : ''}
|
|
176
|
+
${PLUGINS.colorInspector !== false ? `
|
|
177
|
+
<button class="proto-nav__segmented-btn" data-mode="color">Color</button>
|
|
178
|
+
` : ''}
|
|
179
|
+
</div>
|
|
180
|
+
` : ''}
|
|
181
|
+
</div>
|
|
182
|
+
</details>
|
|
183
|
+
` : ''}
|
|
184
|
+
${(PLUGINS.storybook !== false && isLocalDev) ? `
|
|
185
|
+
<div class="proto-nav__storybook" id="protoNavStorybook">
|
|
186
|
+
<span class="proto-nav__storybook-status" data-status="checking"></span>
|
|
187
|
+
<span class="proto-nav__storybook-text">Checking Storybook...</span>
|
|
188
|
+
</div>
|
|
189
|
+
` : ''}
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
`;
|
|
193
|
+
|
|
194
|
+
// Create styles - flex-based layout with Bedrock 3 light theme
|
|
195
|
+
const styles = `
|
|
196
|
+
<style id="proto-nav-styles">
|
|
197
|
+
:root {
|
|
198
|
+
--br-bg: #fff;
|
|
199
|
+
--br-border: #e5e5e5;
|
|
200
|
+
--br-shadow: 2px 0 8px rgba(0,0,0,0.08), 1px 0 2px rgba(0,0,0,0.04);
|
|
201
|
+
--br-text: #333;
|
|
202
|
+
--br-text-muted: #555;
|
|
203
|
+
--br-text-faint: #888;
|
|
204
|
+
--br-hover-bg: #f5f5f5;
|
|
205
|
+
--br-kbd-bg: #f0f0f0;
|
|
206
|
+
--br-kbd-border: #d0d0d0;
|
|
207
|
+
--br-active: #2563eb;
|
|
208
|
+
--br-viewport-bg: #e5e7eb;
|
|
209
|
+
--br-viewport-border: #d1d5db;
|
|
210
|
+
--br-seg-bg: #e5e7eb;
|
|
211
|
+
}
|
|
212
|
+
@media (prefers-color-scheme: dark) {
|
|
213
|
+
:root {
|
|
214
|
+
--br-bg: #1a1a1a;
|
|
215
|
+
--br-border: #333;
|
|
216
|
+
--br-shadow: 2px 0 8px rgba(0,0,0,0.3);
|
|
217
|
+
--br-text: #eee;
|
|
218
|
+
--br-text-muted: #ccc;
|
|
219
|
+
--br-text-faint: #888;
|
|
220
|
+
--br-hover-bg: #2a2a2a;
|
|
221
|
+
--br-kbd-bg: #2a2a2a;
|
|
222
|
+
--br-kbd-border: #444;
|
|
223
|
+
--br-active: #2563eb;
|
|
224
|
+
--br-viewport-bg: #2a2a2a;
|
|
225
|
+
--br-viewport-border: #444;
|
|
226
|
+
--br-seg-bg: #2a2a2a;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/* Flex wrapper for prototype layout */
|
|
230
|
+
.proto-wrapper {
|
|
231
|
+
display: flex;
|
|
232
|
+
min-height: 100vh;
|
|
233
|
+
background: #e5e7eb;
|
|
234
|
+
}
|
|
235
|
+
@media (prefers-color-scheme: dark) {
|
|
236
|
+
.proto-wrapper {
|
|
237
|
+
background: #111;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
.proto-wrapper__site {
|
|
241
|
+
flex: 1;
|
|
242
|
+
min-width: 0;
|
|
243
|
+
height: 100vh;
|
|
244
|
+
overflow-y: auto;
|
|
245
|
+
background: #fff;
|
|
246
|
+
display: flex;
|
|
247
|
+
flex-direction: column;
|
|
248
|
+
}
|
|
249
|
+
.proto-wrapper__site--hidden {
|
|
250
|
+
display: none;
|
|
251
|
+
}
|
|
252
|
+
/* Iframe preview container */
|
|
253
|
+
.proto-wrapper__preview {
|
|
254
|
+
flex: 1;
|
|
255
|
+
display: none;
|
|
256
|
+
flex-direction: column;
|
|
257
|
+
align-items: center;
|
|
258
|
+
height: 100vh;
|
|
259
|
+
overflow: hidden;
|
|
260
|
+
padding: 16px;
|
|
261
|
+
box-sizing: border-box;
|
|
262
|
+
}
|
|
263
|
+
.proto-wrapper__preview--active {
|
|
264
|
+
display: flex;
|
|
265
|
+
}
|
|
266
|
+
.proto-wrapper__iframe-container {
|
|
267
|
+
position: relative;
|
|
268
|
+
height: 100%;
|
|
269
|
+
}
|
|
270
|
+
.proto-wrapper__iframe {
|
|
271
|
+
display: block;
|
|
272
|
+
width: 100%;
|
|
273
|
+
height: 100%;
|
|
274
|
+
border: none;
|
|
275
|
+
}
|
|
276
|
+
/* Nav container - controls width */
|
|
277
|
+
.proto-nav {
|
|
278
|
+
width: 0;
|
|
279
|
+
flex-shrink: 0;
|
|
280
|
+
transition: width 0.2s ease;
|
|
281
|
+
overflow: hidden;
|
|
282
|
+
}
|
|
283
|
+
.proto-nav.is-open {
|
|
284
|
+
width: 220px;
|
|
285
|
+
}
|
|
286
|
+
/* Inner nav - fixed width, scrollable */
|
|
287
|
+
.proto-nav__inner {
|
|
288
|
+
width: 220px;
|
|
289
|
+
height: 100vh;
|
|
290
|
+
position: sticky;
|
|
291
|
+
top: 0;
|
|
292
|
+
background: var(--br-bg);
|
|
293
|
+
color: var(--br-text);
|
|
294
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
295
|
+
font-size: 13px;
|
|
296
|
+
display: flex;
|
|
297
|
+
flex-direction: column;
|
|
298
|
+
box-shadow: var(--br-shadow);
|
|
299
|
+
border-right: 1px solid var(--br-border);
|
|
300
|
+
}
|
|
301
|
+
.proto-nav__header {
|
|
302
|
+
display: flex;
|
|
303
|
+
align-items: center;
|
|
304
|
+
justify-content: space-between;
|
|
305
|
+
padding: 12px 14px;
|
|
306
|
+
border-bottom: 1px solid var(--br-border);
|
|
307
|
+
}
|
|
308
|
+
.proto-nav__title {
|
|
309
|
+
font-weight: 600;
|
|
310
|
+
font-size: 13px;
|
|
311
|
+
color: var(--br-text);
|
|
312
|
+
}
|
|
313
|
+
.proto-nav__close {
|
|
314
|
+
background: none;
|
|
315
|
+
border: none;
|
|
316
|
+
color: var(--br-text-faint);
|
|
317
|
+
font-size: 18px;
|
|
318
|
+
cursor: pointer;
|
|
319
|
+
padding: 0;
|
|
320
|
+
line-height: 1;
|
|
321
|
+
}
|
|
322
|
+
.proto-nav__close:hover {
|
|
323
|
+
color: var(--br-text);
|
|
324
|
+
}
|
|
325
|
+
.proto-nav__content {
|
|
326
|
+
flex: 1;
|
|
327
|
+
overflow-y: auto;
|
|
328
|
+
padding: 8px 0;
|
|
329
|
+
}
|
|
330
|
+
.proto-nav__section {
|
|
331
|
+
padding: 4px 14px 8px;
|
|
332
|
+
}
|
|
333
|
+
.proto-nav__section-title {
|
|
334
|
+
font-size: 11px;
|
|
335
|
+
font-weight: 600;
|
|
336
|
+
text-transform: uppercase;
|
|
337
|
+
letter-spacing: 0.5px;
|
|
338
|
+
color: var(--br-text-faint);
|
|
339
|
+
margin-bottom: 6px;
|
|
340
|
+
padding-top: 8px;
|
|
341
|
+
}
|
|
342
|
+
.proto-nav__section:first-child .proto-nav__section-title {
|
|
343
|
+
padding-top: 0;
|
|
344
|
+
}
|
|
345
|
+
.proto-nav__content a,
|
|
346
|
+
.proto-nav__states a {
|
|
347
|
+
display: block;
|
|
348
|
+
padding: 5px 8px;
|
|
349
|
+
color: var(--br-text-muted);
|
|
350
|
+
text-decoration: none;
|
|
351
|
+
border-radius: 4px;
|
|
352
|
+
transition: background 0.15s, color 0.15s;
|
|
353
|
+
}
|
|
354
|
+
.proto-nav__content a:hover,
|
|
355
|
+
.proto-nav__states a:hover {
|
|
356
|
+
background: var(--br-hover-bg);
|
|
357
|
+
color: var(--br-text);
|
|
358
|
+
}
|
|
359
|
+
.proto-nav__content a.is-current,
|
|
360
|
+
.proto-nav__states a.is-current {
|
|
361
|
+
background: var(--br-active);
|
|
362
|
+
color: #fff;
|
|
363
|
+
}
|
|
364
|
+
.proto-nav__states {
|
|
365
|
+
border-top: 1px solid var(--br-border);
|
|
366
|
+
padding: 8px 0;
|
|
367
|
+
}
|
|
368
|
+
.proto-nav__states .proto-nav__section-title {
|
|
369
|
+
color: var(--br-text-faint);
|
|
370
|
+
}
|
|
371
|
+
.proto-nav__shortcut {
|
|
372
|
+
margin-left: 8px;
|
|
373
|
+
font-weight: normal;
|
|
374
|
+
font-size: 10px;
|
|
375
|
+
color: var(--br-text-faint);
|
|
376
|
+
}
|
|
377
|
+
.proto-nav__shortcut kbd {
|
|
378
|
+
display: inline-block;
|
|
379
|
+
padding: 2px 5px;
|
|
380
|
+
background: var(--br-kbd-bg);
|
|
381
|
+
border: 1px solid var(--br-kbd-border);
|
|
382
|
+
border-radius: 4px;
|
|
383
|
+
font-family: inherit;
|
|
384
|
+
font-size: 10px;
|
|
385
|
+
color: var(--br-text-muted);
|
|
386
|
+
box-shadow: 0 1px 0 var(--br-kbd-border);
|
|
387
|
+
}
|
|
388
|
+
.proto-nav__checkbox-label {
|
|
389
|
+
display: flex;
|
|
390
|
+
align-items: center;
|
|
391
|
+
gap: 8px;
|
|
392
|
+
padding: 6px 8px;
|
|
393
|
+
cursor: pointer;
|
|
394
|
+
color: var(--br-text-muted);
|
|
395
|
+
border-radius: 4px;
|
|
396
|
+
transition: background 0.15s, color 0.15s;
|
|
397
|
+
}
|
|
398
|
+
.proto-nav__checkbox-label:hover {
|
|
399
|
+
background: var(--br-hover-bg);
|
|
400
|
+
color: var(--br-text);
|
|
401
|
+
}
|
|
402
|
+
.proto-nav__checkbox {
|
|
403
|
+
width: 14px;
|
|
404
|
+
height: 14px;
|
|
405
|
+
margin: 0;
|
|
406
|
+
cursor: pointer;
|
|
407
|
+
accent-color: var(--br-active);
|
|
408
|
+
}
|
|
409
|
+
/* Viewport section */
|
|
410
|
+
.proto-nav__viewport {
|
|
411
|
+
padding: 8px 12px 10px;
|
|
412
|
+
border-top: 1px solid var(--br-border);
|
|
413
|
+
}
|
|
414
|
+
.proto-nav__viewport-buttons {
|
|
415
|
+
display: grid;
|
|
416
|
+
grid-template-columns: repeat(5, 1fr);
|
|
417
|
+
gap: 4px;
|
|
418
|
+
margin-bottom: 6px;
|
|
419
|
+
}
|
|
420
|
+
.proto-nav__viewport-btn {
|
|
421
|
+
display: inline-flex;
|
|
422
|
+
align-items: center;
|
|
423
|
+
justify-content: center;
|
|
424
|
+
padding: 6px;
|
|
425
|
+
height: 28px;
|
|
426
|
+
background: var(--br-viewport-bg);
|
|
427
|
+
border: 1px solid var(--br-viewport-border);
|
|
428
|
+
border-radius: 4px;
|
|
429
|
+
color: var(--br-text-muted);
|
|
430
|
+
cursor: pointer;
|
|
431
|
+
transition: all 0.15s ease;
|
|
432
|
+
font-family: inherit;
|
|
433
|
+
}
|
|
434
|
+
.proto-nav__viewport-btn svg {
|
|
435
|
+
width: 16px;
|
|
436
|
+
height: 16px;
|
|
437
|
+
display: block;
|
|
438
|
+
}
|
|
439
|
+
.proto-nav__viewport-btn:hover {
|
|
440
|
+
background: var(--br-hover-bg);
|
|
441
|
+
color: var(--br-text);
|
|
442
|
+
}
|
|
443
|
+
.proto-nav__viewport-btn.is-active {
|
|
444
|
+
background: var(--br-active);
|
|
445
|
+
border-color: var(--br-active);
|
|
446
|
+
color: #fff;
|
|
447
|
+
}
|
|
448
|
+
.proto-nav__viewport-indicator {
|
|
449
|
+
font-size: 10px;
|
|
450
|
+
color: var(--br-text-faint);
|
|
451
|
+
text-align: center;
|
|
452
|
+
padding: 4px 0;
|
|
453
|
+
}
|
|
454
|
+
/* Segmented control */
|
|
455
|
+
.proto-nav__segmented {
|
|
456
|
+
display: flex;
|
|
457
|
+
background: var(--br-seg-bg);
|
|
458
|
+
border-radius: 4px;
|
|
459
|
+
padding: 2px;
|
|
460
|
+
gap: 2px;
|
|
461
|
+
}
|
|
462
|
+
.proto-nav__segmented-btn {
|
|
463
|
+
flex: 1;
|
|
464
|
+
padding: 6px 4px;
|
|
465
|
+
background: transparent;
|
|
466
|
+
border: none;
|
|
467
|
+
border-radius: 3px;
|
|
468
|
+
color: var(--br-text-faint);
|
|
469
|
+
font-size: 11px;
|
|
470
|
+
cursor: pointer;
|
|
471
|
+
transition: all 0.15s ease;
|
|
472
|
+
font-family: inherit;
|
|
473
|
+
}
|
|
474
|
+
.proto-nav__segmented-btn:hover {
|
|
475
|
+
color: var(--br-text-muted);
|
|
476
|
+
}
|
|
477
|
+
.proto-nav__segmented-btn.is-active {
|
|
478
|
+
background: var(--br-active);
|
|
479
|
+
color: #fff;
|
|
480
|
+
}
|
|
481
|
+
/* Tools accordion */
|
|
482
|
+
.proto-nav__tools-details {
|
|
483
|
+
border-top: 1px solid var(--br-border);
|
|
484
|
+
}
|
|
485
|
+
.proto-nav__tools-summary {
|
|
486
|
+
padding: 10px 14px;
|
|
487
|
+
font-size: 11px;
|
|
488
|
+
font-weight: 600;
|
|
489
|
+
text-transform: uppercase;
|
|
490
|
+
letter-spacing: 0.5px;
|
|
491
|
+
color: var(--br-text-faint);
|
|
492
|
+
cursor: pointer;
|
|
493
|
+
list-style: none;
|
|
494
|
+
display: flex;
|
|
495
|
+
align-items: center;
|
|
496
|
+
justify-content: space-between;
|
|
497
|
+
}
|
|
498
|
+
.proto-nav__tools-summary::-webkit-details-marker {
|
|
499
|
+
display: none;
|
|
500
|
+
}
|
|
501
|
+
.proto-nav__tools-summary::after {
|
|
502
|
+
content: '+';
|
|
503
|
+
font-size: 14px;
|
|
504
|
+
font-weight: normal;
|
|
505
|
+
}
|
|
506
|
+
.proto-nav__tools-details[open] .proto-nav__tools-summary::after {
|
|
507
|
+
content: '-';
|
|
508
|
+
}
|
|
509
|
+
.proto-nav__tools-details[open] .proto-nav__tools-summary {
|
|
510
|
+
color: var(--br-text-muted);
|
|
511
|
+
}
|
|
512
|
+
.proto-nav__tools-content {
|
|
513
|
+
padding: 0 14px 12px;
|
|
514
|
+
}
|
|
515
|
+
/* Storybook status */
|
|
516
|
+
.proto-nav__storybook {
|
|
517
|
+
display: flex;
|
|
518
|
+
align-items: center;
|
|
519
|
+
gap: 8px;
|
|
520
|
+
padding: 10px 14px;
|
|
521
|
+
border-top: 1px solid var(--br-border);
|
|
522
|
+
font-size: 12px;
|
|
523
|
+
color: var(--br-text-muted);
|
|
524
|
+
}
|
|
525
|
+
.proto-nav__storybook-status {
|
|
526
|
+
width: 8px;
|
|
527
|
+
height: 8px;
|
|
528
|
+
border-radius: 50%;
|
|
529
|
+
flex-shrink: 0;
|
|
530
|
+
}
|
|
531
|
+
.proto-nav__storybook-status[data-status="checking"] {
|
|
532
|
+
background: var(--br-text-faint);
|
|
533
|
+
}
|
|
534
|
+
.proto-nav__storybook-status[data-status="running"] {
|
|
535
|
+
background: #22c55e;
|
|
536
|
+
}
|
|
537
|
+
.proto-nav__storybook-status[data-status="stopped"] {
|
|
538
|
+
background: #ef4444;
|
|
539
|
+
}
|
|
540
|
+
.proto-nav__storybook a {
|
|
541
|
+
color: var(--br-active);
|
|
542
|
+
text-decoration: none;
|
|
543
|
+
}
|
|
544
|
+
.proto-nav__storybook a:hover {
|
|
545
|
+
text-decoration: underline;
|
|
546
|
+
}
|
|
547
|
+
</style>
|
|
548
|
+
`;
|
|
549
|
+
|
|
550
|
+
// Inject styles
|
|
551
|
+
document.head.insertAdjacentHTML('beforeend', styles);
|
|
552
|
+
|
|
553
|
+
// Create flex wrapper and wrap all body contents
|
|
554
|
+
const wrapper = document.createElement('div');
|
|
555
|
+
wrapper.className = 'proto-wrapper';
|
|
556
|
+
|
|
557
|
+
const siteWrapper = document.createElement('div');
|
|
558
|
+
siteWrapper.className = 'proto-wrapper__site';
|
|
559
|
+
siteWrapper.id = 'protoSiteWrapper';
|
|
560
|
+
|
|
561
|
+
// Create preview container with iframe
|
|
562
|
+
const previewWrapper = document.createElement('div');
|
|
563
|
+
previewWrapper.className = 'proto-wrapper__preview';
|
|
564
|
+
previewWrapper.id = 'protoPreviewWrapper';
|
|
565
|
+
|
|
566
|
+
const iframeContainer = document.createElement('div');
|
|
567
|
+
iframeContainer.className = 'proto-wrapper__iframe-container';
|
|
568
|
+
iframeContainer.id = 'protoIframeContainer';
|
|
569
|
+
|
|
570
|
+
const iframe = document.createElement('iframe');
|
|
571
|
+
iframe.className = 'proto-wrapper__iframe';
|
|
572
|
+
iframe.id = 'protoIframe';
|
|
573
|
+
|
|
574
|
+
iframeContainer.appendChild(iframe);
|
|
575
|
+
previewWrapper.appendChild(iframeContainer);
|
|
576
|
+
|
|
577
|
+
// Move all body children into site wrapper
|
|
578
|
+
while (document.body.firstChild) {
|
|
579
|
+
siteWrapper.appendChild(document.body.firstChild);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Build structure: body > wrapper > [nav, site, preview]
|
|
583
|
+
wrapper.innerHTML = sidebarHTML;
|
|
584
|
+
wrapper.appendChild(siteWrapper);
|
|
585
|
+
wrapper.appendChild(previewWrapper);
|
|
586
|
+
document.body.appendChild(wrapper);
|
|
587
|
+
|
|
588
|
+
const sidebar = document.getElementById('protoNav');
|
|
589
|
+
const closeBtn = sidebar.querySelector('.proto-nav__close');
|
|
590
|
+
const viewportIndicator = document.getElementById('viewportIndicator');
|
|
591
|
+
|
|
592
|
+
function openSidebar() {
|
|
593
|
+
sidebar.classList.add('is-open');
|
|
594
|
+
sidebar.setAttribute('aria-hidden', 'false');
|
|
595
|
+
localStorage.setItem(STORAGE_KEY, 'true');
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function closeSidebar() {
|
|
599
|
+
sidebar.classList.remove('is-open');
|
|
600
|
+
sidebar.setAttribute('aria-hidden', 'true');
|
|
601
|
+
localStorage.setItem(STORAGE_KEY, 'false');
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function toggleSidebar() {
|
|
605
|
+
const isOpen = sidebar.classList.contains('is-open');
|
|
606
|
+
if (isOpen) {
|
|
607
|
+
closeSidebar();
|
|
608
|
+
} else {
|
|
609
|
+
openSidebar();
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Viewport/breakpoint functions
|
|
614
|
+
let currentViewport = 'full';
|
|
615
|
+
|
|
616
|
+
function setViewport(viewportId) {
|
|
617
|
+
const breakpoint = BREAKPOINTS.find(bp => bp.id === viewportId);
|
|
618
|
+
if (!breakpoint) return;
|
|
619
|
+
|
|
620
|
+
currentViewport = viewportId;
|
|
621
|
+
|
|
622
|
+
// Update button states
|
|
623
|
+
document.querySelectorAll('.proto-nav__viewport-btn').forEach(btn => {
|
|
624
|
+
btn.classList.toggle('is-active', btn.dataset.viewport === viewportId);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
const iframeContainer = document.getElementById('protoIframeContainer');
|
|
628
|
+
const iframe = document.getElementById('protoIframe');
|
|
629
|
+
|
|
630
|
+
if (breakpoint.width) {
|
|
631
|
+
// Show iframe preview
|
|
632
|
+
siteWrapper.classList.add('proto-wrapper__site--hidden');
|
|
633
|
+
previewWrapper.classList.add('proto-wrapper__preview--active');
|
|
634
|
+
|
|
635
|
+
// Set iframe container width (and optional fixed height)
|
|
636
|
+
iframeContainer.style.width = `${breakpoint.width}px`;
|
|
637
|
+
if (breakpoint.height) {
|
|
638
|
+
iframeContainer.style.height = `${breakpoint.height}px`;
|
|
639
|
+
iframeContainer.style.maxHeight = '100%';
|
|
640
|
+
} else {
|
|
641
|
+
iframeContainer.style.height = '';
|
|
642
|
+
iframeContainer.style.maxHeight = '';
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Load current page in iframe if not already loaded or URL changed
|
|
646
|
+
const currentUrl = window.location.href;
|
|
647
|
+
if (iframe.src !== currentUrl) {
|
|
648
|
+
iframe.src = currentUrl;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
viewportIndicator.textContent = breakpoint.height
|
|
652
|
+
? `${breakpoint.width}×${breakpoint.height}`
|
|
653
|
+
: `${breakpoint.width}px`;
|
|
654
|
+
} else {
|
|
655
|
+
// Show normal site view
|
|
656
|
+
siteWrapper.classList.remove('proto-wrapper__site--hidden');
|
|
657
|
+
previewWrapper.classList.remove('proto-wrapper__preview--active');
|
|
658
|
+
|
|
659
|
+
// Clear iframe src to stop loading
|
|
660
|
+
iframe.src = 'about:blank';
|
|
661
|
+
iframeContainer.style.width = '';
|
|
662
|
+
iframeContainer.style.height = '';
|
|
663
|
+
iframeContainer.style.maxHeight = '';
|
|
664
|
+
|
|
665
|
+
viewportIndicator.textContent = '';
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Save state
|
|
669
|
+
localStorage.setItem(VIEWPORT_STORAGE_KEY, viewportId);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Sync navigation: when clicking links in sidebar, update iframe too
|
|
673
|
+
function handleNavLinkClick(e) {
|
|
674
|
+
if (currentViewport === 'full') return;
|
|
675
|
+
e.preventDefault();
|
|
676
|
+
const href = e.currentTarget.getAttribute('href');
|
|
677
|
+
const iframe = document.getElementById('protoIframe');
|
|
678
|
+
iframe.src = href;
|
|
679
|
+
// Update URL without reload
|
|
680
|
+
history.pushState(null, '', href);
|
|
681
|
+
// Update active states + page-states section
|
|
682
|
+
updateActiveLinks(href);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
sidebar.querySelectorAll('.proto-nav__content a, .proto-nav__states a').forEach(link => {
|
|
686
|
+
link.addEventListener('click', handleNavLinkClick);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
function updateActiveLinks(href) {
|
|
690
|
+
// Re-render the Page States block for the new page so navigating
|
|
691
|
+
// between two state-having pages doesn't keep showing the old states.
|
|
692
|
+
const newPath = new URL(href, window.location.origin).pathname;
|
|
693
|
+
const newStates = findPageStatesFor(newPath);
|
|
694
|
+
const statesContainer = sidebar.querySelector('#protoNavStates');
|
|
695
|
+
if (statesContainer) {
|
|
696
|
+
statesContainer.innerHTML = newStates ? renderPageStatesInner(newStates, newPath) : '';
|
|
697
|
+
}
|
|
698
|
+
// Update is-current on the persistent main-section links.
|
|
699
|
+
sidebar.querySelectorAll('.proto-nav__content a').forEach(link => {
|
|
700
|
+
link.classList.toggle('is-current', link.getAttribute('href') === href);
|
|
701
|
+
});
|
|
702
|
+
// Re-bind click interception on the freshly rendered state links so
|
|
703
|
+
// they too navigate the iframe rather than the top window.
|
|
704
|
+
sidebar.querySelectorAll('#protoNavStates a').forEach(link => {
|
|
705
|
+
link.addEventListener('click', handleNavLinkClick);
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Initialize viewport buttons
|
|
710
|
+
document.querySelectorAll('.proto-nav__viewport-btn').forEach(btn => {
|
|
711
|
+
btn.addEventListener('click', () => {
|
|
712
|
+
setViewport(btn.dataset.viewport);
|
|
713
|
+
});
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
// Restore viewport state
|
|
717
|
+
const savedViewport = localStorage.getItem(VIEWPORT_STORAGE_KEY) || 'full';
|
|
718
|
+
setViewport(savedViewport);
|
|
719
|
+
|
|
720
|
+
// Restore sidebar state from localStorage
|
|
721
|
+
const savedState = localStorage.getItem(STORAGE_KEY);
|
|
722
|
+
if (savedState === 'true') {
|
|
723
|
+
openSidebar();
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Keyboard shortcut: Ctrl+M (or Cmd+M on Mac)
|
|
727
|
+
document.addEventListener('keydown', (e) => {
|
|
728
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'm') {
|
|
729
|
+
e.preventDefault();
|
|
730
|
+
toggleSidebar();
|
|
731
|
+
}
|
|
732
|
+
// Also close on Escape
|
|
733
|
+
if (e.key === 'Escape' && sidebar.classList.contains('is-open')) {
|
|
734
|
+
closeSidebar();
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
// Close button
|
|
739
|
+
closeBtn.addEventListener('click', closeSidebar);
|
|
740
|
+
|
|
741
|
+
// BEM Overlay checkboxes sync (only if component overlay plugin enabled)
|
|
742
|
+
if (PLUGINS.componentOverlay !== false) {
|
|
743
|
+
const topLevelCheckbox = document.getElementById('bemOverlayTopLevel');
|
|
744
|
+
const atomsCheckbox = document.getElementById('bemOverlayAtoms');
|
|
745
|
+
|
|
746
|
+
if (topLevelCheckbox && atomsCheckbox) {
|
|
747
|
+
// Initialize checkbox states from BEMOverlay API or localStorage
|
|
748
|
+
function initBemCheckboxes() {
|
|
749
|
+
if (window.BEMOverlay) {
|
|
750
|
+
topLevelCheckbox.checked = window.BEMOverlay.isTopLevelVisible();
|
|
751
|
+
atomsCheckbox.checked = window.BEMOverlay.isAtomsVisible();
|
|
752
|
+
} else {
|
|
753
|
+
topLevelCheckbox.checked = localStorage.getItem('bemOverlayTopLevel') === 'true';
|
|
754
|
+
atomsCheckbox.checked = localStorage.getItem('bemOverlayAtoms') === 'true';
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
initBemCheckboxes();
|
|
759
|
+
|
|
760
|
+
topLevelCheckbox.addEventListener('change', () => {
|
|
761
|
+
if (window.BEMOverlay) {
|
|
762
|
+
if (topLevelCheckbox.checked) {
|
|
763
|
+
window.BEMOverlay.showTopLevel();
|
|
764
|
+
} else {
|
|
765
|
+
window.BEMOverlay.hideTopLevel();
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
atomsCheckbox.addEventListener('change', () => {
|
|
771
|
+
if (window.BEMOverlay) {
|
|
772
|
+
if (atomsCheckbox.checked) {
|
|
773
|
+
window.BEMOverlay.showAtoms();
|
|
774
|
+
} else {
|
|
775
|
+
window.BEMOverlay.hideAtoms();
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
window.addEventListener('bemOverlayToggled', (e) => {
|
|
781
|
+
topLevelCheckbox.checked = e.detail.topLevel;
|
|
782
|
+
atomsCheckbox.checked = e.detail.atoms;
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Grid Overlay checkboxes sync (only if grid overlay plugin enabled)
|
|
788
|
+
if (PLUGINS.gridOverlay !== false) {
|
|
789
|
+
const gridColCheckbox = document.getElementById('gridOverlayCol');
|
|
790
|
+
const gridSpacingCheckbox = document.getElementById('gridOverlaySpacing');
|
|
791
|
+
|
|
792
|
+
if (gridColCheckbox && gridSpacingCheckbox) {
|
|
793
|
+
function initGridCheckboxes() {
|
|
794
|
+
if (window.GridOverlay) {
|
|
795
|
+
gridColCheckbox.checked = window.GridOverlay.isColEnabled();
|
|
796
|
+
gridSpacingCheckbox.checked = window.GridOverlay.isSpacingEnabled();
|
|
797
|
+
} else {
|
|
798
|
+
gridColCheckbox.checked = localStorage.getItem('gridOverlayEnabled') === 'true';
|
|
799
|
+
gridSpacingCheckbox.checked = localStorage.getItem('sectionSpacingEnabled') === 'true';
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
initGridCheckboxes();
|
|
804
|
+
|
|
805
|
+
function postToIframe(action) {
|
|
806
|
+
const iframe = document.getElementById('protoIframe');
|
|
807
|
+
if (iframe && iframe.contentWindow && iframe.src && iframe.src !== 'about:blank') {
|
|
808
|
+
iframe.contentWindow.postMessage({ type: 'gridOverlay', action }, '*');
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
gridColCheckbox.addEventListener('change', () => {
|
|
813
|
+
const on = gridColCheckbox.checked;
|
|
814
|
+
if (window.GridOverlay) {
|
|
815
|
+
on ? window.GridOverlay.enableCol() : window.GridOverlay.disableCol();
|
|
816
|
+
}
|
|
817
|
+
postToIframe(on ? 'enableCol' : 'disableCol');
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
gridSpacingCheckbox.addEventListener('change', () => {
|
|
821
|
+
const on = gridSpacingCheckbox.checked;
|
|
822
|
+
if (window.GridOverlay) {
|
|
823
|
+
on ? window.GridOverlay.enableSpacing() : window.GridOverlay.disableSpacing();
|
|
824
|
+
}
|
|
825
|
+
postToIframe(on ? 'enableSpacing' : 'disableSpacing');
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
window.addEventListener('gridOverlayToggled', (e) => {
|
|
829
|
+
gridColCheckbox.checked = e.detail.colEnabled;
|
|
830
|
+
gridSpacingCheckbox.checked = e.detail.spacingEnabled;
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
// State updates posted from the viewport-preview iframe
|
|
834
|
+
window.addEventListener('message', (e) => {
|
|
835
|
+
const msg = e.data;
|
|
836
|
+
if (!msg || msg.type !== 'gridOverlayState') return;
|
|
837
|
+
gridColCheckbox.checked = !!msg.colEnabled;
|
|
838
|
+
gridSpacingCheckbox.checked = !!msg.spacingEnabled;
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// Inspector segmented control (if any inspector plugin enabled)
|
|
844
|
+
if (PLUGINS.typographyInspector !== false || PLUGINS.colorInspector !== false) {
|
|
845
|
+
const inspectorSegmented = document.getElementById('inspectorSegmented');
|
|
846
|
+
|
|
847
|
+
if (inspectorSegmented) {
|
|
848
|
+
const inspectorButtons = inspectorSegmented.querySelectorAll('.proto-nav__segmented-btn');
|
|
849
|
+
|
|
850
|
+
function setInspectorMode(mode) {
|
|
851
|
+
inspectorButtons.forEach(btn => {
|
|
852
|
+
btn.classList.toggle('is-active', btn.dataset.mode === mode);
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
if (window.TypographyOverlay) {
|
|
856
|
+
if (mode === 'font') {
|
|
857
|
+
window.TypographyOverlay.enableFontMode();
|
|
858
|
+
} else if (mode === 'spacing') {
|
|
859
|
+
window.TypographyOverlay.enableDistanceMode();
|
|
860
|
+
} else {
|
|
861
|
+
window.TypographyOverlay.disableFontMode();
|
|
862
|
+
window.TypographyOverlay.disableDistanceMode();
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (window.ColorOverlay) {
|
|
867
|
+
if (mode === 'color') {
|
|
868
|
+
window.ColorOverlay.enable();
|
|
869
|
+
} else {
|
|
870
|
+
window.ColorOverlay.disable();
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
function initInspectorSegmented() {
|
|
876
|
+
let activeMode = 'none';
|
|
877
|
+
if (window.ColorOverlay && window.ColorOverlay.isEnabled()) {
|
|
878
|
+
activeMode = 'color';
|
|
879
|
+
} else if (window.TypographyOverlay) {
|
|
880
|
+
if (window.TypographyOverlay.isFontModeEnabled()) {
|
|
881
|
+
activeMode = 'font';
|
|
882
|
+
} else if (window.TypographyOverlay.isDistanceModeEnabled()) {
|
|
883
|
+
activeMode = 'spacing';
|
|
884
|
+
}
|
|
885
|
+
} else {
|
|
886
|
+
if (localStorage.getItem('typographyOverlayFont') === 'true') {
|
|
887
|
+
activeMode = 'font';
|
|
888
|
+
} else if (localStorage.getItem('typographyOverlayDistance') === 'true') {
|
|
889
|
+
activeMode = 'spacing';
|
|
890
|
+
} else if (localStorage.getItem('colorOverlayEnabled') === 'true') {
|
|
891
|
+
activeMode = 'color';
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
inspectorButtons.forEach(btn => {
|
|
895
|
+
btn.classList.toggle('is-active', btn.dataset.mode === activeMode);
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
initInspectorSegmented();
|
|
900
|
+
|
|
901
|
+
inspectorButtons.forEach(btn => {
|
|
902
|
+
btn.addEventListener('click', () => {
|
|
903
|
+
setInspectorMode(btn.dataset.mode);
|
|
904
|
+
});
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
function syncActiveFromState() {
|
|
908
|
+
let activeMode = 'none';
|
|
909
|
+
if (window.ColorOverlay && window.ColorOverlay.isEnabled()) {
|
|
910
|
+
activeMode = 'color';
|
|
911
|
+
} else if (window.TypographyOverlay) {
|
|
912
|
+
if (window.TypographyOverlay.isFontModeEnabled()) {
|
|
913
|
+
activeMode = 'font';
|
|
914
|
+
} else if (window.TypographyOverlay.isDistanceModeEnabled()) {
|
|
915
|
+
activeMode = 'spacing';
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
inspectorButtons.forEach(btn => {
|
|
919
|
+
btn.classList.toggle('is-active', btn.dataset.mode === activeMode);
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
window.addEventListener('typographyOverlayToggled', syncActiveFromState);
|
|
924
|
+
window.addEventListener('colorOverlayToggled', syncActiveFromState);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// Check Storybook status (only if storybook plugin enabled and on local dev)
|
|
929
|
+
if (PLUGINS.storybook !== false && isLocalDev) {
|
|
930
|
+
(async function checkStorybookStatus() {
|
|
931
|
+
const storybookEl = document.getElementById('protoNavStorybook');
|
|
932
|
+
if (!storybookEl) return;
|
|
933
|
+
|
|
934
|
+
const statusEl = storybookEl.querySelector('.proto-nav__storybook-status');
|
|
935
|
+
const textEl = storybookEl.querySelector('.proto-nav__storybook-text');
|
|
936
|
+
|
|
937
|
+
for (const port of STORYBOOK_PORTS) {
|
|
938
|
+
try {
|
|
939
|
+
const controller = new AbortController();
|
|
940
|
+
const timeout = setTimeout(() => controller.abort(), 1000);
|
|
941
|
+
await fetch(`http://localhost:${port}`, {
|
|
942
|
+
mode: 'no-cors',
|
|
943
|
+
signal: controller.signal
|
|
944
|
+
});
|
|
945
|
+
clearTimeout(timeout);
|
|
946
|
+
|
|
947
|
+
statusEl.dataset.status = 'running';
|
|
948
|
+
textEl.innerHTML = `<a href="http://localhost:${port}" target="_blank">Storybook :${port}</a>`;
|
|
949
|
+
return;
|
|
950
|
+
} catch (e) {
|
|
951
|
+
// Port not responding, try next
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
statusEl.dataset.status = 'stopped';
|
|
956
|
+
textEl.textContent = 'Storybook not running';
|
|
957
|
+
})();
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
// Helper function to check if a href matches the current path
|
|
962
|
+
// Handles both with and without .html extension (for Cloudflare Pages etc.)
|
|
963
|
+
function isCurrentPage(href, currentPath) {
|
|
964
|
+
// Normalize paths by removing trailing .html
|
|
965
|
+
const normalizeHref = href.replace(/\.html$/, '').replace(/\/index$/, '');
|
|
966
|
+
const normalizePath = currentPath.replace(/\.html$/, '').replace(/\/index$/, '');
|
|
967
|
+
|
|
968
|
+
return href === currentPath ||
|
|
969
|
+
normalizeHref === normalizePath ||
|
|
970
|
+
(currentPath === '/' && (href === '/' || href === '/index.html')) ||
|
|
971
|
+
(normalizePath === '' && normalizeHref === '');
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
} // End of iframe check
|