@pushwoosh/kit-charts 0.0.2 → 0.0.3

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.
@@ -11,10 +11,9 @@ export function Tooltip({
11
11
  pointer,
12
12
  children
13
13
  }) {
14
- var _options$style, _options$style2, _options$style3, _options$style4;
15
14
  const ref = useRef(null);
16
- const callout = ((options === null || options === void 0 ? void 0 : options.shape) ?? 'callout') === 'callout' && !(options !== null && options !== void 0 && options.followPointer);
17
- const target = options !== null && options !== void 0 && options.followPointer ? pointer : at;
15
+ const callout = (options?.shape ?? 'callout') === 'callout' && !options?.followPointer;
16
+ const target = options?.followPointer ? pointer : at;
18
17
  const [placement, setPlacement] = useState({
19
18
  x: target.x + TOOLTIP_CURSOR_OFFSET,
20
19
  y: target.y + TOOLTIP_CURSOR_OFFSET,
@@ -57,25 +56,25 @@ export function Tooltip({
57
56
  if (typeof document === 'undefined') {
58
57
  return null;
59
58
  }
60
- const shadow = options === null || options === void 0 ? void 0 : options.shadow;
59
+ const shadow = options?.shadow;
61
60
  // A table needs room and columns; the dark hint chrome reads badly at that size,
62
61
  // so the tabular tooltip is a light card unless the caller says otherwise.
63
- const tabular = Boolean(options === null || options === void 0 ? void 0 : options.table);
64
- const background = (options === null || options === void 0 ? void 0 : options.backgroundColor) ?? (tabular ? DEFAULTS.surface : DEFAULTS.tooltipSurface);
65
- const color = (options === null || options === void 0 || (_options$style = options.style) === null || _options$style === void 0 ? void 0 : _options$style.color) ?? (tabular ? DEFAULTS.text : DEFAULTS.tooltipText);
62
+ const tabular = Boolean(options?.table);
63
+ const background = options?.backgroundColor ?? (tabular ? DEFAULTS.surface : DEFAULTS.tooltipSurface);
64
+ const color = options?.style?.color ?? (tabular ? DEFAULTS.text : DEFAULTS.tooltipText);
66
65
  return createPortal(_jsxs(TooltipBox, {
67
66
  ref: ref,
68
67
  role: "tooltip",
69
68
  "$x": placement.x,
70
69
  "$y": placement.y,
71
- "$padding": (options === null || options === void 0 ? void 0 : options.padding) ?? TOOLTIP_PADDING,
72
- "$radius": (options === null || options === void 0 ? void 0 : options.borderRadius) ?? TOOLTIP_RADIUS,
73
- "$borderColor": (options === null || options === void 0 ? void 0 : options.borderColor) ?? (tabular ? DEFAULTS.border : 'transparent'),
70
+ "$padding": options?.padding ?? TOOLTIP_PADDING,
71
+ "$radius": options?.borderRadius ?? TOOLTIP_RADIUS,
72
+ "$borderColor": options?.borderColor ?? (tabular ? DEFAULTS.border : 'transparent'),
74
73
  "$background": background,
75
74
  "$color": color,
76
- "$fontFamily": (options === null || options === void 0 || (_options$style2 = options.style) === null || _options$style2 === void 0 ? void 0 : _options$style2.fontFamily) ?? DEFAULTS.fontFamily,
77
- "$fontSize": (options === null || options === void 0 || (_options$style3 = options.style) === null || _options$style3 === void 0 ? void 0 : _options$style3.fontSize) ?? DEFAULTS.fontSize,
78
- "$whiteSpace": (options === null || options === void 0 || (_options$style4 = options.style) === null || _options$style4 === void 0 ? void 0 : _options$style4.whiteSpace) ?? 'nowrap',
75
+ "$fontFamily": options?.style?.fontFamily ?? DEFAULTS.fontFamily,
76
+ "$fontSize": options?.style?.fontSize ?? DEFAULTS.fontSize,
77
+ "$whiteSpace": options?.style?.whiteSpace ?? 'nowrap',
79
78
  "$shadow": shadow ? `${shadow.offsetX ?? 0}px ${shadow.offsetY ?? 2}px ${shadow.width ?? 8}px ` + `${shadow.color ?? SHADOW_COLOR}` : DEFAULT_SHADOW,
80
79
  children: [children, placement.tailX === null ? null : _jsx(Tail, {
81
80
  "$left": placement.tailX,
@@ -15,7 +15,7 @@ const isPointObject = point => typeof point === 'object' && point !== null && !A
15
15
  /** What a hit or a tooltip should show for the point's x: its category, else its x. */
16
16
  export const pointKey = point => point.band ?? point.name ?? point.x;
17
17
  export function normalizePoints(data, seriesName = 'series') {
18
- if (!(data !== null && data !== void 0 && data.length)) {
18
+ if (!data?.length) {
19
19
  return [];
20
20
  }
21
21
  return data.map((point, index) => {
@@ -24,8 +24,7 @@ export function projectSeries(ctx) {
24
24
  return [];
25
25
  }
26
26
  return ctx.points.map((point, index) => {
27
- var _ctx$stacked;
28
- const stacked = (_ctx$stacked = ctx.stacked) === null || _ctx$stacked === void 0 ? void 0 : _ctx$stacked[index];
27
+ const stacked = ctx.stacked?.[index];
29
28
  if (point.y === null || ctx.hiddenPoints.has(point.index) || ctx.stacked && !stacked) {
30
29
  return null;
31
30
  }
@@ -1,16 +1,16 @@
1
1
  import { measureText, textHeight } from './measure-text';
2
2
  import { LEGEND_BLOCK_GAP, LEGEND_ROW_GAP } from './metrics';
3
3
  const itemWidth = (item, options) => {
4
- const symbolSize = (options === null || options === void 0 ? void 0 : options.symbolHeight) ?? 8;
5
- return symbolSize + ((options === null || options === void 0 ? void 0 : options.symbolPadding) ?? 6) + measureText(item.label, options === null || options === void 0 ? void 0 : options.itemStyle);
4
+ const symbolSize = options?.symbolHeight ?? 8;
5
+ return symbolSize + (options?.symbolPadding ?? 6) + measureText(item.label, options?.itemStyle);
6
6
  };
7
7
  export function measureLegend(items, options) {
8
- const rowHeight = textHeight(options === null || options === void 0 ? void 0 : options.itemStyle);
9
- const vertical = ((options === null || options === void 0 ? void 0 : options.layout) ?? 'horizontal') === 'vertical';
8
+ const rowHeight = textHeight(options?.itemStyle);
9
+ const vertical = (options?.layout ?? 'horizontal') === 'vertical';
10
10
  // A middle-aligned legend has nowhere to go above or below the plot, so it
11
11
  // takes a column at the side instead — the same trade Highcharts makes.
12
- const beside = ((options === null || options === void 0 ? void 0 : options.verticalAlign) ?? 'bottom') === 'middle';
13
- if (!items.length || (options === null || options === void 0 ? void 0 : options.enabled) === false) {
12
+ const beside = (options?.verticalAlign ?? 'bottom') === 'middle';
13
+ if (!items.length || options?.enabled === false) {
14
14
  return {
15
15
  width: 0,
16
16
  height: 0,
@@ -43,10 +43,10 @@ export function measureLegend(items, options) {
43
43
  */
44
44
  export function layoutLegend(items, options, plot, size, spacing) {
45
45
  const metrics = measureLegend(items, options);
46
- const distance = (options === null || options === void 0 ? void 0 : options.itemDistance) ?? 16;
47
- const align = (options === null || options === void 0 ? void 0 : options.align) ?? 'left';
48
- const shiftX = (options === null || options === void 0 ? void 0 : options.x) ?? 0;
49
- const shiftY = (options === null || options === void 0 ? void 0 : options.y) ?? 0;
46
+ const distance = options?.itemDistance ?? 16;
47
+ const align = options?.align ?? 'left';
48
+ const shiftX = options?.x ?? 0;
49
+ const shiftY = options?.y ?? 0;
50
50
  const widths = items.map(item => itemWidth(item, options));
51
51
  const rowStep = metrics.rowHeight + LEGEND_ROW_GAP;
52
52
  if (metrics.vertical || metrics.beside) {
@@ -65,7 +65,7 @@ export function layoutLegend(items, options, plot, size, spacing) {
65
65
  if (metrics.beside) {
66
66
  return plot.y + (plot.height - block) / 2 + metrics.rowHeight / 2;
67
67
  }
68
- return ((options === null || options === void 0 ? void 0 : options.verticalAlign) ?? 'bottom') === 'top' ? plot.y - metrics.height + metrics.rowHeight : plot.y + plot.height + metrics.rowHeight + LEGEND_ROW_GAP;
68
+ return (options?.verticalAlign ?? 'bottom') === 'top' ? plot.y - metrics.height + metrics.rowHeight : plot.y + plot.height + metrics.rowHeight + LEGEND_ROW_GAP;
69
69
  })();
70
70
  return {
71
71
  metrics,
@@ -88,7 +88,7 @@ export function layoutLegend(items, options, plot, size, spacing) {
88
88
  // Sit on the last row inside the chart's bottom spacing, not on the canvas
89
89
  // edge: flush with the edge, the row's own descenders and its focus ring are
90
90
  // cut off by the viewport.
91
- const y = ((options === null || options === void 0 ? void 0 : options.verticalAlign) ?? 'bottom') === 'top' ? plot.y - metrics.height + metrics.rowHeight : size.height - spacing[2] - metrics.rowHeight / 2;
91
+ const y = (options?.verticalAlign ?? 'bottom') === 'top' ? plot.y - metrics.height + metrics.rowHeight : size.height - spacing[2] - metrics.rowHeight / 2;
92
92
  const offsets = widths.reduce((acc, _width, index) => {
93
93
  acc.push(index === 0 ? startX : acc[index - 1] + widths[index - 1] + distance);
94
94
  return acc;
@@ -17,9 +17,9 @@ export const DEFAULT_FONT_FAMILY = FontFamily.MAIN;
17
17
  export const DEFAULT_TITLE_FONT_SIZE = FontSize.HEADING2;
18
18
  export const DEFAULT_SUBTITLE_FONT_SIZE = FontSize.SMALL;
19
19
  export const toCssFont = style => {
20
- const weight = (style === null || style === void 0 ? void 0 : style.fontWeight) ?? 'normal';
21
- const size = (style === null || style === void 0 ? void 0 : style.fontSize) ?? DEFAULT_FONT_SIZE;
22
- const family = (style === null || style === void 0 ? void 0 : style.fontFamily) ?? DEFAULT_FONT_FAMILY;
20
+ const weight = style?.fontWeight ?? 'normal';
21
+ const size = style?.fontSize ?? DEFAULT_FONT_SIZE;
22
+ const family = style?.fontFamily ?? DEFAULT_FONT_FAMILY;
23
23
  return `${weight} ${size} ${family}`;
24
24
  };
25
25
  const parseFontSize = font => {
@@ -5,14 +5,8 @@ const ANCHOR = {
5
5
  center: 'middle',
6
6
  right: 'end'
7
7
  };
8
- const shown = title => {
9
- var _title$style;
10
- return Boolean(title === null || title === void 0 ? void 0 : title.text) && (title === null || title === void 0 || (_title$style = title.style) === null || _title$style === void 0 ? void 0 : _title$style.display) !== 'none';
11
- };
12
- const sizeOf = (title, fallback) => {
13
- var _title$style2;
14
- return (title === null || title === void 0 || (_title$style2 = title.style) === null || _title$style2 === void 0 ? void 0 : _title$style2.fontSize) ?? fallback;
15
- };
8
+ const shown = title => Boolean(title?.text) && title?.style?.display !== 'none';
9
+ const sizeOf = (title, fallback) => title?.style?.fontSize ?? fallback;
16
10
  export function layoutTitles(options, size, spacing) {
17
11
  const [spaceTop, spaceRight,, spaceLeft] = spacing;
18
12
  const {
@@ -22,13 +16,13 @@ export function layoutTitles(options, size, spacing) {
22
16
  const entries = [];
23
17
  const push = (key, source, fontSize, top) => {
24
18
  const height = textHeight({
25
- ...(source === null || source === void 0 ? void 0 : source.style),
19
+ ...source?.style,
26
20
  fontSize
27
21
  });
28
22
  if (!shown(source)) {
29
23
  return 0;
30
24
  }
31
- const align = (source === null || source === void 0 ? void 0 : source.align) ?? 'left';
25
+ const align = source?.align ?? 'left';
32
26
  const x = (() => {
33
27
  if (align === 'center') {
34
28
  return size.width / 2;
@@ -38,10 +32,10 @@ export function layoutTitles(options, size, spacing) {
38
32
  }
39
33
  return spaceLeft;
40
34
  })();
41
- const text = (source === null || source === void 0 ? void 0 : source.text) ?? '';
35
+ const text = source?.text ?? '';
42
36
  const baseline = top + height;
43
37
  const width = measureText(text, {
44
- ...(source === null || source === void 0 ? void 0 : source.style),
38
+ ...source?.style,
45
39
  fontSize
46
40
  });
47
41
  // The icon follows the text, wherever the anchor put it.
@@ -61,9 +55,9 @@ export function layoutTitles(options, size, spacing) {
61
55
  // `y` is a baseline, so the first line starts one line height down.
62
56
  y: baseline,
63
57
  anchor: ANCHOR[align],
64
- style: source === null || source === void 0 ? void 0 : source.style,
58
+ style: source?.style,
65
59
  fontSize,
66
- hint: source !== null && source !== void 0 && source.hint ? {
60
+ hint: source?.hint ? {
67
61
  text: source.hint,
68
62
  x: textEnd + HINT_ICON_GAP + HINT_ICON_SIZE / 2,
69
63
  y: baseline - textHeight({
@@ -76,7 +70,7 @@ export function layoutTitles(options, size, spacing) {
76
70
  const titleHeight = push('title', title, sizeOf(title, DEFAULT_TITLE_FONT_SIZE), spaceTop);
77
71
  // The caption shares the title's row, pushed to the far edge: the note about
78
72
  // what the figures cover belongs beside the name, not under it.
79
- if (title !== null && title !== void 0 && title.caption) {
73
+ if (title?.caption) {
80
74
  push('caption', {
81
75
  text: title.caption,
82
76
  align: 'right',
@@ -18,8 +18,7 @@ const DEFAULT_SPACING = [10, 10, 15, 10];
18
18
  const isBand = scale => scale.kind === 'band';
19
19
  const isContinuous = scale => scale.kind === 'linear' || scale.kind === 'time';
20
20
  function tickLabel(value, axis, timePattern, index, count, kind, numberFormat) {
21
- var _axis$labels, _axis$labels2;
22
- const formatter = (_axis$labels = axis.labels) === null || _axis$labels === void 0 ? void 0 : _axis$labels.formatter;
21
+ const formatter = axis.labels?.formatter;
23
22
  if (formatter) {
24
23
  const node = formatter({
25
24
  value,
@@ -37,7 +36,7 @@ function tickLabel(value, axis, timePattern, index, count, kind, numberFormat) {
37
36
  }
38
37
  return '';
39
38
  }
40
- if ((_axis$labels2 = axis.labels) !== null && _axis$labels2 !== void 0 && _axis$labels2.format) {
39
+ if (axis.labels?.format) {
41
40
  return applyValueFormat(axis.labels.format, value);
42
41
  }
43
42
  if (typeof value === 'number') {
@@ -46,8 +45,7 @@ function tickLabel(value, axis, timePattern, index, count, kind, numberFormat) {
46
45
  return String(value);
47
46
  }
48
47
  function collectCategories(axis, series, numeric = false) {
49
- var _axis$categories;
50
- if ((_axis$categories = axis.categories) !== null && _axis$categories !== void 0 && _axis$categories.length) {
48
+ if (axis.categories?.length) {
51
49
  return [...axis.categories];
52
50
  }
53
51
  const seen = [];
@@ -104,11 +102,10 @@ function buildXScale(axis, kind, extent, categories, range, hint) {
104
102
  });
105
103
  }
106
104
  function buildYScale(axis, extent, range, hint, linked) {
107
- var _axis$categories2;
108
- if ((_axis$categories2 = axis.categories) !== null && _axis$categories2 !== void 0 && _axis$categories2.length) {
105
+ if (axis.categories?.length) {
109
106
  return createBandScale({
110
107
  domain: [...axis.categories],
111
- range: (linked === null || linked === void 0 ? void 0 : linked.kind) === 'band' ? linked.range() : range,
108
+ range: linked?.kind === 'band' ? linked.range() : range,
112
109
  paddingInner: 0,
113
110
  paddingOuter: 0,
114
111
  // A y axis counts up from the bottom; `reversed` puts the first row on top.
@@ -133,13 +130,12 @@ function buildYScale(axis, extent, range, hint, linked) {
133
130
  });
134
131
  }
135
132
  function buildColorScale(options, extent, colors) {
136
- var _axis$stops;
137
133
  const axis = options.colorAxis;
138
134
  if (!axis) {
139
135
  return undefined;
140
136
  }
141
137
  const [min, max] = settled(extent, [0, 1]);
142
- const stops = (_axis$stops = axis.stops) !== null && _axis$stops !== void 0 && _axis$stops.length ? axis.stops : [[0, DEFAULT_SURFACE_COLOR], [1, colorAt(colors, 0)]];
138
+ const stops = axis.stops?.length ? axis.stops : [[0, DEFAULT_SURFACE_COLOR], [1, colorAt(colors, 0)]];
143
139
  return createColorScale({
144
140
  domain: [axis.min ?? min, axis.max ?? max],
145
141
  stops
@@ -151,15 +147,14 @@ const EXPLICIT_ANCHOR = {
151
147
  right: 'end'
152
148
  };
153
149
  function tickAnchor(label, position, axis, bounds) {
154
- var _axis$labels3, _axis$labels4;
155
- const explicit = ((_axis$labels3 = axis.labels) === null || _axis$labels3 === void 0 ? void 0 : _axis$labels3.align) && EXPLICIT_ANCHOR[axis.labels.align];
150
+ const explicit = axis.labels?.align && EXPLICIT_ANCHOR[axis.labels.align];
156
151
  if (explicit) {
157
152
  return explicit;
158
153
  }
159
154
  if (!bounds || !label) {
160
155
  return 'middle';
161
156
  }
162
- const half = measureText(label, (_axis$labels4 = axis.labels) === null || _axis$labels4 === void 0 ? void 0 : _axis$labels4.style) / 2;
157
+ const half = measureText(label, axis.labels?.style) / 2;
163
158
  if (position - half < bounds[0]) {
164
159
  return 'start';
165
160
  }
@@ -174,11 +169,10 @@ function tickAnchor(label, position, axis, bounds) {
174
169
  * axis readable instead of turning it into a smear.
175
170
  */
176
171
  function dropOverlapping(ticks, axis, along) {
177
- var _axis$labels5, _axis$labels6;
178
- if (((_axis$labels5 = axis.labels) === null || _axis$labels5 === void 0 ? void 0 : _axis$labels5.allowOverlap) === true || ticks.length < 2) {
172
+ if (axis.labels?.allowOverlap === true || ticks.length < 2) {
179
173
  return ticks;
180
174
  }
181
- const style = (_axis$labels6 = axis.labels) === null || _axis$labels6 === void 0 ? void 0 : _axis$labels6.style;
175
+ const style = axis.labels?.style;
182
176
  // A y axis counts upwards, so its tick positions descend. Comparing distance
183
177
  // rather than order keeps one rule for both directions.
184
178
  let kept = null;
@@ -201,9 +195,8 @@ function dropOverlapping(ticks, axis, along) {
201
195
  });
202
196
  }
203
197
  function axisTicks(axis, scale, hint, timePattern, along, numberFormat, bounds) {
204
- var _axis$tickPositions;
205
198
  let values;
206
- if ((_axis$tickPositions = axis.tickPositions) !== null && _axis$tickPositions !== void 0 && _axis$tickPositions.length) {
199
+ if (axis.tickPositions?.length) {
207
200
  values = [...axis.tickPositions];
208
201
  } else if (isBand(scale)) {
209
202
  values = scale.ticks();
@@ -218,7 +211,6 @@ function axisTicks(axis, scale, hint, timePattern, along, numberFormat, bounds)
218
211
  }
219
212
  const offset = isBand(scale) ? scale.bandwidth() / 2 : 0;
220
213
  const built = values.map((value, index) => {
221
- var _axis$labels7, _axis$labels7$hint;
222
214
  const position = (isBand(scale) ? scale.map(String(value)) : scale.map(Number(value))) + offset;
223
215
  const label = tickLabel(value, axis, timePattern, index, values.length, scale.kind, numberFormat);
224
216
  return {
@@ -226,7 +218,7 @@ function axisTicks(axis, scale, hint, timePattern, along, numberFormat, bounds)
226
218
  position,
227
219
  label,
228
220
  anchor: tickAnchor(label, position, axis, bounds),
229
- hint: (_axis$labels7 = axis.labels) === null || _axis$labels7 === void 0 || (_axis$labels7$hint = _axis$labels7.hint) === null || _axis$labels7$hint === void 0 ? void 0 : _axis$labels7$hint.call(_axis$labels7, {
221
+ hint: axis.labels?.hint?.({
230
222
  value,
231
223
  index,
232
224
  isFirst: index === 0,
@@ -272,8 +264,7 @@ export function buildChartModel({
272
264
  renderers,
273
265
  hidden
274
266
  }) {
275
- var _options$colors, _xAxisOptions$categor, _resolved$, _options$chart, _xAxisOptions$labels, _xAxisOptions$labels2, _xAxisOptions$labels3, _xAxisOptions$labels4, _options$legend, _options$legend2, _yAxes$, _yAxes$2, _yAxes$3, _yAxes$4, _options$legend3, _options$chart2, _options$chart3, _options$chart4, _options$chart5;
276
- const colors = (_options$colors = options.colors) !== null && _options$colors !== void 0 && _options$colors.length ? options.colors : DEFAULT_COLORS;
267
+ const colors = options.colors?.length ? options.colors : DEFAULT_COLORS;
277
268
  const numberFormat = options.numberFormat ?? String;
278
269
  const {
279
270
  xAxes,
@@ -292,8 +283,8 @@ export function buildChartModel({
292
283
  index,
293
284
  color: series.color ?? colorAt(colors, index),
294
285
  points: normalizePoints(series.data, series.name ?? `series ${index}`),
295
- hiddenPoints: new Set(normalizePoints(series.data, series.name ?? `series ${index}`).filter(point => hidden === null || hidden === void 0 ? void 0 : hidden.has(hiddenKey(index, point.index))).map(point => point.index)),
296
- hidden: !isSeriesVisible(series) || ((hidden === null || hidden === void 0 ? void 0 : hidden.has(hiddenKey(index))) ?? false)
286
+ hiddenPoints: new Set(normalizePoints(series.data, series.name ?? `series ${index}`).filter(point => hidden?.has(hiddenKey(index, point.index))).map(point => point.index)),
287
+ hidden: !isSeriesVisible(series) || (hidden?.has(hiddenKey(index)) ?? false)
297
288
  });
298
289
  });
299
290
  // Everything that shapes the chart — domains, stacks, column slots — comes from
@@ -303,12 +294,12 @@ export function buildChartModel({
303
294
  // the series being switched off would jump there before it finished fading, so
304
295
  // the chart holds the shape it had until something is switched back on.
305
296
  const resolved = shown.length ? shown : all;
306
- const explicitCategories = Boolean((_xAxisOptions$categor = xAxisOptions.categories) === null || _xAxisOptions$categor === void 0 ? void 0 : _xAxisOptions$categor.length);
297
+ const explicitCategories = Boolean(xAxisOptions.categories?.length);
307
298
  const axisless = resolved.length > 0 && resolved.every(({
308
299
  renderer
309
300
  }) => renderer.scales.x === 'none');
310
301
  const labelled = collectCategories(xAxisOptions, resolved);
311
- const rendererXKind = (_resolved$ = resolved[0]) === null || _resolved$ === void 0 ? void 0 : _resolved$.renderer.scales.x;
302
+ const rendererXKind = resolved[0]?.renderer.scales.x;
312
303
  const xKind = (() => {
313
304
  if (xAxisOptions.type === 'datetime') {
314
305
  return 'time';
@@ -365,7 +356,7 @@ export function buildChartModel({
365
356
  grow(xExtent, point.x);
366
357
  }
367
358
  grow(colorExtent, point.value);
368
- const cumulative = stacked === null || stacked === void 0 ? void 0 : stacked[pointIndex];
359
+ const cumulative = stacked?.[pointIndex];
369
360
  if (cumulative) {
370
361
  grow(yExtents[yIndex], cumulative.from);
371
362
  grow(yExtents[yIndex], cumulative.to);
@@ -399,16 +390,16 @@ export function buildChartModel({
399
390
  max: acc.x.max + pad
400
391
  } : acc.x;
401
392
  const yDomains = acc.y;
402
- const spacing = ((_options$chart = options.chart) === null || _options$chart === void 0 ? void 0 : _options$chart.spacing) ?? DEFAULT_SPACING;
393
+ const spacing = options.chart?.spacing ?? DEFAULT_SPACING;
403
394
  const [spaceTop, spaceRight, spaceBottom, spaceLeft] = spacing;
404
395
  const labelsOnTop = xAxisOptions.opposite === true;
405
- const labelStyle = (_xAxisOptions$labels = xAxisOptions.labels) === null || _xAxisOptions$labels === void 0 ? void 0 : _xAxisOptions$labels.style;
396
+ const labelStyle = xAxisOptions.labels?.style;
406
397
  // A tick label is drawn a tick's length plus a fixed drop away from the axis
407
398
  // line, and `labels.y` pushes it further still. Reserving only its text height
408
399
  // leaves it hanging over the chart edge, where the svg clips it.
409
- const xLabelPush = labelsOnTop ? Math.max(0, -(((_xAxisOptions$labels2 = xAxisOptions.labels) === null || _xAxisOptions$labels2 === void 0 ? void 0 : _xAxisOptions$labels2.y) ?? 0)) : Math.max(0, ((_xAxisOptions$labels3 = xAxisOptions.labels) === null || _xAxisOptions$labels3 === void 0 ? void 0 : _xAxisOptions$labels3.y) ?? 0);
410
- const xLabelHeight = axisless || ((_xAxisOptions$labels4 = xAxisOptions.labels) === null || _xAxisOptions$labels4 === void 0 ? void 0 : _xAxisOptions$labels4.enabled) === false ? 0 : textHeight(labelStyle) + (xAxisOptions.tickLength ?? 0) + (labelsOnTop ? AXIS_LABEL_GAP : X_AXIS_LABEL_DROP) + xLabelPush;
411
- const legendEnabled = ((_options$legend = options.legend) === null || _options$legend === void 0 ? void 0 : _options$legend.enabled) !== false;
400
+ const xLabelPush = labelsOnTop ? Math.max(0, -(xAxisOptions.labels?.y ?? 0)) : Math.max(0, xAxisOptions.labels?.y ?? 0);
401
+ const xLabelHeight = axisless || xAxisOptions.labels?.enabled === false ? 0 : textHeight(labelStyle) + (xAxisOptions.tickLength ?? 0) + (labelsOnTop ? AXIS_LABEL_GAP : X_AXIS_LABEL_DROP) + xLabelPush;
402
+ const legendEnabled = options.legend?.enabled !== false;
412
403
  const legend = legendEnabled ? all.flatMap(({
413
404
  renderer,
414
405
  options: series,
@@ -419,59 +410,47 @@ export function buildChartModel({
419
410
  return items.map((item, ordinal) => ({
420
411
  ...item,
421
412
  color: item.color || colorAt(colors, items.length > 1 ? ordinal : index),
422
- visible: !off && !(hidden !== null && hidden !== void 0 && hidden.has(hiddenKey(index, item.pointIndex)))
413
+ visible: !off && !hidden?.has(hiddenKey(index, item.pointIndex))
423
414
  }));
424
415
  }) : [];
425
416
  const legendMetrics = measureLegend(legend, options.legend);
426
417
  const legendHeight = legendMetrics.height;
427
- const legendAtTop = (((_options$legend2 = options.legend) === null || _options$legend2 === void 0 ? void 0 : _options$legend2.verticalAlign) ?? 'bottom') === 'top';
418
+ const legendAtTop = (options.legend?.verticalAlign ?? 'bottom') === 'top';
428
419
  const titles = layoutTitles(options, size, spacing);
429
420
  const titleHeight = titles.height;
430
- const yLabelsShown = !axisless && ((_yAxes$ = yAxes[0]) === null || _yAxes$ === void 0 ? void 0 : _yAxes$.visible) !== false && ((_yAxes$2 = yAxes[0]) === null || _yAxes$2 === void 0 || (_yAxes$2 = _yAxes$2.labels) === null || _yAxes$2 === void 0 ? void 0 : _yAxes$2.enabled) !== false;
421
+ const yLabelsShown = !axisless && yAxes[0]?.visible !== false && yAxes[0]?.labels?.enabled !== false;
431
422
  const markerReach = resolved.some(({
432
423
  options: series
433
- }) => {
434
- var _series$marker;
435
- return ((_series$marker = series.marker) === null || _series$marker === void 0 ? void 0 : _series$marker.enabled) !== false;
436
- }) ? MARKER_REACH : 0;
424
+ }) => series.marker?.enabled !== false) ? MARKER_REACH : 0;
437
425
  // The topmost tick label is centred on the plot's top edge, so half of it hangs
438
426
  // above the plot: without this the highest grid line and its label sit on the
439
427
  // chart border.
440
- const topOverflow = Math.max(yLabelsShown ? Math.ceil(textHeight((_yAxes$3 = yAxes[0]) === null || _yAxes$3 === void 0 || (_yAxes$3 = _yAxes$3.labels) === null || _yAxes$3 === void 0 ? void 0 : _yAxes$3.style) / 2) : 0, axisless ? 0 : markerReach);
428
+ const topOverflow = Math.max(yLabelsShown ? Math.ceil(textHeight(yAxes[0]?.labels?.style) / 2) : 0, axisless ? 0 : markerReach);
441
429
  // Point and stack labels are drawn above the shape they belong to, so the plot
442
430
  // has to end below the chart edge — otherwise the topmost value sits on the
443
431
  // border, or gets pushed inside the shape to avoid being cut off.
444
432
  const outsideLabels = axisless ? undefined : resolved.find(({
445
433
  options: series,
446
434
  renderer
447
- }) => {
448
- var _series$dataLabels;
449
- return ((_series$dataLabels = series.dataLabels) === null || _series$dataLabels === void 0 ? void 0 : _series$dataLabels.enabled) && !series.stacking && renderer.scales.y === 'linear';
450
- });
451
- const stackLabels = yAxes.find(axis => {
452
- var _axis$stackLabels;
453
- return (_axis$stackLabels = axis.stackLabels) === null || _axis$stackLabels === void 0 ? void 0 : _axis$stackLabels.enabled;
454
- });
435
+ }) => series.dataLabels?.enabled && !series.stacking && renderer.scales.y === 'linear');
436
+ const stackLabels = yAxes.find(axis => axis.stackLabels?.enabled);
455
437
  const labelHeadroom = (() => {
456
438
  if (outsideLabels) {
457
- var _outsideLabels$option;
458
- return textHeight((_outsideLabels$option = outsideLabels.options.dataLabels) === null || _outsideLabels$option === void 0 ? void 0 : _outsideLabels$option.style) + VALUE_LABEL_HEADROOM;
439
+ return textHeight(outsideLabels.options.dataLabels?.style) + VALUE_LABEL_HEADROOM;
459
440
  }
460
441
  if (stackLabels && !axisless) {
461
- var _stackLabels$stackLab;
462
- return textHeight((_stackLabels$stackLab = stackLabels.stackLabels) === null || _stackLabels$stackLab === void 0 ? void 0 : _stackLabels$stackLab.style) + VALUE_LABEL_HEADROOM;
442
+ return textHeight(stackLabels.stackLabels?.style) + VALUE_LABEL_HEADROOM;
463
443
  }
464
444
  return 0;
465
445
  })();
466
446
  const marginTop = spaceTop + titleHeight + (legendAtTop ? legendHeight : 0) + (labelsOnTop ? xLabelHeight : 0) + Math.max(labelHeadroom, topOverflow);
467
447
  const marginBottom = spaceBottom + (labelsOnTop ? 0 : xLabelHeight) + (legendAtTop ? 0 : legendHeight);
468
448
  const measureGutter = estimate => {
469
- var _axis$labels8, _axis$categories3, _axis$title;
470
449
  const axis = yAxes[0] ?? {};
471
- if (((_axis$labels8 = axis.labels) === null || _axis$labels8 === void 0 ? void 0 : _axis$labels8.enabled) === false) {
450
+ if (axis.labels?.enabled === false) {
472
451
  return estimate;
473
452
  }
474
- const labels = (_axis$categories3 = axis.categories) !== null && _axis$categories3 !== void 0 && _axis$categories3.length ? [...axis.categories] : axisTicks(axis, createLinearScale({
453
+ const labels = axis.categories?.length ? [...axis.categories] : axisTicks(axis, createLinearScale({
475
454
  domain: (() => {
476
455
  const [min, max] = settled(yDomains[0] ?? emptyExtent(), [0, 1]);
477
456
  return [axis.min ?? min, axis.max ?? max];
@@ -481,21 +460,18 @@ export function buildChartModel({
481
460
  tickHint: axis.tickAmount ?? 5,
482
461
  allowDecimals: axis.allowDecimals
483
462
  }), axis.tickAmount ?? 5, undefined, 'y', numberFormat).map(tick => tick.label);
484
- const widest = labels.reduce((widest_, label) => {
485
- var _axis$labels9;
486
- return Math.max(widest_, measureText(label, (_axis$labels9 = axis.labels) === null || _axis$labels9 === void 0 ? void 0 : _axis$labels9.style));
487
- }, 0);
488
- const title = (_axis$title = axis.title) !== null && _axis$title !== void 0 && _axis$title.text && (axis.title.rotation ?? -90) !== 0 ? textHeight(axis.title.style) + AXIS_TITLE_GAP : 0;
463
+ const widest = labels.reduce((widest_, label) => Math.max(widest_, measureText(label, axis.labels?.style)), 0);
464
+ const title = axis.title?.text && (axis.title.rotation ?? -90) !== 0 ? textHeight(axis.title.style) + AXIS_TITLE_GAP : 0;
489
465
  return Math.ceil(widest) + AXIS_GUTTER_PADDING + title;
490
466
  };
491
- const gutter = axisless || ((_yAxes$4 = yAxes[0]) === null || _yAxes$4 === void 0 ? void 0 : _yAxes$4.visible) === false ? 0 : measureGutter(32);
492
- const legendAlign = ((_options$legend3 = options.legend) === null || _options$legend3 === void 0 ? void 0 : _options$legend3.align) ?? 'left';
467
+ const gutter = axisless || yAxes[0]?.visible === false ? 0 : measureGutter(32);
468
+ const legendAlign = options.legend?.align ?? 'left';
493
469
  const legendBesideLeft = legendMetrics.beside && legendAlign === 'left';
494
470
  // Highcharts margins replace the computed edge distance rather than adding to it.
495
- const left = ((_options$chart2 = options.chart) === null || _options$chart2 === void 0 ? void 0 : _options$chart2.marginLeft) ?? spaceLeft + gutter + (legendBesideLeft ? legendMetrics.width : 0);
496
- const right = ((_options$chart3 = options.chart) === null || _options$chart3 === void 0 ? void 0 : _options$chart3.marginRight) ?? spaceRight + (legendMetrics.beside && !legendBesideLeft ? legendMetrics.width : 0);
497
- const top = ((_options$chart4 = options.chart) === null || _options$chart4 === void 0 ? void 0 : _options$chart4.marginTop) ?? marginTop;
498
- const bottom = ((_options$chart5 = options.chart) === null || _options$chart5 === void 0 ? void 0 : _options$chart5.marginBottom) ?? marginBottom;
471
+ const left = options.chart?.marginLeft ?? spaceLeft + gutter + (legendBesideLeft ? legendMetrics.width : 0);
472
+ const right = options.chart?.marginRight ?? spaceRight + (legendMetrics.beside && !legendBesideLeft ? legendMetrics.width : 0);
473
+ const top = options.chart?.marginTop ?? marginTop;
474
+ const bottom = options.chart?.marginBottom ?? marginBottom;
499
475
  const plot = {
500
476
  x: left,
501
477
  y: top,
@@ -517,11 +493,10 @@ export function buildChartModel({
517
493
  stacks
518
494
  });
519
495
  const yAxisModels = yAxes.reduce((built, axis, index) => {
520
- var _axis$categories4;
521
496
  const hint = axis.tickAmount ?? 5;
522
497
  const target = axis.linkedTo === undefined ? undefined : built[axis.linkedTo];
523
- const range = (_axis$categories4 = axis.categories) !== null && _axis$categories4 !== void 0 && _axis$categories4.length ? [plot.y, plot.y + plot.height] : [plot.y + plot.height, plot.y];
524
- const scale = buildYScale(axis, yDomains[index] ?? emptyExtent(), range, hint, target === null || target === void 0 ? void 0 : target.scale);
498
+ const range = axis.categories?.length ? [plot.y, plot.y + plot.height] : [plot.y + plot.height, plot.y];
499
+ const scale = buildYScale(axis, yDomains[index] ?? emptyExtent(), range, hint, target?.scale);
525
500
  built.push({
526
501
  id: `y${index}`,
527
502
  options: axis,
@@ -23,8 +23,7 @@ export const toAxisArray = axis => {
23
23
  return Array.isArray(axis) ? [...axis] : [axis];
24
24
  };
25
25
  export function resolveSeries(options) {
26
- var _options$chart;
27
- const defaultType = ((_options$chart = options.chart) === null || _options$chart === void 0 ? void 0 : _options$chart.type) ?? 'line';
26
+ const defaultType = options.chart?.type ?? 'line';
28
27
  const plotOptions = options.plotOptions ?? {};
29
28
  return (options.series ?? []).map(series => {
30
29
  const type = series.type ?? defaultType;
@@ -16,7 +16,7 @@ export function clearSeriesRegistry() {
16
16
  globalRegistry.clear();
17
17
  }
18
18
  export function resolveRenderers(local) {
19
- if (!(local !== null && local !== void 0 && local.length)) {
19
+ if (!local?.length) {
20
20
  return globalRegistry;
21
21
  }
22
22
  const resolved = new Map(globalRegistry);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushwoosh/kit-charts",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Small React chart library on SVG, with a Highcharts-shaped configuration model",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,7 +38,7 @@
38
38
  "@pushwoosh/kit-constants": "^1.8.0"
39
39
  },
40
40
  "devDependencies": {
41
- "@pushwoosh/frontend-builder-engine": "^3.0.0",
41
+ "@pushwoosh/frontend-builder-engine": "^3.1.0",
42
42
  "@testing-library/jest-dom": "^6.9.1",
43
43
  "@testing-library/react": "^16.3.0",
44
44
  "@types/jest": "^30.0.0",
@@ -13,7 +13,6 @@ export const area = {
13
13
  },
14
14
  extendDomain: (series, acc) => includeZero(series, acc),
15
15
  render: ctx => {
16
- var _ctx$hovered, _ctx$series$marker;
17
16
  const slots = projectSeries(ctx);
18
17
  const projected = drawable(slots);
19
18
  if (!projected.length) {
@@ -21,7 +20,7 @@ export const area = {
21
20
  }
22
21
  const color = ctx.color;
23
22
  const lineWidth = ctx.series.lineWidth ?? 2;
24
- const hoveredIndex = (_ctx$hovered = ctx.hovered) === null || _ctx$hovered === void 0 ? void 0 : _ctx$hovered.pointIndex;
23
+ const hoveredIndex = ctx.hovered?.pointIndex;
25
24
  const hovered = hoveredIndex === undefined ? undefined : projected.find(({
26
25
  point
27
26
  }) => point.index === hoveredIndex);
@@ -45,7 +44,7 @@ export const area = {
45
44
  }), hovered ? _jsx("circle", {
46
45
  cx: hovered.px,
47
46
  cy: hovered.py,
48
- r: ((_ctx$series$marker = ctx.series.marker) === null || _ctx$series$marker === void 0 || (_ctx$series$marker = _ctx$series$marker.states) === null || _ctx$series$marker === void 0 || (_ctx$series$marker = _ctx$series$marker.hover) === null || _ctx$series$marker === void 0 ? void 0 : _ctx$series$marker.radius) ?? 4,
47
+ r: ctx.series.marker?.states?.hover?.radius ?? 4,
49
48
  fill: color,
50
49
  stroke: "#FFFFFF",
51
50
  strokeWidth: 2
@@ -42,12 +42,11 @@ function toBars(ctx) {
42
42
  });
43
43
  }
44
44
  const fillOf = (ctx, bar, hovered) => {
45
- var _ctx$series$states;
46
45
  const base = bar.point.color ?? ctx.color;
47
46
  if (!hovered) {
48
47
  return base;
49
48
  }
50
- return brighten(base, ((_ctx$series$states = ctx.series.states) === null || _ctx$series$states === void 0 || (_ctx$series$states = _ctx$series$states.hover) === null || _ctx$series$states === void 0 ? void 0 : _ctx$series$states.brightness) ?? DEFAULT_HOVER_BRIGHTNESS);
49
+ return brighten(base, ctx.series.states?.hover?.brightness ?? DEFAULT_HOVER_BRIGHTNESS);
51
50
  };
52
51
  /**
53
52
  * A translucent body under a solid rule is the funnel look: the series keeps its
@@ -55,12 +54,11 @@ const fillOf = (ctx, bar, hovered) => {
55
54
  * bar is washed out. Hovering deepens it.
56
55
  */
57
56
  const opacityOf = (ctx, hovered) => {
58
- var _ctx$series$states2;
59
57
  const base = ctx.series.fillOpacity;
60
58
  if (!hovered) {
61
59
  return base;
62
60
  }
63
- return ((_ctx$series$states2 = ctx.series.states) === null || _ctx$series$states2 === void 0 || (_ctx$series$states2 = _ctx$series$states2.hover) === null || _ctx$series$states2 === void 0 ? void 0 : _ctx$series$states2.fillOpacity) ?? base;
61
+ return ctx.series.states?.hover?.fillOpacity ?? base;
64
62
  };
65
63
  export const column = {
66
64
  type: 'column',
@@ -71,12 +69,11 @@ export const column = {
71
69
  exit: 'collapse',
72
70
  extendDomain: (series, acc) => includeZero(series, acc),
73
71
  render: ctx => {
74
- var _ctx$hovered;
75
72
  const bars = toBars(ctx);
76
73
  if (!bars.length) {
77
74
  return null;
78
75
  }
79
- const hoveredIndex = (_ctx$hovered = ctx.hovered) === null || _ctx$hovered === void 0 ? void 0 : _ctx$hovered.pointIndex;
76
+ const hoveredIndex = ctx.hovered?.pointIndex;
80
77
  const radius = ctx.series.borderRadius;
81
78
  const borderWidth = ctx.series.borderWidth ?? 0;
82
79
  const capped = borderWidth > 0 && ctx.series.borderPosition === 'top';
@@ -120,7 +117,6 @@ export const column = {
120
117
  });
121
118
  },
122
119
  hitTest: (ctx, pointer) => {
123
- var _ctx$slot;
124
120
  const bars = toBars(ctx);
125
121
  if (!bars.length) {
126
122
  return null;
@@ -130,7 +126,7 @@ export const column = {
130
126
  if (pointer.y < top || pointer.y > bottom) {
131
127
  return null;
132
128
  }
133
- const step = ((_ctx$slot = ctx.slot) === null || _ctx$slot === void 0 ? void 0 : _ctx$slot.step) ?? ctx.plot.width / 2;
129
+ const step = ctx.slot?.step ?? ctx.plot.width / 2;
134
130
  let candidate;
135
131
  let candidateGap = Infinity;
136
132
  bars.forEach(bar => {