@sorb/leaf 0.2.1 → 0.3.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/README.md +8 -2
- package/dist/core.js +665 -0
- package/dist/core.js.map +7 -0
- package/dist/core.mjs +643 -0
- package/dist/core.mjs.map +7 -0
- package/dist/index.js +760 -65
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +758 -63
- package/dist/index.mjs.map +4 -4
- package/package.json +8 -3
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/TokenProvider.jsx
|
|
2
|
-
import React, { useCallback, useEffect,
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
3
3
|
|
|
4
4
|
// src/context.js
|
|
5
5
|
import { createContext, useContext } from "react";
|
|
@@ -92,6 +92,112 @@ var applyTokens = (tokens) => {
|
|
|
92
92
|
root.style.setProperty(`--${key}`, result.value);
|
|
93
93
|
});
|
|
94
94
|
};
|
|
95
|
+
var clearTokenOverrides = (tokens) => {
|
|
96
|
+
const root = document.documentElement;
|
|
97
|
+
Object.keys(tokens).forEach((key) => {
|
|
98
|
+
root.style.removeProperty(`--${key}`);
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
var MODE_STYLESHEET_ID = "sorb-tokens";
|
|
102
|
+
var injectModeStylesheet = (css) => {
|
|
103
|
+
let tag = document.getElementById(MODE_STYLESHEET_ID);
|
|
104
|
+
if (!tag) {
|
|
105
|
+
tag = document.createElement("style");
|
|
106
|
+
tag.id = MODE_STYLESHEET_ID;
|
|
107
|
+
document.head.appendChild(tag);
|
|
108
|
+
}
|
|
109
|
+
tag.textContent = css;
|
|
110
|
+
};
|
|
111
|
+
var clearModeStylesheet = () => {
|
|
112
|
+
const tag = document.getElementById(MODE_STYLESHEET_ID);
|
|
113
|
+
if (tag && tag.parentNode) tag.parentNode.removeChild(tag);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// src/modeStylesheet.js
|
|
117
|
+
var buildModeStylesheet = (lightVars, darkVars, darkMode) => {
|
|
118
|
+
const lightDecls = normalizeDecls(lightVars);
|
|
119
|
+
const hasDark = !!darkMode && !!darkVars && Object.keys(darkVars).length > 0;
|
|
120
|
+
if (!hasDark) {
|
|
121
|
+
return `:root {
|
|
122
|
+
${indent(lightDecls)}
|
|
123
|
+
}
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
const darkDecls = normalizeDecls(darkVars);
|
|
127
|
+
const darkSelector = darkMode.darkSelector;
|
|
128
|
+
const lightSelector = darkMode.lightSelector;
|
|
129
|
+
const mediaScopeSelector = lightSelector ? `:root:not(${lightSelector})` : ":root";
|
|
130
|
+
const lines = [];
|
|
131
|
+
lines.push(":root {");
|
|
132
|
+
lines.push(indent([...lightDecls, "color-scheme: light;"]));
|
|
133
|
+
lines.push("}");
|
|
134
|
+
lines.push("@media (prefers-color-scheme: dark) {");
|
|
135
|
+
lines.push(` ${mediaScopeSelector} {`);
|
|
136
|
+
lines.push(indent([...darkDecls, "color-scheme: dark;"], 2));
|
|
137
|
+
lines.push(" }");
|
|
138
|
+
lines.push("}");
|
|
139
|
+
lines.push(`${darkSelector} {`);
|
|
140
|
+
lines.push(indent([...darkDecls, "color-scheme: dark;"]));
|
|
141
|
+
lines.push("}");
|
|
142
|
+
if (lightSelector) {
|
|
143
|
+
lines.push(`${lightSelector} {`);
|
|
144
|
+
lines.push(indent([...lightDecls, "color-scheme: light;"]));
|
|
145
|
+
lines.push("}");
|
|
146
|
+
}
|
|
147
|
+
return `${lines.join("\n")}
|
|
148
|
+
`;
|
|
149
|
+
};
|
|
150
|
+
var normalizeDecls = (vars) => {
|
|
151
|
+
if (!vars) return [];
|
|
152
|
+
return Object.entries(vars).reduce((acc, [key, value]) => {
|
|
153
|
+
const result = sanitizeCssValue(String(value));
|
|
154
|
+
if (!result.ok) return acc;
|
|
155
|
+
const cssVar = key.startsWith("--") ? key : `--${key}`;
|
|
156
|
+
acc.push(`${cssVar}: ${result.value};`);
|
|
157
|
+
return acc;
|
|
158
|
+
}, []);
|
|
159
|
+
};
|
|
160
|
+
var indent = (lines, level = 1) => {
|
|
161
|
+
const pad = " ".repeat(level);
|
|
162
|
+
return lines.map((l) => `${pad}${l}`).join("\n");
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// node_modules/@sorb/core/src/index.js
|
|
166
|
+
var TIERS = Object.freeze(["component", "semantic", "primitive"]);
|
|
167
|
+
var TIER_RANK = Object.freeze({ component: 0, semantic: 1, primitive: 2 });
|
|
168
|
+
var connectors = Object.freeze({
|
|
169
|
+
source: /* @__PURE__ */ new Map(),
|
|
170
|
+
codeSource: /* @__PURE__ */ new Map(),
|
|
171
|
+
target: /* @__PURE__ */ new Map()
|
|
172
|
+
});
|
|
173
|
+
function registerTarget(adapter) {
|
|
174
|
+
connectors.target.set(adapter.id, adapter);
|
|
175
|
+
return adapter;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/targets/reactBootstrap.js
|
|
179
|
+
var SORB_TOKENSET_FORMAT_ID = "sorb/tokenset-esm";
|
|
180
|
+
var reactBootstrapTarget = {
|
|
181
|
+
id: "react-bootstrap",
|
|
182
|
+
emitFormat: SORB_TOKENSET_FORMAT_ID,
|
|
183
|
+
// The Bootstrap-styled vocab namespace (matches `sorb-demo/src/sorbConfig.js`'s
|
|
184
|
+
// `preview.expectPrefixes: ['bs-']`).
|
|
185
|
+
expectPrefixes: ["bs-"],
|
|
186
|
+
// Left undefined on purpose — see file header.
|
|
187
|
+
inject: void 0,
|
|
188
|
+
// Bootstrap 5.3's native dark-mode convention (real-dark-mode spec D1): a
|
|
189
|
+
// `data-bs-theme` attribute on any ancestor (Bootstrap recommends
|
|
190
|
+
// `<html>`) selects the mode; absent ⇒ OS `prefers-color-scheme` governs.
|
|
191
|
+
darkMode: {
|
|
192
|
+
strategy: "attribute",
|
|
193
|
+
attribute: "data-bs-theme",
|
|
194
|
+
darkSelector: '[data-bs-theme="dark"]',
|
|
195
|
+
lightSelector: '[data-bs-theme="light"]'
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
if (typeof registerTarget === "function") {
|
|
199
|
+
registerTarget(reactBootstrapTarget);
|
|
200
|
+
}
|
|
95
201
|
|
|
96
202
|
// src/previewGuard.js
|
|
97
203
|
var DEFAULT_ORIGIN = "http://localhost:7777";
|
|
@@ -134,6 +240,26 @@ var shouldLoadPreview = (config) => {
|
|
|
134
240
|
return { allowed: false, origin, reason: "origin-not-allowlisted" };
|
|
135
241
|
};
|
|
136
242
|
|
|
243
|
+
// src/previewVocab.js
|
|
244
|
+
var countMatchingPrefixes = (tokens, prefixes) => {
|
|
245
|
+
const list = Array.isArray(prefixes) ? prefixes : [];
|
|
246
|
+
return Object.keys(tokens || {}).filter((key) => list.some((p) => key.startsWith(p))).length;
|
|
247
|
+
};
|
|
248
|
+
var checkPreviewVocabulary = ({ tokens, expectPrefixes, previewId }) => {
|
|
249
|
+
if (!Array.isArray(expectPrefixes) || expectPrefixes.length === 0) return false;
|
|
250
|
+
const appliedCount = Object.keys(tokens || {}).length;
|
|
251
|
+
if (appliedCount === 0) return false;
|
|
252
|
+
const matched = countMatchingPrefixes(tokens, expectPrefixes);
|
|
253
|
+
if (matched > 0) return false;
|
|
254
|
+
try {
|
|
255
|
+
console.warn(
|
|
256
|
+
`[Sorb] preview ${previewId ? `"${previewId}" ` : ""}applied ${appliedCount} tokens but none match expected prefixes ${JSON.stringify(expectPrefixes)} \u2014 the app may not visibly re-skin (token-vocabulary mismatch).`
|
|
257
|
+
);
|
|
258
|
+
} catch (e) {
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
261
|
+
};
|
|
262
|
+
|
|
137
263
|
// src/bridgeAuth.js
|
|
138
264
|
var bridgeHeaders = (key, base) => {
|
|
139
265
|
const headers = base ? { ...base } : {};
|
|
@@ -143,8 +269,163 @@ var bridgeHeaders = (key, base) => {
|
|
|
143
269
|
return headers;
|
|
144
270
|
};
|
|
145
271
|
|
|
146
|
-
// src/
|
|
147
|
-
|
|
272
|
+
// src/connection.js
|
|
273
|
+
var DEFAULT_CLOUD_BASE = "https://api.sorbcloud.com";
|
|
274
|
+
var getOrgKey = (config) => {
|
|
275
|
+
if (!config) return null;
|
|
276
|
+
const key = config.orgKey || config.publishableKey;
|
|
277
|
+
return typeof key === "string" && key.trim() !== "" ? key.trim() : null;
|
|
278
|
+
};
|
|
279
|
+
var shouldResolveOrgConnection = (config) => {
|
|
280
|
+
const key = getOrgKey(config);
|
|
281
|
+
if (!key) return false;
|
|
282
|
+
const explicitOrigin = config && config.preview && config.preview.origin;
|
|
283
|
+
return !(typeof explicitOrigin === "string" && explicitOrigin.trim() !== "");
|
|
284
|
+
};
|
|
285
|
+
var resolveOrgConnection = async (orgKey, opts) => {
|
|
286
|
+
const { cloudBase = DEFAULT_CLOUD_BASE, fetchImpl } = opts || {};
|
|
287
|
+
const doFetch = fetchImpl || (typeof fetch !== "undefined" ? fetch : null);
|
|
288
|
+
if (!doFetch || typeof orgKey !== "string" || orgKey.trim() === "") return null;
|
|
289
|
+
try {
|
|
290
|
+
const base = cloudBase.replace(/\/$/, "");
|
|
291
|
+
const url = `${base}/api/orgs/resolve?key=${encodeURIComponent(orgKey.trim())}`;
|
|
292
|
+
const res = await doFetch(url);
|
|
293
|
+
if (!res || !res.ok) return null;
|
|
294
|
+
const data = await res.json();
|
|
295
|
+
if (!data || typeof data !== "object") return null;
|
|
296
|
+
if (typeof data.bridgeUrl !== "string" || data.bridgeUrl.trim() === "") return null;
|
|
297
|
+
const bridgeMode = typeof data.bridgeMode === "string" ? data.bridgeMode : "C";
|
|
298
|
+
return {
|
|
299
|
+
bridgeMode,
|
|
300
|
+
bridgeUrl: data.bridgeUrl,
|
|
301
|
+
orgId: typeof data.orgId === "string" ? data.orgId : null,
|
|
302
|
+
tokenSource: typeof data.tokenSource === "string" ? data.tokenSource : null,
|
|
303
|
+
// sorb-cloud's /api/orgs/resolve returns this as a boolean (entitlement
|
|
304
|
+
// flag), not an object — see cloud src/lib/orgResolve.ts.
|
|
305
|
+
previewPersistence: typeof data.previewPersistence === "boolean" ? data.previewPersistence : null,
|
|
306
|
+
transport: data.transport === "poll" || data.transport === "sse" ? data.transport : bridgeMode === "A" ? "sse" : "poll"
|
|
307
|
+
};
|
|
308
|
+
} catch (e) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var buildEffectivePreviewConfig = (config, resolved) => {
|
|
313
|
+
const base = config && config.preview || {};
|
|
314
|
+
if (!resolved) return base;
|
|
315
|
+
const orgKey = getOrgKey(config);
|
|
316
|
+
return {
|
|
317
|
+
...base,
|
|
318
|
+
enabled: true,
|
|
319
|
+
origin: resolved.bridgeUrl,
|
|
320
|
+
allowedOrigins: [...Array.isArray(base.allowedOrigins) ? base.allowedOrigins : [], resolved.bridgeUrl],
|
|
321
|
+
key: base.key || orgKey || void 0
|
|
322
|
+
};
|
|
323
|
+
};
|
|
324
|
+
var buildEffectiveConfig = (config, resolved) => {
|
|
325
|
+
if (!resolved) return config;
|
|
326
|
+
return { ...config, preview: buildEffectivePreviewConfig(config, resolved) };
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// src/sse.js
|
|
330
|
+
var buildSubscribeUrl = (bridgeUrl, orgId, previewId, key) => {
|
|
331
|
+
const base = String(bridgeUrl).replace(/\/$/, "");
|
|
332
|
+
const path = `${base}/orgs/${encodeURIComponent(orgId)}/preview/${encodeURIComponent(previewId)}/subscribe`;
|
|
333
|
+
if (typeof key === "string" && key.trim() !== "") {
|
|
334
|
+
return `${path}?key=${encodeURIComponent(key.trim())}`;
|
|
335
|
+
}
|
|
336
|
+
return path;
|
|
337
|
+
};
|
|
338
|
+
var parsePreviewFrame = (raw) => {
|
|
339
|
+
let frame;
|
|
340
|
+
try {
|
|
341
|
+
frame = JSON.parse(raw);
|
|
342
|
+
} catch (e) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
if (!frame || typeof frame !== "object") return null;
|
|
346
|
+
if (frame.type === "ping") return { type: "ping" };
|
|
347
|
+
if (frame.type === "delete") return { type: "delete", tokens: null };
|
|
348
|
+
if ((frame.type === "snapshot" || frame.type === "update") && frame.tokens && typeof frame.tokens === "object") {
|
|
349
|
+
return { type: frame.type, tokens: frame.tokens };
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
};
|
|
353
|
+
var createPreviewSubscription = ({ EventSourceImpl, url, onTokens, onDelete, onError }) => {
|
|
354
|
+
if (typeof EventSourceImpl !== "function") return null;
|
|
355
|
+
const es = new EventSourceImpl(url);
|
|
356
|
+
es.onmessage = (evt) => {
|
|
357
|
+
const parsed = parsePreviewFrame(evt && evt.data);
|
|
358
|
+
if (!parsed || parsed.type === "ping") return;
|
|
359
|
+
if (parsed.type === "delete") {
|
|
360
|
+
if (onDelete) onDelete();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
onTokens(parsed.tokens);
|
|
364
|
+
};
|
|
365
|
+
const handleError = (evt) => {
|
|
366
|
+
if (onError) onError(evt);
|
|
367
|
+
};
|
|
368
|
+
if (typeof es.addEventListener === "function") {
|
|
369
|
+
es.addEventListener("error", handleError);
|
|
370
|
+
} else {
|
|
371
|
+
es.onerror = handleError;
|
|
372
|
+
}
|
|
373
|
+
return () => {
|
|
374
|
+
try {
|
|
375
|
+
es.close();
|
|
376
|
+
} catch (e) {
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// src/previewMode.js
|
|
382
|
+
var isModeAwarePreviewBody = (body) => !!body && typeof body === "object" && !Array.isArray(body) && "tokens" in body;
|
|
383
|
+
var resolvePreviewBody = (body, fallbackDarkMode) => {
|
|
384
|
+
if (!isModeAwarePreviewBody(body)) {
|
|
385
|
+
return { kind: "flat", tokens: (
|
|
386
|
+
/** @type {import('./types').TokenSet} */
|
|
387
|
+
body
|
|
388
|
+
) };
|
|
389
|
+
}
|
|
390
|
+
const wrapper = (
|
|
391
|
+
/** @type {{ tokens: import('./types').TokenSet, darkTokens?: import('./types').TokenSet, darkMode?: import('@sorb/core').DarkModeConvention }} */
|
|
392
|
+
body
|
|
393
|
+
);
|
|
394
|
+
const darkTokens = wrapper.darkTokens;
|
|
395
|
+
if (darkTokens && Object.keys(darkTokens).length > 0) {
|
|
396
|
+
return {
|
|
397
|
+
kind: "mode-aware",
|
|
398
|
+
lightTokens: wrapper.tokens,
|
|
399
|
+
darkTokens,
|
|
400
|
+
darkMode: wrapper.darkMode ?? fallbackDarkMode
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
return { kind: "flat", tokens: wrapper.tokens };
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
// src/modeAction.js
|
|
407
|
+
var darkClassName = (darkModeConvention) => {
|
|
408
|
+
const selector = String(darkModeConvention?.darkSelector || "").trim();
|
|
409
|
+
const match = /^\.([a-zA-Z0-9_-]+)$/.exec(selector);
|
|
410
|
+
return match ? match[1] : "dark";
|
|
411
|
+
};
|
|
412
|
+
var resolveModeAction = (darkModeConvention, next) => {
|
|
413
|
+
const strategy = darkModeConvention?.strategy || "attribute";
|
|
414
|
+
if (strategy === "media") {
|
|
415
|
+
return { type: "none" };
|
|
416
|
+
}
|
|
417
|
+
if (strategy === "class") {
|
|
418
|
+
const className = darkClassName(darkModeConvention);
|
|
419
|
+
return next === "dark" ? { type: "class-add", className } : { type: "class-remove", className };
|
|
420
|
+
}
|
|
421
|
+
const attribute = darkModeConvention?.attribute || "data-bs-theme";
|
|
422
|
+
return next === "auto" ? { type: "attr-remove", attribute } : { type: "attr-set", attribute, value: next };
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// src/core.js
|
|
426
|
+
var EventSourceCtor = typeof EventSource !== "undefined" ? EventSource : null;
|
|
427
|
+
var matchMediaFn = typeof matchMedia !== "undefined" ? matchMedia : null;
|
|
428
|
+
var DARK_MEDIA_QUERY = "(prefers-color-scheme: dark)";
|
|
148
429
|
var devWarn = (msg) => {
|
|
149
430
|
try {
|
|
150
431
|
if (typeof process !== "undefined" && process.env && true) {
|
|
@@ -153,54 +434,187 @@ var devWarn = (msg) => {
|
|
|
153
434
|
} catch (e) {
|
|
154
435
|
}
|
|
155
436
|
};
|
|
156
|
-
var
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
);
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
437
|
+
var warnedDeprecations = /* @__PURE__ */ new Set();
|
|
438
|
+
function warnDeprecated(resolved) {
|
|
439
|
+
if (typeof process !== "undefined" && false) return;
|
|
440
|
+
for (let i = 0; i < resolved.length; i++) {
|
|
441
|
+
const token = resolved[i];
|
|
442
|
+
if (!token.deprecated) continue;
|
|
443
|
+
if (warnedDeprecations.has(token.id)) continue;
|
|
444
|
+
warnedDeprecations.add(token.id);
|
|
445
|
+
const replacedBy = token.replacedBy || token.$extensions && token.$extensions.sorb && token.$extensions.sorb.replacedBy || null;
|
|
446
|
+
if (replacedBy) {
|
|
447
|
+
console.warn("[@sorb/leaf] Deprecated token: " + token.id + " \u2014 use " + replacedBy + " instead");
|
|
448
|
+
} else {
|
|
449
|
+
console.warn("[@sorb/leaf] Deprecated token: " + token.id + " is deprecated");
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function sorbInit(config) {
|
|
454
|
+
let activeTokens = config.tokens;
|
|
455
|
+
let isPreview = false;
|
|
456
|
+
let previewId = null;
|
|
457
|
+
let previewMismatch = false;
|
|
458
|
+
let pollId = null;
|
|
459
|
+
let cancelled = false;
|
|
460
|
+
let unsubscribeSSE = null;
|
|
461
|
+
const hasDarkMode = !!(config.darkTokens && Object.keys(config.darkTokens).length > 0);
|
|
462
|
+
const darkModeConvention = config.darkModeConvention || reactBootstrapTarget.darkMode;
|
|
463
|
+
let mode = "auto";
|
|
464
|
+
let systemScheme = matchMediaFn ? matchMediaFn(DARK_MEDIA_QUERY).matches ? "dark" : "light" : "light";
|
|
465
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
466
|
+
const getState = () => ({
|
|
467
|
+
tokens: activeTokens,
|
|
468
|
+
isPreview,
|
|
469
|
+
previewId,
|
|
470
|
+
previewMismatch,
|
|
471
|
+
mode,
|
|
472
|
+
resolvedScheme: mode === "auto" ? systemScheme : mode
|
|
473
|
+
});
|
|
474
|
+
const notify = () => {
|
|
475
|
+
const state = getState();
|
|
476
|
+
listeners.forEach((listener) => listener(state));
|
|
477
|
+
};
|
|
478
|
+
const subscribe = (listener) => {
|
|
479
|
+
listeners.add(listener);
|
|
480
|
+
return () => listeners.delete(listener);
|
|
481
|
+
};
|
|
482
|
+
let mql = null;
|
|
483
|
+
const onSchemeChange = (e) => {
|
|
484
|
+
systemScheme = e.matches ? "dark" : "light";
|
|
485
|
+
notify();
|
|
486
|
+
};
|
|
487
|
+
if (matchMediaFn) {
|
|
488
|
+
mql = matchMediaFn(DARK_MEDIA_QUERY);
|
|
489
|
+
if (typeof mql.addEventListener === "function") mql.addEventListener("change", onSchemeChange);
|
|
490
|
+
else if (typeof mql.addListener === "function") mql.addListener(onSchemeChange);
|
|
491
|
+
}
|
|
492
|
+
let inlineTokens = null;
|
|
493
|
+
const applyFlat = (tokens) => {
|
|
494
|
+
clearModeStylesheet();
|
|
495
|
+
applyTokens(tokens);
|
|
496
|
+
inlineTokens = tokens;
|
|
497
|
+
};
|
|
498
|
+
const applyModeAware = (lightTokens, darkTokens, convention) => {
|
|
499
|
+
if (inlineTokens) {
|
|
500
|
+
clearTokenOverrides(inlineTokens);
|
|
501
|
+
inlineTokens = null;
|
|
502
|
+
}
|
|
503
|
+
injectModeStylesheet(buildModeStylesheet(lightTokens, darkTokens, convention));
|
|
504
|
+
};
|
|
505
|
+
const loadCommitted = () => {
|
|
506
|
+
if (hasDarkMode) {
|
|
507
|
+
applyModeAware(config.tokens, config.darkTokens, darkModeConvention);
|
|
508
|
+
} else {
|
|
509
|
+
applyFlat(config.tokens);
|
|
510
|
+
}
|
|
511
|
+
activeTokens = config.tokens;
|
|
512
|
+
isPreview = false;
|
|
513
|
+
previewId = null;
|
|
514
|
+
previewMismatch = false;
|
|
515
|
+
notify();
|
|
516
|
+
};
|
|
517
|
+
const applyPreviewTokens = (body, id, effectiveConfig) => {
|
|
518
|
+
const resolved = resolvePreviewBody(body, darkModeConvention);
|
|
519
|
+
let flatTokens;
|
|
520
|
+
if (resolved.kind === "mode-aware") {
|
|
521
|
+
applyModeAware(resolved.lightTokens, resolved.darkTokens, resolved.darkMode);
|
|
522
|
+
flatTokens = resolved.lightTokens;
|
|
523
|
+
} else {
|
|
524
|
+
applyFlat(resolved.tokens);
|
|
525
|
+
flatTokens = resolved.tokens;
|
|
526
|
+
}
|
|
527
|
+
activeTokens = flatTokens;
|
|
528
|
+
isPreview = true;
|
|
529
|
+
previewId = id;
|
|
530
|
+
previewMismatch = checkPreviewVocabulary({
|
|
531
|
+
tokens: flatTokens,
|
|
532
|
+
expectPrefixes: effectiveConfig.preview?.expectPrefixes,
|
|
533
|
+
previewId: id
|
|
534
|
+
});
|
|
535
|
+
notify();
|
|
536
|
+
};
|
|
537
|
+
const loadPreview = async (id, effectiveConfig) => {
|
|
538
|
+
const cfg = effectiveConfig || config;
|
|
539
|
+
const guard = shouldLoadPreview(cfg);
|
|
540
|
+
if (!guard.allowed) {
|
|
541
|
+
loadCommitted();
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
const origin = guard.origin;
|
|
545
|
+
try {
|
|
546
|
+
const res = await fetch(`${origin}/preview/${id}`, {
|
|
547
|
+
headers: bridgeHeaders(cfg.preview?.key)
|
|
548
|
+
});
|
|
549
|
+
if (!res.ok) throw new Error("preview not found");
|
|
550
|
+
const tokens = await res.json();
|
|
551
|
+
applyPreviewTokens(tokens, id, cfg);
|
|
552
|
+
return true;
|
|
553
|
+
} catch (e) {
|
|
554
|
+
loadCommitted();
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
const clearPreview = () => {
|
|
559
|
+
if (pollId) {
|
|
560
|
+
clearInterval(pollId);
|
|
561
|
+
pollId = null;
|
|
562
|
+
}
|
|
563
|
+
if (typeof location !== "undefined" && typeof history !== "undefined") {
|
|
564
|
+
const params = new URLSearchParams(location.search);
|
|
565
|
+
params.delete("preview");
|
|
566
|
+
const qs = params.toString();
|
|
567
|
+
history.replaceState(null, "", qs ? `?${qs}` : location.pathname);
|
|
568
|
+
}
|
|
199
569
|
loadCommitted();
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
570
|
+
};
|
|
571
|
+
const setMode = (next) => {
|
|
572
|
+
mode = next;
|
|
573
|
+
if (typeof document !== "undefined") {
|
|
574
|
+
const action = resolveModeAction(darkModeConvention, next);
|
|
575
|
+
switch (action.type) {
|
|
576
|
+
case "attr-set":
|
|
577
|
+
document.documentElement.setAttribute(action.attribute, action.value);
|
|
578
|
+
break;
|
|
579
|
+
case "attr-remove":
|
|
580
|
+
document.documentElement.removeAttribute(action.attribute);
|
|
581
|
+
break;
|
|
582
|
+
case "class-add":
|
|
583
|
+
document.documentElement.classList.add(action.className);
|
|
584
|
+
break;
|
|
585
|
+
case "class-remove":
|
|
586
|
+
document.documentElement.classList.remove(action.className);
|
|
587
|
+
break;
|
|
588
|
+
case "none":
|
|
589
|
+
default:
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
notify();
|
|
594
|
+
};
|
|
595
|
+
const destroy = () => {
|
|
596
|
+
cancelled = true;
|
|
597
|
+
if (pollId) clearInterval(pollId);
|
|
598
|
+
if (unsubscribeSSE) unsubscribeSSE();
|
|
599
|
+
if (mql) {
|
|
600
|
+
if (typeof mql.removeEventListener === "function") mql.removeEventListener("change", onSchemeChange);
|
|
601
|
+
else if (typeof mql.removeListener === "function") mql.removeListener(onSchemeChange);
|
|
602
|
+
}
|
|
603
|
+
listeners.clear();
|
|
604
|
+
};
|
|
605
|
+
const init = async () => {
|
|
606
|
+
if (config.resolved && config.resolved.length) warnDeprecated(config.resolved);
|
|
607
|
+
let effectiveConfig = config;
|
|
608
|
+
let resolvedConnection = null;
|
|
609
|
+
if (shouldResolveOrgConnection(config)) {
|
|
610
|
+
resolvedConnection = await resolveOrgConnection(getOrgKey(config), {
|
|
611
|
+
cloudBase: config.cloudBase
|
|
612
|
+
});
|
|
613
|
+
if (cancelled) return;
|
|
614
|
+
effectiveConfig = buildEffectiveConfig(config, resolvedConnection);
|
|
615
|
+
}
|
|
616
|
+
const guard = shouldLoadPreview(effectiveConfig);
|
|
617
|
+
const id = typeof location !== "undefined" ? new URLSearchParams(location.search).get("preview") : null;
|
|
204
618
|
if (!guard.allowed || !id) {
|
|
205
619
|
if (id && !guard.allowed) {
|
|
206
620
|
devWarn(
|
|
@@ -210,16 +624,213 @@ var SorbProvider = ({ config, children }) => {
|
|
|
210
624
|
loadCommitted();
|
|
211
625
|
return;
|
|
212
626
|
}
|
|
213
|
-
loadPreview(id)
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
627
|
+
const ok = await loadPreview(id, effectiveConfig);
|
|
628
|
+
if (!ok || cancelled) return;
|
|
629
|
+
const useSSE = resolvedConnection && resolvedConnection.transport === "sse" && resolvedConnection.orgId && EventSourceCtor;
|
|
630
|
+
if (useSSE) {
|
|
631
|
+
const url = buildSubscribeUrl(
|
|
632
|
+
resolvedConnection.bridgeUrl,
|
|
633
|
+
resolvedConnection.orgId,
|
|
634
|
+
id,
|
|
635
|
+
effectiveConfig.preview?.key
|
|
636
|
+
);
|
|
637
|
+
unsubscribeSSE = createPreviewSubscription({
|
|
638
|
+
EventSourceImpl: EventSourceCtor,
|
|
639
|
+
url,
|
|
640
|
+
onTokens: (tokens) => applyPreviewTokens(tokens, id, effectiveConfig),
|
|
641
|
+
onDelete: () => loadCommitted(),
|
|
642
|
+
onError: () => devWarn("SSE preview subscription error \u2014 preview may be stale")
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
if (!unsubscribeSSE) {
|
|
646
|
+
const interval = effectiveConfig.preview?.pollInterval ?? 1500;
|
|
647
|
+
pollId = setInterval(() => loadPreview(id, effectiveConfig), interval);
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
init();
|
|
651
|
+
return { getState, subscribe, setMode, clearPreview, destroy };
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// src/legacyMap.js
|
|
655
|
+
var normalizeProp = (prop) => {
|
|
656
|
+
if (typeof prop !== "string") return "";
|
|
657
|
+
return prop.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/_/g, "-").toLowerCase();
|
|
658
|
+
};
|
|
659
|
+
var canonicalizeColor = (v) => {
|
|
660
|
+
const hex = v.match(/^#([0-9a-f]{3,8})$/);
|
|
661
|
+
if (hex) {
|
|
662
|
+
let h = hex[1];
|
|
663
|
+
if (h.length === 3 || h.length === 4) {
|
|
664
|
+
h = h.split("").map((c) => c + c).join("");
|
|
665
|
+
}
|
|
666
|
+
if (h.length !== 6 && h.length !== 8) return null;
|
|
667
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
668
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
669
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
670
|
+
if (h.length === 8) {
|
|
671
|
+
const a = parseInt(h.slice(6, 8), 16) / 255;
|
|
672
|
+
const as = String(Math.round(a * 1e3) / 1e3);
|
|
673
|
+
return `rgba(${r}, ${g}, ${b}, ${as})`;
|
|
674
|
+
}
|
|
675
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
676
|
+
}
|
|
677
|
+
const fn = v.match(/^(rgba?)\(([^)]*)\)$/);
|
|
678
|
+
if (fn) {
|
|
679
|
+
const parts = fn[2].split(",").map((p) => p.trim()).filter((p) => p !== "");
|
|
680
|
+
if (parts.length === 3) return `rgb(${parts[0]}, ${parts[1]}, ${parts[2]})`;
|
|
681
|
+
if (parts.length === 4) return `rgba(${parts[0]}, ${parts[1]}, ${parts[2]}, ${parts[3]})`;
|
|
682
|
+
}
|
|
683
|
+
return null;
|
|
684
|
+
};
|
|
685
|
+
var normalizeValue = (value) => {
|
|
686
|
+
if (value == null) return "";
|
|
687
|
+
let v = String(value).trim().toLowerCase();
|
|
688
|
+
if (v === "") return "";
|
|
689
|
+
v = v.replace(/\s+/g, " ");
|
|
690
|
+
const color = canonicalizeColor(v);
|
|
691
|
+
if (color) return color;
|
|
692
|
+
if (/^-?\d*\.?\d+$/.test(v)) v = `${v}px`;
|
|
693
|
+
return v;
|
|
694
|
+
};
|
|
695
|
+
var indexLegacyMap = (legacyMap) => {
|
|
696
|
+
const idx = /* @__PURE__ */ new Map();
|
|
697
|
+
if (!Array.isArray(legacyMap)) return idx;
|
|
698
|
+
for (const row of legacyMap) {
|
|
699
|
+
if (!row || row.cssVar == null || row.raw == null || row.prop == null) continue;
|
|
700
|
+
const p = normalizeProp(row.prop);
|
|
701
|
+
const entry = {
|
|
702
|
+
normValue: normalizeValue(row.raw),
|
|
703
|
+
cssVar: String(row.cssVar).replace(/^--/, ""),
|
|
704
|
+
raw: String(row.raw)
|
|
705
|
+
};
|
|
706
|
+
const list = idx.get(p);
|
|
707
|
+
if (list) list.push(entry);
|
|
708
|
+
else idx.set(p, [entry]);
|
|
709
|
+
}
|
|
710
|
+
return idx;
|
|
711
|
+
};
|
|
712
|
+
var computeLegacyOverride = (prop, computedValue, legacyMap) => {
|
|
713
|
+
const idx = legacyMap instanceof Map ? legacyMap : indexLegacyMap(legacyMap);
|
|
714
|
+
const list = idx.get(normalizeProp(prop));
|
|
715
|
+
if (!list || list.length === 0) return null;
|
|
716
|
+
const target = normalizeValue(computedValue);
|
|
717
|
+
if (target === "") return null;
|
|
718
|
+
for (const entry of list) {
|
|
719
|
+
if (entry.normValue === target) {
|
|
720
|
+
return `var(--${entry.cssVar}, ${entry.raw})`;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return null;
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
// src/legacyDom.js
|
|
727
|
+
var applyLegacyMap = (root, legacyMap) => {
|
|
728
|
+
const restores = [];
|
|
729
|
+
const handle = { restores };
|
|
730
|
+
if (typeof document === "undefined") return handle;
|
|
731
|
+
const start = root ?? document.body;
|
|
732
|
+
if (!start || !Array.isArray(legacyMap) || legacyMap.length === 0) return handle;
|
|
733
|
+
const idx = indexLegacyMap(legacyMap);
|
|
734
|
+
if (idx.size === 0) return handle;
|
|
735
|
+
const props = Array.from(idx.keys());
|
|
736
|
+
const getView = () => {
|
|
737
|
+
const doc = start.ownerDocument || (start.nodeType === 9 ? start : document);
|
|
738
|
+
return doc.defaultView || (typeof window !== "undefined" ? window : null);
|
|
739
|
+
};
|
|
740
|
+
const view = getView();
|
|
741
|
+
if (!view || typeof view.getComputedStyle !== "function") return handle;
|
|
742
|
+
const visit = (el) => {
|
|
743
|
+
if (!el || el.nodeType !== 1) return;
|
|
744
|
+
const cs = view.getComputedStyle(el);
|
|
745
|
+
for (const prop of props) {
|
|
746
|
+
const computed = cs.getPropertyValue(prop);
|
|
747
|
+
const override = computeLegacyOverride(prop, computed, idx);
|
|
748
|
+
if (override == null) continue;
|
|
749
|
+
const prev = el.style.getPropertyValue(prop);
|
|
750
|
+
restores.push({ el: (
|
|
751
|
+
/** @type {HTMLElement} */
|
|
752
|
+
el
|
|
753
|
+
), prop, prev });
|
|
754
|
+
el.style.setProperty(prop, override);
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
if (start.nodeType === 1) visit(
|
|
758
|
+
/** @type {Element} */
|
|
759
|
+
start
|
|
760
|
+
);
|
|
761
|
+
const all = start.querySelectorAll ? start.querySelectorAll("*") : [];
|
|
762
|
+
for (const el of all) visit(el);
|
|
763
|
+
return handle;
|
|
764
|
+
};
|
|
765
|
+
var clearLegacyMap = (handle) => {
|
|
766
|
+
if (!handle || !Array.isArray(handle.restores)) return;
|
|
767
|
+
for (const { el, prop, prev } of handle.restores) {
|
|
768
|
+
if (!el || !el.style) continue;
|
|
769
|
+
if (prev === "" || prev == null) el.style.removeProperty(prop);
|
|
770
|
+
else el.style.setProperty(prop, prev);
|
|
771
|
+
}
|
|
772
|
+
handle.restores = [];
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
// src/TokenProvider.jsx
|
|
776
|
+
import { jsx } from "react/jsx-runtime";
|
|
777
|
+
var matchMediaFn2 = typeof matchMedia !== "undefined" ? matchMedia : null;
|
|
778
|
+
var DARK_MEDIA_QUERY2 = "(prefers-color-scheme: dark)";
|
|
779
|
+
var SorbProvider = ({ config, legacyMap, children }) => {
|
|
780
|
+
const instanceRef = useRef(null);
|
|
781
|
+
const legacyHandleRef = useRef(null);
|
|
782
|
+
const [state, setState] = React.useState(() => ({
|
|
783
|
+
tokens: config.tokens,
|
|
784
|
+
isPreview: false,
|
|
785
|
+
previewId: null,
|
|
786
|
+
previewMismatch: false,
|
|
787
|
+
mode: "auto",
|
|
788
|
+
resolvedScheme: matchMediaFn2 ? matchMediaFn2(DARK_MEDIA_QUERY2).matches ? "dark" : "light" : "light"
|
|
789
|
+
}));
|
|
790
|
+
const resolvedLegacyMap = legacyMap ?? config.legacyMap ?? null;
|
|
791
|
+
useEffect(() => {
|
|
792
|
+
const instance = sorbInit(config);
|
|
793
|
+
instanceRef.current = instance;
|
|
794
|
+
setState(instance.getState());
|
|
795
|
+
const unsubscribe = instance.subscribe(setState);
|
|
796
|
+
return () => {
|
|
797
|
+
unsubscribe();
|
|
798
|
+
instance.destroy();
|
|
799
|
+
instanceRef.current = null;
|
|
800
|
+
};
|
|
801
|
+
}, []);
|
|
802
|
+
useEffect(() => {
|
|
803
|
+
if (!resolvedLegacyMap || resolvedLegacyMap.length === 0) return void 0;
|
|
804
|
+
if (typeof document === "undefined") return void 0;
|
|
805
|
+
if (legacyHandleRef.current) clearLegacyMap(legacyHandleRef.current);
|
|
806
|
+
legacyHandleRef.current = applyLegacyMap(document.body, resolvedLegacyMap);
|
|
218
807
|
return () => {
|
|
219
|
-
if (
|
|
808
|
+
if (legacyHandleRef.current) {
|
|
809
|
+
clearLegacyMap(legacyHandleRef.current);
|
|
810
|
+
legacyHandleRef.current = null;
|
|
811
|
+
}
|
|
220
812
|
};
|
|
813
|
+
}, [resolvedLegacyMap, state.tokens]);
|
|
814
|
+
const setMode = useCallback((next) => {
|
|
815
|
+
if (instanceRef.current) instanceRef.current.setMode(next);
|
|
221
816
|
}, []);
|
|
222
|
-
|
|
817
|
+
const clearPreview = useCallback(() => {
|
|
818
|
+
if (instanceRef.current) instanceRef.current.clearPreview();
|
|
819
|
+
}, []);
|
|
820
|
+
const value = useMemo(
|
|
821
|
+
() => ({
|
|
822
|
+
tokens: state.tokens,
|
|
823
|
+
isPreview: state.isPreview,
|
|
824
|
+
previewId: state.previewId,
|
|
825
|
+
previewMismatch: state.previewMismatch,
|
|
826
|
+
clearPreview,
|
|
827
|
+
mode: state.mode,
|
|
828
|
+
setMode,
|
|
829
|
+
resolvedScheme: state.resolvedScheme
|
|
830
|
+
}),
|
|
831
|
+
[state, clearPreview, setMode]
|
|
832
|
+
);
|
|
833
|
+
return /* @__PURE__ */ jsx(TokenContext.Provider, { value, children });
|
|
223
834
|
};
|
|
224
835
|
|
|
225
836
|
// src/PreviewBanner.jsx
|
|
@@ -241,15 +852,21 @@ var useIsPreview = () => {
|
|
|
241
852
|
return useTokenContext().isPreview;
|
|
242
853
|
};
|
|
243
854
|
var usePreviewState = () => {
|
|
244
|
-
const { isPreview, previewId, clearPreview } = useTokenContext();
|
|
245
|
-
return { isPreview, previewId, clearPreview };
|
|
855
|
+
const { isPreview, previewId, previewMismatch, clearPreview } = useTokenContext();
|
|
856
|
+
return { isPreview, previewId, previewMismatch, clearPreview };
|
|
857
|
+
};
|
|
858
|
+
var useTheme = () => {
|
|
859
|
+
const { mode, setMode, resolvedScheme } = useTokenContext();
|
|
860
|
+
return { mode, setMode, resolvedScheme };
|
|
246
861
|
};
|
|
247
862
|
|
|
248
863
|
// src/PreviewBanner.jsx
|
|
249
864
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
250
865
|
var PreviewBanner = () => {
|
|
251
|
-
const { isPreview, previewId, clearPreview } = usePreviewState();
|
|
866
|
+
const { isPreview, previewId, previewMismatch, clearPreview } = usePreviewState();
|
|
252
867
|
if (!isPreview) return null;
|
|
868
|
+
const background = previewMismatch ? "var(--sorb-preview-warning-bg, #B54708)" : "#3B5BDB";
|
|
869
|
+
const accent = previewMismatch ? "var(--sorb-preview-warning-accent, #F59E0B)" : "transparent";
|
|
253
870
|
return /* @__PURE__ */ jsxs(
|
|
254
871
|
"div",
|
|
255
872
|
{
|
|
@@ -260,7 +877,8 @@ var PreviewBanner = () => {
|
|
|
260
877
|
bottom: 0,
|
|
261
878
|
left: 0,
|
|
262
879
|
right: 0,
|
|
263
|
-
background
|
|
880
|
+
background,
|
|
881
|
+
borderTop: `3px solid ${accent}`,
|
|
264
882
|
color: "#fff",
|
|
265
883
|
padding: "10px 20px",
|
|
266
884
|
display: "flex",
|
|
@@ -275,7 +893,7 @@ var PreviewBanner = () => {
|
|
|
275
893
|
},
|
|
276
894
|
children: [
|
|
277
895
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
278
|
-
/* @__PURE__ */ jsx2("strong", { style: { fontWeight: 600 }, children: "Sorb preview active" }),
|
|
896
|
+
/* @__PURE__ */ jsx2("strong", { style: { fontWeight: 600 }, children: previewMismatch ? "Sorb preview active \u2014 may not re-skin" : "Sorb preview active" }),
|
|
279
897
|
previewId && /* @__PURE__ */ jsx2(
|
|
280
898
|
"code",
|
|
281
899
|
{
|
|
@@ -290,7 +908,7 @@ var PreviewBanner = () => {
|
|
|
290
908
|
children: previewId
|
|
291
909
|
}
|
|
292
910
|
),
|
|
293
|
-
/* @__PURE__ */ jsx2("span", { style: { marginLeft: "8px", opacity: 0.75, fontSize: "12px" }, children: "Token changes from Figma are live" })
|
|
911
|
+
/* @__PURE__ */ jsx2("span", { style: { marginLeft: "8px", opacity: 0.75, fontSize: "12px" }, children: previewMismatch ? "No matching tokens for this app \u2014 colours may be unchanged" : "Token changes from Figma are live" })
|
|
294
912
|
] }),
|
|
295
913
|
/* @__PURE__ */ jsx2(
|
|
296
914
|
"button",
|
|
@@ -318,6 +936,55 @@ var PreviewBanner = () => {
|
|
|
318
936
|
);
|
|
319
937
|
};
|
|
320
938
|
|
|
939
|
+
// src/ThemeToggle.jsx
|
|
940
|
+
import React3 from "react";
|
|
941
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
942
|
+
var OPTIONS = [
|
|
943
|
+
{ value: "light", label: "Light" },
|
|
944
|
+
{ value: "dark", label: "Dark" },
|
|
945
|
+
{ value: "auto", label: "Auto" }
|
|
946
|
+
];
|
|
947
|
+
var ThemeToggle = ({ className } = {}) => {
|
|
948
|
+
const { mode, setMode } = useTheme();
|
|
949
|
+
return /* @__PURE__ */ jsx3(
|
|
950
|
+
"div",
|
|
951
|
+
{
|
|
952
|
+
role: "radiogroup",
|
|
953
|
+
"aria-label": "Color mode",
|
|
954
|
+
className,
|
|
955
|
+
style: {
|
|
956
|
+
display: "inline-flex",
|
|
957
|
+
gap: "4px",
|
|
958
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
959
|
+
fontSize: "13px"
|
|
960
|
+
},
|
|
961
|
+
children: OPTIONS.map(({ value, label }) => {
|
|
962
|
+
const active = mode === value;
|
|
963
|
+
return /* @__PURE__ */ jsx3(
|
|
964
|
+
"button",
|
|
965
|
+
{
|
|
966
|
+
type: "button",
|
|
967
|
+
role: "radio",
|
|
968
|
+
"aria-checked": active,
|
|
969
|
+
onClick: () => setMode(value),
|
|
970
|
+
style: {
|
|
971
|
+
padding: "4px 10px",
|
|
972
|
+
borderRadius: "6px",
|
|
973
|
+
border: "1px solid rgba(0,0,0,0.15)",
|
|
974
|
+
background: active ? "var(--sorb-theme-toggle-active-bg, #3B5BDB)" : "transparent",
|
|
975
|
+
color: active ? "#fff" : "inherit",
|
|
976
|
+
cursor: "pointer",
|
|
977
|
+
fontWeight: active ? 600 : 400
|
|
978
|
+
},
|
|
979
|
+
children: label
|
|
980
|
+
},
|
|
981
|
+
value
|
|
982
|
+
);
|
|
983
|
+
})
|
|
984
|
+
}
|
|
985
|
+
);
|
|
986
|
+
};
|
|
987
|
+
|
|
321
988
|
// src/verify.js
|
|
322
989
|
var toCssVar = (name) => {
|
|
323
990
|
const s = String(name).trim();
|
|
@@ -362,12 +1029,40 @@ var verifyResolved = async (tokens, { origin = "http://localhost:7777", key, fet
|
|
|
362
1029
|
return { ok: false, reason: "bridge-unreachable", error: e && e.message };
|
|
363
1030
|
}
|
|
364
1031
|
};
|
|
1032
|
+
|
|
1033
|
+
// src/darkModeConventions.js
|
|
1034
|
+
var tailwindDarkMode = {
|
|
1035
|
+
strategy: "class",
|
|
1036
|
+
darkSelector: ".dark"
|
|
1037
|
+
};
|
|
1038
|
+
var dataThemeDarkMode = {
|
|
1039
|
+
strategy: "attribute",
|
|
1040
|
+
attribute: "data-theme",
|
|
1041
|
+
darkSelector: '[data-theme="dark"]',
|
|
1042
|
+
lightSelector: '[data-theme="light"]'
|
|
1043
|
+
};
|
|
365
1044
|
export {
|
|
1045
|
+
MODE_STYLESHEET_ID,
|
|
366
1046
|
PreviewBanner,
|
|
367
1047
|
SorbProvider,
|
|
1048
|
+
ThemeToggle,
|
|
1049
|
+
applyLegacyMap,
|
|
1050
|
+
buildModeStylesheet,
|
|
1051
|
+
clearLegacyMap,
|
|
1052
|
+
clearModeStylesheet,
|
|
1053
|
+
computeLegacyOverride,
|
|
1054
|
+
dataThemeDarkMode,
|
|
1055
|
+
indexLegacyMap,
|
|
1056
|
+
injectModeStylesheet,
|
|
1057
|
+
normalizeProp,
|
|
1058
|
+
normalizeValue,
|
|
1059
|
+
reactBootstrapTarget,
|
|
368
1060
|
sanitizeCssValue,
|
|
1061
|
+
sorbInit,
|
|
1062
|
+
tailwindDarkMode,
|
|
369
1063
|
useIsPreview,
|
|
370
1064
|
usePreviewState,
|
|
1065
|
+
useTheme,
|
|
371
1066
|
useToken,
|
|
372
1067
|
useTokens,
|
|
373
1068
|
verifyResolved
|