waveform-analysis 0.1.29 → 0.1.30
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/index.cjs +1 -1
- package/dist/index.js +312 -303
- package/dist/types/components/WaveformChart.vue.d.ts +1 -0
- package/dist/types/components/core/constants.d.ts +2 -0
- package/dist/types/components/core/useWaveformChartController.d.ts +6 -1
- package/dist/types/components/core/useWaveformLayout.d.ts +0 -1
- package/dist/types/components/core/useWaveformPresentation.d.ts +5 -0
- package/dist/types/components/core/waveformChartTypes.d.ts +3 -2
- package/dist/types/components/data/types.d.ts +1 -1
- package/dist/types/components/index.d.ts +1 -1
- package/dist/types/components/waveform.d.ts +1 -1
- package/dist/types/demo/types.d.ts +4 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types/chart.d.ts +7 -0
- package/dist/types/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/App.vue +10 -0
- package/src/components/WaveformChart.vue +1 -0
- package/src/components/WaveformChartView.vue +13 -10
- package/src/components/core/constants.ts +3 -0
- package/src/components/core/useWaveformLayout.ts +0 -3
- package/src/components/core/useWaveformPresentation.ts +17 -2
- package/src/components/core/waveformChartTypes.ts +3 -0
- package/src/components/data/types.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/components/waveform.ts +1 -0
- package/src/components/waveformChartCases/sizingAndTitles.test.ts +59 -0
- package/src/demo/DemoChartHost.vue +1 -0
- package/src/demo/DemoGridFrameControls.vue +29 -0
- package/src/demo/plotMarginControls.test.ts +29 -0
- package/src/demo/types.ts +4 -0
- package/src/index.ts +1 -0
- package/src/styles.css +1 -0
- package/src/types/chart.ts +8 -0
- package/src/types/index.ts +1 -0
|
@@ -48,6 +48,7 @@ declare const __VLS_export: import("vue").DefineComponent<WaveformChartProps, {
|
|
|
48
48
|
annotationsVisible: boolean;
|
|
49
49
|
grid: import("..").WaveformGridOptions;
|
|
50
50
|
rendering: import("..").WaveformRenderingOptions;
|
|
51
|
+
plotMargin: import("..").WaveformPlotMargin;
|
|
51
52
|
defaultHiddenSeriesIds: string[];
|
|
52
53
|
cleanView: boolean;
|
|
53
54
|
presentationMode: boolean;
|
|
@@ -287,7 +287,6 @@ export declare function useWaveformChartController(props: ResolvedWaveformChartP
|
|
|
287
287
|
resolveSeriesYScale: (track: import("./types").TrackLayout, seriesId: string) => import("d3").ScaleLinear<number, number, never>;
|
|
288
288
|
annotationTrackLayouts: import("../annotation").AnnotationTrackLayout[];
|
|
289
289
|
renderedAnnotations: import("../annotation").RenderedAnnotation[];
|
|
290
|
-
xAxisTitleY: number;
|
|
291
290
|
editorSeries: import("../annotation").AnnotationSeriesInfo | undefined;
|
|
292
291
|
resolveFrameNumber: (trackIndex: number) => string | number | undefined;
|
|
293
292
|
fixedWidth: number | undefined;
|
|
@@ -318,8 +317,13 @@ export declare function useWaveformChartController(props: ResolvedWaveformChartP
|
|
|
318
317
|
titleMeasureStyle: import("vue").CSSProperties;
|
|
319
318
|
titleLayout: import("./title").RotatedTitleLayout;
|
|
320
319
|
titleAreaHeight: number;
|
|
320
|
+
resolvedPlotMargin: {
|
|
321
|
+
top: number;
|
|
322
|
+
bottom: number;
|
|
323
|
+
};
|
|
321
324
|
chartTopMargin: number;
|
|
322
325
|
drawingHeight: number;
|
|
326
|
+
xAxisTitleY: number;
|
|
323
327
|
innerHeight: number;
|
|
324
328
|
titleAreaStyle: import("vue").CSSProperties;
|
|
325
329
|
titleVisualStyle: import("vue").CSSProperties;
|
|
@@ -349,6 +353,7 @@ export declare function useWaveformChartController(props: ResolvedWaveformChartP
|
|
|
349
353
|
interactionMode?: import("..").WaveformInteractionMode | undefined;
|
|
350
354
|
grid: import("./grid").WaveformGridOptions;
|
|
351
355
|
rendering: import("..").WaveformRenderingOptions;
|
|
356
|
+
plotMargin: import("..").WaveformPlotMargin;
|
|
352
357
|
title?: import("..").WaveformTitleOptions | undefined;
|
|
353
358
|
legend: import("..").WaveformLegendOptions;
|
|
354
359
|
hiddenSeriesIds?: string[] | undefined;
|
|
@@ -54,7 +54,6 @@ export declare function useWaveformLayout(context: LayoutContext): {
|
|
|
54
54
|
resolveSeriesYScale: (track: TrackLayout, seriesId: string) => import("d3").ScaleLinear<number, number, never>;
|
|
55
55
|
annotationTrackLayouts: ComputedRef<AnnotationTrackLayout[]>;
|
|
56
56
|
renderedAnnotations: ComputedRef<import("../annotation").RenderedAnnotation[]>;
|
|
57
|
-
xAxisTitleY: ComputedRef<number>;
|
|
58
57
|
editorSeries: ComputedRef<AnnotationSeriesInfo | undefined>;
|
|
59
58
|
resolveFrameNumber: (trackIndex: number) => string | number | undefined;
|
|
60
59
|
};
|
|
@@ -39,8 +39,13 @@ export declare function useWaveformPresentation(context: PresentationContext): {
|
|
|
39
39
|
titleMeasureStyle: import("vue").ComputedRef<CSSProperties>;
|
|
40
40
|
titleLayout: import("vue").ComputedRef<import("./title").RotatedTitleLayout>;
|
|
41
41
|
titleAreaHeight: import("vue").ComputedRef<number>;
|
|
42
|
+
resolvedPlotMargin: import("vue").ComputedRef<{
|
|
43
|
+
top: number;
|
|
44
|
+
bottom: number;
|
|
45
|
+
}>;
|
|
42
46
|
chartTopMargin: import("vue").ComputedRef<number>;
|
|
43
47
|
drawingHeight: import("vue").ComputedRef<number>;
|
|
48
|
+
xAxisTitleY: import("vue").ComputedRef<number>;
|
|
44
49
|
innerHeight: import("vue").ComputedRef<number>;
|
|
45
50
|
titleAreaStyle: import("vue").ComputedRef<CSSProperties>;
|
|
46
51
|
titleVisualStyle: import("vue").ComputedRef<CSSProperties>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WaveformAnnotation, WaveformAxesOptions, WaveformData, WaveformDisplayMode, WaveformFrameStyle, WaveformInteractionMode, WaveformLegendOptions, WaveformOverlayMode, WaveformPoint, WaveformRenderingOptions, WaveformTitleOptions, WaveformZeroLineOptions, WaveformZoomEndPayload } from '../data/types';
|
|
1
|
+
import type { WaveformAnnotation, WaveformAxesOptions, WaveformData, WaveformDisplayMode, WaveformFrameStyle, WaveformInteractionMode, WaveformLegendOptions, WaveformOverlayMode, WaveformPlotMargin, WaveformPoint, WaveformRenderingOptions, WaveformTitleOptions, WaveformZeroLineOptions, WaveformZoomEndPayload } from '../data/types';
|
|
2
2
|
import type { WaveformGridOptions } from './grid';
|
|
3
3
|
export interface WaveformChartProps {
|
|
4
4
|
data: WaveformData;
|
|
@@ -27,6 +27,7 @@ export interface WaveformChartProps {
|
|
|
27
27
|
interactionMode?: WaveformInteractionMode;
|
|
28
28
|
grid?: WaveformGridOptions;
|
|
29
29
|
rendering?: WaveformRenderingOptions;
|
|
30
|
+
plotMargin?: WaveformPlotMargin;
|
|
30
31
|
title?: WaveformTitleOptions;
|
|
31
32
|
legend?: WaveformLegendOptions;
|
|
32
33
|
hiddenSeriesIds?: string[];
|
|
@@ -35,7 +36,7 @@ export interface WaveformChartProps {
|
|
|
35
36
|
presentationMode?: boolean;
|
|
36
37
|
zeroLine?: WaveformZeroLineOptions;
|
|
37
38
|
}
|
|
38
|
-
type DefaultedProp = 'displayMode' | 'overlayMode' | 'yLabel' | 'lineColor' | 'showTooltip' | 'zoomable' | 'pannable' | 'minVisiblePoints' | 'timeUnit' | 'annotations' | 'annotationsVisible' | 'grid' | 'rendering' | 'legend' | 'defaultHiddenSeriesIds' | 'cleanView' | 'presentationMode' | 'zeroLine';
|
|
39
|
+
type DefaultedProp = 'displayMode' | 'overlayMode' | 'yLabel' | 'lineColor' | 'showTooltip' | 'zoomable' | 'pannable' | 'minVisiblePoints' | 'timeUnit' | 'annotations' | 'annotationsVisible' | 'grid' | 'rendering' | 'plotMargin' | 'legend' | 'defaultHiddenSeriesIds' | 'cleanView' | 'presentationMode' | 'zeroLine';
|
|
39
40
|
export type ResolvedWaveformChartProps = Readonly<WaveformChartProps & Required<Pick<WaveformChartProps, DefaultedProp>>>;
|
|
40
41
|
export interface WaveformChartEmit {
|
|
41
42
|
(event: 'point-hover', point: WaveformPoint | null): void;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* 数据系统类型定义
|
|
3
3
|
* 重新导出 src/types 中的类型,方便组件内部使用
|
|
4
4
|
*/
|
|
5
|
-
export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from '../../types';
|
|
5
|
+
export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from '../../types';
|
|
6
6
|
export type { WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from '../core/grid';
|
|
7
7
|
export { normalizeWaveformData, normalizeWaveformSeries } from '../../core';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default as WaveformChart } from './WaveformChart.vue';
|
|
2
|
-
export type { SingleWaveformData, WaveformData, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformAxesOptions, WaveformZeroLineOptions, WaveformPoint, WaveformSeries, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from './data/types';
|
|
2
|
+
export type { SingleWaveformData, WaveformData, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformAxesOptions, WaveformZeroLineOptions, WaveformPoint, WaveformSeries, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from './data/types';
|
|
3
3
|
export type { WaveformGridOptions as WaveformGridConfig } from './core/grid';
|
|
4
4
|
export { WaveformTooltip } from './interaction';
|
|
5
5
|
export { WaveformTrack } from './rendering';
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* 向后兼容的导出文件
|
|
3
3
|
* 保留原有导入路径,内部从新的模块结构导出
|
|
4
4
|
*/
|
|
5
|
-
export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformTitleTextStyle, WaveformTitleOptions, WaveformFrameStyle, SingleWaveformData, WaveformSeries, WaveformLineStyle, WaveformData, NormalizedWaveformSeries, } from '../types';
|
|
5
|
+
export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformFrameStyle, SingleWaveformData, WaveformSeries, WaveformLineStyle, WaveformData, NormalizedWaveformSeries, } from '../types';
|
|
6
6
|
export { normalizeWaveformData, normalizeWaveformSeries } from '../core';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WaveformAnnotation, WaveformAxesOptions, WaveformData, WaveformDisplayMode, WaveformFrameStyle, WaveformGridTrackLines, WaveformInteractionMode, WaveformLegendOrientation, WaveformLegendPosition, WaveformLineStyle, WaveformOverlayMode, WaveformTitleOptions, WaveformZeroLineOptions, WaveformZoomEndPayload } from '../components';
|
|
1
|
+
import type { WaveformAnnotation, WaveformAxesOptions, WaveformData, WaveformDisplayMode, WaveformFrameStyle, WaveformGridTrackLines, WaveformInteractionMode, WaveformLegendOrientation, WaveformLegendPosition, WaveformLineStyle, WaveformOverlayMode, WaveformPlotMargin, WaveformTitleOptions, WaveformZeroLineOptions, WaveformZoomEndPayload } from '../components';
|
|
2
2
|
import type { DemoXAxisLabelFormat, DemoXAxisLabelTimeZone } from './useDemoXAxisLabelControls';
|
|
3
3
|
interface SelectOption<T> {
|
|
4
4
|
label: string;
|
|
@@ -9,6 +9,8 @@ export interface DemoControlPanelModel {
|
|
|
9
9
|
displayMode: WaveformDisplayMode;
|
|
10
10
|
overlayMode: WaveformOverlayMode;
|
|
11
11
|
showTooltip: boolean;
|
|
12
|
+
plotMarginTop: number;
|
|
13
|
+
plotMarginBottom: number;
|
|
12
14
|
cleanView: boolean;
|
|
13
15
|
presentationMode: boolean;
|
|
14
16
|
selectedSeriesId: string;
|
|
@@ -81,6 +83,7 @@ export interface DemoChartModel {
|
|
|
81
83
|
cleanView: boolean;
|
|
82
84
|
presentationMode: boolean;
|
|
83
85
|
showTooltip: boolean;
|
|
86
|
+
plotMargin: WaveformPlotMargin;
|
|
84
87
|
zeroLine: WaveformZeroLineOptions;
|
|
85
88
|
frameWatermarkVisible: boolean;
|
|
86
89
|
annotations: WaveformAnnotation[];
|
package/dist/types/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @packageDocumentation
|
|
4
4
|
*/
|
|
5
5
|
export { default as WaveformChart } from './components/WaveformChart.vue';
|
|
6
|
-
export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './types';
|
|
6
|
+
export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './types';
|
|
7
7
|
export type { WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from './components/core/grid';
|
|
8
8
|
export { normalizeWaveformData, normalizeWaveformSeries } from './core';
|
|
9
9
|
export { paddedDomain, buildMinorTicks, displayTime, formatEndpointTime, formatAxisTime, formatTooltipTime, resolveTrackGeometry, clamp, type TimeUnit, type TrackGeometry, } from './utils';
|
|
@@ -66,6 +66,13 @@ export interface WaveformRenderingOptions {
|
|
|
66
66
|
/** Minimum horizontal CSS-pixel spacing between rendered error bars. Use 0 to disable. */
|
|
67
67
|
errorBarMinSpacing?: number;
|
|
68
68
|
}
|
|
69
|
+
/** Pixel margins reserved above and below the waveform plotting area. */
|
|
70
|
+
export interface WaveformPlotMargin {
|
|
71
|
+
/** Space between the top of the drawing SVG and the plotting area. Defaults to 18. */
|
|
72
|
+
top?: number;
|
|
73
|
+
/** Space between the plotting area and the bottom of the drawing SVG. Defaults to 52. */
|
|
74
|
+
bottom?: number;
|
|
75
|
+
}
|
|
69
76
|
/** Text styling for the chart-level title. */
|
|
70
77
|
export interface WaveformTitleTextStyle {
|
|
71
78
|
color?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 类型定义统一导出
|
|
3
3
|
*/
|
|
4
|
-
export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, } from './chart';
|
|
4
|
+
export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, } from './chart';
|
|
5
5
|
export type { SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './data';
|
package/package.json
CHANGED
package/src/App.vue
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type WaveformLegendOrientation,
|
|
15
15
|
type WaveformLegendPosition,
|
|
16
16
|
type WaveformOverlayMode,
|
|
17
|
+
type WaveformPlotMargin,
|
|
17
18
|
type WaveformTitleOptions,
|
|
18
19
|
type WaveformZoomEndPayload,
|
|
19
20
|
type WaveformZeroLineOptions,
|
|
@@ -47,6 +48,8 @@ const annotationsVisible = ref(true)
|
|
|
47
48
|
const cleanView = ref(false)
|
|
48
49
|
const presentationMode = ref(false)
|
|
49
50
|
const showTooltip = ref(true)
|
|
51
|
+
const plotMarginTop = ref(18)
|
|
52
|
+
const plotMarginBottom = ref(52)
|
|
50
53
|
const zeroLineVisible = ref(false)
|
|
51
54
|
const zeroLineColor = ref('#98a2b3')
|
|
52
55
|
const zeroLineWidth = ref(1)
|
|
@@ -126,6 +129,10 @@ const zeroLine = computed<WaveformZeroLineOptions>(() => ({
|
|
|
126
129
|
width: zeroLineWidth.value,
|
|
127
130
|
dash: zeroLineDash.value,
|
|
128
131
|
}))
|
|
132
|
+
const plotMargin = computed<WaveformPlotMargin>(() => ({
|
|
133
|
+
top: plotMarginTop.value,
|
|
134
|
+
bottom: plotMarginBottom.value,
|
|
135
|
+
}))
|
|
129
136
|
|
|
130
137
|
const initialXValues = normalizeWaveformSeries(fullChartData).flatMap((series) =>
|
|
131
138
|
series.points.map((point) => point.x),
|
|
@@ -294,6 +301,8 @@ const controlPanelModel = reactive({
|
|
|
294
301
|
displayMode,
|
|
295
302
|
overlayMode,
|
|
296
303
|
showTooltip,
|
|
304
|
+
plotMarginTop,
|
|
305
|
+
plotMarginBottom,
|
|
297
306
|
cleanView,
|
|
298
307
|
presentationMode,
|
|
299
308
|
selectedSeriesId,
|
|
@@ -360,6 +369,7 @@ const chartModel = reactive({
|
|
|
360
369
|
cleanView,
|
|
361
370
|
presentationMode,
|
|
362
371
|
showTooltip,
|
|
372
|
+
plotMargin,
|
|
363
373
|
zeroLine,
|
|
364
374
|
frameWatermarkVisible,
|
|
365
375
|
annotations,
|
|
@@ -23,6 +23,7 @@ const props = withDefaults(defineProps<WaveformChartProps>(), {
|
|
|
23
23
|
interactionMode: undefined,
|
|
24
24
|
grid: () => ({ rowCount: 2, columnCount: 1, showPagination: true }),
|
|
25
25
|
rendering: () => ({}),
|
|
26
|
+
plotMargin: () => ({}),
|
|
26
27
|
legend: () => ({ position: 'top-right', orientation: 'auto' }),
|
|
27
28
|
defaultHiddenSeriesIds: () => [],
|
|
28
29
|
cleanView: false,
|
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
overlayMode,
|
|
20
20
|
resolvedChartLeftMargin,
|
|
21
21
|
titleAreaHeight,
|
|
22
|
+
resolvedPlotMargin,
|
|
22
23
|
pointerInsideChart,
|
|
23
24
|
handleNativeContextMenu,
|
|
24
25
|
titleAreaReserved,
|
|
@@ -118,6 +119,8 @@ const {
|
|
|
118
119
|
:data-presentation-mode="isPresentationMode"
|
|
119
120
|
:data-overlay-mode="overlayMode"
|
|
120
121
|
:data-chart-left-margin="resolvedChartLeftMargin"
|
|
122
|
+
:data-plot-margin-top="resolvedPlotMargin.top"
|
|
123
|
+
:data-plot-margin-bottom="resolvedPlotMargin.bottom"
|
|
121
124
|
:data-title-area-height="titleAreaHeight"
|
|
122
125
|
@pointerenter="pointerInsideChart = true"
|
|
123
126
|
@pointerleave="pointerInsideChart = false"
|
|
@@ -287,18 +290,18 @@ const {
|
|
|
287
290
|
/>
|
|
288
291
|
</g>
|
|
289
292
|
</g>
|
|
290
|
-
|
|
291
|
-
<text
|
|
292
|
-
v-if="resolvedXLabel && !isCleanView"
|
|
293
|
-
class="waveform-chart__label waveform-chart__x-label"
|
|
294
|
-
:x="innerWidth / 2"
|
|
295
|
-
:y="xAxisTitleY"
|
|
296
|
-
text-anchor="middle"
|
|
297
|
-
>
|
|
298
|
-
{{ resolvedXLabel }}
|
|
299
|
-
</text>
|
|
300
293
|
</g>
|
|
301
294
|
|
|
295
|
+
<text
|
|
296
|
+
v-if="resolvedXLabel && !isCleanView"
|
|
297
|
+
class="waveform-chart__label waveform-chart__x-label"
|
|
298
|
+
:x="resolvedChartLeftMargin + innerWidth / 2"
|
|
299
|
+
:y="xAxisTitleY"
|
|
300
|
+
text-anchor="middle"
|
|
301
|
+
>
|
|
302
|
+
{{ resolvedXLabel }}
|
|
303
|
+
</text>
|
|
304
|
+
|
|
302
305
|
<text
|
|
303
306
|
v-if="hasChartArea && !hasWaveformData"
|
|
304
307
|
class="waveform-chart__empty"
|
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
normalizeGridOptions,
|
|
25
25
|
paginateSeries,
|
|
26
26
|
resolveGridCellGeometry,
|
|
27
|
-
X_AXIS_BAND,
|
|
28
27
|
} from './grid'
|
|
29
28
|
import {
|
|
30
29
|
buildTrackLayouts,
|
|
@@ -316,7 +315,6 @@ export function useWaveformLayout(context: LayoutContext) {
|
|
|
316
315
|
)
|
|
317
316
|
: [],
|
|
318
317
|
)
|
|
319
|
-
const xAxisTitleY = computed(() => innerHeight.value + X_AXIS_BAND + 10)
|
|
320
318
|
const editorSeries = computed<AnnotationSeriesInfo | undefined>(() => {
|
|
321
319
|
const seriesId = annotationInteraction.editorDraft.value?.annotation.seriesId
|
|
322
320
|
const series = chartSeries.value.find((item) => item.id === seriesId)
|
|
@@ -360,7 +358,6 @@ export function useWaveformLayout(context: LayoutContext) {
|
|
|
360
358
|
resolveSeriesYScale,
|
|
361
359
|
annotationTrackLayouts,
|
|
362
360
|
renderedAnnotations,
|
|
363
|
-
xAxisTitleY,
|
|
364
361
|
editorSeries,
|
|
365
362
|
resolveFrameNumber,
|
|
366
363
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
TITLE_CHAR_WIDTH_RATIO,
|
|
8
8
|
TITLE_DEFAULT_FONT_SIZE,
|
|
9
9
|
TITLE_LINE_HEIGHT,
|
|
10
|
+
X_AXIS_TITLE_BOTTOM_OFFSET,
|
|
10
11
|
ZERO_LINE_DEFAULTS,
|
|
11
12
|
} from './constants'
|
|
12
13
|
import { calculateRotatedTitleLayout, TITLE_AREA_HORIZONTAL_PADDING } from './title'
|
|
@@ -144,11 +145,23 @@ export function useWaveformPresentation(context: PresentationContext) {
|
|
|
144
145
|
const titleAreaHeight = computed(() =>
|
|
145
146
|
titleAreaReserved.value ? titleLayout.value.areaHeight : 0,
|
|
146
147
|
)
|
|
147
|
-
const
|
|
148
|
+
const resolvePlotMargin = (value: number | undefined, fallback: number) =>
|
|
149
|
+
typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback
|
|
150
|
+
const resolvedPlotMargin = computed(() => ({
|
|
151
|
+
top: resolvePlotMargin(props.plotMargin.top, margin.top),
|
|
152
|
+
bottom: resolvePlotMargin(props.plotMargin.bottom, margin.bottom),
|
|
153
|
+
}))
|
|
154
|
+
const chartTopMargin = computed(() => resolvedPlotMargin.value.top)
|
|
148
155
|
const drawingHeight = computed(() =>
|
|
149
156
|
Math.max(0, chartHeight.value - titleAreaHeight.value - paginationBandHeight.value),
|
|
150
157
|
)
|
|
151
|
-
const
|
|
158
|
+
const xAxisTitleY = computed(() => Math.max(0, drawingHeight.value - X_AXIS_TITLE_BOTTOM_OFFSET))
|
|
159
|
+
const innerHeight = computed(() =>
|
|
160
|
+
Math.max(
|
|
161
|
+
0,
|
|
162
|
+
drawingHeight.value - resolvedPlotMargin.value.top - resolvedPlotMargin.value.bottom,
|
|
163
|
+
),
|
|
164
|
+
)
|
|
152
165
|
const titleAreaStyle = computed<CSSProperties>(() => ({
|
|
153
166
|
height: `${titleAreaHeight.value}px`,
|
|
154
167
|
justifyContent:
|
|
@@ -193,8 +206,10 @@ export function useWaveformPresentation(context: PresentationContext) {
|
|
|
193
206
|
titleMeasureStyle,
|
|
194
207
|
titleLayout,
|
|
195
208
|
titleAreaHeight,
|
|
209
|
+
resolvedPlotMargin,
|
|
196
210
|
chartTopMargin,
|
|
197
211
|
drawingHeight,
|
|
212
|
+
xAxisTitleY,
|
|
198
213
|
innerHeight,
|
|
199
214
|
titleAreaStyle,
|
|
200
215
|
titleVisualStyle,
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
WaveformInteractionMode,
|
|
8
8
|
WaveformLegendOptions,
|
|
9
9
|
WaveformOverlayMode,
|
|
10
|
+
WaveformPlotMargin,
|
|
10
11
|
WaveformPoint,
|
|
11
12
|
WaveformRenderingOptions,
|
|
12
13
|
WaveformTitleOptions,
|
|
@@ -42,6 +43,7 @@ export interface WaveformChartProps {
|
|
|
42
43
|
interactionMode?: WaveformInteractionMode
|
|
43
44
|
grid?: WaveformGridOptions
|
|
44
45
|
rendering?: WaveformRenderingOptions
|
|
46
|
+
plotMargin?: WaveformPlotMargin
|
|
45
47
|
title?: WaveformTitleOptions
|
|
46
48
|
legend?: WaveformLegendOptions
|
|
47
49
|
hiddenSeriesIds?: string[]
|
|
@@ -65,6 +67,7 @@ type DefaultedProp =
|
|
|
65
67
|
| 'annotationsVisible'
|
|
66
68
|
| 'grid'
|
|
67
69
|
| 'rendering'
|
|
70
|
+
| 'plotMargin'
|
|
68
71
|
| 'legend'
|
|
69
72
|
| 'defaultHiddenSeriesIds'
|
|
70
73
|
| 'cleanView'
|
package/src/components/index.ts
CHANGED
|
@@ -105,6 +105,65 @@ describe('WaveformChart', () => {
|
|
|
105
105
|
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('300')
|
|
106
106
|
})
|
|
107
107
|
|
|
108
|
+
it('configures plot top and bottom margins and reacts to updates', async () => {
|
|
109
|
+
const wrapper = await mountSizedChart(
|
|
110
|
+
{ kind: 'samples', values: [0, 1], sampleRate: 1 },
|
|
111
|
+
{
|
|
112
|
+
displayMode: 'compact',
|
|
113
|
+
grid: { rowCount: 1, columnCount: 1 },
|
|
114
|
+
plotMargin: { top: 30, bottom: 70 },
|
|
115
|
+
},
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
expect(wrapper.attributes('data-plot-margin-top')).toBe('30')
|
|
119
|
+
expect(wrapper.attributes('data-plot-margin-bottom')).toBe('70')
|
|
120
|
+
expect(wrapper.get('.waveform-chart__overlay').attributes('height')).toBe('260')
|
|
121
|
+
expect(wrapper.get('.waveform-chart__svg > g').attributes('transform')).toContain(', 30)')
|
|
122
|
+
|
|
123
|
+
await wrapper.setProps({ plotMargin: { top: 12 } })
|
|
124
|
+
|
|
125
|
+
expect(wrapper.attributes('data-plot-margin-top')).toBe('12')
|
|
126
|
+
expect(wrapper.attributes('data-plot-margin-bottom')).toBe('52')
|
|
127
|
+
expect(wrapper.get('.waveform-chart__overlay').attributes('height')).toBe('296')
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('falls back to default plot margins for invalid values', async () => {
|
|
131
|
+
const wrapper = await mountSizedChart(
|
|
132
|
+
{ kind: 'samples', values: [0, 1], sampleRate: 1 },
|
|
133
|
+
{
|
|
134
|
+
displayMode: 'compact',
|
|
135
|
+
grid: { rowCount: 1, columnCount: 1 },
|
|
136
|
+
plotMargin: { top: -1, bottom: Number.NaN },
|
|
137
|
+
},
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
expect(wrapper.attributes('data-plot-margin-top')).toBe('18')
|
|
141
|
+
expect(wrapper.attributes('data-plot-margin-bottom')).toBe('52')
|
|
142
|
+
expect(wrapper.get('.waveform-chart__overlay').attributes('height')).toBe('290')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('keeps the chart title and X-axis title fixed when plot margins change', async () => {
|
|
146
|
+
const wrapper = await mountSizedChart(
|
|
147
|
+
{ kind: 'samples', values: [0, 1], sampleRate: 1 },
|
|
148
|
+
{
|
|
149
|
+
displayMode: 'compact',
|
|
150
|
+
grid: { rowCount: 1, columnCount: 1 },
|
|
151
|
+
title: { text: '固定标题' },
|
|
152
|
+
},
|
|
153
|
+
)
|
|
154
|
+
const titleAreaStyle = wrapper.get('.waveform-chart__title-area').attributes('style')
|
|
155
|
+
const xAxisTitle = wrapper.get('.waveform-chart__x-label')
|
|
156
|
+
const initialX = xAxisTitle.attributes('x')
|
|
157
|
+
const initialY = xAxisTitle.attributes('y')
|
|
158
|
+
|
|
159
|
+
await wrapper.setProps({ plotMargin: { top: 60, bottom: 90 } })
|
|
160
|
+
|
|
161
|
+
expect(wrapper.get('.waveform-chart__title-area').attributes('style')).toBe(titleAreaStyle)
|
|
162
|
+
expect(wrapper.get('.waveform-chart__x-label').attributes('x')).toBe(initialX)
|
|
163
|
+
expect(wrapper.get('.waveform-chart__x-label').attributes('y')).toBe(initialY)
|
|
164
|
+
expect(wrapper.get('.waveform-chart__svg > g').attributes('transform')).toContain(', 60)')
|
|
165
|
+
})
|
|
166
|
+
|
|
108
167
|
it('does not render or reserve space for missing, hidden, or blank titles', async () => {
|
|
109
168
|
for (const title of [undefined, { visible: false, text: '隐藏标题' }, { text: ' ' }]) {
|
|
110
169
|
const wrapper = await mountSizedChart(
|
|
@@ -45,6 +45,7 @@ defineExpose({ resetViewport })
|
|
|
45
45
|
:clean-view="model.cleanView"
|
|
46
46
|
:presentation-mode="model.presentationMode"
|
|
47
47
|
:show-tooltip="model.showTooltip"
|
|
48
|
+
:plot-margin="model.plotMargin"
|
|
48
49
|
:zero-line="model.zeroLine"
|
|
49
50
|
:frame-number="model.frameWatermarkVisible ? 1 : undefined"
|
|
50
51
|
:annotations-visible="model.annotationsVisible"
|
|
@@ -8,6 +8,35 @@ const model = defineModel<DemoControlPanelModel>('model', { required: true })
|
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
10
|
<template>
|
|
11
|
+
<section class="control-section">
|
|
12
|
+
<h2>绘图区边距</h2>
|
|
13
|
+
<div class="plot-margin-controls">
|
|
14
|
+
<label class="frame-style-control">
|
|
15
|
+
<span>上边距</span>
|
|
16
|
+
<InputNumber
|
|
17
|
+
v-model:value="model.plotMarginTop"
|
|
18
|
+
:min="0"
|
|
19
|
+
:max="300"
|
|
20
|
+
:step="1"
|
|
21
|
+
addon-after="px"
|
|
22
|
+
size="small"
|
|
23
|
+
aria-label="绘图区上边距"
|
|
24
|
+
/>
|
|
25
|
+
</label>
|
|
26
|
+
<label class="frame-style-control">
|
|
27
|
+
<span>下边距</span>
|
|
28
|
+
<InputNumber
|
|
29
|
+
v-model:value="model.plotMarginBottom"
|
|
30
|
+
:min="0"
|
|
31
|
+
:max="300"
|
|
32
|
+
:step="1"
|
|
33
|
+
addon-after="px"
|
|
34
|
+
size="small"
|
|
35
|
+
aria-label="绘图区下边距"
|
|
36
|
+
/>
|
|
37
|
+
</label>
|
|
38
|
+
</div>
|
|
39
|
+
</section>
|
|
11
40
|
<section class="control-section">
|
|
12
41
|
<div class="control-section__header">
|
|
13
42
|
<h2>零值参考线</h2>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { flushPromises, mount } from '@vue/test-utils'
|
|
2
|
+
import { InputNumber } from 'ant-design-vue'
|
|
3
|
+
import { describe, expect, it } from 'vitest'
|
|
4
|
+
|
|
5
|
+
import App from '../App.vue'
|
|
6
|
+
import { WaveformChart } from '../components'
|
|
7
|
+
|
|
8
|
+
describe('plot margin demo controls', () => {
|
|
9
|
+
it('updates the chart top and bottom margins from the sidebar', async () => {
|
|
10
|
+
const wrapper = mount(App)
|
|
11
|
+
await flushPromises()
|
|
12
|
+
const chart = wrapper.getComponent(WaveformChart)
|
|
13
|
+
const controls = wrapper.get('.plot-margin-controls').findAllComponents(InputNumber)
|
|
14
|
+
|
|
15
|
+
expect(controls).toHaveLength(2)
|
|
16
|
+
expect(controls[0]?.props('value')).toBe(18)
|
|
17
|
+
expect(controls[1]?.props('value')).toBe(52)
|
|
18
|
+
expect(chart.props('plotMargin')).toEqual({ top: 18, bottom: 52 })
|
|
19
|
+
|
|
20
|
+
controls[0]?.vm.$emit('update:value', 30)
|
|
21
|
+
controls[1]?.vm.$emit('update:value', 70)
|
|
22
|
+
await flushPromises()
|
|
23
|
+
|
|
24
|
+
expect(chart.props('plotMargin')).toEqual({ top: 30, bottom: 70 })
|
|
25
|
+
expect(wrapper.get('.waveform-chart').attributes('data-plot-margin-top')).toBe('30')
|
|
26
|
+
expect(wrapper.get('.waveform-chart').attributes('data-plot-margin-bottom')).toBe('70')
|
|
27
|
+
wrapper.unmount()
|
|
28
|
+
})
|
|
29
|
+
})
|
package/src/demo/types.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
WaveformLegendPosition,
|
|
11
11
|
WaveformLineStyle,
|
|
12
12
|
WaveformOverlayMode,
|
|
13
|
+
WaveformPlotMargin,
|
|
13
14
|
WaveformTitleOptions,
|
|
14
15
|
WaveformZeroLineOptions,
|
|
15
16
|
WaveformZoomEndPayload,
|
|
@@ -26,6 +27,8 @@ export interface DemoControlPanelModel {
|
|
|
26
27
|
displayMode: WaveformDisplayMode
|
|
27
28
|
overlayMode: WaveformOverlayMode
|
|
28
29
|
showTooltip: boolean
|
|
30
|
+
plotMarginTop: number
|
|
31
|
+
plotMarginBottom: number
|
|
29
32
|
cleanView: boolean
|
|
30
33
|
presentationMode: boolean
|
|
31
34
|
selectedSeriesId: string
|
|
@@ -99,6 +102,7 @@ export interface DemoChartModel {
|
|
|
99
102
|
cleanView: boolean
|
|
100
103
|
presentationMode: boolean
|
|
101
104
|
showTooltip: boolean
|
|
105
|
+
plotMargin: WaveformPlotMargin
|
|
102
106
|
zeroLine: WaveformZeroLineOptions
|
|
103
107
|
frameWatermarkVisible: boolean
|
|
104
108
|
annotations: WaveformAnnotation[]
|
package/src/index.ts
CHANGED
package/src/styles.css
CHANGED
package/src/types/chart.ts
CHANGED
|
@@ -74,6 +74,14 @@ export interface WaveformRenderingOptions {
|
|
|
74
74
|
errorBarMinSpacing?: number
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/** Pixel margins reserved above and below the waveform plotting area. */
|
|
78
|
+
export interface WaveformPlotMargin {
|
|
79
|
+
/** Space between the top of the drawing SVG and the plotting area. Defaults to 18. */
|
|
80
|
+
top?: number
|
|
81
|
+
/** Space between the plotting area and the bottom of the drawing SVG. Defaults to 52. */
|
|
82
|
+
bottom?: number
|
|
83
|
+
}
|
|
84
|
+
|
|
77
85
|
/** Text styling for the chart-level title. */
|
|
78
86
|
export interface WaveformTitleTextStyle {
|
|
79
87
|
color?: string
|