Disable buttons when zooming is not possible
This commit is contained in:
parent
a4e9ebca3b
commit
9ae4335b19
|
@ -16,7 +16,12 @@ import { SeekBar } from './SeekBar';
|
||||||
import { firstValueFrom, from, Observable } from 'rxjs';
|
import { firstValueFrom, from, Observable } from 'rxjs';
|
||||||
import { first, map } from 'rxjs/operators';
|
import { first, map } from 'rxjs/operators';
|
||||||
import millisFromDuration from './helpers/millisFromDuration';
|
import millisFromDuration from './helpers/millisFromDuration';
|
||||||
import { zoomViewportIn, zoomViewportOut } from './helpers/zoom';
|
import {
|
||||||
|
canZoomViewportIn,
|
||||||
|
canZoomViewportOut,
|
||||||
|
zoomViewportIn,
|
||||||
|
zoomViewportOut,
|
||||||
|
} from './helpers/zoom';
|
||||||
import toHHMMSS from './helpers/toHHMMSS';
|
import toHHMMSS from './helpers/toHHMMSS';
|
||||||
import framesToDuration from './helpers/framesToDuration';
|
import framesToDuration from './helpers/framesToDuration';
|
||||||
import { ClockIcon, ExternalLinkIcon } from '@heroicons/react/solid';
|
import { ClockIcon, ExternalLinkIcon } from '@heroicons/react/solid';
|
||||||
|
@ -448,6 +453,8 @@ function App(): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
<ControlBar
|
<ControlBar
|
||||||
playState={playState}
|
playState={playState}
|
||||||
|
zoomInEnabled={canZoomViewportIn(viewport, selection, zoomFactor)}
|
||||||
|
zoomOutEnabled={canZoomViewportOut(viewport, mediaSet.audioFrames)}
|
||||||
onTogglePlay={togglePlay}
|
onTogglePlay={togglePlay}
|
||||||
onClip={handleClip}
|
onClip={handleClip}
|
||||||
onZoomIn={handleZoomIn}
|
onZoomIn={handleZoomIn}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import {
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
playState: PlayState;
|
playState: PlayState;
|
||||||
|
zoomInEnabled: boolean;
|
||||||
|
zoomOutEnabled: boolean;
|
||||||
onTogglePlay: () => void;
|
onTogglePlay: () => void;
|
||||||
onClip: () => void;
|
onClip: () => void;
|
||||||
onZoomIn: () => void;
|
onZoomIn: () => void;
|
||||||
|
@ -21,11 +23,14 @@ interface Props {
|
||||||
|
|
||||||
const ControlBar: React.FC<Props> = React.memo((props: Props) => {
|
const ControlBar: React.FC<Props> = React.memo((props: Props) => {
|
||||||
const buttonStyle =
|
const buttonStyle =
|
||||||
'bg-gray-700 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded';
|
'bg-gray-600 hover:bg-gray-500 text-white font-bold py-2 px-4 rounded';
|
||||||
|
|
||||||
|
const disabledButtonStyle =
|
||||||
|
'bg-gray-700 text-white font-bold py-2 px-4 rounded cursor-auto';
|
||||||
|
|
||||||
const downloadButtonStyle = props.downloadClipEnabled
|
const downloadButtonStyle = props.downloadClipEnabled
|
||||||
? 'bg-green-700 hover:bg-green-600 text-white font-bold py-2 px-4 rounded absolute right-0'
|
? 'bg-green-600 hover:bg-green-600 text-white font-bold py-2 px-4 rounded absolute right-0'
|
||||||
: 'bg-gray-700 hover:cursor-not-allowed text-gray-500 font-bold py-2 px-4 rounded absolute right-0';
|
: 'bg-gray-600 hover:cursor-not-allowed text-gray-500 font-bold py-2 px-4 rounded absolute right-0';
|
||||||
|
|
||||||
const iconStyle = 'inline h-7 w-7 text-white-500';
|
const iconStyle = 'inline h-7 w-7 text-white-500';
|
||||||
|
|
||||||
|
@ -67,13 +72,13 @@ const ControlBar: React.FC<Props> = React.memo((props: Props) => {
|
||||||
<FastForwardIcon className={iconStyle} />
|
<FastForwardIcon className={iconStyle} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={buttonStyle}
|
className={props.zoomInEnabled ? buttonStyle : disabledButtonStyle}
|
||||||
onClick={(evt) => filterMouseEvent(evt, props.onZoomIn)}
|
onClick={(evt) => filterMouseEvent(evt, props.onZoomIn)}
|
||||||
>
|
>
|
||||||
<ZoomInIcon className={iconStyle} />
|
<ZoomInIcon className={iconStyle} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={buttonStyle}
|
className={props.zoomOutEnabled ? buttonStyle : disabledButtonStyle}
|
||||||
onClick={(evt) => filterMouseEvent(evt, props.onZoomOut)}
|
onClick={(evt) => filterMouseEvent(evt, props.onZoomOut)}
|
||||||
>
|
>
|
||||||
<ZoomOutIcon className={iconStyle} />
|
<ZoomOutIcon className={iconStyle} />
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import { zoomViewportIn, zoomViewportOut } from './zoom';
|
import {
|
||||||
|
zoomViewportIn,
|
||||||
|
zoomViewportOut,
|
||||||
|
canZoomViewportIn,
|
||||||
|
canZoomViewportOut,
|
||||||
|
} from './zoom';
|
||||||
|
|
||||||
// zf is the zoom factor.
|
// zf is the zoom factor.
|
||||||
const zf = 2;
|
const zf = 2;
|
||||||
|
@ -220,3 +225,49 @@ describe('zoomViewportOut', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('canZoomViewportIn', () => {
|
||||||
|
it('does now allow zooming when the viewport is inactive', () => {
|
||||||
|
const result = canZoomViewportIn(
|
||||||
|
{ start: 0, end: 0 },
|
||||||
|
{ start: 0, end: 0 },
|
||||||
|
zf
|
||||||
|
);
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not allow zooming past the selection', () => {
|
||||||
|
const result = canZoomViewportIn(
|
||||||
|
{ start: 1000, end: 2000 },
|
||||||
|
{ start: 1100, end: 1900 },
|
||||||
|
zf
|
||||||
|
);
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows zooming', () => {
|
||||||
|
const result = canZoomViewportIn(
|
||||||
|
{ start: 1000, end: 2000 },
|
||||||
|
{ start: 0, end: 100 },
|
||||||
|
zf
|
||||||
|
);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('canZoomViewportOut', () => {
|
||||||
|
it('does now allow zooming when the viewport is inactive', () => {
|
||||||
|
const result = canZoomViewportOut({ start: 0, end: 0 }, 1000);
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does now allow zooming when already at maximum zoom', () => {
|
||||||
|
const result = canZoomViewportOut({ start: 0, end: 1000 }, 1000);
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows zooming', () => {
|
||||||
|
const result = canZoomViewportOut({ start: 1000, end: 2000 }, 5000);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ export function zoomViewportIn(
|
||||||
position: number,
|
position: number,
|
||||||
factor: number
|
factor: number
|
||||||
): Frames {
|
): Frames {
|
||||||
if (viewport.start == viewport.end) {
|
if (!canZoomViewportIn(viewport, selection, factor)) {
|
||||||
return viewport;
|
return viewport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export function zoomViewportOut(
|
||||||
position: number,
|
position: number,
|
||||||
factor: number
|
factor: number
|
||||||
): Frames {
|
): Frames {
|
||||||
if (viewport.start == viewport.end) {
|
if (!canZoomViewportOut(viewport, numFrames)) {
|
||||||
return viewport;
|
return viewport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,3 +74,31 @@ function zoom(
|
||||||
|
|
||||||
return { start: newStart, end: newEnd };
|
return { start: newStart, end: newEnd };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function canZoomViewportIn(
|
||||||
|
viewport: Frames,
|
||||||
|
selection: Frames,
|
||||||
|
factor: number
|
||||||
|
): boolean {
|
||||||
|
if (viewport.start == viewport.end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selection.start != selection.end) {
|
||||||
|
const newWidth = Math.round((viewport.end - viewport.start) / factor);
|
||||||
|
return newWidth > selection.end - selection.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canZoomViewportOut(
|
||||||
|
viewport: Frames,
|
||||||
|
numFrames: number
|
||||||
|
): boolean {
|
||||||
|
if (viewport.start == viewport.end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewport.start > 0 || viewport.end < numFrames;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue