UNPKG

chrome-devtools-frontend

Version:
143 lines (118 loc) 4.17 kB
// Copyright 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. export const enum ResizerType { WIDTH = 'width', HEIGHT = 'height', BIDIRECTION = 'bidirection', } export interface Draggable { type: ResizerType; initialWidth?: number; initialHeight?: number; update({width, height}: {width?: number, height?: number}): void; } export interface Delegate { getDraggable(x: number, y: number): Draggable|undefined; } const cursorByResizerType = new Map([ [ResizerType.WIDTH, 'ew-resize'], [ResizerType.HEIGHT, 'ns-resize'], [ResizerType.BIDIRECTION, 'nwse-resize'], ]); type OriginInfo = { coord: number, value: number, }; export class DragResizeHandler { private document: Document; private delegate: Delegate; private originX?: OriginInfo; private originY?: OriginInfo; private boundMousemove: (event: MouseEvent) => void; private boundMousedown: (event: MouseEvent) => void; constructor(document: Document, delegate: Delegate) { this.document = document; this.delegate = delegate; this.boundMousemove = this.onMousemove.bind(this); this.boundMousedown = this.onMousedown.bind(this); } install() { this.document.body.addEventListener('mousemove', this.boundMousemove); this.document.body.addEventListener('mousedown', this.boundMousedown); } uninstall() { this.document.body.removeEventListener('mousemove', this.boundMousemove); this.document.body.removeEventListener('mousedown', this.boundMousedown); } /** * Updates the cursor style of the mouse is hovered over a resizeable area. */ private onMousemove(event: MouseEvent) { const match = this.delegate.getDraggable(event.clientX, event.clientY); if (!match) { this.document.body.style.cursor = 'default'; return; } this.document.body.style.cursor = cursorByResizerType.get(match.type) || 'default'; } /** * Starts dragging */ private onMousedown(event: MouseEvent) { const match = this.delegate.getDraggable(event.clientX, event.clientY); if (!match) { return; } const boundOnDrag = this.onDrag.bind(this, match); event.stopPropagation(); event.preventDefault(); if (match.initialWidth !== undefined && (match.type === ResizerType.WIDTH || match.type === ResizerType.BIDIRECTION)) { this.originX = { coord: Math.round(event.clientX), value: match.initialWidth, }; } if (match.initialHeight !== undefined && (match.type === ResizerType.HEIGHT || match.type === ResizerType.BIDIRECTION)) { this.originY = { coord: Math.round(event.clientY), value: match.initialHeight, }; } this.document.body.removeEventListener('mousemove', this.boundMousemove); this.document.body.style.cursor = cursorByResizerType.get(match.type) || 'default'; const endDrag = (event: Event) => { event.stopPropagation(); event.preventDefault(); this.originX = undefined; this.originY = undefined; this.document.body.style.cursor = 'default'; this.document.body.removeEventListener('mousemove', boundOnDrag); this.document.body.addEventListener('mousemove', this.boundMousemove); }; this.document.body.addEventListener('mouseup', endDrag, {once: true}); window.addEventListener('mouseout', endDrag, {once: true}); this.document.body.addEventListener('mousemove', boundOnDrag); } /** * Computes the new value while the cursor is being dragged and calls InspectorOverlayHost with the new value. */ private onDrag(match: Draggable, e: MouseEvent) { if (!this.originX && !this.originY) { return; } let width: number|undefined; let height: number|undefined; if (this.originX) { const delta = this.originX.coord - e.clientX; width = Math.round(this.originX.value - delta); } if (this.originY) { const delta = this.originY.coord - e.clientY; height = Math.round(this.originY.value - delta); } match.update({width, height}); } }