@uni-design-system/uni-core 7.2.0 → 8.0.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/dist/cjs/index.cjs +462 -7
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +444 -8
- package/dist/esm/index.js.map +1 -1
- package/dist/types/concepts/animation/duration.helpers.d.ts +25 -0
- package/dist/types/concepts/animation/duration.helpers.d.ts.map +1 -0
- package/dist/types/concepts/animation/duration.helpers.spec.d.ts +2 -0
- package/dist/types/concepts/animation/duration.helpers.spec.d.ts.map +1 -0
- package/dist/types/concepts/animation/index.d.ts +1 -0
- package/dist/types/concepts/animation/index.d.ts.map +1 -1
- package/dist/types/concepts/component/component.types.d.ts +1 -1
- package/dist/types/concepts/component/component.types.d.ts.map +1 -1
- package/dist/types/concepts/generation/theme-file.emitter.d.ts.map +1 -1
- package/dist/types/concepts/generation/theme.generator.d.ts +9 -2
- package/dist/types/concepts/generation/theme.generator.d.ts.map +1 -1
- package/dist/types/concepts/style/index.d.ts +1 -0
- package/dist/types/concepts/style/index.d.ts.map +1 -1
- package/dist/types/concepts/style/selectors.constants.d.ts +18 -0
- package/dist/types/concepts/style/selectors.constants.d.ts.map +1 -0
- package/dist/types/concepts/theme/index.d.ts +1 -0
- package/dist/types/concepts/theme/index.d.ts.map +1 -1
- package/dist/types/concepts/theme/theme.types.d.ts +7 -0
- package/dist/types/concepts/theme/theme.types.d.ts.map +1 -1
- package/dist/types/concepts/theme/theme.validation.d.ts +51 -0
- package/dist/types/concepts/theme/theme.validation.d.ts.map +1 -0
- package/dist/types/concepts/theme/theme.validation.spec.d.ts +2 -0
- package/dist/types/concepts/theme/theme.validation.spec.d.ts.map +1 -0
- package/dist/types/concepts/theme/themes/base.theme.d.ts +16 -0
- package/dist/types/concepts/theme/themes/base.theme.d.ts.map +1 -1
- package/dist/types/concepts/typography/typeface.helpers.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,4 +1,35 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/concepts/animation/duration.helpers.ts
|
|
3
|
+
/** Duration (seconds) the default theme assigns `expand`'s `transitionSpeed`. */
|
|
4
|
+
var EXPAND_DEFAULT_SPEED = .35;
|
|
5
|
+
/**
|
|
6
|
+
* Content height (px) at which a reveal runs at exactly its `transitionSpeed`.
|
|
7
|
+
* Roughly a few lines of card content — the size the default 0.35s was tuned on.
|
|
8
|
+
*/
|
|
9
|
+
var EXPAND_REFERENCE_HEIGHT = 240;
|
|
10
|
+
/**
|
|
11
|
+
* Duration envelope at the default speed. A tiny region never blinks past
|
|
12
|
+
* faster than the min; a full-page region never drags longer than the max.
|
|
13
|
+
* Expressed at `EXPAND_DEFAULT_SPEED` and applied as scale-factor clamps, so a
|
|
14
|
+
* theme that slows `transitionSpeed` down widens its envelope proportionally
|
|
15
|
+
* instead of being capped back to the stock feel.
|
|
16
|
+
*/
|
|
17
|
+
var EXPAND_MIN_DURATION = .15;
|
|
18
|
+
var EXPAND_MAX_DURATION = .6;
|
|
19
|
+
var MIN_SCALE = EXPAND_MIN_DURATION / EXPAND_DEFAULT_SPEED;
|
|
20
|
+
var MAX_SCALE = EXPAND_MAX_DURATION / EXPAND_DEFAULT_SPEED;
|
|
21
|
+
/**
|
|
22
|
+
* Size-aware reveal duration: `speed × √(height ÷ reference)`, clamped.
|
|
23
|
+
*
|
|
24
|
+
* A fixed duration reads as sluggish on a short region and rushed on a tall
|
|
25
|
+
* one; scaling by the square root of height keeps perceived speed steady —
|
|
26
|
+
* bigger reveals get more time, but sublinearly.
|
|
27
|
+
*/
|
|
28
|
+
var expandDuration = (contentHeight, speed = EXPAND_DEFAULT_SPEED) => {
|
|
29
|
+
const scale = Math.sqrt((Number.isFinite(contentHeight) ? Math.max(contentHeight, 0) : 0) / 240);
|
|
30
|
+
return speed * Math.min(Math.max(scale, MIN_SCALE), MAX_SCALE);
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
2
33
|
//#region src/concepts/animation/keyframes.constants.ts
|
|
3
34
|
var fadeIn = {
|
|
4
35
|
"0%": { opacity: 0 },
|
|
@@ -899,6 +930,25 @@ var BaseIcons = {
|
|
|
899
930
|
spinner: "data:image/svg+xml;charset=UTF-8,%3csvg stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' %3e%3cstyle%3e .oui_spinner %7b transform-origin: center; animation: rotate_360 2s linear infinite;%7d .oui_spinner circle %7b stroke-linecap: round; animation: dash 1.5s ease-in-out infinite;%7d %40keyframes rotate_360 %7b 100%25 %7b transform: rotate(360deg);%7d %7d %40keyframes dash %7b 0%25 %7b stroke-dasharray: 0 150; stroke-dashoffset: 0;%7d 47.5%25 %7b stroke-dasharray: 42 150; stroke-dashoffset: -16;%7d 95%25, 100%25 %7b stroke-dasharray: 42 150; stroke-dashoffset: -59;%7d %7d %3c/style%3e%3cg class='oui_spinner'%3e%3ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3e%3c/circle%3e%3c/g%3e%3c/svg%3e "
|
|
900
931
|
};
|
|
901
932
|
//#endregion
|
|
933
|
+
//#region src/concepts/style/selectors.constants.ts
|
|
934
|
+
/**
|
|
935
|
+
* The highlight state for roving-focus composites (menus, and any other widget
|
|
936
|
+
* that moves focus programmatically): pointer hover, or focus the user drove
|
|
937
|
+
* from the keyboard.
|
|
938
|
+
*
|
|
939
|
+
* Why not plain `:focus` — a roving-focus composite calls `.focus()` on an item
|
|
940
|
+
* every time it opens, including pointer opens, and `:focus` would paint that
|
|
941
|
+
* as a highlight the mouse user never asked for, reading as a preselected
|
|
942
|
+
* item. `:focus-visible` excludes programmatic focus that follows a click while
|
|
943
|
+
* still matching keyboard navigation.
|
|
944
|
+
*
|
|
945
|
+
* Shared as a constant because Emotion merges styles by **exact selector
|
|
946
|
+
* text**: a component's base rule and any theme variant restyling that rule
|
|
947
|
+
* must key both with this value, or the variant silently fails to override and
|
|
948
|
+
* reintroduces the phantom highlight.
|
|
949
|
+
*/
|
|
950
|
+
var HOVER_OR_KEYBOARD_FOCUS = "&:hover, &:focus-visible";
|
|
951
|
+
//#endregion
|
|
902
952
|
//#region src/concepts/theme/themes/base.theme.ts
|
|
903
953
|
var BaseTypography = {
|
|
904
954
|
"display-large": {
|
|
@@ -1096,6 +1146,25 @@ var buildBorders = (c) => ({
|
|
|
1096
1146
|
dark: `1px solid ${c["on-background"]}`,
|
|
1097
1147
|
dotted: `1px dotted ${c["on-background"]}`
|
|
1098
1148
|
});
|
|
1149
|
+
/**
|
|
1150
|
+
* One tag colour role across all three tones. `soft` is the resting look (the
|
|
1151
|
+
* container pair), `solid` fills with the role itself, `outline` keeps the
|
|
1152
|
+
* surface and draws the edge — so every role stays consistent and a theme can
|
|
1153
|
+
* still override any single cell.
|
|
1154
|
+
*/
|
|
1155
|
+
var tagVariant = (c, role) => ({ [role]: {
|
|
1156
|
+
backgroundColor: c[`${role}-container`],
|
|
1157
|
+
color: c[`on-${role}-container`],
|
|
1158
|
+
"&.tone-solid": {
|
|
1159
|
+
backgroundColor: c[role],
|
|
1160
|
+
color: c[`on-${role}`]
|
|
1161
|
+
},
|
|
1162
|
+
"&.tone-outline": {
|
|
1163
|
+
backgroundColor: "transparent",
|
|
1164
|
+
color: c[role],
|
|
1165
|
+
borderColor: c[`on-${role}-container-border`] ?? c[role]
|
|
1166
|
+
}
|
|
1167
|
+
} });
|
|
1099
1168
|
var buildComponents = (c) => ({
|
|
1100
1169
|
alert: { options: {
|
|
1101
1170
|
topPosition: 40,
|
|
@@ -1241,6 +1310,38 @@ var buildComponents = (c) => ({
|
|
|
1241
1310
|
color: "primary-surface",
|
|
1242
1311
|
shadow: "menu"
|
|
1243
1312
|
} },
|
|
1313
|
+
menu: { options: {
|
|
1314
|
+
minWidth: 184,
|
|
1315
|
+
color: void 0,
|
|
1316
|
+
border: void 0,
|
|
1317
|
+
borderRadius: void 0,
|
|
1318
|
+
shadow: void 0,
|
|
1319
|
+
paddingVertical: "xs",
|
|
1320
|
+
paddingHorizontal: "none",
|
|
1321
|
+
dividerBorder: "light",
|
|
1322
|
+
dividerSpacing: "xs"
|
|
1323
|
+
} },
|
|
1324
|
+
menuItem: {
|
|
1325
|
+
options: {
|
|
1326
|
+
height: 38,
|
|
1327
|
+
paddingHorizontal: "md",
|
|
1328
|
+
gap: "md",
|
|
1329
|
+
borderRadius: "none",
|
|
1330
|
+
typeface: "label",
|
|
1331
|
+
textColor: void 0,
|
|
1332
|
+
hoverColor: "primary-container",
|
|
1333
|
+
activeSymbol: "check",
|
|
1334
|
+
transitionSpeed: .35
|
|
1335
|
+
},
|
|
1336
|
+
variants: { warn: {
|
|
1337
|
+
color: c.warn,
|
|
1338
|
+
[HOVER_OR_KEYBOARD_FOCUS]: {
|
|
1339
|
+
backgroundColor: c["warn-container"],
|
|
1340
|
+
color: c["on-warn-container"]
|
|
1341
|
+
}
|
|
1342
|
+
} }
|
|
1343
|
+
},
|
|
1344
|
+
expand: { options: { transitionSpeed: .35 } },
|
|
1244
1345
|
footer: { options: {
|
|
1245
1346
|
height: 52,
|
|
1246
1347
|
color: "primary",
|
|
@@ -1288,6 +1389,79 @@ var buildComponents = (c) => ({
|
|
|
1288
1389
|
focusOutlineOffset: 2
|
|
1289
1390
|
} },
|
|
1290
1391
|
badge: { options: { borderRadius: "xxs" } },
|
|
1392
|
+
tag: {
|
|
1393
|
+
options: {
|
|
1394
|
+
borderRadius: "max",
|
|
1395
|
+
typeface: "tag",
|
|
1396
|
+
gap: "xs",
|
|
1397
|
+
removeIcon: "close",
|
|
1398
|
+
selectedIcon: "check"
|
|
1399
|
+
},
|
|
1400
|
+
fixed: {
|
|
1401
|
+
display: "inline-flex",
|
|
1402
|
+
alignItems: "center",
|
|
1403
|
+
maxWidth: "100%",
|
|
1404
|
+
border: "1px solid transparent",
|
|
1405
|
+
transition: "background-color .2s ease, color .2s ease"
|
|
1406
|
+
},
|
|
1407
|
+
variants: {
|
|
1408
|
+
...tagVariant(c, "primary"),
|
|
1409
|
+
...tagVariant(c, "secondary"),
|
|
1410
|
+
...tagVariant(c, "tertiary"),
|
|
1411
|
+
...tagVariant(c, "warn"),
|
|
1412
|
+
...tagVariant(c, "success"),
|
|
1413
|
+
ghost: {
|
|
1414
|
+
backgroundColor: "transparent",
|
|
1415
|
+
color: c["on-background"],
|
|
1416
|
+
"&.tone-solid": {
|
|
1417
|
+
backgroundColor: c["surface-variant"],
|
|
1418
|
+
color: c["on-surface-variant"]
|
|
1419
|
+
},
|
|
1420
|
+
"&.tone-outline": {
|
|
1421
|
+
backgroundColor: "transparent",
|
|
1422
|
+
borderColor: c.outline
|
|
1423
|
+
}
|
|
1424
|
+
},
|
|
1425
|
+
disabled: {
|
|
1426
|
+
backgroundColor: c["disabled-container"],
|
|
1427
|
+
color: c["on-disabled"],
|
|
1428
|
+
"&.tone-solid": {
|
|
1429
|
+
backgroundColor: c.disabled,
|
|
1430
|
+
color: c["on-disabled"]
|
|
1431
|
+
},
|
|
1432
|
+
"&.tone-outline": {
|
|
1433
|
+
backgroundColor: "transparent",
|
|
1434
|
+
borderColor: c.disabled
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
},
|
|
1438
|
+
sizes: {
|
|
1439
|
+
sm: {
|
|
1440
|
+
height: 20,
|
|
1441
|
+
fontSize: 12,
|
|
1442
|
+
padding: "0 8px"
|
|
1443
|
+
},
|
|
1444
|
+
md: {
|
|
1445
|
+
height: 24,
|
|
1446
|
+
fontSize: 13,
|
|
1447
|
+
padding: "0 10px"
|
|
1448
|
+
},
|
|
1449
|
+
lg: {
|
|
1450
|
+
height: 32,
|
|
1451
|
+
fontSize: 15,
|
|
1452
|
+
padding: "0 12px"
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
},
|
|
1456
|
+
tagInput: { options: {
|
|
1457
|
+
chipGap: "xs",
|
|
1458
|
+
chipSize: "md",
|
|
1459
|
+
minInputWidth: "12ch",
|
|
1460
|
+
listColor: "primary-surface",
|
|
1461
|
+
listShadow: "menu",
|
|
1462
|
+
listBorderRadius: "xs",
|
|
1463
|
+
maxSuggestions: 8
|
|
1464
|
+
} },
|
|
1291
1465
|
button: {
|
|
1292
1466
|
options: {
|
|
1293
1467
|
borderRadius: "max",
|
|
@@ -1591,11 +1765,11 @@ var buildComponents = (c) => ({
|
|
|
1591
1765
|
typeface: "label"
|
|
1592
1766
|
} }
|
|
1593
1767
|
});
|
|
1594
|
-
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1768
|
+
var isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1595
1769
|
var deepMerge = (base, override) => {
|
|
1596
1770
|
if (!override) return base;
|
|
1597
1771
|
const out = { ...base };
|
|
1598
|
-
for (const [key, value] of Object.entries(override)) out[key] = isRecord(value) && isRecord(out[key]) ? deepMerge(out[key], value) : value;
|
|
1772
|
+
for (const [key, value] of Object.entries(override)) out[key] = isRecord$1(value) && isRecord$1(out[key]) ? deepMerge(out[key], value) : value;
|
|
1599
1773
|
return out;
|
|
1600
1774
|
};
|
|
1601
1775
|
var createTheme = ({ id, name, colors, icons = {}, radii = BaseRadii, shadows = BaseShadows, borders, components }) => ({
|
|
@@ -1615,6 +1789,31 @@ var createTheme = ({ id, name, colors, icons = {}, radii = BaseRadii, shadows =
|
|
|
1615
1789
|
components: deepMerge(buildComponents(colors), components)
|
|
1616
1790
|
});
|
|
1617
1791
|
/**
|
|
1792
|
+
* Re-attach the built-in icon set to a theme that traveled without it.
|
|
1793
|
+
*
|
|
1794
|
+
* A serialized theme is ~50 KB, ~71% of which is {@link BaseIcons} — bytes
|
|
1795
|
+
* every uni-core consumer already ships. Transports (MCP tool results, theme
|
|
1796
|
+
* JSON over HTTP) send the {@link dehydrateTheme} form and the receiver
|
|
1797
|
+
* hydrates, applying exactly the `{...BaseIcons, ...icons}` contract
|
|
1798
|
+
* {@link createTheme} applies at construction: the theme's own icons win,
|
|
1799
|
+
* built-ins fill the rest.
|
|
1800
|
+
*/
|
|
1801
|
+
var hydrateTheme = (theme) => ({
|
|
1802
|
+
...theme,
|
|
1803
|
+
icons: {
|
|
1804
|
+
...BaseIcons,
|
|
1805
|
+
...theme.icons
|
|
1806
|
+
}
|
|
1807
|
+
});
|
|
1808
|
+
/**
|
|
1809
|
+
* The wire form of a theme: icons identical to the built-in set are dropped,
|
|
1810
|
+
* so only genuine overrides travel. Reverse with {@link hydrateTheme}.
|
|
1811
|
+
*/
|
|
1812
|
+
var dehydrateTheme = (theme) => ({
|
|
1813
|
+
...theme,
|
|
1814
|
+
icons: Object.fromEntries(Object.entries(theme.icons).filter(([name, uri]) => BaseIcons[name] !== uri))
|
|
1815
|
+
});
|
|
1816
|
+
/**
|
|
1618
1817
|
* Build a full {@link UniTheme} straight from a {@link PaletteConfig} — the
|
|
1619
1818
|
* one-call path a theme builder uses to turn a brand color (or a seed +
|
|
1620
1819
|
* scheme + category) into a complete, ready-to-apply theme.
|
|
@@ -1762,11 +1961,17 @@ var generateThemes = (input) => {
|
|
|
1762
1961
|
};
|
|
1763
1962
|
};
|
|
1764
1963
|
/**
|
|
1964
|
+
* One-line human summary of a {@link ContrastReport}. Shared so the generated
|
|
1965
|
+
* theme file's header and the MCP's theme tools report identical wording.
|
|
1966
|
+
*/
|
|
1967
|
+
var summarizeContrast = (report) => `${report.checks.length} contrast pairs checked · worst ${report.worstRatio}:1 · ${report.pass ? "all AA" : `${report.checks.filter((check) => !check.pass).length} failing`}`;
|
|
1968
|
+
/**
|
|
1765
1969
|
* Convenience wrapper: {@link generateThemes} piped through `createTheme()`,
|
|
1766
|
-
* returning a registration-ready light/dark {@link UniTheme} pair
|
|
1970
|
+
* returning a registration-ready light/dark {@link UniTheme} pair alongside
|
|
1971
|
+
* the contrast report the colors were audited against.
|
|
1767
1972
|
*/
|
|
1768
1973
|
var generateUniThemes = (input) => {
|
|
1769
|
-
const { lightColors, darkColors, radii, lightShadows, darkShadows } = generateThemes(input);
|
|
1974
|
+
const { lightColors, darkColors, radii, lightShadows, darkShadows, report } = generateThemes(input);
|
|
1770
1975
|
const name = input.name ?? "Brand";
|
|
1771
1976
|
const id = name.replace(/\W+/g, "") || "Brand";
|
|
1772
1977
|
return {
|
|
@@ -1783,7 +1988,8 @@ var generateUniThemes = (input) => {
|
|
|
1783
1988
|
colors: darkColors,
|
|
1784
1989
|
radii,
|
|
1785
1990
|
shadows: darkShadows
|
|
1786
|
-
})
|
|
1991
|
+
}),
|
|
1992
|
+
report
|
|
1787
1993
|
};
|
|
1788
1994
|
};
|
|
1789
1995
|
//#endregion
|
|
@@ -1842,7 +2048,7 @@ var emitThemeFile = (input) => {
|
|
|
1842
2048
|
input.shape && `--shape=${input.shape}`,
|
|
1843
2049
|
!darkMode && "--dark-mode=false"
|
|
1844
2050
|
].filter(Boolean).join(" ");
|
|
1845
|
-
const reportSummary =
|
|
2051
|
+
const reportSummary = summarizeContrast(report);
|
|
1846
2052
|
const modes = [{
|
|
1847
2053
|
exportName: `${id}Light`,
|
|
1848
2054
|
displayName: `${name} Light`,
|
|
@@ -2135,6 +2341,235 @@ var UniThemes = {
|
|
|
2135
2341
|
};
|
|
2136
2342
|
var DefaultThemeId = Object.keys(UniThemes)[0];
|
|
2137
2343
|
//#endregion
|
|
2344
|
+
//#region src/concepts/theme/theme.validation.ts
|
|
2345
|
+
/**
|
|
2346
|
+
* Color tokens components cannot render without: the default variant pairs,
|
|
2347
|
+
* the base surfaces, and the disabled/warn states. Themes may define any
|
|
2348
|
+
* further tokens; these must exist.
|
|
2349
|
+
*/
|
|
2350
|
+
var REQUIRED_COLOR_TOKENS = [
|
|
2351
|
+
"primary",
|
|
2352
|
+
"on-primary",
|
|
2353
|
+
"primary-container",
|
|
2354
|
+
"on-primary-container",
|
|
2355
|
+
"primary-surface",
|
|
2356
|
+
"on-primary-surface",
|
|
2357
|
+
"surface",
|
|
2358
|
+
"on-surface",
|
|
2359
|
+
"background",
|
|
2360
|
+
"on-background",
|
|
2361
|
+
"warn",
|
|
2362
|
+
"on-warn",
|
|
2363
|
+
"disabled",
|
|
2364
|
+
"on-disabled",
|
|
2365
|
+
"outline",
|
|
2366
|
+
"transparent"
|
|
2367
|
+
];
|
|
2368
|
+
/** The canonical type scale; every theme must style each role. */
|
|
2369
|
+
var REQUIRED_TEXT_ROLES = [
|
|
2370
|
+
"display-large",
|
|
2371
|
+
"display-medium",
|
|
2372
|
+
"display-small",
|
|
2373
|
+
"headline-large",
|
|
2374
|
+
"headline-medium",
|
|
2375
|
+
"headline-small",
|
|
2376
|
+
"title-large",
|
|
2377
|
+
"title-medium",
|
|
2378
|
+
"title-small",
|
|
2379
|
+
"body-1-long",
|
|
2380
|
+
"body-1-short",
|
|
2381
|
+
"body-2-long",
|
|
2382
|
+
"body-2-short",
|
|
2383
|
+
"subtitle-1",
|
|
2384
|
+
"subtitle-2",
|
|
2385
|
+
"label",
|
|
2386
|
+
"button",
|
|
2387
|
+
"caption",
|
|
2388
|
+
"overline",
|
|
2389
|
+
"paragraph",
|
|
2390
|
+
"quote",
|
|
2391
|
+
"note"
|
|
2392
|
+
];
|
|
2393
|
+
var REQUIRED_SPACING_SIZES = [
|
|
2394
|
+
"none",
|
|
2395
|
+
"xxs",
|
|
2396
|
+
"xs",
|
|
2397
|
+
"sm",
|
|
2398
|
+
"md",
|
|
2399
|
+
"lg",
|
|
2400
|
+
"xl"
|
|
2401
|
+
];
|
|
2402
|
+
var REQUIRED_RADII_SIZES = [
|
|
2403
|
+
"none",
|
|
2404
|
+
"xxs",
|
|
2405
|
+
"xs",
|
|
2406
|
+
"sm",
|
|
2407
|
+
"md",
|
|
2408
|
+
"lg",
|
|
2409
|
+
"max"
|
|
2410
|
+
];
|
|
2411
|
+
var REQUIRED_SHADOWS = [
|
|
2412
|
+
"raised",
|
|
2413
|
+
"menu",
|
|
2414
|
+
"dialog",
|
|
2415
|
+
"warn"
|
|
2416
|
+
];
|
|
2417
|
+
var REQUIRED_THICKNESSES = [
|
|
2418
|
+
"thin",
|
|
2419
|
+
"standard",
|
|
2420
|
+
"thick"
|
|
2421
|
+
];
|
|
2422
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2423
|
+
var isCssValue = (value) => typeof value === "string" || typeof value === "number";
|
|
2424
|
+
/** Nested style expression: string/number leaves, objects for selectors. */
|
|
2425
|
+
var checkStyleExpression = (value, path, issues) => {
|
|
2426
|
+
if (!isRecord(value)) {
|
|
2427
|
+
issues.push({
|
|
2428
|
+
path,
|
|
2429
|
+
message: "must be a style object"
|
|
2430
|
+
});
|
|
2431
|
+
return;
|
|
2432
|
+
}
|
|
2433
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2434
|
+
if (entry === void 0 || isCssValue(entry)) continue;
|
|
2435
|
+
if (isRecord(entry)) checkStyleExpression(entry, `${path}.${key}`, issues);
|
|
2436
|
+
else issues.push({
|
|
2437
|
+
path: `${path}.${key}`,
|
|
2438
|
+
message: "must be a string, number, or nested style object"
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
};
|
|
2442
|
+
var checkStringRecord = (value, path, issues, required = [], allowNumbers = false) => {
|
|
2443
|
+
if (!isRecord(value)) {
|
|
2444
|
+
issues.push({
|
|
2445
|
+
path,
|
|
2446
|
+
message: "must be an object"
|
|
2447
|
+
});
|
|
2448
|
+
return;
|
|
2449
|
+
}
|
|
2450
|
+
for (const key of required) if (!(key in value)) issues.push({
|
|
2451
|
+
path: `${path}.${key}`,
|
|
2452
|
+
message: "is required"
|
|
2453
|
+
});
|
|
2454
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2455
|
+
if (entry === void 0) continue;
|
|
2456
|
+
if (!(allowNumbers ? isCssValue(entry) : typeof entry === "string")) issues.push({
|
|
2457
|
+
path: `${path}.${key}`,
|
|
2458
|
+
message: allowNumbers ? "must be a string or number" : "must be a string"
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
};
|
|
2462
|
+
var checkTypography = (value, issues) => {
|
|
2463
|
+
if (!isRecord(value)) {
|
|
2464
|
+
issues.push({
|
|
2465
|
+
path: "typography",
|
|
2466
|
+
message: "must be an object"
|
|
2467
|
+
});
|
|
2468
|
+
return;
|
|
2469
|
+
}
|
|
2470
|
+
for (const role of REQUIRED_TEXT_ROLES) if (!(role in value)) issues.push({
|
|
2471
|
+
path: `typography.${role}`,
|
|
2472
|
+
message: "is required"
|
|
2473
|
+
});
|
|
2474
|
+
for (const [role, style] of Object.entries(value)) {
|
|
2475
|
+
if (!isRecord(style)) {
|
|
2476
|
+
issues.push({
|
|
2477
|
+
path: `typography.${role}`,
|
|
2478
|
+
message: "must be a TextStyle object"
|
|
2479
|
+
});
|
|
2480
|
+
continue;
|
|
2481
|
+
}
|
|
2482
|
+
if (typeof style["fontFamily"] !== "string") issues.push({
|
|
2483
|
+
path: `typography.${role}.fontFamily`,
|
|
2484
|
+
message: "must be a string"
|
|
2485
|
+
});
|
|
2486
|
+
for (const field of ["fontSize", "lineHeight"]) if (!isCssValue(style[field])) issues.push({
|
|
2487
|
+
path: `typography.${role}.${field}`,
|
|
2488
|
+
message: "must be a string or number"
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
};
|
|
2492
|
+
var checkComponents = (value, issues) => {
|
|
2493
|
+
if (!isRecord(value)) {
|
|
2494
|
+
issues.push({
|
|
2495
|
+
path: "components",
|
|
2496
|
+
message: "must be an object"
|
|
2497
|
+
});
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2500
|
+
for (const [name, entry] of Object.entries(value)) {
|
|
2501
|
+
if (entry === void 0) continue;
|
|
2502
|
+
if (!isRecord(entry)) {
|
|
2503
|
+
issues.push({
|
|
2504
|
+
path: `components.${name}`,
|
|
2505
|
+
message: "must be a component theme object"
|
|
2506
|
+
});
|
|
2507
|
+
continue;
|
|
2508
|
+
}
|
|
2509
|
+
if (entry["fixed"] !== void 0) checkStyleExpression(entry["fixed"], `components.${name}.fixed`, issues);
|
|
2510
|
+
for (const section of ["variants", "sizes"]) {
|
|
2511
|
+
const styles = entry[section];
|
|
2512
|
+
if (styles === void 0) continue;
|
|
2513
|
+
if (!isRecord(styles)) {
|
|
2514
|
+
issues.push({
|
|
2515
|
+
path: `components.${name}.${section}`,
|
|
2516
|
+
message: "must be an object"
|
|
2517
|
+
});
|
|
2518
|
+
continue;
|
|
2519
|
+
}
|
|
2520
|
+
for (const [key, style] of Object.entries(styles)) if (style !== void 0) checkStyleExpression(style, `components.${name}.${section}.${key}`, issues);
|
|
2521
|
+
}
|
|
2522
|
+
if (entry["options"] !== void 0 && !isRecord(entry["options"])) issues.push({
|
|
2523
|
+
path: `components.${name}.options`,
|
|
2524
|
+
message: "must be an object"
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
};
|
|
2528
|
+
/**
|
|
2529
|
+
* Validate a candidate theme, collecting every structural issue. Never
|
|
2530
|
+
* throws; a failed result carries the complete repair list.
|
|
2531
|
+
*/
|
|
2532
|
+
var parseTheme = (input) => {
|
|
2533
|
+
const issues = [];
|
|
2534
|
+
if (!isRecord(input)) return {
|
|
2535
|
+
success: false,
|
|
2536
|
+
issues: [{
|
|
2537
|
+
path: "",
|
|
2538
|
+
message: "theme must be an object"
|
|
2539
|
+
}]
|
|
2540
|
+
};
|
|
2541
|
+
for (const field of ["id", "name"]) if (typeof input[field] !== "string" || input[field] === "") issues.push({
|
|
2542
|
+
path: field,
|
|
2543
|
+
message: "must be a non-empty string"
|
|
2544
|
+
});
|
|
2545
|
+
checkStringRecord(input["colors"], "colors", issues, REQUIRED_COLOR_TOKENS);
|
|
2546
|
+
checkTypography(input["typography"], issues);
|
|
2547
|
+
checkStringRecord(input["borders"], "borders", issues);
|
|
2548
|
+
checkStringRecord(input["radii"], "radii", issues, REQUIRED_RADII_SIZES);
|
|
2549
|
+
checkStringRecord(input["shadows"], "shadows", issues, REQUIRED_SHADOWS);
|
|
2550
|
+
checkStringRecord(input["spacing"], "spacing", issues, REQUIRED_SPACING_SIZES, true);
|
|
2551
|
+
checkStringRecord(input["thicknesses"], "thicknesses", issues, REQUIRED_THICKNESSES, true);
|
|
2552
|
+
checkStringRecord(input["icons"], "icons", issues);
|
|
2553
|
+
checkComponents(input["components"], issues);
|
|
2554
|
+
return issues.length === 0 ? {
|
|
2555
|
+
success: true,
|
|
2556
|
+
theme: input,
|
|
2557
|
+
issues: []
|
|
2558
|
+
} : {
|
|
2559
|
+
success: false,
|
|
2560
|
+
issues
|
|
2561
|
+
};
|
|
2562
|
+
};
|
|
2563
|
+
/** Format issues for logs and error messages. */
|
|
2564
|
+
var formatThemeIssues = (issues) => issues.map(({ path, message }) => path ? `${path} ${message}` : message).join("; ");
|
|
2565
|
+
/** Validate or throw, with every reason in the error message. */
|
|
2566
|
+
var assertTheme = (input) => {
|
|
2567
|
+
const result = parseTheme(input);
|
|
2568
|
+
if (!result.success) throw new Error(`Invalid UniTheme: ${formatThemeIssues(result.issues)}`);
|
|
2569
|
+
return result.theme;
|
|
2570
|
+
};
|
|
2571
|
+
var isUniTheme = (input) => parseTheme(input).success;
|
|
2572
|
+
//#endregion
|
|
2138
2573
|
//#region src/concepts/typography/typography.records.ts
|
|
2139
2574
|
var FontWeightMap = {
|
|
2140
2575
|
thin: 100,
|
|
@@ -2151,6 +2586,7 @@ var FontWeightMap = {
|
|
|
2151
2586
|
//#endregion
|
|
2152
2587
|
//#region src/concepts/typography/typeface.helpers.ts
|
|
2153
2588
|
var px = (value) => typeof value === "number" ? `${value}px` : value;
|
|
2589
|
+
var weight = (value) => typeof value === "number" ? value : FontWeightMap[value];
|
|
2154
2590
|
/**
|
|
2155
2591
|
* Converts a TextStyle (numeric, design-token oriented) into a
|
|
2156
2592
|
* TypeFaceDefinition (CSS-ready) so a theme's `typefaces` map can be
|
|
@@ -2162,7 +2598,7 @@ var toTypeface = (style) => ({
|
|
|
2162
2598
|
lineHeight: px(style.lineHeight),
|
|
2163
2599
|
...style.letterSpacing !== void 0 && { letterSpacing: px(style.letterSpacing) },
|
|
2164
2600
|
...style.textTransform === "uppercase" && { textTransform: "uppercase" },
|
|
2165
|
-
...style.fontWeight !== void 0 && { fontWeight: style.fontWeight },
|
|
2601
|
+
...style.fontWeight !== void 0 && { fontWeight: weight(style.fontWeight) },
|
|
2166
2602
|
...style.fontStyle && { fontStyle: style.fontStyle }
|
|
2167
2603
|
});
|
|
2168
2604
|
var toTypefaces = (typography) => Object.fromEntries(Object.entries(typography).map(([role, style]) => [role, toTypeface(style)]));
|
|
@@ -2192,11 +2628,22 @@ exports.CategoryLightness = CategoryLightness;
|
|
|
2192
2628
|
exports.CategorySaturation = CategorySaturation;
|
|
2193
2629
|
exports.DarkTheme = DarkTheme;
|
|
2194
2630
|
exports.DefaultThemeId = DefaultThemeId;
|
|
2631
|
+
exports.EXPAND_DEFAULT_SPEED = EXPAND_DEFAULT_SPEED;
|
|
2632
|
+
exports.EXPAND_MAX_DURATION = EXPAND_MAX_DURATION;
|
|
2633
|
+
exports.EXPAND_MIN_DURATION = EXPAND_MIN_DURATION;
|
|
2634
|
+
exports.EXPAND_REFERENCE_HEIGHT = EXPAND_REFERENCE_HEIGHT;
|
|
2195
2635
|
exports.FontWeightMap = FontWeightMap;
|
|
2636
|
+
exports.HOVER_OR_KEYBOARD_FOCUS = HOVER_OR_KEYBOARD_FOCUS;
|
|
2196
2637
|
exports.HSLAToString = HSLAToString;
|
|
2197
2638
|
exports.HSLToHex = HSLToHex;
|
|
2198
2639
|
exports.HSLToRGB = HSLToRGB;
|
|
2199
2640
|
exports.LightTheme = LightTheme;
|
|
2641
|
+
exports.REQUIRED_COLOR_TOKENS = REQUIRED_COLOR_TOKENS;
|
|
2642
|
+
exports.REQUIRED_RADII_SIZES = REQUIRED_RADII_SIZES;
|
|
2643
|
+
exports.REQUIRED_SHADOWS = REQUIRED_SHADOWS;
|
|
2644
|
+
exports.REQUIRED_SPACING_SIZES = REQUIRED_SPACING_SIZES;
|
|
2645
|
+
exports.REQUIRED_TEXT_ROLES = REQUIRED_TEXT_ROLES;
|
|
2646
|
+
exports.REQUIRED_THICKNESSES = REQUIRED_THICKNESSES;
|
|
2200
2647
|
exports.RGBToHSL = RGBToHSL;
|
|
2201
2648
|
exports.RGBToString = RGBToString;
|
|
2202
2649
|
exports.RoleHues = RoleHues;
|
|
@@ -2205,6 +2652,7 @@ exports.ShadowMap = ShadowMap;
|
|
|
2205
2652
|
exports.ShapeRadii = ShapeRadii;
|
|
2206
2653
|
exports.UniThemes = UniThemes;
|
|
2207
2654
|
exports.Z_INDEX = Z_INDEX;
|
|
2655
|
+
exports.assertTheme = assertTheme;
|
|
2208
2656
|
exports.classifyScheme = classifyScheme;
|
|
2209
2657
|
exports.collapseFadeOut = collapseFadeOut;
|
|
2210
2658
|
exports.contrastRatio = contrastRatio;
|
|
@@ -2213,11 +2661,14 @@ exports.createThemeFromPalette = createThemeFromPalette;
|
|
|
2213
2661
|
exports.cycle = cycle;
|
|
2214
2662
|
exports.darkColors = darkColors;
|
|
2215
2663
|
exports.debounce = debounce;
|
|
2664
|
+
exports.dehydrateTheme = dehydrateTheme;
|
|
2216
2665
|
exports.emitDtcgTokens = emitDtcgTokens;
|
|
2217
2666
|
exports.emitThemeFile = emitThemeFile;
|
|
2667
|
+
exports.expandDuration = expandDuration;
|
|
2218
2668
|
exports.expandFadeIn = expandFadeIn;
|
|
2219
2669
|
exports.fadeIn = fadeIn;
|
|
2220
2670
|
exports.fadeOut = fadeOut;
|
|
2671
|
+
exports.formatThemeIssues = formatThemeIssues;
|
|
2221
2672
|
exports.generateColors = generateColors;
|
|
2222
2673
|
exports.generatePalette = generatePalette;
|
|
2223
2674
|
exports.generateShadows = generateShadows;
|
|
@@ -2234,14 +2685,18 @@ exports.gradient = gradient;
|
|
|
2234
2685
|
exports.hexToHSL = hexToHSL;
|
|
2235
2686
|
exports.hexToOklch = hexToOklch;
|
|
2236
2687
|
exports.hexToRgb = hexToRgb;
|
|
2688
|
+
exports.hydrateTheme = hydrateTheme;
|
|
2237
2689
|
exports.inferCategory = inferCategory;
|
|
2690
|
+
exports.isUniTheme = isUniTheme;
|
|
2238
2691
|
exports.lightColors = lightColors;
|
|
2239
2692
|
exports.oklchToHex = oklchToHex;
|
|
2693
|
+
exports.parseTheme = parseTheme;
|
|
2240
2694
|
exports.randomRangeValue = randomRangeValue;
|
|
2241
2695
|
exports.relativeLuminance = relativeLuminance;
|
|
2242
2696
|
exports.removeInputPlatformStyling = removeInputPlatformStyling;
|
|
2243
2697
|
exports.rgbToHex = rgbToHex;
|
|
2244
2698
|
exports.schemeHues = schemeHues;
|
|
2699
|
+
exports.summarizeContrast = summarizeContrast;
|
|
2245
2700
|
exports.svgToIconUri = svgToIconUri;
|
|
2246
2701
|
exports.toTypeface = toTypeface;
|
|
2247
2702
|
exports.toTypefaces = toTypefaces;
|