Skip to content

Commit

Permalink
Animate upon scroll, manage zoom resize events
Browse files Browse the repository at this point in the history
Contributes: #71
  • Loading branch information
stephanzwicknagl committed Apr 2, 2024
1 parent 40477f5 commit 269d19c
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 93 deletions.
8 changes: 1 addition & 7 deletions frontend/src/lib/components/DragHandle.react.js
Expand Up @@ -3,19 +3,17 @@ import './draghandle.css';
import PropTypes from 'prop-types';
import dragHandleRounded from '@iconify/icons-material-symbols/drag-handle-rounded';
import {IconWrapper} from '../LazyLoader';
import {MAPZOOMSTATE} from '../types/propTypes';

export class DragHandle extends React.Component {
constructor(props) {
super(props);
}

render() {
const {dragHandleProps, transform} = this.props;
const {dragHandleProps} = this.props;
return (
<div
className="dragHandle"
style={{left: `${-transform.translation.x}px`}}
{...dragHandleProps}
>
<React.Suspense fallback={<div>=</div>}>
Expand All @@ -32,9 +30,5 @@ DragHandle.propTypes = {
* The whole item will be draggable by the wrapped element.
**/
dragHandleProps: PropTypes.object,
/**
* The current zoom transformation of the graph
*/
transform: MAPZOOMSTATE,
};

16 changes: 8 additions & 8 deletions frontend/src/lib/components/Row.react.js
Expand Up @@ -279,26 +279,26 @@ export function Row(props) {
return (
<div
className={`row_container ${transformation.hash}`}
style={{
width: `${transform.scale * 100}%`,
transform: `translateX(${transform.translation.x}px)`,
}}
>
>
{transformation.rules.length === 0 ? null : (
<RowHeader
transformation={transformation.rules}
transform={transform}
/>
)}
{dragHandleProps === null ? null : (
<DragHandle
ref={handleRef}
dragHandleProps={dragHandleProps}
transform={transform}
/>
)}
{!showNodes ? null : (
<div ref={rowbodyRef} className="row_row">
<div ref={rowbodyRef}
className="row_row"
style={{
width: `${nodes.length === 1 ? 100 : transform.scale * 100}%`,
transform: `translateX(${nodes.length === 1 ? 0 : transform.translation.x}px)`,
}}
>
{nodes.map((child, index) => {
const space_multiplier = child.space_multiplier * 100;
if (
Expand Down
8 changes: 1 addition & 7 deletions frontend/src/lib/components/RowHeader.react.js
@@ -1,10 +1,9 @@
import React from "react";
import PropTypes from "prop-types";
import {useColorPalette} from "../contexts/ColorPalette";
import {MAPZOOMSTATE} from "../types/propTypes";

export function RowHeader(props) {
const { transformation, transform } = props;
const { transformation } = props;
const colorPalette = useColorPalette();


Expand All @@ -14,7 +13,6 @@ export function RowHeader(props) {
backgroundColor: colorPalette.primary,
color: colorPalette.light,
borderColor: colorPalette.primary,
left: `${-transform.translation.x}px`
}}
className="row_header"
>
Expand All @@ -39,8 +37,4 @@ RowHeader.propTypes = {
* The rule string to be displayed in the header
*/
transformation: PropTypes.arrayOf(PropTypes.string),
/**
* The current zoom transformation of the graph
*/
transform: MAPZOOMSTATE,
};
169 changes: 100 additions & 69 deletions frontend/src/lib/main/ViaspDash.react.js
Expand Up @@ -49,7 +49,7 @@ import {
import DraggableList from 'react-draggable-list';
import {MapInteraction} from 'react-map-interaction';
import useResizeObserver from '@react-hook/resize-observer';
import useWindowResizeObserver from '../hooks/windowResizeObserver';
import debounce from 'lodash.debounce';

function postCurrentSort(backendURL, hash) {
return fetch(`${backendURL('graph/sorts')}`, {
Expand Down Expand Up @@ -158,7 +158,7 @@ function MainWindow(props) {
const [ctrlPressed, setCtrlPressed] = React.useState(false);
const [translationBounds, setTranslationBounds] = React.useState({
scale: 1,
translation: {xMin: 0, xMax: 0}
translation: {xMin: 0, xMax: 0},
});
const [mapShiftValue, setMapShiftValue] = React.useState({
translation: {x: 0, y: 0},
Expand Down Expand Up @@ -214,53 +214,43 @@ function MainWindow(props) {
window.removeEventListener('keyup', handleKeyUp);
};
}, []);

// Manage Graph Zoom
const {shownDetail} = useShownDetail();
const prevShownDetail = React.useRef(null);
const detailOpenWidthRatio = parseFloat(
const detailOpenWidthRatio =
parseFloat(
getComputedStyle(document.documentElement).getPropertyValue(
'--detail-open-width'
)
) / 100;
)
) / 100;
const detailClosedShiftThreshold = 0.2;
const detailOpenShiftThreshold = 0.05;


// React.useEffect(() => {
// if (shownDetail !== null) {
// setMapMinScale(1 - detailOpenWidthRatio);
// } else {
// setMapMinScale(1);
// }
// }, [shownDetail, detailOpenWidthRatio]);

const handleMapChange = ({translation, scale}) => {
if (ctrlPressed) {
const newTranslation = {...translation};
let newScale = scale;
let translationBounds = {};


const contentWidth = contentDivRef.current.clientWidth;
if (shownDetail !== null) {
const detailOpenWidth = detailOpenWidthRatio * contentWidth;
translationBounds = {
scale: 1 - detailOpenWidthRatio,
translation: {
xMax: 0,
xMin: (1 - scale) * contentWidth - detailOpenWidth,
},
};
} else {
translationBounds = {
scale: 1,
translation: {
xMax: 0,
xMin: (1 - scale) * contentWidth,
},
};
};
setTranslationBounds(translationBounds);
const rowWidth = contentWidth * newScale;
const detailOpenWidth =
shownDetail === null
? 0
: detailOpenWidthRatio * contentWidth;
setTranslationBounds({
scale: shownDetail === null
? 1
: 1 - detailOpenWidthRatio,
translation: {
xMax: 0,
xMin:
contentWidth -
rowWidth -
detailOpenWidth,
},
});

setMapShiftValue(() => {
if (newTranslation.x < translationBounds.translation.xMin) {
Expand All @@ -279,10 +269,10 @@ function MainWindow(props) {
});
}
};

const handleMapChangeOnDetailChange = React.useCallback(() => {
if (shownDetail === null && prevShownDetail.current !== null) {
// close a potential gap between the right border of the graph
// close a potential gap between the right border of the graph
// and the right border of the content div
setMapShiftValue((oldShiftValue) => {
const rowWidth =
Expand Down Expand Up @@ -319,17 +309,19 @@ function MainWindow(props) {
}
if (shownDetail !== null && prevShownDetail.current === null) {
// if the graph is shifted all the way to the left
// and the zoom scale is > 1, shift the graph to the left
// and the zoom scale is > 1, shift the graph to the left
setMapShiftValue((oldShiftValue) => {
const rowWidth = contentDivRef.current.clientWidth * oldShiftValue.scale;
const rowWidth =
contentDivRef.current.clientWidth * oldShiftValue.scale;
const distanceOfRightSideOfGraphFromRightBorder =
rowWidth -
contentDivRef.current.clientWidth +
oldShiftValue.translation.x
oldShiftValue.translation.x;
if (
oldShiftValue.scale === 1 ||
distanceOfRightSideOfGraphFromRightBorder >=
detailOpenShiftThreshold * contentDivRef.current.clientWidth
detailOpenShiftThreshold *
contentDivRef.current.clientWidth
) {
return {...oldShiftValue};
}
Expand All @@ -348,39 +340,42 @@ function MainWindow(props) {
}
prevShownDetail.current = shownDetail;
}, [setMapShiftValue, shownDetail, detailOpenWidthRatio]);

React.useEffect(() => {
handleMapChangeOnDetailChange();
}, [shownDetail, handleMapChangeOnDetailChange]);

const animateResize = React.useCallback(() => {
if (shownDetail) {
setMapShiftValue((oldShiftValue) => {
const contentWidth = contentDivRef.current.clientWidth;
const detailWidth = detailOpenWidthRatio * contentWidth;
const rowWidth = contentWidth * oldShiftValue.scale;
const distanceOfRightSideOfGraphFromRightBorder =
contentWidth - rowWidth - oldShiftValue.translation.x;
if (
detailWidth - distanceOfRightSideOfGraphFromRightBorder >=
detailOpenShiftThreshold * contentWidth
) {
return {...oldShiftValue};
}
return {
...oldShiftValue,
translation: {
...oldShiftValue.translation,
x:
contentWidth -
rowWidth -
detailWidth,
},
};
});
}
}, [shownDetail, detailOpenWidthRatio]);
useResizeObserver(contentDivRef, animateResize);
const minBound = translationBounds.translation.xMin;
setMapShiftValue((oldShiftValue) => {
const contentWidth = contentDivRef.current.clientWidth;
const detailWidth = shownDetail === null
? 0
: detailOpenWidthRatio * contentWidth;
const rowWidth = contentWidth * oldShiftValue.scale;
if (
minBound + oldShiftValue.translation.x >=
detailOpenShiftThreshold * contentWidth
) {
return {...oldShiftValue};
}
return {
...oldShiftValue,
translation: {
...oldShiftValue.translation,
x:
contentWidth -
rowWidth -
detailWidth,
},
};
});
}, [shownDetail, detailOpenWidthRatio, translationBounds]);

const debouncedAnimateResize = React.useMemo(
() => debounce(animateResize, 100)
, [animateResize]);
useResizeObserver(contentDivRef, debouncedAnimateResize);

React.useEffect(() => {
setAnimationState((oldValue) => ({
Expand All @@ -395,6 +390,42 @@ function MainWindow(props) {
}));
}, [mapShiftValue, setAnimationState]);

// observe scroll position
// Add a state for the scroll position
const [scrollPosition, setScrollPosition] = React.useState(0);

// Add an effect to update the scroll position state when the contentDiv scrolls
React.useEffect(() => {
const contentDiv = contentDivRef.current;
const handleScroll = () => {
setScrollPosition(contentDiv.scrollTop);
};

if (contentDiv) {
contentDiv.addEventListener('scroll', handleScroll);
}

return () => {
if (contentDiv) {
contentDiv.removeEventListener('scroll', handleScroll);
}
};
}, [contentDivRef]);

// Add an effect to update the animation state when the scroll position changes
React.useEffect(() => {
setAnimationState((oldValue) => ({
...oldValue,
graph_zoom: {
...oldValue.graph_zoom,
translation: {
...oldValue.translation,
x: scrollPosition,
},
},
}));
}, [scrollPosition, setAnimationState]);

return (
<>
<Detail />
Expand Down
2 changes: 1 addition & 1 deletion frontend/viasp_dash/viasp_dash.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion frontend/viasp_dash/viasp_dash.min.js.map

Large diffs are not rendered by default.

0 comments on commit 269d19c

Please sign in to comment.