@uni-design-system/uni-core 7.3.0 → 8.1.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 +453 -11
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +440 -12
- package/dist/esm/index.js.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.model.d.ts +2 -1
- package/dist/types/concepts/theme/theme.model.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 +24 -2
- package/dist/types/concepts/theme/themes/base.theme.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -930,6 +930,25 @@ var BaseIcons = {
|
|
|
930
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 "
|
|
931
931
|
};
|
|
932
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
|
|
933
952
|
//#region src/concepts/theme/themes/base.theme.ts
|
|
934
953
|
var BaseTypography = {
|
|
935
954
|
"display-large": {
|
|
@@ -1127,6 +1146,25 @@ var buildBorders = (c) => ({
|
|
|
1127
1146
|
dark: `1px solid ${c["on-background"]}`,
|
|
1128
1147
|
dotted: `1px dotted ${c["on-background"]}`
|
|
1129
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
|
+
} });
|
|
1130
1168
|
var buildComponents = (c) => ({
|
|
1131
1169
|
alert: { options: {
|
|
1132
1170
|
topPosition: 40,
|
|
@@ -1228,12 +1266,15 @@ var buildComponents = (c) => ({
|
|
|
1228
1266
|
} },
|
|
1229
1267
|
checkbox: { options: {
|
|
1230
1268
|
size: 20,
|
|
1231
|
-
boxColor: "surface"
|
|
1269
|
+
boxColor: "surface",
|
|
1270
|
+
borderRadius: 2,
|
|
1271
|
+
focusRingGap: 2
|
|
1232
1272
|
} },
|
|
1233
1273
|
radio: { options: {
|
|
1234
1274
|
size: 20,
|
|
1235
1275
|
ringColor: "outline",
|
|
1236
|
-
fillColor: "surface"
|
|
1276
|
+
fillColor: "surface",
|
|
1277
|
+
transitionSpeed: .3
|
|
1237
1278
|
} },
|
|
1238
1279
|
dialog: { options: {
|
|
1239
1280
|
borderRadius: "lg",
|
|
@@ -1297,7 +1338,7 @@ var buildComponents = (c) => ({
|
|
|
1297
1338
|
},
|
|
1298
1339
|
variants: { warn: {
|
|
1299
1340
|
color: c.warn,
|
|
1300
|
-
|
|
1341
|
+
[HOVER_OR_KEYBOARD_FOCUS]: {
|
|
1301
1342
|
backgroundColor: c["warn-container"],
|
|
1302
1343
|
color: c["on-warn-container"]
|
|
1303
1344
|
}
|
|
@@ -1351,6 +1392,129 @@ var buildComponents = (c) => ({
|
|
|
1351
1392
|
focusOutlineOffset: 2
|
|
1352
1393
|
} },
|
|
1353
1394
|
badge: { options: { borderRadius: "xxs" } },
|
|
1395
|
+
tag: {
|
|
1396
|
+
options: {
|
|
1397
|
+
borderRadius: "max",
|
|
1398
|
+
typeface: "tag",
|
|
1399
|
+
gap: "xs",
|
|
1400
|
+
removeIcon: "close",
|
|
1401
|
+
selectedIcon: "check"
|
|
1402
|
+
},
|
|
1403
|
+
fixed: {
|
|
1404
|
+
display: "inline-flex",
|
|
1405
|
+
alignItems: "center",
|
|
1406
|
+
maxWidth: "100%",
|
|
1407
|
+
border: "1px solid transparent",
|
|
1408
|
+
transition: "background-color .2s ease, color .2s ease"
|
|
1409
|
+
},
|
|
1410
|
+
variants: {
|
|
1411
|
+
...tagVariant(c, "primary"),
|
|
1412
|
+
...tagVariant(c, "secondary"),
|
|
1413
|
+
...tagVariant(c, "tertiary"),
|
|
1414
|
+
...tagVariant(c, "warn"),
|
|
1415
|
+
...tagVariant(c, "success"),
|
|
1416
|
+
ghost: {
|
|
1417
|
+
backgroundColor: "transparent",
|
|
1418
|
+
color: c["on-background"],
|
|
1419
|
+
"&.tone-solid": {
|
|
1420
|
+
backgroundColor: c["surface-variant"],
|
|
1421
|
+
color: c["on-surface-variant"]
|
|
1422
|
+
},
|
|
1423
|
+
"&.tone-outline": {
|
|
1424
|
+
backgroundColor: "transparent",
|
|
1425
|
+
borderColor: c.outline
|
|
1426
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
disabled: {
|
|
1429
|
+
backgroundColor: c["disabled-container"],
|
|
1430
|
+
color: c["on-disabled"],
|
|
1431
|
+
"&.tone-solid": {
|
|
1432
|
+
backgroundColor: c.disabled,
|
|
1433
|
+
color: c["on-disabled"]
|
|
1434
|
+
},
|
|
1435
|
+
"&.tone-outline": {
|
|
1436
|
+
backgroundColor: "transparent",
|
|
1437
|
+
borderColor: c.disabled
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
},
|
|
1441
|
+
sizes: {
|
|
1442
|
+
sm: {
|
|
1443
|
+
height: 20,
|
|
1444
|
+
fontSize: 12,
|
|
1445
|
+
padding: "0 8px"
|
|
1446
|
+
},
|
|
1447
|
+
md: {
|
|
1448
|
+
height: 24,
|
|
1449
|
+
fontSize: 13,
|
|
1450
|
+
padding: "0 10px"
|
|
1451
|
+
},
|
|
1452
|
+
lg: {
|
|
1453
|
+
height: 32,
|
|
1454
|
+
fontSize: 15,
|
|
1455
|
+
padding: "0 12px"
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
},
|
|
1459
|
+
tagInput: { options: {
|
|
1460
|
+
chipGap: "xs",
|
|
1461
|
+
chipSize: "md",
|
|
1462
|
+
minInputWidth: "12ch",
|
|
1463
|
+
listColor: "primary-surface",
|
|
1464
|
+
listShadow: "menu",
|
|
1465
|
+
listBorderRadius: "xs",
|
|
1466
|
+
maxSuggestions: 8
|
|
1467
|
+
} },
|
|
1468
|
+
calendar: {
|
|
1469
|
+
options: {
|
|
1470
|
+
dayBorderRadius: "max",
|
|
1471
|
+
typeface: "label",
|
|
1472
|
+
gap: "xxs",
|
|
1473
|
+
navPrevSymbol: "chevron_left",
|
|
1474
|
+
navNextSymbol: "chevron_right",
|
|
1475
|
+
weekdayFormat: "short",
|
|
1476
|
+
showOutsideDays: false,
|
|
1477
|
+
todayStyle: "outline"
|
|
1478
|
+
},
|
|
1479
|
+
fixed: {
|
|
1480
|
+
display: "inline-block",
|
|
1481
|
+
userSelect: "none"
|
|
1482
|
+
},
|
|
1483
|
+
sizes: {
|
|
1484
|
+
sm: {
|
|
1485
|
+
width: 28,
|
|
1486
|
+
height: 28,
|
|
1487
|
+
fontSize: 12
|
|
1488
|
+
},
|
|
1489
|
+
md: {
|
|
1490
|
+
width: 34,
|
|
1491
|
+
height: 34,
|
|
1492
|
+
fontSize: 13
|
|
1493
|
+
},
|
|
1494
|
+
lg: {
|
|
1495
|
+
width: 40,
|
|
1496
|
+
height: 40,
|
|
1497
|
+
fontSize: 15
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
},
|
|
1501
|
+
dateInput: { options: {
|
|
1502
|
+
toggleSymbol: "calendar_month",
|
|
1503
|
+
popupShadow: "menu",
|
|
1504
|
+
popupBorderRadius: "xs",
|
|
1505
|
+
popupColor: "primary-surface"
|
|
1506
|
+
} },
|
|
1507
|
+
timeInput: { options: {
|
|
1508
|
+
toggleSymbol: "schedule",
|
|
1509
|
+
listColor: "primary-surface",
|
|
1510
|
+
listShadow: "menu",
|
|
1511
|
+
listBorderRadius: "xs",
|
|
1512
|
+
maxVisibleOptions: 7
|
|
1513
|
+
} },
|
|
1514
|
+
dateTimeInput: { options: {
|
|
1515
|
+
partGap: "sm",
|
|
1516
|
+
dividerColor: "outline"
|
|
1517
|
+
} },
|
|
1354
1518
|
button: {
|
|
1355
1519
|
options: {
|
|
1356
1520
|
borderRadius: "max",
|
|
@@ -1654,14 +1818,14 @@ var buildComponents = (c) => ({
|
|
|
1654
1818
|
typeface: "label"
|
|
1655
1819
|
} }
|
|
1656
1820
|
});
|
|
1657
|
-
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1821
|
+
var isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1658
1822
|
var deepMerge = (base, override) => {
|
|
1659
1823
|
if (!override) return base;
|
|
1660
1824
|
const out = { ...base };
|
|
1661
|
-
for (const [key, value] of Object.entries(override)) out[key] = isRecord(value) && isRecord(out[key]) ? deepMerge(out[key], value) : value;
|
|
1825
|
+
for (const [key, value] of Object.entries(override)) out[key] = isRecord$1(value) && isRecord$1(out[key]) ? deepMerge(out[key], value) : value;
|
|
1662
1826
|
return out;
|
|
1663
1827
|
};
|
|
1664
|
-
var createTheme = ({ id, name, colors, icons = {}, radii = BaseRadii, shadows = BaseShadows, borders, components }) => ({
|
|
1828
|
+
var createTheme = ({ id, name, colors, icons = {}, radii = BaseRadii, shadows = BaseShadows, borders, thicknesses, components }) => ({
|
|
1665
1829
|
id,
|
|
1666
1830
|
name,
|
|
1667
1831
|
colors,
|
|
@@ -1670,7 +1834,10 @@ var createTheme = ({ id, name, colors, icons = {}, radii = BaseRadii, shadows =
|
|
|
1670
1834
|
radii,
|
|
1671
1835
|
shadows,
|
|
1672
1836
|
spacing: BaseSpacing,
|
|
1673
|
-
thicknesses:
|
|
1837
|
+
thicknesses: {
|
|
1838
|
+
...BaseThicknesses,
|
|
1839
|
+
...thicknesses
|
|
1840
|
+
},
|
|
1674
1841
|
icons: {
|
|
1675
1842
|
...BaseIcons,
|
|
1676
1843
|
...icons
|
|
@@ -1678,6 +1845,31 @@ var createTheme = ({ id, name, colors, icons = {}, radii = BaseRadii, shadows =
|
|
|
1678
1845
|
components: deepMerge(buildComponents(colors), components)
|
|
1679
1846
|
});
|
|
1680
1847
|
/**
|
|
1848
|
+
* Re-attach the built-in icon set to a theme that traveled without it.
|
|
1849
|
+
*
|
|
1850
|
+
* A serialized theme is ~50 KB, ~71% of which is {@link BaseIcons} — bytes
|
|
1851
|
+
* every uni-core consumer already ships. Transports (MCP tool results, theme
|
|
1852
|
+
* JSON over HTTP) send the {@link dehydrateTheme} form and the receiver
|
|
1853
|
+
* hydrates, applying exactly the `{...BaseIcons, ...icons}` contract
|
|
1854
|
+
* {@link createTheme} applies at construction: the theme's own icons win,
|
|
1855
|
+
* built-ins fill the rest.
|
|
1856
|
+
*/
|
|
1857
|
+
var hydrateTheme = (theme) => ({
|
|
1858
|
+
...theme,
|
|
1859
|
+
icons: {
|
|
1860
|
+
...BaseIcons,
|
|
1861
|
+
...theme.icons
|
|
1862
|
+
}
|
|
1863
|
+
});
|
|
1864
|
+
/**
|
|
1865
|
+
* The wire form of a theme: icons identical to the built-in set are dropped,
|
|
1866
|
+
* so only genuine overrides travel. Reverse with {@link hydrateTheme}.
|
|
1867
|
+
*/
|
|
1868
|
+
var dehydrateTheme = (theme) => ({
|
|
1869
|
+
...theme,
|
|
1870
|
+
icons: Object.fromEntries(Object.entries(theme.icons).filter(([name, uri]) => BaseIcons[name] !== uri))
|
|
1871
|
+
});
|
|
1872
|
+
/**
|
|
1681
1873
|
* Build a full {@link UniTheme} straight from a {@link PaletteConfig} — the
|
|
1682
1874
|
* one-call path a theme builder uses to turn a brand color (or a seed +
|
|
1683
1875
|
* scheme + category) into a complete, ready-to-apply theme.
|
|
@@ -1825,11 +2017,17 @@ var generateThemes = (input) => {
|
|
|
1825
2017
|
};
|
|
1826
2018
|
};
|
|
1827
2019
|
/**
|
|
2020
|
+
* One-line human summary of a {@link ContrastReport}. Shared so the generated
|
|
2021
|
+
* theme file's header and the MCP's theme tools report identical wording.
|
|
2022
|
+
*/
|
|
2023
|
+
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`}`;
|
|
2024
|
+
/**
|
|
1828
2025
|
* Convenience wrapper: {@link generateThemes} piped through `createTheme()`,
|
|
1829
|
-
* returning a registration-ready light/dark {@link UniTheme} pair
|
|
2026
|
+
* returning a registration-ready light/dark {@link UniTheme} pair alongside
|
|
2027
|
+
* the contrast report the colors were audited against.
|
|
1830
2028
|
*/
|
|
1831
2029
|
var generateUniThemes = (input) => {
|
|
1832
|
-
const { lightColors, darkColors, radii, lightShadows, darkShadows } = generateThemes(input);
|
|
2030
|
+
const { lightColors, darkColors, radii, lightShadows, darkShadows, report } = generateThemes(input);
|
|
1833
2031
|
const name = input.name ?? "Brand";
|
|
1834
2032
|
const id = name.replace(/\W+/g, "") || "Brand";
|
|
1835
2033
|
return {
|
|
@@ -1846,7 +2044,8 @@ var generateUniThemes = (input) => {
|
|
|
1846
2044
|
colors: darkColors,
|
|
1847
2045
|
radii,
|
|
1848
2046
|
shadows: darkShadows
|
|
1849
|
-
})
|
|
2047
|
+
}),
|
|
2048
|
+
report
|
|
1850
2049
|
};
|
|
1851
2050
|
};
|
|
1852
2051
|
//#endregion
|
|
@@ -1905,7 +2104,7 @@ var emitThemeFile = (input) => {
|
|
|
1905
2104
|
input.shape && `--shape=${input.shape}`,
|
|
1906
2105
|
!darkMode && "--dark-mode=false"
|
|
1907
2106
|
].filter(Boolean).join(" ");
|
|
1908
|
-
const reportSummary =
|
|
2107
|
+
const reportSummary = summarizeContrast(report);
|
|
1909
2108
|
const modes = [{
|
|
1910
2109
|
exportName: `${id}Light`,
|
|
1911
2110
|
displayName: `${name} Light`,
|
|
@@ -2198,6 +2397,235 @@ var UniThemes = {
|
|
|
2198
2397
|
};
|
|
2199
2398
|
var DefaultThemeId = Object.keys(UniThemes)[0];
|
|
2200
2399
|
//#endregion
|
|
2400
|
+
//#region src/concepts/theme/theme.validation.ts
|
|
2401
|
+
/**
|
|
2402
|
+
* Color tokens components cannot render without: the default variant pairs,
|
|
2403
|
+
* the base surfaces, and the disabled/warn states. Themes may define any
|
|
2404
|
+
* further tokens; these must exist.
|
|
2405
|
+
*/
|
|
2406
|
+
var REQUIRED_COLOR_TOKENS = [
|
|
2407
|
+
"primary",
|
|
2408
|
+
"on-primary",
|
|
2409
|
+
"primary-container",
|
|
2410
|
+
"on-primary-container",
|
|
2411
|
+
"primary-surface",
|
|
2412
|
+
"on-primary-surface",
|
|
2413
|
+
"surface",
|
|
2414
|
+
"on-surface",
|
|
2415
|
+
"background",
|
|
2416
|
+
"on-background",
|
|
2417
|
+
"warn",
|
|
2418
|
+
"on-warn",
|
|
2419
|
+
"disabled",
|
|
2420
|
+
"on-disabled",
|
|
2421
|
+
"outline",
|
|
2422
|
+
"transparent"
|
|
2423
|
+
];
|
|
2424
|
+
/** The canonical type scale; every theme must style each role. */
|
|
2425
|
+
var REQUIRED_TEXT_ROLES = [
|
|
2426
|
+
"display-large",
|
|
2427
|
+
"display-medium",
|
|
2428
|
+
"display-small",
|
|
2429
|
+
"headline-large",
|
|
2430
|
+
"headline-medium",
|
|
2431
|
+
"headline-small",
|
|
2432
|
+
"title-large",
|
|
2433
|
+
"title-medium",
|
|
2434
|
+
"title-small",
|
|
2435
|
+
"body-1-long",
|
|
2436
|
+
"body-1-short",
|
|
2437
|
+
"body-2-long",
|
|
2438
|
+
"body-2-short",
|
|
2439
|
+
"subtitle-1",
|
|
2440
|
+
"subtitle-2",
|
|
2441
|
+
"label",
|
|
2442
|
+
"button",
|
|
2443
|
+
"caption",
|
|
2444
|
+
"overline",
|
|
2445
|
+
"paragraph",
|
|
2446
|
+
"quote",
|
|
2447
|
+
"note"
|
|
2448
|
+
];
|
|
2449
|
+
var REQUIRED_SPACING_SIZES = [
|
|
2450
|
+
"none",
|
|
2451
|
+
"xxs",
|
|
2452
|
+
"xs",
|
|
2453
|
+
"sm",
|
|
2454
|
+
"md",
|
|
2455
|
+
"lg",
|
|
2456
|
+
"xl"
|
|
2457
|
+
];
|
|
2458
|
+
var REQUIRED_RADII_SIZES = [
|
|
2459
|
+
"none",
|
|
2460
|
+
"xxs",
|
|
2461
|
+
"xs",
|
|
2462
|
+
"sm",
|
|
2463
|
+
"md",
|
|
2464
|
+
"lg",
|
|
2465
|
+
"max"
|
|
2466
|
+
];
|
|
2467
|
+
var REQUIRED_SHADOWS = [
|
|
2468
|
+
"raised",
|
|
2469
|
+
"menu",
|
|
2470
|
+
"dialog",
|
|
2471
|
+
"warn"
|
|
2472
|
+
];
|
|
2473
|
+
var REQUIRED_THICKNESSES = [
|
|
2474
|
+
"thin",
|
|
2475
|
+
"standard",
|
|
2476
|
+
"thick"
|
|
2477
|
+
];
|
|
2478
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2479
|
+
var isCssValue = (value) => typeof value === "string" || typeof value === "number";
|
|
2480
|
+
/** Nested style expression: string/number leaves, objects for selectors. */
|
|
2481
|
+
var checkStyleExpression = (value, path, issues) => {
|
|
2482
|
+
if (!isRecord(value)) {
|
|
2483
|
+
issues.push({
|
|
2484
|
+
path,
|
|
2485
|
+
message: "must be a style object"
|
|
2486
|
+
});
|
|
2487
|
+
return;
|
|
2488
|
+
}
|
|
2489
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2490
|
+
if (entry === void 0 || isCssValue(entry)) continue;
|
|
2491
|
+
if (isRecord(entry)) checkStyleExpression(entry, `${path}.${key}`, issues);
|
|
2492
|
+
else issues.push({
|
|
2493
|
+
path: `${path}.${key}`,
|
|
2494
|
+
message: "must be a string, number, or nested style object"
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2497
|
+
};
|
|
2498
|
+
var checkStringRecord = (value, path, issues, required = [], allowNumbers = false) => {
|
|
2499
|
+
if (!isRecord(value)) {
|
|
2500
|
+
issues.push({
|
|
2501
|
+
path,
|
|
2502
|
+
message: "must be an object"
|
|
2503
|
+
});
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2506
|
+
for (const key of required) if (!(key in value)) issues.push({
|
|
2507
|
+
path: `${path}.${key}`,
|
|
2508
|
+
message: "is required"
|
|
2509
|
+
});
|
|
2510
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2511
|
+
if (entry === void 0) continue;
|
|
2512
|
+
if (!(allowNumbers ? isCssValue(entry) : typeof entry === "string")) issues.push({
|
|
2513
|
+
path: `${path}.${key}`,
|
|
2514
|
+
message: allowNumbers ? "must be a string or number" : "must be a string"
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
var checkTypography = (value, issues) => {
|
|
2519
|
+
if (!isRecord(value)) {
|
|
2520
|
+
issues.push({
|
|
2521
|
+
path: "typography",
|
|
2522
|
+
message: "must be an object"
|
|
2523
|
+
});
|
|
2524
|
+
return;
|
|
2525
|
+
}
|
|
2526
|
+
for (const role of REQUIRED_TEXT_ROLES) if (!(role in value)) issues.push({
|
|
2527
|
+
path: `typography.${role}`,
|
|
2528
|
+
message: "is required"
|
|
2529
|
+
});
|
|
2530
|
+
for (const [role, style] of Object.entries(value)) {
|
|
2531
|
+
if (!isRecord(style)) {
|
|
2532
|
+
issues.push({
|
|
2533
|
+
path: `typography.${role}`,
|
|
2534
|
+
message: "must be a TextStyle object"
|
|
2535
|
+
});
|
|
2536
|
+
continue;
|
|
2537
|
+
}
|
|
2538
|
+
if (typeof style["fontFamily"] !== "string") issues.push({
|
|
2539
|
+
path: `typography.${role}.fontFamily`,
|
|
2540
|
+
message: "must be a string"
|
|
2541
|
+
});
|
|
2542
|
+
for (const field of ["fontSize", "lineHeight"]) if (!isCssValue(style[field])) issues.push({
|
|
2543
|
+
path: `typography.${role}.${field}`,
|
|
2544
|
+
message: "must be a string or number"
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
};
|
|
2548
|
+
var checkComponents = (value, issues) => {
|
|
2549
|
+
if (!isRecord(value)) {
|
|
2550
|
+
issues.push({
|
|
2551
|
+
path: "components",
|
|
2552
|
+
message: "must be an object"
|
|
2553
|
+
});
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
for (const [name, entry] of Object.entries(value)) {
|
|
2557
|
+
if (entry === void 0) continue;
|
|
2558
|
+
if (!isRecord(entry)) {
|
|
2559
|
+
issues.push({
|
|
2560
|
+
path: `components.${name}`,
|
|
2561
|
+
message: "must be a component theme object"
|
|
2562
|
+
});
|
|
2563
|
+
continue;
|
|
2564
|
+
}
|
|
2565
|
+
if (entry["fixed"] !== void 0) checkStyleExpression(entry["fixed"], `components.${name}.fixed`, issues);
|
|
2566
|
+
for (const section of ["variants", "sizes"]) {
|
|
2567
|
+
const styles = entry[section];
|
|
2568
|
+
if (styles === void 0) continue;
|
|
2569
|
+
if (!isRecord(styles)) {
|
|
2570
|
+
issues.push({
|
|
2571
|
+
path: `components.${name}.${section}`,
|
|
2572
|
+
message: "must be an object"
|
|
2573
|
+
});
|
|
2574
|
+
continue;
|
|
2575
|
+
}
|
|
2576
|
+
for (const [key, style] of Object.entries(styles)) if (style !== void 0) checkStyleExpression(style, `components.${name}.${section}.${key}`, issues);
|
|
2577
|
+
}
|
|
2578
|
+
if (entry["options"] !== void 0 && !isRecord(entry["options"])) issues.push({
|
|
2579
|
+
path: `components.${name}.options`,
|
|
2580
|
+
message: "must be an object"
|
|
2581
|
+
});
|
|
2582
|
+
}
|
|
2583
|
+
};
|
|
2584
|
+
/**
|
|
2585
|
+
* Validate a candidate theme, collecting every structural issue. Never
|
|
2586
|
+
* throws; a failed result carries the complete repair list.
|
|
2587
|
+
*/
|
|
2588
|
+
var parseTheme = (input) => {
|
|
2589
|
+
const issues = [];
|
|
2590
|
+
if (!isRecord(input)) return {
|
|
2591
|
+
success: false,
|
|
2592
|
+
issues: [{
|
|
2593
|
+
path: "",
|
|
2594
|
+
message: "theme must be an object"
|
|
2595
|
+
}]
|
|
2596
|
+
};
|
|
2597
|
+
for (const field of ["id", "name"]) if (typeof input[field] !== "string" || input[field] === "") issues.push({
|
|
2598
|
+
path: field,
|
|
2599
|
+
message: "must be a non-empty string"
|
|
2600
|
+
});
|
|
2601
|
+
checkStringRecord(input["colors"], "colors", issues, REQUIRED_COLOR_TOKENS);
|
|
2602
|
+
checkTypography(input["typography"], issues);
|
|
2603
|
+
checkStringRecord(input["borders"], "borders", issues);
|
|
2604
|
+
checkStringRecord(input["radii"], "radii", issues, REQUIRED_RADII_SIZES);
|
|
2605
|
+
checkStringRecord(input["shadows"], "shadows", issues, REQUIRED_SHADOWS);
|
|
2606
|
+
checkStringRecord(input["spacing"], "spacing", issues, REQUIRED_SPACING_SIZES, true);
|
|
2607
|
+
checkStringRecord(input["thicknesses"], "thicknesses", issues, REQUIRED_THICKNESSES, true);
|
|
2608
|
+
checkStringRecord(input["icons"], "icons", issues);
|
|
2609
|
+
checkComponents(input["components"], issues);
|
|
2610
|
+
return issues.length === 0 ? {
|
|
2611
|
+
success: true,
|
|
2612
|
+
theme: input,
|
|
2613
|
+
issues: []
|
|
2614
|
+
} : {
|
|
2615
|
+
success: false,
|
|
2616
|
+
issues
|
|
2617
|
+
};
|
|
2618
|
+
};
|
|
2619
|
+
/** Format issues for logs and error messages. */
|
|
2620
|
+
var formatThemeIssues = (issues) => issues.map(({ path, message }) => path ? `${path} ${message}` : message).join("; ");
|
|
2621
|
+
/** Validate or throw, with every reason in the error message. */
|
|
2622
|
+
var assertTheme = (input) => {
|
|
2623
|
+
const result = parseTheme(input);
|
|
2624
|
+
if (!result.success) throw new Error(`Invalid UniTheme: ${formatThemeIssues(result.issues)}`);
|
|
2625
|
+
return result.theme;
|
|
2626
|
+
};
|
|
2627
|
+
var isUniTheme = (input) => parseTheme(input).success;
|
|
2628
|
+
//#endregion
|
|
2201
2629
|
//#region src/concepts/typography/typography.records.ts
|
|
2202
2630
|
var FontWeightMap = {
|
|
2203
2631
|
thin: 100,
|
|
@@ -2261,10 +2689,17 @@ exports.EXPAND_MAX_DURATION = EXPAND_MAX_DURATION;
|
|
|
2261
2689
|
exports.EXPAND_MIN_DURATION = EXPAND_MIN_DURATION;
|
|
2262
2690
|
exports.EXPAND_REFERENCE_HEIGHT = EXPAND_REFERENCE_HEIGHT;
|
|
2263
2691
|
exports.FontWeightMap = FontWeightMap;
|
|
2692
|
+
exports.HOVER_OR_KEYBOARD_FOCUS = HOVER_OR_KEYBOARD_FOCUS;
|
|
2264
2693
|
exports.HSLAToString = HSLAToString;
|
|
2265
2694
|
exports.HSLToHex = HSLToHex;
|
|
2266
2695
|
exports.HSLToRGB = HSLToRGB;
|
|
2267
2696
|
exports.LightTheme = LightTheme;
|
|
2697
|
+
exports.REQUIRED_COLOR_TOKENS = REQUIRED_COLOR_TOKENS;
|
|
2698
|
+
exports.REQUIRED_RADII_SIZES = REQUIRED_RADII_SIZES;
|
|
2699
|
+
exports.REQUIRED_SHADOWS = REQUIRED_SHADOWS;
|
|
2700
|
+
exports.REQUIRED_SPACING_SIZES = REQUIRED_SPACING_SIZES;
|
|
2701
|
+
exports.REQUIRED_TEXT_ROLES = REQUIRED_TEXT_ROLES;
|
|
2702
|
+
exports.REQUIRED_THICKNESSES = REQUIRED_THICKNESSES;
|
|
2268
2703
|
exports.RGBToHSL = RGBToHSL;
|
|
2269
2704
|
exports.RGBToString = RGBToString;
|
|
2270
2705
|
exports.RoleHues = RoleHues;
|
|
@@ -2273,6 +2708,7 @@ exports.ShadowMap = ShadowMap;
|
|
|
2273
2708
|
exports.ShapeRadii = ShapeRadii;
|
|
2274
2709
|
exports.UniThemes = UniThemes;
|
|
2275
2710
|
exports.Z_INDEX = Z_INDEX;
|
|
2711
|
+
exports.assertTheme = assertTheme;
|
|
2276
2712
|
exports.classifyScheme = classifyScheme;
|
|
2277
2713
|
exports.collapseFadeOut = collapseFadeOut;
|
|
2278
2714
|
exports.contrastRatio = contrastRatio;
|
|
@@ -2281,12 +2717,14 @@ exports.createThemeFromPalette = createThemeFromPalette;
|
|
|
2281
2717
|
exports.cycle = cycle;
|
|
2282
2718
|
exports.darkColors = darkColors;
|
|
2283
2719
|
exports.debounce = debounce;
|
|
2720
|
+
exports.dehydrateTheme = dehydrateTheme;
|
|
2284
2721
|
exports.emitDtcgTokens = emitDtcgTokens;
|
|
2285
2722
|
exports.emitThemeFile = emitThemeFile;
|
|
2286
2723
|
exports.expandDuration = expandDuration;
|
|
2287
2724
|
exports.expandFadeIn = expandFadeIn;
|
|
2288
2725
|
exports.fadeIn = fadeIn;
|
|
2289
2726
|
exports.fadeOut = fadeOut;
|
|
2727
|
+
exports.formatThemeIssues = formatThemeIssues;
|
|
2290
2728
|
exports.generateColors = generateColors;
|
|
2291
2729
|
exports.generatePalette = generatePalette;
|
|
2292
2730
|
exports.generateShadows = generateShadows;
|
|
@@ -2303,14 +2741,18 @@ exports.gradient = gradient;
|
|
|
2303
2741
|
exports.hexToHSL = hexToHSL;
|
|
2304
2742
|
exports.hexToOklch = hexToOklch;
|
|
2305
2743
|
exports.hexToRgb = hexToRgb;
|
|
2744
|
+
exports.hydrateTheme = hydrateTheme;
|
|
2306
2745
|
exports.inferCategory = inferCategory;
|
|
2746
|
+
exports.isUniTheme = isUniTheme;
|
|
2307
2747
|
exports.lightColors = lightColors;
|
|
2308
2748
|
exports.oklchToHex = oklchToHex;
|
|
2749
|
+
exports.parseTheme = parseTheme;
|
|
2309
2750
|
exports.randomRangeValue = randomRangeValue;
|
|
2310
2751
|
exports.relativeLuminance = relativeLuminance;
|
|
2311
2752
|
exports.removeInputPlatformStyling = removeInputPlatformStyling;
|
|
2312
2753
|
exports.rgbToHex = rgbToHex;
|
|
2313
2754
|
exports.schemeHues = schemeHues;
|
|
2755
|
+
exports.summarizeContrast = summarizeContrast;
|
|
2314
2756
|
exports.svgToIconUri = svgToIconUri;
|
|
2315
2757
|
exports.toTypeface = toTypeface;
|
|
2316
2758
|
exports.toTypefaces = toTypefaces;
|