diff options
Diffstat (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx')
-rw-r--r-- | src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx | 133 |
1 files changed, 119 insertions, 14 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index e48a6ada4..bc04f9a69 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -8,7 +8,7 @@ import { DocCast, ImageCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { emptyFunction } from '../../../../Utils'; import { SnappingManager } from '../../../util/SnappingManager'; -import { undoable } from '../../../util/UndoManager'; +import { UndoManager, undoable } from '../../../util/UndoManager'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DocumentView } from '../DocumentView'; import { DataVizBox } from './DataVizBox'; @@ -18,6 +18,8 @@ import { Colors, IconButton, Size } from 'browndash-components'; import { MakeTemplate } from '../../../util/DropConverter'; import { threadId } from 'worker_threads'; import { ideahub } from 'googleapis/build/src/apis/ideahub'; +import { DragManager } from '../../../util/DragManager'; +import { DateField } from '../../../../fields/DateField'; export enum LayoutType { Stacked = 'stacked', @@ -58,6 +60,14 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @observable _dragging: boolean = false; @observable _draggingIndicator: boolean = false; @observable _dataViz?: DataVizBox; + @observable _interactionLock: any; + @observable _snapPt: any; + @observable _resizeHdlId: string = ''; + @observable _resizing: boolean = false; + @observable _offset: {x: number, y: number} = {x: 0, y: 0}; + @observable _resizeUndo: UndoManager.Batch | undefined = undefined; + @observable _initDimensions: {width: number, height: number, x: number, y: number} = {width: 300, height: 400, x: 0, y: 0}; + @observable _menuDimensions: {width: number, height: number} = {width: 300, height: 400}; constructor(props: any) { super(props); @@ -102,6 +112,12 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { return this._selectedTemplate !== undefined && this._layout !== undefined; } + get bounds(): {t: number, b: number, l: number, r: number} { + const rect = this._ref?.getBoundingClientRect(); + const bounds = {t: rect?.top ?? 0, b: rect?.bottom ?? 0, l: rect?.left ?? 0, r: rect?.right ?? 0}; + return bounds; + } + setUpButtonClick = (e: any, func: Function) => { setupMoveUpEvents( this, @@ -123,6 +139,19 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @action onPointerUp = (e: PointerEvent) => { + if (this._resizing) { + this._initDimensions.width = this._menuDimensions.width; + this._initDimensions.height = this._menuDimensions.height; + this._initDimensions.x = this._pageX; + this._initDimensions.y = this._pageY; + document.removeEventListener('pointermove', this.onResize); + SnappingManager.SetIsResizing(undefined); + this._resizing = false; + } + if (this._dragging) { + document.removeEventListener('pointermove', this.onDrag); + this._dragging = false; + } if (e.button !== 2 && !e.ctrlKey) return; const curX = e.clientX; const curY = e.clientY; @@ -164,17 +193,76 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { }; @action - onPointerMove = (e: any) => { - // if (this._draggingIndicator) { - // this._indicatorX = e.pageX - (this._startPos?.x ?? 0) + this._pageX; - // this._indicatorY = e.pageY - (this._startPos?.y ?? 0) + this._pageY; - // } else - if (this._dragging){ - this._pageX = e.pageX - (this._startPos?.x ?? 0); - this._pageY = e.pageY - (this._startPos?.y ?? 0); - } + onResizePointerDown = (e: React.PointerEvent): void => { + this._resizing = true; + document.addEventListener('pointermove', this.onResize); + SnappingManager.SetIsResizing(DocumentView.Selected().lastElement()?.Document[Id]); // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them + e.stopPropagation(); + const id = (this._resizeHdlId = e.currentTarget.className); + const pad = id.includes('Left') || id.includes('Right') ? Number(getComputedStyle(e.target as any).width.replace('px', '')) / 2 : 0; + const bounds = e.currentTarget.getBoundingClientRect(); + this._offset = { + x: id.toLowerCase().includes('left') ? bounds.right - e.clientX - pad : bounds.left - e.clientX + pad, // + y: id.toLowerCase().includes('top') ? bounds.bottom - e.clientY - pad : bounds.top - e.clientY + pad, + }; + this._resizeUndo = UndoManager.StartBatch('drag resizing'); + this._snapPt = { x: e.pageX, y: e.pageY }; + }; + + @action + onResize = (e: any): boolean => { + const dragHdl = this._resizeHdlId.split(' ')[1]; + const thisPt = DragManager.snapDrag(e, -this._offset.x, -this._offset.y, this._offset.x, this._offset.y); + + const { scale, refPt, transl } = this.getResizeVals(thisPt, dragHdl); + !this._interactionLock && runInAction(async () => { // resize selected docs if we're not in the middle of a resize (ie, throttle input events to frame rate) + this._interactionLock = true; + const scaleAspect = {x: scale.x, y: scale.y}; + this.resizeView(refPt, scaleAspect, transl); // prettier-ignore + await new Promise<any>(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); + }); // prettier-ignore + return true; } + @action + onDrag = (e: any): boolean => { + this._pageX = e.pageX - (this._startPos?.x ?? 0); + this._pageY = e.pageY - (this._startPos?.y ?? 0); + return true; + } + + getResizeVals = (thisPt: { x: number; y: number }, dragHdl: string) => { + const [w, h] = [this._initDimensions.width, this._initDimensions.height]; + const [moveX, moveY] = [thisPt.x - this._snapPt.x, thisPt.y - this._snapPt.y]; + let vals: {scale: {x: number, y: number}, refPt: [number, number], transl: {x: number, y: number}}; + switch (dragHdl) { + case 'topLeft': vals = { scale: { x: 1 - moveX / w, y: 1 -moveY / h }, refPt: [this.bounds.r, this.bounds.b], transl: {x: moveX, y: -moveY } }; break; + case 'topRight': vals = { scale: { x: 1 + moveX / w, y: 1 -moveY / h }, refPt: [this.bounds.l, this.bounds.b], transl: {x: 0, y: -moveY } }; break; + case 'top': vals = { scale: { x: 1, y: 1 -moveY / h }, refPt: [this.bounds.l, this.bounds.b], transl: {x: 0, y: moveY } }; break; + case 'left': vals = { scale: { x: 1 - moveX / w, y: 1 }, refPt: [this.bounds.r, this.bounds.t], transl: {x: moveX, y: 0 } }; break; + case 'bottomLeft': vals = { scale: { x: 1 - moveX / w, y: 1 + moveY / h }, refPt: [this.bounds.r, this.bounds.t], transl: {x: moveX, y: moveY } }; break; + case 'right': vals = { scale: { x: 1 + moveX / w, y: 1 }, refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break; + case 'bottomRight':vals = { scale: { x: 1 + moveX / w, y: 1 + moveY / h }, refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break; + case 'bottom': vals = { scale: { x: 1, y: 1 + moveY / h }, refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break; + default: vals = { scale: { x: 1, y: 1 }, refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break; + } // prettier-ignore + return vals; + }; + + resizeView = (refPt: number[], scale: { x: number; y: number }, translation: {x: number, y: number}) => { + const refCent = [refPt[0], refPt[1]] // fixed reference point for resize (ie, a point that doesn't move) + if (this._initDimensions.x === undefined) this._initDimensions.x = this._pageX; + if (this._initDimensions.y === undefined) this._initDimensions.y = this._pageY; + const {height, width, x, y} = this._initDimensions; + + console.log(scale.x) + + this._menuDimensions.width = Math.max(100, scale.x * width); + this._menuDimensions.height = Math.max(100, scale.y * height); + this._pageX = x + translation.x; + this._pageY = y + translation.y; + }; + async getIcon(doc: Doc) { const docView = DocumentView.getDocumentView(doc); if (docView) { @@ -470,6 +558,23 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } } + get resizePanes(){ + const ref = this._ref?.getBoundingClientRect(); + const height: number = ref?.height ?? 0; + const width: number = ref?.width ?? 0; + + return [ + <div className='docCreatorMenu-resizer top' onPointerDown={this.onResizePointerDown} style={{width: width, left: 0, top: -7}}/>, + <div className='docCreatorMenu-resizer right' onPointerDown={this.onResizePointerDown} style={{height: height, left: width - 3, top: 0}}/>, + <div className='docCreatorMenu-resizer bottom' onPointerDown={this.onResizePointerDown} style={{width: width, left: 0, top: height - 3}}/>, + <div className='docCreatorMenu-resizer left' onPointerDown={this.onResizePointerDown} style={{height: height, left: -7, top: 0}}/>, + <div className='docCreatorMenu-resizer topRight' onPointerDown={this.onResizePointerDown} style={{left: width, top: -7, cursor: 'nesw-resize'}}/>, + <div className='docCreatorMenu-resizer topLeft' onPointerDown={this.onResizePointerDown} style={{left: -7, top: -7, cursor: 'nwse-resize'}}/>, + <div className='docCreatorMenu-resizer bottomRight' onPointerDown={this.onResizePointerDown} style={{left: width, top: height, cursor: 'nwse-resize'}}/>, + <div className='docCreatorMenu-resizer bottomLeft' onPointerDown={this.onResizePointerDown} style={{left: -7, top: height, cursor: 'nesw-resize'}}/> + ]; //prettier-ignore + } + render() { const topButton = (icon: string, opt: string, func: Function, tag: string) => { return ( @@ -526,11 +631,12 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { display: '', left: this._pageX, top: this._pageY, - width: 300, - height: 400, + width: this._menuDimensions.width, + height: this._menuDimensions.height, background: SnappingManager.userBackgroundColor, color: SnappingManager.userColor, }}> + {this.resizePanes} <div className='docCreatorMenu-menu' onPointerDown={e => @@ -542,6 +648,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { this._startPos = {x: 0, y: 0}; this._startPos.x = e.pageX - (this._ref?.getBoundingClientRect().left ?? 0); this._startPos.y = e.pageY - (this._ref?.getBoundingClientRect().top ?? 0); + document.addEventListener('pointermove', this.onDrag); return true; }, emptyFunction, @@ -550,8 +657,6 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { }, 'drag menu') ) } - onPointerMove={e => this.onPointerMove(e)} - onPointerUp={() => this._dragging = false} > <div className='docCreatorMenu-top-buttons-container'> {topButton('table-cells', 'templates', onPreviewSelected, 'left')} |