waveform-analysis 0.1.43 → 0.1.47
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 +37 -30
- package/dist/index.cjs +1 -1
- package/dist/index.js +456 -461
- package/dist/style.css +1 -1
- package/dist/types/components/core/constants.d.ts +2 -2
- package/dist/types/components/core/grid.d.ts +3 -0
- package/dist/types/components/core/types.d.ts +4 -2
- package/dist/types/components/core/yAxisConstants.d.ts +2 -2
- package/dist/types/components/interaction/WaveformTooltip.vue.d.ts +1 -1
- package/dist/types/components/interaction/useWaveformViewport.d.ts +2 -1
- package/package.json +1 -1
- package/src/App.test.ts +2 -7
- package/src/components/WaveformChart.css +1 -1
- package/src/components/WaveformChartView.vue +2 -2
- package/src/components/core/constants.ts +3 -3
- package/src/components/core/grid.test.ts +13 -1
- package/src/components/core/grid.ts +14 -4
- package/src/components/core/layout.test.ts +3 -3
- package/src/components/core/layout.ts +2 -2
- package/src/components/core/trackLayoutBuilder.ts +14 -23
- package/src/components/core/types.ts +4 -2
- package/src/components/core/useWaveformChartController.ts +1 -0
- package/src/components/core/useWaveformLayout.ts +12 -6
- package/src/components/core/yAxisConstants.ts +2 -2
- package/src/components/interaction/WaveformHoverHost.vue +3 -1
- package/src/components/interaction/WaveformTooltip.test.ts +23 -0
- package/src/components/interaction/WaveformTooltip.vue +9 -6
- package/src/components/interaction/useWaveformHover.ts +21 -15
- package/src/components/interaction/useWaveformViewport.ts +20 -15
- package/src/components/interaction/useWaveformZoom.ts +1 -1
- package/src/components/rendering/WaveformHoverLayer.vue +5 -11
- package/src/components/rendering/WaveformTrack.vue +1 -0
- package/src/components/rendering/WaveformTrackAxes.vue +8 -6
- package/src/components/waveformChartCases/dataLifecycle.test.ts +1 -1
- package/src/components/waveformChartCases/frameGridAndFormatting.test.ts +10 -0
- package/src/components/waveformChartCases/incompleteDataHover.test.ts +67 -0
- package/src/components/waveformChartCases/layoutAndDomains.test.ts +5 -5
- package/src/components/waveformChartCases/legendVisibility.test.ts +1 -1
- package/src/components/waveformChartCases/multiAxisAndPagination.test.ts +50 -1
- package/src/components/waveformChartCases/trackOrder.test.ts +131 -0
- package/src/data/simulatedWaveforms.test.ts +7 -2
- package/src/data/simulatedWaveforms.ts +12 -7
package/dist/style.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.waveform-annotation-layer[data-v-7254383e]{pointer-events:none}.waveform-annotation-layer--interactive .waveform-annotation[data-v-7254383e]{pointer-events:auto;cursor:grab;touch-action:none}.waveform-annotation-layer--interactive .waveform-annotation[data-v-7254383e]:active{cursor:grabbing}.waveform-annotation__arrow[data-v-7254383e]{stroke-width:2px;stroke-linecap:round;pointer-events:none}.waveform-annotation__box[data-v-7254383e]{stroke-width:1px;rx:3}.waveform-annotation-layer--interactive .waveform-annotation__box[data-v-7254383e]{pointer-events:auto}.waveform-annotation__text[data-v-7254383e]{pointer-events:none;-webkit-user-select:none;user-select:none}.waveform-annotation-context-menu[data-v-c70c98a1]{z-index:30;background:#fff;border:1px solid #dfe5ef;border-radius:4px;min-width:112px;padding:4px;display:grid;position:absolute;box-shadow:0 4px 14px #10182829}.waveform-annotation-context-menu button[data-v-c70c98a1]{color:#344054;text-align:left;white-space:nowrap;cursor:pointer;background:0 0;border:0;border-radius:3px;padding:7px 10px;font-size:12px}.waveform-annotation-context-menu button[data-v-c70c98a1]:hover{background:#f2f4f7}.waveform-annotation-editor-root,.waveform-annotation-editor-root *{box-sizing:border-box}.waveform-annotation-editor-root{color:#344054;text-align:left;text-transform:none;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif;line-height:1.5715}.waveform-annotation-editor-root button,.waveform-annotation-editor-root input,.waveform-annotation-editor-root select,.waveform-annotation-editor-root textarea{letter-spacing:normal;text-transform:none;font-family:inherit}.waveform-annotation-editor-root button{appearance:auto}.waveform-annotation-editor__content[data-v-42466cf9]{color:#344054;gap:20px;font-size:13px;display:grid}.waveform-annotation-editor__coordinates[data-v-42466cf9]{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;display:grid}.waveform-annotation-editor__coordinates span[data-v-42466cf9],.waveform-annotation-editor__series[data-v-42466cf9]{background:#f8fafc;border:1px solid #eaecf0;border-radius:7px;align-items:center;gap:8px;min-width:0;padding:9px 10px;display:flex}.waveform-annotation-editor__coordinates span[data-v-42466cf9]{color:#667085}.waveform-annotation-editor__coordinate-input[data-v-42466cf9]{color:#667085;background:#f8fafc;border:1px solid #eaecf0;border-radius:7px;align-items:center;gap:8px;min-width:0;padding:9px 10px;display:flex}.waveform-annotation-editor__coordinate-input[data-v-42466cf9] .ant-input-number{color:#344054;width:auto;min-width:96px;font-family:SFMono-Regular,Consolas,monospace}.waveform-annotation-editor__coordinate-value[data-v-42466cf9]{display:none}.waveform-annotation-editor__coordinate-error[data-v-42466cf9]{color:#d92d20;grid-column:1/-1;margin:-12px 0 0;font-size:11px;line-height:1.4}.waveform-annotation-editor__coordinate-hint[data-v-42466cf9]{color:#98a2b3;margin:-12px 0 0;font-size:11px;line-height:1.4}.waveform-annotation-editor__coordinates b[data-v-42466cf9]{color:#1677ff;font-size:11px}.waveform-annotation-editor__coordinates code[data-v-42466cf9]{color:#344054;text-overflow:ellipsis;white-space:nowrap;min-width:0;font-family:SFMono-Regular,Consolas,monospace;font-size:11px;overflow:hidden}.waveform-annotation-editor__series[data-v-42466cf9]{gap:10px}.waveform-annotation-editor__series i[data-v-42466cf9]{border:1px solid #0000001f;border-radius:2px;flex:0 0 12px;width:12px;height:12px}.waveform-annotation-editor__series-label[data-v-42466cf9]{gap:1px;min-width:0;display:grid}.waveform-annotation-editor__series select[data-v-42466cf9]{color:#344054;min-width:0;max-width:190px;font:inherit;background:#fff;border:1px solid #d0d5dd;border-radius:5px;margin-left:auto;padding:5px 7px}.waveform-annotation-editor__series small[data-v-42466cf9]{color:#667085;font-size:10px}.waveform-annotation-editor__series b[data-v-42466cf9]{color:#344054;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.waveform-annotation-editor__series code[data-v-42466cf9]{color:#667085;margin-left:auto;font-size:11px}.waveform-annotation-editor__field[data-v-42466cf9],.waveform-annotation-editor__color-field[data-v-42466cf9]{gap:4px;display:grid}.waveform-annotation-editor__label-row[data-v-42466cf9]{justify-content:space-between;align-items:center;gap:8px;display:flex}.waveform-annotation-editor__label-row b[data-v-42466cf9],.waveform-annotation-editor__colors legend[data-v-42466cf9]{color:#344054;font-size:12px;font-weight:600}.waveform-annotation-editor__label-row small[data-v-42466cf9]{color:#98a2b3;font-size:11px}.waveform-annotation-editor textarea[data-v-42466cf9]{box-sizing:border-box;color:#1d2939;width:100%;min-height:92px;font:inherit;resize:vertical;background:#fff;border:1px solid #d0d5dd;border-radius:7px;padding:10px 11px;line-height:1.5}.waveform-annotation-editor textarea[data-v-42466cf9]:focus{border-color:#1677ff;outline:3px solid #1677ff24}.waveform-annotation-editor__colors[data-v-42466cf9]{border:0;border-top:1px solid #eaecf0;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;min-width:0;margin:0;padding:14px 0 0;display:grid}.waveform-annotation-editor__colors legend[data-v-42466cf9]{grid-column:1/-1;padding:0}.waveform-annotation-editor__color-field[data-v-42466cf9]{color:#667085;text-align:left;place-items:stretch stretch;min-width:0;font-size:11px}.waveform-annotation-editor__color-field[data-v-42466cf9] .vc-color-wrap{border:1px solid #cfd5df;border-radius:6px;width:100%;height:34px;margin-right:0;box-shadow:inset 0 0 0 1px #ffffff61}.waveform-annotation-editor__color-field[data-v-42466cf9] .vc-color-wrap:hover{border-color:#98a2b3;box-shadow:0 0 0 3px #1677ff1f}@media (width<=420px){.waveform-annotation-editor__content[data-v-42466cf9]{gap:16px}.waveform-annotation-editor__coordinates[data-v-42466cf9]{grid-template-columns:1fr}}.waveform-tooltip[data-v-1f55d6c8]{box-sizing:border-box;z-index:2;color:#505050;pointer-events:none;background:#fff;border:1px solid #e3e7eb;border-radius:4px;gap:2px;width:max-content;max-width:min(560px,100% - 16px);padding:9px 12px;font:14px/1.35 Arial,sans-serif;display:grid;position:absolute;box-shadow:0 3px 10px #10182826}.waveform-tooltip__series[data-v-1f55d6c8]{grid-template-columns:8px minmax(0,1fr);align-items:start;gap:6px;display:grid}.waveform-tooltip__series i[data-v-1f55d6c8]{border-radius:50%;flex:none;width:8px;height:8px;margin-top:4px}.waveform-tooltip__series-content[data-v-1f55d6c8]{flex-wrap:nowrap;align-items:baseline;gap:12px;min-width:0;display:flex}.waveform-tooltip__series-label[data-v-1f55d6c8]{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}.waveform-tooltip__value[data-v-1f55d6c8]{white-space:nowrap;flex:none}.waveform-tooltip__series strong[data-v-1f55d6c8]{font-weight:600}.waveform-tooltip__value[data-v-1f55d6c8]{color:#555}.waveform-tooltip__series small[data-v-1f55d6c8]{color:#667085}.waveform-track__line[data-v-42a3fd47]{fill:none;stroke-width:1.5px;stroke-linejoin:round;stroke-linecap:round}.waveform-track__error-bar[data-v-42a3fd47]{fill:none}.waveform-track__point[data-v-42a3fd47],.waveform-track__error-bar[data-v-42a3fd47]{pointer-events:none}.waveform-track{isolation:isolate;pointer-events:none}.waveform-track__y-axis-label-bg{fill:#fff;opacity:.9;pointer-events:none}.waveform-track__y-axis-label{pointer-events:none;font-size:12px;font-weight:500}.waveform-track__grid{pointer-events:none}.waveform-track__grid--major line{stroke:#dfe5ef;stroke-width:1px}.waveform-track__grid--minor line{stroke:#f2f5fa;stroke-width:1px}.waveform-track__plot-frame,.waveform-track__plot-background{pointer-events:none}.waveform-track__watermark{fill:#1677ff1a;pointer-events:none;-webkit-user-select:none;user-select:none;font-family:Consolas,Monaco,Courier New,monospace}.waveform-track__overlay{fill:#0000;cursor:crosshair;pointer-events:all;touch-action:none}.waveform-track__no-visible-series{fill:#8c8c8c;pointer-events:none;font:13px sans-serif}.waveform-track__overlay.is-zoomable{cursor:grab}.waveform-track__overlay.is-zoomable:active{cursor:grabbing}.waveform-track__overlay.is-annotating{cursor:crosshair}.waveform-track__overlay.is-presentation{cursor:default;pointer-events:none}.waveform-track__zero-line{fill:none;pointer-events:none}.waveform-track__axis-endpoint{fill:#667085;pointer-events:none;font-size:11px}.waveform-track__axis-exponent{fill:#666;font-family:sans-serif;font-size:10px}.waveform-track__axis path,.waveform-track__axis line{stroke:#1f2937}.waveform-track__axis text{fill:#667085;font-size:11px}.waveform-legend[data-v-2ae12e71]{pointer-events:none;overflow:hidden}.waveform-legend__viewport[data-v-2ae12e71]{box-sizing:border-box;width:100%;height:100%;padding:8px;display:flex;overflow:hidden}.waveform-legend__viewport--top-left[data-v-2ae12e71]{justify-content:flex-start;align-items:flex-start}.waveform-legend__viewport--top[data-v-2ae12e71]{justify-content:center;align-items:flex-start}.waveform-legend__viewport--top-right[data-v-2ae12e71]{justify-content:flex-end;align-items:flex-start}.waveform-legend__viewport--right[data-v-2ae12e71]{justify-content:flex-end;align-items:center}.waveform-legend__viewport--bottom-right[data-v-2ae12e71]{justify-content:flex-end;align-items:flex-end}.waveform-legend__viewport--bottom[data-v-2ae12e71]{justify-content:center;align-items:flex-end}.waveform-legend__viewport--bottom-left[data-v-2ae12e71]{justify-content:flex-start;align-items:flex-end}.waveform-legend__viewport--left[data-v-2ae12e71]{justify-content:flex-start;align-items:center}.waveform-legend__panel[data-v-2ae12e71]{color:#344054;border:1px solid #d0d5dde6;border-radius:4px;flex:0 auto;gap:5px 12px;max-width:100%;max-height:100%;padding:5px 7px;font:12px/1.35 sans-serif;display:flex;overflow:hidden}.waveform-legend__panel--interactive[data-v-2ae12e71]{pointer-events:auto}.waveform-legend__panel--horizontal[data-v-2ae12e71]{flex-flow:wrap;align-items:center}.waveform-legend__panel--vertical[data-v-2ae12e71]{flex-flow:column;align-items:stretch}.waveform-legend__item[data-v-2ae12e71]{min-width:0;max-width:160px;color:inherit;font:inherit;text-align:left;white-space:nowrap;appearance:none;background:0 0;border:0;align-items:center;gap:6px;padding:0;display:flex}.waveform-legend__item[data-v-2ae12e71]:disabled{opacity:1}.waveform-legend__panel--interactive .waveform-legend__item[data-v-2ae12e71]{cursor:pointer}.waveform-legend__panel--interactive .waveform-legend__item[data-v-2ae12e71]:focus-visible{outline-offset:2px;outline:2px solid #1677ff}.waveform-legend__item.is-hidden[data-v-2ae12e71]{opacity:.45}.waveform-legend__item.is-hidden .waveform-legend__label[data-v-2ae12e71]{text-decoration:line-through}.waveform-legend__swatch[data-v-2ae12e71]{stroke-linecap:round;stroke-linejoin:round;flex:0 0 26px;width:26px;height:16px;overflow:visible}.waveform-legend__label[data-v-2ae12e71]{text-overflow:ellipsis;min-width:0;overflow:hidden}.waveform-track__crosshair[data-v-a6f82099]{pointer-events:none}.waveform-track__crosshair line[data-v-a6f82099]{stroke:#57617b;stroke-width:1px;stroke-dasharray:4 3}.waveform-chart{box-sizing:border-box;color:#475467;background:#fff;border:1px solid #00000014;border-radius:6px;min-width:0;min-height:180px;position:relative;overflow:hidden}.waveform-chart--clean{border-color:#0000}.waveform-chart__pagination{z-index:2;position:absolute;bottom:6px;right:10px}.waveform-chart__title-area{background:#fff;align-items:center;width:100%;min-width:0;padding:0 24px;display:flex;position:relative;overflow:hidden}.waveform-chart__title-measure{visibility:hidden;pointer-events:none;display:block;position:absolute}.waveform-chart__title-visual{flex:none;min-width:0;position:relative;overflow:visible}.waveform-chart__title-text{transform-origin:50%;line-height:1.2;display:block;position:absolute;top:50%;left:50%;overflow:visible}.waveform-chart__pagination .ant-pagination-item,.waveform-chart__pagination .ant-pagination-prev .ant-pagination-item-link,.waveform-chart__pagination .ant-pagination-next .ant-pagination-item-link{background:#fff;border-color:#d9d9d9}.waveform-chart__pagination .ant-pagination-item-active{border-color:#1677ff}.waveform-chart__grid-slot-placeholder{fill:#fafbfc;stroke:#e4e7ec;stroke-dasharray:4 4;pointer-events:none}.waveform-chart__svg{max-width:100%;display:block}.waveform-chart__overlay{fill:#0000;cursor:crosshair;touch-action:none}.waveform-chart__overlay.is-zoomable{cursor:crosshair}.waveform-chart--panning .waveform-chart__overlay.is-zoomable{cursor:grabbing}.waveform-chart__zoom-selection{fill:#1677ff24;stroke:#1677ff;stroke-width:1px;pointer-events:none}.waveform-chart__overlay.is-annotating{cursor:crosshair}.waveform-chart--presentation .waveform-chart__overlay{cursor:default;pointer-events:none}.waveform-chart__label,.waveform-chart__empty{fill:#595959;font-size:12px}
|
|
1
|
+
.waveform-annotation-layer[data-v-7254383e]{pointer-events:none}.waveform-annotation-layer--interactive .waveform-annotation[data-v-7254383e]{pointer-events:auto;cursor:grab;touch-action:none}.waveform-annotation-layer--interactive .waveform-annotation[data-v-7254383e]:active{cursor:grabbing}.waveform-annotation__arrow[data-v-7254383e]{stroke-width:2px;stroke-linecap:round;pointer-events:none}.waveform-annotation__box[data-v-7254383e]{stroke-width:1px;rx:3}.waveform-annotation-layer--interactive .waveform-annotation__box[data-v-7254383e]{pointer-events:auto}.waveform-annotation__text[data-v-7254383e]{pointer-events:none;-webkit-user-select:none;user-select:none}.waveform-annotation-context-menu[data-v-c70c98a1]{z-index:30;background:#fff;border:1px solid #dfe5ef;border-radius:4px;min-width:112px;padding:4px;display:grid;position:absolute;box-shadow:0 4px 14px #10182829}.waveform-annotation-context-menu button[data-v-c70c98a1]{color:#344054;text-align:left;white-space:nowrap;cursor:pointer;background:0 0;border:0;border-radius:3px;padding:7px 10px;font-size:12px}.waveform-annotation-context-menu button[data-v-c70c98a1]:hover{background:#f2f4f7}.waveform-annotation-editor-root,.waveform-annotation-editor-root *{box-sizing:border-box}.waveform-annotation-editor-root{color:#344054;text-align:left;text-transform:none;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif;line-height:1.5715}.waveform-annotation-editor-root button,.waveform-annotation-editor-root input,.waveform-annotation-editor-root select,.waveform-annotation-editor-root textarea{letter-spacing:normal;text-transform:none;font-family:inherit}.waveform-annotation-editor-root button{appearance:auto}.waveform-annotation-editor__content[data-v-42466cf9]{color:#344054;gap:20px;font-size:13px;display:grid}.waveform-annotation-editor__coordinates[data-v-42466cf9]{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;display:grid}.waveform-annotation-editor__coordinates span[data-v-42466cf9],.waveform-annotation-editor__series[data-v-42466cf9]{background:#f8fafc;border:1px solid #eaecf0;border-radius:7px;align-items:center;gap:8px;min-width:0;padding:9px 10px;display:flex}.waveform-annotation-editor__coordinates span[data-v-42466cf9]{color:#667085}.waveform-annotation-editor__coordinate-input[data-v-42466cf9]{color:#667085;background:#f8fafc;border:1px solid #eaecf0;border-radius:7px;align-items:center;gap:8px;min-width:0;padding:9px 10px;display:flex}.waveform-annotation-editor__coordinate-input[data-v-42466cf9] .ant-input-number{color:#344054;width:auto;min-width:96px;font-family:SFMono-Regular,Consolas,monospace}.waveform-annotation-editor__coordinate-value[data-v-42466cf9]{display:none}.waveform-annotation-editor__coordinate-error[data-v-42466cf9]{color:#d92d20;grid-column:1/-1;margin:-12px 0 0;font-size:11px;line-height:1.4}.waveform-annotation-editor__coordinate-hint[data-v-42466cf9]{color:#98a2b3;margin:-12px 0 0;font-size:11px;line-height:1.4}.waveform-annotation-editor__coordinates b[data-v-42466cf9]{color:#1677ff;font-size:11px}.waveform-annotation-editor__coordinates code[data-v-42466cf9]{color:#344054;text-overflow:ellipsis;white-space:nowrap;min-width:0;font-family:SFMono-Regular,Consolas,monospace;font-size:11px;overflow:hidden}.waveform-annotation-editor__series[data-v-42466cf9]{gap:10px}.waveform-annotation-editor__series i[data-v-42466cf9]{border:1px solid #0000001f;border-radius:2px;flex:0 0 12px;width:12px;height:12px}.waveform-annotation-editor__series-label[data-v-42466cf9]{gap:1px;min-width:0;display:grid}.waveform-annotation-editor__series select[data-v-42466cf9]{color:#344054;min-width:0;max-width:190px;font:inherit;background:#fff;border:1px solid #d0d5dd;border-radius:5px;margin-left:auto;padding:5px 7px}.waveform-annotation-editor__series small[data-v-42466cf9]{color:#667085;font-size:10px}.waveform-annotation-editor__series b[data-v-42466cf9]{color:#344054;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.waveform-annotation-editor__series code[data-v-42466cf9]{color:#667085;margin-left:auto;font-size:11px}.waveform-annotation-editor__field[data-v-42466cf9],.waveform-annotation-editor__color-field[data-v-42466cf9]{gap:4px;display:grid}.waveform-annotation-editor__label-row[data-v-42466cf9]{justify-content:space-between;align-items:center;gap:8px;display:flex}.waveform-annotation-editor__label-row b[data-v-42466cf9],.waveform-annotation-editor__colors legend[data-v-42466cf9]{color:#344054;font-size:12px;font-weight:600}.waveform-annotation-editor__label-row small[data-v-42466cf9]{color:#98a2b3;font-size:11px}.waveform-annotation-editor textarea[data-v-42466cf9]{box-sizing:border-box;color:#1d2939;width:100%;min-height:92px;font:inherit;resize:vertical;background:#fff;border:1px solid #d0d5dd;border-radius:7px;padding:10px 11px;line-height:1.5}.waveform-annotation-editor textarea[data-v-42466cf9]:focus{border-color:#1677ff;outline:3px solid #1677ff24}.waveform-annotation-editor__colors[data-v-42466cf9]{border:0;border-top:1px solid #eaecf0;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;min-width:0;margin:0;padding:14px 0 0;display:grid}.waveform-annotation-editor__colors legend[data-v-42466cf9]{grid-column:1/-1;padding:0}.waveform-annotation-editor__color-field[data-v-42466cf9]{color:#667085;text-align:left;place-items:stretch stretch;min-width:0;font-size:11px}.waveform-annotation-editor__color-field[data-v-42466cf9] .vc-color-wrap{border:1px solid #cfd5df;border-radius:6px;width:100%;height:34px;margin-right:0;box-shadow:inset 0 0 0 1px #ffffff61}.waveform-annotation-editor__color-field[data-v-42466cf9] .vc-color-wrap:hover{border-color:#98a2b3;box-shadow:0 0 0 3px #1677ff1f}@media (width<=420px){.waveform-annotation-editor__content[data-v-42466cf9]{gap:16px}.waveform-annotation-editor__coordinates[data-v-42466cf9]{grid-template-columns:1fr}}.waveform-tooltip[data-v-b9ed144d]{box-sizing:border-box;z-index:2;color:#505050;pointer-events:none;background:#fff;border:1px solid #e3e7eb;border-radius:4px;gap:2px;width:max-content;max-width:min(560px,100% - 16px);padding:9px 12px;font:14px/1.35 Arial,sans-serif;display:grid;position:absolute;box-shadow:0 3px 10px #10182826}.waveform-tooltip__series[data-v-b9ed144d]{grid-template-columns:8px minmax(0,1fr);align-items:start;gap:6px;display:grid}.waveform-tooltip__series i[data-v-b9ed144d]{border-radius:50%;flex:none;width:8px;height:8px;margin-top:4px}.waveform-tooltip__series-content[data-v-b9ed144d]{flex-wrap:nowrap;align-items:baseline;gap:12px;min-width:0;display:flex}.waveform-tooltip__series-label[data-v-b9ed144d]{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}.waveform-tooltip__value[data-v-b9ed144d]{white-space:nowrap;flex:none}.waveform-tooltip__series strong[data-v-b9ed144d]{font-weight:600}.waveform-tooltip__value[data-v-b9ed144d]{color:#555}.waveform-tooltip__series small[data-v-b9ed144d]{color:#667085}.waveform-track__line[data-v-42a3fd47]{fill:none;stroke-width:1.5px;stroke-linejoin:round;stroke-linecap:round}.waveform-track__error-bar[data-v-42a3fd47]{fill:none}.waveform-track__point[data-v-42a3fd47],.waveform-track__error-bar[data-v-42a3fd47]{pointer-events:none}.waveform-track{isolation:isolate;pointer-events:none}.waveform-track__y-axis-label-bg{fill:#fff;opacity:.9;pointer-events:none}.waveform-track__y-axis-label{pointer-events:none;font-size:12px;font-weight:500}.waveform-track__grid{pointer-events:none}.waveform-track__grid--major line{stroke:#dfe5ef;stroke-width:1px}.waveform-track__grid--minor line{stroke:#f2f5fa;stroke-width:1px}.waveform-track__plot-frame,.waveform-track__plot-background{pointer-events:none}.waveform-track__watermark{fill:#1677ff1a;pointer-events:none;-webkit-user-select:none;user-select:none;font-family:Consolas,Monaco,Courier New,monospace}.waveform-track__overlay{fill:#0000;cursor:crosshair;pointer-events:all;touch-action:none}.waveform-track__no-visible-series{fill:#8c8c8c;pointer-events:none;font:13px sans-serif}.waveform-track__overlay.is-zoomable{cursor:grab}.waveform-track__overlay.is-zoomable:active{cursor:grabbing}.waveform-track__overlay.is-annotating{cursor:crosshair}.waveform-track__overlay.is-presentation{cursor:default;pointer-events:none}.waveform-track__zero-line{fill:none;pointer-events:none}.waveform-track__axis-endpoint{fill:#667085;pointer-events:none;font-size:11px}.waveform-track__axis-exponent{fill:#666;font-family:sans-serif;font-size:10px}.waveform-track__axis path,.waveform-track__axis line{stroke:#1f2937}.waveform-track__axis text{fill:#667085;font-size:11px}.waveform-legend[data-v-2ae12e71]{pointer-events:none;overflow:hidden}.waveform-legend__viewport[data-v-2ae12e71]{box-sizing:border-box;width:100%;height:100%;padding:8px;display:flex;overflow:hidden}.waveform-legend__viewport--top-left[data-v-2ae12e71]{justify-content:flex-start;align-items:flex-start}.waveform-legend__viewport--top[data-v-2ae12e71]{justify-content:center;align-items:flex-start}.waveform-legend__viewport--top-right[data-v-2ae12e71]{justify-content:flex-end;align-items:flex-start}.waveform-legend__viewport--right[data-v-2ae12e71]{justify-content:flex-end;align-items:center}.waveform-legend__viewport--bottom-right[data-v-2ae12e71]{justify-content:flex-end;align-items:flex-end}.waveform-legend__viewport--bottom[data-v-2ae12e71]{justify-content:center;align-items:flex-end}.waveform-legend__viewport--bottom-left[data-v-2ae12e71]{justify-content:flex-start;align-items:flex-end}.waveform-legend__viewport--left[data-v-2ae12e71]{justify-content:flex-start;align-items:center}.waveform-legend__panel[data-v-2ae12e71]{color:#344054;border:1px solid #d0d5dde6;border-radius:4px;flex:0 auto;gap:5px 12px;max-width:100%;max-height:100%;padding:5px 7px;font:12px/1.35 sans-serif;display:flex;overflow:hidden}.waveform-legend__panel--interactive[data-v-2ae12e71]{pointer-events:auto}.waveform-legend__panel--horizontal[data-v-2ae12e71]{flex-flow:wrap;align-items:center}.waveform-legend__panel--vertical[data-v-2ae12e71]{flex-flow:column;align-items:stretch}.waveform-legend__item[data-v-2ae12e71]{min-width:0;max-width:160px;color:inherit;font:inherit;text-align:left;white-space:nowrap;appearance:none;background:0 0;border:0;align-items:center;gap:6px;padding:0;display:flex}.waveform-legend__item[data-v-2ae12e71]:disabled{opacity:1}.waveform-legend__panel--interactive .waveform-legend__item[data-v-2ae12e71]{cursor:pointer}.waveform-legend__panel--interactive .waveform-legend__item[data-v-2ae12e71]:focus-visible{outline-offset:2px;outline:2px solid #1677ff}.waveform-legend__item.is-hidden[data-v-2ae12e71]{opacity:.45}.waveform-legend__item.is-hidden .waveform-legend__label[data-v-2ae12e71]{text-decoration:line-through}.waveform-legend__swatch[data-v-2ae12e71]{stroke-linecap:round;stroke-linejoin:round;flex:0 0 26px;width:26px;height:16px;overflow:visible}.waveform-legend__label[data-v-2ae12e71]{text-overflow:ellipsis;min-width:0;overflow:hidden}.waveform-track__crosshair[data-v-044318c2]{pointer-events:none}.waveform-track__crosshair line[data-v-044318c2]{stroke:#57617b;stroke-width:1px;stroke-dasharray:4 3}.waveform-chart{box-sizing:border-box;color:#475467;background:#fff;border:1.2px solid #00000014;border-radius:6px;min-width:0;min-height:180px;position:relative;overflow:hidden}.waveform-chart--clean{border-color:#0000}.waveform-chart__pagination{z-index:2;position:absolute;bottom:6px;right:10px}.waveform-chart__title-area{background:#fff;align-items:center;width:100%;min-width:0;padding:0 24px;display:flex;position:relative;overflow:hidden}.waveform-chart__title-measure{visibility:hidden;pointer-events:none;display:block;position:absolute}.waveform-chart__title-visual{flex:none;min-width:0;position:relative;overflow:visible}.waveform-chart__title-text{transform-origin:50%;line-height:1.2;display:block;position:absolute;top:50%;left:50%;overflow:visible}.waveform-chart__pagination .ant-pagination-item,.waveform-chart__pagination .ant-pagination-prev .ant-pagination-item-link,.waveform-chart__pagination .ant-pagination-next .ant-pagination-item-link{background:#fff;border-color:#d9d9d9}.waveform-chart__pagination .ant-pagination-item-active{border-color:#1677ff}.waveform-chart__grid-slot-placeholder{fill:#fafbfc;stroke:#e4e7ec;stroke-dasharray:4 4;pointer-events:none}.waveform-chart__svg{max-width:100%;display:block}.waveform-chart__overlay{fill:#0000;cursor:crosshair;touch-action:none}.waveform-chart__overlay.is-zoomable{cursor:crosshair}.waveform-chart--panning .waveform-chart__overlay.is-zoomable{cursor:grabbing}.waveform-chart__zoom-selection{fill:#1677ff24;stroke:#1677ff;stroke-width:1px;pointer-events:none}.waveform-chart__overlay.is-annotating{cursor:crosshair}.waveform-chart--presentation .waveform-chart__overlay{cursor:default;pointer-events:none}.waveform-chart__label,.waveform-chart__empty{fill:#595959;font-size:12px}
|
|
2
2
|
/*$vite$:1*/
|
|
@@ -37,11 +37,11 @@ export declare const Y_AXIS_OUTER_PADDING = 4;
|
|
|
37
37
|
/**
|
|
38
38
|
* Y轴标签间距(像素)
|
|
39
39
|
*/
|
|
40
|
-
export declare const Y_AXIS_LABEL_GAP =
|
|
40
|
+
export declare const Y_AXIS_LABEL_GAP = 0;
|
|
41
41
|
/**
|
|
42
42
|
* Y轴标签带宽度(像素)
|
|
43
43
|
*/
|
|
44
|
-
export declare const Y_AXIS_LABEL_BAND_WIDTH =
|
|
44
|
+
export declare const Y_AXIS_LABEL_BAND_WIDTH = 12;
|
|
45
45
|
/**
|
|
46
46
|
* Y轴指数标签间距(像素)
|
|
47
47
|
*/
|
|
@@ -7,6 +7,8 @@ export interface WaveformGridOptions {
|
|
|
7
7
|
columnCount?: number;
|
|
8
8
|
showPagination?: boolean;
|
|
9
9
|
fillIncompleteLastRow?: boolean;
|
|
10
|
+
/** Stable grid slots. Empty IDs remain as empty chart frames and keep their page position. */
|
|
11
|
+
trackOrder?: string[];
|
|
10
12
|
trackLines?: WaveformGridTrackLines;
|
|
11
13
|
}
|
|
12
14
|
export interface WaveformGridLineOptions {
|
|
@@ -29,6 +31,7 @@ export interface NormalizedWaveformGridOptions {
|
|
|
29
31
|
columnCount: number;
|
|
30
32
|
showPagination: boolean;
|
|
31
33
|
fillIncompleteLastRow: boolean;
|
|
34
|
+
trackOrder?: string[];
|
|
32
35
|
trackLines: Record<string, NormalizedWaveformGridLineOptions>;
|
|
33
36
|
}
|
|
34
37
|
export interface GridCellGeometry {
|
|
@@ -58,11 +58,12 @@ export interface HoveredSeriesPoint {
|
|
|
58
58
|
unit?: string;
|
|
59
59
|
color: string;
|
|
60
60
|
trackIndex: number;
|
|
61
|
-
point: WaveformPoint;
|
|
61
|
+
point: WaveformPoint | null;
|
|
62
62
|
}
|
|
63
63
|
export interface WaveformHoverState {
|
|
64
64
|
points: HoveredSeriesPoint[];
|
|
65
65
|
trackIndex: number | null;
|
|
66
|
+
queryX: number | null;
|
|
66
67
|
position: {
|
|
67
68
|
x: number;
|
|
68
69
|
y: number;
|
|
@@ -75,7 +76,8 @@ export interface TrackLayout {
|
|
|
75
76
|
/** Stable track key derived from trackId, or from the series id when trackId is omitted. */
|
|
76
77
|
id: string;
|
|
77
78
|
index: number;
|
|
78
|
-
series
|
|
79
|
+
/** Primary visible series, if this grid slot has waveform data. */
|
|
80
|
+
series: DisplaySeries | null;
|
|
79
81
|
/** Visible series used by rendering and interaction code. */
|
|
80
82
|
seriesList: DisplaySeries[];
|
|
81
83
|
/** Complete series list used by the legend. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const Y_AXIS_CHARACTER_WIDTH = 7;
|
|
2
2
|
export declare const Y_AXIS_TICK_PADDING = 7;
|
|
3
3
|
export declare const Y_AXIS_OUTER_PADDING = 4;
|
|
4
|
-
export declare const Y_AXIS_LABEL_GAP =
|
|
5
|
-
export declare const Y_AXIS_LABEL_BAND_WIDTH =
|
|
4
|
+
export declare const Y_AXIS_LABEL_GAP = 0;
|
|
5
|
+
export declare const Y_AXIS_LABEL_BAND_WIDTH = 12;
|
|
6
6
|
export declare function resolveYAxisSides(axisCount: number): Array<'left' | 'right'>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type ZoomTransform } from 'd3';
|
|
2
2
|
import { type ComputedRef, type Ref, type ShallowRef } from 'vue';
|
|
3
3
|
import type { DisplayTrack, TrackLayout } from '../core/types';
|
|
4
|
-
import type { ResolvedWaveformChartProps, ViewportSelectionState
|
|
4
|
+
import type { ResolvedWaveformChartProps, ViewportSelectionState } from '../core/waveformChartTypes';
|
|
5
|
+
import type { WaveformChartEmit } from '../core/waveformChartTypes';
|
|
5
6
|
import type { AnnotationSeriesCandidate } from '../annotation';
|
|
6
7
|
interface ViewportContext {
|
|
7
8
|
props: ResolvedWaveformChartProps;
|
package/package.json
CHANGED
package/src/App.test.ts
CHANGED
|
@@ -18,23 +18,19 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
|
|
|
18
18
|
? data.series[0].data.points.length
|
|
19
19
|
: 0
|
|
20
20
|
const initialPointCount = pointCount(chart.props('data') as WaveformData)
|
|
21
|
-
|
|
22
21
|
chart.vm.$emit('zoom-end', { start: 0, end: 0.001 })
|
|
23
22
|
await wrapper.get('[aria-label="重置波形视图"]').trigger('click')
|
|
24
23
|
await vi.advanceTimersByTimeAsync(100)
|
|
25
24
|
await flushPromises()
|
|
26
|
-
|
|
27
25
|
expect(pointCount(chart.props('data') as WaveformData)).toBe(initialPointCount)
|
|
28
26
|
} finally {
|
|
29
27
|
wrapper.unmount()
|
|
30
28
|
vi.useRealTimers()
|
|
31
29
|
}
|
|
32
30
|
})
|
|
33
|
-
|
|
34
31
|
it('places controls in the sidebar beside the chart', async () => {
|
|
35
32
|
const wrapper = mount(App)
|
|
36
33
|
await flushPromises()
|
|
37
|
-
|
|
38
34
|
const panel = wrapper.get('#waveform-control-panel')
|
|
39
35
|
const frameControls = panel.get('.frame-style-controls')
|
|
40
36
|
expect(panel.find('h1').exists()).toBe(false)
|
|
@@ -88,7 +84,6 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
|
|
|
88
84
|
expect(legendColorPicker.props('disableAlpha')).toBe(false)
|
|
89
85
|
expect(panel.text()).not.toContain('数据摘要')
|
|
90
86
|
expect(wrapper.get('.chart-panel').find('.waveform-chart').exists()).toBe(true)
|
|
91
|
-
|
|
92
87
|
wrapper.unmount()
|
|
93
88
|
})
|
|
94
89
|
|
|
@@ -262,8 +257,8 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
|
|
|
262
257
|
simulatedSeries.forEach((item) => {
|
|
263
258
|
expect(item.data.kind).toBe('points')
|
|
264
259
|
if (item.data.kind === 'points') {
|
|
265
|
-
expect(item.data.points).toHaveLength(1000)
|
|
266
|
-
expect(item.data.points[0]?.x).
|
|
260
|
+
expect(item.data.points).toHaveLength(item.name === '阶跃响应' ? 500 : 1000)
|
|
261
|
+
expect(item.data.points[0]?.x).toBeCloseTo(item.name === '阶跃响应' ? 5 / 999 : -5, 10)
|
|
267
262
|
}
|
|
268
263
|
})
|
|
269
264
|
|
|
@@ -222,7 +222,7 @@ function handleChartPointerLeave() {
|
|
|
222
222
|
<!-- 轨道渲染 -->
|
|
223
223
|
<WaveformTrack
|
|
224
224
|
v-for="track in trackLayouts"
|
|
225
|
-
:key="`${track.index}-${track.
|
|
225
|
+
:key="`${track.index}-${track.id}`"
|
|
226
226
|
:track="track"
|
|
227
227
|
:clip-path-id="clipPathId"
|
|
228
228
|
:inner-width="innerWidth"
|
|
@@ -277,7 +277,7 @@ function handleChartPointerLeave() {
|
|
|
277
277
|
<g v-if="!isCleanView" class="waveform-chart__legend-layer">
|
|
278
278
|
<g
|
|
279
279
|
v-for="track in trackLayouts"
|
|
280
|
-
:key="`legend-${track.index}-${track.
|
|
280
|
+
:key="`legend-${track.index}-${track.id}`"
|
|
281
281
|
class="waveform-chart__legend-track"
|
|
282
282
|
:data-legend-track-index="track.index"
|
|
283
283
|
:data-legend-track-id="track.id"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// ==================== 布局常量 ====================
|
|
6
6
|
|
|
7
7
|
/** 图表边距 */
|
|
8
|
-
export const margin = { top: 18, right: 24, bottom: 52, left:
|
|
8
|
+
export const margin = { top: 18, right: 24, bottom: 52, left: 80 }
|
|
9
9
|
|
|
10
10
|
/** Distance from the drawing SVG bottom edge to the X-axis title baseline. */
|
|
11
11
|
export const X_AXIS_TITLE_BOTTOM_OFFSET = 12
|
|
@@ -44,12 +44,12 @@ export const Y_AXIS_OUTER_PADDING = 4
|
|
|
44
44
|
/**
|
|
45
45
|
* Y轴标签间距(像素)
|
|
46
46
|
*/
|
|
47
|
-
export const Y_AXIS_LABEL_GAP =
|
|
47
|
+
export const Y_AXIS_LABEL_GAP = 0
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* Y轴标签带宽度(像素)
|
|
51
51
|
*/
|
|
52
|
-
export const Y_AXIS_LABEL_BAND_WIDTH =
|
|
52
|
+
export const Y_AXIS_LABEL_BAND_WIDTH = 12
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Y轴指数标签间距(像素)
|
|
@@ -16,6 +16,7 @@ describe('waveform grid helpers', () => {
|
|
|
16
16
|
columnCount: 1,
|
|
17
17
|
showPagination: true,
|
|
18
18
|
fillIncompleteLastRow: false,
|
|
19
|
+
trackOrder: [],
|
|
19
20
|
trackLines: {},
|
|
20
21
|
})
|
|
21
22
|
expect(normalizeGridOptions({ rowCount: 0, columnCount: 99 })).toEqual({
|
|
@@ -23,10 +24,17 @@ describe('waveform grid helpers', () => {
|
|
|
23
24
|
columnCount: 10,
|
|
24
25
|
showPagination: true,
|
|
25
26
|
fillIncompleteLastRow: false,
|
|
27
|
+
trackOrder: [],
|
|
26
28
|
trackLines: {},
|
|
27
29
|
})
|
|
28
30
|
})
|
|
29
31
|
|
|
32
|
+
it('normalizes track order by trimming, dropping blanks, and removing duplicates', () => {
|
|
33
|
+
expect(
|
|
34
|
+
normalizeGridOptions({ trackOrder: [' voltage ', '', 'current', 'voltage', ' '] }),
|
|
35
|
+
).toMatchObject({ trackOrder: ['voltage', 'current'] })
|
|
36
|
+
})
|
|
37
|
+
|
|
30
38
|
it('normalizes per-track grid line visibility with visible defaults', () => {
|
|
31
39
|
expect(
|
|
32
40
|
normalizeGridOptions({
|
|
@@ -170,7 +178,11 @@ describe('waveform grid helpers', () => {
|
|
|
170
178
|
|
|
171
179
|
it('keeps full pages and the disabled option byte-for-byte compatible', () => {
|
|
172
180
|
const base = normalizeGridOptions({ rowCount: 2, columnCount: 2 })
|
|
173
|
-
const filled = normalizeGridOptions({
|
|
181
|
+
const filled = normalizeGridOptions({
|
|
182
|
+
rowCount: 2,
|
|
183
|
+
columnCount: 2,
|
|
184
|
+
fillIncompleteLastRow: true,
|
|
185
|
+
})
|
|
174
186
|
const fullSlots = [true, true, true, true]
|
|
175
187
|
expect(resolveGridCellGeometry(400, 300, filled, 'independent', fullSlots)).toEqual(
|
|
176
188
|
resolveGridCellGeometry(400, 300, base, 'independent', fullSlots),
|
|
@@ -9,6 +9,8 @@ export interface WaveformGridOptions {
|
|
|
9
9
|
columnCount?: number
|
|
10
10
|
showPagination?: boolean
|
|
11
11
|
fillIncompleteLastRow?: boolean
|
|
12
|
+
/** Stable grid slots. Empty IDs remain as empty chart frames and keep their page position. */
|
|
13
|
+
trackOrder?: string[]
|
|
12
14
|
trackLines?: WaveformGridTrackLines
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -35,6 +37,7 @@ export interface NormalizedWaveformGridOptions {
|
|
|
35
37
|
columnCount: number
|
|
36
38
|
showPagination: boolean
|
|
37
39
|
fillIncompleteLastRow: boolean
|
|
40
|
+
trackOrder?: string[]
|
|
38
41
|
trackLines: Record<string, NormalizedWaveformGridLineOptions>
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -59,6 +62,15 @@ const normalizeCount = (value: unknown, fallback: number) => {
|
|
|
59
62
|
return Math.min(GRID_MAX_COUNT, Math.max(GRID_MIN_COUNT, Math.floor(numeric)))
|
|
60
63
|
}
|
|
61
64
|
|
|
65
|
+
const normalizeTrackOrder = (trackOrder: unknown): string[] => {
|
|
66
|
+
if (!Array.isArray(trackOrder)) return []
|
|
67
|
+
const normalized = trackOrder
|
|
68
|
+
.filter((trackId): trackId is string => typeof trackId === 'string')
|
|
69
|
+
.map((trackId) => trackId.trim())
|
|
70
|
+
.filter(Boolean)
|
|
71
|
+
return Array.from(new Set(normalized))
|
|
72
|
+
}
|
|
73
|
+
|
|
62
74
|
export function normalizeGridOptions(options?: WaveformGridOptions): NormalizedWaveformGridOptions {
|
|
63
75
|
const trackLines = Object.fromEntries(
|
|
64
76
|
Object.entries(options?.trackLines ?? {}).map(([trackId, lines]) => [
|
|
@@ -82,6 +94,7 @@ export function normalizeGridOptions(options?: WaveformGridOptions): NormalizedW
|
|
|
82
94
|
columnCount: normalizeCount(options?.columnCount, 1),
|
|
83
95
|
showPagination: options?.showPagination ?? true,
|
|
84
96
|
fillIncompleteLastRow: options?.fillIncompleteLastRow ?? false,
|
|
97
|
+
trackOrder: normalizeTrackOrder(options?.trackOrder),
|
|
85
98
|
trackLines,
|
|
86
99
|
}
|
|
87
100
|
}
|
|
@@ -148,10 +161,7 @@ export function resolveGridCellGeometry(
|
|
|
148
161
|
}
|
|
149
162
|
const totalVerticalGap = Math.max(0, rowCount - 1) * defaultGap
|
|
150
163
|
const totalAxisBand = axisRows.size * X_AXIS_BAND
|
|
151
|
-
const plotHeight = Math.max(
|
|
152
|
-
1,
|
|
153
|
-
(innerHeight - totalVerticalGap - totalAxisBand) / rowCount,
|
|
154
|
-
)
|
|
164
|
+
const plotHeight = Math.max(1, (innerHeight - totalVerticalGap - totalAxisBand) / rowCount)
|
|
155
165
|
|
|
156
166
|
return Array.from({ length: filledLastRow ? seriesCount : pageSize }, (_, slotIndex) => {
|
|
157
167
|
const row = Math.floor(slotIndex / options.columnCount)
|
|
@@ -253,8 +253,8 @@ describe('multi-value Y-axis grouping', () => {
|
|
|
253
253
|
labelOffset: Math.abs(labelX - x),
|
|
254
254
|
})),
|
|
255
255
|
).toEqual([
|
|
256
|
-
{ side: 'left', labelOffset:
|
|
257
|
-
{ side: 'right', labelOffset:
|
|
256
|
+
{ side: 'left', labelOffset: 55 },
|
|
257
|
+
{ side: 'right', labelOffset: 55 },
|
|
258
258
|
])
|
|
259
259
|
})
|
|
260
260
|
|
|
@@ -262,7 +262,7 @@ describe('multi-value Y-axis grouping', () => {
|
|
|
262
262
|
const [group] = buildYAxisSeriesGroups(track([series('long', -1e120, 1e120)]), 'multi-axis')
|
|
263
263
|
|
|
264
264
|
expect(group).toBeDefined()
|
|
265
|
-
expect(measureYAxisGroupClearance(group!)).toBe(
|
|
265
|
+
expect(measureYAxisGroupClearance(group!)).toBe(72)
|
|
266
266
|
})
|
|
267
267
|
})
|
|
268
268
|
|
|
@@ -17,8 +17,8 @@ export { MAX_MULTI_Y_AXIS_COUNT } from './constants'
|
|
|
17
17
|
const Y_AXIS_CHARACTER_WIDTH = 7
|
|
18
18
|
const Y_AXIS_TICK_PADDING = 7
|
|
19
19
|
const Y_AXIS_OUTER_PADDING = 4
|
|
20
|
-
const Y_AXIS_LABEL_GAP =
|
|
21
|
-
const Y_AXIS_LABEL_BAND_WIDTH =
|
|
20
|
+
const Y_AXIS_LABEL_GAP = 0
|
|
21
|
+
const Y_AXIS_LABEL_BAND_WIDTH = 12
|
|
22
22
|
|
|
23
23
|
export function resolveYAxisTickCount(_plotHeight: number, splitNumber?: number): number {
|
|
24
24
|
if (typeof splitNumber === 'number' && Number.isFinite(splitNumber)) {
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
type NormalizedWaveformGridOptions,
|
|
27
27
|
} from './grid'
|
|
28
28
|
import { axisTextMetrics, resolveYAxisSeriesGroups, resolveYAxisTickCount } from './layout'
|
|
29
|
-
import type {
|
|
29
|
+
import type { DisplayTrack, TrackLayout, WaveformYAxisLayout } from './types'
|
|
30
30
|
import { applyXDomainStrategy } from './xDomain'
|
|
31
31
|
import {
|
|
32
32
|
Y_AXIS_LABEL_BAND_WIDTH,
|
|
@@ -81,33 +81,24 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
|
|
81
81
|
const bottomCells = getBottomRowCellIndexes(visibleCells, options.grid.columnCount)
|
|
82
82
|
|
|
83
83
|
return visibleCells.flatMap((cell, index) => {
|
|
84
|
-
const isEmpty = !cell.series
|
|
85
|
-
if (
|
|
86
|
-
|
|
84
|
+
const isEmpty = !cell.series || cell.series.series.length === 0
|
|
85
|
+
if (!cell.series && (options.displayMode !== 'compact' || !options.showCompactEmptyTracks))
|
|
86
|
+
return []
|
|
87
|
+
const displayTrack: DisplayTrack = cell.series ?? {
|
|
87
88
|
id: `empty-grid-slot-${cell.slotIndex}`,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
lineType: 'linear',
|
|
91
|
-
lineStyle: 'solid',
|
|
92
|
-
pointType: 'none',
|
|
93
|
-
errorBar: { visible: false, width: 1.5, capWidth: 8 },
|
|
94
|
-
points: [],
|
|
89
|
+
series: [],
|
|
90
|
+
visibleSeries: [],
|
|
95
91
|
xDomain: [0, 1],
|
|
96
92
|
yDomain: [0, 1],
|
|
97
|
-
hasErrorPoints: false,
|
|
98
|
-
}
|
|
99
|
-
const displayTrack: DisplayTrack = cell.series ?? {
|
|
100
|
-
id: emptySeries.id,
|
|
101
|
-
series: [emptySeries],
|
|
102
|
-
visibleSeries: [emptySeries],
|
|
103
|
-
xDomain: emptySeries.xDomain,
|
|
104
|
-
yDomain: emptySeries.yDomain,
|
|
105
93
|
}
|
|
106
94
|
const hasVisibleSeries = !isEmpty && displayTrack.visibleSeries.length > 0
|
|
107
|
-
const series = displayTrack.visibleSeries[0] ?? displayTrack.series[0] ??
|
|
95
|
+
const series = displayTrack.visibleSeries[0] ?? displayTrack.series[0] ?? null
|
|
108
96
|
const baseXScale =
|
|
109
97
|
options.displayMode === 'independent'
|
|
110
|
-
? scaleLinear(
|
|
98
|
+
? scaleLinear(
|
|
99
|
+
resolveIndependentXDomain(displayTrack, series?.id ?? displayTrack.id, options),
|
|
100
|
+
[0, cell.width],
|
|
101
|
+
)
|
|
111
102
|
: scaleLinear(options.sharedZoomDomain, [0, cell.width])
|
|
112
103
|
const transform =
|
|
113
104
|
options.displayMode === 'independent'
|
|
@@ -162,7 +153,7 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
|
|
162
153
|
labelX,
|
|
163
154
|
scale,
|
|
164
155
|
majorTicks,
|
|
165
|
-
minorTicks: buildMinorTicks(majorTicks),
|
|
156
|
+
minorTicks: buildMinorTicks(majorTicks, 2),
|
|
166
157
|
tickValues,
|
|
167
158
|
seriesList: group.seriesList,
|
|
168
159
|
}
|
|
@@ -248,7 +239,7 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
|
|
248
239
|
yScale,
|
|
249
240
|
yAxes,
|
|
250
241
|
xMajorTicks,
|
|
251
|
-
xMinorTicks: buildMinorTicks(xMajorTicks,
|
|
242
|
+
xMinorTicks: buildMinorTicks(xMajorTicks, 3, domain),
|
|
252
243
|
yMajorTicks,
|
|
253
244
|
yMinorTicks: yAxes[0]?.minorTicks ?? [],
|
|
254
245
|
yAxisTickValues,
|
|
@@ -69,12 +69,13 @@ export interface HoveredSeriesPoint {
|
|
|
69
69
|
unit?: string
|
|
70
70
|
color: string
|
|
71
71
|
trackIndex: number
|
|
72
|
-
point: WaveformPoint
|
|
72
|
+
point: WaveformPoint | null
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
export interface WaveformHoverState {
|
|
76
76
|
points: HoveredSeriesPoint[]
|
|
77
77
|
trackIndex: number | null
|
|
78
|
+
queryX: number | null
|
|
78
79
|
position: { x: number; y: number }
|
|
79
80
|
}
|
|
80
81
|
|
|
@@ -85,7 +86,8 @@ export interface TrackLayout {
|
|
|
85
86
|
/** Stable track key derived from trackId, or from the series id when trackId is omitted. */
|
|
86
87
|
id: string
|
|
87
88
|
index: number
|
|
88
|
-
series
|
|
89
|
+
/** Primary visible series, if this grid slot has waveform data. */
|
|
90
|
+
series: DisplaySeries | null
|
|
89
91
|
/** Visible series used by rendering and interaction code. */
|
|
90
92
|
seriesList: DisplaySeries[]
|
|
91
93
|
/** Complete series list used by the legend. */
|
|
@@ -75,6 +75,7 @@ export function useWaveformLayout(context: LayoutContext) {
|
|
|
75
75
|
(index === 0 ? props.lineColor : channelColors[index % channelColors.length]),
|
|
76
76
|
})),
|
|
77
77
|
)
|
|
78
|
+
const gridOptions = computed(() => normalizeGridOptions(props.grid))
|
|
78
79
|
const chartTracks = computed<DisplayTrack[]>(() => {
|
|
79
80
|
const groupedSeries = new Map<string, DisplaySeries[]>()
|
|
80
81
|
chartSeries.value.forEach((series) => {
|
|
@@ -83,7 +84,13 @@ export function useWaveformLayout(context: LayoutContext) {
|
|
|
83
84
|
if (trackSeries) trackSeries.push(series)
|
|
84
85
|
else groupedSeries.set(trackId, [series])
|
|
85
86
|
})
|
|
86
|
-
|
|
87
|
+
const trackOrder = gridOptions.value.trackOrder ?? []
|
|
88
|
+
const orderedTrackIds = [
|
|
89
|
+
...trackOrder,
|
|
90
|
+
...Array.from(groupedSeries.keys()).filter((trackId) => !trackOrder.includes(trackId)),
|
|
91
|
+
]
|
|
92
|
+
return orderedTrackIds.map((id) => {
|
|
93
|
+
const series = groupedSeries.get(id) ?? []
|
|
87
94
|
const visibleSeries = series.filter((item) => !hiddenSeriesIdSet.value.has(item.id))
|
|
88
95
|
const xDomainValues: number[] = []
|
|
89
96
|
const yDomainValues: number[] = []
|
|
@@ -95,12 +102,11 @@ export function useWaveformLayout(context: LayoutContext) {
|
|
|
95
102
|
id,
|
|
96
103
|
series,
|
|
97
104
|
visibleSeries,
|
|
98
|
-
xDomain: paddedDomain(xDomainValues),
|
|
99
|
-
yDomain: paddedDomain(yDomainValues),
|
|
105
|
+
xDomain: xDomainValues.length ? paddedDomain(xDomainValues) : [0, 1],
|
|
106
|
+
yDomain: yDomainValues.length ? paddedDomain(yDomainValues) : [0, 1],
|
|
100
107
|
}
|
|
101
108
|
})
|
|
102
109
|
})
|
|
103
|
-
const gridOptions = computed(() => normalizeGridOptions(props.grid))
|
|
104
110
|
const renderingOptions = computed(() => resolveWaveformRenderingOptions(props.rendering))
|
|
105
111
|
const pageCount = computed(() => getPageCount(chartTracks.value.length, gridOptions.value))
|
|
106
112
|
const pagedTracks = computed(() =>
|
|
@@ -245,8 +251,8 @@ export function useWaveformLayout(context: LayoutContext) {
|
|
|
245
251
|
})
|
|
246
252
|
const resolveInitialTrackDomain = (track: TrackLayout): [number, number] => {
|
|
247
253
|
const configuredDomain =
|
|
248
|
-
props.initialXDomains?.[track.series
|
|
249
|
-
props.initialXDomains?.[track.series.id] ??
|
|
254
|
+
props.initialXDomains?.[track.series?.trackId ?? track.series?.id ?? track.id] ??
|
|
255
|
+
(track.series ? props.initialXDomains?.[track.series.id] : undefined) ??
|
|
250
256
|
props.initialXDomain
|
|
251
257
|
if (
|
|
252
258
|
configuredDomain &&
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const Y_AXIS_CHARACTER_WIDTH = 7
|
|
2
2
|
export const Y_AXIS_TICK_PADDING = 7
|
|
3
3
|
export const Y_AXIS_OUTER_PADDING = 4
|
|
4
|
-
export const Y_AXIS_LABEL_GAP =
|
|
5
|
-
export const Y_AXIS_LABEL_BAND_WIDTH =
|
|
4
|
+
export const Y_AXIS_LABEL_GAP = 0
|
|
5
|
+
export const Y_AXIS_LABEL_BAND_WIDTH = 12
|
|
6
6
|
|
|
7
7
|
export function resolveYAxisSides(axisCount: number): Array<'left' | 'right'> {
|
|
8
8
|
if (axisCount >= 4) return ['left', 'left', 'right', 'right']
|
|
@@ -12,7 +12,9 @@ const props = defineProps<{
|
|
|
12
12
|
containerHeight: number
|
|
13
13
|
}>()
|
|
14
14
|
|
|
15
|
-
const hoveredPoint = computed(
|
|
15
|
+
const hoveredPoint = computed(
|
|
16
|
+
() => props.state.points.find((seriesPoint) => seriesPoint.point)?.point ?? null,
|
|
17
|
+
)
|
|
16
18
|
</script>
|
|
17
19
|
|
|
18
20
|
<template>
|
|
@@ -170,4 +170,27 @@ describe('WaveformTooltip', () => {
|
|
|
170
170
|
|
|
171
171
|
expect(wrapper.get('.waveform-tooltip__series-label').text()).toBe('未配置炮号: 温度')
|
|
172
172
|
})
|
|
173
|
+
|
|
174
|
+
it('uses each series sample time and marks unavailable series without synthesizing values', () => {
|
|
175
|
+
const wrapper = mount(WaveformTooltip, {
|
|
176
|
+
props: {
|
|
177
|
+
visible: true,
|
|
178
|
+
position: { x: 10, y: 10 },
|
|
179
|
+
timeUnit: 's',
|
|
180
|
+
hoveredPoint: { x: 1, y: 12 },
|
|
181
|
+
seriesPoints: [
|
|
182
|
+
{ trackIndex: 0, name: '完整数据', color: '#f00', point: { x: 1, y: 12 } },
|
|
183
|
+
{ trackIndex: 1, name: '异步采样', color: '#0a0', point: { x: 1.25, y: 8 } },
|
|
184
|
+
{ trackIndex: 2, name: '缺失数据', color: '#00f', point: null },
|
|
185
|
+
],
|
|
186
|
+
containerWidth: 400,
|
|
187
|
+
containerHeight: 300,
|
|
188
|
+
},
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
const values = wrapper
|
|
192
|
+
.findAll('.waveform-tooltip__value')
|
|
193
|
+
.map((value) => value.text())
|
|
194
|
+
expect(values).toEqual(['(x:1 y:12)', '(x:1.25 y:8)', '无数据'])
|
|
195
|
+
})
|
|
173
196
|
})
|
|
@@ -9,7 +9,7 @@ interface SeriesPoint {
|
|
|
9
9
|
shotNo?: string
|
|
10
10
|
color: string
|
|
11
11
|
unit?: string
|
|
12
|
-
point: WaveformPoint
|
|
12
|
+
point: WaveformPoint | null
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
interface Props {
|
|
@@ -45,10 +45,12 @@ function estimateLineCount(text: string, width: number): number {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function formatSeriesText(seriesPoint: SeriesPoint): string {
|
|
48
|
-
const
|
|
48
|
+
const valueText = seriesPoint.point
|
|
49
|
+
? `(x:${formatTooltipTime(seriesPoint.point.x, props.timeUnit)} y:${formatTooltipNumber(seriesPoint.point.y)})`
|
|
50
|
+
: '无数据'
|
|
49
51
|
return `${seriesPoint.shotNo?.trim() || '未配置炮号'}: ${seriesPoint.name}${
|
|
50
52
|
seriesPoint.unit ? `(${seriesPoint.unit})` : ''
|
|
51
|
-
}
|
|
53
|
+
} ${valueText}`
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
function estimateTooltipHeight(width: number): number {
|
|
@@ -114,9 +116,10 @@ const tooltipStyle = computed(() => {
|
|
|
114
116
|
<span class="waveform-tooltip__series-label">{{ seriesPoint.shotNo?.trim() || '未配置炮号' }}: {{
|
|
115
117
|
seriesPoint.name
|
|
116
118
|
}}<template v-if="seriesPoint.unit">({{ seriesPoint.unit }})</template></span>
|
|
117
|
-
<span class="waveform-tooltip__value">(x:{{
|
|
118
|
-
|
|
119
|
-
}})</span>
|
|
119
|
+
<span v-if="seriesPoint.point" class="waveform-tooltip__value">(x:{{
|
|
120
|
+
formatTooltipTime(seriesPoint.point.x, timeUnit)
|
|
121
|
+
}} y:{{ formatTooltipNumber(seriesPoint.point.y) }})</span>
|
|
122
|
+
<span v-else class="waveform-tooltip__value waveform-tooltip__value--missing">无数据</span>
|
|
120
123
|
</span>
|
|
121
124
|
</span>
|
|
122
125
|
</div>
|