@ -112,6 +112,7 @@ import useIsMobile, { IsMobileProvider } from "./is-mobile";
import { copyToAppClipboard , getClipboardContent } from "./clipboard" ;
import { copyToAppClipboard , getClipboardContent } from "./clipboard" ;
import { normalizeScroll } from "./scene/data" ;
import { normalizeScroll } from "./scene/data" ;
import { getCenter , getDistance } from "./gesture" ;
import { getCenter , getDistance } from "./gesture" ;
import { ScrollBars } from "./scene/types" ;
import { createUndoAction , createRedoAction } from "./actions/actionHistory" ;
import { createUndoAction , createRedoAction } from "./actions/actionHistory" ;
let { elements } = createScene ( ) ;
let { elements } = createScene ( ) ;
@ -214,6 +215,8 @@ let cursorX = 0;
let cursorY = 0 ;
let cursorY = 0 ;
let isHoldingSpace : boolean = false ;
let isHoldingSpace : boolean = false ;
let isPanning : boolean = false ;
let isPanning : boolean = false ;
let isDraggingScrollBar : boolean = false ;
let currentScrollBars : ScrollBars = { horizontal : null , vertical : null } ;
interface LayerUIProps {
interface LayerUIProps {
actionManager : ActionManager ;
actionManager : ActionManager ;
@ -1159,12 +1162,9 @@ export class App extends React.Component<any, AppState> {
isOverHorizontalScrollBar ,
isOverHorizontalScrollBar ,
isOverVerticalScrollBar ,
isOverVerticalScrollBar ,
} = isOverScrollBars (
} = isOverScrollBars (
elements ,
currentScrollBars ,
event . clientX / window . devicePixelRatio ,
event . clientX ,
event . clientY / window . devicePixelRatio ,
event . clientY ,
canvasWidth / window . devicePixelRatio ,
canvasHeight / window . devicePixelRatio ,
this . state ,
) ;
) ;
const { x , y } = viewportCoordsToSceneCoords (
const { x , y } = viewportCoordsToSceneCoords (
@ -1172,6 +1172,60 @@ export class App extends React.Component<any, AppState> {
this . state ,
this . state ,
this . canvas ,
this . canvas ,
) ;
) ;
let lastX = x ;
let lastY = y ;
if (
( isOverHorizontalScrollBar || isOverVerticalScrollBar ) &&
! this . state . multiElement
) {
isDraggingScrollBar = true ;
lastX = event . clientX ;
lastY = event . clientY ;
const onPointerMove = ( event : PointerEvent ) = > {
const target = event . target ;
if ( ! ( target instanceof HTMLElement ) ) {
return ;
}
if ( isOverHorizontalScrollBar ) {
const x = event . clientX ;
const dx = x - lastX ;
this . setState ( {
scrollX : normalizeScroll (
this . state . scrollX - dx / this . state . zoom ,
) ,
} ) ;
lastX = x ;
return ;
}
if ( isOverVerticalScrollBar ) {
const y = event . clientY ;
const dy = y - lastY ;
this . setState ( {
scrollY : normalizeScroll (
this . state . scrollY - dy / this . state . zoom ,
) ,
} ) ;
lastY = y ;
}
} ;
const onPointerUp = ( ) = > {
isDraggingScrollBar = false ;
setCursorForShape ( this . state . elementType ) ;
lastPointerUp = null ;
window . removeEventListener ( "pointermove" , onPointerMove ) ;
window . removeEventListener ( "pointerup" , onPointerUp ) ;
} ;
lastPointerUp = onPointerUp ;
window . addEventListener ( "pointermove" , onPointerMove ) ;
window . addEventListener ( "pointerup" , onPointerUp ) ;
return ;
}
const originX = x ;
const originX = x ;
const originY = y ;
const originY = y ;
@ -1373,14 +1427,6 @@ export class App extends React.Component<any, AppState> {
this . setState ( { multiElement : null , draggingElement : element } ) ;
this . setState ( { multiElement : null , draggingElement : element } ) ;
}
}
let lastX = x ;
let lastY = y ;
if ( isOverHorizontalScrollBar || isOverVerticalScrollBar ) {
lastX = event . clientX ;
lastY = event . clientY ;
}
let resizeArrowFn :
let resizeArrowFn :
| ( (
| ( (
element : ExcalidrawElement ,
element : ExcalidrawElement ,
@ -2115,10 +2161,27 @@ export class App extends React.Component<any, AppState> {
gesture . lastCenter = gesture . initialDistance = gesture . initialScale = null ;
gesture . lastCenter = gesture . initialDistance = gesture . initialScale = null ;
}
}
if ( isHoldingSpace || isPanning ) {
if ( isHoldingSpace || isPanning || isDraggingScrollBar ) {
return ;
return ;
}
}
const hasDeselectedButton = Boolean ( event . buttons ) ;
const {
isOverHorizontalScrollBar ,
isOverVerticalScrollBar ,
} = isOverScrollBars (
currentScrollBars ,
event . clientX ,
event . clientY ,
) ;
const isOverScrollBar =
isOverVerticalScrollBar || isOverHorizontalScrollBar ;
if ( ! this . state . draggingElement && ! this . state . multiElement ) {
if ( isOverScrollBar ) {
resetCursor ( ) ;
} else {
setCursorForShape ( this . state . elementType ) ;
}
}
const { x , y } = viewportCoordsToSceneCoords (
const { x , y } = viewportCoordsToSceneCoords (
event ,
event ,
@ -2138,6 +2201,7 @@ export class App extends React.Component<any, AppState> {
return ;
return ;
}
}
const hasDeselectedButton = Boolean ( event . buttons ) ;
if (
if (
hasDeselectedButton ||
hasDeselectedButton ||
this . state . elementType !== "selection"
this . state . elementType !== "selection"
@ -2146,7 +2210,7 @@ export class App extends React.Component<any, AppState> {
}
}
const selectedElements = getSelectedElements ( elements ) ;
const selectedElements = getSelectedElements ( elements ) ;
if ( selectedElements . length === 1 ) {
if ( selectedElements . length === 1 && ! isOverScrollBar ) {
const resizeElement = getElementWithResizeHandler (
const resizeElement = getElementWithResizeHandler (
elements ,
elements ,
{ x , y } ,
{ x , y } ,
@ -2166,7 +2230,8 @@ export class App extends React.Component<any, AppState> {
y ,
y ,
this . state . zoom ,
this . state . zoom ,
) ;
) ;
document . documentElement . style . cursor = hitElement ? "move" : "" ;
document . documentElement . style . cursor =
hitElement && ! isOverScrollBar ? "move" : "" ;
} }
} }
onPointerUp = { this . removePointer }
onPointerUp = { this . removePointer }
onPointerLeave = { this . removePointer }
onPointerLeave = { this . removePointer }
@ -2279,7 +2344,7 @@ export class App extends React.Component<any, AppState> {
} , 300 ) ;
} , 300 ) ;
componentDidUpdate() {
componentDidUpdate() {
const atLeastOneVisibleElement = renderScene (
const { atLeastOneVisibleElement , scrollBars } = renderScene (
elements ,
elements ,
this . state . selectionElement ,
this . state . selectionElement ,
this . rc ! ,
this . rc ! ,
@ -2294,6 +2359,9 @@ export class App extends React.Component<any, AppState> {
renderOptimizations : true ,
renderOptimizations : true ,
} ,
} ,
) ;
) ;
if ( scrollBars ) {
currentScrollBars = scrollBars ;
}
const scrolledOutside = ! atLeastOneVisibleElement && elements . length > 0 ;
const scrolledOutside = ! atLeastOneVisibleElement && elements . length > 0 ;
if ( this . state . scrolledOutside !== scrolledOutside ) {
if ( this . state . scrolledOutside !== scrolledOutside ) {
this . setState ( { scrolledOutside : scrolledOutside } ) ;
this . setState ( { scrolledOutside : scrolledOutside } ) ;