@pixerate/editor 0.46.0 → 0.46.1

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.
@@ -335,9 +335,12 @@ function niceStep(span, count) {
335
335
  }
336
336
  var clean = (n) => Math.abs(n) < 1e-12 ? 0 : Number(n.toPrecision(12));
337
337
  function niceScale(min, max, count = 5, fixedMin = null, fixedMax = null) {
338
+ if (fixedMin !== null && !Number.isFinite(fixedMin)) fixedMin = null;
339
+ if (fixedMax !== null && !Number.isFinite(fixedMax)) fixedMax = null;
338
340
  let lo = _nullishCoalesce(fixedMin, () => ( min));
339
341
  let hi = _nullishCoalesce(fixedMax, () => ( max));
340
342
  if (!Number.isFinite(lo) || !Number.isFinite(hi)) {
343
+ fixedMin = fixedMax = null;
341
344
  lo = 0;
342
345
  hi = 1;
343
346
  }
@@ -347,17 +350,36 @@ function niceScale(min, max, count = 5, fixedMin = null, fixedMax = null) {
347
350
  if (fixedMin === null && lo !== 0) lo -= pad;
348
351
  if (fixedMax === null || lo === hi) hi += pad;
349
352
  }
350
- const step = niceStep(hi - lo, count);
351
- const niceMin = _nullishCoalesce(fixedMin, () => ( clean(Math.floor(lo / step + 1e-9) * step)));
352
- const niceMax = _nullishCoalesce(fixedMax, () => ( clean(Math.ceil(hi / step - 1e-9) * step)));
353
+ const span = hi - lo;
354
+ const step = Number.isFinite(span) ? niceStep(span, count) : niceStep(hi / 2 - lo / 2, count) * 2;
355
+ const finiteOr = (n2, fallback) => Number.isFinite(n2) ? n2 : fallback;
356
+ const niceMin = _nullishCoalesce(fixedMin, () => ( finiteOr(snap(Math.floor(lo / step + 1e-9) * step, step), lo)));
357
+ const niceMax = _nullishCoalesce(fixedMax, () => ( finiteOr(snap(Math.ceil(hi / step - 1e-9) * step, step), hi)));
358
+ const firstIndex = Math.ceil(niceMin / step - 1e-9);
359
+ const lastIndex = Math.floor(niceMax / step + 1e-9);
360
+ const n = Math.min(999, Math.max(-1, lastIndex - firstIndex));
353
361
  const ticks = [];
354
- const first = Math.ceil(niceMin / step - 1e-9) * step;
355
- for (let t = first, i = 0; t <= niceMax + step * 1e-9 && i < 1e3; t += step, i++) ticks.push(clean(t));
362
+ for (let i = 0; i <= n; i++) {
363
+ const t = snap((firstIndex + i) * step, step);
364
+ if (Number.isFinite(t) && t !== ticks[ticks.length - 1]) ticks.push(t);
365
+ }
356
366
  return { min: niceMin, max: niceMax, step, ticks };
357
367
  }
368
+ function snap(n, step) {
369
+ const c = clean(n);
370
+ return Math.abs(c - n) <= step * 1e-6 ? c : n;
371
+ }
358
372
  function stepDecimals(step) {
359
- if (!(step > 0) || step >= 1) return 0;
360
- return Math.min(6, Math.max(0, Math.ceil(-Math.log10(step) - 1e-9)));
373
+ if (!(step > 0) || !Number.isFinite(step)) return 0;
374
+ for (let d = 0; d < 6; d++) {
375
+ const scaled = step * Math.pow(10, d);
376
+ if (Math.abs(scaled - Math.round(scaled)) < 1e-9) return d;
377
+ }
378
+ return 6;
379
+ }
380
+ function magnitudeDecimals(value) {
381
+ if (!(value > 0) || value >= 1) return 0;
382
+ return Math.min(6, Math.max(0, Math.ceil(-Math.log10(value) - 1e-9)));
361
383
  }
362
384
  var COMPACT = [
363
385
  { value: 1e12, suffix: "T" },
@@ -379,7 +401,7 @@ function formatChartNumber(value, format = {}, fallbackDecimals) {
379
401
  const decimalsFor = (v) => {
380
402
  if (format.decimals !== void 0) return format.decimals;
381
403
  if (fallbackDecimals !== void 0) return fallbackDecimals;
382
- return Number.isInteger(v) ? 0 : Math.abs(v) < 1 ? Math.min(4, Math.max(2, stepDecimals(Math.abs(v)) + 1)) : 2;
404
+ return Number.isInteger(v) ? 0 : Math.abs(v) < 1 ? Math.min(4, Math.max(2, magnitudeDecimals(Math.abs(v)) + 1)) : 2;
383
405
  };
384
406
  const trim = (s) => format.decimals === void 0 && fallbackDecimals === void 0 && s.includes(".") ? s.replace(/\.?0+$/, "") : s;
385
407
  if (style === "percent") {
@@ -426,6 +448,20 @@ function sameChartSelection(a, b) {
426
448
  return true;
427
449
  }
428
450
  }
451
+ function contrastingTextColor(fill) {
452
+ let rgb = null;
453
+ const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(fill.trim());
454
+ if (hex) {
455
+ const h = hex[1].length === 3 ? [...hex[1]].map((c) => c + c).join("") : hex[1];
456
+ rgb = [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16));
457
+ } else {
458
+ const fn = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i.exec(fill.trim());
459
+ if (fn) rgb = fn.slice(1, 4).map(Number);
460
+ }
461
+ if (!rgb) return "#ffffff";
462
+ const luminance = (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255;
463
+ return luminance > 0.5 ? "#0f172a" : "#ffffff";
464
+ }
429
465
  var r2 = (n) => Math.round(n * 100) / 100;
430
466
  function curvePath(points, curve, map, moveFirst = true) {
431
467
  if (!points.length) return "";
@@ -583,8 +619,11 @@ function layoutChart(spec, options) {
583
619
  const xAxis = spec.xAxis;
584
620
  const yAxis = spec.yAxis;
585
621
  const titleBand = fs * 1.3 + 6;
622
+ const frozen = options.valueDomain;
623
+ const fixedMin = frozen && Number.isFinite(frozen.min) ? frozen.min : yAxis.min;
624
+ const fixedMax = frozen && Number.isFinite(frozen.max) ? frozen.max : yAxis.max;
586
625
  const valueLen0 = horizontal ? area.width : area.height;
587
- let scale = niceScale(lo, hi, Math.max(2, Math.min(10, Math.round(valueLen0 / 56))), yAxis.min, yAxis.max);
626
+ let scale = niceScale(lo, hi, Math.max(2, Math.min(10, Math.round(valueLen0 / 56))), fixedMin, fixedMax);
588
627
  const tickText = (t) => formatChartNumber(t, valueFormat, _nullishCoalesce(valueFormat.decimals, () => ( stepDecimals(scale.step))));
589
628
  const valueLabelWidth = Math.max(0, ...scale.ticks.map((t) => measure(tickText(t), fs)));
590
629
  const catLabelWidth = Math.max(0, ...spec.categories.map((c) => measure(c, fs)));
@@ -608,7 +647,7 @@ function layoutChart(spec, options) {
608
647
  width: Math.max(1, area.width - left - rightPad),
609
648
  height: Math.max(1, area.height - bottom - (horizontal ? 4 : fs / 2 + 2))
610
649
  };
611
- scale = niceScale(lo, hi, Math.max(2, Math.min(10, Math.round((horizontal ? plot.width : plot.height) / 56))), yAxis.min, yAxis.max);
650
+ scale = niceScale(lo, hi, Math.max(2, Math.min(10, Math.round((horizontal ? plot.width : plot.height) / 56))), fixedMin, fixedMax);
612
651
  const domain = [scale.min, scale.max];
613
652
  const valueRange = horizontal ? [plot.x, plot.x + plot.width] : [plot.y + plot.height, plot.y];
614
653
  const valuePx = (v) => linearMap(Math.min(scale.max, Math.max(scale.min, v)), domain, valueRange);
@@ -718,6 +757,8 @@ function layoutChart(spec, options) {
718
757
  if (selected) outline(`bar-${s.id}-${i}-selection`, { x: rect.x - 2, y: rect.y - 2, width: rect.width + 4, height: rect.height + 4 });
719
758
  if (spec.dataLabels) {
720
759
  const v = percent ? seg[1] - seg[0] : raw;
760
+ const text2 = dataLabel(v);
761
+ if (stacked && Math.abs(b - a) < (horizontal ? measure(text2, fs - 1) + 4 : fs + 1)) return;
721
762
  const end = valuePx(seg[1]);
722
763
  const outward = (seg[1] >= seg[0] ? 1 : -1) * (horizontal ? 1 : -1);
723
764
  const [lx, ly] = map({ c: c0 + cw / 2, v: stacked ? (a + b) / 2 : end + outward * 4 });
@@ -727,11 +768,11 @@ function layoutChart(spec, options) {
727
768
  role: "data-label",
728
769
  x: lx,
729
770
  y: ly,
730
- text: dataLabel(v),
771
+ text: text2,
731
772
  anchor: horizontal && !stacked ? outward > 0 ? "start" : "end" : "middle",
732
773
  baseline: horizontal || stacked ? "middle" : outward < 0 ? "bottom" : "top",
733
774
  size: fs - 1,
734
- fill: stacked ? "#ffffff" : theme.text,
775
+ fill: stacked ? contrastingTextColor(color) : theme.text,
735
776
  target
736
777
  });
737
778
  }
@@ -1015,6 +1056,20 @@ function chartToSvg(spec, options) {
1015
1056
  }
1016
1057
 
1017
1058
  // src/chart/controller.ts
1059
+ function sameChartJson(a, b) {
1060
+ if (a === b) return true;
1061
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return Number.isNaN(a) && Number.isNaN(b);
1062
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
1063
+ if (Array.isArray(a)) {
1064
+ const bb = b;
1065
+ return a.length === bb.length && a.every((v, i) => sameChartJson(v, bb[i]));
1066
+ }
1067
+ const ao = a;
1068
+ const bo = b;
1069
+ const keys = Object.keys(ao).filter((k) => ao[k] !== void 0);
1070
+ if (keys.length !== Object.keys(bo).filter((k) => bo[k] !== void 0).length) return false;
1071
+ return keys.every((k) => Object.prototype.hasOwnProperty.call(bo, k) && sameChartJson(ao[k], bo[k]));
1072
+ }
1018
1073
  var fail = (message, issues) => ({ ok: false, message, ...issues ? { issues } : {} });
1019
1074
  var ChartController = (_class = class {
1020
1075
 
@@ -1023,13 +1078,14 @@ var ChartController = (_class = class {
1023
1078
  __init2() {this.issues = []}
1024
1079
  __init3() {this.gestureKey = null}
1025
1080
  __init4() {this.gestureCount = 0}
1026
- __init5() {this.revision = 0}
1027
- __init6() {this.applying = false}
1081
+ __init5() {this.gestureStart = null}
1082
+ __init6() {this.revision = 0}
1083
+ __init7() {this.applying = false}
1028
1084
 
1029
- __init7() {this.listeners = /* @__PURE__ */ new Set()}
1085
+ __init8() {this.listeners = /* @__PURE__ */ new Set()}
1030
1086
 
1031
1087
 
1032
- constructor(options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);
1088
+ constructor(options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);
1033
1089
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
1034
1090
  this.onChange = options.onChange;
1035
1091
  const { spec, issues } = normalizeChartSpec(_nullishCoalesce(options.spec, () => ( {})));
@@ -1050,9 +1106,9 @@ var ChartController = (_class = class {
1050
1106
  // State
1051
1107
  // -------------------------------------------------------------------------
1052
1108
  /** The current snapshot (stable until the next change). */
1053
- __init8() {this.getState = () => this.state}
1109
+ __init9() {this.getState = () => this.state}
1054
1110
  /** Calls `listener` after every change. Returns an unsubscribe function. */
1055
- __init9() {this.subscribe = (listener) => {
1111
+ __init10() {this.subscribe = (listener) => {
1056
1112
  if (!this.unsubscribeHistory) this.connect();
1057
1113
  this.listeners.add(listener);
1058
1114
  return () => {
@@ -1130,7 +1186,14 @@ var ChartController = (_class = class {
1130
1186
  }
1131
1187
  commit(label, next, select) {
1132
1188
  const before = this.spec;
1189
+ const hadIssues = this.issues.length > 0;
1133
1190
  this.issues = [];
1191
+ if (sameChartJson(before, next)) {
1192
+ const reselect = select !== void 0 && !sameChartSelection(this.selection, select) && !(select === null && this.selection === null);
1193
+ if (reselect) this.selection = select;
1194
+ if (reselect || hadIssues) this.notify(false);
1195
+ return;
1196
+ }
1134
1197
  const command = {
1135
1198
  name: label,
1136
1199
  tag: "chart",
@@ -1486,12 +1549,16 @@ var ChartController = (_class = class {
1486
1549
  this.history.endGesture();
1487
1550
  this.gestureKey = key;
1488
1551
  this.gestureCount++;
1552
+ this.gestureStart = { spec: this.spec, mark: this.history.mark() };
1489
1553
  this.notify(false);
1490
1554
  }
1491
1555
  endGesture() {
1492
1556
  if (this.gestureKey === null) return;
1493
1557
  this.gestureKey = null;
1494
1558
  this.history.endGesture();
1559
+ const start = this.gestureStart;
1560
+ this.gestureStart = null;
1561
+ if (start && sameChartJson(start.spec, this.spec)) this.history.revertTo(start.mark);
1495
1562
  this.notify(false);
1496
1563
  }
1497
1564
  // -------------------------------------------------------------------------
@@ -1857,7 +1924,9 @@ function startChartPointDrag(controller, scene, target, start, options = {}) {
1857
1924
  if (startAt === void 0) return null;
1858
1925
  controller.beginGesture(`drag:${seriesId}:${index}`);
1859
1926
  let ended = false;
1927
+ const [min, max] = scene.valueScale.domain;
1860
1928
  return {
1929
+ valueDomain: { min, max },
1861
1930
  move(point) {
1862
1931
  if (ended) return;
1863
1932
  const at = chartValueAtPoint(scene, point);
@@ -1980,5 +2049,6 @@ function describeChartSelection(controller, selection = controller.getState().se
1980
2049
 
1981
2050
 
1982
2051
 
1983
- exports.CHART_CURVES = CHART_CURVES; exports.CHART_LEGEND_POSITIONS = CHART_LEGEND_POSITIONS; exports.CHART_NUMBER_STYLES = CHART_NUMBER_STYLES; exports.CHART_ORIENTATIONS = CHART_ORIENTATIONS; exports.CHART_SERIES_TYPES = CHART_SERIES_TYPES; exports.CHART_STACKINGS = CHART_STACKINGS; exports.CHART_TYPES = CHART_TYPES; exports.CHART_TYPE_LABELS = CHART_TYPE_LABELS; exports.CSS_VARIABLE_CHART_THEME = CSS_VARIABLE_CHART_THEME; exports.ChartController = ChartController; exports.DEFAULT_CHART_PALETTE = DEFAULT_CHART_PALETTE; exports.DEFAULT_CHART_THEME = DEFAULT_CHART_THEME; exports.canDragChartPoint = canDragChartPoint; exports.chartClientToScene = chartClientToScene; exports.chartColorInputValue = chartColorInputValue; exports.chartDataFromTable = chartDataFromTable; exports.chartDataToTable = chartDataToTable; exports.chartSceneToSvg = chartSceneToSvg; exports.chartSeriesColor = chartSeriesColor; exports.chartSeriesType = chartSeriesType; exports.chartSliceColor = chartSliceColor; exports.chartTextBaseline = chartTextBaseline; exports.chartTextTransform = chartTextTransform; exports.chartToSvg = chartToSvg; exports.chartValueAtPoint = chartValueAtPoint; exports.cloneChartSeries = cloneChartSeries; exports.cloneChartSpec = cloneChartSpec; exports.createChartCommands = createChartCommands; exports.createChartController = createChartController; exports.createChartSpec = createChartSpec; exports.defaultChartAxis = defaultChartAxis; exports.describeChartSelection = describeChartSelection; exports.estimateChartTextWidth = estimateChartTextWidth; exports.formatChartNumber = formatChartNumber; exports.handleChartKey = handleChartKey; exports.isCartesianChart = isCartesianChart; exports.layoutChart = layoutChart; exports.linearMap = linearMap; exports.niceScale = niceScale; exports.niceStep = niceStep; exports.normalizeChartAxis = normalizeChartAxis; exports.normalizeChartSpec = normalizeChartSpec; exports.normalizeChartValues = normalizeChartValues; exports.parseChartValue = parseChartValue; exports.sameChartSelection = sameChartSelection; exports.snapChartValue = snapChartValue; exports.startChartPointDrag = startChartPointDrag; exports.stepDecimals = stepDecimals; exports.uniqueChartSeriesId = uniqueChartSeriesId;
2052
+
2053
+ exports.CHART_CURVES = CHART_CURVES; exports.CHART_LEGEND_POSITIONS = CHART_LEGEND_POSITIONS; exports.CHART_NUMBER_STYLES = CHART_NUMBER_STYLES; exports.CHART_ORIENTATIONS = CHART_ORIENTATIONS; exports.CHART_SERIES_TYPES = CHART_SERIES_TYPES; exports.CHART_STACKINGS = CHART_STACKINGS; exports.CHART_TYPES = CHART_TYPES; exports.CHART_TYPE_LABELS = CHART_TYPE_LABELS; exports.CSS_VARIABLE_CHART_THEME = CSS_VARIABLE_CHART_THEME; exports.ChartController = ChartController; exports.DEFAULT_CHART_PALETTE = DEFAULT_CHART_PALETTE; exports.DEFAULT_CHART_THEME = DEFAULT_CHART_THEME; exports.canDragChartPoint = canDragChartPoint; exports.chartClientToScene = chartClientToScene; exports.chartColorInputValue = chartColorInputValue; exports.chartDataFromTable = chartDataFromTable; exports.chartDataToTable = chartDataToTable; exports.chartSceneToSvg = chartSceneToSvg; exports.chartSeriesColor = chartSeriesColor; exports.chartSeriesType = chartSeriesType; exports.chartSliceColor = chartSliceColor; exports.chartTextBaseline = chartTextBaseline; exports.chartTextTransform = chartTextTransform; exports.chartToSvg = chartToSvg; exports.chartValueAtPoint = chartValueAtPoint; exports.cloneChartSeries = cloneChartSeries; exports.cloneChartSpec = cloneChartSpec; exports.contrastingTextColor = contrastingTextColor; exports.createChartCommands = createChartCommands; exports.createChartController = createChartController; exports.createChartSpec = createChartSpec; exports.defaultChartAxis = defaultChartAxis; exports.describeChartSelection = describeChartSelection; exports.estimateChartTextWidth = estimateChartTextWidth; exports.formatChartNumber = formatChartNumber; exports.handleChartKey = handleChartKey; exports.isCartesianChart = isCartesianChart; exports.layoutChart = layoutChart; exports.linearMap = linearMap; exports.niceScale = niceScale; exports.niceStep = niceStep; exports.normalizeChartAxis = normalizeChartAxis; exports.normalizeChartSpec = normalizeChartSpec; exports.normalizeChartValues = normalizeChartValues; exports.parseChartValue = parseChartValue; exports.sameChartSelection = sameChartSelection; exports.snapChartValue = snapChartValue; exports.startChartPointDrag = startChartPointDrag; exports.stepDecimals = stepDecimals; exports.uniqueChartSeriesId = uniqueChartSeriesId;
1984
2054
  //# sourceMappingURL=index.cjs.map