waveform-analysis 0.1.40 → 0.1.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/dist/index.cjs +1 -1
- package/dist/index.js +478 -477
- package/dist/style.css +1 -1
- package/dist/types/components/core/grid.d.ts +3 -0
- package/dist/types/components/core/layout.d.ts +4 -3
- package/dist/types/components/core/trackLayoutBuilder.d.ts +1 -0
- package/dist/types/components/core/useWaveformChartController.d.ts +1 -0
- package/dist/types/components/core/useWaveformChartLifecycle.d.ts +2 -4
- package/dist/types/components/core/useWaveformLayout.d.ts +1 -0
- package/dist/types/types/chart.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/annotation/WaveformAnnotationEditor.css +30 -0
- package/src/components/annotation/WaveformAnnotationEditor.vue +1 -0
- package/src/components/annotation/components.test.ts +14 -1
- package/src/components/core/fixedYDomainLayout.test.ts +94 -6
- package/src/components/core/grid.test.ts +48 -0
- package/src/components/core/grid.ts +20 -7
- package/src/components/core/layout.test.ts +31 -5
- package/src/components/core/layout.ts +21 -8
- package/src/components/core/trackLayoutBuilder.ts +14 -9
- package/src/components/core/useWaveformChartLifecycle.ts +25 -15
- package/src/components/core/useWaveformLayout.ts +9 -2
- package/src/components/waveformChartCases/compactAndSharedHover.test.ts +2 -2
- package/src/components/waveformChartCases/dataLifecycle.test.ts +2 -2
- package/src/components/waveformChartCases/gridFill.test.ts +95 -0
- package/src/components/waveformChartCases/layoutAndDomains.test.ts +23 -3
- package/src/types/chart.ts +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__content[data-v-8ce79dc8]{color:#344054;gap:20px;font-size:13px;display:grid}.waveform-annotation-editor__coordinates[data-v-8ce79dc8]{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;display:grid}.waveform-annotation-editor__coordinates span[data-v-8ce79dc8],.waveform-annotation-editor__series[data-v-8ce79dc8]{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-8ce79dc8]{color:#667085}.waveform-annotation-editor__coordinate-input[data-v-8ce79dc8]{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-8ce79dc8] .ant-input-number{color:#344054;width:auto;min-width:96px;font-family:SFMono-Regular,Consolas,monospace}.waveform-annotation-editor__coordinate-value[data-v-8ce79dc8]{display:none}.waveform-annotation-editor__coordinate-error[data-v-8ce79dc8]{color:#d92d20;grid-column:1/-1;margin:-12px 0 0;font-size:11px;line-height:1.4}.waveform-annotation-editor__coordinate-hint[data-v-8ce79dc8]{color:#98a2b3;margin:-12px 0 0;font-size:11px;line-height:1.4}.waveform-annotation-editor__coordinates b[data-v-8ce79dc8]{color:#1677ff;font-size:11px}.waveform-annotation-editor__coordinates code[data-v-8ce79dc8]{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-8ce79dc8]{gap:10px}.waveform-annotation-editor__series i[data-v-8ce79dc8]{border:1px solid #0000001f;border-radius:2px;flex:0 0 12px;width:12px;height:12px}.waveform-annotation-editor__series-label[data-v-8ce79dc8]{gap:1px;min-width:0;display:grid}.waveform-annotation-editor__series select[data-v-8ce79dc8]{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-8ce79dc8]{color:#667085;font-size:10px}.waveform-annotation-editor__series b[data-v-8ce79dc8]{color:#344054;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.waveform-annotation-editor__series code[data-v-8ce79dc8]{color:#667085;margin-left:auto;font-size:11px}.waveform-annotation-editor__field[data-v-8ce79dc8],.waveform-annotation-editor__color-field[data-v-8ce79dc8]{gap:4px;display:grid}.waveform-annotation-editor__label-row[data-v-8ce79dc8]{justify-content:space-between;align-items:center;gap:8px;display:flex}.waveform-annotation-editor__label-row b[data-v-8ce79dc8],.waveform-annotation-editor__colors legend[data-v-8ce79dc8]{color:#344054;font-size:12px;font-weight:600}.waveform-annotation-editor__label-row small[data-v-8ce79dc8]{color:#98a2b3;font-size:11px}.waveform-annotation-editor textarea[data-v-8ce79dc8]{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-8ce79dc8]:focus{border-color:#1677ff;outline:3px solid #1677ff24}.waveform-annotation-editor__colors[data-v-8ce79dc8]{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-8ce79dc8]{grid-column:1/-1;padding:0}.waveform-annotation-editor__color-field[data-v-8ce79dc8]{color:#667085;text-align:left;place-items:stretch stretch;min-width:0;font-size:11px}.waveform-annotation-editor__color-field[data-v-8ce79dc8] .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-8ce79dc8] .vc-color-wrap:hover{border-color:#98a2b3;box-shadow:0 0 0 3px #1677ff1f}@media (width<=420px){.waveform-annotation-editor__content[data-v-8ce79dc8]{gap:16px}.waveform-annotation-editor__coordinates[data-v-8ce79dc8]{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-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*/
|
|
@@ -6,6 +6,7 @@ export interface WaveformGridOptions {
|
|
|
6
6
|
rowCount?: number;
|
|
7
7
|
columnCount?: number;
|
|
8
8
|
showPagination?: boolean;
|
|
9
|
+
fillIncompleteLastRow?: boolean;
|
|
9
10
|
trackLines?: WaveformGridTrackLines;
|
|
10
11
|
}
|
|
11
12
|
export interface WaveformGridLineOptions {
|
|
@@ -27,6 +28,7 @@ export interface NormalizedWaveformGridOptions {
|
|
|
27
28
|
rowCount: number;
|
|
28
29
|
columnCount: number;
|
|
29
30
|
showPagination: boolean;
|
|
31
|
+
fillIncompleteLastRow: boolean;
|
|
30
32
|
trackLines: Record<string, NormalizedWaveformGridLineOptions>;
|
|
31
33
|
}
|
|
32
34
|
export interface GridCellGeometry {
|
|
@@ -41,6 +43,7 @@ export interface GridCellGeometry {
|
|
|
41
43
|
plotHeight: number;
|
|
42
44
|
cellHeight: number;
|
|
43
45
|
xAxisBand: number;
|
|
46
|
+
isLastRow?: boolean;
|
|
44
47
|
}
|
|
45
48
|
export declare function normalizeGridOptions(options?: WaveformGridOptions): NormalizedWaveformGridOptions;
|
|
46
49
|
export declare function getPageSize(options: NormalizedWaveformGridOptions): number;
|
|
@@ -2,6 +2,7 @@ import type { WaveformOverlayMode } from '../../types';
|
|
|
2
2
|
import type { DisplaySeries, DisplayTrack, TrackLayout } from './types';
|
|
3
3
|
import { type WaveformYDomain } from './yDomain';
|
|
4
4
|
export { MAX_MULTI_Y_AXIS_COUNT } from './constants';
|
|
5
|
+
export declare function resolveYAxisTickCount(_plotHeight: number, splitNumber?: number): number;
|
|
5
6
|
export interface YAxisSeriesGroup {
|
|
6
7
|
index: number;
|
|
7
8
|
side: 'left' | 'right';
|
|
@@ -11,11 +12,11 @@ export interface YAxisSeriesGroup {
|
|
|
11
12
|
}
|
|
12
13
|
export declare function buildYAxisSeriesGroups(track: DisplayTrack, overlayMode: WaveformOverlayMode): YAxisSeriesGroup[];
|
|
13
14
|
export declare function resolveYAxisSeriesGroups(track: DisplayTrack, overlayMode: WaveformOverlayMode, yDomain?: WaveformYDomain, yDomains?: Record<string, WaveformYDomain>): YAxisSeriesGroup[];
|
|
14
|
-
export declare function axisTextMetrics(domain: [number, number], nice?: boolean, tickValues?: number[], unit?: string): {
|
|
15
|
+
export declare function axisTextMetrics(domain: [number, number], nice?: boolean, tickValues?: number[], unit?: string, tickCount?: number): {
|
|
15
16
|
tickTextWidth: number;
|
|
16
17
|
};
|
|
17
|
-
export declare function measureYAxisGroupClearance(group: YAxisSeriesGroup): number;
|
|
18
|
-
export declare function measureTrackYAxisClearance(track: DisplayTrack, overlayMode: WaveformOverlayMode, yDomain?: WaveformYDomain, yDomains?: Record<string, WaveformYDomain
|
|
18
|
+
export declare function measureYAxisGroupClearance(group: YAxisSeriesGroup, tickCount?: number): number;
|
|
19
|
+
export declare function measureTrackYAxisClearance(track: DisplayTrack, overlayMode: WaveformOverlayMode, yDomain?: WaveformYDomain, yDomains?: Record<string, WaveformYDomain>, tickCount?: number): {
|
|
19
20
|
left: number;
|
|
20
21
|
right: number;
|
|
21
22
|
};
|
|
@@ -21,6 +21,7 @@ export interface BuildTrackLayoutsOptions {
|
|
|
21
21
|
yDomains?: Record<string, [number, number]>;
|
|
22
22
|
timeUnit: 's' | 'ms';
|
|
23
23
|
xAxisLabelFormatter?: WaveformXAxisLabelFormatter;
|
|
24
|
+
yAxisSplitNumber?: number;
|
|
24
25
|
rendering: ResolvedWaveformRenderingOptions;
|
|
25
26
|
hideSecondaryLabels: boolean;
|
|
26
27
|
yAxisLabelX: number;
|
|
@@ -272,6 +272,7 @@ export declare function useWaveformChartController(props: ResolvedWaveformChartP
|
|
|
272
272
|
plotHeight: number;
|
|
273
273
|
cellHeight: number;
|
|
274
274
|
xAxisBand: number;
|
|
275
|
+
isLastRow?: boolean;
|
|
275
276
|
}[];
|
|
276
277
|
trackLayouts: import("./types").TrackLayout[];
|
|
277
278
|
annotationLayoutsForTrack: (track: import("./types").TrackLayout) => import("../annotation").AnnotationTrackLayout[];
|
|
@@ -2,6 +2,7 @@ import { type ZoomTransform } from 'd3';
|
|
|
2
2
|
import { type ComputedRef, type Ref, type ShallowRef } from 'vue';
|
|
3
3
|
import type { AnnotationSeriesCandidate } from '../annotation';
|
|
4
4
|
import type { useWaveformAnnotationInteraction } from '../annotation';
|
|
5
|
+
import type { NormalizedWaveformGridOptions } from './grid';
|
|
5
6
|
import type { DisplaySeries, DisplayTrack, TrackLayout } from './types';
|
|
6
7
|
import type { ResolvedWaveformChartProps, WaveformChartEmit } from './waveformChartTypes';
|
|
7
8
|
interface LifecycleContext {
|
|
@@ -22,10 +23,7 @@ interface LifecycleContext {
|
|
|
22
23
|
currentPage: Ref<number>;
|
|
23
24
|
pageCount: ComputedRef<number>;
|
|
24
25
|
pagedTracks: ComputedRef<Array<DisplayTrack | undefined>>;
|
|
25
|
-
gridOptions: ComputedRef<
|
|
26
|
-
rowCount: number;
|
|
27
|
-
columnCount: number;
|
|
28
|
-
}>;
|
|
26
|
+
gridOptions: ComputedRef<NormalizedWaveformGridOptions>;
|
|
29
27
|
chartSeries: ComputedRef<DisplaySeries[]>;
|
|
30
28
|
chartTracks: ComputedRef<DisplayTrack[]>;
|
|
31
29
|
trackLayouts: ComputedRef<TrackLayout[]>;
|
|
@@ -48,6 +48,7 @@ export declare function useWaveformLayout(context: LayoutContext): {
|
|
|
48
48
|
plotHeight: number;
|
|
49
49
|
cellHeight: number;
|
|
50
50
|
xAxisBand: number;
|
|
51
|
+
isLastRow?: boolean;
|
|
51
52
|
}[]>;
|
|
52
53
|
trackLayouts: ComputedRef<TrackLayout[]>;
|
|
53
54
|
annotationLayoutsForTrack: (track: TrackLayout) => AnnotationTrackLayout[];
|
|
@@ -150,6 +150,8 @@ export interface WaveformAxesOptions {
|
|
|
150
150
|
};
|
|
151
151
|
y?: {
|
|
152
152
|
lineVisible?: boolean;
|
|
153
|
+
/** Target total number of equal Y-axis ticks, including endpoints. Defaults to 5. */
|
|
154
|
+
splitNumber?: number;
|
|
153
155
|
};
|
|
154
156
|
}
|
|
155
157
|
/** Styling and visibility options for the horizontal zero-value reference line. */
|
package/package.json
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/* The modal is teleported to body. Keep its entire subtree namespaced so host
|
|
2
|
+
application resets (for example `button { ... }`) cannot change the editor. */
|
|
3
|
+
:global(.waveform-annotation-editor-root),
|
|
4
|
+
:global(.waveform-annotation-editor-root *) {
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
:global(.waveform-annotation-editor-root) {
|
|
9
|
+
color: #344054;
|
|
10
|
+
font-family:
|
|
11
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
|
12
|
+
'Noto Sans', sans-serif;
|
|
13
|
+
line-height: 1.5715;
|
|
14
|
+
text-align: left;
|
|
15
|
+
text-transform: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
:global(.waveform-annotation-editor-root button),
|
|
19
|
+
:global(.waveform-annotation-editor-root input),
|
|
20
|
+
:global(.waveform-annotation-editor-root select),
|
|
21
|
+
:global(.waveform-annotation-editor-root textarea) {
|
|
22
|
+
font-family: inherit;
|
|
23
|
+
letter-spacing: normal;
|
|
24
|
+
text-transform: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:global(.waveform-annotation-editor-root button) {
|
|
28
|
+
appearance: auto;
|
|
29
|
+
}
|
|
30
|
+
|
|
1
31
|
.waveform-annotation-editor__content {
|
|
2
32
|
display: grid;
|
|
3
33
|
gap: 20px;
|
|
@@ -126,6 +126,7 @@ function handleSeriesChange(event: Event) {
|
|
|
126
126
|
cancel-text="取消"
|
|
127
127
|
ok-text="保存标注"
|
|
128
128
|
:ok-button-props="{ disabled: !canConfirm }"
|
|
129
|
+
root-class-name="waveform-annotation-editor-root"
|
|
129
130
|
wrap-class-name="waveform-annotation-editor"
|
|
130
131
|
@cancel="emit('cancel')"
|
|
131
132
|
@ok="confirm"
|
|
@@ -18,13 +18,14 @@ const modalStub = defineComponent({
|
|
|
18
18
|
'cancelText',
|
|
19
19
|
'okText',
|
|
20
20
|
'okButtonProps',
|
|
21
|
+
'rootClassName',
|
|
21
22
|
],
|
|
22
23
|
emits: ['cancel', 'ok'],
|
|
23
24
|
setup(props, { emit, slots }) {
|
|
24
25
|
return () => {
|
|
25
26
|
const title = slots.title?.() ?? []
|
|
26
27
|
const titleId = (title[0]?.props as { id?: string } | undefined)?.id
|
|
27
|
-
return h('div', { class: 'ant-modal-root' }, [
|
|
28
|
+
return h('div', { class: ['ant-modal-root', props.rootClassName] }, [
|
|
28
29
|
h(
|
|
29
30
|
'div',
|
|
30
31
|
{
|
|
@@ -96,6 +97,18 @@ describe('waveform annotation controls', () => {
|
|
|
96
97
|
expect(second.get('[role="dialog"]').attributes('aria-labelledby')).toBe(secondTitleId)
|
|
97
98
|
})
|
|
98
99
|
|
|
100
|
+
it('uses a dedicated root class to isolate teleported modal styles', () => {
|
|
101
|
+
const wrapper = mountEditor({
|
|
102
|
+
props: {
|
|
103
|
+
annotation: { id: 'isolated', seriesId: 'a', x: 1, y: 2, text: '说明' },
|
|
104
|
+
mode: 'add',
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
expect(wrapper.get('.ant-modal-root').classes()).toContain('waveform-annotation-editor-root')
|
|
109
|
+
expect(wrapper.get('.ant-modal-wrap').classes()).toContain('waveform-annotation-editor')
|
|
110
|
+
})
|
|
111
|
+
|
|
99
112
|
it.each(['add', 'edit'] as const)('shows the X-axis snapping hint in %s mode', (mode) => {
|
|
100
113
|
const wrapper = mountEditor({
|
|
101
114
|
props: {
|
|
@@ -35,7 +35,7 @@ function track(seriesList: DisplaySeries[]): DisplayTrack {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
describe('fixed Y-domain layout', () => {
|
|
38
|
-
it('
|
|
38
|
+
it('expands a global fixed domain to nice equal intervals', () => {
|
|
39
39
|
const sourceTrack = track([series('a', 0, 100)])
|
|
40
40
|
const result = buildTrackLayouts({
|
|
41
41
|
cells: [
|
|
@@ -53,7 +53,13 @@ describe('fixed Y-domain layout', () => {
|
|
|
53
53
|
series: sourceTrack,
|
|
54
54
|
},
|
|
55
55
|
],
|
|
56
|
-
grid: {
|
|
56
|
+
grid: {
|
|
57
|
+
rowCount: 1,
|
|
58
|
+
columnCount: 1,
|
|
59
|
+
showPagination: false,
|
|
60
|
+
fillIncompleteLastRow: false,
|
|
61
|
+
trackLines: {},
|
|
62
|
+
},
|
|
57
63
|
displayMode: 'independent',
|
|
58
64
|
overlayMode: 'single-axis',
|
|
59
65
|
independentTransforms: [zoomIdentity],
|
|
@@ -66,10 +72,92 @@ describe('fixed Y-domain layout', () => {
|
|
|
66
72
|
showCompactEmptyTracks: false,
|
|
67
73
|
})[0]
|
|
68
74
|
|
|
69
|
-
expect(result?.yScale.domain()).toEqual([
|
|
70
|
-
expect(result?.yAxes[0]?.tickValues).toContain(
|
|
71
|
-
expect(result?.yAxes[0]?.tickValues).toContain(
|
|
72
|
-
expect(result?.seriesPaths[0]?.yScale.domain()).toEqual([
|
|
75
|
+
expect(result?.yScale.domain()).toEqual([0, 100])
|
|
76
|
+
expect(result?.yAxes[0]?.tickValues).toContain(0)
|
|
77
|
+
expect(result?.yAxes[0]?.tickValues).toContain(100)
|
|
78
|
+
expect(result?.seriesPaths[0]?.yScale.domain()).toEqual([0, 100])
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('uses the configured split number for fixed domains', () => {
|
|
82
|
+
const result = buildTrackLayouts({
|
|
83
|
+
cells: [
|
|
84
|
+
{
|
|
85
|
+
slotIndex: 0,
|
|
86
|
+
row: 0,
|
|
87
|
+
column: 0,
|
|
88
|
+
left: 0,
|
|
89
|
+
top: 0,
|
|
90
|
+
width: 120,
|
|
91
|
+
height: 100,
|
|
92
|
+
plotHeight: 100,
|
|
93
|
+
cellHeight: 130,
|
|
94
|
+
xAxisBand: 30,
|
|
95
|
+
series: track([series('a', 0, 100)]),
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
grid: {
|
|
99
|
+
rowCount: 1,
|
|
100
|
+
columnCount: 1,
|
|
101
|
+
showPagination: false,
|
|
102
|
+
fillIncompleteLastRow: false,
|
|
103
|
+
trackLines: {},
|
|
104
|
+
},
|
|
105
|
+
displayMode: 'independent',
|
|
106
|
+
overlayMode: 'single-axis',
|
|
107
|
+
independentTransforms: [zoomIdentity],
|
|
108
|
+
sharedZoomDomain: [0, 1],
|
|
109
|
+
fixedYDomain: [3, 97],
|
|
110
|
+
yAxisSplitNumber: 5,
|
|
111
|
+
timeUnit: 'ms',
|
|
112
|
+
rendering: DEFAULT_WAVEFORM_RENDERING_OPTIONS,
|
|
113
|
+
hideSecondaryLabels: false,
|
|
114
|
+
yAxisLabelX: -50,
|
|
115
|
+
showCompactEmptyTracks: false,
|
|
116
|
+
})[0]
|
|
117
|
+
|
|
118
|
+
expect(result?.yAxes[0]?.majorTicks).toEqual([0, 25, 50, 75, 100])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('defaults to five ticks and supports a two-tick axis', () => {
|
|
122
|
+
const build = (splitNumber?: number) =>
|
|
123
|
+
buildTrackLayouts({
|
|
124
|
+
cells: [
|
|
125
|
+
{
|
|
126
|
+
slotIndex: 0,
|
|
127
|
+
row: 0,
|
|
128
|
+
column: 0,
|
|
129
|
+
left: 0,
|
|
130
|
+
top: 0,
|
|
131
|
+
width: 120,
|
|
132
|
+
height: 100,
|
|
133
|
+
plotHeight: 100,
|
|
134
|
+
cellHeight: 130,
|
|
135
|
+
xAxisBand: 30,
|
|
136
|
+
series: track([series('a', 3, 97)]),
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
grid: {
|
|
140
|
+
rowCount: 1,
|
|
141
|
+
columnCount: 1,
|
|
142
|
+
showPagination: false,
|
|
143
|
+
fillIncompleteLastRow: false,
|
|
144
|
+
trackLines: {},
|
|
145
|
+
},
|
|
146
|
+
displayMode: 'independent',
|
|
147
|
+
overlayMode: 'single-axis',
|
|
148
|
+
independentTransforms: [zoomIdentity],
|
|
149
|
+
sharedZoomDomain: [0, 1],
|
|
150
|
+
fixedYDomain: [3, 97],
|
|
151
|
+
yAxisSplitNumber: splitNumber,
|
|
152
|
+
timeUnit: 'ms',
|
|
153
|
+
rendering: DEFAULT_WAVEFORM_RENDERING_OPTIONS,
|
|
154
|
+
hideSecondaryLabels: false,
|
|
155
|
+
yAxisLabelX: -50,
|
|
156
|
+
showCompactEmptyTracks: false,
|
|
157
|
+
})[0]?.yAxes[0]?.majorTicks
|
|
158
|
+
|
|
159
|
+
expect(build()).toHaveLength(5)
|
|
160
|
+
expect(build(2)).toEqual([0, 100])
|
|
73
161
|
})
|
|
74
162
|
|
|
75
163
|
it('resolves track, series, and global fixed-domain precedence', () => {
|
|
@@ -15,12 +15,14 @@ describe('waveform grid helpers', () => {
|
|
|
15
15
|
rowCount: 2,
|
|
16
16
|
columnCount: 1,
|
|
17
17
|
showPagination: true,
|
|
18
|
+
fillIncompleteLastRow: false,
|
|
18
19
|
trackLines: {},
|
|
19
20
|
})
|
|
20
21
|
expect(normalizeGridOptions({ rowCount: 0, columnCount: 99 })).toEqual({
|
|
21
22
|
rowCount: 1,
|
|
22
23
|
columnCount: 10,
|
|
23
24
|
showPagination: true,
|
|
25
|
+
fillIncompleteLastRow: false,
|
|
24
26
|
trackLines: {},
|
|
25
27
|
})
|
|
26
28
|
})
|
|
@@ -131,4 +133,50 @@ describe('waveform grid helpers', () => {
|
|
|
131
133
|
expect(cells[1].left).toBe(232)
|
|
132
134
|
expect(cells[2].top).toBe(cells[0].plotHeight + X_AXIS_BAND + 14)
|
|
133
135
|
})
|
|
136
|
+
|
|
137
|
+
it('fills incomplete final rows without changing page capacity', () => {
|
|
138
|
+
const oneColumn = resolveGridCellGeometry(
|
|
139
|
+
400,
|
|
140
|
+
400,
|
|
141
|
+
normalizeGridOptions({ rowCount: 4, columnCount: 1, fillIncompleteLastRow: true }),
|
|
142
|
+
'separated',
|
|
143
|
+
[true, true, true],
|
|
144
|
+
)
|
|
145
|
+
expect(oneColumn).toHaveLength(3)
|
|
146
|
+
expect(oneColumn.map((cell) => cell.width)).toEqual([400, 400, 400])
|
|
147
|
+
expect(oneColumn.at(-1)?.isLastRow).toBe(true)
|
|
148
|
+
|
|
149
|
+
const twoColumns = resolveGridCellGeometry(
|
|
150
|
+
400,
|
|
151
|
+
300,
|
|
152
|
+
normalizeGridOptions({ rowCount: 2, columnCount: 2, fillIncompleteLastRow: true }),
|
|
153
|
+
'separated',
|
|
154
|
+
[true, true, true],
|
|
155
|
+
)
|
|
156
|
+
expect(twoColumns).toHaveLength(3)
|
|
157
|
+
expect(twoColumns[2]).toMatchObject({ row: 1, column: 0, left: 0, width: 400 })
|
|
158
|
+
|
|
159
|
+
const threeColumns = resolveGridCellGeometry(
|
|
160
|
+
400,
|
|
161
|
+
300,
|
|
162
|
+
normalizeGridOptions({ rowCount: 2, columnCount: 3, fillIncompleteLastRow: true }),
|
|
163
|
+
'separated',
|
|
164
|
+
[true, true, true, true, true],
|
|
165
|
+
)
|
|
166
|
+
expect(threeColumns).toHaveLength(5)
|
|
167
|
+
expect(threeColumns.slice(3).map((cell) => cell.width)).toEqual([192, 192])
|
|
168
|
+
expect(threeColumns.slice(3).map((cell) => cell.left)).toEqual([0, 208])
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('keeps full pages and the disabled option byte-for-byte compatible', () => {
|
|
172
|
+
const base = normalizeGridOptions({ rowCount: 2, columnCount: 2 })
|
|
173
|
+
const filled = normalizeGridOptions({ rowCount: 2, columnCount: 2, fillIncompleteLastRow: true })
|
|
174
|
+
const fullSlots = [true, true, true, true]
|
|
175
|
+
expect(resolveGridCellGeometry(400, 300, filled, 'independent', fullSlots)).toEqual(
|
|
176
|
+
resolveGridCellGeometry(400, 300, base, 'independent', fullSlots),
|
|
177
|
+
)
|
|
178
|
+
expect(resolveGridCellGeometry(400, 300, base, 'independent', [true, true, true])).toHaveLength(
|
|
179
|
+
4,
|
|
180
|
+
)
|
|
181
|
+
})
|
|
134
182
|
})
|
|
@@ -8,6 +8,7 @@ export interface WaveformGridOptions {
|
|
|
8
8
|
rowCount?: number
|
|
9
9
|
columnCount?: number
|
|
10
10
|
showPagination?: boolean
|
|
11
|
+
fillIncompleteLastRow?: boolean
|
|
11
12
|
trackLines?: WaveformGridTrackLines
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -33,6 +34,7 @@ export interface NormalizedWaveformGridOptions {
|
|
|
33
34
|
rowCount: number
|
|
34
35
|
columnCount: number
|
|
35
36
|
showPagination: boolean
|
|
37
|
+
fillIncompleteLastRow: boolean
|
|
36
38
|
trackLines: Record<string, NormalizedWaveformGridLineOptions>
|
|
37
39
|
}
|
|
38
40
|
|
|
@@ -48,6 +50,7 @@ export interface GridCellGeometry {
|
|
|
48
50
|
plotHeight: number
|
|
49
51
|
cellHeight: number
|
|
50
52
|
xAxisBand: number
|
|
53
|
+
isLastRow?: boolean
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
const normalizeCount = (value: unknown, fallback: number) => {
|
|
@@ -78,6 +81,7 @@ export function normalizeGridOptions(options?: WaveformGridOptions): NormalizedW
|
|
|
78
81
|
rowCount: normalizeCount(options?.rowCount, 2),
|
|
79
82
|
columnCount: normalizeCount(options?.columnCount, 1),
|
|
80
83
|
showPagination: options?.showPagination ?? true,
|
|
84
|
+
fillIncompleteLastRow: options?.fillIncompleteLastRow ?? false,
|
|
81
85
|
trackLines,
|
|
82
86
|
}
|
|
83
87
|
}
|
|
@@ -114,20 +118,23 @@ export function resolveGridCellGeometry(
|
|
|
114
118
|
horizontalGap?: number,
|
|
115
119
|
showXAxis = true,
|
|
116
120
|
): GridCellGeometry[] {
|
|
121
|
+
const pageSize = getPageSize(options)
|
|
122
|
+
const seriesCount = slotHasSeries.filter(Boolean).length
|
|
123
|
+
const filledLastRow = options.fillIncompleteLastRow && seriesCount > 0 && seriesCount < pageSize
|
|
124
|
+
const rowCount = filledLastRow ? Math.ceil(seriesCount / options.columnCount) : options.rowCount
|
|
117
125
|
const defaultGap = getGridGap(displayMode)
|
|
118
126
|
const columnGap = Number.isFinite(horizontalGap)
|
|
119
127
|
? Math.max(0, horizontalGap as number)
|
|
120
128
|
: defaultGap
|
|
121
|
-
const totalHorizontalGap = Math.max(0, options.columnCount - 1) * columnGap
|
|
122
129
|
const axisRows = new Set<number>()
|
|
123
130
|
if (!showXAxis) {
|
|
124
131
|
// Net view uses the full drawing area for waveform pixels.
|
|
125
132
|
} else if (displayMode === 'independent') {
|
|
126
|
-
for (let row = 0; row <
|
|
133
|
+
for (let row = 0; row < rowCount; row += 1) axisRows.add(row)
|
|
127
134
|
} else if (displayMode === 'compact') {
|
|
128
135
|
// Compact tracks share one continuous plot stack. Reserve the X-axis band
|
|
129
136
|
// only beneath the final grid row, including when that row is empty.
|
|
130
|
-
axisRows.add(
|
|
137
|
+
axisRows.add(rowCount - 1)
|
|
131
138
|
} else {
|
|
132
139
|
for (let column = 0; column < options.columnCount; column += 1) {
|
|
133
140
|
for (let slotIndex = getPageSize(options) - 1; slotIndex >= 0; slotIndex -= 1) {
|
|
@@ -139,17 +146,22 @@ export function resolveGridCellGeometry(
|
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
148
|
}
|
|
142
|
-
const totalVerticalGap = Math.max(0,
|
|
149
|
+
const totalVerticalGap = Math.max(0, rowCount - 1) * defaultGap
|
|
143
150
|
const totalAxisBand = axisRows.size * X_AXIS_BAND
|
|
144
|
-
const width = Math.max(1, (innerWidth - totalHorizontalGap) / options.columnCount)
|
|
145
151
|
const plotHeight = Math.max(
|
|
146
152
|
1,
|
|
147
|
-
(innerHeight - totalVerticalGap - totalAxisBand) /
|
|
153
|
+
(innerHeight - totalVerticalGap - totalAxisBand) / rowCount,
|
|
148
154
|
)
|
|
149
155
|
|
|
150
|
-
return Array.from({ length:
|
|
156
|
+
return Array.from({ length: filledLastRow ? seriesCount : pageSize }, (_, slotIndex) => {
|
|
151
157
|
const row = Math.floor(slotIndex / options.columnCount)
|
|
152
158
|
const column = slotIndex % options.columnCount
|
|
159
|
+
const rowSeriesCount =
|
|
160
|
+
filledLastRow && row === rowCount - 1
|
|
161
|
+
? seriesCount - row * options.columnCount
|
|
162
|
+
: options.columnCount
|
|
163
|
+
const rowGap = Math.max(0, rowSeriesCount - 1) * columnGap
|
|
164
|
+
const width = Math.max(1, (innerWidth - rowGap) / rowSeriesCount)
|
|
153
165
|
const xAxisBand = axisRows.has(row) ? X_AXIS_BAND : 0
|
|
154
166
|
const cellHeight = plotHeight + xAxisBand
|
|
155
167
|
const top = Array.from({ length: row }, (_, previousRow) => {
|
|
@@ -167,6 +179,7 @@ export function resolveGridCellGeometry(
|
|
|
167
179
|
plotHeight,
|
|
168
180
|
cellHeight,
|
|
169
181
|
xAxisBand,
|
|
182
|
+
isLastRow: row === rowCount - 1,
|
|
170
183
|
}
|
|
171
184
|
})
|
|
172
185
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
findClosestTrackAtPointer,
|
|
10
10
|
MAX_MULTI_Y_AXIS_COUNT,
|
|
11
11
|
measureYAxisGroupClearance,
|
|
12
|
+
resolveYAxisTickCount,
|
|
12
13
|
} from './layout'
|
|
13
14
|
|
|
14
15
|
function series(id: string, minimum: number, maximum: number): DisplaySeries {
|
|
@@ -64,7 +65,13 @@ function layoutForSeries(
|
|
|
64
65
|
series: sourceTrack,
|
|
65
66
|
},
|
|
66
67
|
],
|
|
67
|
-
grid: {
|
|
68
|
+
grid: {
|
|
69
|
+
rowCount: 1,
|
|
70
|
+
columnCount: 1,
|
|
71
|
+
showPagination: false,
|
|
72
|
+
fillIncompleteLastRow: false,
|
|
73
|
+
trackLines: {},
|
|
74
|
+
},
|
|
68
75
|
displayMode: 'independent',
|
|
69
76
|
overlayMode: 'single-axis',
|
|
70
77
|
independentTransforms: [transform],
|
|
@@ -106,7 +113,13 @@ describe('multi-value Y-axis grouping', () => {
|
|
|
106
113
|
series: sourceTrack,
|
|
107
114
|
},
|
|
108
115
|
],
|
|
109
|
-
grid: {
|
|
116
|
+
grid: {
|
|
117
|
+
rowCount: 1,
|
|
118
|
+
columnCount: 1,
|
|
119
|
+
showPagination: false,
|
|
120
|
+
fillIncompleteLastRow: false,
|
|
121
|
+
trackLines: {},
|
|
122
|
+
},
|
|
110
123
|
displayMode: 'independent',
|
|
111
124
|
overlayMode: 'single-axis',
|
|
112
125
|
independentTransforms: [zoomIdentity],
|
|
@@ -119,8 +132,15 @@ describe('multi-value Y-axis grouping', () => {
|
|
|
119
132
|
showCompactEmptyTracks: false,
|
|
120
133
|
})[0]
|
|
121
134
|
|
|
122
|
-
expect(result?.yScale.domain()).toEqual([
|
|
123
|
-
expect(result?.seriesPaths[0]?.yScale.domain()).toEqual([
|
|
135
|
+
expect(result?.yScale.domain()).toEqual([20, 80])
|
|
136
|
+
expect(result?.seriesPaths[0]?.yScale.domain()).toEqual([20, 80])
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('normalizes configured Y-axis split counts', () => {
|
|
140
|
+
expect(resolveYAxisTickCount(100, 0)).toBe(2)
|
|
141
|
+
expect(resolveYAxisTickCount(100, 4.8)).toBe(4)
|
|
142
|
+
expect(resolveYAxisTickCount(100, Number.NaN)).toBe(5)
|
|
143
|
+
expect(resolveYAxisTickCount(220)).toBe(5)
|
|
124
144
|
})
|
|
125
145
|
|
|
126
146
|
it('keeps every overlaid series on one axis in single-axis mode', () => {
|
|
@@ -209,7 +229,13 @@ describe('multi-value Y-axis grouping', () => {
|
|
|
209
229
|
series: track([series('left', 1000, 3000), series('right', 1000, 3000)]),
|
|
210
230
|
},
|
|
211
231
|
],
|
|
212
|
-
grid: {
|
|
232
|
+
grid: {
|
|
233
|
+
rowCount: 1,
|
|
234
|
+
columnCount: 1,
|
|
235
|
+
showPagination: false,
|
|
236
|
+
fillIncompleteLastRow: false,
|
|
237
|
+
trackLines: {},
|
|
238
|
+
},
|
|
213
239
|
displayMode: 'independent',
|
|
214
240
|
overlayMode: 'multi-axis',
|
|
215
241
|
independentTransforms: [zoomIdentity],
|