| 695 | }; |
| 696 | |
| 697 | const createCoordinatorZoomStateLike = (): ZoomState => { |
| 698 | const getRange: ZoomState['getRange'] = () => coordinator?.getZoomRange() ?? { start: 0, end: 100 }; |
| 699 | const setRange: ZoomState['setRange'] = (start, end) => { |
| 700 | coordinator?.setZoomRange(start, end); |
| 701 | }; |
| 702 | const zoomIn: ZoomState['zoomIn'] = (center, factor) => { |
| 703 | if (!Number.isFinite(center) || !Number.isFinite(factor) || factor <= 1) return; |
| 704 | const r = coordinator?.getZoomRange(); |
| 705 | if (!r) return; |
| 706 | const c = clamp(center, 0, 100); |
| 707 | const ratio = computeZoomInOutAnchorRatio(r, c); |
| 708 | const span = r.end - r.start; |
| 709 | const nextSpan = span / factor; |
| 710 | const nextStart = c - ratio * nextSpan; |
| 711 | coordinator?.setZoomRange(nextStart, nextStart + nextSpan); |
| 712 | }; |
| 713 | const zoomOut: ZoomState['zoomOut'] = (center, factor) => { |
| 714 | if (!Number.isFinite(center) || !Number.isFinite(factor) || factor <= 1) return; |
| 715 | const r = coordinator?.getZoomRange(); |
| 716 | if (!r) return; |
| 717 | const c = clamp(center, 0, 100); |
| 718 | const ratio = computeZoomInOutAnchorRatio(r, c); |
| 719 | const span = r.end - r.start; |
| 720 | const nextSpan = span * factor; |
| 721 | const nextStart = c - ratio * nextSpan; |
| 722 | coordinator?.setZoomRange(nextStart, nextStart + nextSpan); |
| 723 | }; |
| 724 | const pan: ZoomState['pan'] = (delta) => { |
| 725 | if (!Number.isFinite(delta)) return; |
| 726 | const r = coordinator?.getZoomRange(); |
| 727 | if (!r) return; |
| 728 | coordinator?.setZoomRange(r.start + delta, r.end + delta); |
| 729 | }; |
| 730 | const onChange: ZoomState['onChange'] = (callback) => coordinator?.onZoomRangeChange(callback) ?? (() => {}); |
| 731 | |
| 732 | return { getRange, setRange, zoomIn, zoomOut, pan, onChange }; |
| 733 | }; |
| 734 | |
| 735 | const syncDataZoomUi = (): void => { |
| 736 | const shouldHaveSlider = hasSliderDataZoom(currentOptions); |