diff options
| author | bobzel <zzzman@gmail.com> | 2023-12-21 14:55:48 -0500 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2023-12-21 14:55:48 -0500 |
| commit | 1caba64ee0f32ee8af79263cd4ef2a8bc5d5146e (patch) | |
| tree | 0fa0e957d1f342fdc6ed4a4b43f5dddfddb1298a /src/client/views/DocumentDecorations.tsx | |
| parent | 02eb7da95df283606d4275a22d9451cef371c3b5 (diff) | |
| parent | 2691b951d96f2ce7652acbea9e340b61737b3b57 (diff) | |
Merge branch 'moreUpgrading' into dataViz-annotations
Diffstat (limited to 'src/client/views/DocumentDecorations.tsx')
| -rw-r--r-- | src/client/views/DocumentDecorations.tsx | 257 |
1 files changed, 129 insertions, 128 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index d4b474de9..7003485d2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,12 +1,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { IconButton } from 'browndash-components'; -import { action, computed, observable, runInAction } from 'mobx'; +import { action, computed, makeObservable, observable, 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 { AclAdmin, AclAugment, AclEdit } from '../../fields/DocSymbols'; import { InkField } from '../../fields/InkField'; import { RichTextField } from '../../fields/RichTextField'; import { ScriptField } from '../../fields/ScriptField'; @@ -33,11 +33,17 @@ import { LightboxView } from './LightboxView'; import { DocumentView, OpenWhereMod } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; -import React = require('react'); -import _ = require('lodash'); - +import * as React from 'react'; +import { ObservableReactComponent } from './ObservableReactComponent'; + +interface DocumentDecorationsProps { + PanelWidth: number; + PanelHeight: number; + boundsLeft: number; + boundsTop: number; +} @observer -export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { +export class DocumentDecorations extends ObservableReactComponent<DocumentDecorationsProps> { static Instance: DocumentDecorations; private _resizeHdlId = ''; private _keyinput = React.createRef<HTMLInputElement>(); @@ -61,27 +67,27 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @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) { + constructor(props: React.PropsWithChildren<DocumentDecorationsProps>) { super(props); + makeObservable(this); + DocumentDecorations.Instance = this; document.addEventListener('pointermove', // show decorations whenever pointer moves outside of selection bounds. action(e => { const center = {x: (this.Bounds.x+this.Bounds.r)/2, y: (this.Bounds.y+this.Bounds.b)/2}; const {x,y} = Utils.rotPt(e.clientX - center.x, e.clientY - center.y, - NumCast(SelectionManager.Views().lastElement()?.screenToLocalTransform().Rotate)); + NumCast(SelectionManager.Views.lastElement()?.screenToLocalTransform().Rotate)); (this._showNothing = !(this.Bounds.x !== Number.MAX_VALUE && // - (this.Bounds.x > center.x+x + this._resizeBorderWidth / 2 || - this.Bounds.r < center.x+x - this._resizeBorderWidth / 2 || - this.Bounds.y > center.y+y + this._resizeBorderWidth / 2 || - this.Bounds.b < center.y+y - this._resizeBorderWidth / 2))); + (this.Bounds.x > center.x+x || this.Bounds.r < center.x+x || + this.Bounds.y > center.y+y || this.Bounds.b < center.y+y ))); })); // prettier-ignore } @computed get ClippedBounds() { const bounds = this.Bounds; - const leftBounds = this.props.boundsLeft; - const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop; + 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; bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight; const borderRadiusDraggerWidth = 15; @@ -93,15 +99,15 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @computed get Bounds() { return (LinkFollower.IsFollowing || DocumentView.ExploreMode) ? { x: 0, y: 0, r: 0, b: 0 } - : SelectionManager.Views() - .filter(dv => dv.props.renderDepth > 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 }, + 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 } @@ -115,14 +121,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P UndoManager.RunInBatch( () => titleFieldKey && - SelectionManager.Views().forEach(d => { + SelectionManager.Views.forEach(d => { if (titleFieldKey === 'title') { d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-'); - if (StrCast(d.rootDoc.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) { - Doc.RemFromMyPublished(d.rootDoc); + if (StrCast(d.Document.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) { + Doc.RemFromMyPublished(d.Document); } - if (!StrCast(d.rootDoc.title).startsWith('@') && this._accumulatedTitle.startsWith('@')) { - Doc.AddToMyPublished(d.rootDoc); + if (!StrCast(d.Document.title).startsWith('@') && this._accumulatedTitle.startsWith('@')) { + Doc.AddToMyPublished(d.Document); } } //@ts-ignore @@ -130,20 +136,20 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (titleField.toString().startsWith('<this>')) { const title = titleField.toString().replace(/<this>\.?/, ''); - const curKey = Doc.LayoutFieldKey(d.rootDoc); + const curKey = Doc.LayoutFieldKey(d.Document); if (curKey !== title) { if (title) { if (d.dataDoc[title] === undefined || d.dataDoc[title] instanceof RichTextField || typeof d.dataDoc[title] === 'string') { - d.rootDoc.layout_fieldKey = `layout_${title}`; - d.rootDoc[`layout_${title}`] = FormattedTextBox.LayoutString(title); - d.rootDoc[`${title}_nativeWidth`] = d.rootDoc[`${title}_nativeHeight`] = 0; + d.Document.layout_fieldKey = `layout_${title}`; + d.Document[`layout_${title}`] = FormattedTextBox.LayoutString(title); + d.Document[`${title}_nativeWidth`] = d.Document[`${title}_nativeHeight`] = 0; } } else { - d.rootDoc.layout_fieldKey = undefined; + d.Document.layout_fieldKey = undefined; } } } else { - Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true); + Doc.SetInPlace(d.Document, titleFieldKey, titleField, true); } }), 'edit title' @@ -159,7 +165,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }; onContainerDown = (e: React.PointerEvent) => { - const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].rootDoc); + const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views[0].Document); if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) { setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), emptyFunction, emptyFunction); e.stopPropagation(); @@ -167,7 +173,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }; onTitleDown = (e: React.PointerEvent) => { - const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].rootDoc); + const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views[0].Document); if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) { setupMoveUpEvents( this, @@ -190,27 +196,27 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }; @action onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => { - const dragDocView = SelectionManager.Views()[0]; - const effectiveLayoutAcl = GetEffectiveAcl(dragDocView.rootDoc); + const dragDocView = SelectionManager.Views[0]; + const effectiveLayoutAcl = GetEffectiveAcl(dragDocView.Document); if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment) { return false; } const containers = new Set<Doc | undefined>(); - SelectionManager.Views().forEach(v => containers.add(DocCast(v.rootDoc.embedContainer))); + SelectionManager.Views.forEach(v => containers.add(DocCast(v.Document.embedContainer))); if (containers.size > 1) return false; const { left, top } = dragDocView.getBounds() || { left: 0, top: 0 }; const dragData = new DragManager.DocumentDragData( - SelectionManager.Views().map(dv => dv.props.Document), - dragDocView.props.dropAction + SelectionManager.Views.map(dv => dv.Document), + dragDocView._props.dropAction ); - dragData.offset = dragDocView.props.ScreenToLocalTransform().transformDirection(e.x - left, e.y - top); - dragData.moveDocument = dragDocView.props.moveDocument; - dragData.removeDocument = dragDocView.props.removeDocument; + dragData.offset = dragDocView._props.ScreenToLocalTransform().transformDirection(e.x - left, e.y - top); + dragData.moveDocument = dragDocView._props.moveDocument; + dragData.removeDocument = dragDocView._props.removeDocument; dragData.isDocDecorationMove = true; dragData.canEmbed = dragTitle; this._hidden = true; DragManager.StartDocumentDrag( - SelectionManager.Views().map(dv => dv.ContentDiv!), + SelectionManager.Views.map(dv => dv.ContentDiv!), dragData, e.x, e.y, @@ -225,7 +231,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P _deleteAfterIconify = false; _iconifyBatch: UndoManager.Batch | undefined; onCloseClick = (forceDeleteOrIconify: boolean | undefined) => { - const views = SelectionManager.Views().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; @@ -233,11 +239,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if ((force || --iconifyingCount === 0) && this._iconifyBatch) { if (this._deleteAfterIconify) { views.forEach(iconView => { - Doc.setNativeView(iconView.props.Document); - if (iconView.props.Document.activeFrame) { - iconView.props.Document.opacity = 0; // bcz: hacky ... allows inkMasks and other documents to be "turned off" without removing them from the animated collection which allows them to function properly in a presenation. + Doc.setNativeView(iconView.Document); + if (iconView.Document.activeFrame) { + iconView.Document.opacity = 0; // bcz: hacky ... allows inkMasks and other documents to be "turned off" without removing them from the animated collection which allows them to function properly in a presenation. } else { - iconView.props.removeDocument?.(iconView.props.Document); + iconView._props.removeDocument?.(iconView.Document); } }); views.forEach(SelectionManager.DeselectView); @@ -258,27 +264,27 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }; onMaximizeDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views().lastElement().rootDoc]) ?? false, emptyFunction, this.onMaximizeClick, false, false); + setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views.lastElement().Document]) ?? false, emptyFunction, this.onMaximizeClick, false, false); e.stopPropagation(); }; onMaximizeClick = (e: any): void => { - const selectedDocs = SelectionManager.Views(); + const selectedDocs = SelectionManager.Views; if (selectedDocs.length) { if (e.ctrlKey) { // open an embedding in a new tab with Ctrl Key - CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].rootDoc), OpenWhereMod.right); + CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].Document), OpenWhereMod.right); } else if (e.shiftKey) { // open centered in a new workspace with Shift Key - const embedding = Doc.MakeEmbedding(selectedDocs[0].rootDoc); + const embedding = Doc.MakeEmbedding(selectedDocs[0].Document); embedding.embedContainer = undefined; embedding.x = -NumCast(embedding._width) / 2; embedding.y = -NumCast(embedding._height) / 2; CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right); } else if (e.altKey) { // open same document in new tab - CollectionDockingView.ToggleSplit(selectedDocs[0].rootDoc, OpenWhereMod.right); + CollectionDockingView.ToggleSplit(selectedDocs[0].Document, OpenWhereMod.right); } else { - var openDoc = selectedDocs[0].rootDoc; + var openDoc = selectedDocs[0].Document; if (openDoc.layout_fieldKey === 'layout_icon') { openDoc = DocListCast(openDoc.proto_embeddings).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc); Doc.deiconifyView(openDoc); @@ -286,7 +292,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P LightboxView.Instance.SetLightboxDoc( openDoc, undefined, - selectedDocs.slice(1).map(view => view.rootDoc) + selectedDocs.slice(1).map(view => view.Document) ); } } @@ -294,11 +300,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }; onIconifyClick = (): void => { - SelectionManager.Views().forEach(dv => dv?.iconify()); + SelectionManager.Views.forEach(dv => dv?.iconify()); SelectionManager.DeselectAll(); }; - onSelectContainerDocClick = () => SelectionManager.Views()?.[0]?.props.docViewPath?.().lastElement()?.select(false); + onSelectContainerDocClick = () => SelectionManager.Views?.[0]?._props.docViewPath?.().lastElement()?.select(false); /** * sets up events when user clicks on the border radius editor */ @@ -313,7 +319,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P 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); 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 => { + 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`; @@ -338,7 +344,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P 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 => + () => SelectionManager.Docs.forEach(doc => doc._pointerEvents = (doc._lockedPosition = !doc._lockedPosition)? 'none' : undefined ), 'toggleBackground' ) // prettier-ignore ); @@ -346,24 +352,24 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }; setRotateCenter = (seldocview: DocumentView, rotCenter: number[]) => { - const newloccentern = seldocview.props.ScreenToLocalTransform().transformPoint(rotCenter[0], rotCenter[1]); + const newloccentern = seldocview._props.ScreenToLocalTransform().transformPoint(rotCenter[0], rotCenter[1]); const newlocenter = [newloccentern[0] - NumCast(seldocview.layoutDoc._width) / 2, newloccentern[1] - NumCast(seldocview.layoutDoc._height) / 2]; - const final = Utils.rotPt(newlocenter[0], newlocenter[1], -(NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI); - seldocview.rootDoc.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width); - seldocview.rootDoc.rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height); + const final = Utils.rotPt(newlocenter[0], newlocenter[1], -(NumCast(seldocview.Document._rotation) / 180) * Math.PI); + seldocview.Document.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width); + seldocview.Document.rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height); }; @action onRotateCenterDown = (e: React.PointerEvent): void => { this._isRotating = true; - const seldocview = SelectionManager.Views()[0]; + const seldocview = SelectionManager.Views[0]; setupMoveUpEvents( this, e, (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)) + action(e => (seldocview.Document.rotation_centerX = seldocview.Document.rotation_centerY = 0)) ); // prettier-ignore }; @@ -372,27 +378,27 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P this._isRotating = true; const rcScreen = { X: this.rotCenter[0], Y: this.rotCenter[1] }; const rotateUndo = UndoManager.StartBatch('drag rotation'); - const selectedInk = SelectionManager.Views().filter(i => i.ComponentView instanceof InkingStroke); + const selectedInk = SelectionManager.Views.filter(i => i.ComponentView instanceof InkingStroke); const centerPoint = this.rotCenter.slice(); const infos = new Map<Doc, { unrotatedDocPos: { x: number; y: number }; startRotCtr: { x: number; y: number }; accumRot: number }>(); - const seldocview = SelectionManager.Views()[0]; - SelectionManager.Views().forEach(dv => { - const accumRot = (NumCast(dv.rootDoc._rotation) / 180) * Math.PI; - const localRotCtr = dv.props.ScreenToLocalTransform().transformPoint(rcScreen.X, rcScreen.Y); - const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.rootDoc.width) / 2, localRotCtr[1] - NumCast(dv.rootDoc.height) / 2]; + const seldocview = SelectionManager.Views[0]; + SelectionManager.Views.forEach(dv => { + const accumRot = (NumCast(dv.Document._rotation) / 180) * Math.PI; + const localRotCtr = dv._props.ScreenToLocalTransform().transformPoint(rcScreen.X, rcScreen.Y); + const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document.width) / 2, localRotCtr[1] - NumCast(dv.Document.height) / 2]; const startRotCtr = Utils.rotPt(localRotCtrOffset[0], localRotCtrOffset[1], -accumRot); - const unrotatedDocPos = { x: NumCast(dv.rootDoc.x) + localRotCtrOffset[0] - startRotCtr.x, y: NumCast(dv.rootDoc.y) + localRotCtrOffset[1] - startRotCtr.y }; - infos.set(dv.rootDoc, { unrotatedDocPos, startRotCtr, accumRot }); + const unrotatedDocPos = { x: NumCast(dv.Document.x) + localRotCtrOffset[0] - startRotCtr.x, y: NumCast(dv.Document.y) + localRotCtrOffset[1] - startRotCtr.y }; + infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot }); }); const infoRot = (angle: number, isAbs = false) => { - SelectionManager.Views().forEach( + SelectionManager.Views.forEach( action(dv => { - const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.rootDoc)!; + const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.Document)!; const endRotCtr = Utils.rotPt(startRotCtr.x, startRotCtr.y, isAbs ? angle : accumRot + angle); - infos.set(dv.rootDoc, { unrotatedDocPos, startRotCtr, accumRot: isAbs ? angle : accumRot + angle }); - dv.rootDoc.x = infos.get(dv.rootDoc)!.unrotatedDocPos.x - (endRotCtr.x - startRotCtr.x); - dv.rootDoc.y = infos.get(dv.rootDoc)!.unrotatedDocPos.y - (endRotCtr.y - startRotCtr.y); - dv.rootDoc._rotation = ((isAbs ? 0 : NumCast(dv.rootDoc._rotation)) + (angle * 180) / Math.PI) % 360; // Rotation between -360 and 360 + infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot: isAbs ? angle : accumRot + angle }); + dv.Document.x = infos.get(dv.Document)!.unrotatedDocPos.x - (endRotCtr.x - startRotCtr.x); + dv.Document.y = infos.get(dv.Document)!.unrotatedDocPos.y - (endRotCtr.y - startRotCtr.y); + dv.Document._rotation = ((isAbs ? 0 : NumCast(dv.Document._rotation)) + (angle * 180) / Math.PI) % 360; // Rotation between -360 and 360 }) ); }; @@ -412,7 +418,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P return false; }, // moveEvent action(() => { - const oldRotation = NumCast(seldocview.rootDoc._rotation); + const oldRotation = NumCast(seldocview.Document._rotation); const diff = oldRotation - Math.round(oldRotation / 45) * 45; if (Math.abs(diff) < 5) { if (selectedInk.length) { @@ -433,7 +439,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onPointerDown = (e: React.PointerEvent): void => { - SnappingManager.SetIsResizing(SelectionManager.Docs().lastElement()); + 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 @@ -442,7 +448,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P this._offset = { x: this._resizeHdlId.toLowerCase().includes('left') ? bounds.right - e.clientX : bounds.left - e.clientX, y: this._resizeHdlId.toLowerCase().includes('top') ? bounds.bottom - e.clientY : bounds.top - e.clientY }; this._resizeUndo = UndoManager.StartBatch('drag resizing'); this._snapPt = { x: e.pageX, y: e.pageY }; - SelectionManager.Views().forEach(docView => docView.CollectionFreeFormView?.dragStarting(false, false)); + SelectionManager.Views.forEach(docView => docView.CollectionFreeFormView?.dragStarting(false, false)); }; projectDragToAspect = (e: PointerEvent, docView: DocumentView, fixedAspect: number) => { @@ -456,12 +462,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P 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); + 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); + const first = SelectionManager.Views[0]; + const effectiveAcl = GetEffectiveAcl(first.Document); if (!(effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment)) return false; if (!first) return false; var fixedAspect = Doc.NativeAspect(first.layoutDoc); @@ -477,10 +483,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P !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); + e.ctrlKey && (SelectionManager.Views.forEach(docView => !Doc.NativeHeight(docView.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 => + SelectionManager.Views.forEach(docView => this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore await new Promise<any>(res => setTimeout(() => res(this._interactionLock = undefined))); }); // prettier-ignore @@ -516,7 +522,7 @@ 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[], scale: { x: number; y: number }, opts: { dragHdl: string; ctrlKey: boolean }) => { - const doc = docView.rootDoc; + const doc = docView.Document; if (doc.isGroup) { DocListCast(doc.data) .map(member => DocumentManager.Instance.getDocumentView(member, docView)!) @@ -524,7 +530,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P doc.xPadding = NumCast(doc.xPadding) * scale.x; doc.yPadding = NumCast(doc.yPadding) * scale.y; } else { - const refCent = docView.props.ScreenToLocalTransform().transformPoint(refPt[0], refPt[1]); // fixed reference point for resize (ie, a point that doesn't move) + 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)]; @@ -551,8 +557,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (setData) Doc.SetNativeHeight(Doc.GetProto(doc), NumCast(doc._nativeHeight)); } - doc._width = NumCast(doc._width) * scale.x; - doc._height = NumCast(doc._height) * scale.y; + doc._width = Math.max(1, NumCast(doc._width) * scale.x); + doc._height = Math.max(1, 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; @@ -594,7 +600,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P this._resizeUndo?.end(); // detect layout_autoHeight gesture and apply - SelectionManager.Docs().forEach(doc => NumCast(doc._height) < 20 && (doc._layout_autoHeight = true)); + SelectionManager.Docs.forEach(doc => NumCast(doc._height) < 20 && (doc._layout_autoHeight = true)); //need to change points for resize, or else rotation/control points will fail. this._inkDragDocs .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] })) @@ -611,26 +617,23 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @computed get selectionTitle(): string { - if (SelectionManager.Views().length === 1) { - const selected = SelectionManager.Views()[0]; - if (selected.ComponentView?.getTitle?.()) { - return selected.ComponentView.getTitle(); - } + if (SelectionManager.Views.length === 1) { + const selected = SelectionManager.Views[0]; if (this._titleControlString.startsWith('=')) { - return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || ''; + return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.Document, this: selected.layoutDoc }, console.log).result?.toString() || ''; } if (this._titleControlString.startsWith('#')) { - return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || '-unset-'; + return Field.toString(selected.Document[this._titleControlString.substring(1)] as Field) || '-unset-'; } return this._accumulatedTitle; } - return SelectionManager.Views().length > 1 ? '-multiple-' : '-unset-'; + return SelectionManager.Views.length > 1 ? '-multiple-' : '-unset-'; } @computed get rotCenter() { - const lastView = SelectionManager.Views().lastElement(); + const lastView = SelectionManager.Views.lastElement(); if (lastView) { - const invXf = lastView.props.ScreenToLocalTransform().inverse(); + const invXf = lastView._props.ScreenToLocalTransform().inverse(); const seldoc = lastView.layoutDoc; const loccenter = Utils.rotPt(NumCast(seldoc.rotation_centerX) * NumCast(seldoc._width), NumCast(seldoc.rotation_centerY) * NumCast(seldoc._height), invXf.Rotate); return invXf.transformPoint(loccenter.x + NumCast(seldoc._width) / 2, loccenter.y + NumCast(seldoc._height) / 2); @@ -640,31 +643,31 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P render() { const { b, r, x, y } = this.Bounds; - const seldocview = SelectionManager.Views().lastElement(); - if (SnappingManager.GetIsDragging() || r - x < 1 || x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(r) || isNaN(b) || isNaN(x) || isNaN(y)) { + const seldocview = SelectionManager.Views.lastElement(); + if (SnappingManager.IsDragging || 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.Document) : seldocview.Document); const docShareMode = HierarchyMapping.get(acl)!.name; const shareMode = StrCast(docShareMode); var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image; // hide the decorations if the parent chooses to hide it or if the document itself hides it - const hideDecorations = SnappingManager.GetIsResizing() || seldocview.props.hideDecorations || seldocview.rootDoc.layout_hideDecorations; + const hideDecorations = SnappingManager.IsResizing || seldocview._props.hideDecorations || seldocview.Document.layout_hideDecorations; const hideResizers = - ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating; - const hideTitle = this._showNothing || hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating; - const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating; + ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.Document)) || hideDecorations || seldocview._props.hideResizeHandles || seldocview.Document.layout_hideResizeHandles || this._isRounding || this._isRotating; + const hideTitle = this._showNothing || hideDecorations || seldocview._props.hideDecorationTitle || seldocview.Document.layout_hideDecorationTitle || this._isRounding || this._isRotating; + const hideDocumentButtonBar = hideDecorations || seldocview._props.hideDocumentButtonBar || seldocview.Document.layout_hideDocumentButtonBar || this._isRounding || this._isRotating; // if multiple documents have been opened at the same time, then don't show open button const hideOpenButton = this._showNothing || hideDecorations || - seldocview.props.hideOpenButton || - seldocview.rootDoc.layout_hideOpenButton || - SelectionManager.Views().some(docView => docView.rootDoc._dragOnlyWithinContainer || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) || + seldocview._props.hideOpenButton || + seldocview.Document.layout_hideOpenButton || + SelectionManager.Views.some(docView => docView.Document._dragOnlyWithinContainer || docView.Document.isGroup || docView.Document.layout_hideOpenButton) || this._isRounding || this._isRotating; const hideDeleteButton = @@ -672,11 +675,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P hideDecorations || this._isRounding || this._isRotating || - seldocview.props.hideDeleteButton || - seldocview.rootDoc.hideDeleteButton || - SelectionManager.Views().some(docView => { - const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit; - return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin; + seldocview._props.hideDeleteButton || + seldocview.Document.hideDeleteButton || + SelectionManager.Views.some(docView => { + const collectionAcl = docView._props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView._props.docViewPath().lastElement().dataDoc) : AclEdit; + return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.Document) !== AclAdmin; }); const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => ( <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top"> @@ -688,13 +691,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const bounds = this.ClippedBounds; const useLock = bounds.r - bounds.x > 135 && seldocview.CollectionFreeFormDocumentView; - 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 ? seldocview.screenToLocalTransform().inverse().RotateDeg : 0; + const useRotation = !hideResizers && seldocview.Document.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate? + const rotation = SelectionManager.Views.length == 1 ? seldocview.screenToLocalTransform().inverse().RotateDeg : 0; // 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)); - const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2); + const borderRadius = numberValue(Cast(seldocview.Document.layout_borderRounding, 'string', null)); + const docMax = Math.min(NumCast(seldocview.Document.width) / 2, NumCast(seldocview.Document.height) / 2); const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); const radiusHandle = (borderRadius / docMax) * maxDist; const radiusHandleLocation = Math.min(radiusHandle, maxDist); @@ -742,7 +745,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}> + <div className="documentDecorations-lock" style={{ color: seldocview.Document._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown}> <FontAwesomeIcon size="sm" icon="lock" /> </div> </Tooltip> @@ -751,7 +754,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P ); const centery = hideTitle ? 0 : this._titleHeight; const transformOrigin = `${50}% calc(50% + ${centery / 2}px)`; - const freeformDoc = SelectionManager.Views().some(v => v.CollectionFreeFormDocumentView); + const freeformDoc = SelectionManager.Views.some(v => v.CollectionFreeFormDocumentView); return ( <div className="documentDecorations" style={{ display: this._showNothing && !freeformDoc ? 'none' : undefined }}> <div @@ -762,9 +765,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P left: bounds.x - this._resizeBorderWidth / 2, top: bounds.y - this._resizeBorderWidth / 2, transformOrigin, - background: SnappingManager.GetShiftKey() ? undefined : 'yellow', - pointerEvents: SnappingManager.GetShiftKey() || DocumentView.Interacting ? 'none' : 'all', - display: SelectionManager.Views().length <= 1 || hideDecorations ? 'none' : undefined, + background: SnappingManager.ShiftKey ? undefined : 'yellow', + pointerEvents: SnappingManager.ShiftKey || DocumentView.Interacting ? 'none' : 'all', + display: SelectionManager.Views.length <= 1 || hideDecorations ? 'none' : undefined, transform: `rotate(${rotation}deg)`, }} onPointerDown={this.onBackgroundDown} @@ -801,11 +804,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P <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')} </> )} + {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 className="documentDecorations-borderRadius" @@ -823,7 +824,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P style={{ transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `, }}> - <DocumentButtonBar views={SelectionManager.Views} /> + <DocumentButtonBar views={() => SelectionManager.Views} /> </div> )} </div> |
