aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/DocumentDecorations.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/DocumentDecorations.tsx')
-rw-r--r--src/client/views/DocumentDecorations.tsx283
1 files changed, 208 insertions, 75 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d5db45494..cec03c991 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -12,7 +12,7 @@ import { InkField } from '../../fields/InkField';
import { ScriptField } from '../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../fields/Types';
import { GetEffectiveAcl } from '../../fields/util';
-import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
+import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
import { Docs } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
import { DragManager } from '../util/DragManager';
@@ -30,7 +30,6 @@ import { LightboxView } from './LightboxView';
import { DocumentView } from './nodes/DocumentView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { ImageBox } from './nodes/ImageBox';
-import { VideoBox } from './nodes/VideoBox';
import React = require('react');
@observer
@@ -71,8 +70,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
);
}
+ @observable overrideBounds = false;
@computed
get Bounds() {
+ if (this.overrideBounds) return { x: 0, y: 0, r: 0, b: 0 };
const views = SelectionManager.Views();
return views
.filter(dv => dv.props.renderDepth > 0)
@@ -113,7 +114,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}
}
//@ts-ignore
- const titleField = +this._accumulatedTitle === this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle;
+ const titleField = +this._accumulatedTitle == this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle;
Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
if (d.rootDoc.syncLayoutFieldWithTitle) {
@@ -141,6 +142,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}
};
+ @action onContainerDown = (e: React.PointerEvent): void => {
+ setupMoveUpEvents(
+ this,
+ e,
+ e => this.onBackgroundMove(true, e),
+ e => {},
+ emptyFunction
+ );
+ };
+
@action onTitleDown = (e: React.PointerEvent): void => {
setupMoveUpEvents(
this,
@@ -168,6 +179,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
);
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 = this.Interacting = true;
@@ -241,8 +253,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
if (selectedDocs.length) {
if (e.ctrlKey) {
// open an alias in a new tab with Ctrl Key
- const bestAlias = DocListCast(selectedDocs[0].props.Document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
- CollectionDockingView.AddSplit(bestAlias ?? Doc.MakeAlias(selectedDocs[0].props.Document), 'right');
+ CollectionDockingView.AddSplit(Doc.BestAlias(selectedDocs[0].props.Document), 'right');
} else if (e.shiftKey) {
// open centered in a new workspace with Shift Key
const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
@@ -313,49 +324,116 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
};
@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
+ );
+ };
+
+ setRotateCenter = (seldocview: DocumentView, rotCenter: number[]) => {
+ 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.rotateCenterX = final.x / NumCast(seldocview.layoutDoc._width);
+ seldocview.rootDoc.rotateCenterY = final.y / NumCast(seldocview.layoutDoc._height);
+ };
+
+ @action
+ onRotateCenterDown = (e: React.PointerEvent): void => {
+ this._isRotating = true;
+ const seldocview = SelectionManager.Views()[0];
+ 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) => {
+ if (doubleTap) {
+ seldocview.rootDoc.rotateCenterX = 0.5;
+ seldocview.rootDoc.rotateCenterY = 0.5;
+ }
+ })
+ );
+ };
+
+ @action
onRotateDown = (e: React.PointerEvent): void => {
this._isRotating = true;
+ const rcScreen = { X: this.rotCenter[0], Y: this.rotCenter[1] };
const rotateUndo = UndoManager.StartBatch('rotatedown');
const selectedInk = SelectionManager.Views().filter(i => i.ComponentView instanceof InkingStroke);
- const centerPoint = { X: (this.Bounds.x + this.Bounds.r) / 2, Y: (this.Bounds.y + this.Bounds.b) / 2 };
+ 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 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 infoRot = (angle: number, isAbs = false) => {
+ SelectionManager.Views().forEach(
+ action(dv => {
+ const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.rootDoc)!;
+ 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
+ })
+ );
+ };
setupMoveUpEvents(
this,
e,
(e: PointerEvent, down: number[], delta: number[]) => {
const previousPoint = { X: e.clientX, Y: e.clientY };
const movedPoint = { X: e.clientX - delta[0], Y: e.clientY - delta[1] };
- const angle = InkStrokeProperties.angleChange(previousPoint, movedPoint, centerPoint);
+ const deltaAng = InkStrokeProperties.angleChange(movedPoint, previousPoint, rcScreen);
if (selectedInk.length) {
- angle && InkStrokeProperties.Instance.rotateInk(selectedInk, -angle, centerPoint);
+ deltaAng && InkStrokeProperties.Instance.rotateInk(selectedInk, deltaAng, rcScreen);
+ this.setRotateCenter(seldocview, centerPoint);
} else {
- SelectionManager.Views().forEach(dv => {
- const oldRotation = NumCast(dv.rootDoc._jitterRotation);
- // Rotation between -360 and 360
- let newRotation = (oldRotation - (angle * 180) / Math.PI) % 360;
-
- const diff = Math.round(newRotation / 45) - newRotation / 45;
- if (diff < 0.05) {
- console.log('show lines');
- }
- dv.rootDoc._jitterRotation = newRotation;
- });
+ infoRot(deltaAng);
}
return false;
}, // moveEvent
action(() => {
- SelectionManager.Views().forEach(dv => {
- const oldRotation = NumCast(dv.rootDoc._jitterRotation);
- const diff = Math.round(oldRotation / 45) - oldRotation / 45;
- if (diff < 0.05) {
- let newRotation = Math.round(oldRotation / 45) * 45;
- dv.rootDoc._jitterRotation = newRotation;
+ const oldRotation = NumCast(seldocview.rootDoc._rotation);
+ const diff = oldRotation - Math.round(oldRotation / 45) * 45;
+ if (Math.abs(diff) < 5) {
+ if (selectedInk.length) {
+ InkStrokeProperties.Instance.rotateInk(selectedInk, ((Math.round(oldRotation / 45) * 45 - oldRotation) / 180) * Math.PI, rcScreen);
+ } else {
+ infoRot(((Math.round(oldRotation / 45) * 45) / 180) * Math.PI, true);
}
- });
+ }
+ if (selectedInk.length) {
+ this.setRotateCenter(seldocview, centerPoint);
+ }
this._isRotating = false;
rotateUndo?.end();
- UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
}), // upEvent
- emptyFunction
+ action(e => (this._showRotCenter = !this._showRotCenter)) // clickEvent
);
};
@@ -534,10 +612,18 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
} else doc._height = actualdH;
}
} else {
+ const rotCtr = [NumCast(doc._width) / 2, NumCast(doc._height) / 2];
+ const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI);
+
const maxHeight = doc.nativHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH);
dW && (doc._width = actualdW);
dH && (doc._autoHeight = false);
+
+ const rotCtr2 = [NumCast(doc._width) / 2, NumCast(doc._height) / 2];
+ const tlRotated2 = Utils.rotPt(-rotCtr2[0], -rotCtr2[1], (NumCast(doc._rotation) / 180) * Math.PI);
+ doc.x = NumCast(doc.x) + tlRotated.x + rotCtr[0] - (tlRotated2.x + rotCtr2[0]); // doc shifts by amount topleft moves because rotation is about center of doc
+ doc.y = NumCast(doc.y) + tlRotated.y + rotCtr[1] - (tlRotated2.y + rotCtr2[1]);
}
doc.x = (doc.x || 0) + dX * (actualdW - docwidth);
doc.y = (doc.y || 0) + (dragBottom ? 0 : dY * (actualdH - docheight));
@@ -603,28 +689,47 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === 'layout_icon');
}
+ @observable _showRotCenter = false;
+ @observable _rotCenter = [0, 0];
+ @computed get rotCenter() {
+ if (SelectionManager.Views().length) {
+ const seldocview = SelectionManager.Views()[0];
+ const loccenter = Utils.rotPt(
+ NumCast(seldocview.rootDoc.rotateCenterX) * NumCast(seldocview.layoutDoc._width),
+ NumCast(seldocview.rootDoc.rotateCenterY) * NumCast(seldocview.layoutDoc._height),
+ (NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI
+ );
+ return seldocview.props
+ .ScreenToLocalTransform()
+ .inverse()
+ .transformPoint(loccenter.x + NumCast(seldocview.layoutDoc._width) / 2, loccenter.y + NumCast(seldocview.layoutDoc._height) / 2);
+ }
+ return this._rotCenter;
+ }
+
render() {
- const bounds = this.Bounds;
- const seldoc = SelectionManager.Views().slice(-1)[0];
- if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
+ const { b, c, r, x, y } = this.Bounds;
+ const bounds = { b, c, r, x, y };
+ const seldocview = SelectionManager.Views().slice(-1)[0];
+ 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)) {
return null;
}
// hide the decorations if the parent chooses to hide it or if the document itself hides it
- const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc._isGroup || this._isRounding || this._isRotating;
- const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating;
- const hideDocumentButtonBar = seldoc.props.hideDocumentButtonBar || seldoc.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
+ const hideResizers = seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || seldocview.rootDoc._isGroup || this._isRounding || this._isRotating;
+ const hideTitle = seldocview.props.hideDecorationTitle || seldocview.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating;
+ const hideDocumentButtonBar = seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
// if multiple documents have been opened at the same time, then don't show open button
const hideOpenButton =
- seldoc.props.hideOpenButton ||
- seldoc.rootDoc.hideOpenButton ||
+ seldocview.props.hideOpenButton ||
+ seldocview.rootDoc.hideOpenButton ||
SelectionManager.Views().some(docView => docView.props.Document._stayInCollection || docView.props.Document.isGroup || docView.props.Document.hideOpenButton) ||
this._isRounding ||
this._isRotating;
const hideDeleteButton =
this._isRounding ||
this._isRotating ||
- seldoc.props.hideDeleteButton ||
- seldoc.rootDoc.hideDeleteButton ||
+ seldocview.props.hideDeleteButton ||
+ seldocview.rootDoc.hideDeleteButton ||
SelectionManager.Views().some(docView => {
const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
@@ -652,6 +757,29 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
);
const colorScheme = StrCast(Doc.ActiveDashboard?.colorScheme);
+
+ 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;
+ bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
+ bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
+
+ const useLock = bounds.r - bounds.x > 135 && seldocview.props.CollectionFreeFormDocumentView;
+ const useRotation = seldocview.rootDoc.type !== DocumentType.EQUATION; // when do we want an object to not rotate?
+ const rotation = NumCast(seldocview.rootDoc._rotation);
+
+ const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : '';
+
+ // Radius constants
+ const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox;
+ const borderRadius = numberValue(StrCast(seldocview.rootDoc.borderRounding));
+ const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.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);
+
const titleArea = this._editingTitle ? (
<input
ref={this._keyinput}
@@ -663,41 +791,25 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
onBlur={e => !hideTitle && this.titleBlur()}
onChange={action(e => !hideTitle && (this._accumulatedTitle = e.target.value))}
onKeyDown={hideTitle ? emptyFunction : this.titleEntered}
+ onPointerDown={e => e.stopPropagation()}
/>
) : (
<div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown}>
<span className={`documentDecorations-titleSpan${colorScheme}`}>{`${hideTitle ? '' : this.selectionTitle}`}</span>
+ {!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()}>
+ <FontAwesomeIcon size="sm" icon="lock" />
+ </div>
+ </Tooltip>
+ )}
</div>
);
-
- 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;
- bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
- bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
-
- // Rotation constants: Only allow rotation on ink and images
- const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox || seldoc.ComponentView instanceof VideoBox;
- const rotation = NumCast(seldoc.rootDoc._jitterRotation);
-
- const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : '';
-
- // Radius constants
- const useRounding = seldoc.ComponentView instanceof ImageBox || seldoc.ComponentView instanceof FormattedTextBox;
- const borderRadius = numberValue(StrCast(seldoc.rootDoc.borderRounding));
- const docMax = Math.min(NumCast(seldoc.rootDoc.width) / 2, NumCast(seldoc.rootDoc.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);
return (
<div className={`documentDecorations${colorScheme}`}>
<div
className="documentDecorations-background"
style={{
- transform: `rotate(${rotation}deg)`,
- transformOrigin: 'top left',
width: bounds.r - bounds.x + this._resizeBorderWidth + 'px',
height: bounds.b - bounds.y + this._resizeBorderWidth + 'px',
left: bounds.x - this._resizeBorderWidth / 2,
@@ -722,10 +834,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
width: bounds.r - bounds.x + this._resizeBorderWidth + 'px',
height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px',
}}>
- <div className="documentDecorations-topbar">
+ <div className="documentDecorations-topbar" onPointerDown={this.onContainerDown}>
{hideDeleteButton ? <div /> : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
{hideResizers || hideDeleteButton ? <div /> : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
- {hideResizers ? <div /> : titleArea}
+ {titleArea}
{hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
</div>
{hideResizers ? null : (
@@ -740,23 +852,15 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
<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()} />
- {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')}
+ {seldocview.props.renderDepth <= 1 || !seldocview.props.ContainingCollectionView ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')}
</>
)}
-
- {useRotation && (
- <div key="rot" className={`documentDecorations-rotation`} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}>
- <IconButton icon={<FaUndo />} isCircle={true} hoverStyle={'lighten'} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY} />
- </div>
- )}
-
{useRounding && (
<div
key="rad"
style={{
background: `${this._isRounding ? Colors.MEDIUM_BLUE : undefined}`,
- left: `${radiusHandleLocation + 3}`,
- top: `${radiusHandleLocation + 23}`,
+ transform: `translate(${radiusHandleLocation}px, ${radiusHandleLocation}px)`,
}}
className={`documentDecorations-borderRadius`}
onPointerDown={this.onRadiusDown}
@@ -769,12 +873,41 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
className="link-button-container"
key="links"
style={{
- transform: ` translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
+ transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
}}>
<DocumentButtonBar views={SelectionManager.Views} />
</div>
)}
</div>
+
+ {useRotation && (
+ <>
+ <div
+ style={{
+ position: 'absolute',
+ transform: `rotate(${rotation}deg)`,
+ width: this.Bounds.r - this.Bounds.x + 'px',
+ height: this.Bounds.b - this.Bounds.y + 'px',
+ left: this.Bounds.x,
+ top: this.Bounds.y,
+ pointerEvents: 'none',
+ }}>
+ {this._isRotating ? null : (
+ <div className="documentDecorations-rotation" style={{ pointerEvents: 'all' }} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}>
+ <IconButton icon={<FaUndo />} isCircle={true} hoverStyle={'lighten'} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY} />
+ </div>
+ )}
+ </div>
+ {!this._showRotCenter ? null : (
+ <div
+ className="documentDecorations-rotationCenter"
+ style={{ transform: `translate(${this.rotCenter[0] - 3}px, ${this.rotCenter[1] - 3}px)` }}
+ onPointerDown={this.onRotateCenterDown}
+ onContextMenu={e => e.preventDefault()}
+ />
+ )}
+ </>
+ )}
</div>
)}
</div>