Skip to content

Improve PivotViewer: faster pinch zoom and fix grouped-mode fly-to animation#17

Merged
einari merged 4 commits intomainfrom
copilot/improve-pivotviewer-features
Mar 7, 2026
Merged

Improve PivotViewer: faster pinch zoom and fix grouped-mode fly-to animation#17
einari merged 4 commits intomainfrom
copilot/improve-pivotviewer-features

Conversation

Copy link
Contributor

Copilot AI commented Mar 3, 2026

Addresses two UX issues raised in review: trackpad pinch zoom was too sluggish, and clicking a card in grouped mode failed to animate the camera to it.

Fixed

  • Grouped-mode fly-to broken: Both useCardSelection and useDetailPanelClose resolved the scroll container via DOM traversal (.pv-main > parentElement), which returned the outer .pivot-viewer wrapper — a non-scrollable element. All scroll reads/writes were silently no-ops. Both hooks now receive containerRef directly and use containerRef.current (the actual .pv-viewport scrollable element).

  • Spacer always null during zoom animation: useCardSelection was finding the animation spacer via container.querySelector('.pv-spacer') — no element with that class exists, so the spacer was always null. This caused incorrect scroll-range clamping during zoom animations. Hook now accepts spacerRef and uses spacerRef.current directly.

  • Pinch zoom too slow: Trackpad pixel-mode factor increased 0.003 → 0.01; mouse-wheel line-mode factor increased 0.05 → 0.12 for a snappier infinite-canvas feel.

Original prompt

This section details on the original issue you should resolve

<issue_title>Improve PivotViewer</issue_title>
<issue_description>Lets improve the PivotViewer

  • When in "Collection" view mode - the content and details and the should fill the height of its container, when switching to "Grouped", we can animate / fade in the titles at the bottom.
  • Implement trackpad support for pinch to zoom
  • When an event is selected, it should always "fly to it" (animate) and keep the event centered in both view modes</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

…-to animation

- Collection view now fills full container height by collapsing axis labels bar to 0 height (max-height/padding/border-color CSS transitions)
- Grouped view animates axis labels at bottom with a 600ms fade-in + expand effect
- Trackpad pinch-to-zoom uses normalized delta factor (0.003 for pixel-mode, 0.05 for line-mode) for consistent feel
- Selecting an event now uses animateZoomAndScroll in both collection and grouped view modes for a smooth 'fly to' animation that centers the card

Co-authored-by: einari <134365+einari@users.noreply.github.com>
Copilot AI changed the title [WIP] Improve PivotViewer with new features and animations Improve PivotViewer: height filling, trackpad pinch zoom, and fly-to selection animation Mar 3, 2026
@einari
Copy link
Contributor

einari commented Mar 3, 2026

@copilot Great. The pinch to zoom can be a bit "faster" - greater steps. Get the same feeling as an infinite canvas Miro type of application.

Also, as you can see in the GIF, when I'm in for instance Group mode and click an event, it doesn't "move the camera" - scroll to it as I would expect.

2026-03-03_10-48-03 (1)

Remember, we should aim at keeping the selected card in center at all times.

- Increase wheel zoom factors for Miro-like responsiveness:
  - DOM_DELTA_PIXEL (trackpad): 0.003 → 0.01 (~3x faster)
  - DOM_DELTA_LINE (mouse wheel): 0.05 → 0.12 (~2.5x faster)
- Fix grouped-view card selection: both useCardSelection and useDetailPanelClose
  were computing the scroll container incorrectly (finding .pv-main parent instead
  of the actual scrollable .pv-viewport). Pass containerRef directly to both hooks
  and use containerRef.current as the scroll target.

Co-authored-by: einari <134365+einari@users.noreply.github.com>
Copilot AI changed the title Improve PivotViewer: height filling, trackpad pinch zoom, and fly-to selection animation Improve PivotViewer: faster pinch-to-zoom and fix grouped-view fly-to animation Mar 3, 2026
…range

Co-authored-by: einari <134365+einari@users.noreply.github.com>
Copilot AI changed the title Improve PivotViewer: faster pinch-to-zoom and fix grouped-view fly-to animation Improve PivotViewer: faster pinch zoom and fix grouped-mode fly-to animation Mar 3, 2026
@einari einari requested a review from Copilot March 4, 2026 05:25
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves PivotViewer interaction fidelity by making zoom gestures feel more responsive and fixing “fly-to” camera animations in grouped mode by using the actual scrollable viewport and a real spacer element during animations.

Changes:

  • Normalize wheel/pinch zoom deltas by deltaMode to speed up trackpad pinch and mouse-wheel zoom.
  • Fix grouped-mode fly-to by passing containerRef (scrollable .pv-viewport) and spacerRef directly into selection/close hooks instead of relying on DOM traversal/querying.
  • Update axis label styling to fade and collapse when switching views.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
Source/PivotViewer/utils/selection.ts Uses animateZoomAndScroll to animate scroll-to-center (including collection mode / subsequent selections).
Source/PivotViewer/hooks/useWheelZoom.ts Adjusts zoom delta scaling based on WheelEvent.deltaMode for snappier pinch/wheel zoom.
Source/PivotViewer/hooks/useDetailPanelClose.ts Switches to containerRef.current so grouped-mode close can scroll/animate the correct viewport.
Source/PivotViewer/hooks/useCardSelection.ts Switches to containerRef.current and spacerRef.current to ensure fly-to uses real scroll container + spacer.
Source/PivotViewer/PivotViewer.tsx Threads containerRef/spacerRef into the updated hooks.
Source/PivotViewer/PivotViewer.css Collapses axis-label area (max-height/padding/border) in addition to opacity fade.
Comments suppressed due to low confidence (2)

Source/PivotViewer/utils/selection.ts:117

  • In collection mode this path uses animateZoomAndScroll with startZoom === targetZoom, which still runs a full rAF animation loop and calls setZoomLevel on every frame even though the zoom never changes. To reduce unnecessary work/state scheduling, consider using smoothScrollTo here (as before) or adjusting the animation helper so it can animate scroll-only without repeatedly invoking onUpdate when zoom is unchanged.
    if (viewMode === 'collection') {
      // Collection mode: animate scroll to center the selected card (no zoom change)
      if (cardPosition) {
        setIsZooming(true);
        const { scrollLeft, scrollTop } = calculateCenterScrollPosition(container, cardPosition, zoomLevel, 0, totalHeight);
        animateZoomAndScroll({
          container,
          cardPosition,
          startZoom: zoomLevel,
          targetZoom: zoomLevel,
          targetScrollLeft: scrollLeft,
          targetScrollTop: scrollTop,
          onUpdate: setZoomLevel,
          onComplete: () => setIsZooming(false),
        });

Source/PivotViewer/utils/selection.ts:151

  • For subsequent selections, animateZoomAndScroll is also used with startZoom === targetZoom (scroll-only), which means the helper still schedules rAF frames and calls setZoomLevel each frame despite no zoom delta. Consider switching this to smoothScrollTo (or a scroll-only animation path) to avoid extra per-frame state work.
    // Subsequent selections: animate the scroll to center the new card
    if (cardPosition) {
      setIsZooming(true);
      const detailWidth = viewMode === 'collection' ? 0 : DETAIL_PANEL_WIDTH;
      const { scrollLeft, scrollTop } = calculateCenterScrollPosition(container, cardPosition, zoomLevel, detailWidth, totalHeight);
      animateZoomAndScroll({
        container,
        cardPosition,
        startZoom: zoomLevel,
        targetZoom: zoomLevel,
        targetScrollLeft: scrollLeft,
        targetScrollTop: scrollTop,
        onUpdate: setZoomLevel,
        onComplete: () => setIsZooming(false),
      });

Comment on lines +50 to 54
// Use the containerRef directly as the scrollable viewport
const container = containerRef.current;
if (!container || !selectedItem) {
setSelectedItem(null);
return;
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useDetailPanelClose now correctly uses containerRef.current, but the zoom-out animation later calls animateZoomAndScroll without providing getLayoutSizeAtZoom (available from createCardPositionCallbacks) and without a spacer element to resize during the animation. This can leave scrollWidth/scrollHeight out of sync with the animated zoom, causing clamped/jumpy scroll during the fly-to. Consider passing getLayoutSizeAtZoom: callbacks.getLayoutSizeAtZoom and threading a spacerRef into this hook (similar to useCardSelection) so the spacer can be updated synchronously during the animation.

Copilot uses AI. Check for mistakes.
@einari einari marked this pull request as ready for review March 7, 2026 14:25
@einari einari merged commit 268f4d1 into main Mar 7, 2026
4 checks passed
@einari einari deleted the copilot/improve-pivotviewer-features branch March 7, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve PivotViewer

3 participants