Skip to content

Commit 2c65951

Browse files
committed
Merge branch 'zoom-dropdown' of Arnei/opencast-editor into r/18.x
Pull request #1593 Add Zoom dropdown
2 parents c7a55c5 + 3fe134e commit 2c65951

File tree

3 files changed

+89
-10
lines changed

3 files changed

+89
-10
lines changed

src/main/CuttingActions.tsx

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react";
22

3-
import { BREAKPOINTS, basicButtonStyle, customIconStyle, undisplay } from "../cssStyles";
3+
import { BREAKPOINTS, basicButtonStyle, customIconStyle, selectFieldStyle, undisplay } from "../cssStyles";
44

55
import { IconType } from "react-icons";
66
import { LuScissors, LuChevronLeft, LuChevronRight, LuTrash, LuMoveHorizontal } from "react-icons/lu";
@@ -15,6 +15,8 @@ import {
1515
mergeAll,
1616
mergeLeft,
1717
mergeRight,
18+
selectDisplayDuration,
19+
selectDurationInSeconds,
1820
selectIsCurrentSegmentAlive,
1921
selectTimelineZoom,
2022
setTimelineZoom,
@@ -30,6 +32,7 @@ import { ThemedTooltip } from "./Tooltip";
3032
import { Slider } from "@mui/material";
3133
import { useHotkeys } from "react-hotkeys-hook";
3234
import { ProtoButton } from "@opencast/appkit";
35+
import Select, { components } from "react-select";
3336

3437
/**
3538
* Defines the different actions a user can perform while in cutting mode
@@ -179,6 +182,7 @@ const CuttingActions: React.FC = () => {
179182
hotkeyNameOut: rewriteKeys(KEYMAP.cutting.zoomOut),
180183
})}
181184
/>
185+
<ZoomDropdown />
182186
{/* <CuttingActionsButton Icon={faQuestion} actionName="Reset changes" action={null}
183187
tooltip="Not implemented"
184188
ariaLabelText="Reset changes. Not implemented"
@@ -365,4 +369,75 @@ const ZoomSlider : React.FC<ZoomSliderInterface> = ({
365369
);
366370
};
367371

372+
const ZoomDropdown : React.FC = () => {
373+
const theme = useTheme();
374+
const dispatch = useAppDispatch();
375+
const seconds = useAppSelector(selectDisplayDuration);
376+
const durationInSeconds = useAppSelector(selectDurationInSeconds);
377+
378+
const options = [
379+
{
380+
label: "All",
381+
value: "0",
382+
},
383+
];
384+
if (durationInSeconds > 60) {
385+
options.push({
386+
label: "10 s",
387+
value: "1",
388+
});
389+
}
390+
if (durationInSeconds > 60 * 10) {
391+
options.push({
392+
label: "1 m",
393+
value: (1 - (60 / durationInSeconds)).toString(),
394+
});
395+
}
396+
if (durationInSeconds > 300 * 5) {
397+
options.push({
398+
label: "5 m",
399+
value: (1 - (300 / durationInSeconds)).toString(),
400+
});
401+
}
402+
403+
const renderTime = (seconds: number) => {
404+
const minutes = seconds / 60;
405+
const hours = seconds / 3600;
406+
407+
if (hours >= 1) {
408+
return Math.round(hours) + " h";
409+
}
410+
if (minutes >= 1) {
411+
return Math.round(minutes) + " m";
412+
}
413+
414+
return Math.round(seconds) + " s";
415+
};
416+
417+
// @ts-expect-error No proper type for children available
418+
const Control = ({ children, ...props }) => (
419+
// @ts-expect-error And therefore this complains as well
420+
<components.Control {...props}>
421+
<div style={{ paddingLeft: "5px", paddingRight: "5px" }}>
422+
~{renderTime(seconds)}
423+
</div>
424+
{children[1]}
425+
</components.Control>
426+
);
427+
428+
return (
429+
<Select
430+
name="Zoom Dropdown"
431+
styles={selectFieldStyle(theme)}
432+
options={options}
433+
onChange={option => {
434+
if (option) {
435+
dispatch(setTimelineZoom(parseFloat(option.value)));
436+
}
437+
}}
438+
components={{ Control }}
439+
/>
440+
);
441+
};
442+
368443
export default CuttingActions;

src/main/Timeline.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
selectTimelineZoom,
1818
moveCut,
1919
selectDurationInSeconds,
20+
selectDisplayDuration,
2021
} from "../redux/videoSlice";
2122

2223
import { LuMenu } from "react-icons/lu";
@@ -67,13 +68,13 @@ const Timeline: React.FC<{
6768
const duration = useAppSelector(selectDuration);
6869
const durationInSeconds = useAppSelector(selectDurationInSeconds);
6970
const timelineZoom = useAppSelector(selectTimelineZoom);
71+
const displayDuration = useAppSelector(selectDisplayDuration);
7072

7173
const scrubberRef = useRef<HTMLDivElement | null>(null);
7274
const { ref, width = 1 } = useResizeObserver<HTMLDivElement>();
7375
const scrollContainerRef = useRef<HTMLElement>(null);
7476
const { width: scrollContainerWidth = 1 } = useResizeObserver<HTMLElement>({ ref: scrollContainerRef });
7577
const topOffset = 20;
76-
const minDisplayTime = 10; // in seconds, what is shown at max zoom
7778

7879
const currentlyScrolling = useRef(false);
7980
const zoomCenter = useRef(0);
@@ -92,15 +93,11 @@ const Timeline: React.FC<{
9293
zoomCenter.current = (scrubberVisible ? scrubberPosition : centerPosition) / width;
9394
};
9495

95-
const getDisplayPercentage = (zoomValue: number) => {
96-
const displayDuration = (1 - zoomValue) * (durationInSeconds - minDisplayTime) + minDisplayTime;
97-
return (durationInSeconds / displayDuration);
96+
const displayPercentage = (durationInSeconds / displayDuration);
97+
const getWaveformWidth = (baseWidth: number) => {
98+
return baseWidth * displayPercentage;
9899
};
99-
const getWaveformWidth = (baseWidth: number, zoomValue: number) => {
100-
return baseWidth * getDisplayPercentage(zoomValue);
101-
};
102-
const displayPercentage = getDisplayPercentage(timelineZoom);
103-
const zoomedWidth = getWaveformWidth(scrollContainerWidth, timelineZoom);
100+
const zoomedWidth = getWaveformWidth(scrollContainerWidth);
104101

105102
// eslint-disable-next-line react-hooks/exhaustive-deps
106103
useEffect(updateScroll, [currentlyAt, timelineZoom, width, scrollContainerWidth]);

src/redux/videoSlice.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,12 @@ const videoSlice = createSlice({
408408
}
409409
return undefined;
410410
},
411+
selectDisplayDuration: state => {
412+
const minDisplayTime = 10; // in seconds, what is shown at max zoom
413+
const durationInSeconds = state.duration / 1000;
414+
const displayDuration = (1 - state.timelineZoom) * (durationInSeconds - minDisplayTime) + minDisplayTime;
415+
return displayDuration;
416+
},
411417
},
412418
});
413419

@@ -592,6 +598,7 @@ export const {
592598
selectSubtitlesFromOpencast,
593599
selectSubtitlesFromOpencastById,
594600
selectVideos,
601+
selectDisplayDuration,
595602
} = videoSlice.selectors;
596603

597604
export default videoSlice.reducer;

0 commit comments

Comments
 (0)