waveform-analysis 0.1.57 → 0.1.58

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.
@@ -9,6 +9,8 @@ export interface WaveformGridOptions {
9
9
  fillIncompleteLastRow?: boolean;
10
10
  /** Stable grid slots. Empty IDs remain as empty chart frames and keep their page position. */
11
11
  trackOrder?: string[];
12
+ /** Omits ordered tracks without waveform series from layout and pagination. */
13
+ hideEmptyTracks?: boolean;
12
14
  trackLines?: WaveformGridTrackLines;
13
15
  }
14
16
  export interface WaveformGridLineOptions {
@@ -32,6 +34,7 @@ export interface NormalizedWaveformGridOptions {
32
34
  showPagination: boolean;
33
35
  fillIncompleteLastRow: boolean;
34
36
  trackOrder?: string[];
37
+ hideEmptyTracks?: boolean;
35
38
  trackLines: Record<string, NormalizedWaveformGridLineOptions>;
36
39
  }
37
40
  export interface GridCellGeometry {
@@ -0,0 +1,4 @@
1
+ import type { DisplayTrack } from './types';
2
+ export declare const resolvePageableTracks: (tracks: DisplayTrack[], hideEmptyTracks?: boolean) => DisplayTrack[];
3
+ export declare const resolveTrackFrameNumber: (frameNumber: string | number | undefined, frameNumbers: Record<string, string | number> | undefined, trackCount: number, trackIndex: number, trackId: string) => string | number | undefined;
4
+ export declare const createFrameNumberResolver: (getFrameNumber: () => string | number | undefined, getFrameNumbers: () => Record<string, string | number> | undefined, getTracks: () => DisplayTrack[]) => (trackId: string) => string | number | undefined;
@@ -246,6 +246,7 @@ export declare function useWaveformChartController(props: ResolvedWaveformChartP
246
246
  confirmAnnotation: (annotation: import("..").WaveformAnnotation) => void;
247
247
  chartSeries: import("./types").DisplaySeries[];
248
248
  chartTracks: import("./types").DisplayTrack[];
249
+ pageableTracks: import("./types").DisplayTrack[];
249
250
  renderingOptions: import("../..").ResolvedWaveformRenderingOptions;
250
251
  gridOptions: import("./grid").NormalizedWaveformGridOptions;
251
252
  pageCount: number;
@@ -281,7 +282,7 @@ export declare function useWaveformChartController(props: ResolvedWaveformChartP
281
282
  annotationTrackLayouts: import("../annotation").AnnotationTrackLayout[];
282
283
  renderedAnnotations: import("../annotation").RenderedAnnotation[];
283
284
  editorSeries: import("../annotation").AnnotationSeriesInfo | undefined;
284
- resolveFrameNumber: (trackIndex: number) => string | number | undefined;
285
+ resolveFrameNumber: (trackId: string) => string | number | undefined;
285
286
  fixedWidth: number | undefined;
286
287
  fixedHeight: number | undefined;
287
288
  chartWidth: number;
@@ -341,6 +342,7 @@ export declare function useWaveformChartController(props: ResolvedWaveformChartP
341
342
  yDomains?: Record<string, [number, number]> | undefined;
342
343
  timeUnit: "s" | "ms";
343
344
  frameNumber?: string | number | undefined;
345
+ frameNumbers?: Record<string, string | number> | undefined;
344
346
  frameStyle?: import("..").WaveformFrameStyle | undefined;
345
347
  axes?: import("..").WaveformAxesOptions | undefined;
346
348
  annotations: import("..").WaveformAnnotation[];
@@ -23,6 +23,7 @@ interface LayoutContext {
23
23
  export declare function useWaveformLayout(context: LayoutContext): {
24
24
  chartSeries: ComputedRef<DisplaySeries[]>;
25
25
  chartTracks: ComputedRef<DisplayTrack[]>;
26
+ pageableTracks: ComputedRef<DisplayTrack[]>;
26
27
  renderingOptions: ComputedRef<import("../..").ResolvedWaveformRenderingOptions>;
27
28
  gridOptions: ComputedRef<import("./grid").NormalizedWaveformGridOptions>;
28
29
  pageCount: ComputedRef<number>;
@@ -58,6 +59,6 @@ export declare function useWaveformLayout(context: LayoutContext): {
58
59
  annotationTrackLayouts: ComputedRef<AnnotationTrackLayout[]>;
59
60
  renderedAnnotations: ComputedRef<import("../annotation").RenderedAnnotation[]>;
60
61
  editorSeries: ComputedRef<AnnotationSeriesInfo | undefined>;
61
- resolveFrameNumber: (trackIndex: number) => string | number | undefined;
62
+ resolveFrameNumber: (trackId: string) => string | number | undefined;
62
63
  };
63
64
  export {};
@@ -22,6 +22,8 @@ export interface WaveformChartProps {
22
22
  yDomains?: Record<string, [number, number]>;
23
23
  timeUnit?: 's' | 'ms';
24
24
  frameNumber?: string | number;
25
+ /** Per-track frame watermark overrides, keyed by stable trackId. */
26
+ frameNumbers?: Record<string, string | number>;
25
27
  frameStyle?: WaveformFrameStyle;
26
28
  axes?: WaveformAxesOptions;
27
29
  annotations?: WaveformAnnotation[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waveform-analysis",
3
- "version": "0.1.57",
3
+ "version": "0.1.58",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/types/index.d.ts",
@@ -80,7 +80,7 @@ const {
80
80
  pageCount,
81
81
  currentPage,
82
82
  getPageSize,
83
- chartTracks,
83
+ pageableTracks,
84
84
  goToPage,
85
85
  annotationInteraction,
86
86
  editorDraft,
@@ -230,7 +230,7 @@ function handleChartPointerLeave() {
230
230
  :interactive="!isPresentationMode"
231
231
  :display-mode="displayMode"
232
232
  :interaction-mode="activeInteractionMode"
233
- :frame-number="resolveFrameNumber(track.index)"
233
+ :frame-number="resolveFrameNumber(track.id)"
234
234
  :frame-style="frameStyle"
235
235
  :axes="axes"
236
236
  :clean-view="isCleanView"
@@ -326,7 +326,7 @@ function handleChartPointerLeave() {
326
326
  aria-label="波形分页"
327
327
  :current="currentPage"
328
328
  :page-size="getPageSize(gridOptions)"
329
- :total="chartTracks.length"
329
+ :total="pageableTracks.length"
330
330
  :show-size-changer="false"
331
331
  :show-quick-jumper="false"
332
332
  @change="goToPage"
@@ -17,6 +17,7 @@ describe('waveform grid helpers', () => {
17
17
  showPagination: true,
18
18
  fillIncompleteLastRow: false,
19
19
  trackOrder: [],
20
+ hideEmptyTracks: false,
20
21
  trackLines: {},
21
22
  })
22
23
  expect(normalizeGridOptions({ rowCount: 0, columnCount: 99 })).toEqual({
@@ -25,8 +26,10 @@ describe('waveform grid helpers', () => {
25
26
  showPagination: true,
26
27
  fillIncompleteLastRow: false,
27
28
  trackOrder: [],
29
+ hideEmptyTracks: false,
28
30
  trackLines: {},
29
31
  })
32
+ expect(normalizeGridOptions({ hideEmptyTracks: true }).hideEmptyTracks).toBe(true)
30
33
  })
31
34
 
32
35
  it('normalizes track order by trimming, dropping blanks, and removing duplicates', () => {
@@ -11,6 +11,8 @@ export interface WaveformGridOptions {
11
11
  fillIncompleteLastRow?: boolean
12
12
  /** Stable grid slots. Empty IDs remain as empty chart frames and keep their page position. */
13
13
  trackOrder?: string[]
14
+ /** Omits ordered tracks without waveform series from layout and pagination. */
15
+ hideEmptyTracks?: boolean
14
16
  trackLines?: WaveformGridTrackLines
15
17
  }
16
18
 
@@ -38,6 +40,7 @@ export interface NormalizedWaveformGridOptions {
38
40
  showPagination: boolean
39
41
  fillIncompleteLastRow: boolean
40
42
  trackOrder?: string[]
43
+ hideEmptyTracks?: boolean
41
44
  trackLines: Record<string, NormalizedWaveformGridLineOptions>
42
45
  }
43
46
 
@@ -95,6 +98,7 @@ export function normalizeGridOptions(options?: WaveformGridOptions): NormalizedW
95
98
  showPagination: options?.showPagination ?? true,
96
99
  fillIncompleteLastRow: options?.fillIncompleteLastRow ?? false,
97
100
  trackOrder: normalizeTrackOrder(options?.trackOrder),
101
+ hideEmptyTracks: options?.hideEmptyTracks ?? false,
98
102
  trackLines,
99
103
  }
100
104
  }
@@ -0,0 +1,40 @@
1
+ import type { DisplayTrack } from './types'
2
+
3
+ export const resolvePageableTracks = (
4
+ tracks: DisplayTrack[],
5
+ hideEmptyTracks?: boolean,
6
+ ): DisplayTrack[] => (hideEmptyTracks ? tracks.filter((track) => track.series.length > 0) : tracks)
7
+
8
+ export const resolveTrackFrameNumber = (
9
+ frameNumber: string | number | undefined,
10
+ frameNumbers: Record<string, string | number> | undefined,
11
+ trackCount: number,
12
+ trackIndex: number,
13
+ trackId: string,
14
+ ): string | number | undefined => {
15
+ const mappedFrameNumber = frameNumbers?.[trackId]
16
+ if (mappedFrameNumber !== undefined) return mappedFrameNumber
17
+ if (frameNumber === undefined) return undefined
18
+ if (trackCount === 1) return frameNumber
19
+ return typeof frameNumber === 'number'
20
+ ? frameNumber + trackIndex
21
+ : `${frameNumber}-${trackIndex + 1}`
22
+ }
23
+
24
+ export const createFrameNumberResolver =
25
+ (
26
+ getFrameNumber: () => string | number | undefined,
27
+ getFrameNumbers: () => Record<string, string | number> | undefined,
28
+ getTracks: () => DisplayTrack[],
29
+ ) =>
30
+ (trackId: string): string | number | undefined => {
31
+ const tracks = getTracks()
32
+ const trackIndex = tracks.findIndex((track) => track.id === trackId)
33
+ return resolveTrackFrameNumber(
34
+ getFrameNumber(),
35
+ getFrameNumbers(),
36
+ tracks.length,
37
+ trackIndex >= 0 ? trackIndex : 0,
38
+ trackId,
39
+ )
40
+ }
@@ -31,6 +31,7 @@ import {
31
31
  resolveYAxisSeriesGroups,
32
32
  } from './layout'
33
33
  import type { DisplaySeries, DisplayTrack, TrackLayout } from './types'
34
+ import { createFrameNumberResolver, resolvePageableTracks } from './trackPagination'
34
35
  import type { PreparedWaveformSeries } from './useWaveformData'
35
36
  import type { ResolvedWaveformChartProps } from './waveformChartTypes'
36
37
  import { applyXDomainStrategy } from './xDomain'
@@ -107,9 +108,12 @@ export function useWaveformLayout(context: LayoutContext) {
107
108
  })
108
109
  })
109
110
  const renderingOptions = computed(() => resolveWaveformRenderingOptions(props.rendering))
110
- const pageCount = computed(() => getPageCount(chartTracks.value.length, gridOptions.value))
111
+ const pageableTracks = computed(() =>
112
+ resolvePageableTracks(chartTracks.value, gridOptions.value.hideEmptyTracks),
113
+ )
114
+ const pageCount = computed(() => getPageCount(pageableTracks.value.length, gridOptions.value))
111
115
  const pagedTracks = computed(() =>
112
- paginateSeries(chartTracks.value, currentPage.value, gridOptions.value),
116
+ paginateSeries(pageableTracks.value, currentPage.value, gridOptions.value),
113
117
  )
114
118
  const layoutTracks = computed(() => {
115
119
  const tracksWithSeries = pagedTracks.value.filter((track) => track.series.length > 0)
@@ -357,16 +361,15 @@ export function useWaveformLayout(context: LayoutContext) {
357
361
  }
358
362
  : undefined
359
363
  })
360
- const resolveFrameNumber = (trackIndex: number): string | number | undefined => {
361
- if (props.frameNumber === undefined || props.frameNumber === null) return undefined
362
- if (chartTracks.value.length === 1) return props.frameNumber
363
- return typeof props.frameNumber === 'number'
364
- ? props.frameNumber + trackIndex
365
- : `${props.frameNumber}-${trackIndex + 1}`
366
- }
364
+ const resolveFrameNumber = createFrameNumberResolver(
365
+ () => props.frameNumber,
366
+ () => props.frameNumbers,
367
+ () => chartTracks.value,
368
+ )
367
369
  return {
368
370
  chartSeries,
369
371
  chartTracks,
372
+ pageableTracks,
370
373
  renderingOptions,
371
374
  gridOptions,
372
375
  pageCount,
@@ -43,6 +43,8 @@ export interface WaveformChartProps {
43
43
  yDomains?: Record<string, [number, number]>
44
44
  timeUnit?: 's' | 'ms'
45
45
  frameNumber?: string | number
46
+ /** Per-track frame watermark overrides, keyed by stable trackId. */
47
+ frameNumbers?: Record<string, string | number>
46
48
  frameStyle?: WaveformFrameStyle
47
49
  axes?: WaveformAxesOptions
48
50
  annotations?: WaveformAnnotation[]
@@ -104,6 +104,68 @@ describe('WaveformChart track order', () => {
104
104
  expect(wrapper.findAll('.waveform-chart__crosshair line')).toHaveLength(2)
105
105
  })
106
106
 
107
+ it('keeps incremental watermarks stable when empty ordered tracks are hidden', async () => {
108
+ const wrapper = await mountSizedChart(mergedFourTrackSeries(), {
109
+ frameNumber: 1,
110
+ grid: {
111
+ rowCount: 2,
112
+ columnCount: 1,
113
+ hideEmptyTracks: true,
114
+ trackOrder: ['frame-1', 'frame-2', 'frame-3', 'frame-4'],
115
+ },
116
+ })
117
+
118
+ expect(
119
+ wrapper.findAll('.waveform-chart__track').map((track) => track.attributes('data-track-id')),
120
+ ).toEqual(['frame-1', 'frame-3'])
121
+ expect(
122
+ wrapper.findAll('.waveform-chart__watermark').map((watermark) => watermark.text()),
123
+ ).toEqual(['1', '3'])
124
+
125
+ await wrapper.get('.ant-pagination-next button').trigger('click')
126
+
127
+ expect(
128
+ wrapper.findAll('.waveform-chart__track').map((track) => track.attributes('data-track-id')),
129
+ ).toEqual(['frame-4'])
130
+ expect(
131
+ wrapper.findAll('.waveform-chart__watermark').map((watermark) => watermark.text()),
132
+ ).toEqual(['4'])
133
+ })
134
+
135
+ it('uses stable track IDs for explicit frame watermark overrides', async () => {
136
+ const wrapper = await mountSizedChart(mergedFourTrackSeries(), {
137
+ frameNumber: 1,
138
+ frameNumbers: { 'frame-1': 'A', 'frame-3': 'C', 'frame-4': 'D' },
139
+ grid: {
140
+ rowCount: 2,
141
+ columnCount: 1,
142
+ hideEmptyTracks: true,
143
+ trackOrder: ['frame-1', 'frame-2', 'frame-3', 'frame-4'],
144
+ },
145
+ })
146
+
147
+ expect(
148
+ wrapper.findAll('.waveform-chart__watermark').map((watermark) => watermark.text()),
149
+ ).toEqual(['A', 'C'])
150
+
151
+ await wrapper.get('.ant-pagination-next button').trigger('click')
152
+
153
+ expect(
154
+ wrapper.findAll('.waveform-chart__watermark').map((watermark) => watermark.text()),
155
+ ).toEqual(['D'])
156
+ })
157
+
158
+ it('keeps the existing incremental watermark fallback when frame mappings are absent', async () => {
159
+ const wrapper = await mountSizedChart(orderedSeries(['frame-1', 'frame-2']), {
160
+ frameNumber: 7,
161
+ grid: { rowCount: 2, columnCount: 1 },
162
+ })
163
+
164
+ expect(
165
+ wrapper.findAll('.waveform-chart__watermark').map((watermark) => watermark.text()),
166
+ ).toEqual(['7', '8'])
167
+ })
168
+
107
169
  it('appends data tracks absent from trackOrder in input order', async () => {
108
170
  const wrapper = await mountSizedChart(
109
171
  {