@tsingroc/tsingroc-components 5.0.0-alpha.12 → 5.0.0-alpha.13

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.
@@ -0,0 +1,22 @@
1
+ import type { MarkAreaComponentOption } from "echarts";
2
+ import type { ZRStyleProps } from "echarts/types/src/util/types.js";
3
+ import { type EChartsHOCType } from "./ECharts";
4
+ export interface SelectableEChartsProps {
5
+ /** 编辑器的选中方式。 @default "add" */
6
+ mode?: "add" | "subtract";
7
+ /** 可选中的系列名,若为 `undefined` 则均不可选中。(如果需要选中多个系列,可以嵌套多层 `withSelectable`) */
8
+ seriesName?: string;
9
+ /** 选中的数据点下标集合。 @default new Set() */
10
+ selectedIndices?: Set<number>;
11
+ /** 选中区域变化时的回调。 */
12
+ onSelectedIndicesChange?: (selectedIndices: Set<number>) => void;
13
+ /** 选框的样式,可使用 `fill` 和 `stroke` 修改颜色。 */
14
+ selectionBoxStyle?: Partial<ZRStyleProps>;
15
+ /** 是否用有色背景高亮选中点在 x 轴上的投影区域。只有 x 轴数值为 `number` 类型的点才能被高亮。 */
16
+ markArea?: boolean;
17
+ /** 每个点高亮背景的宽度。如果不填,默认为取相邻两点间距的最小值(x 轴数值必须为 `number` 类型)。 */
18
+ markAreaWidth?: number;
19
+ /** 高亮区域的样式,可使用 `color` 修改颜色。 */
20
+ markAreaStyle?: MarkAreaComponentOption["itemStyle"];
21
+ }
22
+ export declare const withSelectable: EChartsHOCType<SelectableEChartsProps>;
@@ -0,0 +1,402 @@
1
+ import { c as _c } from "react/compiler-runtime";
2
+ import { GraphicComponent, MarkAreaComponent } from "echarts/components";
3
+ import * as echarts from "echarts/core";
4
+ import { useEffectEvent, useState } from "react";
5
+ import { debugAssert } from "../utils";
6
+ import { destructureLineDataItem } from "../utils/destructureLineDataItem";
7
+ import { normalizeIntoArray } from "../utils/normalizeIntoArray";
8
+ import "./ECharts";
9
+ import { jsx as _jsx } from "react/jsx-runtime";
10
+ const DEFAULT_SELECTED_INDICES = new Set();
11
+ const DEFAULT_SELECTION_BOX_STYLE = {
12
+ fill: "#000",
13
+ fillOpacity: 16 / 255,
14
+ stroke: "#000",
15
+ strokeOpacity: 176 / 255,
16
+ lineWidth: 1
17
+ };
18
+ const DEFAULT_MARK_AREA_STYLE = {
19
+ color: "#000",
20
+ opacity: 16 / 255
21
+ };
22
+ export const withSelectable = ECharts => function SelectableECharts(props) {
23
+ "use memo";
24
+
25
+ const $ = _c(31);
26
+ let inputMarkAreaWidth;
27
+ let inputOption;
28
+ let markArea;
29
+ let markAreaStyle;
30
+ let onInit;
31
+ let onSelectedIndicesChange;
32
+ let rest;
33
+ let selectionBoxStyle;
34
+ let seriesName;
35
+ let t0;
36
+ let t1;
37
+ if ($[0] !== props) {
38
+ ({
39
+ option: inputOption,
40
+ onInit,
41
+ mode: t0,
42
+ seriesName,
43
+ selectedIndices: t1,
44
+ onSelectedIndicesChange,
45
+ selectionBoxStyle,
46
+ markArea,
47
+ markAreaStyle,
48
+ markAreaWidth: inputMarkAreaWidth,
49
+ ...rest
50
+ } = props);
51
+ $[0] = props;
52
+ $[1] = inputMarkAreaWidth;
53
+ $[2] = inputOption;
54
+ $[3] = markArea;
55
+ $[4] = markAreaStyle;
56
+ $[5] = onInit;
57
+ $[6] = onSelectedIndicesChange;
58
+ $[7] = rest;
59
+ $[8] = selectionBoxStyle;
60
+ $[9] = seriesName;
61
+ $[10] = t0;
62
+ $[11] = t1;
63
+ } else {
64
+ inputMarkAreaWidth = $[1];
65
+ inputOption = $[2];
66
+ markArea = $[3];
67
+ markAreaStyle = $[4];
68
+ onInit = $[5];
69
+ onSelectedIndicesChange = $[6];
70
+ rest = $[7];
71
+ selectionBoxStyle = $[8];
72
+ seriesName = $[9];
73
+ t0 = $[10];
74
+ t1 = $[11];
75
+ }
76
+ const mode = t0 === undefined ? "add" : t0;
77
+ const selectedIndices = t1 === undefined ? DEFAULT_SELECTED_INDICES : t1;
78
+ echarts.use(GraphicComponent);
79
+ echarts.use(MarkAreaComponent);
80
+ const inputSeriesArray = normalizeIntoArray(inputOption?.series);
81
+ let t2;
82
+ if ($[12] !== seriesName) {
83
+ t2 = it => it.name === seriesName;
84
+ $[12] = seriesName;
85
+ $[13] = t2;
86
+ } else {
87
+ t2 = $[13];
88
+ }
89
+ const inputSeries = inputSeriesArray.find(t2);
90
+ if (inputSeries && inputSeries.type !== "line") {
91
+ throw new Error("only line series is supported");
92
+ }
93
+ const inputTooltip = normalizeIntoArray(inputOption?.tooltip);
94
+ let markAreaWidth = inputMarkAreaWidth;
95
+ if (inputSeries?.data && markArea && !markAreaWidth) {
96
+ markAreaWidth = Infinity;
97
+ const xValues = inputSeries.data.map(_temp);
98
+ for (let i = 1; i < xValues.length; i++) {
99
+ const prevX = xValues[i - 1];
100
+ const thisX = xValues[i];
101
+ if (typeof prevX === "number" && typeof thisX === "number") {
102
+ markAreaWidth = Math.min(markAreaWidth, thisX - prevX);
103
+ }
104
+ }
105
+ if (markAreaWidth === Infinity) {
106
+ throw new Error("failed to automatically determine the width of mark area; please ensure the x axis value of the data points are number-typed, or provide the width manually");
107
+ }
108
+ }
109
+ const modelMatcher = inputSeries && ((inputSeries.xAxisId || inputSeries.xAxisIndex) && (inputSeries.yAxisId || inputSeries.yAxisIndex) || inputSeries.polarId || inputSeries.polarIndex) ? inputSeries : {
110
+ gridIndex: 0
111
+ };
112
+ const convertToPixel = (instance, t3) => {
113
+ const [px, py] = t3;
114
+ return px != null && py != null ? instance.convertToPixel(modelMatcher, [px, py]) : undefined;
115
+ };
116
+ const [mouseState, setMouseState] = useState();
117
+ let t4;
118
+ if ($[14] === Symbol.for("react.memo_cache_sentinel")) {
119
+ t4 = (instance_0, params) => setMouseState({
120
+ instance: instance_0,
121
+ start: [params.offsetX, params.offsetY]
122
+ });
123
+ $[14] = t4;
124
+ } else {
125
+ t4 = $[14];
126
+ }
127
+ const onMouseDown = useEffectEvent(t4);
128
+ let t5;
129
+ if ($[15] !== mouseState) {
130
+ t5 = params_0 => {
131
+ if (!mouseState) {
132
+ return;
133
+ }
134
+ setMouseState({
135
+ ...mouseState,
136
+ end: [params_0.offsetX, params_0.offsetY]
137
+ });
138
+ };
139
+ $[15] = mouseState;
140
+ $[16] = t5;
141
+ } else {
142
+ t5 = $[16];
143
+ }
144
+ const onMouseMove = useEffectEvent(t5);
145
+ const onMouseUp = useEffectEvent((instance_1, params_1) => {
146
+ if (!mouseState) {
147
+ return;
148
+ }
149
+ let eventTriggered = false;
150
+ if (onSelectedIndicesChange && inputSeries?.data) {
151
+ if (mouseState.end) {
152
+ const [startX, startY] = mouseState.start;
153
+ const {
154
+ offsetX,
155
+ offsetY
156
+ } = params_1;
157
+ const [minX, maxX] = [startX, offsetX].sort(_temp2);
158
+ const [minY, maxY] = [startY, offsetY].sort(_temp3);
159
+ const newSelectedIndices = new Set(selectedIndices);
160
+ inputSeries.data.forEach((point, i_0) => {
161
+ const destructured = destructureLineDataItem(point, i_0);
162
+ if (destructured == null) {
163
+ return;
164
+ }
165
+ const pixelValue = convertToPixel(instance_1, destructured[0]);
166
+ if (pixelValue === undefined) {
167
+ return;
168
+ }
169
+ const [x, y] = pixelValue;
170
+ const selected = minX <= x && x <= maxX && minY <= y && y <= maxY;
171
+ if (selected) {
172
+ if (mode === "add") {
173
+ newSelectedIndices.add(i_0);
174
+ } else {
175
+ newSelectedIndices.delete(i_0);
176
+ }
177
+ }
178
+ });
179
+ onSelectedIndicesChange(newSelectedIndices);
180
+ eventTriggered = true;
181
+ } else {
182
+ if (instance_1.containPixel(modelMatcher, mouseState.start)) {
183
+ let minDist = Infinity;
184
+ let minIdx = -1;
185
+ inputSeries.data.forEach((point_0, i_1) => {
186
+ const destructured_0 = destructureLineDataItem(point_0, i_1);
187
+ if (destructured_0 == null) {
188
+ return;
189
+ }
190
+ const pixelValue_0 = convertToPixel(instance_1, destructured_0[0]);
191
+ if (pixelValue_0 === undefined) {
192
+ return;
193
+ }
194
+ const [x_0] = pixelValue_0;
195
+ const dist = Math.abs(mouseState.start[0] - x_0);
196
+ if (dist < minDist) {
197
+ minDist = dist;
198
+ minIdx = i_1;
199
+ }
200
+ });
201
+ if (minIdx > 0) {
202
+ const newSelectedIndices_0 = new Set(selectedIndices);
203
+ if (mode === "add") {
204
+ newSelectedIndices_0.add(minIdx);
205
+ } else {
206
+ newSelectedIndices_0.delete(minIdx);
207
+ }
208
+ onSelectedIndicesChange(newSelectedIndices_0);
209
+ eventTriggered = true;
210
+ }
211
+ }
212
+ }
213
+ }
214
+ if (!eventTriggered) {
215
+ for (let elem = params_1.target; elem; elem = elem.parent) {
216
+ if (elem._$handlers?.click) {
217
+ elem._$handlers?.click.forEach(_temp4);
218
+ }
219
+ }
220
+ }
221
+ setMouseState(undefined);
222
+ });
223
+ const option = {
224
+ ...inputOption
225
+ };
226
+ if (inputSeries) {
227
+ if (selectedIndices.size > 0 || mode === "add" && mouseState?.end) {
228
+ const series = {
229
+ ...inputSeries,
230
+ showSymbol: true
231
+ };
232
+ const allSelectedIndices = new Set();
233
+ series.data = inputSeries.data?.map((point_1, i_2) => {
234
+ const destructured_1 = destructureLineDataItem(point_1, i_2);
235
+ if (destructured_1 == null) {
236
+ return point_1;
237
+ }
238
+ const [value, option_0] = destructured_1;
239
+ const stateSelected = selectedIndices.has(i_2);
240
+ let tempSelected = false;
241
+ if (mouseState?.end) {
242
+ const [minX_0, maxX_0] = [mouseState.start[0], mouseState.end[0]].sort(_temp5);
243
+ const [minY_0, maxY_0] = [mouseState.start[1], mouseState.end[1]].sort(_temp6);
244
+ const pixelValue_1 = convertToPixel(mouseState.instance, value);
245
+ if (pixelValue_1 !== undefined) {
246
+ const [x_1, y_0] = pixelValue_1;
247
+ tempSelected = minX_0 <= x_1 && x_1 <= maxX_0 && minY_0 <= y_0 && y_0 <= maxY_0;
248
+ }
249
+ }
250
+ if (mode === "add" ? stateSelected || tempSelected : stateSelected && !tempSelected) {
251
+ allSelectedIndices.add(i_2);
252
+ return {
253
+ value,
254
+ ...option_0,
255
+ symbol: "emptyCircle",
256
+ symbolSize: 6
257
+ };
258
+ } else {
259
+ return {
260
+ value,
261
+ symbol: "none",
262
+ ...option_0
263
+ };
264
+ }
265
+ });
266
+ if (markArea) {
267
+ debugAssert(markAreaWidth !== undefined, "when markArea === true, markAreaWidth is guaranteed to be defined at the beginning of this function");
268
+ const markAreas = [];
269
+ if (inputSeries.data) {
270
+ for (const i_3 of allSelectedIndices) {
271
+ const destructured_2 = destructureLineDataItem(inputSeries.data[i_3], i_3);
272
+ if (destructured_2 == null) {
273
+ continue;
274
+ }
275
+ const [t6] = destructured_2;
276
+ const [xValue] = t6;
277
+ if (typeof xValue !== "number") {
278
+ continue;
279
+ }
280
+ markAreas.push([{
281
+ coord: [xValue - markAreaWidth / 2]
282
+ }, {
283
+ coord: [xValue + markAreaWidth / 2]
284
+ }]);
285
+ }
286
+ }
287
+ series.markArea = {
288
+ itemStyle: {
289
+ ...DEFAULT_MARK_AREA_STYLE,
290
+ ...markAreaStyle
291
+ },
292
+ emphasis: {
293
+ disabled: true
294
+ },
295
+ data: markAreas
296
+ };
297
+ }
298
+ const seriesArray = [...inputSeriesArray];
299
+ seriesArray[seriesArray.indexOf(inputSeries)] = series;
300
+ option.series = seriesArray;
301
+ }
302
+ }
303
+ let t6;
304
+ if ($[17] !== selectionBoxStyle) {
305
+ t6 = {
306
+ ...DEFAULT_SELECTION_BOX_STYLE,
307
+ ...selectionBoxStyle
308
+ };
309
+ $[17] = selectionBoxStyle;
310
+ $[18] = t6;
311
+ } else {
312
+ t6 = $[18];
313
+ }
314
+ let t7;
315
+ if ($[19] !== mouseState) {
316
+ t7 = mouseState?.end ? {
317
+ ignore: false,
318
+ shape: {
319
+ x: mouseState.start[0],
320
+ y: mouseState.start[1],
321
+ width: mouseState.end[0] - mouseState.start[0],
322
+ height: mouseState.end[1] - mouseState.start[1]
323
+ }
324
+ } : {
325
+ ignore: true
326
+ };
327
+ $[19] = mouseState;
328
+ $[20] = t7;
329
+ } else {
330
+ t7 = $[20];
331
+ }
332
+ let t8;
333
+ if ($[21] !== t6 || $[22] !== t7) {
334
+ t8 = {
335
+ id: "selectionBox",
336
+ type: "rect",
337
+ z: 1000,
338
+ style: t6,
339
+ ...t7
340
+ };
341
+ $[21] = t6;
342
+ $[22] = t7;
343
+ $[23] = t8;
344
+ } else {
345
+ t8 = $[23];
346
+ }
347
+ option.graphic = [...normalizeIntoArray(option.graphic), t8];
348
+ if (inputTooltip.length) {
349
+ let t9;
350
+ if ($[24] !== mouseState?.end) {
351
+ t9 = it_1 => ({
352
+ ...it_1,
353
+ show: mouseState?.end === undefined
354
+ });
355
+ $[24] = mouseState?.end;
356
+ $[25] = t9;
357
+ } else {
358
+ t9 = $[25];
359
+ }
360
+ option.tooltip = inputTooltip.map(t9);
361
+ }
362
+ let t9;
363
+ if ($[26] !== onInit || $[27] !== onMouseDown || $[28] !== onMouseMove || $[29] !== onMouseUp) {
364
+ t9 = instance_2 => {
365
+ const zr = instance_2.getZr();
366
+ zr.on("mousedown", params_2 => onMouseDown(instance_2, params_2));
367
+ zr.on("mousemove", onMouseMove);
368
+ zr.on("mouseup", params_3 => onMouseUp(instance_2, params_3));
369
+ return onInit?.(instance_2);
370
+ };
371
+ $[26] = onInit;
372
+ $[27] = onMouseDown;
373
+ $[28] = onMouseMove;
374
+ $[29] = onMouseUp;
375
+ $[30] = t9;
376
+ } else {
377
+ t9 = $[30];
378
+ }
379
+ return /*#__PURE__*/_jsx(ECharts, {
380
+ option: option,
381
+ onInit: t9,
382
+ ...rest
383
+ });
384
+ };
385
+ function _temp(item, idx) {
386
+ return destructureLineDataItem(item, idx)?.[0][0];
387
+ }
388
+ function _temp2(a, b) {
389
+ return a - b;
390
+ }
391
+ function _temp3(a_0, b_0) {
392
+ return a_0 - b_0;
393
+ }
394
+ function _temp4(it_0) {
395
+ return it_0.h();
396
+ }
397
+ function _temp5(a_1, b_1) {
398
+ return a_1 - b_1;
399
+ }
400
+ function _temp6(a_2, b_2) {
401
+ return a_2 - b_2;
402
+ }
@@ -9,10 +9,11 @@ export function grid(option) {
9
9
  echarts.use(GridComponent);
10
10
  const grid = {
11
11
  left: 0,
12
- right: 0,
13
- top: 24,
12
+ right: 1,
13
+ top: 1,
14
14
  bottom: 0,
15
- containLabel: true,
15
+ outerBoundsContain: "all",
16
+ outerBoundsMode: "same",
16
17
  ...option.option
17
18
  };
18
19
  const xAxis = {
@@ -5,7 +5,7 @@ export function legend(option) {
5
5
  echarts.use(LegendComponent);
6
6
  return {
7
7
  legend: [{
8
- top: 0,
8
+ top: 1,
9
9
  right: 0,
10
10
  padding: 0,
11
11
  data: option.data,
@@ -10,9 +10,13 @@ import * as echarts from "echarts/core";
10
10
  *
11
11
  * [1]: https://echarts.apache.org/zh/option.html#tooltip
12
12
  */
13
- export function tooltip(option = {}) {
13
+ export function tooltip(option) {
14
14
  echarts.use(TooltipComponent);
15
15
  return {
16
- tooltip: [option]
16
+ tooltip: [{
17
+ trigger: "axis",
18
+ transitionDuration: 0,
19
+ ...option
20
+ }]
17
21
  };
18
22
  }
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export { default as LineChartTable, type LineChartTableProps, } from "./componen
13
13
  export { default as LinkedLineChart, LineChartLinkProvider, type LinkedLineChartProps, type LineChartLinkProviderProps, } from "./components/LinkedLineChart";
14
14
  export { default as QuickDateRangePicker, type QuickDateRangePickerProps, } from "./components/QuickDateRangePicker";
15
15
  export { default as SegmentedButtons, TimeUnitSwitcher, type TimeUnitSwitcherProps, } from "./components/SegmentedButtons";
16
+ export { withSelectable, type SelectableEChartsProps, } from "./components/SelectableECharts";
16
17
  export { default as Sidebar, type SidebarProps } from "./components/Sidebar";
17
18
  export { default as TsingrocDatePicker, type DatePickerProps, } from "./components/TsingrocDatePicker";
18
19
  export { default as TsingrocTheme, type TsingrocThemeProps, } from "./components/TsingrocTheme";
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ export { default as LineChartTable } from "./components/LineChartTable";
13
13
  export { default as LinkedLineChart, LineChartLinkProvider } from "./components/LinkedLineChart";
14
14
  export { default as QuickDateRangePicker } from "./components/QuickDateRangePicker";
15
15
  export { default as SegmentedButtons, TimeUnitSwitcher } from "./components/SegmentedButtons";
16
+ export { withSelectable } from "./components/SelectableECharts";
16
17
  export { default as Sidebar } from "./components/Sidebar";
17
18
  export { default as TsingrocDatePicker } from "./components/TsingrocDatePicker";
18
19
  export { default as TsingrocTheme } from "./components/TsingrocTheme";
@@ -0,0 +1,6 @@
1
+ import type { LineSeriesOption } from "echarts";
2
+ import type { OptionDataValue } from "echarts/types/src/util/types.js";
3
+ export type LineDataItem = LineSeriesOption["data"] extends (infer T)[] | undefined ? T : never;
4
+ export type LineDataValue = OptionDataValue | OptionDataValue[];
5
+ export type LineDataItemOption = LineDataItem extends LineDataValue | infer T ? T : never;
6
+ export declare function destructureLineDataItem(value: LineDataItem, i: number): [OptionDataValue[], LineDataItemOption | undefined] | null | undefined;
@@ -0,0 +1,17 @@
1
+ export function destructureLineDataItem(value, i) {
2
+ if (value == null) return value;
3
+ let rawValue,
4
+ option = undefined;
5
+ if (typeof value === "object" && !(value instanceof Date) && !Array.isArray(value)) {
6
+ ({
7
+ value: rawValue,
8
+ ...option
9
+ } = value);
10
+ } else {
11
+ rawValue = value;
12
+ }
13
+ if (!Array.isArray(rawValue)) {
14
+ rawValue = [i, rawValue];
15
+ }
16
+ return [rawValue, option];
17
+ }
@@ -0,0 +1 @@
1
+ export declare function filterMap<T, R>(array: T[], func: (elem: T, index: number, array: T[]) => R): NonNullable<R>[];
@@ -0,0 +1,11 @@
1
+ export function filterMap(array, func) {
2
+ const result = [];
3
+ for (let i = 0; i < array.length; i++) {
4
+ const elem = array[i];
5
+ const resultElem = func(elem, i, array);
6
+ if (resultElem != null) {
7
+ result.push(resultElem);
8
+ }
9
+ }
10
+ return result;
11
+ }
@@ -1,4 +1,8 @@
1
1
  export * from "./debug";
2
+ export * from "./destructureLineDataItem";
3
+ export * from "./filterMap";
2
4
  export * from "./math";
3
5
  export * from "./mock";
6
+ export * from "./normalizeIntoArray";
4
7
  export * from "./startOfQuarter";
8
+ export * from "./timeAxisLabel";
@@ -1,4 +1,8 @@
1
1
  export * from "./debug";
2
+ export * from "./destructureLineDataItem";
3
+ export * from "./filterMap";
2
4
  export * from "./math";
3
5
  export * from "./mock";
4
- export * from "./startOfQuarter";
6
+ export * from "./normalizeIntoArray";
7
+ export * from "./startOfQuarter";
8
+ export * from "./timeAxisLabel";
@@ -0,0 +1 @@
1
+ export declare function normalizeIntoArray<T extends object>(value: T | T[] | undefined): T[];
@@ -0,0 +1,3 @@
1
+ export function normalizeIntoArray(value) {
2
+ return value === undefined ? [] : Array.isArray(value) ? value : [value];
3
+ }
@@ -0,0 +1,5 @@
1
+ import { type Dayjs } from "dayjs";
2
+ import type { XAXisComponentOption } from "echarts";
3
+ export default function timeAxisLabel(startTime: Dayjs, endTime: Dayjs): (XAXisComponentOption & {
4
+ type: "time";
5
+ })["axisLabel"];
@@ -0,0 +1,18 @@
1
+ import dayjs from "dayjs";
2
+ export default function timeAxisLabel(startTime, endTime) {
3
+ return {
4
+ formatter: endTime.diff(startTime, "day", true) <= 1 ? value => dayjs(value).isSame(endTime) && endTime.hour() === 0 ? "24:00" : "{HH}:{mm}" : value => {
5
+ const time = dayjs(value);
6
+ if (time.hour() === 0 && time.minute() === 0) {
7
+ return "{date|{MM}-{dd}}";
8
+ } else {
9
+ return "{HH}:{mm}";
10
+ }
11
+ },
12
+ rich: {
13
+ date: {
14
+ fontWeight: "bold"
15
+ }
16
+ }
17
+ };
18
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsingroc/tsingroc-components",
3
- "version": "5.0.0-alpha.12",
3
+ "version": "5.0.0-alpha.13",
4
4
  "author": "",
5
5
  "license": "ISC",
6
6
  "description": "",