@opendata-ai/openchart-core 7.6.0 → 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.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
- theme.colors.annotationFill,
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
- // Sparkline trend colors tuned for dark surfaces: teal-leaning green
1425
- // and coral red read better than the saturated light-mode tokens.
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); the other
1432
- // chrome elements desaturate to a muted gray on the dark canvas.
1433
- // Each color only adapts if the spec left it at the light default,
1434
- // so explicit chrome color overrides survive dark-mode adaptation.
1435
- eyebrow: theme.chrome.eyebrow,
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, color2] of Object.entries(config.chrome)) {
1513
- if (color2 !== void 0) chrome[element] = { color: color2 };
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