waveform-analysis 0.1.32 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +28 -3
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +426 -411
  4. package/dist/style.css +1 -1
  5. package/dist/types/components/WaveformChart.vue.d.ts +1 -0
  6. package/dist/types/components/core/trackLayoutBuilder.d.ts +2 -1
  7. package/dist/types/components/core/types.d.ts +2 -0
  8. package/dist/types/components/core/useWaveformChartController.d.ts +1 -0
  9. package/dist/types/components/core/waveformChartTypes.d.ts +3 -2
  10. package/dist/types/components/core/xDomain.d.ts +3 -0
  11. package/dist/types/components/data/types.d.ts +1 -1
  12. package/dist/types/components/interaction/WaveformTooltip.vue.d.ts +1 -0
  13. package/dist/types/components/waveform.d.ts +1 -1
  14. package/dist/types/index.d.ts +1 -1
  15. package/dist/types/types/chart.d.ts +10 -0
  16. package/dist/types/types/data.d.ts +3 -0
  17. package/dist/types/types/index.d.ts +1 -1
  18. package/package.json +1 -1
  19. package/src/components/WaveformChart.vue +1 -0
  20. package/src/components/core/trackLayoutBuilder.ts +19 -7
  21. package/src/components/core/types.ts +2 -0
  22. package/src/components/core/useWaveformChartLifecycle.ts +1 -0
  23. package/src/components/core/useWaveformLayout.ts +19 -6
  24. package/src/components/core/waveformChartTypes.ts +3 -0
  25. package/src/components/core/xDomain.test.ts +28 -0
  26. package/src/components/core/xDomain.ts +25 -0
  27. package/src/components/data/types.ts +1 -0
  28. package/src/components/interaction/WaveformTooltip.test.ts +17 -9
  29. package/src/components/interaction/WaveformTooltip.vue +41 -45
  30. package/src/components/interaction/useWaveformHover.ts +1 -0
  31. package/src/components/waveform.ts +1 -0
  32. package/src/components/waveformChartCases/compactAndSharedHover.test.ts +4 -4
  33. package/src/components/waveformChartCases/dataAndHoverPerformance.test.ts +2 -2
  34. package/src/components/waveformChartCases/viewportReset.test.ts +35 -0
  35. package/src/components/waveformChartCases/xDomainStrategy.test.ts +96 -0
  36. package/src/core/data.test.ts +22 -0
  37. package/src/core/data.ts +1 -0
  38. package/src/data/simulatedWaveforms.test.ts +1 -0
  39. package/src/data/simulatedWaveforms.ts +7 -1
  40. package/src/index.ts +1 -0
  41. package/src/types/chart.ts +11 -0
  42. package/src/types/data.ts +3 -0
  43. package/src/types/index.ts +1 -0
  44. package/src/views/FixedYDomainDemo.vue +2 -0
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[data-v-52818b45]{z-index:40;background:#10182852;place-items:center;padding:20px;animation:.16s ease-out waveform-annotation-editor-fade-in-52818b45;display:grid;position:absolute;inset:0}.waveform-annotation-editor__panel[data-v-52818b45]{color:#344054;background:#fff;border:1px solid #e4e7ec;border-radius:12px;gap:20px;width:min(440px,100%);max-height:100%;padding:22px;font-size:13px;animation:.18s ease-out waveform-annotation-editor-rise-in-52818b45;display:grid;overflow-y:auto;box-shadow:0 20px 50px #10182838}.waveform-annotation-editor__header[data-v-52818b45]{justify-content:space-between;align-items:flex-start;gap:16px;display:flex}.waveform-annotation-editor__header h2[data-v-52818b45]{color:#101828;margin:0;font-size:18px;line-height:1.3}.waveform-annotation-editor__header p[data-v-52818b45]{color:#667085;margin:5px 0 0;font-size:12px}.waveform-annotation-editor__close[data-v-52818b45]{color:#667085;cursor:pointer;background:0 0;border:0;border-radius:6px;flex:0 0 30px;place-items:center;width:30px;height:30px;padding:0;font-size:22px;line-height:1;display:inline-grid}.waveform-annotation-editor__close[data-v-52818b45]:hover{color:#101828;background:#f2f4f7}.waveform-annotation-editor__coordinates[data-v-52818b45]{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;display:grid}.waveform-annotation-editor__coordinates span[data-v-52818b45],.waveform-annotation-editor__series[data-v-52818b45]{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-52818b45]{color:#667085}.waveform-annotation-editor__coordinates b[data-v-52818b45]{color:#1677ff;font-size:11px}.waveform-annotation-editor__coordinates code[data-v-52818b45]{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-52818b45]{gap:10px}.waveform-annotation-editor__series i[data-v-52818b45]{border:1px solid #0000001f;border-radius:2px;flex:0 0 12px;width:12px;height:12px}.waveform-annotation-editor__series-label[data-v-52818b45]{gap:1px;min-width:0;display:grid}.waveform-annotation-editor__series select[data-v-52818b45]{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-52818b45]{color:#667085;font-size:10px}.waveform-annotation-editor__series b[data-v-52818b45]{color:#344054;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.waveform-annotation-editor__series code[data-v-52818b45]{color:#667085;margin-left:auto;font-size:11px}.waveform-annotation-editor__field[data-v-52818b45],.waveform-annotation-editor__color-field[data-v-52818b45]{gap:4px;display:grid}.waveform-annotation-editor__label-row[data-v-52818b45]{justify-content:space-between;align-items:center;gap:8px;display:flex}.waveform-annotation-editor__label-row b[data-v-52818b45],.waveform-annotation-editor__colors legend[data-v-52818b45]{color:#344054;font-size:12px;font-weight:600}.waveform-annotation-editor__label-row small[data-v-52818b45]{color:#98a2b3;font-size:11px}.waveform-annotation-editor textarea[data-v-52818b45]{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-52818b45]:focus{border-color:#1677ff;outline:3px solid #1677ff24}.waveform-annotation-editor__colors[data-v-52818b45]{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-52818b45]{grid-column:1/-1;padding:0}.waveform-annotation-editor__color-field[data-v-52818b45]{color:#667085;text-align:left;place-items:stretch stretch;min-width:0;font-size:11px}.waveform-annotation-editor__color-field[data-v-52818b45] .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-52818b45] .vc-color-wrap:hover{border-color:#98a2b3;box-shadow:0 0 0 3px #1677ff1f}.waveform-annotation-editor__actions[data-v-52818b45]{border-top:1px solid #eaecf0;justify-content:flex-end;gap:8px;padding-top:8px;display:flex}.waveform-annotation-editor__actions button[data-v-52818b45]{color:#475467;cursor:pointer;background:#fff;border:1px solid #d0d5dd;border-radius:6px;height:34px;padding:0 12px}.waveform-annotation-editor__actions button.is-primary[data-v-52818b45]{color:#fff;background:#1677ff;border-color:#1677ff}.waveform-annotation-editor__actions button[data-v-52818b45]:disabled{opacity:.45;cursor:not-allowed}@keyframes waveform-annotation-editor-fade-in-52818b45{0%{opacity:0}to{opacity:1}}@keyframes waveform-annotation-editor-rise-in-52818b45{0%{opacity:0;transform:translateY(8px)scale(.98)}to{opacity:1;transform:translateY(0)scale(1)}}@media (width<=420px){.waveform-annotation-editor[data-v-52818b45]{padding:12px}.waveform-annotation-editor__panel[data-v-52818b45]{gap:16px;padding:18px}.waveform-annotation-editor__coordinates[data-v-52818b45]{grid-template-columns:1fr}}.waveform-tooltip[data-v-93daa641]{box-sizing:border-box;z-index:2;color:#333;pointer-events:none;background:#fff;border:1px solid #d9d9d9;border-radius:2px;gap:3px;width:max-content;max-width:min(320px,100% - 16px);padding:8px 10px;font:12px/1.45 ui-monospace,SFMono-Regular,Consolas,monospace;display:grid;position:absolute;box-shadow:0 4px 12px #1018281f}.waveform-tooltip__time[data-v-93daa641]{border-bottom:1px solid #f0f0f0;padding-bottom:4px}.waveform-tooltip__series[data-v-93daa641]{grid-template-columns:8px minmax(0,1fr);align-items:start;gap:6px;display:grid}.waveform-tooltip__series i[data-v-93daa641]{border-radius:50%;flex:none;width:8px;height:8px;margin-top:4px}.waveform-tooltip__series-content[data-v-93daa641]{overflow-wrap:anywhere;white-space:normal;min-width:0}.waveform-tooltip__series strong[data-v-93daa641]{font-weight:600}.waveform-tooltip__value[data-v-93daa641]{overflow-wrap:anywhere}.waveform-tooltip__series small[data-v-93daa641]{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[data-v-52818b45]{z-index:40;background:#10182852;place-items:center;padding:20px;animation:.16s ease-out waveform-annotation-editor-fade-in-52818b45;display:grid;position:absolute;inset:0}.waveform-annotation-editor__panel[data-v-52818b45]{color:#344054;background:#fff;border:1px solid #e4e7ec;border-radius:12px;gap:20px;width:min(440px,100%);max-height:100%;padding:22px;font-size:13px;animation:.18s ease-out waveform-annotation-editor-rise-in-52818b45;display:grid;overflow-y:auto;box-shadow:0 20px 50px #10182838}.waveform-annotation-editor__header[data-v-52818b45]{justify-content:space-between;align-items:flex-start;gap:16px;display:flex}.waveform-annotation-editor__header h2[data-v-52818b45]{color:#101828;margin:0;font-size:18px;line-height:1.3}.waveform-annotation-editor__header p[data-v-52818b45]{color:#667085;margin:5px 0 0;font-size:12px}.waveform-annotation-editor__close[data-v-52818b45]{color:#667085;cursor:pointer;background:0 0;border:0;border-radius:6px;flex:0 0 30px;place-items:center;width:30px;height:30px;padding:0;font-size:22px;line-height:1;display:inline-grid}.waveform-annotation-editor__close[data-v-52818b45]:hover{color:#101828;background:#f2f4f7}.waveform-annotation-editor__coordinates[data-v-52818b45]{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;display:grid}.waveform-annotation-editor__coordinates span[data-v-52818b45],.waveform-annotation-editor__series[data-v-52818b45]{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-52818b45]{color:#667085}.waveform-annotation-editor__coordinates b[data-v-52818b45]{color:#1677ff;font-size:11px}.waveform-annotation-editor__coordinates code[data-v-52818b45]{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-52818b45]{gap:10px}.waveform-annotation-editor__series i[data-v-52818b45]{border:1px solid #0000001f;border-radius:2px;flex:0 0 12px;width:12px;height:12px}.waveform-annotation-editor__series-label[data-v-52818b45]{gap:1px;min-width:0;display:grid}.waveform-annotation-editor__series select[data-v-52818b45]{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-52818b45]{color:#667085;font-size:10px}.waveform-annotation-editor__series b[data-v-52818b45]{color:#344054;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.waveform-annotation-editor__series code[data-v-52818b45]{color:#667085;margin-left:auto;font-size:11px}.waveform-annotation-editor__field[data-v-52818b45],.waveform-annotation-editor__color-field[data-v-52818b45]{gap:4px;display:grid}.waveform-annotation-editor__label-row[data-v-52818b45]{justify-content:space-between;align-items:center;gap:8px;display:flex}.waveform-annotation-editor__label-row b[data-v-52818b45],.waveform-annotation-editor__colors legend[data-v-52818b45]{color:#344054;font-size:12px;font-weight:600}.waveform-annotation-editor__label-row small[data-v-52818b45]{color:#98a2b3;font-size:11px}.waveform-annotation-editor textarea[data-v-52818b45]{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-52818b45]:focus{border-color:#1677ff;outline:3px solid #1677ff24}.waveform-annotation-editor__colors[data-v-52818b45]{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-52818b45]{grid-column:1/-1;padding:0}.waveform-annotation-editor__color-field[data-v-52818b45]{color:#667085;text-align:left;place-items:stretch stretch;min-width:0;font-size:11px}.waveform-annotation-editor__color-field[data-v-52818b45] .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-52818b45] .vc-color-wrap:hover{border-color:#98a2b3;box-shadow:0 0 0 3px #1677ff1f}.waveform-annotation-editor__actions[data-v-52818b45]{border-top:1px solid #eaecf0;justify-content:flex-end;gap:8px;padding-top:8px;display:flex}.waveform-annotation-editor__actions button[data-v-52818b45]{color:#475467;cursor:pointer;background:#fff;border:1px solid #d0d5dd;border-radius:6px;height:34px;padding:0 12px}.waveform-annotation-editor__actions button.is-primary[data-v-52818b45]{color:#fff;background:#1677ff;border-color:#1677ff}.waveform-annotation-editor__actions button[data-v-52818b45]:disabled{opacity:.45;cursor:not-allowed}@keyframes waveform-annotation-editor-fade-in-52818b45{0%{opacity:0}to{opacity:1}}@keyframes waveform-annotation-editor-rise-in-52818b45{0%{opacity:0;transform:translateY(8px)scale(.98)}to{opacity:1;transform:translateY(0)scale(1)}}@media (width<=420px){.waveform-annotation-editor[data-v-52818b45]{padding:12px}.waveform-annotation-editor__panel[data-v-52818b45]{gap:16px;padding:18px}.waveform-annotation-editor__coordinates[data-v-52818b45]{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}
2
2
  /*$vite$:1*/
@@ -44,6 +44,7 @@ declare const __VLS_export: import("vue").DefineComponent<WaveformChartProps, {
44
44
  zoomable: boolean;
45
45
  pannable: boolean;
46
46
  minVisiblePoints: number;
47
+ xDomainStrategy: import("..").WaveformXDomainStrategy;
47
48
  timeUnit: "s" | "ms";
48
49
  annotationsVisible: boolean;
49
50
  grid: import("..").WaveformGridOptions;
@@ -1,6 +1,6 @@
1
1
  import { type ZoomTransform } from 'd3';
2
2
  import { type ResolvedWaveformRenderingOptions } from '../../core/rendering';
3
- import type { WaveformDisplayMode, WaveformOverlayMode, WaveformXAxisLabelFormatter } from '../../types';
3
+ import type { WaveformDisplayMode, WaveformOverlayMode, WaveformXDomainStrategy, WaveformXAxisLabelFormatter } from '../../types';
4
4
  import { type GridCellGeometry, type NormalizedWaveformGridOptions } from './grid';
5
5
  import type { DisplayTrack, TrackLayout } from './types';
6
6
  interface SeriesGridCell extends GridCellGeometry {
@@ -15,6 +15,7 @@ export interface BuildTrackLayoutsOptions {
15
15
  sharedZoomDomain: [number, number];
16
16
  initialXDomain?: [number, number];
17
17
  initialXDomains?: Record<string, [number, number]>;
18
+ xDomainStrategy?: WaveformXDomainStrategy;
18
19
  fixedYDomain?: [number, number];
19
20
  fixedYDomains?: Record<string, [number, number]>;
20
21
  yDomains?: Record<string, [number, number]>;
@@ -6,6 +6,7 @@ import type { NormalizedWaveformGridLineOptions } from './grid';
6
6
  */
7
7
  export interface DisplaySeries {
8
8
  id: string;
9
+ shotNo?: string;
9
10
  trackId?: string;
10
11
  name: string;
11
12
  unit?: string;
@@ -52,6 +53,7 @@ export interface WaveformYAxisLayout {
52
53
  */
53
54
  export interface HoveredSeriesPoint {
54
55
  id: string;
56
+ shotNo?: string;
55
57
  name: string;
56
58
  unit?: string;
57
59
  color: string;
@@ -330,6 +330,7 @@ export declare function useWaveformChartController(props: ResolvedWaveformChartP
330
330
  minZoomSpan?: number | undefined;
331
331
  minVisiblePoints: number;
332
332
  initialXDomains?: Record<string, [number, number]> | undefined;
333
+ xDomainStrategy: import("../..").WaveformXDomainStrategy;
333
334
  yDomain?: [number, number] | undefined;
334
335
  yDomains?: Record<string, [number, number]> | undefined;
335
336
  timeUnit: "s" | "ms";
@@ -1,4 +1,4 @@
1
- import type { WaveformAnnotation, WaveformAxesOptions, WaveformData, WaveformDisplayMode, WaveformFrameStyle, WaveformInteractionMode, WaveformLegendOptions, WaveformOverlayMode, WaveformPlotMargin, WaveformPoint, WaveformRenderingOptions, WaveformTitleOptions, WaveformZeroLineOptions, WaveformZoomEndPayload, WaveformZoomResetPayload } from '../data/types';
1
+ import type { WaveformAnnotation, WaveformAxesOptions, WaveformData, WaveformDisplayMode, WaveformFrameStyle, WaveformInteractionMode, WaveformLegendOptions, WaveformOverlayMode, WaveformPlotMargin, WaveformPoint, WaveformRenderingOptions, WaveformTitleOptions, WaveformXDomainStrategy, WaveformZeroLineOptions, WaveformZoomEndPayload, WaveformZoomResetPayload } from '../data/types';
2
2
  import type { WaveformGridOptions } from './grid';
3
3
  export interface WaveformChartProps {
4
4
  data: WaveformData;
@@ -16,6 +16,7 @@ export interface WaveformChartProps {
16
16
  minVisiblePoints?: number;
17
17
  initialXDomain?: [number, number];
18
18
  initialXDomains?: Record<string, [number, number]>;
19
+ xDomainStrategy?: WaveformXDomainStrategy;
19
20
  yDomain?: [number, number];
20
21
  yDomains?: Record<string, [number, number]>;
21
22
  timeUnit?: 's' | 'ms';
@@ -36,7 +37,7 @@ export interface WaveformChartProps {
36
37
  presentationMode?: boolean;
37
38
  zeroLine?: WaveformZeroLineOptions;
38
39
  }
39
- type DefaultedProp = 'displayMode' | 'overlayMode' | 'yLabel' | 'lineColor' | 'showTooltip' | 'zoomable' | 'pannable' | 'minVisiblePoints' | 'timeUnit' | 'annotations' | 'annotationsVisible' | 'grid' | 'rendering' | 'plotMargin' | 'legend' | 'defaultHiddenSeriesIds' | 'cleanView' | 'presentationMode' | 'zeroLine';
40
+ type DefaultedProp = 'displayMode' | 'overlayMode' | 'yLabel' | 'lineColor' | 'showTooltip' | 'zoomable' | 'pannable' | 'minVisiblePoints' | 'xDomainStrategy' | 'timeUnit' | 'annotations' | 'annotationsVisible' | 'grid' | 'rendering' | 'plotMargin' | 'legend' | 'defaultHiddenSeriesIds' | 'cleanView' | 'presentationMode' | 'zeroLine';
40
41
  export type ResolvedWaveformChartProps = Readonly<WaveformChartProps & Required<Pick<WaveformChartProps, DefaultedProp>>>;
41
42
  export interface WaveformChartEmit {
42
43
  (event: 'point-hover', point: WaveformPoint | null): void;
@@ -0,0 +1,3 @@
1
+ import type { WaveformXDomainStrategy } from '../../types';
2
+ /** Derives a viewport domain without changing any source coordinates. */
3
+ export declare function applyXDomainStrategy(domain: [number, number], strategy: WaveformXDomainStrategy, explicit?: boolean): [number, number];
@@ -2,6 +2,6 @@
2
2
  * 数据系统类型定义
3
3
  * 重新导出 src/types 中的类型,方便组件内部使用
4
4
  */
5
- export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from '../../types';
5
+ export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from '../../types';
6
6
  export type { WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from '../core/grid';
7
7
  export { normalizeWaveformData, normalizeWaveformSeries } from '../../core';
@@ -2,6 +2,7 @@ import type { WaveformPoint } from '../data/types';
2
2
  interface SeriesPoint {
3
3
  trackIndex: number;
4
4
  name: string;
5
+ shotNo?: string;
5
6
  color: string;
6
7
  unit?: string;
7
8
  point: WaveformPoint;
@@ -2,5 +2,5 @@
2
2
  * 向后兼容的导出文件
3
3
  * 保留原有导入路径,内部从新的模块结构导出
4
4
  */
5
- export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformFrameStyle, SingleWaveformData, WaveformSeries, WaveformLineStyle, WaveformData, NormalizedWaveformSeries, } from '../types';
5
+ export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformFrameStyle, SingleWaveformData, WaveformSeries, WaveformLineStyle, WaveformData, NormalizedWaveformSeries, } from '../types';
6
6
  export { normalizeWaveformData, normalizeWaveformSeries } from '../core';
@@ -3,7 +3,7 @@
3
3
  * @packageDocumentation
4
4
  */
5
5
  export { default as WaveformChart } from './components/WaveformChart.vue';
6
- export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './types';
6
+ export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './types';
7
7
  export type { WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from './components/core/grid';
8
8
  export { normalizeWaveformData, normalizeWaveformSeries } from './core';
9
9
  export { paddedDomain, buildMinorTicks, displayTime, formatEndpointTime, formatAxisTime, formatTooltipTime, resolveTrackGeometry, clamp, type TimeUnit, type TrackGeometry, } from './utils';
@@ -22,6 +22,16 @@ export type WaveformDisplayMode = 'independent' | 'separated' | 'compact';
22
22
  export type WaveformOverlayMode = 'single-axis' | 'multi-axis';
23
23
  /** 标注工具模式 */
24
24
  export type WaveformInteractionMode = 'zoom' | 'annotation';
25
+ /** Controls how the initial X viewport is derived when no explicit domain is configured. */
26
+ export interface WaveformXDomainStrategy {
27
+ type: 'data' | 'nice';
28
+ /** Selects which bounds are expanded when type is `nice`. Defaults to `both`. */
29
+ bounds?: 'both' | 'end';
30
+ /** Stable tick count used to calculate nice bounds. Defaults to 10. */
31
+ tickCount?: number;
32
+ /** Applies the strategy to explicit initial domains. Defaults to false. */
33
+ includeExplicit?: boolean;
34
+ }
25
35
  /** Describes the X-axis viewport after a zoom gesture completes. */
26
36
  export interface WaveformZoomEndPayload {
27
37
  start: number;
@@ -33,6 +33,8 @@ export type SingleWaveformData = {
33
33
  */
34
34
  export interface WaveformSeries {
35
35
  id?: string;
36
+ /** 炮号,未提供时 Tooltip 显示“未配置炮号”。 */
37
+ shotNo?: string;
36
38
  /** 相同 trackId 的系列叠加在同一图框中;默认每个系列独占一个图框。 */
37
39
  trackId?: string;
38
40
  name: string;
@@ -56,6 +58,7 @@ export type WaveformData = SingleWaveformData | {
56
58
  */
57
59
  export interface NormalizedWaveformSeries {
58
60
  id: string;
61
+ shotNo?: string;
59
62
  trackId?: string;
60
63
  name: string;
61
64
  unit?: string;
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * 类型定义统一导出
3
3
  */
4
- export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, } from './chart';
4
+ export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, } from './chart';
5
5
  export type { SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './data';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waveform-analysis",
3
- "version": "0.1.32",
3
+ "version": "0.1.36",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/types/index.d.ts",
@@ -16,6 +16,7 @@ const props = withDefaults(defineProps<WaveformChartProps>(), {
16
16
  zoomable: true,
17
17
  pannable: false,
18
18
  minVisiblePoints: 0,
19
+ xDomainStrategy: () => ({ type: 'data' }),
19
20
  timeUnit: 'ms',
20
21
  frameNumber: undefined,
21
22
  annotations: () => [],
@@ -16,6 +16,7 @@ import type {
16
16
  WaveformDisplayMode,
17
17
  WaveformOverlayMode,
18
18
  WaveformPoint,
19
+ WaveformXDomainStrategy,
19
20
  WaveformXAxisLabelFormatter,
20
21
  } from '../../types'
21
22
  import { buildMinorTicks, formatXAxisLabel } from '../../utils'
@@ -26,6 +27,7 @@ import {
26
27
  } from './grid'
27
28
  import { axisTextMetrics, resolveYAxisSeriesGroups } from './layout'
28
29
  import type { DisplaySeries, DisplayTrack, TrackLayout, WaveformYAxisLayout } from './types'
30
+ import { applyXDomainStrategy } from './xDomain'
29
31
  import {
30
32
  Y_AXIS_LABEL_BAND_WIDTH,
31
33
  Y_AXIS_LABEL_GAP,
@@ -46,6 +48,7 @@ export interface BuildTrackLayoutsOptions {
46
48
  sharedZoomDomain: [number, number]
47
49
  initialXDomain?: [number, number]
48
50
  initialXDomains?: Record<string, [number, number]>
51
+ xDomainStrategy?: WaveformXDomainStrategy
49
52
  fixedYDomain?: [number, number]
50
53
  fixedYDomains?: Record<string, [number, number]>
51
54
  yDomains?: Record<string, [number, number]>
@@ -57,6 +60,21 @@ export interface BuildTrackLayoutsOptions {
57
60
  showCompactEmptyTracks: boolean
58
61
  }
59
62
 
63
+ function resolveIndependentXDomain(
64
+ track: DisplayTrack,
65
+ seriesId: string,
66
+ options: BuildTrackLayoutsOptions,
67
+ ): [number, number] {
68
+ const strategy = options.xDomainStrategy ?? { type: 'data' }
69
+ const explicitDomain =
70
+ options.initialXDomains?.[track.id] ??
71
+ options.initialXDomains?.[seriesId] ??
72
+ options.initialXDomain
73
+ return explicitDomain
74
+ ? applyXDomainStrategy(explicitDomain, strategy, true)
75
+ : applyXDomainStrategy(track.xDomain, strategy)
76
+ }
77
+
60
78
  export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayout[] {
61
79
  const visibleCells = options.cells.map((cell) => ({ ...cell, hasSeries: Boolean(cell.series) }))
62
80
  const bottomCells = getBottomRowCellIndexes(visibleCells, options.grid.columnCount)
@@ -88,13 +106,7 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
88
106
  const series = displayTrack.visibleSeries[0] ?? displayTrack.series[0] ?? emptySeries
89
107
  const baseXScale =
90
108
  options.displayMode === 'independent'
91
- ? scaleLinear(
92
- options.initialXDomains?.[displayTrack.id] ??
93
- options.initialXDomains?.[series.id] ??
94
- options.initialXDomain ??
95
- displayTrack.xDomain,
96
- [0, cell.width],
97
- )
109
+ ? scaleLinear(resolveIndependentXDomain(displayTrack, series.id, options), [0, cell.width])
98
110
  : scaleLinear(options.sharedZoomDomain, [0, cell.width])
99
111
  const transform =
100
112
  options.displayMode === 'independent'
@@ -13,6 +13,7 @@ import type { NormalizedWaveformGridLineOptions } from './grid'
13
13
  */
14
14
  export interface DisplaySeries {
15
15
  id: string
16
+ shotNo?: string
16
17
  trackId?: string
17
18
  name: string
18
19
  unit?: string
@@ -63,6 +64,7 @@ export interface WaveformYAxisLayout {
63
64
  */
64
65
  export interface HoveredSeriesPoint {
65
66
  id: string
67
+ shotNo?: string
66
68
  name: string
67
69
  unit?: string
68
70
  color: string
@@ -204,6 +204,7 @@ export function useWaveformChartLifecycle(context: LifecycleContext) {
204
204
  () => props.minVisiblePoints,
205
205
  () => props.initialXDomain,
206
206
  () => props.initialXDomains,
207
+ () => props.xDomainStrategy,
207
208
  () => props.displayMode,
208
209
  () => chartTracks.value.length,
209
210
  currentPage,
@@ -34,6 +34,7 @@ import {
34
34
  import type { DisplaySeries, DisplayTrack, TrackLayout } from './types'
35
35
  import type { PreparedWaveformSeries } from './useWaveformData'
36
36
  import type { ResolvedWaveformChartProps } from './waveformChartTypes'
37
+ import { applyXDomainStrategy } from './xDomain'
37
38
  import type { useWaveformAnnotationInteraction } from '../annotation'
38
39
 
39
40
  interface LayoutContext {
@@ -224,9 +225,13 @@ export function useWaveformLayout(context: LayoutContext) {
224
225
  Number.isFinite(domain[1]) &&
225
226
  domain[0] !== domain[1]
226
227
  ) {
227
- return domain[0] < domain[1] ? domain : [domain[1], domain[0]]
228
+ return applyXDomainStrategy(
229
+ domain[0] < domain[1] ? domain : [domain[1], domain[0]],
230
+ props.xDomainStrategy,
231
+ true,
232
+ )
228
233
  }
229
- return sharedXDomain.value
234
+ return applyXDomainStrategy(sharedXDomain.value, props.xDomainStrategy)
230
235
  })
231
236
  const resolveInitialTrackDomain = (track: TrackLayout): [number, number] => {
232
237
  const configuredDomain =
@@ -239,11 +244,18 @@ export function useWaveformLayout(context: LayoutContext) {
239
244
  Number.isFinite(configuredDomain[1]) &&
240
245
  configuredDomain[0] !== configuredDomain[1]
241
246
  ) {
242
- return configuredDomain[0] < configuredDomain[1]
243
- ? configuredDomain
244
- : [configuredDomain[1], configuredDomain[0]]
247
+ return applyXDomainStrategy(
248
+ configuredDomain[0] < configuredDomain[1]
249
+ ? configuredDomain
250
+ : [configuredDomain[1], configuredDomain[0]],
251
+ props.xDomainStrategy,
252
+ true,
253
+ )
245
254
  }
246
- return paddedDomain(track.seriesList.flatMap((series) => series.xDomain))
255
+ return applyXDomainStrategy(
256
+ paddedDomain(track.seriesList.flatMap((series) => series.xDomain)),
257
+ props.xDomainStrategy,
258
+ )
247
259
  }
248
260
  const sharedZoomDomain = computed(
249
261
  () =>
@@ -272,6 +284,7 @@ export function useWaveformLayout(context: LayoutContext) {
272
284
  sharedZoomDomain: sharedZoomDomain.value,
273
285
  initialXDomain: props.initialXDomain ? initialXDomain.value : undefined,
274
286
  initialXDomains: props.initialXDomains,
287
+ xDomainStrategy: props.xDomainStrategy,
275
288
  fixedYDomain: props.yDomain,
276
289
  fixedYDomains: props.yDomains,
277
290
  yDomains:
@@ -11,6 +11,7 @@ import type {
11
11
  WaveformPoint,
12
12
  WaveformRenderingOptions,
13
13
  WaveformTitleOptions,
14
+ WaveformXDomainStrategy,
14
15
  WaveformZeroLineOptions,
15
16
  WaveformZoomEndPayload,
16
17
  WaveformZoomResetPayload,
@@ -33,6 +34,7 @@ export interface WaveformChartProps {
33
34
  minVisiblePoints?: number
34
35
  initialXDomain?: [number, number]
35
36
  initialXDomains?: Record<string, [number, number]>
37
+ xDomainStrategy?: WaveformXDomainStrategy
36
38
  yDomain?: [number, number]
37
39
  yDomains?: Record<string, [number, number]>
38
40
  timeUnit?: 's' | 'ms'
@@ -63,6 +65,7 @@ type DefaultedProp =
63
65
  | 'zoomable'
64
66
  | 'pannable'
65
67
  | 'minVisiblePoints'
68
+ | 'xDomainStrategy'
66
69
  | 'timeUnit'
67
70
  | 'annotations'
68
71
  | 'annotationsVisible'
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { applyXDomainStrategy } from './xDomain'
4
+
5
+ describe('applyXDomainStrategy', () => {
6
+ it('keeps the exact data domain by default', () => {
7
+ expect(applyXDomainStrategy([0, 4999.999], { type: 'data' })).toEqual([0, 4999.999])
8
+ })
9
+
10
+ it('expands both bounds to stable nice values', () => {
11
+ expect(applyXDomainStrategy([123, 456], { type: 'nice' })).toEqual([100, 500])
12
+ })
13
+
14
+ it('can expand only the end bound', () => {
15
+ expect(applyXDomainStrategy([123, 456], { type: 'nice', bounds: 'end' })).toEqual([123, 500])
16
+ })
17
+
18
+ it('keeps explicit domains exact unless they are included', () => {
19
+ expect(applyXDomainStrategy([123, 456], { type: 'nice' }, true)).toEqual([123, 456])
20
+ expect(applyXDomainStrategy([123, 456], { type: 'nice', includeExplicit: true }, true)).toEqual(
21
+ [100, 500],
22
+ )
23
+ })
24
+
25
+ it('falls back to the default tick count for invalid values', () => {
26
+ expect(applyXDomainStrategy([0, 4999.999], { type: 'nice', tickCount: 0 })).toEqual([0, 5000])
27
+ })
28
+ })
@@ -0,0 +1,25 @@
1
+ import { scaleLinear } from 'd3'
2
+
3
+ import type { WaveformXDomainStrategy } from '../../types'
4
+
5
+ const DEFAULT_NICE_TICK_COUNT = 10
6
+
7
+ function resolveTickCount(value: number | undefined): number {
8
+ if (!Number.isFinite(value) || (value ?? 0) < 1) return DEFAULT_NICE_TICK_COUNT
9
+ return Math.max(1, Math.trunc(value as number))
10
+ }
11
+
12
+ /** Derives a viewport domain without changing any source coordinates. */
13
+ export function applyXDomainStrategy(
14
+ domain: [number, number],
15
+ strategy: WaveformXDomainStrategy,
16
+ explicit = false,
17
+ ): [number, number] {
18
+ if (strategy.type !== 'nice' || (explicit && !strategy.includeExplicit)) return [...domain]
19
+
20
+ const niceDomain = scaleLinear()
21
+ .domain(domain)
22
+ .nice(resolveTickCount(strategy.tickCount))
23
+ .domain() as [number, number]
24
+ return strategy.bounds === 'end' ? [domain[0], niceDomain[1]] : niceDomain
25
+ }
@@ -9,6 +9,7 @@ export type {
9
9
  WaveformDisplayMode,
10
10
  WaveformOverlayMode,
11
11
  WaveformInteractionMode,
12
+ WaveformXDomainStrategy,
12
13
  WaveformZoomEndPayload,
13
14
  WaveformZoomResetPayload,
14
15
  WaveformAnnotationStyle,
@@ -80,7 +80,7 @@ describe('WaveformTooltip', () => {
80
80
  expect(tooltip.attributes('style')).toContain('max-width: 280px')
81
81
  })
82
82
 
83
- it('shows resolved asymmetric errors beside the hovered value', () => {
83
+ it('omits units and errors from the strict x/y value format', () => {
84
84
  const pointWithErrors = { x: 1, y: 12, error: 1, upperError: 2 }
85
85
  const wrapper = mount(WaveformTooltip, {
86
86
  props: {
@@ -102,10 +102,12 @@ describe('WaveformTooltip', () => {
102
102
  },
103
103
  })
104
104
 
105
- expect(wrapper.get('.waveform-tooltip__series small').text()).toBe('(+2 / -1)')
105
+ expect(wrapper.get('.waveform-tooltip__value').text()).toBe('(x:1 y:12)')
106
+ expect(wrapper.get('.waveform-tooltip__value').text()).not.toContain('C')
107
+ expect(wrapper.get('.waveform-tooltip__value').text()).not.toContain('+2')
106
108
  })
107
109
 
108
- it('keeps a formatted value and its unit in one value container', () => {
110
+ it('renders the configured shot number, channel, and formatted coordinates', () => {
109
111
  const hoveredPoint = { x: 1, y: -1405.4932 }
110
112
  const wrapper = mount(WaveformTooltip, {
111
113
  props: {
@@ -117,6 +119,7 @@ describe('WaveformTooltip', () => {
117
119
  {
118
120
  trackIndex: 0,
119
121
  name: 'ENG8KJXAc(10001)',
122
+ shotNo: '炮 7',
120
123
  color: '#ffb43b',
121
124
  unit: 'A',
122
125
  point: hoveredPoint,
@@ -127,26 +130,31 @@ describe('WaveformTooltip', () => {
127
130
  },
128
131
  })
129
132
 
130
- expect(wrapper.get('.waveform-tooltip__value').text()).toBe('-1,405.4932 A')
133
+ expect(wrapper.get('.waveform-tooltip__series-label').text()).toBe('炮 7: ENG8KJXAc(10001)(A)')
134
+ expect(wrapper.get('.waveform-tooltip__value').text()).toBe('(x:1,000 y:-1,405.4932)')
131
135
  })
132
136
 
133
- it('formats tooltip time with at most four decimal places', () => {
137
+ it('renders each series row with the formatted x coordinate and series label', () => {
134
138
  const wrapper = mount(WaveformTooltip, {
135
139
  props: {
136
140
  visible: true,
137
141
  position: { x: 10, y: 10 },
138
142
  timeUnit: 's',
139
143
  hoveredPoint: { x: 1.234567, y: 12 },
140
- seriesPoints: [],
144
+ seriesPoints: [
145
+ { trackIndex: 0, name: '温度', color: '#f00', point: { x: 1.234567, y: 12 } },
146
+ ],
141
147
  containerWidth: 400,
142
148
  containerHeight: 300,
143
149
  },
144
150
  })
145
151
 
146
- expect(wrapper.get('.waveform-tooltip__time').text()).toBe('s: 1.2346')
152
+ expect(wrapper.find('.waveform-tooltip__time').exists()).toBe(false)
153
+ expect(wrapper.get('.waveform-tooltip__series-label').text()).toBe('未配置炮号: 温度')
154
+ expect(wrapper.get('.waveform-tooltip__value').text()).toBe('(x:1.2346 y:12)')
147
155
  })
148
156
 
149
- it('omits the error label when both resolved errors are zero', () => {
157
+ it('uses the fallback shot number when shotNo is blank', () => {
150
158
  const point = { x: 1, y: 12 }
151
159
  const wrapper = mount(WaveformTooltip, {
152
160
  props: {
@@ -160,6 +168,6 @@ describe('WaveformTooltip', () => {
160
168
  },
161
169
  })
162
170
 
163
- expect(wrapper.find('.waveform-tooltip__series small').exists()).toBe(false)
171
+ expect(wrapper.get('.waveform-tooltip__series-label').text()).toBe('未配置炮号: 温度')
164
172
  })
165
173
  })