@blotch/dolan 22.2.2 → 22.2.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.
- package/dist/commands/add.d.ts +6 -2
- package/dist/commands/add.js +32 -10
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/add.test.js +129 -9
- package/dist/commands/add.test.js.map +1 -1
- package/dist/commands/diff.d.ts +2 -1
- package/dist/commands/diff.js +38 -25
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/diff.test.js +72 -11
- package/dist/commands/diff.test.js.map +1 -1
- package/dist/commands/doctor.js +41 -27
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.js +93 -9
- package/dist/commands/doctor.test.js.map +1 -1
- package/dist/commands/eject.js +7 -0
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/eject.test.js +39 -0
- package/dist/commands/eject.test.js.map +1 -1
- package/dist/commands/info.js +3 -2
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/init.d.ts +37 -1
- package/dist/commands/init.js +149 -40
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +285 -26
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/install-packages.d.ts +6 -0
- package/dist/commands/install-packages.js +20 -0
- package/dist/commands/install-packages.js.map +1 -0
- package/dist/commands/install-packages.test.d.ts +1 -0
- package/dist/commands/install-packages.test.js +55 -0
- package/dist/commands/install-packages.test.js.map +1 -0
- package/dist/commands/list.js +21 -14
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/list.test.js +62 -5
- package/dist/commands/list.test.js.map +1 -1
- package/dist/commands/outdated.js +11 -10
- package/dist/commands/outdated.js.map +1 -1
- package/dist/commands/outdated.test.js +20 -5
- package/dist/commands/outdated.test.js.map +1 -1
- package/dist/commands/remove.d.ts +4 -0
- package/dist/commands/remove.js +17 -4
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/remove.test.js +53 -2
- package/dist/commands/remove.test.js.map +1 -1
- package/dist/commands/update.d.ts +6 -0
- package/dist/commands/update.js +59 -24
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/update.test.js +273 -31
- package/dist/commands/update.test.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/registry.d.ts +8 -0
- package/dist/registry.js +32 -24
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +76 -37
- package/dist/registry.test.js.map +1 -1
- package/dist/testing/set-input-signal.d.ts +13 -0
- package/dist/testing/set-input-signal.js +20 -0
- package/dist/testing/set-input-signal.js.map +1 -0
- package/dist/testing/set-input-signal.test.d.ts +1 -0
- package/dist/testing/set-input-signal.test.js +20 -0
- package/dist/testing/set-input-signal.test.js.map +1 -0
- package/dist/ui/components/action-sheet/action-sheet.test.ts +48 -1
- package/dist/ui/components/action-sheet/action-sheet.ts +37 -9
- package/dist/ui/components/alert-dialog/alert-dialog.test.ts +49 -2
- package/dist/ui/components/alert-dialog/alert-dialog.ts +43 -12
- package/dist/ui/components/area-chart/area-chart-render.test.ts +52 -110
- package/dist/ui/components/area-chart/area-chart.test.ts +58 -123
- package/dist/ui/components/area-chart/area-chart.ts +100 -315
- package/dist/ui/components/area-chart/index.ts +2 -3
- package/dist/ui/components/bar-chart/bar-chart-render.test.ts +47 -163
- package/dist/ui/components/bar-chart/bar-chart.test.ts +83 -37
- package/dist/ui/components/bar-chart/bar-chart.ts +140 -294
- package/dist/ui/components/bar-chart/index.ts +3 -3
- package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +191 -9
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +136 -20
- package/dist/ui/components/button/button-render.test.ts +19 -0
- package/dist/ui/components/button/button.ts +18 -2
- package/dist/ui/components/candlestick-chart/candlestick-chart-render.test.ts +48 -163
- package/dist/ui/components/candlestick-chart/candlestick-chart.test.ts +106 -62
- package/dist/ui/components/candlestick-chart/candlestick-chart.ts +150 -323
- package/dist/ui/components/candlestick-chart/index.ts +3 -4
- package/dist/ui/components/chart/chart-focus.test.ts +132 -0
- package/dist/ui/components/chart/chart-focus.ts +115 -0
- package/dist/ui/components/chart/chart-presets.test.ts +139 -0
- package/dist/ui/components/chart/chart-presets.ts +271 -0
- package/dist/ui/components/chart/chart-render.test.ts +730 -0
- package/dist/ui/components/chart/chart-scene.test.ts +589 -0
- package/dist/ui/components/chart/chart-scene.ts +483 -0
- package/dist/ui/components/chart/chart-theme.test.ts +85 -0
- package/dist/ui/components/chart/chart-theme.ts +198 -0
- package/dist/ui/components/chart/chart-zoom.test.ts +159 -0
- package/dist/ui/components/chart/chart-zoom.ts +162 -0
- package/dist/ui/components/chart/chart.ts +921 -0
- package/dist/ui/components/chart/index.ts +24 -0
- package/dist/ui/components/dialog/dialog.test.ts +49 -2
- package/dist/ui/components/dialog/dialog.ts +43 -12
- package/dist/ui/components/icon/icon-theme.test.ts +133 -0
- package/dist/ui/components/icon/icon-theme.ts +204 -0
- package/dist/ui/components/icon/icon.test.ts +51 -2
- package/dist/ui/components/icon/icon.ts +38 -21
- package/dist/ui/components/line-chart/index.ts +2 -3
- package/dist/ui/components/line-chart/line-chart-render.test.ts +69 -107
- package/dist/ui/components/line-chart/line-chart.test.ts +63 -45
- package/dist/ui/components/line-chart/line-chart.ts +83 -241
- package/dist/ui/components/list/list.test.ts +69 -2
- package/dist/ui/components/list/list.ts +21 -15
- package/dist/ui/components/nav-drawer/nav-drawer.test.ts +80 -1
- package/dist/ui/components/nav-drawer/nav-drawer.ts +66 -11
- package/dist/ui/components/pagination/pagination.test.ts +62 -0
- package/dist/ui/components/pagination/pagination.ts +55 -8
- package/dist/ui/components/scatter-chart/index.ts +2 -3
- package/dist/ui/components/scatter-chart/scatter-chart-render.test.ts +51 -144
- package/dist/ui/components/scatter-chart/scatter-chart.test.ts +57 -25
- package/dist/ui/components/scatter-chart/scatter-chart.ts +104 -250
- package/dist/ui/components/scroll-area/scroll-area.test.ts +37 -2
- package/dist/ui/components/scroll-area/scroll-area.ts +8 -8
- package/dist/ui/components/spinner/spinner.test.ts +27 -0
- package/dist/ui/components/spinner/spinner.ts +33 -23
- package/dist/ui/components/toast/toast.test.ts +37 -0
- package/dist/ui/components/toast/toast.ts +20 -4
- package/dist/ui/theme/chart-colors.test.ts +30 -0
- package/dist/ui/theme/dark.css +8 -0
- package/dist/ui/theme/default.css +8 -0
- package/dist/ui/theme/icon-colors.test.ts +30 -0
- package/dist/ui/theme/tailwind-plugin.ts +10 -1
- package/dist/utils/installed.d.ts +17 -0
- package/dist/utils/installed.js +27 -0
- package/dist/utils/installed.js.map +1 -0
- package/dist/utils/installed.test.d.ts +1 -0
- package/dist/utils/installed.test.js +41 -0
- package/dist/utils/installed.test.js.map +1 -0
- package/dist/utils/interactive.d.ts +9 -0
- package/dist/utils/interactive.js +10 -0
- package/dist/utils/interactive.js.map +1 -0
- package/dist/utils/interactive.test.d.ts +1 -0
- package/dist/utils/interactive.test.js +24 -0
- package/dist/utils/interactive.test.js.map +1 -0
- package/dist/utils/packages.d.ts +36 -0
- package/dist/utils/packages.js +70 -0
- package/dist/utils/packages.js.map +1 -0
- package/dist/utils/packages.test.d.ts +1 -0
- package/dist/utils/packages.test.js +89 -0
- package/dist/utils/packages.test.js.map +1 -0
- package/dist/utils/resolve-paths.d.ts +17 -1
- package/dist/utils/resolve-paths.js +23 -3
- package/dist/utils/resolve-paths.js.map +1 -1
- package/dist/utils/resolve-paths.test.js +34 -2
- package/dist/utils/resolve-paths.test.js.map +1 -1
- package/package.json +5 -1
- package/dist/ui/components/cartesian-chart/cartesian-chart-render.test.ts +0 -188
- package/dist/ui/components/cartesian-chart/cartesian-chart.test.ts +0 -525
- package/dist/ui/components/cartesian-chart/cartesian-chart.ts +0 -1140
- package/dist/ui/components/cartesian-chart/index.ts +0 -18
|
@@ -2,326 +2,172 @@ import {
|
|
|
2
2
|
Component,
|
|
3
3
|
ViewEncapsulation,
|
|
4
4
|
computed,
|
|
5
|
-
inject,
|
|
6
5
|
input,
|
|
7
6
|
output,
|
|
8
7
|
} from '@angular/core';
|
|
9
|
-
import {
|
|
8
|
+
import { barY, defineChart, rect } from '@tanstack/charts/universal';
|
|
9
|
+
import { scaleBand } from '@tanstack/charts/scales/band';
|
|
10
|
+
import type { ChartPoint } from '@tanstack/charts/types';
|
|
10
11
|
|
|
12
|
+
import { UiChart, tooltip } from '../chart/chart';
|
|
11
13
|
import {
|
|
12
|
-
type
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// so it reuses the shared `ChartPoint` rather than defining its own. That also
|
|
24
|
-
// lets a bar series and a line/area series share one dataset on the same axes.
|
|
25
|
-
|
|
26
|
-
/** One bar after projection into plot-local pixel space. */
|
|
27
|
-
type ProjectedBar = {
|
|
28
|
-
/** Center x of the bar, in px. */
|
|
29
|
-
cx: number;
|
|
30
|
-
/** Pixel y of the bar's value (its far edge from the baseline). */
|
|
31
|
-
valueY: number;
|
|
14
|
+
type CartesianPresetOptions,
|
|
15
|
+
PRESET_CHART_BINDINGS,
|
|
16
|
+
UiCartesianPreset,
|
|
17
|
+
minSpacing,
|
|
18
|
+
presetAxes,
|
|
19
|
+
} from '../chart/chart-presets';
|
|
20
|
+
|
|
21
|
+
/** One bar: a category (`'Mon'`) or a number on a continuous x axis. */
|
|
22
|
+
export type BarDatum = {
|
|
23
|
+
readonly x: number | string;
|
|
24
|
+
readonly y: number;
|
|
32
25
|
};
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
27
|
+
export type BarChartOptions = {
|
|
28
|
+
readonly data: readonly BarDatum[];
|
|
29
|
+
/** Any CSS color; theme tokens like `var(--primary)` follow dark mode. */
|
|
30
|
+
readonly color?: string;
|
|
31
|
+
/** One color per bar, cycling; overrides `color` when not empty. */
|
|
32
|
+
readonly colors?: readonly string[];
|
|
33
|
+
/** Bar width as a fraction of the space each bar has, `0`–`1`. */
|
|
34
|
+
readonly barWidthRatio?: number;
|
|
35
|
+
/** The value bars grow from; negative values grow down from it. */
|
|
36
|
+
readonly baseline?: number;
|
|
37
|
+
/** Corner radius in px. */
|
|
38
|
+
readonly radius?: number;
|
|
39
|
+
} & CartesianPresetOptions;
|
|
45
40
|
|
|
46
41
|
/**
|
|
47
|
-
*
|
|
48
|
-
* ready to render as `<view>`s. A bar is the simplest chart mark on Lynx: one
|
|
49
|
-
* axis-aligned box from the shared `baseY` (the baseline's pixel y) to the
|
|
50
|
-
* value, so this is a straight geometric mapping with no rotation or fill
|
|
51
|
-
* rasterization (unlike the line and area series).
|
|
52
|
-
*
|
|
53
|
-
* Screen y grows *downward*, so a value above the baseline has a *smaller*
|
|
54
|
-
* `valueY` than `baseY`. Taking `top = min(valueY, baseY)` therefore anchors the
|
|
55
|
-
* rectangle at whichever edge is visually higher, which makes bars for negative
|
|
56
|
-
* values (value below the baseline) grow *downward* from the baseline correctly.
|
|
57
|
-
*
|
|
58
|
-
* Unlike a candlestick doji, `minBarHeight` defaults to `0`: a bar's height *is*
|
|
59
|
-
* its datum, so a value exactly on the baseline should draw nothing rather than
|
|
60
|
-
* a misleading sliver. Callers wanting every bar guaranteed visible can raise it.
|
|
42
|
+
* The definition `<ui-bar-chart>` renders.
|
|
61
43
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
44
|
+
* With category labels (any string `x`) the bars sit on a band scale. With
|
|
45
|
+
* numbers they sit on a linear axis at their values, `barWidthRatio` of the
|
|
46
|
+
* smallest gap wide, so a sparse series keeps its spacing and the chart can
|
|
47
|
+
* pan and zoom along x.
|
|
65
48
|
*/
|
|
66
|
-
export const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
49
|
+
export const barChartDefinition = (options: BarChartOptions) => {
|
|
50
|
+
const {
|
|
51
|
+
data,
|
|
52
|
+
color = 'var(--primary)',
|
|
53
|
+
colors = [],
|
|
54
|
+
barWidthRatio = 0.7,
|
|
55
|
+
baseline = 0,
|
|
56
|
+
radius = 0,
|
|
57
|
+
} = options;
|
|
58
|
+
const ratio = Math.min(1, Math.max(0, barWidthRatio));
|
|
59
|
+
const categorical = data.some((d) => typeof d.x === 'string');
|
|
60
|
+
const xs = categorical ? [] : data.map((d) => d.x as number);
|
|
61
|
+
const spacing = minSpacing(xs);
|
|
62
|
+
const axes = presetAxes(options, {
|
|
63
|
+
x: xs,
|
|
64
|
+
y: data.map((d) => d.y),
|
|
65
|
+
yInclude: [baseline],
|
|
66
|
+
// Half a slot of room either side so the edge bars fit inside the plot.
|
|
67
|
+
xMargin: spacing / 2,
|
|
76
68
|
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* A single bar series drawn inside a `<ui-cartesian-chart>`. Reads the shared
|
|
103
|
-
* scales from the parent chart via DI and renders one `<view>` rectangle per
|
|
104
|
-
* datum, from the shared `baseline` up (or down) to the value.
|
|
105
|
-
*
|
|
106
|
-
* ## Why the host is `position: absolute; width/height: 100%`
|
|
107
|
-
* Identical to `UiLineSeries`: Lynx resolves an absolute child against its
|
|
108
|
-
* **direct parent** (there is no `position: static`), so the host itself must be
|
|
109
|
-
* the plot-sized containing block. Every bar is a direct child of it.
|
|
110
|
-
*
|
|
111
|
-
* ## Per-bar colors
|
|
112
|
-
* `color` fills every bar by default. Pass `colors` to give each bar its own
|
|
113
|
-
* hue (a categorical bar chart) — the palette cycles if it is shorter than the
|
|
114
|
-
* data, so a handful of colors tint any number of bars.
|
|
115
|
-
*/
|
|
116
|
-
@Component({
|
|
117
|
-
selector: 'ui-bar-series',
|
|
118
|
-
standalone: true,
|
|
119
|
-
imports: [LYNX_ELEMENTS],
|
|
120
|
-
encapsulation: ViewEncapsulation.None,
|
|
121
|
-
host: {
|
|
122
|
-
style:
|
|
123
|
-
'position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;',
|
|
124
|
-
'[class]': 'userClass()',
|
|
125
|
-
},
|
|
126
|
-
template: `
|
|
127
|
-
@for (bar of bars(); track $index) {
|
|
128
|
-
<view [style]="bar.style" (bindtap)="onBarTap(bar.point)"></view>
|
|
129
|
-
}
|
|
130
|
-
`,
|
|
131
|
-
})
|
|
132
|
-
export class UiBarSeries {
|
|
133
|
-
readonly #chart = inject(UiCartesianChart);
|
|
134
|
-
|
|
135
|
-
readonly data = input.required<ChartPoint[]>();
|
|
136
|
-
/** Fill for every bar. Overridden per bar by `colors` when that is set. */
|
|
137
|
-
readonly color = input('var(--primary)');
|
|
138
|
-
/** Per-bar fills (a categorical palette). Cycles when shorter than the data;
|
|
139
|
-
* an empty array falls back to `color`. */
|
|
140
|
-
readonly colors = input<string[]>([]);
|
|
141
|
-
/** Bar width as a fraction of the spacing between adjacent bars. 0.7 leaves a
|
|
142
|
-
* ~30% gap between bars, the conventional bar-chart look. */
|
|
143
|
-
readonly barWidthRatio = input(0.7);
|
|
144
|
-
/** Data-space y the bars grow from (usually 0). Values below it grow downward. */
|
|
145
|
-
readonly baseline = input(0);
|
|
146
|
-
/** Corner radius of each bar in px (0 = square corners). */
|
|
147
|
-
readonly radius = input(0);
|
|
148
|
-
readonly userClass = input<string>('', { alias: 'class' });
|
|
149
|
-
|
|
150
|
-
readonly barTap = output<ChartPoint>();
|
|
151
|
-
|
|
152
|
-
// Project each datum into plot-local pixel space via the shared scales.
|
|
153
|
-
// Reading the chart's scale signals here makes this recompute whenever the
|
|
154
|
-
// domain or plot size changes.
|
|
155
|
-
readonly #projected = computed(() => {
|
|
156
|
-
const scaleX = this.#chart.xScale();
|
|
157
|
-
const scaleY = this.#chart.yScale();
|
|
158
|
-
return this.data().map((point) => ({
|
|
159
|
-
cx: scaleX(point.x),
|
|
160
|
-
valueY: scaleY(point.y),
|
|
161
|
-
point,
|
|
162
|
-
}));
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// Bar width = a fraction of the pixel spacing between bar centers, so bars
|
|
166
|
-
// scale with the plot and never overlap. With one bar there is no spacing to
|
|
167
|
-
// measure, so fall back to a fixed width.
|
|
168
|
-
readonly #barWidth = computed(() => {
|
|
169
|
-
const projected = this.#projected();
|
|
170
|
-
const n = projected.length;
|
|
171
|
-
if (n < 2) return SINGLE_BAR_WIDTH;
|
|
172
|
-
const span = Math.abs(projected[n - 1].cx - projected[0].cx);
|
|
173
|
-
return (span / (n - 1)) * this.barWidthRatio();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
protected readonly bars = computed(() => {
|
|
177
|
-
const projected = this.#projected();
|
|
178
|
-
const fallback = this.color();
|
|
179
|
-
const palette = this.colors();
|
|
180
|
-
const radius = this.radius();
|
|
181
|
-
// Clamp the baseline into the plot so a baseline outside the domain anchors
|
|
182
|
-
// the bars to the nearest edge (top/bottom) instead of drawing off-plot.
|
|
183
|
-
const baseY = clamp(
|
|
184
|
-
this.#chart.yScale()(this.baseline()),
|
|
185
|
-
0,
|
|
186
|
-
this.#chart.plotHeight(),
|
|
187
|
-
);
|
|
188
|
-
// computeBars works purely from the projected pixel fields; zip its output
|
|
189
|
-
// back with each source point so a bar's tap can emit the original datum.
|
|
190
|
-
return computeBars(projected, baseY, this.#barWidth()).map((shape, i) => {
|
|
191
|
-
const fill = palette.length ? palette[i % palette.length] : fallback;
|
|
192
|
-
return {
|
|
193
|
-
point: projected[i].point,
|
|
194
|
-
style:
|
|
195
|
-
`position: absolute; left: ${px(shape.left)}; top: ${px(shape.top)}; ` +
|
|
196
|
-
`width: ${px(shape.width)}; height: ${px(shape.height)}; ` +
|
|
197
|
-
`background-color: ${fill}; border-radius: ${px(radius)};`,
|
|
198
|
-
};
|
|
69
|
+
const tooltipOption = options.tooltip === false ? false : tooltip;
|
|
70
|
+
|
|
71
|
+
if (categorical) {
|
|
72
|
+
return defineChart({
|
|
73
|
+
marks: [
|
|
74
|
+
barY(data, {
|
|
75
|
+
x: (d) => String(d.x),
|
|
76
|
+
y: 'y',
|
|
77
|
+
y1: baseline,
|
|
78
|
+
fill: colors.length
|
|
79
|
+
? (_d, { index }) => colors[index % colors.length]!
|
|
80
|
+
: color,
|
|
81
|
+
radius,
|
|
82
|
+
}),
|
|
83
|
+
],
|
|
84
|
+
scales: {
|
|
85
|
+
x: {
|
|
86
|
+
scale: () => scaleBand().padding(1 - ratio),
|
|
87
|
+
grid: options.showXGrid ?? false,
|
|
88
|
+
axis: { label: options.xAxisLabel || undefined },
|
|
89
|
+
},
|
|
90
|
+
y: axes.y,
|
|
91
|
+
},
|
|
92
|
+
tooltip: tooltipOption,
|
|
199
93
|
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
protected onBarTap(point: ChartPoint): void {
|
|
203
|
-
this.barTap.emit(point);
|
|
204
94
|
}
|
|
205
|
-
|
|
95
|
+
|
|
96
|
+
const half = (ratio * spacing) / 2;
|
|
97
|
+
return defineChart({
|
|
98
|
+
marks: [
|
|
99
|
+
rect(data, {
|
|
100
|
+
x1: (d) => (d.x as number) - half,
|
|
101
|
+
x2: (d) => (d.x as number) + half,
|
|
102
|
+
y1: () => baseline,
|
|
103
|
+
y2: 'y',
|
|
104
|
+
// rect's fill is a single color, so per-bar colors go through the
|
|
105
|
+
// color scale, keyed by bar index.
|
|
106
|
+
...(colors.length
|
|
107
|
+
? { color: (_d: BarDatum, { index }: { index: number }) => index }
|
|
108
|
+
: { fill: color }),
|
|
109
|
+
radius,
|
|
110
|
+
}),
|
|
111
|
+
],
|
|
112
|
+
scales: axes,
|
|
113
|
+
...(colors.length
|
|
114
|
+
? {
|
|
115
|
+
color: {
|
|
116
|
+
domain: data.map((_d, index) => index),
|
|
117
|
+
range: data.map((_d, index) => colors[index % colors.length]!),
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
: {}),
|
|
121
|
+
tooltip: tooltipOption,
|
|
122
|
+
});
|
|
123
|
+
};
|
|
206
124
|
|
|
207
125
|
/**
|
|
208
|
-
* A
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
126
|
+
* A bar chart for one series. For grouped or stacked bars, horizontal bars or
|
|
127
|
+
* a line over the bars, build a definition with `barY`/`barX` and render it
|
|
128
|
+
* with `<ui-chart>`.
|
|
129
|
+
*
|
|
130
|
+
* ```html
|
|
131
|
+
* <ui-bar-chart [data]="sales" [radius]="4" />
|
|
132
|
+
* ```
|
|
213
133
|
*/
|
|
214
134
|
@Component({
|
|
215
135
|
selector: 'ui-bar-chart',
|
|
216
|
-
|
|
217
|
-
imports: [LYNX_ELEMENTS, UiCartesianChart, UiBarSeries],
|
|
136
|
+
imports: [UiChart],
|
|
218
137
|
encapsulation: ViewEncapsulation.None,
|
|
219
138
|
template: `
|
|
220
|
-
<ui-
|
|
221
|
-
[
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
[width]="width()"
|
|
226
|
-
[height]="height()"
|
|
227
|
-
[tickCount]="tickCount()"
|
|
228
|
-
[showXGrid]="showXGrid()"
|
|
229
|
-
[xAxisLabel]="xAxisLabel()"
|
|
230
|
-
[yAxisLabel]="yAxisLabel()"
|
|
231
|
-
[padding]="effectivePadding()"
|
|
232
|
-
[xTickFormat]="xTickFormat()"
|
|
233
|
-
[yTickFormat]="yTickFormat()"
|
|
234
|
-
[zoomable]="zoomable()"
|
|
235
|
-
[zoomAxes]="zoomAxes()"
|
|
236
|
-
[showZoomControls]="showZoomControls()"
|
|
237
|
-
[minZoom]="minZoom()"
|
|
238
|
-
[maxZoom]="maxZoom()"
|
|
239
|
-
[zoomStep]="zoomStep()"
|
|
240
|
-
[class]="userClass()"
|
|
241
|
-
>
|
|
242
|
-
<ui-bar-series
|
|
243
|
-
[data]="data()"
|
|
244
|
-
[color]="color()"
|
|
245
|
-
[colors]="colors()"
|
|
246
|
-
[barWidthRatio]="barWidthRatio()"
|
|
247
|
-
[baseline]="baseline()"
|
|
248
|
-
[radius]="radius()"
|
|
249
|
-
(barTap)="barTap.emit($event)"
|
|
250
|
-
/>
|
|
251
|
-
</ui-cartesian-chart>
|
|
139
|
+
<ui-chart
|
|
140
|
+
[definition]="definition()"
|
|
141
|
+
${PRESET_CHART_BINDINGS}
|
|
142
|
+
(pointSelect)="onSelect($event)"
|
|
143
|
+
/>
|
|
252
144
|
`,
|
|
253
145
|
})
|
|
254
|
-
export class UiBarChart {
|
|
255
|
-
readonly data = input.required<
|
|
146
|
+
export class UiBarChart extends UiCartesianPreset<BarDatum> {
|
|
147
|
+
readonly data = input.required<readonly BarDatum[]>();
|
|
256
148
|
readonly color = input('var(--primary)');
|
|
257
|
-
readonly colors = input<string[]>([]);
|
|
258
|
-
readonly width = input(320);
|
|
259
|
-
readonly height = input(200);
|
|
260
|
-
readonly tickCount = input(5);
|
|
149
|
+
readonly colors = input<readonly string[]>([]);
|
|
261
150
|
readonly barWidthRatio = input(0.7);
|
|
262
|
-
/** Data-space y the bars grow from (usually 0). Values below it grow downward. */
|
|
263
151
|
readonly baseline = input(0);
|
|
264
|
-
/** Corner radius of each bar in px. */
|
|
265
152
|
readonly radius = input(0);
|
|
266
|
-
/** Draw vertical gridlines at each x tick. */
|
|
267
|
-
readonly showXGrid = input(false);
|
|
268
|
-
/** Title for the x-axis (centered below the tick labels). */
|
|
269
|
-
readonly xAxisLabel = input<string>('');
|
|
270
|
-
/** Title for the y-axis (rotated in the left gutter). */
|
|
271
|
-
readonly yAxisLabel = input<string>('');
|
|
272
|
-
/** Inner inset of the plot area — merged over the auto half-bar padding
|
|
273
|
-
* (below), so any side you set wins. See {@link ChartPadding}. */
|
|
274
|
-
readonly padding = input<ChartPadding>({});
|
|
275
|
-
/** Force the x-axis lower/upper bound instead of deriving it from the data.
|
|
276
|
-
* Omitted bounds fall back to the data extremes. */
|
|
277
|
-
readonly xMin = input<number | undefined>(undefined);
|
|
278
|
-
readonly xMax = input<number | undefined>(undefined);
|
|
279
|
-
/** Force the y-axis lower/upper bound. The `baseline` is still folded in, so
|
|
280
|
-
* `yMin` only lowers the floor further — it can't clip a bar's base off. */
|
|
281
|
-
readonly yMin = input<number | undefined>(undefined);
|
|
282
|
-
readonly yMax = input<number | undefined>(undefined);
|
|
283
|
-
readonly xTickFormat = input<(value: number) => string>(defaultTickFormat);
|
|
284
|
-
readonly yTickFormat = input<(value: number) => string>(defaultTickFormat);
|
|
285
|
-
readonly userClass = input<string>('', { alias: 'class' });
|
|
286
|
-
|
|
287
|
-
// Pan/zoom — forwarded to the underlying <ui-cartesian-chart>. Off by default
|
|
288
|
-
// so a bar chart stays static (and scroll-view friendly) unless opted in.
|
|
289
|
-
readonly zoomable = input(false);
|
|
290
|
-
readonly zoomAxes = input<'x' | 'y' | 'xy'>('xy');
|
|
291
|
-
readonly showZoomControls = input(true);
|
|
292
|
-
readonly minZoom = input(1);
|
|
293
|
-
readonly maxZoom = input(8);
|
|
294
|
-
readonly zoomStep = input(1.4);
|
|
295
153
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
protected
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const hi = this.yMax() ?? Math.max(base, ...ys);
|
|
316
|
-
return niceScale(lo, hi, this.tickCount());
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
// A bar is centered on its x, so the first and last bars would spill over the
|
|
320
|
-
// plot edges. Insetting the scales by half a bar's spacing keeps them fully
|
|
321
|
-
// inside. User `padding` is spread last, so an explicit side wins.
|
|
322
|
-
protected readonly effectivePadding = computed<ChartPadding>(() => {
|
|
323
|
-
const n = this.data().length;
|
|
324
|
-
const half = n > 1 ? 0.5 / (n - 1) : 0;
|
|
325
|
-
return { left: half, right: half, ...this.padding() };
|
|
326
|
-
});
|
|
154
|
+
/** The tapped bar's datum. */
|
|
155
|
+
readonly barTap = output<BarDatum>();
|
|
156
|
+
|
|
157
|
+
protected readonly definition = computed(() =>
|
|
158
|
+
barChartDefinition({
|
|
159
|
+
...this.commonOptions(),
|
|
160
|
+
data: this.data(),
|
|
161
|
+
color: this.color(),
|
|
162
|
+
colors: this.colors(),
|
|
163
|
+
barWidthRatio: this.barWidthRatio(),
|
|
164
|
+
baseline: this.baseline(),
|
|
165
|
+
radius: this.radius(),
|
|
166
|
+
}),
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
protected onSelect(point: ChartPoint | null): void {
|
|
170
|
+
const datum = this.datumOf(point);
|
|
171
|
+
if (datum) this.barTap.emit(datum);
|
|
172
|
+
}
|
|
327
173
|
}
|