@ojiepermana/angular-chart 22.0.27

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.
Files changed (32) hide show
  1. package/README.md +249 -0
  2. package/fesm2022/ojiepermana-angular-chart-area.mjs +266 -0
  3. package/fesm2022/ojiepermana-angular-chart-area.mjs.map +1 -0
  4. package/fesm2022/ojiepermana-angular-chart-bar.mjs +674 -0
  5. package/fesm2022/ojiepermana-angular-chart-bar.mjs.map +1 -0
  6. package/fesm2022/ojiepermana-angular-chart-core.mjs +764 -0
  7. package/fesm2022/ojiepermana-angular-chart-core.mjs.map +1 -0
  8. package/fesm2022/ojiepermana-angular-chart-line.mjs +281 -0
  9. package/fesm2022/ojiepermana-angular-chart-line.mjs.map +1 -0
  10. package/fesm2022/ojiepermana-angular-chart-pie.mjs +248 -0
  11. package/fesm2022/ojiepermana-angular-chart-pie.mjs.map +1 -0
  12. package/fesm2022/ojiepermana-angular-chart-primitives.mjs +1186 -0
  13. package/fesm2022/ojiepermana-angular-chart-primitives.mjs.map +1 -0
  14. package/fesm2022/ojiepermana-angular-chart-radar.mjs +329 -0
  15. package/fesm2022/ojiepermana-angular-chart-radar.mjs.map +1 -0
  16. package/fesm2022/ojiepermana-angular-chart-radial.mjs +255 -0
  17. package/fesm2022/ojiepermana-angular-chart-radial.mjs.map +1 -0
  18. package/fesm2022/ojiepermana-angular-chart-scatter.mjs +253 -0
  19. package/fesm2022/ojiepermana-angular-chart-scatter.mjs.map +1 -0
  20. package/fesm2022/ojiepermana-angular-chart.mjs +20 -0
  21. package/fesm2022/ojiepermana-angular-chart.mjs.map +1 -0
  22. package/package.json +76 -0
  23. package/types/ojiepermana-angular-chart-area.d.ts +58 -0
  24. package/types/ojiepermana-angular-chart-bar.d.ts +171 -0
  25. package/types/ojiepermana-angular-chart-core.d.ts +369 -0
  26. package/types/ojiepermana-angular-chart-line.d.ts +57 -0
  27. package/types/ojiepermana-angular-chart-pie.d.ts +93 -0
  28. package/types/ojiepermana-angular-chart-primitives.d.ts +265 -0
  29. package/types/ojiepermana-angular-chart-radar.d.ts +89 -0
  30. package/types/ojiepermana-angular-chart-radial.d.ts +86 -0
  31. package/types/ojiepermana-angular-chart-scatter.d.ts +95 -0
  32. package/types/ojiepermana-angular-chart.d.ts +2 -0
@@ -0,0 +1,674 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, input, output, computed, effect, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { seriesColorVar, ChartContext, CartesianContext, elementClientCenter } from '@ojiepermana/angular-chart/core';
4
+ import { scaleBand, scaleLinear } from 'd3-scale';
5
+ import { min, max } from 'd3-array';
6
+ import { stack } from 'd3-shape';
7
+ import { ChartPointerTracker } from '@ojiepermana/angular-chart/primitives';
8
+
9
+ /** Read a numeric value from a datum, tolerating strings. */
10
+ function readNumber(datum, key) {
11
+ const raw = datum[key];
12
+ if (typeof raw === 'number' && Number.isFinite(raw)) {
13
+ return raw;
14
+ }
15
+ if (typeof raw === 'string') {
16
+ const n = Number(raw);
17
+ return Number.isFinite(n) ? n : 0;
18
+ }
19
+ return 0;
20
+ }
21
+ function resolveBarColor(datum, seriesKey, colorKey) {
22
+ if (!colorKey) {
23
+ return seriesColorVar(seriesKey);
24
+ }
25
+ const raw = datum[colorKey];
26
+ if (typeof raw !== 'string' || raw.length === 0) {
27
+ return seriesColorVar(seriesKey);
28
+ }
29
+ if (raw.startsWith('var(') ||
30
+ raw.startsWith('#') ||
31
+ raw.startsWith('rgb') ||
32
+ raw.startsWith('hsl') ||
33
+ raw.includes('(')) {
34
+ return raw;
35
+ }
36
+ return seriesColorVar(raw);
37
+ }
38
+ function isActiveBar(datum, activeKey, activeValue) {
39
+ if (!activeKey || activeValue === undefined) {
40
+ return false;
41
+ }
42
+ const value = datum[activeKey];
43
+ if (typeof value === 'number' && typeof activeValue === 'number') {
44
+ return value === activeValue;
45
+ }
46
+ return String(value ?? '') === String(activeValue);
47
+ }
48
+ /** Build all bar rectangles for the given config. */
49
+ function computeBarLayout(input) {
50
+ const { data, xKey, seriesKeys, variant, orientation, innerWidth, innerHeight, bandPadding, groupPadding, colorKey, activeKey, activeValue, } = input;
51
+ const categories = data.map((d) => String(d[xKey] ?? ''));
52
+ const isVertical = orientation === 'vertical';
53
+ const categoryScale = scaleBand()
54
+ .domain(categories)
55
+ .range(isVertical ? [0, innerWidth] : [0, innerHeight])
56
+ .padding(bandPadding);
57
+ const valueScale = scaleLinear();
58
+ if (variant === 'stacked' && seriesKeys.length > 0) {
59
+ return stackedLayout({
60
+ data,
61
+ xKey,
62
+ seriesKeys,
63
+ orientation,
64
+ innerWidth,
65
+ innerHeight,
66
+ categoryScale,
67
+ valueScale,
68
+ categories,
69
+ colorKey,
70
+ activeKey,
71
+ activeValue,
72
+ });
73
+ }
74
+ return groupedLayout({
75
+ data,
76
+ xKey,
77
+ seriesKeys,
78
+ orientation,
79
+ innerWidth,
80
+ innerHeight,
81
+ categoryScale,
82
+ valueScale,
83
+ groupPadding,
84
+ categories,
85
+ colorKey,
86
+ activeKey,
87
+ activeValue,
88
+ });
89
+ }
90
+ function groupedLayout(input) {
91
+ const { data, seriesKeys, orientation, innerWidth, innerHeight, categoryScale, valueScale, groupPadding, categories, colorKey, activeKey, activeValue, } = input;
92
+ const isVertical = orientation === 'vertical';
93
+ const minValue = min(data, (d) => min(seriesKeys, (k) => readNumber(d, k)) ?? 0) ?? 0;
94
+ const maxValue = max(data, (d) => max(seriesKeys, (k) => readNumber(d, k)) ?? 0) ?? 0;
95
+ const domainMin = Math.min(0, minValue);
96
+ const domainMax = Math.max(0, maxValue, domainMin === 0 ? 1 : 0);
97
+ valueScale
98
+ .domain([domainMin, domainMax])
99
+ .nice()
100
+ .range(isVertical ? [innerHeight, 0] : [0, innerWidth]);
101
+ const baseline = valueScale(0);
102
+ const subScale = scaleBand()
103
+ .domain(seriesKeys)
104
+ .range([0, categoryScale.bandwidth()])
105
+ .padding(groupPadding);
106
+ const bars = [];
107
+ data.forEach((datum, datumIndex) => {
108
+ const category = categories[datumIndex];
109
+ const bandStart = categoryScale(category) ?? 0;
110
+ seriesKeys.forEach((seriesKey) => {
111
+ const value = readNumber(datum, seriesKey);
112
+ const sub = subScale(seriesKey) ?? 0;
113
+ const scaledValue = valueScale(value);
114
+ const color = resolveBarColor(datum, seriesKey, colorKey);
115
+ const active = isActiveBar(datum, activeKey, activeValue);
116
+ const rect = isVertical
117
+ ? {
118
+ key: `${datumIndex}-${seriesKey}`,
119
+ seriesKey,
120
+ datumIndex,
121
+ category,
122
+ value,
123
+ x: bandStart + sub,
124
+ y: Math.min(scaledValue, baseline),
125
+ width: subScale.bandwidth(),
126
+ height: Math.abs(baseline - scaledValue),
127
+ color,
128
+ active,
129
+ }
130
+ : {
131
+ key: `${datumIndex}-${seriesKey}`,
132
+ seriesKey,
133
+ datumIndex,
134
+ category,
135
+ value,
136
+ x: Math.min(scaledValue, baseline),
137
+ y: bandStart + sub,
138
+ width: Math.abs(baseline - scaledValue),
139
+ height: subScale.bandwidth(),
140
+ color,
141
+ active,
142
+ };
143
+ bars.push(rect);
144
+ });
145
+ });
146
+ return { bars, categoryScale, valueScale, categories };
147
+ }
148
+ function stackedLayout(input) {
149
+ const { data, seriesKeys, orientation, innerWidth, innerHeight, categoryScale, valueScale, categories, colorKey, activeKey, activeValue, } = input;
150
+ const isVertical = orientation === 'vertical';
151
+ const normalized = data.map((d) => {
152
+ const out = {};
153
+ for (const k of seriesKeys) {
154
+ out[k] = readNumber(d, k);
155
+ }
156
+ return out;
157
+ });
158
+ const series = stack().keys(seriesKeys)(normalized);
159
+ const maxTotal = max(series[series.length - 1] ?? [], (p) => p[1]) ?? 0;
160
+ valueScale
161
+ .domain([0, maxTotal === 0 ? 1 : maxTotal])
162
+ .nice()
163
+ .range(isVertical ? [innerHeight, 0] : [0, innerWidth]);
164
+ const bars = [];
165
+ series.forEach((layer) => {
166
+ const seriesKey = layer.key;
167
+ layer.forEach((point, datumIndex) => {
168
+ const [lower, upper] = point;
169
+ const value = upper - lower;
170
+ const category = categories[datumIndex];
171
+ const bandStart = categoryScale(category) ?? 0;
172
+ const color = resolveBarColor(data[datumIndex], seriesKey, colorKey);
173
+ const active = isActiveBar(data[datumIndex], activeKey, activeValue);
174
+ const rect = isVertical
175
+ ? {
176
+ key: `${datumIndex}-${seriesKey}`,
177
+ seriesKey,
178
+ datumIndex,
179
+ category,
180
+ value,
181
+ x: bandStart,
182
+ y: valueScale(upper),
183
+ width: categoryScale.bandwidth(),
184
+ height: valueScale(lower) - valueScale(upper),
185
+ color,
186
+ active,
187
+ }
188
+ : {
189
+ key: `${datumIndex}-${seriesKey}`,
190
+ seriesKey,
191
+ datumIndex,
192
+ category,
193
+ value,
194
+ x: valueScale(lower),
195
+ y: bandStart,
196
+ width: valueScale(upper) - valueScale(lower),
197
+ height: categoryScale.bandwidth(),
198
+ color,
199
+ active,
200
+ };
201
+ bars.push(rect);
202
+ });
203
+ });
204
+ return { bars, categoryScale, valueScale, categories };
205
+ }
206
+
207
+ /** Hard cap so degenerate inputs (tiny `dotSize`) cannot explode the DOM. */
208
+ const MAX_ROWS_PER_COLUMN = 400;
209
+ const EDGE_TOLERANCE = 0.5;
210
+ /**
211
+ * Build the dot-matrix cells for the `dot` bar style.
212
+ *
213
+ * Cells tile a global grid anchored at the value baseline, so stacked
214
+ * segments snap to the same rows and never overlap. A bar fills every cell
215
+ * whose center lies inside its value range; bars too short to cover a center
216
+ * still claim the cell nearest their midpoint when it is free.
217
+ */
218
+ function computeBarDotCells(input) {
219
+ const { bars, orientation, innerWidth, innerHeight, baseline, dotSize, dotGap } = input;
220
+ const isVertical = orientation === 'vertical';
221
+ const innerLength = isVertical ? innerHeight : innerWidth;
222
+ const track = [];
223
+ const values = [];
224
+ if (innerLength <= 0 || bars.length === 0) {
225
+ return { track, values };
226
+ }
227
+ const columns = groupColumns(bars, isVertical);
228
+ columns.forEach((column, columnIndex) => {
229
+ const cellLength = dotSize ?? column.thickness;
230
+ const step = cellLength + dotGap;
231
+ if (cellLength <= 0 || step <= EDGE_TOLERANCE) {
232
+ return;
233
+ }
234
+ // Signed row index r anchored at the baseline: r = 0 is the first cell on
235
+ // the positive-value side, negative r tiles the negative side.
236
+ // `rowStart(r)` is the cell's lower main-axis coordinate (top edge for
237
+ // vertical, left edge for horizontal); `rowAt(c)` is the fractional row
238
+ // whose cell center sits at coordinate c.
239
+ const rowStart = (r) => (isVertical ? baseline - r * step - cellLength : baseline + r * step);
240
+ const rowAt = (c) => isVertical ? (baseline - c - cellLength / 2) / step : (c - baseline - cellLength / 2) / step;
241
+ // Rows whose cell lies fully inside the plot area.
242
+ let rMin = Math.ceil(isVertical ? (baseline - innerLength - EDGE_TOLERANCE) / step : (-baseline - EDGE_TOLERANCE) / step);
243
+ let rMax = Math.floor(isVertical
244
+ ? (baseline - cellLength + EDGE_TOLERANCE) / step
245
+ : (innerLength - baseline - cellLength + EDGE_TOLERANCE) / step);
246
+ if (rMax - rMin + 1 > MAX_ROWS_PER_COLUMN) {
247
+ rMax = rMin + MAX_ROWS_PER_COLUMN - 1;
248
+ }
249
+ const cellRect = (r) => isVertical
250
+ ? { x: column.pos, y: rowStart(r), width: column.thickness, height: cellLength }
251
+ : { x: rowStart(r), y: column.pos, width: cellLength, height: column.thickness };
252
+ for (let r = rMin; r <= rMax; r++) {
253
+ track.push({ key: `${columnIndex}:${r}`, ...cellRect(r) });
254
+ }
255
+ const usedRows = new Set();
256
+ for (const bar of column.bars) {
257
+ const barLength = isVertical ? bar.height : bar.width;
258
+ if (barLength <= EDGE_TOLERANCE) {
259
+ continue;
260
+ }
261
+ const barStart = isVertical ? bar.y : bar.x;
262
+ const barEnd = barStart + barLength;
263
+ // Rows whose cell center falls inside [barStart, barEnd] (± tolerance).
264
+ const rA = rowAt(barStart);
265
+ const rB = rowAt(barEnd);
266
+ const first = Math.max(Math.ceil(Math.min(rA, rB) - EDGE_TOLERANCE / step), rMin);
267
+ const last = Math.min(Math.floor(Math.max(rA, rB) + EDGE_TOLERANCE / step), rMax);
268
+ const rows = [];
269
+ for (let r = first; r <= last; r++) {
270
+ rows.push(r);
271
+ }
272
+ // Guarantee visibility: a non-zero bar shorter than one cell claims the
273
+ // row nearest its midpoint when that row is still free.
274
+ if (rows.length === 0) {
275
+ const r = Math.round(rowAt(barStart + barLength / 2));
276
+ if (r >= rMin && r <= rMax && !usedRows.has(r)) {
277
+ rows.push(r);
278
+ }
279
+ }
280
+ for (const r of rows) {
281
+ if (usedRows.has(r)) {
282
+ continue;
283
+ }
284
+ usedRows.add(r);
285
+ values.push({
286
+ key: `${bar.key}:${r}`,
287
+ ...cellRect(r),
288
+ seriesKey: bar.seriesKey,
289
+ datumIndex: bar.datumIndex,
290
+ color: bar.color,
291
+ active: bar.active,
292
+ });
293
+ }
294
+ }
295
+ });
296
+ return { track, values };
297
+ }
298
+ /** Group bars sharing the same cross-axis band into one cell column. */
299
+ function groupColumns(bars, isVertical) {
300
+ const map = new Map();
301
+ for (const bar of bars) {
302
+ const pos = isVertical ? bar.x : bar.y;
303
+ const thickness = isVertical ? bar.width : bar.height;
304
+ if (thickness <= 0) {
305
+ continue;
306
+ }
307
+ const key = `${pos.toFixed(2)}:${thickness.toFixed(2)}`;
308
+ const existing = map.get(key);
309
+ if (existing) {
310
+ existing.bars.push(bar);
311
+ }
312
+ else {
313
+ map.set(key, { pos, thickness, bars: [bar] });
314
+ }
315
+ }
316
+ return [...map.values()];
317
+ }
318
+
319
+ const DEFAULT_MARGIN = { top: 8, right: 8, bottom: 24, left: 40 };
320
+ const defaultBarValueFormatter = (value) => `${value}`;
321
+ /**
322
+ * Bar chart — composable within `<Chart>`.
323
+ *
324
+ * Layout variants (via inputs):
325
+ * - `orientation`: `'vertical'` (default) or `'horizontal'`
326
+ * - `variant`: `'grouped'` (default) or `'stacked'`
327
+ * - `styles`: `'base'` (default) or `'dot'` (dot-matrix cells)
328
+ */
329
+ class BarChart {
330
+ root = inject(ChartContext);
331
+ cart = inject(CartesianContext);
332
+ data = input.required(/* @ts-ignore */
333
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
334
+ xKey = input.required(/* @ts-ignore */
335
+ ...(ngDevMode ? [{ debugName: "xKey" }] : /* istanbul ignore next */ []));
336
+ orientation = input('vertical', /* @ts-ignore */
337
+ ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
338
+ variant = input('grouped', /* @ts-ignore */
339
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
340
+ styles = input('base', /* @ts-ignore */
341
+ ...(ngDevMode ? [{ debugName: "styles" }] : /* istanbul ignore next */ []));
342
+ margin = input(DEFAULT_MARGIN, /* @ts-ignore */
343
+ ...(ngDevMode ? [{ debugName: "margin" }] : /* istanbul ignore next */ []));
344
+ bandPadding = input(0.2, /* @ts-ignore */
345
+ ...(ngDevMode ? [{ debugName: "bandPadding" }] : /* istanbul ignore next */ []));
346
+ groupPadding = input(0.05, /* @ts-ignore */
347
+ ...(ngDevMode ? [{ debugName: "groupPadding" }] : /* istanbul ignore next */ []));
348
+ cornerRadius = input(4, /* @ts-ignore */
349
+ ...(ngDevMode ? [{ debugName: "cornerRadius" }] : /* istanbul ignore next */ []));
350
+ colorKey = input(undefined, /* @ts-ignore */
351
+ ...(ngDevMode ? [{ debugName: "colorKey" }] : /* istanbul ignore next */ []));
352
+ activeKey = input(undefined, /* @ts-ignore */
353
+ ...(ngDevMode ? [{ debugName: "activeKey" }] : /* istanbul ignore next */ []));
354
+ activeValue = input(undefined, /* @ts-ignore */
355
+ ...(ngDevMode ? [{ debugName: "activeValue" }] : /* istanbul ignore next */ []));
356
+ showValueLabels = input(false, /* @ts-ignore */
357
+ ...(ngDevMode ? [{ debugName: "showValueLabels" }] : /* istanbul ignore next */ []));
358
+ valueLabelFormat = input(defaultBarValueFormatter, /* @ts-ignore */
359
+ ...(ngDevMode ? [{ debugName: "valueLabelFormat" }] : /* istanbul ignore next */ []));
360
+ /** Cell length along the value axis for the `dot` style; defaults to the bar thickness. */
361
+ dotSize = input(undefined, /* @ts-ignore */
362
+ ...(ngDevMode ? [{ debugName: "dotSize" }] : /* istanbul ignore next */ []));
363
+ /** Gap between consecutive cells along the value axis for the `dot` style. */
364
+ dotGap = input(2, /* @ts-ignore */
365
+ ...(ngDevMode ? [{ debugName: "dotGap" }] : /* istanbul ignore next */ []));
366
+ /** Corner radius of each `dot` cell. */
367
+ dotCornerRadius = input(2, /* @ts-ignore */
368
+ ...(ngDevMode ? [{ debugName: "dotCornerRadius" }] : /* istanbul ignore next */ []));
369
+ /** Render the faint full-height cell track behind the `dot` values. */
370
+ showDotTrack = input(true, /* @ts-ignore */
371
+ ...(ngDevMode ? [{ debugName: "showDotTrack" }] : /* istanbul ignore next */ []));
372
+ barClick = output();
373
+ innerWidth = computed(() => Math.max(0, this.root.dimensions().width - this.margin().left - this.margin().right), /* @ts-ignore */
374
+ ...(ngDevMode ? [{ debugName: "innerWidth" }] : /* istanbul ignore next */ []));
375
+ innerHeight = computed(() => Math.max(0, this.root.dimensions().height - this.margin().top - this.margin().bottom), /* @ts-ignore */
376
+ ...(ngDevMode ? [{ debugName: "innerHeight" }] : /* istanbul ignore next */ []));
377
+ layout = computed(() => computeBarLayout({
378
+ data: this.data(),
379
+ xKey: this.xKey(),
380
+ seriesKeys: this.root.visibleSeriesKeys(),
381
+ variant: this.variant(),
382
+ orientation: this.orientation(),
383
+ innerWidth: this.innerWidth(),
384
+ innerHeight: this.innerHeight(),
385
+ bandPadding: this.bandPadding(),
386
+ groupPadding: this.groupPadding(),
387
+ colorKey: this.colorKey(),
388
+ activeKey: this.activeKey(),
389
+ activeValue: this.activeValue(),
390
+ }), /* @ts-ignore */
391
+ ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
392
+ bars = computed(() => this.layout().bars, /* @ts-ignore */
393
+ ...(ngDevMode ? [{ debugName: "bars" }] : /* istanbul ignore next */ []));
394
+ dotLayout = computed(() => {
395
+ if (this.styles() !== 'dot') {
396
+ return null;
397
+ }
398
+ const layout = this.layout();
399
+ return computeBarDotCells({
400
+ bars: layout.bars,
401
+ orientation: this.orientation(),
402
+ innerWidth: this.innerWidth(),
403
+ innerHeight: this.innerHeight(),
404
+ baseline: layout.valueScale(0),
405
+ dotSize: this.dotSize(),
406
+ dotGap: this.dotGap(),
407
+ });
408
+ }, /* @ts-ignore */
409
+ ...(ngDevMode ? [{ debugName: "dotLayout" }] : /* istanbul ignore next */ []));
410
+ dotTrackCells = computed(() => (this.showDotTrack() ? (this.dotLayout()?.track ?? []) : []), /* @ts-ignore */
411
+ ...(ngDevMode ? [{ debugName: "dotTrackCells" }] : /* istanbul ignore next */ []));
412
+ dotValueCells = computed(() => this.dotLayout()?.values ?? [], /* @ts-ignore */
413
+ ...(ngDevMode ? [{ debugName: "dotValueCells" }] : /* istanbul ignore next */ []));
414
+ viewBox = computed(() => {
415
+ const { width, height } = this.root.dimensions();
416
+ return `0 0 ${Math.max(0, width)} ${Math.max(0, height)}`;
417
+ }, /* @ts-ignore */
418
+ ...(ngDevMode ? [{ debugName: "viewBox" }] : /* istanbul ignore next */ []));
419
+ innerTransform = computed(() => `translate(${this.margin().left},${this.margin().top})`, /* @ts-ignore */
420
+ ...(ngDevMode ? [{ debugName: "innerTransform" }] : /* istanbul ignore next */ []));
421
+ ariaSummary = computed(() => {
422
+ const keys = this.root.visibleSeriesKeys();
423
+ const n = this.data().length;
424
+ return `Bar chart, ${n} categories, ${keys.length} series: ${keys.join(', ')}.`;
425
+ }, /* @ts-ignore */
426
+ ...(ngDevMode ? [{ debugName: "ariaSummary" }] : /* istanbul ignore next */ []));
427
+ constructor() {
428
+ effect(() => {
429
+ const layout = this.layout();
430
+ this.cart.orientation.set(this.orientation());
431
+ this.cart.margin.set(this.margin());
432
+ this.cart.innerWidth.set(this.innerWidth());
433
+ this.cart.innerHeight.set(this.innerHeight());
434
+ this.cart.categoryScale.set(layout.categoryScale);
435
+ this.cart.valueScale.set(layout.valueScale);
436
+ this.cart.categories.set(layout.categories);
437
+ });
438
+ }
439
+ emitClick(bar) {
440
+ this.barClick.emit({
441
+ seriesKey: bar.seriesKey,
442
+ datumIndex: bar.datumIndex,
443
+ category: bar.category,
444
+ value: bar.value,
445
+ datum: this.data()[bar.datumIndex],
446
+ });
447
+ }
448
+ setActivePoint(event, bar) {
449
+ const center = elementClientCenter(event.currentTarget);
450
+ this.root.activePoint.set({
451
+ index: bar.datumIndex,
452
+ seriesKey: bar.seriesKey,
453
+ clientX: center?.clientX,
454
+ clientY: center?.clientY,
455
+ });
456
+ }
457
+ clearActivePoint() {
458
+ this.root.activePoint.set(null);
459
+ }
460
+ barOpacity(bar) {
461
+ return this.emphasisOpacity(bar.active);
462
+ }
463
+ dotCellOpacity(cell) {
464
+ return this.emphasisOpacity(cell.active);
465
+ }
466
+ /** In `dot` style the bar rect stays as an invisible interaction target. */
467
+ barFill(bar) {
468
+ return this.styles() === 'dot' ? 'transparent' : bar.color;
469
+ }
470
+ emphasisOpacity(active) {
471
+ return this.activeKey() && this.activeValue() !== undefined ? (active ? 1 : 0.42) : 1;
472
+ }
473
+ formatValueLabel(bar) {
474
+ return this.valueLabelFormat()(bar.value);
475
+ }
476
+ barLabelX(bar) {
477
+ if (this.orientation() === 'vertical') {
478
+ return bar.x + bar.width / 2;
479
+ }
480
+ return bar.value >= 0 ? bar.x + bar.width + 6 : bar.x - 6;
481
+ }
482
+ barLabelY(bar) {
483
+ if (this.orientation() === 'vertical') {
484
+ return bar.value >= 0 ? bar.y - 8 : bar.y + bar.height + 10;
485
+ }
486
+ return bar.y + bar.height / 2;
487
+ }
488
+ barLabelAnchor(bar) {
489
+ if (this.orientation() === 'vertical') {
490
+ return 'middle';
491
+ }
492
+ return bar.value >= 0 ? 'start' : 'end';
493
+ }
494
+ barAriaLabel(bar) {
495
+ const label = this.root.config()[bar.seriesKey]?.label ?? bar.seriesKey;
496
+ return `${label} in ${bar.category}: ${bar.value}`;
497
+ }
498
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BarChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
499
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BarChart, isStandalone: true, selector: "ChartBar", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, xKey: { classPropertyName: "xKey", publicName: "xKey", isSignal: true, isRequired: true, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, styles: { classPropertyName: "styles", publicName: "styles", isSignal: true, isRequired: false, transformFunction: null }, margin: { classPropertyName: "margin", publicName: "margin", isSignal: true, isRequired: false, transformFunction: null }, bandPadding: { classPropertyName: "bandPadding", publicName: "bandPadding", isSignal: true, isRequired: false, transformFunction: null }, groupPadding: { classPropertyName: "groupPadding", publicName: "groupPadding", isSignal: true, isRequired: false, transformFunction: null }, cornerRadius: { classPropertyName: "cornerRadius", publicName: "cornerRadius", isSignal: true, isRequired: false, transformFunction: null }, colorKey: { classPropertyName: "colorKey", publicName: "colorKey", isSignal: true, isRequired: false, transformFunction: null }, activeKey: { classPropertyName: "activeKey", publicName: "activeKey", isSignal: true, isRequired: false, transformFunction: null }, activeValue: { classPropertyName: "activeValue", publicName: "activeValue", isSignal: true, isRequired: false, transformFunction: null }, showValueLabels: { classPropertyName: "showValueLabels", publicName: "showValueLabels", isSignal: true, isRequired: false, transformFunction: null }, valueLabelFormat: { classPropertyName: "valueLabelFormat", publicName: "valueLabelFormat", isSignal: true, isRequired: false, transformFunction: null }, dotSize: { classPropertyName: "dotSize", publicName: "dotSize", isSignal: true, isRequired: false, transformFunction: null }, dotGap: { classPropertyName: "dotGap", publicName: "dotGap", isSignal: true, isRequired: false, transformFunction: null }, dotCornerRadius: { classPropertyName: "dotCornerRadius", publicName: "dotCornerRadius", isSignal: true, isRequired: false, transformFunction: null }, showDotTrack: { classPropertyName: "showDotTrack", publicName: "showDotTrack", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { barClick: "barClick" }, host: { properties: { "attr.data-style": "styles()" }, classAttribute: "relative block h-full w-full" }, providers: [CartesianContext], ngImport: i0, template: `
500
+ <svg:svg
501
+ ChartPointerTracker
502
+ class="block h-full w-full overflow-visible"
503
+ [attr.viewBox]="viewBox()"
504
+ preserveAspectRatio="none"
505
+ role="img"
506
+ [attr.aria-label]="ariaSummary()">
507
+ <svg:g [attr.transform]="innerTransform()">
508
+ <ng-content select="svg\\:g[ChartGrid]" />
509
+ <svg:g class="chart-bars">
510
+ @if (styles() === 'dot') {
511
+ <svg:g class="chart-bar-dots" aria-hidden="true">
512
+ @for (cell of dotTrackCells(); track cell.key) {
513
+ <svg:rect
514
+ class="chart-bar-dot-track"
515
+ [attr.x]="cell.x"
516
+ [attr.y]="cell.y"
517
+ [attr.width]="cell.width"
518
+ [attr.height]="cell.height"
519
+ [attr.rx]="dotCornerRadius()"
520
+ [attr.ry]="dotCornerRadius()"
521
+ fill="hsl(var(--muted))" />
522
+ }
523
+ @for (cell of dotValueCells(); track cell.key) {
524
+ <svg:rect
525
+ class="chart-bar-dot-value"
526
+ [attr.x]="cell.x"
527
+ [attr.y]="cell.y"
528
+ [attr.width]="cell.width"
529
+ [attr.height]="cell.height"
530
+ [attr.rx]="dotCornerRadius()"
531
+ [attr.ry]="dotCornerRadius()"
532
+ [attr.fill]="cell.color"
533
+ [attr.opacity]="dotCellOpacity(cell)" />
534
+ }
535
+ </svg:g>
536
+ }
537
+ @for (bar of bars(); track bar.key) {
538
+ <svg:rect
539
+ class="chart-bar cursor-pointer outline-none transition-opacity hover:opacity-80"
540
+ [attr.x]="bar.x"
541
+ [attr.y]="bar.y"
542
+ [attr.width]="bar.width"
543
+ [attr.height]="bar.height"
544
+ [attr.rx]="cornerRadius()"
545
+ [attr.ry]="cornerRadius()"
546
+ [attr.fill]="barFill(bar)"
547
+ [attr.opacity]="barOpacity(bar)"
548
+ [attr.stroke]="bar.active ? 'hsl(var(--foreground))' : null"
549
+ [attr.stroke-width]="bar.active ? 1.5 : null"
550
+ [attr.aria-label]="barAriaLabel(bar)"
551
+ tabindex="0"
552
+ (focus)="setActivePoint($event, bar)"
553
+ (blur)="clearActivePoint()"
554
+ (click)="emitClick(bar)"
555
+ (keydown.enter)="emitClick(bar)"
556
+ (keydown.space)="emitClick(bar); $event.preventDefault()" />
557
+ @if (showValueLabels() && bar.height > 0 && bar.width > 0) {
558
+ <svg:text
559
+ class="chart-bar-value pointer-events-none fill-muted-foreground text-2xs"
560
+ [attr.x]="barLabelX(bar)"
561
+ [attr.y]="barLabelY(bar)"
562
+ [attr.text-anchor]="barLabelAnchor(bar)"
563
+ dominant-baseline="middle">
564
+ {{ formatValueLabel(bar) }}
565
+ </svg:text>
566
+ }
567
+ }
568
+ </svg:g>
569
+ <ng-content select="svg\\:g[ChartAxisX]" />
570
+ <ng-content select="svg\\:g[ChartAxisY]" />
571
+ <ng-content select="svg\\:g[ChartCrosshair]" />
572
+ <ng-content />
573
+ </svg:g>
574
+ </svg:svg>
575
+ <ng-content select="ChartTooltip" />
576
+ <ng-content select="ChartLegend" />
577
+ `, isInline: true, dependencies: [{ kind: "directive", type: ChartPointerTracker, selector: "svg:svg[ChartPointerTracker]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
578
+ }
579
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BarChart, decorators: [{
580
+ type: Component,
581
+ args: [{
582
+ selector: 'ChartBar',
583
+ changeDetection: ChangeDetectionStrategy.OnPush,
584
+ providers: [CartesianContext],
585
+ imports: [ChartPointerTracker],
586
+ host: { class: 'relative block h-full w-full', '[attr.data-style]': 'styles()' },
587
+ template: `
588
+ <svg:svg
589
+ ChartPointerTracker
590
+ class="block h-full w-full overflow-visible"
591
+ [attr.viewBox]="viewBox()"
592
+ preserveAspectRatio="none"
593
+ role="img"
594
+ [attr.aria-label]="ariaSummary()">
595
+ <svg:g [attr.transform]="innerTransform()">
596
+ <ng-content select="svg\\:g[ChartGrid]" />
597
+ <svg:g class="chart-bars">
598
+ @if (styles() === 'dot') {
599
+ <svg:g class="chart-bar-dots" aria-hidden="true">
600
+ @for (cell of dotTrackCells(); track cell.key) {
601
+ <svg:rect
602
+ class="chart-bar-dot-track"
603
+ [attr.x]="cell.x"
604
+ [attr.y]="cell.y"
605
+ [attr.width]="cell.width"
606
+ [attr.height]="cell.height"
607
+ [attr.rx]="dotCornerRadius()"
608
+ [attr.ry]="dotCornerRadius()"
609
+ fill="hsl(var(--muted))" />
610
+ }
611
+ @for (cell of dotValueCells(); track cell.key) {
612
+ <svg:rect
613
+ class="chart-bar-dot-value"
614
+ [attr.x]="cell.x"
615
+ [attr.y]="cell.y"
616
+ [attr.width]="cell.width"
617
+ [attr.height]="cell.height"
618
+ [attr.rx]="dotCornerRadius()"
619
+ [attr.ry]="dotCornerRadius()"
620
+ [attr.fill]="cell.color"
621
+ [attr.opacity]="dotCellOpacity(cell)" />
622
+ }
623
+ </svg:g>
624
+ }
625
+ @for (bar of bars(); track bar.key) {
626
+ <svg:rect
627
+ class="chart-bar cursor-pointer outline-none transition-opacity hover:opacity-80"
628
+ [attr.x]="bar.x"
629
+ [attr.y]="bar.y"
630
+ [attr.width]="bar.width"
631
+ [attr.height]="bar.height"
632
+ [attr.rx]="cornerRadius()"
633
+ [attr.ry]="cornerRadius()"
634
+ [attr.fill]="barFill(bar)"
635
+ [attr.opacity]="barOpacity(bar)"
636
+ [attr.stroke]="bar.active ? 'hsl(var(--foreground))' : null"
637
+ [attr.stroke-width]="bar.active ? 1.5 : null"
638
+ [attr.aria-label]="barAriaLabel(bar)"
639
+ tabindex="0"
640
+ (focus)="setActivePoint($event, bar)"
641
+ (blur)="clearActivePoint()"
642
+ (click)="emitClick(bar)"
643
+ (keydown.enter)="emitClick(bar)"
644
+ (keydown.space)="emitClick(bar); $event.preventDefault()" />
645
+ @if (showValueLabels() && bar.height > 0 && bar.width > 0) {
646
+ <svg:text
647
+ class="chart-bar-value pointer-events-none fill-muted-foreground text-2xs"
648
+ [attr.x]="barLabelX(bar)"
649
+ [attr.y]="barLabelY(bar)"
650
+ [attr.text-anchor]="barLabelAnchor(bar)"
651
+ dominant-baseline="middle">
652
+ {{ formatValueLabel(bar) }}
653
+ </svg:text>
654
+ }
655
+ }
656
+ </svg:g>
657
+ <ng-content select="svg\\:g[ChartAxisX]" />
658
+ <ng-content select="svg\\:g[ChartAxisY]" />
659
+ <ng-content select="svg\\:g[ChartCrosshair]" />
660
+ <ng-content />
661
+ </svg:g>
662
+ </svg:svg>
663
+ <ng-content select="ChartTooltip" />
664
+ <ng-content select="ChartLegend" />
665
+ `,
666
+ }]
667
+ }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], xKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "xKey", required: true }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], styles: [{ type: i0.Input, args: [{ isSignal: true, alias: "styles", required: false }] }], margin: [{ type: i0.Input, args: [{ isSignal: true, alias: "margin", required: false }] }], bandPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "bandPadding", required: false }] }], groupPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupPadding", required: false }] }], cornerRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "cornerRadius", required: false }] }], colorKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "colorKey", required: false }] }], activeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeKey", required: false }] }], activeValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeValue", required: false }] }], showValueLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValueLabels", required: false }] }], valueLabelFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueLabelFormat", required: false }] }], dotSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "dotSize", required: false }] }], dotGap: [{ type: i0.Input, args: [{ isSignal: true, alias: "dotGap", required: false }] }], dotCornerRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "dotCornerRadius", required: false }] }], showDotTrack: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDotTrack", required: false }] }], barClick: [{ type: i0.Output, args: ["barClick"] }] } });
668
+
669
+ /**
670
+ * Generated bundle index. Do not edit.
671
+ */
672
+
673
+ export { BarChart, computeBarDotCells, computeBarLayout };
674
+ //# sourceMappingURL=ojiepermana-angular-chart-bar.mjs.map