@meonode/canvas 1.1.0 → 1.3.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.
@@ -170,13 +170,22 @@ class ChartNode extends BoxNode {
170
170
  return;
171
171
  const chartData = this.chartData;
172
172
  const chartOptions = this.chartOptions;
173
+ const { labels, datasets } = chartData;
174
+ const maxValue = Math.max(...datasets.flatMap(d => d.data));
173
175
  const legendLayout = this.getLegendLayout(ctx, width, height);
174
- const chartX = x + legendLayout.chartX;
176
+ let chartX = x + legendLayout.chartX;
175
177
  const chartY = y + legendLayout.chartY;
176
- const chartWidth = legendLayout.chartWidth;
178
+ let chartWidth = legendLayout.chartWidth;
177
179
  const chartHeight = legendLayout.chartHeight;
178
- const { labels, datasets } = chartData;
179
- const maxValue = Math.max(...datasets.flatMap(d => d.data));
180
+ if (chartOptions?.showYAxis) {
181
+ const fontSize = chartOptions.yAxisFontSize || 12;
182
+ ctx.font = `${fontSize}px ${this.props.fontFamily || 'sans-serif'}`;
183
+ const formatter = chartOptions.yAxisLabelFormatter || ((v) => v.toString());
184
+ const maxLabel = formatter(maxValue);
185
+ const yAxisWidth = ctx.measureText(maxLabel).width + 10;
186
+ chartX += yAxisWidth;
187
+ chartWidth -= yAxisWidth;
188
+ }
180
189
  let labelHeight = 0;
181
190
  if (chartOptions?.showLabels) {
182
191
  const fontSize = chartOptions.labelFontSize || 12;
@@ -204,6 +213,18 @@ class ChartNode extends BoxNode {
204
213
  ctx.moveTo(chartX, gridY);
205
214
  ctx.lineTo(chartX + chartWidth, gridY);
206
215
  ctx.stroke();
216
+ if (chartOptions?.showYAxis) {
217
+ const value = maxValue - (maxValue / 5) * i;
218
+ const formatter = chartOptions.yAxisLabelFormatter || ((v) => (Math.round(v * 100) / 100).toString());
219
+ const label = formatter(value);
220
+ TextNode.renderSimpleText(ctx, label, chartX - 5, gridY, {
221
+ color: chartOptions.yAxisColor || chartOptions.axisColor || '#000',
222
+ fontSize: chartOptions.yAxisFontSize || 12,
223
+ fontFamily: this.props.fontFamily,
224
+ textAlign: 'right',
225
+ textBaseline: 'middle',
226
+ });
227
+ }
207
228
  }
208
229
  ctx.setLineDash([]);
209
230
  }
@@ -216,6 +237,31 @@ class ChartNode extends BoxNode {
216
237
  const barY = chartY + finalChartHeight - barHeight;
217
238
  ctx.fillStyle = dataset.color || this.generateColor(datasetIndex);
218
239
  ctx.fillRect(barX, barY, barWidth, barHeight);
240
+ // Render values
241
+ if (chartOptions?.showValues) {
242
+ const value = dataset.data[index];
243
+ const { renderValueItem } = chartOptions;
244
+ const valueX = barX + barWidth / 2;
245
+ const valueY = barY - 5; // 5px padding above bar
246
+ if (renderValueItem) {
247
+ const valueNode = renderValueItem({ item: value, index, datasetIndex });
248
+ if (valueNode) {
249
+ valueNode.processInitialChildren();
250
+ valueNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
251
+ const layout = valueNode.node.getComputedLayout();
252
+ valueNode.render(ctx, valueX - layout.width / 2, valueY - layout.height);
253
+ }
254
+ }
255
+ else {
256
+ TextNode.renderSimpleText(ctx, value.toString(), valueX, valueY, {
257
+ color: chartOptions.valueColor || '#000',
258
+ fontSize: chartOptions.valueFontSize || 12,
259
+ fontFamily: this.props.fontFamily,
260
+ textAlign: 'center',
261
+ textBaseline: 'bottom',
262
+ });
263
+ }
264
+ }
219
265
  });
220
266
  // Render labels
221
267
  if (chartOptions?.showLabels) {
@@ -250,13 +296,22 @@ class ChartNode extends BoxNode {
250
296
  return;
251
297
  const chartData = this.chartData;
252
298
  const chartOptions = this.chartOptions;
299
+ const { labels, datasets } = chartData;
300
+ const maxValue = Math.max(...datasets.flatMap(d => d.data));
253
301
  const legendLayout = this.getLegendLayout(ctx, width, height);
254
- const chartX = x + legendLayout.chartX;
302
+ let chartX = x + legendLayout.chartX;
255
303
  const chartY = y + legendLayout.chartY;
256
- const chartWidth = legendLayout.chartWidth;
304
+ let chartWidth = legendLayout.chartWidth;
257
305
  const chartHeight = legendLayout.chartHeight;
258
- const { labels, datasets } = chartData;
259
- const maxValue = Math.max(...datasets.flatMap(d => d.data));
306
+ if (chartOptions?.showYAxis) {
307
+ const fontSize = chartOptions.yAxisFontSize || 12;
308
+ ctx.font = `${fontSize}px ${this.props.fontFamily || 'sans-serif'}`;
309
+ const formatter = chartOptions.yAxisLabelFormatter || ((v) => v.toString());
310
+ const maxLabel = formatter(maxValue);
311
+ const yAxisWidth = ctx.measureText(maxLabel).width + 10;
312
+ chartX += yAxisWidth;
313
+ chartWidth -= yAxisWidth;
314
+ }
260
315
  let labelHeight = 0;
261
316
  if (chartOptions?.showLabels) {
262
317
  const fontSize = chartOptions.labelFontSize || 12;
@@ -282,6 +337,18 @@ class ChartNode extends BoxNode {
282
337
  ctx.moveTo(chartX, gridY);
283
338
  ctx.lineTo(chartX + chartWidth, gridY);
284
339
  ctx.stroke();
340
+ if (chartOptions?.showYAxis) {
341
+ const value = maxValue - (maxValue / 5) * i;
342
+ const formatter = chartOptions.yAxisLabelFormatter || ((v) => (Math.round(v * 100) / 100).toString());
343
+ const label = formatter(value);
344
+ TextNode.renderSimpleText(ctx, label, chartX - 5, gridY, {
345
+ color: chartOptions.yAxisColor || chartOptions.axisColor || '#000',
346
+ fontSize: chartOptions.yAxisFontSize || 12,
347
+ fontFamily: this.props.fontFamily,
348
+ textAlign: 'right',
349
+ textBaseline: 'middle',
350
+ });
351
+ }
285
352
  }
286
353
  ctx.setLineDash([]);
287
354
  }
@@ -1,39 +1,46 @@
1
- import type { GridProps } from '../canvas/canvas.type.js';
1
+ import type { GridProps, GridItemProps } from '../canvas/canvas.type.js';
2
2
  import { BoxNode, RowNode } from '../canvas/layout.canvas.util.js';
3
3
  /**
4
- * Grid layout node that arranges children in a configurable number of columns or rows.
5
- * Uses Yoga's flexbox capabilities with wrapping and gap properties to simulate a grid.
6
- * @extends RowNode
4
+ * GridItem Node. Theoretically just a BoxNode but typed differently in factory.
5
+ * In runtime, it behaves almost like a BoxNode, but we can detect it if needed,
6
+ * or simply rely on the props being present in the instance.
7
+ */
8
+ export declare class GridItemNode extends BoxNode {
9
+ constructor(props: GridItemProps);
10
+ }
11
+ /**
12
+ * Factory for GridItem.
13
+ */
14
+ export declare const GridItem: (props: GridItemProps) => GridItemNode;
15
+ /**
16
+ * Grid layout node that arranges children in a 2D grid.
17
+ * Implements a simplified version of the CSS Grid Layout algorithm.
7
18
  */
8
19
  export declare class GridNode extends RowNode {
9
- private readonly columns;
10
- private readonly columnGapValue;
11
- private readonly rowGapValue;
12
- private readonly isVertical;
13
20
  /**
14
21
  * Creates a new grid layout node
15
22
  * @param props Grid configuration properties
16
23
  */
17
24
  constructor(props: GridProps);
18
25
  /**
19
- * Appends a child node to this grid.
20
- * Overridden primarily for documentation/clarity, functionality is inherited.
21
- * @param child Child node to append
22
- * @param index Index at which to insert the child
26
+ * Helper to parse a track size definition.
23
27
  */
24
- protected appendChild(child: BoxNode, index: number): void;
28
+ private parseTrack;
29
+ /**
30
+ * Parses the gap property into pixels.
31
+ */
32
+ private getGapPixels;
25
33
  /**
26
34
  * Update layout calculations after the initial layout is computed.
27
- * This method calculates the appropriate flex-basis for children based on the
28
- * number of columns and gaps, respecting the container's padding,
29
- * and applies the gaps using Yoga's built-in properties.
30
35
  */
31
36
  protected updateLayoutBasedOnComputedSize(): void;
37
+ /**
38
+ * Resolves track sizes to pixels.
39
+ */
40
+ private resolveTracks;
32
41
  }
33
42
  /**
34
43
  * Factory function to create a new GridNode instance.
35
- * @param props Grid configuration properties.
36
- * @returns A new GridNode instance.
37
44
  */
38
45
  export declare const Grid: (props: GridProps) => GridNode;
39
46
  //# sourceMappingURL=grid.canvas.util.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"grid.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/grid.canvas.util.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AAGjE;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,OAAO;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAuB;IACtD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC;;;OAGG;gBACS,KAAK,EAAE,SAAS;IAqE5B;;;;;OAKG;cACgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM;IAI5D;;;;;OAKG;cACgB,+BAA+B;CAwLnD;AAED;;;;GAIG;AACH,eAAO,MAAM,IAAI,GAAI,OAAO,SAAS,aAAwB,CAAA"}
1
+ {"version":3,"file":"grid.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/grid.canvas.util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAiB,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACtF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AAIjE;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,OAAO;gBAC3B,KAAK,EAAE,aAAa;CAMjC;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,aAAa,iBAA4B,CAAA;AAEzE;;;GAGG;AACH,qBAAa,QAAS,SAAQ,OAAO;IACnC;;;OAGG;gBACS,KAAK,EAAE,SAAS;IAQ5B;;OAEG;IACH,OAAO,CAAC,UAAU;IAqBlB;;OAEG;IACH,OAAO,CAAC,YAAY;IAmBpB;;OAEG;cACgB,+BAA+B;IA4RlD;;OAEG;IACH,OAAO,CAAC,aAAa;CAkCtB;AAED;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,OAAO,SAAS,aAAwB,CAAA"}