waveform-analysis 0.1.42 → 0.1.46
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 +163 -161
- package/dist/style.css +1 -1
- package/dist/types/components/core/constants.d.ts +2 -2
- package/dist/types/components/core/types.d.ts +2 -1
- package/dist/types/components/core/yAxisConstants.d.ts +2 -2
- package/dist/types/components/interaction/WaveformTooltip.vue.d.ts +1 -1
- package/package.json +1 -1
- package/src/App.test.ts +2 -7
- package/src/components/WaveformChart.css +1 -1
- package/src/components/core/constants.ts +3 -3
- package/src/components/core/layout.test.ts +3 -3
- package/src/components/core/layout.ts +2 -2
- package/src/components/core/trackLayoutBuilder.ts +3 -5
- package/src/components/core/types.ts +2 -1
- package/src/components/core/useWaveformChartController.ts +1 -0
- 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 +19 -14
- package/src/components/rendering/WaveformHoverLayer.vue +5 -11
- package/src/components/rendering/WaveformTrackAxes.vue +5 -4
- package/src/components/waveformChartCases/compactAndSharedHover.test.ts +43 -0
- 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 +1 -1
- 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
|
*/
|
|
@@ -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;
|
|
@@ -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'>;
|
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
|
|
|
@@ -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轴指数标签间距(像素)
|
|
@@ -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)) {
|
|
@@ -135,9 +135,7 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
|
|
135
135
|
(_, index) => axisStart + ((axisEnd - axisStart) * index) / (tickCount - 1),
|
|
136
136
|
)
|
|
137
137
|
const showAxisEnd = options.displayMode !== 'compact' || cell.row === 0
|
|
138
|
-
const visibleMajorTicks = showAxisEnd
|
|
139
|
-
? majorTicks
|
|
140
|
-
: majorTicks.filter((tick) => tick !== axisEnd)
|
|
138
|
+
const visibleMajorTicks = showAxisEnd ? majorTicks : majorTicks.slice(0, -1)
|
|
141
139
|
const tickValues = visibleMajorTicks
|
|
142
140
|
const { tickTextWidth } = axisTextMetrics(
|
|
143
141
|
scale.domain() as [number, number],
|
|
@@ -164,7 +162,7 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
|
|
164
162
|
labelX,
|
|
165
163
|
scale,
|
|
166
164
|
majorTicks,
|
|
167
|
-
minorTicks: buildMinorTicks(majorTicks),
|
|
165
|
+
minorTicks: buildMinorTicks(majorTicks, 2),
|
|
168
166
|
tickValues,
|
|
169
167
|
seriesList: group.seriesList,
|
|
170
168
|
}
|
|
@@ -250,7 +248,7 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
|
|
250
248
|
yScale,
|
|
251
249
|
yAxes,
|
|
252
250
|
xMajorTicks,
|
|
253
|
-
xMinorTicks: buildMinorTicks(xMajorTicks,
|
|
251
|
+
xMinorTicks: buildMinorTicks(xMajorTicks, 3, domain),
|
|
254
252
|
yMajorTicks,
|
|
255
253
|
yMinorTicks: yAxes[0]?.minorTicks ?? [],
|
|
256
254
|
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
|
|
|
@@ -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>
|
|
@@ -62,18 +62,21 @@ export function useWaveformHover(context: HoverContext) {
|
|
|
62
62
|
const commitHover = (
|
|
63
63
|
nextPoints: HoveredSeriesPoint[],
|
|
64
64
|
trackIndex: number | null,
|
|
65
|
+
queryX: number,
|
|
65
66
|
position: { x: number; y: number },
|
|
66
67
|
) => {
|
|
67
68
|
if (isPresentationMode.value) return
|
|
68
69
|
if (!hoveredPointsMatch(nextPoints)) hoverState.points = nextPoints
|
|
69
70
|
hoverState.trackIndex = trackIndex
|
|
71
|
+
hoverState.queryX = queryX
|
|
70
72
|
hoverState.position = position
|
|
71
|
-
emit('point-hover', hoverState.points
|
|
73
|
+
emit('point-hover', hoverState.points.find((point) => point.point)?.point ?? null)
|
|
72
74
|
}
|
|
73
75
|
const clearHover = () => {
|
|
74
76
|
cancelPendingHover()
|
|
75
77
|
hoverState.points = []
|
|
76
78
|
hoverState.trackIndex = null
|
|
79
|
+
hoverState.queryX = null
|
|
77
80
|
emit('point-hover', null)
|
|
78
81
|
}
|
|
79
82
|
const handlePointerLeave = () => {
|
|
@@ -82,7 +85,7 @@ export function useWaveformHover(context: HoverContext) {
|
|
|
82
85
|
const createHoveredSeriesPoint = (
|
|
83
86
|
series: DisplaySeries,
|
|
84
87
|
trackIndex: number,
|
|
85
|
-
point: WaveformPoint,
|
|
88
|
+
point: WaveformPoint | null,
|
|
86
89
|
): HoveredSeriesPoint => ({
|
|
87
90
|
id: series.id,
|
|
88
91
|
shotNo: series.shotNo,
|
|
@@ -106,8 +109,12 @@ export function useWaveformHover(context: HoverContext) {
|
|
|
106
109
|
clearHover()
|
|
107
110
|
return true
|
|
108
111
|
}
|
|
109
|
-
const nearestPoint = (series: DisplaySeries, xValue: number): WaveformPoint | undefined =>
|
|
110
|
-
series.points[
|
|
112
|
+
const nearestPoint = (series: DisplaySeries, xValue: number): WaveformPoint | undefined => {
|
|
113
|
+
const firstPoint = series.points[0]
|
|
114
|
+
const lastPoint = series.points.at(-1)
|
|
115
|
+
if (!firstPoint || !lastPoint || xValue < firstPoint.x || xValue > lastPoint.x) return undefined
|
|
116
|
+
return series.points[pointBisector.center(series.points, xValue)]
|
|
117
|
+
}
|
|
111
118
|
|
|
112
119
|
const handleIndependentPointerMove = (event: PointerEvent, trackIndex: number) => {
|
|
113
120
|
if (isPresentationMode.value) return
|
|
@@ -125,11 +132,10 @@ export function useWaveformHover(context: HoverContext) {
|
|
|
125
132
|
const currentTrack = trackLayouts.value[trackIndex]
|
|
126
133
|
if (!currentTrack || !currentTrack.hasVisibleSeries || currentTrack !== track) return
|
|
127
134
|
const xValue = track.xScale.invert(Math.max(0, Math.min(innerWidth.value, pointerX)))
|
|
128
|
-
const nextPoints = track.seriesList.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
commitHover(nextPoints, trackIndex, {
|
|
135
|
+
const nextPoints = track.seriesList.map((series) =>
|
|
136
|
+
createHoveredSeriesPoint(series, trackIndex, nearestPoint(series, xValue) ?? null),
|
|
137
|
+
)
|
|
138
|
+
commitHover(nextPoints, trackIndex, xValue, {
|
|
133
139
|
x: resolvedChartLeftMargin.value + track.left + pointerX,
|
|
134
140
|
y: titleAreaHeight.value + chartTopMargin.value + track.top + pointerY,
|
|
135
141
|
})
|
|
@@ -155,12 +161,11 @@ export function useWaveformHover(context: HoverContext) {
|
|
|
155
161
|
)
|
|
156
162
|
const xValue = referenceTrack.xScale.invert(localPointerX)
|
|
157
163
|
const nextPoints = trackLayouts.value.flatMap((track) =>
|
|
158
|
-
track.seriesList.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}),
|
|
164
|
+
track.seriesList.map((series) =>
|
|
165
|
+
createHoveredSeriesPoint(series, track.index, nearestPoint(series, xValue) ?? null),
|
|
166
|
+
),
|
|
162
167
|
)
|
|
163
|
-
commitHover(nextPoints, null, {
|
|
168
|
+
commitHover(nextPoints, null, xValue, {
|
|
164
169
|
x: resolvedChartLeftMargin.value + pointerX,
|
|
165
170
|
y: titleAreaHeight.value + chartTopMargin.value + pointerY,
|
|
166
171
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type { TrackLayout, WaveformHoverState } from '../core/types'
|
|
5
5
|
|
|
6
6
|
const props = defineProps<{
|
|
7
7
|
state: WaveformHoverState
|
|
@@ -11,23 +11,17 @@ const props = defineProps<{
|
|
|
11
11
|
}>()
|
|
12
12
|
|
|
13
13
|
interface Crosshair {
|
|
14
|
-
point: HoveredSeriesPoint
|
|
15
14
|
track: TrackLayout
|
|
16
15
|
x: number
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
const crosshairs = computed<Crosshair[]>(() => {
|
|
20
|
-
if (!props.visible) return []
|
|
21
|
-
|
|
22
|
-
const pointByTrack = new Map<number, HoveredSeriesPoint>()
|
|
23
|
-
props.state.points.forEach((point) => {
|
|
24
|
-
if (!pointByTrack.has(point.trackIndex)) pointByTrack.set(point.trackIndex, point)
|
|
25
|
-
})
|
|
19
|
+
if (!props.visible || props.state.queryX === null) return []
|
|
26
20
|
|
|
27
21
|
return props.tracks.flatMap((track) => {
|
|
28
|
-
const
|
|
29
|
-
return
|
|
30
|
-
? [{
|
|
22
|
+
const trackIsActive = props.state.trackIndex === null || props.state.trackIndex === track.index
|
|
23
|
+
return trackIsActive && !track.isEmpty && track.hasVisibleSeries
|
|
24
|
+
? [{ track, x: track.xScale(props.state.queryX!) }]
|
|
31
25
|
: []
|
|
32
26
|
})
|
|
33
27
|
})
|
|
@@ -5,6 +5,7 @@ import { nextTick, onMounted, ref, watch } from 'vue'
|
|
|
5
5
|
import type { WaveformAxesOptions } from '../../types'
|
|
6
6
|
import { formatScientificAxisLabel, formatXAxisLabel } from '../../utils'
|
|
7
7
|
import type { DisplaySeries, TrackLayout, WaveformYAxisLayout } from '../core/types'
|
|
8
|
+
import { Y_AXIS_LABEL_BAND_WIDTH } from '../core/yAxisConstants'
|
|
8
9
|
|
|
9
10
|
interface Props {
|
|
10
11
|
track: TrackLayout
|
|
@@ -169,9 +170,9 @@ watch(
|
|
|
169
170
|
>
|
|
170
171
|
<rect
|
|
171
172
|
class="waveform-track__y-axis-label-bg waveform-chart__y-axis-label-bg"
|
|
172
|
-
:x="track.yAxisLabelX -
|
|
173
|
+
:x="track.yAxisLabelX - Y_AXIS_LABEL_BAND_WIDTH / 2"
|
|
173
174
|
:y="track.height / 2 - 40"
|
|
174
|
-
width="
|
|
175
|
+
:width="Y_AXIS_LABEL_BAND_WIDTH"
|
|
175
176
|
height="80"
|
|
176
177
|
rx="2"
|
|
177
178
|
/>
|
|
@@ -194,9 +195,9 @@ watch(
|
|
|
194
195
|
>
|
|
195
196
|
<rect
|
|
196
197
|
class="waveform-track__y-axis-label-bg waveform-chart__y-axis-label-bg"
|
|
197
|
-
:x="axis.labelX -
|
|
198
|
+
:x="axis.labelX - Y_AXIS_LABEL_BAND_WIDTH / 2"
|
|
198
199
|
:y="track.height / 2 - 40"
|
|
199
|
-
width="
|
|
200
|
+
:width="Y_AXIS_LABEL_BAND_WIDTH"
|
|
200
201
|
height="80"
|
|
201
202
|
rx="2"
|
|
202
203
|
/>
|
|
@@ -72,6 +72,49 @@ describe('WaveformChart', () => {
|
|
|
72
72
|
expect(endTicks[3]).toHaveLength(0)
|
|
73
73
|
})
|
|
74
74
|
|
|
75
|
+
it('removes the later compact row top tick when the Y-axis end has a floating-point tail', async () => {
|
|
76
|
+
const yDomain: [number, number] = [-1, 0.2]
|
|
77
|
+
const calculatedAxisEnd =
|
|
78
|
+
yDomain[0] + ((yDomain[1] - yDomain[0]) * 4) / 4
|
|
79
|
+
expect(calculatedAxisEnd).not.toBe(yDomain[1])
|
|
80
|
+
|
|
81
|
+
const wrapper = await mountSizedChart(
|
|
82
|
+
{
|
|
83
|
+
kind: 'series',
|
|
84
|
+
series: [
|
|
85
|
+
{
|
|
86
|
+
name: 'top',
|
|
87
|
+
data: { kind: 'points', points: [{ x: 0, y: -0.8 }, { x: 1, y: 0.1 }] },
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'bottom',
|
|
91
|
+
data: { kind: 'points', points: [{ x: 0, y: -0.7 }, { x: 1, y: 0 }] },
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
{ displayMode: 'compact', grid: { rowCount: 2, columnCount: 1 }, yDomain },
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
const [topTrack, bottomTrack] = wrapper.findAll('.waveform-chart__track')
|
|
99
|
+
const ticksAt = (track: typeof topTrack, position: number) =>
|
|
100
|
+
track.findAll('.waveform-chart__axis--y .tick').filter((tick) => {
|
|
101
|
+
const match = tick.attributes('transform')?.match(/translate\(0,\s*([\d.]+)\)/)
|
|
102
|
+
return match ? Math.abs(Number(match[1]) - position) <= 1 : false
|
|
103
|
+
})
|
|
104
|
+
const topTrackHeight = Number(topTrack.attributes('data-track-height'))
|
|
105
|
+
|
|
106
|
+
expect(ticksAt(topTrack, 0)).toHaveLength(1)
|
|
107
|
+
expect(ticksAt(topTrack, 0)[0].text()).toBe('0.2')
|
|
108
|
+
expect(ticksAt(bottomTrack, 0)).toHaveLength(0)
|
|
109
|
+
expect(Number(bottomTrack.attributes('data-track-top'))).toBeCloseTo(
|
|
110
|
+
Number(topTrack.attributes('data-track-top')) + topTrackHeight,
|
|
111
|
+
6,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const boundaryLabels = [...ticksAt(topTrack, topTrackHeight), ...ticksAt(bottomTrack, 0)]
|
|
115
|
+
expect(boundaryLabels).toHaveLength(1)
|
|
116
|
+
})
|
|
117
|
+
|
|
75
118
|
it('prefixes one shared exponent to the largest visible tick on every compact Y axis', async () => {
|
|
76
119
|
const wrapper = await mountSizedChart(
|
|
77
120
|
{
|
|
@@ -170,7 +170,7 @@ describe('WaveformChart', () => {
|
|
|
170
170
|
)
|
|
171
171
|
expect(labelX).toBeLessThan(-46)
|
|
172
172
|
expect(Number(wrapper.get('.waveform-chart__y-axis-label-bg').attributes('x'))).toBe(
|
|
173
|
-
labelX -
|
|
173
|
+
labelX - 6,
|
|
174
174
|
)
|
|
175
175
|
expect(yAxisLabels.every((label) => !label.text().includes('(T)'))).toBe(true)
|
|
176
176
|
expect(wrapper.findAll('.waveform-chart__track-label')).toHaveLength(0)
|
|
@@ -22,6 +22,16 @@ describe('WaveformChart', () => {
|
|
|
22
22
|
|
|
23
23
|
expect(wrapper.findAll('.waveform-chart__grid--major line').length).toBeGreaterThan(0)
|
|
24
24
|
expect(wrapper.findAll('.waveform-chart__grid--minor line').length).toBeGreaterThan(0)
|
|
25
|
+
expect(
|
|
26
|
+
wrapper.findAll('.waveform-chart__grid--major [data-grid-direction="horizontal"]'),
|
|
27
|
+
).toHaveLength(5)
|
|
28
|
+
expect(
|
|
29
|
+
wrapper.findAll('.waveform-chart__grid--minor [data-grid-direction="horizontal"]'),
|
|
30
|
+
).toHaveLength(4)
|
|
31
|
+
expect(
|
|
32
|
+
wrapper.findAll('.waveform-chart__grid--minor [data-grid-direction="vertical"]'),
|
|
33
|
+
).not.toHaveLength(0)
|
|
34
|
+
expect(getComputedStyle(wrapper.element).borderTopWidth).toBe('1.2px')
|
|
25
35
|
expect(wrapper.find('.waveform-chart__plot-frame').exists()).toBe(true)
|
|
26
36
|
expect(wrapper.get('.waveform-chart__plot-frame').attributes()).toMatchObject({
|
|
27
37
|
fill: 'none',
|