@opendata-ai/openchart-core 7.6.1 → 7.7.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/index.d.ts +468 -179
- package/dist/index.js +155 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/colors/palettes.ts +3 -3
- package/src/helpers/spec-builders.ts +5 -2
- package/src/index.ts +5 -0
- package/src/styles/dark.css +5 -1
- package/src/styles/tokens.css +6 -0
- package/src/theme/__tests__/dark-mode.test.ts +80 -0
- package/src/theme/__tests__/resolve.test.ts +114 -0
- package/src/theme/dark-mode.ts +56 -26
- package/src/theme/defaults.ts +2 -1
- package/src/theme/index.ts +1 -0
- package/src/theme/presets.ts +64 -0
- package/src/theme/resolve.ts +85 -12
- package/src/types/index.ts +8 -0
- package/src/types/layout.ts +57 -1
- package/src/types/spec.ts +158 -29
- package/src/types/theme.ts +36 -0
package/dist/index.js
CHANGED
|
@@ -1337,7 +1337,8 @@ var DEFAULT_THEME = {
|
|
|
1337
1337
|
color: "#71717a",
|
|
1338
1338
|
lineHeight: 1.4
|
|
1339
1339
|
}
|
|
1340
|
-
}
|
|
1340
|
+
},
|
|
1341
|
+
seriesStrategy: "palette"
|
|
1341
1342
|
};
|
|
1342
1343
|
|
|
1343
1344
|
// src/theme/dark-mode.ts
|
|
@@ -1393,17 +1394,18 @@ function _luminanceFromHex(color2) {
|
|
|
1393
1394
|
return (0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) / 255;
|
|
1394
1395
|
}
|
|
1395
1396
|
function adaptTheme(theme) {
|
|
1397
|
+
const pairs = theme._tokenPairs;
|
|
1396
1398
|
const inputBg = theme.colors.background;
|
|
1397
1399
|
const isTransparent = inputBg === "transparent";
|
|
1398
1400
|
const alreadyDark = isTransparent || _luminanceFromHex(inputBg) < 0.2;
|
|
1399
|
-
const darkBg = alreadyDark ? inputBg : DARK_BG;
|
|
1401
|
+
const darkBg = pairs?.["colors.background"] ? pairs["colors.background"].dark : alreadyDark ? inputBg : DARK_BG;
|
|
1400
1402
|
const darkMuted = ACHROMATIC_RAMP.fgMuted;
|
|
1401
1403
|
const light = DEFAULT_THEME.colors;
|
|
1402
1404
|
const lightChrome = DEFAULT_THEME.chrome;
|
|
1403
1405
|
const overridden = (current, lightDefault, darkDefault) => current === lightDefault ? darkDefault : current;
|
|
1404
|
-
const darkText = overridden(theme.colors.text, light.text, DARK_TEXT);
|
|
1405
|
-
const darkGridline = overridden(theme.colors.gridline, light.gridline, "rgba(255,255,255,0.05)");
|
|
1406
|
-
const darkAxis = overridden(theme.colors.axis, light.axis, "#a1a1aa");
|
|
1406
|
+
const darkText = pairs?.["colors.text"] ? pairs["colors.text"].dark : overridden(theme.colors.text, light.text, DARK_TEXT);
|
|
1407
|
+
const darkGridline = pairs?.["colors.gridline"] ? pairs["colors.gridline"].dark : overridden(theme.colors.gridline, light.gridline, "rgba(255,255,255,0.05)");
|
|
1408
|
+
const darkAxis = pairs?.["colors.axis"] ? pairs["colors.axis"].dark : overridden(theme.colors.axis, light.axis, "#a1a1aa");
|
|
1407
1409
|
const categorical = theme.colors.categorical;
|
|
1408
1410
|
return {
|
|
1409
1411
|
...theme,
|
|
@@ -1414,48 +1416,103 @@ function adaptTheme(theme) {
|
|
|
1414
1416
|
text: darkText,
|
|
1415
1417
|
gridline: darkGridline,
|
|
1416
1418
|
axis: darkAxis,
|
|
1417
|
-
annotationFill: overridden(
|
|
1418
|
-
|
|
1419
|
-
light.annotationFill,
|
|
1420
|
-
"rgba(255,255,255,0.06)"
|
|
1421
|
-
),
|
|
1422
|
-
annotationText: overridden(theme.colors.annotationText, light.annotationText, darkMuted),
|
|
1419
|
+
annotationFill: pairs?.["colors.annotationFill"] ? pairs["colors.annotationFill"].dark : overridden(theme.colors.annotationFill, light.annotationFill, "rgba(255,255,255,0.06)"),
|
|
1420
|
+
annotationText: pairs?.["colors.annotationText"] ? pairs["colors.annotationText"].dark : overridden(theme.colors.annotationText, light.annotationText, darkMuted),
|
|
1423
1421
|
categorical,
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
// Any non-default value is treated as a user override and preserved.
|
|
1427
|
-
positive: theme.colors.positive !== "#16a34a" ? theme.colors.positive : "#34d399",
|
|
1428
|
-
negative: theme.colors.negative !== "#dc2626" ? theme.colors.negative : "#f87171"
|
|
1422
|
+
positive: pairs?.["colors.positive"] ? pairs["colors.positive"].dark : theme.colors.positive !== "#16a34a" ? theme.colors.positive : "#34d399",
|
|
1423
|
+
negative: pairs?.["colors.negative"] ? pairs["colors.negative"].dark : theme.colors.negative !== "#dc2626" ? theme.colors.negative : "#f87171"
|
|
1429
1424
|
},
|
|
1430
1425
|
chrome: {
|
|
1431
|
-
// Eyebrow keeps its accent tint (cyan in both modes)
|
|
1432
|
-
//
|
|
1433
|
-
//
|
|
1434
|
-
// so explicit chrome color
|
|
1435
|
-
|
|
1426
|
+
// Eyebrow keeps its accent tint (cyan in both modes) unless the user
|
|
1427
|
+
// gave an explicit dark value; the other chrome elements desaturate
|
|
1428
|
+
// to a muted gray on the dark canvas. Each color only adapts if the
|
|
1429
|
+
// spec left it at the light default, so explicit chrome color
|
|
1430
|
+
// overrides survive dark-mode adaptation. When a TokenValue pair has
|
|
1431
|
+
// an explicit dark value, use it directly.
|
|
1432
|
+
eyebrow: pairs?.["chrome.eyebrow.color"] ? { ...theme.chrome.eyebrow, color: pairs["chrome.eyebrow.color"].dark } : theme.chrome.eyebrow,
|
|
1436
1433
|
title: {
|
|
1437
1434
|
...theme.chrome.title,
|
|
1438
|
-
color: overridden(theme.chrome.title.color, lightChrome.title.color, darkText)
|
|
1435
|
+
color: pairs?.["chrome.title.color"] ? pairs["chrome.title.color"].dark : overridden(theme.chrome.title.color, lightChrome.title.color, darkText)
|
|
1439
1436
|
},
|
|
1440
1437
|
subtitle: {
|
|
1441
1438
|
...theme.chrome.subtitle,
|
|
1442
|
-
color: overridden(theme.chrome.subtitle.color, lightChrome.subtitle.color, darkMuted)
|
|
1439
|
+
color: pairs?.["chrome.subtitle.color"] ? pairs["chrome.subtitle.color"].dark : overridden(theme.chrome.subtitle.color, lightChrome.subtitle.color, darkMuted)
|
|
1443
1440
|
},
|
|
1444
1441
|
source: {
|
|
1445
1442
|
...theme.chrome.source,
|
|
1446
|
-
color: overridden(theme.chrome.source.color, lightChrome.source.color, darkMuted)
|
|
1443
|
+
color: pairs?.["chrome.source.color"] ? pairs["chrome.source.color"].dark : overridden(theme.chrome.source.color, lightChrome.source.color, darkMuted)
|
|
1447
1444
|
},
|
|
1448
1445
|
byline: {
|
|
1449
1446
|
...theme.chrome.byline,
|
|
1450
|
-
color: overridden(theme.chrome.byline.color, lightChrome.byline.color, darkMuted)
|
|
1447
|
+
color: pairs?.["chrome.byline.color"] ? pairs["chrome.byline.color"].dark : overridden(theme.chrome.byline.color, lightChrome.byline.color, darkMuted)
|
|
1451
1448
|
},
|
|
1452
1449
|
footer: {
|
|
1453
1450
|
...theme.chrome.footer,
|
|
1454
|
-
color: overridden(theme.chrome.footer.color, lightChrome.footer.color, darkMuted)
|
|
1451
|
+
color: pairs?.["chrome.footer.color"] ? pairs["chrome.footer.color"].dark : overridden(theme.chrome.footer.color, lightChrome.footer.color, darkMuted)
|
|
1455
1452
|
}
|
|
1456
1453
|
}
|
|
1457
1454
|
};
|
|
1458
1455
|
}
|
|
1456
|
+
function adaptForLightLineStroke(color2) {
|
|
1457
|
+
if (rgb(color2) == null) return color2;
|
|
1458
|
+
const c = hsl(color2);
|
|
1459
|
+
if (c == null) return color2;
|
|
1460
|
+
if (Number.isNaN(c.h) || c.s < 0.1) return color2;
|
|
1461
|
+
if (c.l <= 0.4) return color2;
|
|
1462
|
+
c.l = Math.max(0, c.l - 0.12);
|
|
1463
|
+
return c.formatHex();
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
// src/theme/presets.ts
|
|
1467
|
+
var editorial = {};
|
|
1468
|
+
var essay = {
|
|
1469
|
+
colors: {
|
|
1470
|
+
background: { light: "#faf8f5", dark: "#1a1816" },
|
|
1471
|
+
text: { light: "#1c1917", dark: "#e7e5e4" },
|
|
1472
|
+
gridline: { light: "rgba(0,0,0,0.08)", dark: "rgba(255,255,255,0.06)" },
|
|
1473
|
+
axis: { light: "#78716c", dark: "#a8a29e" }
|
|
1474
|
+
},
|
|
1475
|
+
fonts: {
|
|
1476
|
+
family: '"Source Serif 4", "Source Serif Pro", Georgia, "Times New Roman", serif',
|
|
1477
|
+
sizes: { title: 28, subtitle: 15, body: 14, small: 12 },
|
|
1478
|
+
weights: { normal: 400, medium: 500, semibold: 600, bold: 700 }
|
|
1479
|
+
},
|
|
1480
|
+
spacing: {
|
|
1481
|
+
padding: 24,
|
|
1482
|
+
chromeGap: 6,
|
|
1483
|
+
chromeToChart: 12,
|
|
1484
|
+
chartToFooter: 12
|
|
1485
|
+
},
|
|
1486
|
+
chrome: {
|
|
1487
|
+
title: { fontWeight: 600, lineHeight: 1.2 },
|
|
1488
|
+
subtitle: { fontWeight: 400, lineHeight: 1.5 }
|
|
1489
|
+
},
|
|
1490
|
+
borderRadius: 0
|
|
1491
|
+
};
|
|
1492
|
+
var wire = {
|
|
1493
|
+
colors: {
|
|
1494
|
+
background: { light: "#ffffff", dark: "#0a0a0a" },
|
|
1495
|
+
text: { light: "#171717", dark: "#ededed" },
|
|
1496
|
+
gridline: { light: "rgba(0,0,0,0.06)", dark: "rgba(255,255,255,0.06)" },
|
|
1497
|
+
axis: { light: "#737373", dark: "#a3a3a3" }
|
|
1498
|
+
},
|
|
1499
|
+
fonts: {
|
|
1500
|
+
family: '"JetBrains Mono", "Fira Code", "Cascadia Code", monospace',
|
|
1501
|
+
sizes: { title: 16, subtitle: 12, body: 11, small: 10, axisTick: 10 },
|
|
1502
|
+
weights: { normal: 400, medium: 500, semibold: 600, bold: 700 }
|
|
1503
|
+
},
|
|
1504
|
+
spacing: {
|
|
1505
|
+
padding: 12,
|
|
1506
|
+
chromeGap: 2,
|
|
1507
|
+
chromeToChart: 4,
|
|
1508
|
+
chartToFooter: 4
|
|
1509
|
+
},
|
|
1510
|
+
chrome: {
|
|
1511
|
+
title: { fontWeight: 600, lineHeight: 1.25 },
|
|
1512
|
+
subtitle: { fontWeight: 400, lineHeight: 1.3 }
|
|
1513
|
+
},
|
|
1514
|
+
borderRadius: 2
|
|
1515
|
+
};
|
|
1459
1516
|
|
|
1460
1517
|
// src/theme/resolve.ts
|
|
1461
1518
|
function deepMerge(target, source) {
|
|
@@ -1471,6 +1528,9 @@ function deepMerge(target, source) {
|
|
|
1471
1528
|
}
|
|
1472
1529
|
return result;
|
|
1473
1530
|
}
|
|
1531
|
+
function resolveTokenLight(v) {
|
|
1532
|
+
return typeof v === "string" ? v : v.light;
|
|
1533
|
+
}
|
|
1474
1534
|
function themeConfigToPartial(config) {
|
|
1475
1535
|
const partial = {};
|
|
1476
1536
|
if (config.colors) {
|
|
@@ -1481,10 +1541,16 @@ function themeConfigToPartial(config) {
|
|
|
1481
1541
|
if (config.colors.categorical) colors.categorical = config.colors.categorical;
|
|
1482
1542
|
if (config.colors.sequential) colors.sequential = config.colors.sequential;
|
|
1483
1543
|
if (config.colors.diverging) colors.diverging = config.colors.diverging;
|
|
1484
|
-
if (config.colors.background) colors.background = config.colors.background;
|
|
1485
|
-
if (config.colors.text) colors.text = config.colors.text;
|
|
1486
|
-
if (config.colors.gridline) colors.gridline = config.colors.gridline;
|
|
1487
|
-
if (config.colors.axis) colors.axis = config.colors.axis;
|
|
1544
|
+
if (config.colors.background) colors.background = resolveTokenLight(config.colors.background);
|
|
1545
|
+
if (config.colors.text) colors.text = resolveTokenLight(config.colors.text);
|
|
1546
|
+
if (config.colors.gridline) colors.gridline = resolveTokenLight(config.colors.gridline);
|
|
1547
|
+
if (config.colors.axis) colors.axis = resolveTokenLight(config.colors.axis);
|
|
1548
|
+
if (config.colors.annotationFill)
|
|
1549
|
+
colors.annotationFill = resolveTokenLight(config.colors.annotationFill);
|
|
1550
|
+
if (config.colors.annotationText)
|
|
1551
|
+
colors.annotationText = resolveTokenLight(config.colors.annotationText);
|
|
1552
|
+
if (config.colors.positive) colors.positive = resolveTokenLight(config.colors.positive);
|
|
1553
|
+
if (config.colors.negative) colors.negative = resolveTokenLight(config.colors.negative);
|
|
1488
1554
|
}
|
|
1489
1555
|
partial.colors = colors;
|
|
1490
1556
|
}
|
|
@@ -1493,12 +1559,18 @@ function themeConfigToPartial(config) {
|
|
|
1493
1559
|
if (config.fonts.family) fonts.family = config.fonts.family;
|
|
1494
1560
|
if (config.fonts.mono) fonts.mono = config.fonts.mono;
|
|
1495
1561
|
if (config.fonts.sizes) fonts.sizes = config.fonts.sizes;
|
|
1562
|
+
if (config.fonts.weights) fonts.weights = config.fonts.weights;
|
|
1496
1563
|
partial.fonts = fonts;
|
|
1497
1564
|
}
|
|
1498
1565
|
if (config.spacing) {
|
|
1499
1566
|
const spacing = {};
|
|
1500
1567
|
if (config.spacing.padding !== void 0) spacing.padding = config.spacing.padding;
|
|
1501
1568
|
if (config.spacing.chromeGap !== void 0) spacing.chromeGap = config.spacing.chromeGap;
|
|
1569
|
+
if (config.spacing.chromeToChart !== void 0)
|
|
1570
|
+
spacing.chromeToChart = config.spacing.chromeToChart;
|
|
1571
|
+
if (config.spacing.chartToFooter !== void 0)
|
|
1572
|
+
spacing.chartToFooter = config.spacing.chartToFooter;
|
|
1573
|
+
if (config.spacing.axisMargin !== void 0) spacing.axisMargin = config.spacing.axisMargin;
|
|
1502
1574
|
if (config.spacing.xAxisHeight !== void 0) spacing.xAxisHeight = config.spacing.xAxisHeight;
|
|
1503
1575
|
if (config.spacing.xAxisLabelPadding !== void 0)
|
|
1504
1576
|
spacing.xAxisLabelPadding = config.spacing.xAxisLabelPadding;
|
|
@@ -1509,13 +1581,56 @@ function themeConfigToPartial(config) {
|
|
|
1509
1581
|
}
|
|
1510
1582
|
if (config.chrome) {
|
|
1511
1583
|
const chrome = {};
|
|
1512
|
-
for (const [element,
|
|
1513
|
-
if (
|
|
1584
|
+
for (const [element, value] of Object.entries(config.chrome)) {
|
|
1585
|
+
if (value === void 0) continue;
|
|
1586
|
+
if (typeof value === "string") {
|
|
1587
|
+
chrome[element] = { color: value };
|
|
1588
|
+
} else {
|
|
1589
|
+
const override = {};
|
|
1590
|
+
const obj = value;
|
|
1591
|
+
if (obj.color !== void 0) override.color = resolveTokenLight(obj.color);
|
|
1592
|
+
if (obj.fontSize !== void 0) override.fontSize = obj.fontSize;
|
|
1593
|
+
if (obj.fontWeight !== void 0) override.fontWeight = obj.fontWeight;
|
|
1594
|
+
if (obj.lineHeight !== void 0) override.lineHeight = obj.lineHeight;
|
|
1595
|
+
chrome[element] = override;
|
|
1596
|
+
}
|
|
1514
1597
|
}
|
|
1515
1598
|
partial.chrome = chrome;
|
|
1516
1599
|
}
|
|
1600
|
+
if (config.seriesStrategy) {
|
|
1601
|
+
partial.seriesStrategy = config.seriesStrategy;
|
|
1602
|
+
}
|
|
1517
1603
|
return partial;
|
|
1518
1604
|
}
|
|
1605
|
+
function collectTokenPairs(config) {
|
|
1606
|
+
const pairs = {};
|
|
1607
|
+
if (config.colors && !Array.isArray(config.colors)) {
|
|
1608
|
+
const colorFields = [
|
|
1609
|
+
"background",
|
|
1610
|
+
"text",
|
|
1611
|
+
"gridline",
|
|
1612
|
+
"axis",
|
|
1613
|
+
"annotationFill",
|
|
1614
|
+
"annotationText",
|
|
1615
|
+
"positive",
|
|
1616
|
+
"negative"
|
|
1617
|
+
];
|
|
1618
|
+
for (const field of colorFields) {
|
|
1619
|
+
const v = config.colors[field];
|
|
1620
|
+
if (v && typeof v === "object" && "light" in v) {
|
|
1621
|
+
pairs[`colors.${field}`] = v;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
if (config.chrome) {
|
|
1626
|
+
for (const [element, value] of Object.entries(config.chrome)) {
|
|
1627
|
+
if (value && typeof value === "object" && "color" in value && value.color && typeof value.color === "object" && "light" in value.color) {
|
|
1628
|
+
pairs[`chrome.${element}.color`] = value.color;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
return Object.keys(pairs).length > 0 ? pairs : void 0;
|
|
1633
|
+
}
|
|
1519
1634
|
function relativeLuminance2(hex2) {
|
|
1520
1635
|
const m = /^#?([0-9a-f]{6})$/i.exec(hex2.trim());
|
|
1521
1636
|
if (!m) return 0;
|
|
@@ -1597,7 +1712,8 @@ function resolveTheme(userTheme, base = DEFAULT_THEME) {
|
|
|
1597
1712
|
}
|
|
1598
1713
|
return {
|
|
1599
1714
|
...merged,
|
|
1600
|
-
isDark: dark
|
|
1715
|
+
isDark: dark,
|
|
1716
|
+
_tokenPairs: userTheme ? collectTokenPairs(userTheme) : void 0
|
|
1601
1717
|
};
|
|
1602
1718
|
}
|
|
1603
1719
|
|
|
@@ -3369,6 +3485,7 @@ function tileMap(data, options) {
|
|
|
3369
3485
|
data,
|
|
3370
3486
|
...options?.encoding && { encoding: options.encoding },
|
|
3371
3487
|
...options?.palette && { palette: options.palette },
|
|
3488
|
+
...options?.colors && { colors: options.colors },
|
|
3372
3489
|
...options?.chrome && { chrome: options.chrome },
|
|
3373
3490
|
...options?.legend && { legend: options.legend },
|
|
3374
3491
|
...options?.theme && { theme: options.theme },
|
|
@@ -3379,6 +3496,7 @@ function tileMap(data, options) {
|
|
|
3379
3496
|
};
|
|
3380
3497
|
}
|
|
3381
3498
|
export {
|
|
3499
|
+
ACHROMATIC_RAMP,
|
|
3382
3500
|
AXIS_TITLE_GAP,
|
|
3383
3501
|
AXIS_TITLE_OFFSET_COMPACT,
|
|
3384
3502
|
AXIS_TITLE_OFFSET_DEFAULT,
|
|
@@ -3421,6 +3539,7 @@ export {
|
|
|
3421
3539
|
X_AXIS_TITLE_BAND_ROTATED,
|
|
3422
3540
|
abbreviateNumber,
|
|
3423
3541
|
adaptColorForDarkMode,
|
|
3542
|
+
adaptForLightLineStroke,
|
|
3424
3543
|
adaptTheme,
|
|
3425
3544
|
areaChart,
|
|
3426
3545
|
axisTitleOffset,
|
|
@@ -3437,7 +3556,9 @@ export {
|
|
|
3437
3556
|
detectCollision,
|
|
3438
3557
|
donutChart,
|
|
3439
3558
|
dotChart,
|
|
3559
|
+
editorial,
|
|
3440
3560
|
elementRef,
|
|
3561
|
+
essay,
|
|
3441
3562
|
estimateTextWidth,
|
|
3442
3563
|
findAccessibleColor,
|
|
3443
3564
|
formatDate,
|
|
@@ -3479,6 +3600,7 @@ export {
|
|
|
3479
3600
|
simulateColorBlindness,
|
|
3480
3601
|
textAscent,
|
|
3481
3602
|
tileMap,
|
|
3603
|
+
wire,
|
|
3482
3604
|
wrapText
|
|
3483
3605
|
};
|
|
3484
3606
|
//# sourceMappingURL=index.js.map
|