aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/DocumentDecorations.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-11-18 23:47:13 -0500
committerbobzel <zzzman@gmail.com>2023-11-18 23:47:13 -0500
commit2b0e4ccc096998eb1d727f2e85ea8c1a63b27e08 (patch)
tree1d5bc81e4cf74b20b599a5069c3448a2de4784fb /src/client/views/DocumentDecorations.tsx
parent1b568af6b2725b9eed6f591bfce193d39d5804de (diff)
fixed ctrl-drag for expressions, maps, fform doc selections. fixed using shift to add Doc to a selection and also when bounding box already covers the doc to add. fixed dragging maximize button to start goldenlayout drag properly. fixed typing character to group,etc a multiselection when a text doc has input focus. fixed using clusters. add Shift-U to ungroup alternate group style. multi-select blurs() all active inputs. shift-selecting a multi-selected Doc, deselects it.
Diffstat (limited to 'src/client/views/DocumentDecorations.tsx')
-rw-r--r--src/client/views/DocumentDecorations.tsx341
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) `,
}}>