mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 05:37:20 +03:00
chore: use glass pane for dragging (#26513)
This commit is contained in:
parent
5bcd1fb65f
commit
049f839b30
@ -25,10 +25,6 @@
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.timeline-view.dragging {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.timeline-divider {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
@ -137,6 +133,10 @@
|
||||
background-color: #3879d91a;
|
||||
}
|
||||
|
||||
body.dark-mode .timeline-window-curtain {
|
||||
background-color: #161718bf;
|
||||
}
|
||||
|
||||
.timeline-window-curtain.left {
|
||||
border-right: 1px solid var(--vscode-panel-border);
|
||||
}
|
||||
|
@ -1,21 +1,21 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { msToString, useMeasure } from '@web/uiUtils';
|
||||
import { GlassPane } from '@web/components/glassPane';
|
||||
import * as React from 'react';
|
||||
import type { Boundaries } from '../geometry';
|
||||
import { FilmStrip } from './filmStrip';
|
||||
@ -111,16 +111,13 @@ export const Timeline: React.FunctionComponent<{
|
||||
}
|
||||
}, [boundaries, measure, ref, selectedTime]);
|
||||
|
||||
const onMouseMove = React.useCallback((event: React.MouseEvent) => {
|
||||
const onGlassPaneMouseMove = React.useCallback((event: MouseEvent) => {
|
||||
if (!ref.current)
|
||||
return;
|
||||
const x = event.clientX - ref.current.getBoundingClientRect().left;
|
||||
const time = positionToTime(measure.width, boundaries, x);
|
||||
const action = model?.actions.findLast(action => action.startTime <= time);
|
||||
if (!dragWindow) {
|
||||
setPreviewPoint({ x, clientY: event.clientY, action, sdkLanguage });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!event.buttons) {
|
||||
setDragWindow(undefined);
|
||||
return;
|
||||
@ -130,6 +127,10 @@ export const Timeline: React.FunctionComponent<{
|
||||
if (action)
|
||||
onSelected(action);
|
||||
|
||||
// Should not happen, but for type safety.
|
||||
if (!dragWindow)
|
||||
return;
|
||||
|
||||
let newDragWindow = dragWindow;
|
||||
if (dragWindow.type === 'resize') {
|
||||
newDragWindow = { ...dragWindow, endX: x };
|
||||
@ -153,9 +154,9 @@ export const Timeline: React.FunctionComponent<{
|
||||
const time2 = positionToTime(measure.width, boundaries, newDragWindow.endX);
|
||||
if (time1 !== time2)
|
||||
setSelectedTime({ minimum: Math.min(time1, time2), maximum: Math.max(time1, time2) });
|
||||
}, [boundaries, dragWindow, measure, model, onSelected, ref, sdkLanguage, setSelectedTime]);
|
||||
}, [boundaries, dragWindow, measure, model, onSelected, ref, setSelectedTime]);
|
||||
|
||||
const onMouseUp = React.useCallback(() => {
|
||||
const onGlassPaneMouseUp = React.useCallback(() => {
|
||||
setPreviewPoint(undefined);
|
||||
if (!dragWindow)
|
||||
return;
|
||||
@ -178,23 +179,35 @@ export const Timeline: React.FunctionComponent<{
|
||||
setDragWindow(undefined);
|
||||
}, [boundaries, dragWindow, measure, model, selectedTime, setSelectedTime, onSelected]);
|
||||
|
||||
const onMouseMove = React.useCallback((event: React.MouseEvent) => {
|
||||
if (!ref.current)
|
||||
return;
|
||||
const x = event.clientX - ref.current.getBoundingClientRect().left;
|
||||
const time = positionToTime(measure.width, boundaries, x);
|
||||
const action = model?.actions.findLast(action => action.startTime <= time);
|
||||
setPreviewPoint({ x, clientY: event.clientY, action, sdkLanguage });
|
||||
}, [boundaries, measure, model, ref, sdkLanguage]);
|
||||
|
||||
const onMouseLeave = React.useCallback(() => {
|
||||
setPreviewPoint(undefined);
|
||||
}, []);
|
||||
|
||||
const onDoubleClick = React.useCallback(() => {
|
||||
const onPaneDoubleClick = React.useCallback(() => {
|
||||
setSelectedTime(undefined);
|
||||
}, [setSelectedTime]);
|
||||
|
||||
return <div style={{ flex: 'none', borderBottom: '1px solid var(--vscode-panel-border)' }}>
|
||||
<div
|
||||
ref={ref}
|
||||
className={'timeline-view' + (dragWindow ? ' dragging' : '')}
|
||||
<GlassPane
|
||||
enabled={!!dragWindow}
|
||||
cursor={dragWindow?.type === 'resize' ? 'ew-resize' : 'grab'}
|
||||
onPaneMouseUp={onGlassPaneMouseUp}
|
||||
onPaneMouseMove={onGlassPaneMouseMove}
|
||||
onPaneDoubleClick={onPaneDoubleClick} />
|
||||
<div ref={ref}
|
||||
className='timeline-view'
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseUp={onMouseUp}
|
||||
onMouseMove={onMouseMove}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onDoubleClick={onDoubleClick}>
|
||||
onMouseLeave={onMouseLeave}>
|
||||
<div className='timeline-grid'>{
|
||||
offsets.map((offset, index) => {
|
||||
return <div key={index} className='timeline-divider' style={{ left: offset.position + 'px' }}>
|
||||
@ -219,7 +232,7 @@ export const Timeline: React.FunctionComponent<{
|
||||
display: (previewPoint !== undefined) ? 'block' : 'none',
|
||||
left: (previewPoint?.x || 0) + 'px',
|
||||
}} />
|
||||
<div className='timeline-window'>
|
||||
{selectedTime && <div className='timeline-window'>
|
||||
<div className='timeline-window-curtain left' style={{ width: curtainLeft }}></div>
|
||||
<div className='timeline-window-resizer'></div>
|
||||
<div className='timeline-window-center'>
|
||||
@ -227,7 +240,7 @@ export const Timeline: React.FunctionComponent<{
|
||||
</div>
|
||||
<div className='timeline-window-resizer'></div>
|
||||
<div className='timeline-window-curtain right' style={{ width: curtainRight }}></div>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
</div>;
|
||||
};
|
||||
|
60
packages/web/src/components/glassPane.tsx
Normal file
60
packages/web/src/components/glassPane.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export const GlassPane: React.FC<{
|
||||
enabled: boolean;
|
||||
cursor: string;
|
||||
onPaneMouseMove?: (e: MouseEvent) => void;
|
||||
onPaneMouseUp?: (e: MouseEvent) => void;
|
||||
onPaneDoubleClick?: (e: MouseEvent) => void;
|
||||
}> = ({ enabled, cursor, onPaneMouseMove, onPaneMouseUp, onPaneDoubleClick }) => {
|
||||
React.useEffect(() => {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
const glassPaneDiv = document.createElement('div');
|
||||
glassPaneDiv.style.position = 'absolute';
|
||||
glassPaneDiv.style.top = '0';
|
||||
glassPaneDiv.style.right = '0';
|
||||
glassPaneDiv.style.bottom = '0';
|
||||
glassPaneDiv.style.left = '0';
|
||||
glassPaneDiv.style.zIndex = '9999';
|
||||
glassPaneDiv.style.cursor = cursor;
|
||||
|
||||
document.body.appendChild(glassPaneDiv);
|
||||
|
||||
if (onPaneMouseMove)
|
||||
glassPaneDiv.addEventListener('mousemove', onPaneMouseMove);
|
||||
if (onPaneMouseUp)
|
||||
glassPaneDiv.addEventListener('mouseup', onPaneMouseUp);
|
||||
if (onPaneDoubleClick)
|
||||
document.body.addEventListener('dblclick', onPaneDoubleClick);
|
||||
|
||||
return () => {
|
||||
if (onPaneMouseMove)
|
||||
glassPaneDiv.removeEventListener('mousemove', onPaneMouseMove);
|
||||
if (onPaneMouseUp)
|
||||
glassPaneDiv.removeEventListener('mouseup', onPaneMouseUp);
|
||||
if (onPaneDoubleClick)
|
||||
document.body.removeEventListener('dblclick', onPaneDoubleClick);
|
||||
document.body.removeChild(glassPaneDiv);
|
||||
};
|
||||
}, [enabled, cursor, onPaneMouseMove, onPaneMouseUp, onPaneDoubleClick]);
|
||||
|
||||
return <></>;
|
||||
};
|
Loading…
Reference in New Issue
Block a user