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