waveform-analysis 0.1.32 → 0.1.36
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 +28 -3
- package/dist/index.cjs +1 -1
- package/dist/index.js +426 -411
- package/dist/style.css +1 -1
- package/dist/types/components/WaveformChart.vue.d.ts +1 -0
- package/dist/types/components/core/trackLayoutBuilder.d.ts +2 -1
- package/dist/types/components/core/types.d.ts +2 -0
- package/dist/types/components/core/useWaveformChartController.d.ts +1 -0
- package/dist/types/components/core/waveformChartTypes.d.ts +3 -2
- package/dist/types/components/core/xDomain.d.ts +3 -0
- package/dist/types/components/data/types.d.ts +1 -1
- package/dist/types/components/interaction/WaveformTooltip.vue.d.ts +1 -0
- package/dist/types/components/waveform.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types/chart.d.ts +10 -0
- package/dist/types/types/data.d.ts +3 -0
- package/dist/types/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/WaveformChart.vue +1 -0
- package/src/components/core/trackLayoutBuilder.ts +19 -7
- package/src/components/core/types.ts +2 -0
- package/src/components/core/useWaveformChartLifecycle.ts +1 -0
- package/src/components/core/useWaveformLayout.ts +19 -6
- package/src/components/core/waveformChartTypes.ts +3 -0
- package/src/components/core/xDomain.test.ts +28 -0
- package/src/components/core/xDomain.ts +25 -0
- package/src/components/data/types.ts +1 -0
- package/src/components/interaction/WaveformTooltip.test.ts +17 -9
- package/src/components/interaction/WaveformTooltip.vue +41 -45
- package/src/components/interaction/useWaveformHover.ts +1 -0
- package/src/components/waveform.ts +1 -0
- package/src/components/waveformChartCases/compactAndSharedHover.test.ts +4 -4
- package/src/components/waveformChartCases/dataAndHoverPerformance.test.ts +2 -2
- package/src/components/waveformChartCases/viewportReset.test.ts +35 -0
- package/src/components/waveformChartCases/xDomainStrategy.test.ts +96 -0
- package/src/core/data.test.ts +22 -0
- package/src/core/data.ts +1 -0
- package/src/data/simulatedWaveforms.test.ts +1 -0
- package/src/data/simulatedWaveforms.ts +7 -1
- package/src/index.ts +1 -0
- package/src/types/chart.ts +11 -0
- package/src/types/data.ts +3 -0
- package/src/types/index.ts +1 -0
- package/src/views/FixedYDomainDemo.vue +2 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
|
-
import { resolveWaveformPointErrors } from '../../core'
|
|
4
3
|
import { formatTooltipNumber, formatTooltipTime } from '../../utils'
|
|
5
4
|
import type { WaveformPoint } from '../data/types'
|
|
6
5
|
|
|
7
6
|
interface SeriesPoint {
|
|
8
7
|
trackIndex: number
|
|
9
8
|
name: string
|
|
9
|
+
shotNo?: string
|
|
10
10
|
color: string
|
|
11
11
|
unit?: string
|
|
12
12
|
point: WaveformPoint
|
|
@@ -34,7 +34,7 @@ const props = defineProps<Props>()
|
|
|
34
34
|
const tooltipGap = 12
|
|
35
35
|
const containerPadding = 8
|
|
36
36
|
const tooltipPlacementWidth = 238
|
|
37
|
-
const tooltipMaxWidth =
|
|
37
|
+
const tooltipMaxWidth = 560
|
|
38
38
|
const tooltipHorizontalPadding = 20
|
|
39
39
|
const tooltipLineHeight = 20
|
|
40
40
|
|
|
@@ -45,18 +45,18 @@ function estimateLineCount(text: string, width: number): number {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function formatSeriesText(seriesPoint: SeriesPoint): string {
|
|
48
|
-
const
|
|
49
|
-
return `${seriesPoint.
|
|
50
|
-
seriesPoint.unit ? `
|
|
51
|
-
}
|
|
48
|
+
const timeText = formatTooltipTime(props.hoveredPoint?.x ?? seriesPoint.point.x, props.timeUnit)
|
|
49
|
+
return `${seriesPoint.shotNo?.trim() || '未配置炮号'}: ${seriesPoint.name}${
|
|
50
|
+
seriesPoint.unit ? `(${seriesPoint.unit})` : ''
|
|
51
|
+
} (x:${timeText} y:${formatTooltipNumber(seriesPoint.point.y)})`
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
function estimateTooltipHeight(width: number
|
|
54
|
+
function estimateTooltipHeight(width: number): number {
|
|
55
55
|
const seriesLines = props.seriesPoints.reduce(
|
|
56
56
|
(total, seriesPoint) => total + estimateLineCount(formatSeriesText(seriesPoint), width),
|
|
57
57
|
0,
|
|
58
58
|
)
|
|
59
|
-
return 16 + tooltipLineHeight *
|
|
59
|
+
return 16 + tooltipLineHeight * seriesLines + 5
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
const tooltipStyle = computed(() => {
|
|
@@ -84,25 +84,18 @@ const tooltipStyle = computed(() => {
|
|
|
84
84
|
: { left: `${containerPadding}px`, maxWidth: `${availableWidth}px` }
|
|
85
85
|
|
|
86
86
|
const maxWidth = Number.parseFloat(horizontalStyle.maxWidth)
|
|
87
|
-
const timeText = `${props.timeUnit}: ${formatTooltipTime(props.hoveredPoint.x, props.timeUnit)}`
|
|
88
|
-
|
|
89
87
|
return {
|
|
90
88
|
...horizontalStyle,
|
|
91
89
|
top: `${Math.max(
|
|
92
90
|
8,
|
|
93
91
|
Math.min(
|
|
94
92
|
props.position.y - 18,
|
|
95
|
-
props.containerHeight - estimateTooltipHeight(maxWidth
|
|
93
|
+
props.containerHeight - estimateTooltipHeight(maxWidth) - 8,
|
|
96
94
|
),
|
|
97
95
|
)}px`,
|
|
98
96
|
}
|
|
99
97
|
})
|
|
100
98
|
|
|
101
|
-
function formatError(point: WaveformPoint): string | null {
|
|
102
|
-
const { lower, upper } = resolveWaveformPointErrors(point)
|
|
103
|
-
if (lower === 0 && upper === 0) return null
|
|
104
|
-
return `(+${formatTooltipNumber(upper)} / -${formatTooltipNumber(lower)})`
|
|
105
|
-
}
|
|
106
99
|
</script>
|
|
107
100
|
|
|
108
101
|
<template>
|
|
@@ -111,9 +104,6 @@ function formatError(point: WaveformPoint): string | null {
|
|
|
111
104
|
class="waveform-tooltip waveform-chart__tooltip"
|
|
112
105
|
:style="tooltipStyle"
|
|
113
106
|
>
|
|
114
|
-
<span class="waveform-tooltip__time waveform-chart__tooltip-time">
|
|
115
|
-
{{ timeUnit }}: {{ formatTooltipTime(hoveredPoint.x, timeUnit) }}
|
|
116
|
-
</span>
|
|
117
107
|
<span
|
|
118
108
|
v-for="seriesPoint in seriesPoints"
|
|
119
109
|
:key="`${seriesPoint.trackIndex}-${seriesPoint.name}`"
|
|
@@ -121,12 +111,12 @@ function formatError(point: WaveformPoint): string | null {
|
|
|
121
111
|
>
|
|
122
112
|
<i :style="{ backgroundColor: seriesPoint.color }" />
|
|
123
113
|
<span class="waveform-tooltip__series-content">
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
</span>
|
|
114
|
+
<span class="waveform-tooltip__series-label">{{ seriesPoint.shotNo?.trim() || '未配置炮号' }}: {{
|
|
115
|
+
seriesPoint.name
|
|
116
|
+
}}<template v-if="seriesPoint.unit">({{ seriesPoint.unit }})</template></span>
|
|
117
|
+
<span class="waveform-tooltip__value">(x:{{ formatTooltipTime(hoveredPoint.x, timeUnit) }} y:{{
|
|
118
|
+
formatTooltipNumber(seriesPoint.point.y)
|
|
119
|
+
}})</span>
|
|
130
120
|
</span>
|
|
131
121
|
</span>
|
|
132
122
|
</div>
|
|
@@ -138,26 +128,17 @@ function formatError(point: WaveformPoint): string | null {
|
|
|
138
128
|
position: absolute;
|
|
139
129
|
z-index: 2;
|
|
140
130
|
display: grid;
|
|
141
|
-
gap:
|
|
131
|
+
gap: 2px;
|
|
142
132
|
width: max-content;
|
|
143
|
-
max-width: min(
|
|
144
|
-
padding:
|
|
145
|
-
color: #
|
|
146
|
-
font:
|
|
147
|
-
12px/1.45 ui-monospace,
|
|
148
|
-
SFMono-Regular,
|
|
149
|
-
Consolas,
|
|
150
|
-
monospace;
|
|
133
|
+
max-width: min(560px, calc(100% - 16px));
|
|
134
|
+
padding: 9px 12px;
|
|
135
|
+
color: #505050;
|
|
136
|
+
font: 14px/1.35 Arial, sans-serif;
|
|
151
137
|
pointer-events: none;
|
|
152
138
|
background: #fff;
|
|
153
|
-
border: 1px solid #
|
|
154
|
-
border-radius:
|
|
155
|
-
box-shadow: 0
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
.waveform-tooltip__time {
|
|
159
|
-
padding-bottom: 4px;
|
|
160
|
-
border-bottom: 1px solid #f0f0f0;
|
|
139
|
+
border: 1px solid #e3e7eb;
|
|
140
|
+
border-radius: 4px;
|
|
141
|
+
box-shadow: 0 3px 10px rgb(16 24 40 / 15%);
|
|
161
142
|
}
|
|
162
143
|
|
|
163
144
|
.waveform-tooltip__series {
|
|
@@ -176,9 +157,24 @@ function formatError(point: WaveformPoint): string | null {
|
|
|
176
157
|
}
|
|
177
158
|
|
|
178
159
|
.waveform-tooltip__series-content {
|
|
160
|
+
display: flex;
|
|
161
|
+
flex-wrap: nowrap;
|
|
162
|
+
gap: 12px;
|
|
163
|
+
align-items: baseline;
|
|
164
|
+
min-width: 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.waveform-tooltip__series-label {
|
|
168
|
+
flex: 1 1 auto;
|
|
179
169
|
min-width: 0;
|
|
180
|
-
overflow
|
|
181
|
-
|
|
170
|
+
overflow: hidden;
|
|
171
|
+
text-overflow: ellipsis;
|
|
172
|
+
white-space: nowrap;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.waveform-tooltip__value {
|
|
176
|
+
flex: 0 0 auto;
|
|
177
|
+
white-space: nowrap;
|
|
182
178
|
}
|
|
183
179
|
|
|
184
180
|
.waveform-tooltip__series strong {
|
|
@@ -186,7 +182,7 @@ function formatError(point: WaveformPoint): string | null {
|
|
|
186
182
|
}
|
|
187
183
|
|
|
188
184
|
.waveform-tooltip__value {
|
|
189
|
-
|
|
185
|
+
color: #555;
|
|
190
186
|
}
|
|
191
187
|
|
|
192
188
|
.waveform-tooltip__series small {
|
|
@@ -179,10 +179,10 @@ describe('WaveformChart', () => {
|
|
|
179
179
|
await flushPromises()
|
|
180
180
|
|
|
181
181
|
const tooltip = wrapper.get('.waveform-chart__tooltip')
|
|
182
|
-
expect(tooltip.text()).toContain('BT2_2M
|
|
183
|
-
expect(tooltip.text()).toContain('2
|
|
184
|
-
expect(tooltip.text()).toContain('BT1_2M
|
|
185
|
-
expect(tooltip.text()).toContain('4
|
|
182
|
+
expect(tooltip.text()).toContain('未配置炮号: BT2_2M')
|
|
183
|
+
expect(tooltip.text()).toContain('(x:1,000 y:2)')
|
|
184
|
+
expect(tooltip.text()).toContain('未配置炮号: BT1_2M')
|
|
185
|
+
expect(tooltip.text()).toContain('(x:1,000 y:4)')
|
|
186
186
|
const crosshairLines = wrapper.findAll('.waveform-chart__crosshair line')
|
|
187
187
|
expect(crosshairLines).toHaveLength(2)
|
|
188
188
|
crosshairLines.forEach((line) => {
|
|
@@ -178,7 +178,7 @@ describe('WaveformChart', () => {
|
|
|
178
178
|
await flushPromises()
|
|
179
179
|
expect(wrapper.emitted('point-hover')?.at(-1)).toEqual([{ x: 1, y: 5 }])
|
|
180
180
|
expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(true)
|
|
181
|
-
expect(wrapper.get('.waveform-
|
|
181
|
+
expect(wrapper.get('.waveform-tooltip__series-label').text()).toContain('未配置炮号:')
|
|
182
182
|
const crosshairLines = wrapper.findAll('.waveform-chart__crosshair line')
|
|
183
183
|
expect(crosshairLines).toHaveLength(1)
|
|
184
184
|
expect(crosshairLines[0].attributes('x1')).toBe(crosshairLines[0].attributes('x2'))
|
|
@@ -303,7 +303,7 @@ describe('WaveformChart', () => {
|
|
|
303
303
|
flushAnimationFrames()
|
|
304
304
|
await flushPromises()
|
|
305
305
|
|
|
306
|
-
expect(wrapper.get('.waveform-
|
|
306
|
+
expect(wrapper.get('.waveform-tooltip__series-label').text()).toContain('未配置炮号:')
|
|
307
307
|
expect(wrapper.findAll('.waveform-chart__crosshair line')).toHaveLength(1)
|
|
308
308
|
expect(wrapper.get('.waveform-chart__line').element).toBe(pathBeforeHover)
|
|
309
309
|
expect(chartUpdate).not.toHaveBeenCalled()
|
|
@@ -5,6 +5,13 @@ import { flushAnimationFrames } from '../../test/setup'
|
|
|
5
5
|
import { gridSeries, mountSizedChart } from '../../test/waveformChart'
|
|
6
6
|
|
|
7
7
|
describe('WaveformChart viewport reset', () => {
|
|
8
|
+
const niceExplicitStrategy = {
|
|
9
|
+
type: 'nice' as const,
|
|
10
|
+
bounds: 'end' as const,
|
|
11
|
+
tickCount: 10,
|
|
12
|
+
includeExplicit: true,
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
it('resets a shared viewport on double-click and emits a global payload', async () => {
|
|
9
16
|
const wrapper = await mountSizedChart(
|
|
10
17
|
{
|
|
@@ -111,4 +118,32 @@ describe('WaveformChart viewport reset', () => {
|
|
|
111
118
|
expect(wrapper.findAll('.waveform-chart__axis-endpoint--start')[0].text()).toBe('0')
|
|
112
119
|
expect(wrapper.findAll('.waveform-chart__axis-endpoint--end')[0].text()).toBe('1000')
|
|
113
120
|
})
|
|
121
|
+
|
|
122
|
+
it('resets shared and independent explicit domains to their included nice bounds', async () => {
|
|
123
|
+
const shared = await mountSizedChart(
|
|
124
|
+
{
|
|
125
|
+
kind: 'points',
|
|
126
|
+
points: [
|
|
127
|
+
{ x: 0, y: 0 },
|
|
128
|
+
{ x: 4.999999, y: 1 },
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
{ initialXDomain: [0, 4.999999], xDomainStrategy: niceExplicitStrategy },
|
|
132
|
+
)
|
|
133
|
+
const independent = await mountSizedChart(gridSeries(2), {
|
|
134
|
+
displayMode: 'independent',
|
|
135
|
+
initialXDomains: { 'channel-0': [0, 4.999999] },
|
|
136
|
+
xDomainStrategy: niceExplicitStrategy,
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
;(shared.vm as unknown as { resetViewport: () => void }).resetViewport()
|
|
140
|
+
;(independent.vm as unknown as { resetViewport: () => void }).resetViewport()
|
|
141
|
+
await flushPromises()
|
|
142
|
+
|
|
143
|
+
expect(shared.get('.waveform-chart__axis-endpoint--start').text()).toBe('0')
|
|
144
|
+
expect(shared.get('.waveform-chart__axis-endpoint--end').text()).toBe('5000')
|
|
145
|
+
const firstTrack = independent.findAll('.waveform-chart__track')[0]
|
|
146
|
+
expect(firstTrack.get('.waveform-chart__axis-endpoint--start').text()).toBe('0')
|
|
147
|
+
expect(firstTrack.get('.waveform-chart__axis-endpoint--end').text()).toBe('5000')
|
|
148
|
+
})
|
|
114
149
|
})
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { gridSeries, mountSizedChart } from '../../test/waveformChart'
|
|
4
|
+
|
|
5
|
+
describe('WaveformChart x domain strategy', () => {
|
|
6
|
+
it('expands automatic x domains without changing source seconds', async () => {
|
|
7
|
+
const data = {
|
|
8
|
+
kind: 'points' as const,
|
|
9
|
+
points: [
|
|
10
|
+
{ x: 0, y: 0 },
|
|
11
|
+
{ x: 4.999999, y: 1 },
|
|
12
|
+
],
|
|
13
|
+
}
|
|
14
|
+
const wrapper = await mountSizedChart(data, {
|
|
15
|
+
timeUnit: 'ms',
|
|
16
|
+
xDomainStrategy: { type: 'nice', bounds: 'end' },
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
expect(wrapper.get('.waveform-chart__axis-endpoint--start').text()).toBe('0')
|
|
20
|
+
expect(wrapper.get('.waveform-chart__axis-endpoint--end').text()).toBe('5000')
|
|
21
|
+
expect(data.points[1]?.x).toBe(4.999999)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('keeps explicit initial x domains exact by default', async () => {
|
|
25
|
+
const wrapper = await mountSizedChart(
|
|
26
|
+
{
|
|
27
|
+
kind: 'points',
|
|
28
|
+
points: [
|
|
29
|
+
{ x: 123, y: 0 },
|
|
30
|
+
{ x: 456, y: 1 },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
timeUnit: 's',
|
|
35
|
+
initialXDomain: [120, 460],
|
|
36
|
+
xDomainStrategy: { type: 'nice' },
|
|
37
|
+
},
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
expect(wrapper.get('.waveform-chart__axis-endpoint--start').text()).toBe('120')
|
|
41
|
+
expect(wrapper.get('.waveform-chart__axis-endpoint--end').text()).toBe('460')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('keeps explicit per-track initial x domains exact by default', async () => {
|
|
45
|
+
const wrapper = await mountSizedChart(gridSeries(1), {
|
|
46
|
+
displayMode: 'independent',
|
|
47
|
+
initialXDomains: { 'channel-0': [0, 4.999999] },
|
|
48
|
+
xDomainStrategy: { type: 'nice', bounds: 'end' },
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
expect(wrapper.get('.waveform-chart__axis-endpoint--start').text()).toBe('0')
|
|
52
|
+
expect(wrapper.get('.waveform-chart__axis-endpoint--end').text()).toBe('4999.999')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('includes an explicit shared initial x domain when requested', async () => {
|
|
56
|
+
const wrapper = await mountSizedChart(
|
|
57
|
+
{
|
|
58
|
+
kind: 'points',
|
|
59
|
+
points: [
|
|
60
|
+
{ x: 0, y: 0 },
|
|
61
|
+
{ x: 4.999999, y: 1 },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
initialXDomain: [0, 4.999999],
|
|
66
|
+
xDomainStrategy: {
|
|
67
|
+
type: 'nice',
|
|
68
|
+
bounds: 'end',
|
|
69
|
+
tickCount: 10,
|
|
70
|
+
includeExplicit: true,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
expect(wrapper.get('.waveform-chart__axis-endpoint--start').text()).toBe('0')
|
|
76
|
+
expect(wrapper.get('.waveform-chart__axis-endpoint--end').text()).toBe('5000')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('includes explicit per-track initial x domains in independent mode', async () => {
|
|
80
|
+
const wrapper = await mountSizedChart(gridSeries(2), {
|
|
81
|
+
displayMode: 'independent',
|
|
82
|
+
grid: { rowCount: 1, columnCount: 2 },
|
|
83
|
+
initialXDomains: {
|
|
84
|
+
'channel-0': [0, 4.999999],
|
|
85
|
+
'channel-1': [10, 14.999999],
|
|
86
|
+
},
|
|
87
|
+
xDomainStrategy: { type: 'nice', bounds: 'end', tickCount: 10, includeExplicit: true },
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const tracks = wrapper.findAll('.waveform-chart__track')
|
|
91
|
+
expect(tracks[0]?.get('.waveform-chart__axis-endpoint--start').text()).toBe('0')
|
|
92
|
+
expect(tracks[0]?.get('.waveform-chart__axis-endpoint--end').text()).toBe('5000')
|
|
93
|
+
expect(tracks[1]?.get('.waveform-chart__axis-endpoint--start').text()).toBe('10000')
|
|
94
|
+
expect(tracks[1]?.get('.waveform-chart__axis-endpoint--end').text()).toBe('15000')
|
|
95
|
+
})
|
|
96
|
+
})
|
package/src/core/data.test.ts
CHANGED
|
@@ -102,4 +102,26 @@ describe('waveform data normalization', () => {
|
|
|
102
102
|
'solid',
|
|
103
103
|
])
|
|
104
104
|
})
|
|
105
|
+
|
|
106
|
+
it('preserves trimmed shot numbers and omits blank values', () => {
|
|
107
|
+
const result = normalizeWaveformSeries({
|
|
108
|
+
kind: 'series',
|
|
109
|
+
series: [
|
|
110
|
+
{
|
|
111
|
+
id: 'with-shot',
|
|
112
|
+
shotNo: ' 13300 ',
|
|
113
|
+
name: '通道 A',
|
|
114
|
+
data: { kind: 'points', points: [{ x: 0, y: 1 }] },
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'without-shot',
|
|
118
|
+
shotNo: ' ',
|
|
119
|
+
name: '通道 B',
|
|
120
|
+
data: { kind: 'points', points: [{ x: 0, y: 2 }] },
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
expect(result.map((series) => series.shotNo)).toEqual(['13300', undefined])
|
|
126
|
+
})
|
|
105
127
|
})
|
package/src/core/data.ts
CHANGED
|
@@ -132,6 +132,7 @@ export function normalizeWaveformSeries(data: WaveformData): NormalizedWaveformS
|
|
|
132
132
|
|
|
133
133
|
return {
|
|
134
134
|
id: uniqueId,
|
|
135
|
+
shotNo: series.shotNo?.trim() || undefined,
|
|
135
136
|
trackId: series.trackId?.trim() || undefined,
|
|
136
137
|
name: series.name,
|
|
137
138
|
unit: series.unit,
|
|
@@ -13,6 +13,7 @@ describe('simulated waveform data', () => {
|
|
|
13
13
|
|
|
14
14
|
expect(first.series).toHaveLength(6)
|
|
15
15
|
expect(new Set(first.series.map((series) => series.id)).size).toBe(6)
|
|
16
|
+
expect(new Set(first.series.map((series) => series.shotNo))).toEqual(new Set(['13300']))
|
|
16
17
|
first.series.forEach((series) => {
|
|
17
18
|
expect(series.data.kind).toBe('points')
|
|
18
19
|
if (series.data.kind !== 'points') return
|
|
@@ -13,7 +13,7 @@ type ErrorGenerator = (
|
|
|
13
13
|
|
|
14
14
|
interface SimulatedSeriesDefinition extends Pick<
|
|
15
15
|
WaveformSeries,
|
|
16
|
-
'id' | 'name' | 'unit' | 'lineType' | 'pointType' | 'errorBar'
|
|
16
|
+
'id' | 'shotNo' | 'name' | 'unit' | 'lineType' | 'pointType' | 'errorBar'
|
|
17
17
|
> {
|
|
18
18
|
signal: SignalGenerator
|
|
19
19
|
errors?: ErrorGenerator
|
|
@@ -46,6 +46,7 @@ function createPoints(
|
|
|
46
46
|
const seriesDefinitions: SimulatedSeriesDefinition[] = [
|
|
47
47
|
{
|
|
48
48
|
id: 'simulated-sine',
|
|
49
|
+
shotNo: '13300',
|
|
49
50
|
name: '正弦基波',
|
|
50
51
|
unit: 'V',
|
|
51
52
|
lineType: 'none',
|
|
@@ -59,6 +60,7 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
|
|
|
59
60
|
},
|
|
60
61
|
{
|
|
61
62
|
id: 'simulated-harmonic',
|
|
63
|
+
shotNo: '13300',
|
|
62
64
|
name: '谐波扰动',
|
|
63
65
|
unit: 'V',
|
|
64
66
|
lineType: 'linear',
|
|
@@ -68,6 +70,7 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
|
|
|
68
70
|
},
|
|
69
71
|
{
|
|
70
72
|
id: 'simulated-damped',
|
|
73
|
+
shotNo: '13300',
|
|
71
74
|
name: '阻尼振荡',
|
|
72
75
|
unit: 'A',
|
|
73
76
|
lineType: 'linear',
|
|
@@ -79,6 +82,7 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
|
|
|
79
82
|
},
|
|
80
83
|
{
|
|
81
84
|
id: 'simulated-step',
|
|
85
|
+
shotNo: '13300',
|
|
82
86
|
name: '阶跃响应',
|
|
83
87
|
unit: 'V',
|
|
84
88
|
lineType: 'linear',
|
|
@@ -87,6 +91,7 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
|
|
|
87
91
|
},
|
|
88
92
|
{
|
|
89
93
|
id: 'simulated-pulse',
|
|
94
|
+
shotNo: '13300',
|
|
90
95
|
name: '脉冲响应',
|
|
91
96
|
unit: 'V',
|
|
92
97
|
lineType: 'linear',
|
|
@@ -95,6 +100,7 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
|
|
|
95
100
|
},
|
|
96
101
|
{
|
|
97
102
|
id: 'simulated-noise',
|
|
103
|
+
shotNo: '13300',
|
|
98
104
|
name: '带噪信号',
|
|
99
105
|
unit: 'A',
|
|
100
106
|
lineType: 'linear',
|
package/src/index.ts
CHANGED
package/src/types/chart.ts
CHANGED
|
@@ -26,6 +26,17 @@ export type WaveformOverlayMode = 'single-axis' | 'multi-axis'
|
|
|
26
26
|
/** 标注工具模式 */
|
|
27
27
|
export type WaveformInteractionMode = 'zoom' | 'annotation'
|
|
28
28
|
|
|
29
|
+
/** Controls how the initial X viewport is derived when no explicit domain is configured. */
|
|
30
|
+
export interface WaveformXDomainStrategy {
|
|
31
|
+
type: 'data' | 'nice'
|
|
32
|
+
/** Selects which bounds are expanded when type is `nice`. Defaults to `both`. */
|
|
33
|
+
bounds?: 'both' | 'end'
|
|
34
|
+
/** Stable tick count used to calculate nice bounds. Defaults to 10. */
|
|
35
|
+
tickCount?: number
|
|
36
|
+
/** Applies the strategy to explicit initial domains. Defaults to false. */
|
|
37
|
+
includeExplicit?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
29
40
|
/** Describes the X-axis viewport after a zoom gesture completes. */
|
|
30
41
|
export interface WaveformZoomEndPayload {
|
|
31
42
|
start: number
|
package/src/types/data.ts
CHANGED
|
@@ -47,6 +47,8 @@ export type SingleWaveformData =
|
|
|
47
47
|
*/
|
|
48
48
|
export interface WaveformSeries {
|
|
49
49
|
id?: string
|
|
50
|
+
/** 炮号,未提供时 Tooltip 显示“未配置炮号”。 */
|
|
51
|
+
shotNo?: string
|
|
50
52
|
/** 相同 trackId 的系列叠加在同一图框中;默认每个系列独占一个图框。 */
|
|
51
53
|
trackId?: string
|
|
52
54
|
name: string
|
|
@@ -74,6 +76,7 @@ export type WaveformData =
|
|
|
74
76
|
*/
|
|
75
77
|
export interface NormalizedWaveformSeries {
|
|
76
78
|
id: string
|
|
79
|
+
shotNo?: string
|
|
77
80
|
trackId?: string
|
|
78
81
|
name: string
|
|
79
82
|
unit?: string
|
package/src/types/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ const chartData: WaveformData = {
|
|
|
19
19
|
series: [
|
|
20
20
|
{
|
|
21
21
|
id: 'voltage',
|
|
22
|
+
shotNo: '13300',
|
|
22
23
|
name: '电压',
|
|
23
24
|
unit: 'V',
|
|
24
25
|
color: '#1677ff',
|
|
@@ -33,6 +34,7 @@ const chartData: WaveformData = {
|
|
|
33
34
|
},
|
|
34
35
|
{
|
|
35
36
|
id: 'current',
|
|
37
|
+
shotNo: '13300',
|
|
36
38
|
name: '电流',
|
|
37
39
|
unit: 'mA',
|
|
38
40
|
color: '#d4380d',
|