aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/DocumentDecorations.scss25
-rw-r--r--src/client/views/DocumentDecorations.tsx324
-rw-r--r--src/client/views/InkStrokeProperties.ts2
-rw-r--r--src/client/views/PropertiesView.tsx3
4 files changed, 104 insertions, 250 deletions
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index de8a3c909..db2d56aa8 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -22,7 +22,7 @@ $linkGap : 3px;
background: none;
}
- .documentDecorations-levelSelector {
+ .documentDecorations-selectorButton {
pointer-events: auto;
height: 15px;
width: 15px;
@@ -210,37 +210,30 @@ $linkGap : 3px;
grid-column-start: 4;
grid-column-end: 4;
pointer-events: all;
- text-align: center;
right: 0;
- top: 0;
cursor: pointer;
position: absolute;
- background: transparent;
width: 20px;
}
-.documentDecorations-openInTab {
+.documentDecorations-openButton {
+ display: flex;
+ align-items: center;
opacity: 1;
grid-column-start: 5;
grid-column-end: 5;
pointer-events: all;
- text-align: center;
cursor: pointer;
}
.documentDecorations-closeButton {
+ display: flex;
+ align-items: center;
opacity: 1;
grid-column-start: 1;
grid-column-end: 3;
pointer-events: all;
- text-align: center;
cursor: pointer;
- position: absolute;
- left: 0px;
- top: 0px;
- width: 8px;
- height: $MINIMIZED_ICON_SIZE;
- max-height: 20px;
>svg {
margin: 0;
@@ -343,12 +336,6 @@ $linkGap : 3px;
.documentdecorations-icon {
margin-top: 3px;
}
-
-.documentdecorations-times {
- margin-top: 3px;
- padding-right: 3px;
-}
-
.templating-button,
.docDecs-tagButton {
width: 20px;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 5b7394c42..ec9cb2714 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -10,7 +10,7 @@ import { InkField } from "../../fields/InkField";
import { ScriptField } from '../../fields/ScriptField';
import { Cast, NumCast } from "../../fields/Types";
import { GetEffectiveAcl } from '../../fields/util';
-import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../Utils";
+import { setupMoveUpEvents } from "../../Utils";
import { Docs, DocUtils } from "../documents/Documents";
import { DocumentType } from '../documents/DocumentTypes';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
@@ -40,10 +40,10 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
private _rotateUndo?: UndoManager.Batch;
private _offX = 0; _offY = 0; // offset from click pt to inner edge of resize border
private _snapX = 0; _snapY = 0; // last snapped location of resize border
- private _prevX = 0;
private _prevY = 0;
- private _centerPoints: { X: number, Y: number }[] = [];
- private _inkDocs: { x: number, y: number, width: number, height: number }[] = [];
+ private _dragHeights = new Map<Doc, { start: number, lowest: number }>();
+ private _inkCenterPts: { doc: Doc, X: number, Y: number }[] = [];
+ private _inkDragDocs: { doc: Doc, x: number, y: number, width: number, height: number }[] = [];
@observable private _accumulatedTitle = "";
@observable private _titleControlString: string = "#title";
@@ -75,7 +75,8 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
return boudns;
}
- titleBlur = action((commit: boolean) => {
+ @action
+ titleBlur = (commit: boolean) => {
this._edtingTitle = false;
if (commit) {
if (this._accumulatedTitle.startsWith("#") || this._accumulatedTitle.startsWith("=")) {
@@ -89,29 +90,26 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
}), "title blur");
}
}
- });
+ }
- @action titleEntered = (e: any) => {
- const key = e.keyCode || e.which;
- // enter pressed
- if (key === 13) {
- const text = e.target.value;
+ @action titleEntered = (e: React.KeyboardEvent) => {
+ if (e.key === "Enter") {
+ const text = (e.target as any).value;
if (text.startsWith("::")) {
this._accumulatedTitle = text.slice(2, text.length);
const promoteDoc = SelectionManager.Views()[0];
Doc.SetInPlace(promoteDoc.props.Document, "title", this._accumulatedTitle, true);
DocUtils.Publish(promoteDoc.props.Document, this._accumulatedTitle, promoteDoc.props.addDocument, promoteDoc.props.removeDocument);
}
- e.target.blur();
+ (e.target as any).blur();
}
}
@action onTitleDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, this.onTitleClick);
- }
- @action onTitleClick = (e: PointerEvent): void => {
- !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString);
- this._edtingTitle = true;
- setTimeout(() => this._keyinput.current!.focus(), 0);
+ setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, action((e) => {
+ !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString);
+ this._edtingTitle = true;
+ setTimeout(() => this._keyinput.current!.focus(), 0);
+ }))
}
onBackgroundDown = (e: React.PointerEvent): void => {
@@ -128,8 +126,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
dragData.isSelectionMove = true;
dragData.canEmbed = dragTitle;
dragData.dropAction = dragDocView.props.dropAction;
- this.Interacting = true;
- this._hidden = true;
+ this._hidden = this.Interacting = true;
DragManager.StartDocumentDrag(SelectionManager.Views().map(dv => dv.ContentDiv!), dragData, e.x, e.y, {
dragComplete: action(e => {
dragData.canEmbed && SelectionManager.DeselectAll();
@@ -140,25 +137,15 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
return true;
}
- onIconifyDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onIconifyClick);
- }
@undoBatch
- @action
- onCloseClick = async (e: React.MouseEvent | undefined) => {
- if (!e?.button) {
- const selected = SelectionManager.Views().slice();
- SelectionManager.DeselectAll();
- selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
- }
- }
- @action
- onMaximizeDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick);
+ onCloseClick = (e: React.MouseEvent) => {
+ const selected = SelectionManager.Views().slice();
+ SelectionManager.DeselectAll();
+ selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
}
@undoBatch
@action
- onMaximizeClick = (e: PointerEvent): void => {
+ onMaximizeClick = (e: React.MouseEvent): void => {
if (e.button === 0) {
const selectedDocs = SelectionManager.Views();
if (selectedDocs.length) {
@@ -181,133 +168,70 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
}
SelectionManager.DeselectAll();
}
+
@undoBatch
- @action
- onIconifyClick = (e: PointerEvent): void => {
- (e.button === 0) && SelectionManager.Views().forEach(dv => dv?.iconify());
+ onIconifyClick = (e: React.MouseEvent): void => {
+ SelectionManager.Views().forEach(dv => dv?.iconify());
SelectionManager.DeselectAll();
}
- @action
- onSelectorUp = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e) =>
- SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false)));
- }
+ onSelectorClick = (e: React.MouseEvent) => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false);
- @action
onRadiusDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, this.onRadiusMove, (e) => this._resizeUndo?.end(), (e) => { });
- if (e.button === 0) {
- this._resizeUndo = UndoManager.StartBatch("DocDecs set radius");
- }
- }
-
- onRadiusMove = (e: PointerEvent, down: number[]): boolean => {
- let dist = Math.sqrt((e.clientX - down[0]) * (e.clientX - down[0]) + (e.clientY - down[1]) * (e.clientY - down[1]));
- dist = dist < 3 ? 0 : dist;
- SelectionManager.Views().map(dv => dv.props.Document).map(doc => doc.layout instanceof Doc ? doc.layout : doc.isTemplateForField ? doc : Doc.GetProto(doc)).
- map(d => d.borderRounding = `${Math.max(0, dist)}px`);
- return false;
+ this._resizeUndo = UndoManager.StartBatch("DocDecs set radius");
+ setupMoveUpEvents(this, e, (e, down) => {
+ const dist = Math.sqrt((e.clientX - down[0]) * (e.clientX - down[0]) + (e.clientY - down[1]) * (e.clientY - down[1]));
+ SelectionManager.Views().map(dv => dv.props.Document).map(doc => doc.layout instanceof Doc ? doc.layout : doc.isTemplateForField ? doc : Doc.GetProto(doc)).
+ map(d => d.borderRounding = `${Math.max(0, dist < 3 ? 0 : dist)}px`);
+ return false;
+ }, (e) => this._resizeUndo?.end(), (e) => { });
}
- @undoBatch
@action
onRotateDown = (e: React.PointerEvent): void => {
this._rotateUndo = UndoManager.StartBatch("rotatedown");
- setupMoveUpEvents(this, e, this.onRotateMove, this.onRotateUp, (e) => { });
- this._prevX = e.clientX;
+ setupMoveUpEvents(this, e, this.onRotateMove, () => this._rotateUndo?.end(), (e) => { });
this._prevY = e.clientY;
- SelectionManager.Views().forEach(action((element: DocumentView) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- const ink = Cast(doc.data, InkField)?.inkData;
- if (ink) {
- const xs = ink.map(p => p.X);
- const ys = ink.map(p => p.Y);
- const left = Math.min(...xs);
- const top = Math.min(...ys);
- const right = Math.max(...xs);
- const bottom = Math.max(...ys);
- // this._centerPoints.push({ X: ((right - left) / 2) + left, Y: ((bottom - top) / 2) + bottom });
- this._centerPoints.push({ X: left, Y: top });
- }
- }
- }));
-
+ this._inkCenterPts = SelectionManager.Views()
+ .filter(dv => dv.rootDoc.type === DocumentType.INK)
+ .map(dv => ({ ink: Cast(dv.rootDoc.data, InkField)?.inkData ?? [{ X: 0, Y: 0 }], doc: dv.rootDoc }))
+ .map(({ ink, doc }) => ({ doc, X: Math.min(...ink.map(p => p.X)), Y: Math.min(...ink.map(p => p.Y)) }));
}
- @undoBatch
@action
onRotateMove = (e: PointerEvent, down: number[]): boolean => {
-
// const distance = Math.sqrt((this._prevY - e.clientY) * (this._prevY - e.clientY) + (this._prevX - e.clientX) * (this._prevX - e.clientX));
const distance = Math.abs(this._prevY - e.clientY);
- var angle = 0;
- //think of a better condition later...
- // if ((down[0] < e.clientX && this._prevY < e.clientY) || (down[0] > e.clientX && this._prevY > e.clientY)) {
- if (e.clientY > this._prevY) {
- angle = distance * (Math.PI / 180);
- // } else if ((down[0] < e.clientX && this._prevY > e.clientY) || (down[0] > e.clientX && this._prevY <= e.clientY)) {
- } else if (e.clientY < this._prevY) {
- angle = - distance * (Math.PI / 180);
- }
- this._prevX = e.clientX;
+ const angle = e.clientY > this._prevY ? distance * (Math.PI / 180) : e.clientY < this._prevY ? - distance * (Math.PI / 180) : 0;
this._prevY = e.clientY;
- var index = 0;
- SelectionManager.Views().forEach(action((element: DocumentView) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- doc.rotation = Number(doc.rotation) + Number(angle);
- const inks = Cast(doc.data, InkField)?.inkData;
- if (inks) {
- const newPoints: { X: number, Y: number }[] = [];
- for (const ink of inks) {
- const newX = Math.cos(angle) * (ink.X - this._centerPoints[index].X) - Math.sin(angle) * (ink.Y - this._centerPoints[index].Y) + this._centerPoints[index].X;
- const newY = Math.sin(angle) * (ink.X - this._centerPoints[index].X) + Math.cos(angle) * (ink.Y - this._centerPoints[index].Y) + this._centerPoints[index].Y;
- newPoints.push({ X: newX, Y: newY });
- }
- Doc.GetProto(doc).data = new InkField(newPoints);
- const xs = newPoints.map(p => p.X);
- const ys = newPoints.map(p => p.Y);
- const left = Math.min(...xs);
- const top = Math.min(...ys);
- const right = Math.max(...xs);
- const bottom = Math.max(...ys);
-
- // doc._height = (bottom - top) * element.props.ScreenToLocalTransform().Scale;
- // doc._width = (right - left) * element.props.ScreenToLocalTransform().Scale;
- doc._height = (bottom - top);
- doc._width = (right - left);
- }
- index++;
- }
- }));
+ this._inkCenterPts.map(({ doc, X, Y }) => ({ doc, X, Y, inkData: Cast(doc.data, InkField)?.inkData }))
+ .forEach(pair => {
+ const newPoints = pair.inkData?.map(ink => ({
+ X: Math.cos(angle) * (ink.X - pair.X) - Math.sin(angle) * (ink.Y - pair.Y) + pair.X,
+ Y: Math.sin(angle) * (ink.X - pair.X) + Math.cos(angle) * (ink.Y - pair.Y) + pair.Y
+ })) || [];
+ Doc.SetInPlace(pair.doc, "data", new InkField(newPoints), true);
+
+ pair.doc._width = ((xs) => (Math.max(...xs) - Math.min(...xs)))(newPoints.map(p => p.X) || [0]);
+ pair.doc._height = ((ys) => (Math.max(...ys) - Math.min(...ys)))(newPoints.map(p => p.Y) || [0]);
+ pair.doc.rotation = NumCast(pair.doc.rotation) + angle;
+ });
return false;
}
- onRotateUp = (e: PointerEvent) => {
- this._centerPoints = [];
- this._rotateUndo?.end();
- this._rotateUndo = undefined;
- }
-
- _dragHeights = new Map<Doc, { start: number, lowest: number }>();
-
@action
onPointerDown = (e: React.PointerEvent): void => {
-
- this._inkDocs = [];
- SelectionManager.Views().forEach(action((element: DocumentView) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height) {
- this._inkDocs.push({ x: doc.x, y: doc.y, width: doc._width, height: doc._height });
+ DragManager.docsBeingDragged = SelectionManager.Views().map(dv => dv.rootDoc);
+ this._inkDragDocs = DragManager.docsBeingDragged
+ .filter(doc => doc.type === DocumentType.INK)
+ .map(doc => {
if (InkStrokeProperties.Instance?._lock) {
- Doc.SetNativeHeight(doc, doc._height);
- Doc.SetNativeWidth(doc, doc._width);
+ Doc.SetNativeHeight(doc, NumCast(doc._height));
+ Doc.SetNativeWidth(doc, NumCast(doc._width));
}
- }
- }));
+ return ({ doc, x: NumCast(doc.x), y: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
+ });
setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { });
if (e.button === 0) {
@@ -320,22 +244,15 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
}
this._snapX = e.pageX;
this._snapY = e.pageY;
- DragManager.docsBeingDragged = SelectionManager.Views().map(dv => dv.rootDoc);
- SelectionManager.Views().map(dv =>
- this._dragHeights.set(dv.layoutDoc, { start: NumCast(dv.layoutDoc._height), lowest: NumCast(dv.layoutDoc._height) }));
+ DragManager.docsBeingDragged.forEach(doc => this._dragHeights.set(doc, { start: NumCast(doc._height), lowest: NumCast(doc._height) }));
}
onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
const first = SelectionManager.Views()[0];
let thisPt = { thisX: e.clientX - this._offX, thisY: e.clientY - this._offY };
var fixedAspect = Doc.NativeAspect(first.layoutDoc);
- SelectionManager.Views().forEach(action((element: DocumentView) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc._width && doc._height && InkStrokeProperties.Instance?._lock) {
- fixedAspect = Doc.NativeHeight(doc);
- }
- }));
-
+ InkStrokeProperties.Instance?._lock && SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK)
+ .forEach(dv => fixedAspect = Doc.NativeAspect(dv.rootDoc));
if (fixedAspect && (this._resizeHdlId === "documentDecorations-bottomRightResizer" || this._resizeHdlId === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
const project = (p: number[], a: number[], b: number[]) => {
@@ -456,6 +373,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
else {
if (dragBottom && (e.ctrlKey || docView.layoutDoc._fitWidth)) { // frozen web pages and others that fitWidth can't grow horizontally to match a vertical resize so the only choice is to change the nativeheight even if the ctrl key isn't used
doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc);
+ doc._autoHeight = false;
} else {
if (!doc._fitWidth) doc._width = nwidth / nheight * actualdH;
else if (!e.ctrlKey) doc._width = actualdW;
@@ -463,8 +381,8 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
doc._height = actualdH;
}
} else {
- dW && (doc._width = actualdW);
dH && (doc._height = actualdH);
+ dW && (doc._width = actualdW);
dH && (doc._autoHeight = false);
}
}
@@ -476,41 +394,26 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
@action
onPointerUp = (e: PointerEvent): void => {
- SelectionManager.Views().map(dv => {
- const hgts = this._dragHeights.get(dv.layoutDoc);
- if (hgts && hgts.lowest < hgts.start && hgts.lowest <= 20) {
- dv.nativeWidth > 0 && Doc.toggleNativeDimensions(dv.layoutDoc, dv.ContentScale(), dv.props.PanelWidth(), dv.props.PanelHeight());
- if (dv.layoutDoc._autoHeight) dv.layoutDoc._autoHeight = false;
- setTimeout(() => dv.layoutDoc._autoHeight = true);
- }
- });
this._resizeHdlId = "";
this.Interacting = false;
- (e.button === 0) && this._resizeUndo?.end();
- this._resizeUndo = undefined;
+ this._resizeUndo?.end();
SnappingManager.clearSnapLines();
-
+ // detect autoHeight gesture and apply
+ DragManager.docsBeingDragged.map(doc => ({ doc, hgts: this._dragHeights.get(doc) }))
+ .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20)
+ .forEach(pair => pair.doc._autoHeight = true);
//need to change points for resize, or else rotation/control points will fail.
- SelectionManager.Views().forEach(action((element: DocumentView, index) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
- const ink = Cast(doc.data, InkField)?.inkData;
- if (ink) {
- const newPoints: { X: number, Y: number }[] = [];
- ink.forEach(i => {
- // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
- const newX = ((doc.x || 0) - this._inkDocs[index].x) + (i.X * (doc._width || 0)) / this._inkDocs[index].width;
- const newY = ((doc.y || 0) - this._inkDocs[index].y) + (i.Y * (doc._height || 0)) / this._inkDocs[index].height;
- newPoints.push({ X: newX, Y: newY });
- });
- Doc.GetProto(doc).data = new InkField(newPoints);
-
- }
- doc._nativeWidth = 0;
- doc._nativeHeight = 0;
- }
- }));
+ this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
+ .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
+ Doc.SetInPlace(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
+ }))), true);
+ Doc.SetNativeWidth(doc, undefined);
+ Doc.SetNativeHeight(doc, undefined);
+ });
}
@computed
@@ -524,54 +427,28 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || "-unset-";
}
return this._accumulatedTitle;
- } else if (SelectionManager.Views().length > 1) {
- return "-multiple-";
}
- return "-unset-";
- }
-
- TextBar: HTMLDivElement | undefined;
- private setTextBar = (ele: HTMLDivElement) => {
- if (ele) {
- this.TextBar = ele;
- }
- }
- public static DocumentIcon(layout: string) {
- const button = layout.indexOf("PDFBox") !== -1 ? "file-pdf" :
- layout.indexOf("ImageBox") !== -1 ? "image" :
- layout.indexOf("Formatted") !== -1 ? "sticky-note" :
- layout.indexOf("Video") !== -1 ? "film" :
- layout.indexOf("Collection") !== -1 ? "object-group" :
- "caret-up";
- return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
+ return SelectionManager.Views().length > 1 ? "-multiple-" : "-unset-";
}
render() {
- const darkScheme = CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : undefined;
const bounds = this.Bounds;
- const seldoc = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+ const seldoc = SelectionManager.Views().lastElement();
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)) {
return (null);
}
+ const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection);
const canDelete = SelectionManager.Views().some(docView => {
const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
- const docAcl = GetEffectiveAcl(docView.props.Document);
- return (!docView.props.Document._stayInCollection || docView.props.Document.isInkMask) &&
- (collectionAcl === AclAdmin || collectionAcl === AclEdit || docAcl === AclAdmin);
+ return (!docView.rootDoc._stayInCollection || docView.rootDoc.isInkMask) &&
+ (collectionAcl === AclAdmin || collectionAcl === AclEdit || GetEffectiveAcl(docView.rootDoc) === AclAdmin);
});
- const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection);
- const closeIcon = !canDelete ? <div></div> : (
- <Tooltip title={<div className="dash-tooltip">Close</div>} placement="top">
- <div className="documentDecorations-closeButton" onClick={this.onCloseClick}>
- <FontAwesomeIcon className="documentdecorations-times" icon={"times"} size="lg" />
- </div></Tooltip>);
-
- const openIcon = !canOpen ? (null) :
- <Tooltip key="open" title={<div className="dash-tooltip">Open in Tab (ctrl: as alias, shift: in new collection)</div>} placement="top">
- <div className="documentDecorations-openInTab" onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}>
- <FontAwesomeIcon icon="external-link-alt" className="documentView-minimizedIcon" />
+ const topBtn = (key: string, icon: string, click: (e: React.MouseEvent) => void, title: string) => (
+ <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
+ <div className={`documentDecorations-${key}Button`} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onClick={click}>
+ <FontAwesomeIcon icon={icon as any} />
</div>
- </Tooltip>;
+ </Tooltip>);
const titleArea = this._edtingTitle ?
<input ref={this._keyinput} className="documentDecorations-title" style={{ width: `calc(100% - ${seldoc?.props.hideResizeHandles ? 0 : 20}px` }} type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle}
@@ -593,7 +470,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
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 useRotation = seldoc.rootDoc.type === DocumentType.INK;
- return (<div className="documentDecorations" style={{ background: darkScheme }} >
+ return (<div className="documentDecorations" style={{ background: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : "" }} >
<div className="documentDecorations-background" style={{
width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
@@ -601,26 +478,21 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
top: bounds.y - this._resizeBorderWidth / 2,
pointerEvents: KeyManager.Instance.ShiftPressed || this.Interacting ? "none" : "all",
display: SelectionManager.Views().length <= 1 ? "none" : undefined
- }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} >
- </div>
+ }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} />
{bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
- <div className="documentDecorations-container" key="container" ref={this.setTextBar} style={{
+ <div className="documentDecorations-container" key="container" style={{
width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
left: bounds.x - this._resizeBorderWidth / 2,
top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
}}>
- {closeIcon}
+ {!canDelete ? <div /> : topBtn("close", "times", this.onCloseClick, "Close")}
{seldoc.props.Document.type === DocumentType.EQUATION ? (null) : titleArea}
{seldoc.props.hideResizeHandles || seldoc.props.Document.type === DocumentType.EQUATION ? (null) :
<>
{SelectionManager.Views().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
- <Tooltip key="i" title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top">
- <div className="documentDecorations-iconifyButton" onPointerDown={this.onIconifyDown}>
- <FontAwesomeIcon icon={seldoc.finalLayoutKey.includes("icon") ? "window-restore" : "window-minimize"} className="documentView-minimizedIcon" />
- </div>
- </Tooltip>}
- {openIcon}
+ topBtn("iconify", `window-${seldoc.finalLayoutKey.includes("icon") ? "restore" : "minimize"}`, this.onIconifyClick, `${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`)}
+ {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeClick, "Open in Tab (ctrl: as alias, shift: in new collection)")}
<div key="tl" className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
<div key="t" className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
<div key="tr" className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
@@ -632,11 +504,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
<div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
{seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
- <Tooltip key="level" title={<div className="dash-tooltip">tap to select containing document</div>} placement="top">
- <div className="documentDecorations-levelSelector"
- onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
- <FontAwesomeIcon className="documentdecorations-times" icon={"arrow-alt-circle-up"} size="lg" />
- </div></Tooltip>}
+ topBtn("selector", "arrow-alt-circle-up", this.onSelectorClick, "tap to select containing document")}
</>
}
<div key="rot" className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index ce7f3d8d9..9257ee4e6 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -171,7 +171,7 @@ export class InkStrokeProperties {
SelectionManager.Views().forEach(action(inkView => {
const doc = Document(inkView.rootDoc);
if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- doc.rotation = Number(doc.rotation) + Number(angle);
+ doc.rotation = NumCast(doc.rotation) + angle;
const ink = Cast(doc.data, InkField)?.inkData;
if (ink) {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 6ee5e4d8c..73c688029 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -487,7 +487,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
var index = 0;
if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- doc.rotation = Number(doc.rotation) + Number(angle);
+ doc.rotation = NumCast(doc.rotation) + angle;
const inks = Cast(doc.data, InkField)?.inkData;
if (inks) {
const newPoints: { X: number, Y: number }[] = [];
@@ -575,7 +575,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
upDownButtons = (dirs: string, field: string) => {
switch (field) {
case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break;
- // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break;
case "Xps": this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === "up" ? 10 : -10)); break;
case "Yps": this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === "up" ? 10 : -10)); break;
case "stk": this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;