From 6197126272d46f9044338ff96bcb3caa69b8d357 Mon Sep 17 00:00:00 2001 From: Gael Leblan Date: Wed, 28 Aug 2024 16:33:25 +0200 Subject: [PATCH] [frontend] Interactive Timeline Display --- .../src/components/ChainedTimeline.tsx | 96 ++++++++++--------- .../src/components/CustomTimelinePanel.tsx | 8 +- openbas-front/src/static/css/index.css | 4 + 3 files changed, 62 insertions(+), 46 deletions(-) diff --git a/openbas-front/src/components/ChainedTimeline.tsx b/openbas-front/src/components/ChainedTimeline.tsx index e9ceba9d6c..c054726810 100644 --- a/openbas-front/src/components/ChainedTimeline.tsx +++ b/openbas-front/src/components/ChainedTimeline.tsx @@ -33,7 +33,7 @@ import NodePhantom from './nodes/NodePhantom'; const useStyles = makeStyles(() => ({ container: { - marginTop: 60, + marginTop: 30, paddingRight: 40, }, rotatedIcon: { @@ -73,6 +73,7 @@ const ChainedTimelineFlow: FunctionComponent = ({ injects, exerciseOrScen const [currentUpdatedNode, setCurrentUpdatedNode] = useState(null); const [currentMousePosition, setCurrentMousePosition] = useState({ x: 0, y: 0 }); const [newNodeCursorVisibility, setNewNodeCursorVisibility] = useState<'visible' | 'hidden'>('visible'); + const [newNodeCursorClickable, setNewNodeCursorClickable] = useState(true); const [currentMouseTime, setCurrentMouseTime] = useState(''); let timer: NodeJS.Timeout; @@ -221,32 +222,42 @@ const ChainedTimelineFlow: FunctionComponent = ({ injects, exerciseOrScen }; const onNodePhantomClick = (event: React.MouseEvent) => { - const position = reactFlow.screenToFlowPosition({ x: event.clientX, y: event.clientY }); - - const totalMinutes = moment.duration((position.x / gapSize) * minutesPerGapAllowed[minutesPerGapIndex] * 60, 's'); - openCreateInjectDrawer({ - inject_depends_duration_days: totalMinutes.days(), - inject_depends_duration_hours: totalMinutes.hours(), - inject_depends_duration_minutes: totalMinutes.minutes(), - }); + if (newNodeCursorClickable) { + const position = reactFlow.screenToFlowPosition({ x: event.clientX, y: event.clientY }); + + const totalMinutes = moment.duration((position.x / gapSize) * minutesPerGapAllowed[minutesPerGapIndex] * 60, 's'); + openCreateInjectDrawer({ + inject_depends_duration_days: totalMinutes.days(), + inject_depends_duration_hours: totalMinutes.hours(), + inject_depends_duration_minutes: totalMinutes.minutes(), + }); + } }; const onMouseMove = (eventMove: React.MouseEvent) => { clearTimeout(timer); if (!draggingOnGoing) { - eventMove.persist(); const position = reactFlow.screenToFlowPosition({ x: eventMove.clientX, y: eventMove.clientY }, { snapToGrid: false }); const sidePosition = reactFlow.screenToFlowPosition({ x: eventMove.clientX - 25, y: eventMove.clientY }, { snapToGrid: false }); const viewPort = reactFlow.getViewport(); - setCurrentMousePosition({ x: ((position.x * reactFlow.getZoom()) + viewPort.x - 25), y: ((position.y * reactFlow.getZoom()) + viewPort.y + 25) }); - const momentOfTime = moment.utc( - moment.duration(convertCoordinatesToTime( - { x: sidePosition.x, y: sidePosition.y }, - ), 's').asMilliseconds(), - ); - - setCurrentMouseTime(`${momentOfTime.dayOfYear() - 1} d, ${momentOfTime.hour()} h, ${momentOfTime.minute()} m`); + setCurrentMousePosition({ x: ((position.x * reactFlow.getZoom()) + viewPort.x - 25), y: ((position.y * reactFlow.getZoom()) + viewPort.y - 25) }); + + if (startDate === undefined) { + const momentOfTime = moment.utc( + moment.duration(convertCoordinatesToTime( + { x: sidePosition.x, y: sidePosition.y }, + ), 's').asMilliseconds(), + ); + + setCurrentMouseTime(`${momentOfTime.dayOfYear() - 1} d, ${momentOfTime.hour()} h, ${momentOfTime.minute()} m`); + } else { + const momentOfTime = moment.utc(startDate) + .add(-new Date().getTimezoneOffset() / 60, 'h') + .add(convertCoordinatesToTime({ x: sidePosition.x, y: sidePosition.y }), 's'); + + setCurrentMouseTime(momentOfTime.format('MMMM Do, YYYY - h:mmA')); + } } }; @@ -256,10 +267,12 @@ const ChainedTimelineFlow: FunctionComponent = ({ injects, exerciseOrScen const nodeMouseEnter = () => { setNewNodeCursorVisibility('hidden'); + setNewNodeCursorClickable(false); }; const nodeMouseLeave = () => { setNewNodeCursorVisibility('visible'); + setNewNodeCursorClickable(true); }; const updateMinutesPerGap = (incrementIndex: number) => { @@ -275,17 +288,6 @@ const ChainedTimelineFlow: FunctionComponent = ({ injects, exerciseOrScen <> {injects.length > 0 ? (
-
- -
= ({ injects, exerciseOrScen nodeExtent={[[0, 0], [Infinity, Infinity]]} defaultViewport={{ x: 60, y: 50, zoom: 0.75 }} minZoom={0.3} + onClick={onNodePhantomClick} > +
+ +
= ({ injects, exerciseOrScen
-
- - -
+ +
) : null diff --git a/openbas-front/src/components/CustomTimelinePanel.tsx b/openbas-front/src/components/CustomTimelinePanel.tsx index 842ddedc16..62cee01084 100644 --- a/openbas-front/src/components/CustomTimelinePanel.tsx +++ b/openbas-front/src/components/CustomTimelinePanel.tsx @@ -61,16 +61,18 @@ function BackgroundComponent({ dateIndex: Math.round(date.unix() / (minutesPerGap * 3 * 60)), }); } else { - const date = moment.utc(startDate) + const beginningDate = moment.utc(startDate) + .add(-new Date().getTimezoneOffset() / 60, 'h'); + const date = moment.utc(beginningDate) .add((minutesPerGap * 3 * i) + offset, 'm'); newParsedDates.push({ parsedDate: date.format('MMMM Do, YYYY - h:mmA'), - dateIndex: Math.round(date.unix() / (minutesPerGap * 3 * 60)), + dateIndex: Math.round((date.unix() - beginningDate.unix()) / (minutesPerGap * 3 * 60)), }); } } setParsedDates(newParsedDates); - }, [viewportData, minutesPerGap]); + }, [viewportData, minutesPerGap, startDate]); return ( diff --git a/openbas-front/src/static/css/index.css b/openbas-front/src/static/css/index.css index 01dbaa35ba..45d1ad16e8 100644 --- a/openbas-front/src/static/css/index.css +++ b/openbas-front/src/static/css/index.css @@ -155,4 +155,8 @@ input[type="file"] { .react-flow__controls { border: 1px solid #b5b7ba; +} + +.react-flow__pane.draggable:not(.react-flow__pane.draggable.dragging) { + cursor: none !important; } \ No newline at end of file