diff options
Diffstat (limited to 'src/client/views/DocumentDecorations.tsx')
| -rw-r--r-- | src/client/views/DocumentDecorations.tsx | 341 |
1 files changed, 130 insertions, 211 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 8df5740fa..dc62e0450 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -5,6 +5,7 @@ import { IconButton } from 'browndash-components'; import { action, computed, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { FaUndo } from 'react-icons/fa'; +import { DateField } from '../../fields/DateField'; import { Doc, DocListCast, Field, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, DocData } from '../../fields/DocSymbols'; import { InkField } from '../../fields/InkField'; @@ -35,7 +36,6 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; import React = require('react'); import _ = require('lodash'); -import { DateField } from '../../fields/DateField'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -49,64 +49,39 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P private _offset = { x: 0, y: 0 }; // offset from click pt to inner edge of resize border private _snapPt = { x: 0, y: 0 }; // last snapped location of resize border private _inkDragDocs: { doc: Doc; x: number; y: number; width: number; height: number }[] = []; + private _interactionLock?: boolean; + @observable _showNothing = true; @observable private _accumulatedTitle = ''; @observable private _titleControlString: string = '#title'; @observable private _editingTitle = false; @observable private _hidden = false; - @observable public AddToSelection = false; // if Shift is pressed, then this should be set so that clicking on the selection background is ignored so overlapped documents can be added to the selection set. - @observable public pushIcon: IconProp = 'arrow-alt-circle-up'; - @observable public pullIcon: IconProp = 'arrow-alt-circle-down'; - @observable public pullColor: string = 'white'; @observable private _isRotating: boolean = false; @observable private _isRounding: boolean = false; - @observable private showLayoutAcl: boolean = false; + @observable private _showLayoutAcl: boolean = false; + @observable private _showRotCenter = false; // whether to show a draggable green dot that represents the center of rotation + @observable private _rotCenter = [0, 0]; // the center of rotation in object coordinates (0,0) = object center (not top left!) constructor(props: any) { super(props); DocumentDecorations.Instance = this; - reaction( - () => SelectionManager.Views().slice(), - action(views => { - this._showNothing = !DocumentView.LongPress && views.length === 1; // show decorations if multiple docs are selected or we're long pressing - this._editingTitle = false; - }) - ); - document.addEventListener( - // show decorations whenever pointer moves outside of selection bounds. - 'pointermove', - action(e => { - if (this.Bounds.x || this.Bounds.y || this.Bounds.r || this.Bounds.b) { - if (this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX + 10 || this.Bounds.r < e.clientX - 10 || this.Bounds.y > e.clientY + 10 || this.Bounds.b < e.clientY - 10)) { - this._showNothing = false; - } else { - this._showNothing = true; - } - } - }) - ); + document.addEventListener('pointermove', // show decorations whenever pointer moves outside of selection bounds. + action(e => (this._showNothing = !(this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX + 10 || this.Bounds.r < e.clientX - 10 || this.Bounds.y > e.clientY + 10 || this.Bounds.b < e.clientY - 10))))); // prettier-ignore } - @computed - get Bounds() { - if (LinkFollower.IsFollowing || DocumentView.ExploreMode) return { x: 0, y: 0, r: 0, b: 0 }; - const views = SelectionManager.Views(); - return views - .filter(dv => dv.props.renderDepth > 0) - .map(dv => dv.getBounds()) - .reduce( - (bounds, rect) => - !rect - ? bounds - : { - x: Math.min(rect.left, bounds.x), - y: Math.min(rect.top, bounds.y), - r: Math.max(rect.right, bounds.r), - b: Math.max(rect.bottom, bounds.b), - c: views.length === 1 ? rect.center : undefined, - }, - { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as { X: number; Y: number } | undefined } - ); + @computed get Bounds() { + return (LinkFollower.IsFollowing || DocumentView.ExploreMode) ? + { x: 0, y: 0, r: 0, b: 0 } + : SelectionManager.Views() + .filter(dv => dv.props.renderDepth > 0) + .map(dv => dv.getBounds()) + .reduce((bounds, rect) => !rect ? bounds + : { x: Math.min(rect.left, bounds.x), + y: Math.min(rect.top, bounds.y), + r: Math.max(rect.right, bounds.r), + b: Math.max(rect.bottom, bounds.b), + c: SelectionManager.Views().length === 1 ? rect.center : undefined }, + { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as { X: number; Y: number } | undefined }); // prettier-ignore } @action @@ -162,48 +137,43 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } }; - @action onContainerDown = (e: React.PointerEvent): void => { - const first = SelectionManager.Views()[0]; - const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc); + onContainerDown = (e: React.PointerEvent) => { + const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].rootDoc); if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) { - setupMoveUpEvents( - this, - e, - e => this.onBackgroundMove(true, e), - e => {}, - emptyFunction - ); + setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), emptyFunction, emptyFunction); + e.stopPropagation(); } }; - @action onTitleDown = (e: React.PointerEvent): void => { - const first = SelectionManager.Views()[0]; - const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc); + onTitleDown = (e: React.PointerEvent) => { + const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].rootDoc); if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) { setupMoveUpEvents( this, e, e => this.onBackgroundMove(true, e), - e => {}, + emptyFunction, action(e => { !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('#') ? this.selectionTitle : this._titleControlString); this._editingTitle = true; this._keyinput.current && setTimeout(this._keyinput.current.focus); }) ); + e.stopPropagation(); } }; - onBackgroundDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction); - + onBackgroundDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction); + e.stopPropagation(); + }; @action onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => { - const first = SelectionManager.Views()[0]; - const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc); + const dragDocView = SelectionManager.Views()[0]; + const effectiveLayoutAcl = GetEffectiveAcl(dragDocView.rootDoc); if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment) { return false; } - const dragDocView = SelectionManager.Views()[0]; const containers = new Set<Doc | undefined>(); SelectionManager.Views().forEach(v => containers.add(DocCast(v.rootDoc.embedContainer))); if (containers.size > 1) return false; @@ -234,9 +204,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P _deleteAfterIconify = false; _iconifyBatch: UndoManager.Batch | undefined; onCloseClick = (forceDeleteOrIconify: boolean | undefined) => { - const views = SelectionManager.Views() - .slice() - .filter(v => v && v.props.renderDepth > 0); + const views = SelectionManager.Views().filter(v => v && v.props.renderDepth > 0); if (forceDeleteOrIconify === false && this._iconifyBatch) return; this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false; var iconifyingCount = views.length; @@ -251,7 +219,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P iconView.props.removeDocument?.(iconView.props.Document); } }); - views.forEach(v => SelectionManager.DeselectView()); + views.forEach(SelectionManager.DeselectView); } this._iconifyBatch?.end(); this._iconifyBatch = undefined; @@ -267,21 +235,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (forceDeleteOrIconify) finished(forceDeleteOrIconify); else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished)); }; + onMaximizeDown = (e: React.PointerEvent) => { - setupMoveUpEvents( - this, - e, - () => { - DragManager.StartWindowDrag?.(e, [SelectionManager.Views().slice(-1)[0].rootDoc]); - return true; - }, - emptyFunction, - this.onMaximizeClick, - false, - false - ); + setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views().lastElement().rootDoc]) ?? false, emptyFunction, this.onMaximizeClick, false, false); + e.stopPropagation(); }; - onMaximizeClick = (e: any): void => { const selectedDocs = SelectionManager.Views(); if (selectedDocs.length) { @@ -319,63 +277,51 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P SelectionManager.DeselectAll(); }; - onSelectorClick = () => SelectionManager.Views()?.[0]?.props.docViewPath?.().lastElement()?.select(false); + onSelectContainerDocClick = () => SelectionManager.Views()?.[0]?.props.docViewPath?.().lastElement()?.select(false); /** - * Handles setting up events when user clicks on the border radius editor - * @param e PointerEvent + * sets up events when user clicks on the border radius editor */ @action onRadiusDown = (e: React.PointerEvent): void => { this._isRounding = DocumentView.Interacting = true; this._resizeUndo = UndoManager.StartBatch('DocDecs set radius'); - // Call util move event function setupMoveUpEvents( - this, // target - e, // pointerEvent - (e, down) => { - const x = this.Bounds.x + 3; - const y = this.Bounds.y + 3; + this, + e, + e => { + const [x, y] = [this.Bounds.x + 3, this.Bounds.y + 3]; const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); - let dist = Math.sqrt((e.clientX - x) * (e.clientX - x) + (e.clientY - y) * (e.clientY - y)); - if (e.clientX < x && e.clientY < y) dist = 0; - SelectionManager.Views() - .map(dv => dv.props.Document) - .map(doc => { - const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2); - const ratio = dist / maxDist; - const radius = Math.min(1, ratio) * docMax; - doc.layout_borderRounding = `${radius}px`; - }); + const dist = e.clientX < x && e.clientY < y ? 0 : Math.sqrt((e.clientX - x) * (e.clientX - x) + (e.clientY - y) * (e.clientY - y)); + SelectionManager.Docs().map(doc => { + const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2); + const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box + doc.layout_borderRounding = `${radius}px`; + }); return false; - }, // moveEvent + }, action(e => { DocumentView.Interacting = this._isRounding = false; this._resizeUndo?.end(); }), // upEvent - e => {}, // clickEvent, + emptyFunction, true ); + e.stopPropagation(); }; @action onLockDown = (e: React.PointerEvent): void => { - // Call util move event function setupMoveUpEvents( - this, // target - e, // pointerEvent - returnFalse, // moveEvent - emptyFunction, // upEvent - e => { - UndoManager.RunInBatch( - () => - SelectionManager.Views().map(dv => { - dv.rootDoc._lockedPosition = !dv.rootDoc._lockedPosition; - dv.rootDoc._pointerEvents = dv.rootDoc._lockedPosition ? 'none' : undefined; - }), - 'toggleBackground' - ); - } // clickEvent + this, + e, + returnFalse, // don't care about move or up event, + emptyFunction, // just care about whether we get a click event + e => UndoManager.RunInBatch( + () => SelectionManager.Docs().forEach(doc => + doc._pointerEvents = (doc._lockedPosition = !doc._lockedPosition)? 'none' : undefined ), + 'toggleBackground' ) // prettier-ignore ); + e.stopPropagation(); }; setRotateCenter = (seldocview: DocumentView, rotCenter: number[]) => { @@ -393,16 +339,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P setupMoveUpEvents( this, e, - action((e: PointerEvent, down: number[], delta: number[]) => { - this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]); - return false; - }), // moveEvent - action(action(() => (this._isRotating = false))), // upEvent - action((e, doubleTap) => { - seldocview.rootDoc.rotation_centerX = 0; - seldocview.rootDoc.rotation_centerY = 0; - }) - ); + (e: PointerEvent, down: number[], delta: number[]) => // return false to keep getting events + this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]) as any as boolean, + action(e => (this._isRotating = false)), // upEvent + action(e => (seldocview.rootDoc.rotation_centerX = seldocview.rootDoc.rotation_centerY = 0)) + ); // prettier-ignore }; @action @@ -473,6 +414,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P onPointerDown = (e: React.PointerEvent): void => { SnappingManager.SetIsResizing(SelectionManager.Docs().lastElement()); setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); + e.stopPropagation(); DocumentView.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them this._resizeHdlId = e.currentTarget.className; const bounds = e.currentTarget.getBoundingClientRect(); @@ -482,46 +424,44 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P SelectionManager.Views().forEach(docView => docView.CollectionFreeFormView?.dragStarting(false, false)); }; - _lock: any; + projectDragToAspect = (e: PointerEvent, docView: DocumentView, fixedAspect: number) => { + // need to generalize for bl and tr drag handles + const project = (p: number[], a: number[], b: number[]) => { + const atob = [b[0] - a[0], b[1] - a[1]]; + const atop = [p[0] - a[0], p[1] - a[1]]; + const len = atob[0] * atob[0] + atob[1] * atob[1]; + let dot = atop[0] * atob[0] + atop[1] * atob[1]; + const t = dot / len; + dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]); + return [a[0] + atob[0] * t, a[1] + atob[1] * t]; + }; + const tl = docView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + return project([e.clientX + this._offset.x, e.clientY + this._offset.y], tl, [tl[0] + fixedAspect, tl[1] + 1]); + }; onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { const first = SelectionManager.Views()[0]; const effectiveAcl = GetEffectiveAcl(first.rootDoc); if (!(effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment)) return false; if (!first) return false; - let thisPt = { x: e.clientX - this._offset.x, y: e.clientY - this._offset.y }; var fixedAspect = Doc.NativeAspect(first.layoutDoc); const dragHdl = this._resizeHdlId.split(' ')[0].replace('documentDecorations-', '').replace('Resizer', ''); - // do snapping of drag point - if (fixedAspect && (dragHdl === 'bottomRight' || dragHdl === 'topLeft')) { - // need to generalize for bl and tr drag handles - const project = (p: number[], a: number[], b: number[]) => { - const atob = [b[0] - a[0], b[1] - a[1]]; - const atop = [p[0] - a[0], p[1] - a[1]]; - const len = atob[0] * atob[0] + atob[1] * atob[1]; - let dot = atop[0] * atob[0] + atop[1] * atob[1]; - const t = dot / len; - dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]); - return [a[0] + atob[0] * t, a[1] + atob[1] * t]; - }; - const tl = first.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - const drag = project([e.clientX + this._offset.x, e.clientY + this._offset.y], tl, [tl[0] + fixedAspect, tl[1] + 1]); - thisPt = DragManager.snapDragAspect(drag, fixedAspect); - } else { - thisPt = DragManager.snapDrag(e, -this._offset.x, -this._offset.y, this._offset.x, this._offset.y); - } + const thisPt = // do snapping of drag point + fixedAspect && (dragHdl === 'bottomRight' || dragHdl === 'topLeft') + ? DragManager.snapDragAspect(this.projectDragToAspect(e, first, fixedAspect), fixedAspect) + : DragManager.snapDrag(e, -this._offset.x, -this._offset.y, this._offset.x, this._offset.y); const { scale, refPt } = this.getResizeVals(thisPt, dragHdl); - // resize selected docs - !this._lock && runInAction(async () => { - this._lock = true; + + !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; this._snapPt = thisPt; e.ctrlKey && (SelectionManager.Views().forEach(docView => !Doc.NativeHeight(docView.props.Document) && docView.toggleNativeDimensions())); const fixedAspect = SelectionManager.Docs().some(this.hasFixedAspect); + const scaleAspect = {x:scale.x === 1 && fixedAspect ? scale.y : scale.x, y: scale.x !== 1 && fixedAspect ? scale.x : scale.y}; SelectionManager.Views().forEach(docView => - this.resizeView(docView, refPt, scale.x === 1 && fixedAspect ? scale.y : scale.x, - scale.x !== 1 && fixedAspect ? scale.x : scale.y, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore - await new Promise<any>(res => setTimeout(() => res(this._lock = undefined))); + this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore + await new Promise<any>(res => setTimeout(() => res(this._interactionLock = undefined))); }); // prettier-ignore return false; @@ -554,16 +494,15 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P // // resize a single DocumentView about the specified reference point, possibly setting/updating the native dimensions of the Doc // - resizeView = (docView: DocumentView, refPt: number[], scaleX: number, scaleY: number, opts: { dragHdl: string; ctrlKey: boolean }) => { + resizeView = (docView: DocumentView, refPt: number[], scale: { x: number; y: number }, opts: { dragHdl: string; ctrlKey: boolean }) => { const doc = docView.rootDoc; if (doc.isGroup) { DocListCast(doc.data) .map(member => DocumentManager.Instance.getDocumentView(member, docView)!) - .forEach(member => this.resizeView(member, refPt, scaleX, scaleY, opts)); - doc.xPadding = NumCast(doc.xPadding) * scaleX; - doc.yPadding = NumCast(doc.yPadding) * scaleY; + .forEach(member => this.resizeView(member, refPt, scale, opts)); + doc.xPadding = NumCast(doc.xPadding) * scale.x; + doc.yPadding = NumCast(doc.yPadding) * scale.y; } else { - const doc = docView.rootDoc; const refCent = docView.props.ScreenToLocalTransform().transformPoint(refPt[0], refPt[1]); // fixed reference point for resize (ie, a point that doesn't move) const [nwidth, nheight] = [docView.nativeWidth, docView.nativeHeight]; const [initWidth, initHeight] = [NumCast(doc._width, 1), NumCast(doc._height)]; @@ -574,12 +513,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P (doc.layout_reflowVertical && (opts.dragHdl === 'bottom' || opts.dragHdl === 'top' || opts.ctrlKey)); // eg rtf, web, pdf if (nwidth && nheight && !modifyNativeDim) { // eg., dragging right resizer on PDF -- enforce native dimensions because not expliclty overridden with ctrl or bottom resize drag - scaleX === 1 ? (scaleX = scaleY) : (scaleY = scaleX); + scale.x === 1 ? (scale.x = scale.y) : (scale.x = scale.x); } if (['right', 'left'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeWidth(doc)) { const setData = Doc.NativeWidth(Doc.GetProto(doc)) === doc.nativeWidth; - doc.nativeWidth = scaleX * Doc.NativeWidth(doc); + doc.nativeWidth = scale.x * Doc.NativeWidth(doc); if (setData) Doc.SetNativeWidth(Doc.GetProto(doc), NumCast(doc.nativeWidth)); if (doc.layout_reflowVertical && !NumCast(doc.nativeHeight)) { doc._nativeHeight = (initHeight / initWidth) * nwidth; // initializes the nativeHeight for a PDF @@ -587,18 +526,18 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } if (['bottom', 'top'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeHeight(doc)) { const setData = Doc.NativeHeight(Doc.GetProto(doc)) === doc.nativeHeight; - doc._nativeHeight = scaleY * Doc.NativeHeight(doc); + doc._nativeHeight = scale.y * Doc.NativeHeight(doc); if (setData) Doc.SetNativeHeight(Doc.GetProto(doc), NumCast(doc._nativeHeight)); } - doc._width = NumCast(doc._width) * scaleX; - doc._height = NumCast(doc._height) * scaleY; + doc._width = NumCast(doc._width) * scale.x; + doc._height = NumCast(doc._height) * scale.y; const { deltaX, deltaY } = this.realignRefPt(doc, refCent, initWidth, initHeight); doc.x = NumCast(doc.x) + deltaX; doc.y = NumCast(doc.y) + deltaY; doc._layout_modificationDate = new DateField(); - scaleY !== 1 && (doc._layout_autoHeight = undefined); + scale.y !== 1 && (doc._layout_autoHeight = undefined); } }; @@ -628,10 +567,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onPointerUp = (e: PointerEvent): void => { SnappingManager.SetIsResizing(undefined); - this._resizeHdlId = ''; + SnappingManager.clearSnapLines(); DocumentView.Interacting = false; + this._resizeHdlId = ''; this._resizeUndo?.end(); - SnappingManager.clearSnapLines(); // detect layout_autoHeight gesture and apply SelectionManager.Docs().forEach(doc => NumCast(doc._height) < 20 && (doc._layout_autoHeight = true)); @@ -639,16 +578,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P this._inkDragDocs .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] })) .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => { - Doc.GetProto(doc).data = new InkField( - inkPts.map( - ( - ipt // (new x — oldx) + newWidth * (oldxpoint /oldWidth) - ) => ({ - X: NumCast(doc.x) - x + (NumCast(doc.width) * ipt.X) / width, - Y: NumCast(doc.y) - y + (NumCast(doc.height) * ipt.Y) / height, - }) - ) - ); + Doc.GetProto(doc).data = new InkField(inkPts.map( + (ipt) => ({// (new x — oldx) + newWidth * (oldxpoint /oldWidth) + X: NumCast(doc.x) - x + (NumCast(doc.width) * ipt.X) / width, + Y: NumCast(doc.y) - y + (NumCast(doc.height) * ipt.Y) / height, + }))); // prettier-ignore Doc.SetNativeWidth(doc, undefined); Doc.SetNativeHeight(doc, undefined); }); @@ -676,8 +610,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P return SelectionManager.Views().some(docView => docView.rootDoc.layout_fieldKey === 'layout_icon'); } - @observable _showRotCenter = false; - @observable _rotCenter = [0, 0]; @computed get rotCenter() { if (SelectionManager.Views().length) { const seldocview = SelectionManager.Views()[0]; @@ -694,19 +626,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P return this._rotCenter; } - @observable _showNothing = true; - render() { const { b, r, x, y } = this.Bounds; - const bounds = { b, r, x, y }; const seldocview = SelectionManager.Views().lastElement(); - if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { + if (SnappingManager.GetIsDragging() || r - x < 1 || x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(r) || isNaN(b) || isNaN(x) || isNaN(y)) { setTimeout(action(() => (this._showNothing = true))); return null; } // sharing - const acl = GetEffectiveAcl(!this.showLayoutAcl ? Doc.GetProto(seldocview.rootDoc) : seldocview.rootDoc); + const acl = GetEffectiveAcl(!this._showLayoutAcl ? Doc.GetProto(seldocview.rootDoc) : seldocview.rootDoc); const docShareMode = HierarchyMapping.get(acl)!.name; const shareMode = StrCast(docShareMode); var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image; @@ -745,6 +674,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P </Tooltip> ); + const bounds = { b, r, x, y }; const leftBounds = this.props.boundsLeft; const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop; bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2; @@ -757,8 +687,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const useRotation = !hideResizers && seldocview.rootDoc.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate? const rotation = SelectionManager.Views().length == 1 ? NumCast(seldocview.rootDoc._rotation) : 0; - const resizerScheme = ''; - // Radius constants const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView; const borderRadius = numberValue(Cast(seldocview.rootDoc.layout_borderRounding, 'string', null)); @@ -801,7 +729,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P onPointerDown={e => e.stopPropagation()} /> ) : ( - <div className="documentDecorations-title" key="title" onPointerDown={e => e.stopPropagation}> + <div className="documentDecorations-title" key="title"> {hideTitle ? null : ( <span className="documentDecorations-titleSpan" onPointerDown={this.onTitleDown}> {this.selectionTitle} @@ -810,7 +738,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P {sharingMenu} {!useLock ? null : ( <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top"> - <div className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown} onContextMenu={e => e.preventDefault()}> + <div className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown}> <FontAwesomeIcon size="sm" icon="lock" /> </div> </Tooltip> @@ -827,30 +755,24 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P height: bounds.b - bounds.y + this._resizeBorderWidth + 'px', left: bounds.x - this._resizeBorderWidth / 2, top: bounds.y - this._resizeBorderWidth / 2, - pointerEvents: DocumentDecorations.Instance.AddToSelection || DocumentView.Interacting ? 'none' : 'all', + background: SnappingManager.GetShiftKey() ? undefined : 'yellow', + pointerEvents: SnappingManager.GetShiftKey() || DocumentView.Interacting ? 'none' : 'all', display: SelectionManager.Views().length <= 1 || hideDecorations ? 'none' : undefined, }} onPointerDown={this.onBackgroundDown} - onContextMenu={e => { - e.preventDefault(); - e.stopPropagation(); - }} /> {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? null : ( <div> <div className={`documentDecorations-container ${this._showNothing ? 'showNothing' : ''}`} - key="container" style={{ transform: `translate(${bounds.x - this._resizeBorderWidth / 2}px, ${bounds.y - this._resizeBorderWidth / 2 - this._titleHeight}px) rotate(${rotation}deg)`, - transformOrigin: `50% calc(50% + 10px)`, width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', height: bounds.b - bounds.y + this._resizeBorderWidth + (this._showNothing ? 0 : this._titleHeight) + 'px', }}> <div className="documentDecorations-topbar" style={{ - color: 'black', display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined, }} onPointerDown={this.onContainerDown}> @@ -861,37 +783,34 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P </div> {hideResizers ? null : ( <> - <div key="tl" className={`documentDecorations-topLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="t" className={`documentDecorations-topResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="tr" className={`documentDecorations-topRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="l" className={`documentDecorations-leftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="c" className={`documentDecorations-centerCont ${resizerScheme}`}></div> - <div key="r" className={`documentDecorations-rightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="bl" className={`documentDecorations-bottomLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - - {seldocview.props.renderDepth <= 1 || !seldocview.props.docViewPath().lastElement() ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')} + <div key="tl" className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} /> + <div key="t" className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} /> + <div key="tr" className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} /> + <div key="l" className="documentDecorations-leftResizer" onPointerDown={this.onPointerDown} /> + <div key="c" className="documentDecorations-centerCont" /> + <div key="r" className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} /> + <div key="bl" className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} /> + <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} /> + <div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} /> + {seldocview.props.renderDepth <= 1 || !seldocview.props.docViewPath().lastElement() + ? null + : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectContainerDocClick, 'tap to select containing document')} </> )} {useRounding && ( <div - key="rad" className="documentDecorations-borderRadius" style={{ - opacity: 0.5, background: `${this._isRounding ? Colors.MEDIUM_BLUE : SettingsManager.userColor}`, transform: `translate(${radiusHandleLocation ?? 0}px, ${(radiusHandleLocation ?? 0) + (this._showNothing ? 0 : this._titleHeight)}px)`, }} onPointerDown={this.onRadiusDown} - onContextMenu={e => e.preventDefault()} /> )} {hideDocumentButtonBar || this._showNothing ? null : ( <div className="link-button-container" - key="links" style={{ transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `, }}> |
