waveform-analysis 0.1.28 → 0.1.29
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/README.md +57 -33
- package/dist/index.cjs +1 -1
- package/dist/index.js +552 -548
- package/dist/types/components/core/trackLayoutBuilder.d.ts +2 -1
- package/dist/types/demo/types.d.ts +9 -0
- package/dist/types/demo/useDemoXAxisLabelControls.d.ts +32 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types/chart.d.ts +15 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/utils/formatters.d.ts +5 -2
- package/dist/types/utils/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/App.vue +7 -1
- package/src/components/core/trackLayoutBuilder.ts +22 -4
- package/src/components/core/useWaveformLayout.ts +1 -0
- package/src/components/rendering/WaveformTrackAxes.vue +6 -3
- package/src/components/waveformChartCases/frameGridAndFormatting.test.ts +70 -3
- package/src/demo/DemoGridFrameControls.vue +70 -0
- package/src/demo/types.ts +9 -0
- package/src/demo/useDemoXAxisLabelControls.ts +77 -0
- package/src/demo/xAxisLabelControls.test.ts +86 -0
- package/src/index.ts +3 -0
- package/src/styles.css +8 -0
- package/src/types/chart.ts +21 -0
- package/src/types/index.ts +3 -0
- package/src/utils/formatters.test.ts +37 -5
- package/src/utils/formatters.ts +26 -16
- package/src/utils/index.ts +1 -0
package/src/index.ts
CHANGED
package/src/styles.css
CHANGED
|
@@ -133,6 +133,14 @@ body {
|
|
|
133
133
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
.x-axis-label-controls {
|
|
137
|
+
display: grid;
|
|
138
|
+
gap: 10px;
|
|
139
|
+
margin-top: 12px;
|
|
140
|
+
padding-top: 12px;
|
|
141
|
+
border-top: 1px solid #eaecf0;
|
|
142
|
+
}
|
|
143
|
+
|
|
136
144
|
.grid-line-color-picker,
|
|
137
145
|
.grid-line-color-picker .vc-color-wrap {
|
|
138
146
|
width: 48px;
|
package/src/types/chart.ts
CHANGED
|
@@ -121,10 +121,31 @@ export interface WaveformFrameStyle {
|
|
|
121
121
|
backgroundColor?: string
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
export type WaveformXAxisLabelKind = 'tick' | 'start' | 'end'
|
|
125
|
+
|
|
126
|
+
/** Context passed to custom X-axis label formatters. */
|
|
127
|
+
export interface WaveformXAxisLabelFormatterContext {
|
|
128
|
+
kind: WaveformXAxisLabelKind
|
|
129
|
+
/** X coordinate in the source data, before time-unit conversion. */
|
|
130
|
+
rawValue: number
|
|
131
|
+
timeUnit: 's' | 'ms'
|
|
132
|
+
/** Current visible X domain in source coordinates. */
|
|
133
|
+
domain: [number, number]
|
|
134
|
+
/** Current visible X domain converted to the selected display unit. */
|
|
135
|
+
displayDomain: [number, number]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type WaveformXAxisLabelFormatter = (
|
|
139
|
+
value: number,
|
|
140
|
+
context: WaveformXAxisLabelFormatterContext,
|
|
141
|
+
) => string
|
|
142
|
+
|
|
124
143
|
/** Controls axis baseline visibility while preserving tick marks and axis text. */
|
|
125
144
|
export interface WaveformAxesOptions {
|
|
126
145
|
x?: {
|
|
127
146
|
lineVisible?: boolean
|
|
147
|
+
/** Formats display-unit X values for ticks and visible-range endpoints. */
|
|
148
|
+
labelFormatter?: WaveformXAxisLabelFormatter
|
|
128
149
|
}
|
|
129
150
|
y?: {
|
|
130
151
|
lineVisible?: boolean
|
package/src/types/index.ts
CHANGED
|
@@ -18,6 +18,9 @@ export type {
|
|
|
18
18
|
WaveformLegendOrientation,
|
|
19
19
|
WaveformLegendOptions,
|
|
20
20
|
WaveformFrameStyle,
|
|
21
|
+
WaveformXAxisLabelKind,
|
|
22
|
+
WaveformXAxisLabelFormatterContext,
|
|
23
|
+
WaveformXAxisLabelFormatter,
|
|
21
24
|
WaveformAxesOptions,
|
|
22
25
|
WaveformZeroLineOptions,
|
|
23
26
|
} from './chart'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
|
|
3
|
+
import type { WaveformXAxisLabelFormatter, WaveformXAxisLabelFormatterContext } from '../types'
|
|
3
4
|
import {
|
|
4
5
|
formatAnnotationTime,
|
|
5
6
|
formatAxisTime,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
formatScientificAxisLabel,
|
|
10
11
|
formatTooltipNumber,
|
|
11
12
|
formatTooltipTime,
|
|
13
|
+
formatXAxisLabel,
|
|
12
14
|
resolveScientificAxisExponent,
|
|
13
15
|
shouldUseScientificAxisLabel,
|
|
14
16
|
} from './formatters'
|
|
@@ -55,20 +57,50 @@ describe('waveform number formatters', () => {
|
|
|
55
57
|
expect(formatScientificAxisExponent(0, 0)).toBeNull()
|
|
56
58
|
})
|
|
57
59
|
|
|
58
|
-
it('formats X-axis ticks and endpoints as plain
|
|
60
|
+
it('formats X-axis ticks and endpoints as complete plain values in the display unit', () => {
|
|
59
61
|
const domain: [number, number] = [0, 1]
|
|
60
|
-
expect(formatAxisTime(0.5004, 'ms', domain)).toBe('500')
|
|
62
|
+
expect(formatAxisTime(0.5004, 'ms', domain)).toBe('500.4')
|
|
61
63
|
expect(formatEndpointTime(1, domain, 'ms')).toBe('1000')
|
|
62
|
-
expect(formatAxisTime(0.5, 's', domain)).toBe('
|
|
64
|
+
expect(formatAxisTime(0.5, 's', domain)).toBe('0.5')
|
|
63
65
|
expect(formatEndpointTime(1, domain, 's')).toBe('1')
|
|
64
66
|
|
|
65
67
|
const tinyDomain: [number, number] = [0, 0.000001]
|
|
66
|
-
expect(formatEndpointTime(0.000001, tinyDomain, 's')).toBe('0')
|
|
67
|
-
expect(formatAxisTime(-0.000001, 's', tinyDomain)).toBe('0')
|
|
68
|
+
expect(formatEndpointTime(0.000001, tinyDomain, 's')).toBe('0.000001')
|
|
69
|
+
expect(formatAxisTime(-0.000001, 's', tinyDomain)).toBe('-0.000001')
|
|
70
|
+
expect(formatAxisTime(-0, 's')).toBe('0')
|
|
68
71
|
expect(formatAxisTime(1e21, 's')).toBe('1000000000000000000000')
|
|
69
72
|
expect(formatAxisTime(Number.POSITIVE_INFINITY, 's')).toBe('Infinity')
|
|
70
73
|
})
|
|
71
74
|
|
|
75
|
+
it('passes display values and complete source context to X-axis label formatters', () => {
|
|
76
|
+
const domain: [number, number] = [0.125, 1.875]
|
|
77
|
+
const formatter: WaveformXAxisLabelFormatter = (value, context) =>
|
|
78
|
+
`${context.kind}:${value / 10}`
|
|
79
|
+
|
|
80
|
+
expect(formatXAxisLabel(0.5, domain, 'ms', 'tick', formatter)).toBe('tick:50')
|
|
81
|
+
expect(formatXAxisLabel(domain[0], domain, 'ms', 'start', formatter)).toBe('start:12.5')
|
|
82
|
+
expect(formatXAxisLabel(domain[1], domain, 'ms', 'end', formatter)).toBe('end:187.5')
|
|
83
|
+
|
|
84
|
+
let receivedContext: WaveformXAxisLabelFormatterContext | undefined
|
|
85
|
+
formatXAxisLabel(0.5, domain, 'ms', 'tick', (value, context) => {
|
|
86
|
+
receivedContext = context
|
|
87
|
+
return String(value)
|
|
88
|
+
})
|
|
89
|
+
expect(receivedContext).toEqual({
|
|
90
|
+
kind: 'tick',
|
|
91
|
+
rawValue: 0.5,
|
|
92
|
+
timeUnit: 'ms',
|
|
93
|
+
domain: [0.125, 1.875],
|
|
94
|
+
displayDomain: [125, 1875],
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
expect(() =>
|
|
98
|
+
formatXAxisLabel(0.5, domain, 's', 'tick', () => {
|
|
99
|
+
throw new Error('formatter failed')
|
|
100
|
+
}),
|
|
101
|
+
).toThrow('formatter failed')
|
|
102
|
+
})
|
|
103
|
+
|
|
72
104
|
it('formats tooltip and raw values for their display contexts', () => {
|
|
73
105
|
expect(formatTooltipNumber(12345.67891)).toBe('12,345.6789')
|
|
74
106
|
expect(formatTooltipNumber(-0)).toBe('0')
|
package/src/utils/formatters.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { WaveformXAxisLabelFormatter, WaveformXAxisLabelKind } from '../types'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* 时间单位类型
|
|
3
5
|
*/
|
|
@@ -22,10 +24,6 @@ const Y_AXIS_NUMBER_FORMATTER = new Intl.NumberFormat('zh-CN', {
|
|
|
22
24
|
const TOOLTIP_NUMBER_FORMATTER = new Intl.NumberFormat('zh-CN', {
|
|
23
25
|
maximumFractionDigits: 4,
|
|
24
26
|
})
|
|
25
|
-
const X_AXIS_TIME_FORMATTER = new Intl.NumberFormat('zh-CN', {
|
|
26
|
-
maximumFractionDigits: 0,
|
|
27
|
-
useGrouping: false,
|
|
28
|
-
})
|
|
29
27
|
|
|
30
28
|
function formatFixedNumber(value: number, precision: number): string {
|
|
31
29
|
const formatted = value.toFixed(Math.max(0, precision))
|
|
@@ -103,13 +101,6 @@ export function formatTooltipNumber(value: number): string {
|
|
|
103
101
|
return TOOLTIP_NUMBER_FORMATTER.format(value)
|
|
104
102
|
}
|
|
105
103
|
|
|
106
|
-
/** Format an X-axis time value as a plain integer without grouping separators. */
|
|
107
|
-
function formatAxisTimeValue(value: number): string {
|
|
108
|
-
if (!Number.isFinite(value)) return String(value)
|
|
109
|
-
const formatted = X_AXIS_TIME_FORMATTER.format(value)
|
|
110
|
-
return formatted === '-0' ? '0' : formatted
|
|
111
|
-
}
|
|
112
|
-
|
|
113
104
|
/** Convert a number to complete plain decimal text without forcing exponential notation. */
|
|
114
105
|
export function formatPlainNumber(value: number): string {
|
|
115
106
|
if (!Number.isFinite(value)) return String(value)
|
|
@@ -143,6 +134,26 @@ export function displayTime(value: number, timeUnit: TimeUnit): number {
|
|
|
143
134
|
return timeUnit === 'ms' ? value * 1000 : value
|
|
144
135
|
}
|
|
145
136
|
|
|
137
|
+
/** Format an X-axis value without changing its source coordinate. */
|
|
138
|
+
export function formatXAxisLabel(
|
|
139
|
+
rawValue: number,
|
|
140
|
+
domain: [number, number],
|
|
141
|
+
timeUnit: TimeUnit,
|
|
142
|
+
kind: WaveformXAxisLabelKind,
|
|
143
|
+
formatter?: WaveformXAxisLabelFormatter,
|
|
144
|
+
): string {
|
|
145
|
+
const value = displayTime(rawValue, timeUnit)
|
|
146
|
+
if (!formatter) return formatPlainNumber(value)
|
|
147
|
+
|
|
148
|
+
return formatter(value, {
|
|
149
|
+
kind,
|
|
150
|
+
rawValue,
|
|
151
|
+
timeUnit,
|
|
152
|
+
domain: [domain[0], domain[1]],
|
|
153
|
+
displayDomain: [displayTime(domain[0], timeUnit), displayTime(domain[1], timeUnit)],
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
146
157
|
/**
|
|
147
158
|
* 计算端点标签的小数位数
|
|
148
159
|
* @param domain 数据域 [最小值, 最大值]
|
|
@@ -158,7 +169,7 @@ export function endpointFractionDigits(domain: [number, number], timeUnit: TimeU
|
|
|
158
169
|
}
|
|
159
170
|
|
|
160
171
|
/**
|
|
161
|
-
* 将 X
|
|
172
|
+
* 将 X 轴端点时间格式化为当前显示单位下的普通十进制文本
|
|
162
173
|
* @param value 时间值(秒)
|
|
163
174
|
* @param _domain 数据域(为保持现有调用签名而保留)
|
|
164
175
|
* @param timeUnit 时间单位
|
|
@@ -169,11 +180,11 @@ export function formatEndpointTime(
|
|
|
169
180
|
_domain: [number, number],
|
|
170
181
|
timeUnit: TimeUnit,
|
|
171
182
|
): string {
|
|
172
|
-
return
|
|
183
|
+
return formatXAxisLabel(value, _domain, timeUnit, 'end')
|
|
173
184
|
}
|
|
174
185
|
|
|
175
186
|
/**
|
|
176
|
-
* 将 X
|
|
187
|
+
* 将 X 轴时间刻度格式化为当前显示单位下的普通十进制文本
|
|
177
188
|
* @param value 时间值(秒)
|
|
178
189
|
* @param timeUnit 时间单位
|
|
179
190
|
* @param _domain 数据域(为保持现有调用签名而保留)
|
|
@@ -184,8 +195,7 @@ export function formatAxisTime(
|
|
|
184
195
|
timeUnit: TimeUnit,
|
|
185
196
|
_domain?: [number, number],
|
|
186
197
|
): string {
|
|
187
|
-
|
|
188
|
-
return formatAxisTimeValue(displayTime(value, timeUnit))
|
|
198
|
+
return formatXAxisLabel(value, _domain ?? [value, value], timeUnit, 'tick')
|
|
189
199
|
}
|
|
190
200
|
|
|
191
201
|
/**
|