aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Utils.ts2
-rw-r--r--src/client/documents/Documents.ts35
-rw-r--r--src/client/util/CurrentUserUtils.ts4
-rw-r--r--src/client/util/DropConverter.ts6
-rw-r--r--src/client/util/InteractionUtils.tsx2
-rw-r--r--src/client/util/LinkManager.ts4
-rw-r--r--src/client/views/DocumentDecorations.scss42
-rw-r--r--src/client/views/DocumentDecorations.tsx50
-rw-r--r--src/client/views/InkControlPtHandles.tsx12
-rw-r--r--src/client/views/InkingStroke.tsx8
-rw-r--r--src/client/views/PropertiesButtons.tsx15
-rw-r--r--src/client/views/StyleProvider.tsx21
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx5
-rw-r--r--src/client/views/collections/TreeView.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx65
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx1
-rw-r--r--src/client/views/global/globalCssVariables.scss1
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx11
-rw-r--r--src/client/views/nodes/DocumentView.tsx23
-rw-r--r--src/client/views/nodes/LinkBox.tsx108
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingBox.tsx2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss8
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx2
-rw-r--r--src/fields/Doc.ts16
27 files changed, 300 insertions, 154 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index b5ca53a33..34419f665 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -555,7 +555,7 @@ export function returnAll(): 'all' {
return 'all';
}
-export function returnNone() {
+export function returnNone(): 'none' {
return 'none';
}
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 11b5f9f08..d61e4b3e9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -215,6 +215,7 @@ export class DocumentOptions {
_lockedTransform?: BOOLt = new BoolInfo('lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed');
layout?: string | Doc; // default layout string or template document
+ layout_isSvg?: BOOLt = new BoolInfo('whether document decorations and other selections should handle pointerEvents for svg content or use doc bounding box');
layout_keyValue?: STRt = new StrInfo('layout definition for showing keyValue view of document', false);
layout_explainer?: STRt = new StrInfo('explanation displayed at top of a collection to describe its purpose', false);
layout_headerButton?: DOCt = new DocInfo('the (button) Doc to display at the top of a collection.', false);
@@ -569,10 +570,10 @@ export namespace Docs {
childDontRegisterViews: true,
onClick: FollowLinkScript(),
layout_hideLinkAnchors: true,
- _height: 150,
+ _height: 1,
+ _width: 1,
link: '',
link_description: '',
- layout_showCaption: 'link_description',
backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
_dropPropertiesToRemove: new List(['onClick']),
},
@@ -667,7 +668,7 @@ export namespace Docs {
{
// NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
layout: { view: InkingStroke, dataField: 'stroke' },
- options: { systemIcon: 'BsFillPencilFill' },
+ options: { systemIcon: 'BsFillPencilFill', nativeDimModifiable: true, nativeHeightUnfrozen: true, layout_isSvg: true, layout_forceReflow: true },
},
],
[
@@ -770,7 +771,7 @@ export namespace Docs {
const existing = actualProtos[id] as Doc;
const type = id.replace(suffix, '') as DocumentType;
// get or create prototype of the specified type...
- const target = existing || buildPrototype(type, id);
+ const target = buildPrototype(type, id, existing);
// ...and set it if not undefined (can be undefined only if TemplateMap does not contain
// an entry dedicated to the given DocumentType)
target && PrototypeMap.set(type, target);
@@ -818,7 +819,7 @@ export namespace Docs {
* @param options any value specified in the DocumentOptions object likewise
* becomes the default value for that key for all delegates
*/
- function buildPrototype(type: DocumentType, prototypeId: string): Opt<Doc> {
+ function buildPrototype(type: DocumentType, prototypeId: string, existing?: Doc): Opt<Doc> {
// load template from type
const template = TemplateMap.get(type);
if (!template) {
@@ -844,12 +845,14 @@ export namespace Docs {
layout: layout.view?.LayoutString(layout.dataField),
data: template.data,
};
- Object.entries(options).map(pair => {
- if (typeof pair[1] === 'string' && pair[1].startsWith('@')) {
- (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1));
- }
- });
- return Doc.assign(new Doc(prototypeId, true), options as any, undefined, true);
+ Object.entries(options)
+ .filter(pair => typeof pair[1] === 'string' && pair[1].startsWith('@'))
+ .map(pair => {
+ if (!existing || ScriptCast(existing[pair[0]])?.script.originalScript !== pair[1].substring(1)) {
+ (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1));
+ }
+ });
+ return Doc.assign(existing ?? new Doc(prototypeId, true), OmitKeys(options, Object.keys(existing ?? {})).omit, undefined, true);
}
}
@@ -1034,6 +1037,10 @@ export namespace Docs {
I[Initializing] = true;
I.type = DocumentType.INK;
I.layout = InkingStroke.LayoutString('stroke');
+ I.nativeDimModifiable = true;
+ I.nativeHeightUnfrozen = true;
+ I.layout_isSvg = true;
+ I.layout_forceReflow = true;
I.layout_fitWidth = false;
I.layout_hideDecorationTitle = true; // don't show title when selected
// I.layout_hideOpenButton = true; // don't show open full screen button when selected
@@ -1402,8 +1409,10 @@ export namespace DocUtils {
link_relationship: linkSettings.link_relationship,
link_description: linkSettings.link_description,
link_autoMoveAnchors: true,
- _layout_showCaption: 'link_description',
- _layout_showTitle: 'link_relationship',
+ _layout_showCaption: '', // removed since they conflict with showing a link with a LinkBox (ie, line, not comparison box)
+ _layout_showTitle: '',
+ // _layout_showCaption: 'link_description',
+ // _layout_showTitle: 'link_relationship',
},
id
),
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index cc8f72ddf..268ee2790 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -659,7 +659,7 @@ export class CurrentUserUtils {
subMenu: [
{ title: "Left", toolTip: "Left align (Cmd-[)", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }},
{ title: "Center", toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
- { title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+ { title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
]
},
{ title: "Dictate", toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", toolType:"dictation", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}},
@@ -832,7 +832,7 @@ export class CurrentUserUtils {
// childContextMenuLabels: new List<string>(["Add to Dashboards",]),
// childContextMenuIcons: new List<string>(["user-plus",]),
"acl-Guest": SharingPermissions.Augment, "_acl-Guest": SharingPermissions.Augment,
- childDragAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
+ childDragAction: "embed", isSystem: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
// NOTE: treeView_HideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
_layout_showTitle: "title", treeView_HideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true,
layout_explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index dbdf580cd..2c371f28e 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -7,7 +7,7 @@ import { Cast, StrCast } from '../../fields/Types';
import { ImageField } from '../../fields/URLField';
import { Docs } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
-import { ButtonType } from '../views/nodes/FontIconBox/FontIconBox';
+import { ButtonType, FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
import { DragManager } from './DragManager';
import { ScriptingGlobals } from './ScriptingGlobals';
@@ -56,7 +56,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
data?.draggedDocuments.map((doc, i) => {
let dbox = doc;
// bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant
- if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes('FontIconBox')) {
+ if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes(FontIconBox.name)) {
if (data.dropPropertiesToRemove || dbox.dropPropertiesToRemove) {
//dbox = Doc.MakeEmbedding(doc); // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon
dbox = Doc.MakeEmbedding(dbox);
@@ -78,7 +78,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
backgroundColor: StrCast(doc.backgroundColor),
title: StrCast(layoutDoc.title),
btnType: ButtonType.ClickButton,
- icon: layoutDoc.isTemplateDoc ? 'font' : 'bolt',
+ icon: 'bolt',
});
dbox.dragFactory = layoutDoc;
dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined;
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 4e32ed67f..6406624bb 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -172,7 +172,7 @@ export namespace InteractionUtils {
<Tag
d={bezier ? strpts : undefined}
points={bezier ? undefined : strpts}
- filter={!dropshadow ? undefined : `drop-shadow(-1px -1px 0px ${dropshadow}) drop-shadow(2px -1px 0px ${dropshadow}) drop-shadow(2px 2px 0px ${dropshadow}) drop-shadow(-1px 2px 0px ${dropshadow})`}
+ //filter={!dropshadow ? undefined : `drop-shadow(-1px -1px 0px ${dropshadow}) `}
style={{
// filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
fill: fill && fill !== 'transparent' ? fill : 'none',
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index a533fdd1f..ba53a760f 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -202,8 +202,8 @@ export class LinkManager {
public static getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined {
const a1 = Cast(linkDoc.link_anchor_1, Doc, null);
const a2 = Cast(linkDoc.link_anchor_2, Doc, null);
- if (Doc.AreProtosEqual(DocCast(anchor.annotationOn, anchor), DocCast(a1.annotationOn, a1))) return a2;
- if (Doc.AreProtosEqual(DocCast(anchor.annotationOn, anchor), DocCast(a2.annotationOn, a2))) return a1;
+ if (Doc.AreProtosEqual(DocCast(anchor.annotationOn, anchor), DocCast(a1?.annotationOn, a1))) return a2;
+ if (Doc.AreProtosEqual(DocCast(anchor.annotationOn, anchor), DocCast(a2?.annotationOn, a2))) return a1;
if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
}
}
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index ca3610cc0..2764339e6 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -19,13 +19,13 @@ $resizeHandler: 8px;
position: absolute;
pointer-events: all;
cursor: pointer;
- background: white;
+ background: transparent;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
font-size: 30px;
- opacity: 0.1;
+ opacity: 0.6;
&:hover {
opacity: 1;
}
@@ -227,20 +227,19 @@ $resizeHandler: 8px;
&:hover {
opacity: 1;
}
-
- .checkbox{
+ .checkbox {
display: inline;
- .checkbox-box{
+ .checkbox-box {
display: inline;
position: relative;
top: -2.5;
left: 35;
- zoom: .7;
+ zoom: 0.7;
}
- & .checkbox-text{
+ & .checkbox-text {
display: inline;
position: relative;
top: 1.5;
@@ -248,7 +247,7 @@ $resizeHandler: 8px;
}
}
- .documentDecorations-shareNone{
+ .documentDecorations-shareNone {
width: calc(100% + 10px);
background: grey;
color: rgb(71, 71, 71);
@@ -256,29 +255,28 @@ $resizeHandler: 8px;
border: 2px solid rgb(71, 71, 71);
}
.documentDecorations-shareEdit,
- .documentDecorations-shareAdmin{
+ .documentDecorations-shareAdmin {
width: calc(100% + 10px);
background: rgb(254, 254, 199);
color: rgb(75, 75, 5);
border-radius: 8px;
border: 2px solid rgb(75, 75, 5);
}
- .documentDecorations-shareAugment{
+ .documentDecorations-shareAugment {
width: calc(100% + 10px);
background: rgb(208, 255, 208);
- color:rgb(19, 80, 19);
+ color: rgb(19, 80, 19);
border-radius: 8px;
border: 2px solid rgb(19, 80, 19);
-
}
- .documentDecorations-shareView{
+ .documentDecorations-shareView {
width: calc(100% + 10px);
background: rgb(213, 213, 255);
color: rgb(25, 25, 101);
border-radius: 8px;
border: 2px solid rgb(25, 25, 101);
}
- .documentDecorations-shareNot-Shared{
+ .documentDecorations-shareNot-Shared {
width: calc(100% + 10px);
background: rgb(255, 207, 207);
color: rgb(146, 58, 58);
@@ -320,8 +318,8 @@ $resizeHandler: 8px;
.documentDecorations-bottomResizer,
.documentDecorations-rightResizer {
pointer-events: auto;
- background: $medium-gray;
- opacity: 0.2;
+ background: $medium-gray-dim;
+ //opacity: 0.2;
&:hover {
opacity: 1;
}
@@ -354,7 +352,7 @@ $resizeHandler: 8px;
position: absolute;
border-radius: 100%;
left: 7px;
- top: 27px;
+ top: 7px;
background: $medium-gray;
height: 10;
width: 10;
@@ -447,9 +445,9 @@ $resizeHandler: 8px;
opacity: 1;
}
- .documentDecorations-bottomRightResizer {
- grid-row: 4;
- }
+ // .documentDecorations-bottomRightResizer {
+ // grid-row: 4;
+ // }
.documentDecorations-topRightResizer,
.documentDecorations-bottomLeftResizer {
@@ -488,6 +486,10 @@ $resizeHandler: 8px;
}
}
+.documentDecorations-container.showNothing {
+ margin-top: $headerHeight;
+ grid-template-rows: $resizeHandler 1fr $resizeHandler;
+}
.documentDecorations-background {
background: lightblue;
position: absolute;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 40eb1fe2b..8b8b7fa4d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -20,6 +20,7 @@ import { DocumentManager } from '../util/DocumentManager';
import { DragManager } from '../util/DragManager';
import { LinkFollower } from '../util/LinkFollower';
import { SelectionManager } from '../util/SelectionManager';
+import { SettingsManager } from '../util/SettingsManager';
import { SnappingManager } from '../util/SnappingManager';
import { UndoManager } from '../util/UndoManager';
import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -46,9 +47,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
private _titleHeight = 20;
private _resizeUndo?: UndoManager.Batch;
private _offX = 0;
- _offY = 0; // offset from click pt to inner edge of resize border
+ private _offY = 0; // offset from click pt to inner edge of resize border
private _snapX = 0;
- _snapY = 0; // last snapped location of resize border
+ private _snapY = 0; // last snapped location of resize border
private _dragHeights = new Map<Doc, { start: number; lowest: number }>();
private _inkDragDocs: { doc: Doc; x: number; y: number; width: number; height: number }[] = [];
@@ -70,8 +71,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
DocumentDecorations.Instance = this;
reaction(
() => SelectionManager.Views().slice(),
- action(docs => {
- this._showNothing = !DocumentView.LongPress && docs.length === 1; // show decorations if multiple docs are selected or we're long pressing
+ action(views => {
+ this._showNothing = !DocumentView.LongPress && views.length === 1; // show decorations if multiple docs are selected or we're long pressing
this._editingTitle = false;
})
);
@@ -80,8 +81,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
'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 || this.Bounds.r < e.clientX || this.Bounds.y > e.clientY || this.Bounds.b < e.clientY)) {
+ 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;
}
}
})
@@ -225,10 +228,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
e.x,
e.y,
{
- dragComplete: action(e => {
- dragData.canEmbed && SelectionManager.DeselectAll();
- this._hidden = false;
- }),
+ dragComplete: action(e => (this._hidden = false)),
hideSource: true,
}
);
@@ -255,7 +255,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
iconView.props.removeDocument?.(iconView.props.Document);
}
});
- SelectionManager.DeselectAll();
+ views.forEach(v => SelectionManager.DeselectView());
}
this._iconifyBatch?.end();
this._iconifyBatch = undefined;
@@ -403,10 +403,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}), // moveEvent
action(action(() => (this._isRotating = false))), // upEvent
action((e, doubleTap) => {
- if (doubleTap) {
- seldocview.rootDoc.rotation_centerX = 0.5;
- seldocview.rootDoc.rotation_centerY = 0.5;
- }
+ seldocview.rootDoc.rotation_centerX = 0;
+ seldocview.rootDoc.rotation_centerY = 0;
})
);
};
@@ -777,10 +775,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
const hideResizers =
![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
- const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || 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;
// 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 ||
@@ -788,6 +787,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
this._isRounding ||
this._isRotating;
const hideDeleteButton =
+ this._showNothing ||
hideDecorations ||
this._isRounding ||
this._isRotating ||
@@ -821,7 +821,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
// Radius constants
const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
- const borderRadius = numberValue(StrCast(seldocview.rootDoc.layout_borderRounding));
+ 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 maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
const radiusHandle = (borderRadius / docMax) * maxDist;
@@ -884,9 +884,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
)}
</div>
);
-
+ const freeformDoc = SelectionManager.Views().some(v => v.props.CollectionFreeFormDocumentView?.());
return (
- <div className="documentDecorations" style={{ display: this._showNothing ? 'none' : undefined }}>
+ <div className="documentDecorations" style={{ display: this._showNothing && !freeformDoc ? 'none' : undefined }}>
<div
className="documentDecorations-background"
style={{
@@ -906,13 +906,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
{bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? null : (
<div>
<div
- className="documentDecorations-container"
+ 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._titleHeight + 'px',
+ height: bounds.b - bounds.y + this._resizeBorderWidth + (this._showNothing ? 0 : this._titleHeight) + 'px',
}}>
<div
className="documentDecorations-topbar"
@@ -944,17 +944,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
{useRounding && (
<div
key="rad"
+ className="documentDecorations-borderRadius"
style={{
background: `${this._isRounding ? Colors.MEDIUM_BLUE : undefined}`,
- transform: `translate(${radiusHandleLocation}px, ${radiusHandleLocation}px)`,
+ transform: `translate(${radiusHandleLocation ?? 0}px, ${(radiusHandleLocation ?? 0) + (this._showNothing ? 0 : this._titleHeight)}px)`,
}}
- className={`documentDecorations-borderRadius`}
onPointerDown={this.onRadiusDown}
onContextMenu={e => e.preventDefault()}
/>
)}
- {hideDocumentButtonBar ? null : (
+ {hideDocumentButtonBar || this._showNothing ? null : (
<div
className="link-button-container"
key="links"
@@ -980,8 +980,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}}>
{this._isRotating ? null : (
<Tooltip enterDelay={750} title={<div className="dash-tooltip">tap to set rotate center, drag to rotate</div>}>
- <div className="documentDecorations-rotation" style={{ pointerEvents: 'all', color: 'blue' }} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}>
- <IconButton icon={<FaUndo />} color={Colors.LIGHT_GRAY} />
+ <div className="documentDecorations-rotation" style={{ pointerEvents: 'all', background: 'transparent' }} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}>
+ <IconButton icon={<FaUndo />} color={SettingsManager.userColor} />
</div>
</Tooltip>
)}
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index 07e3270b1..0d7f7ebd8 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -2,18 +2,16 @@ import React = require('react');
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../fields/Doc';
-import { ControlPoint, InkData, PointData, InkField } from '../../fields/InkField';
+import { ControlPoint, InkData, PointData } from '../../fields/InkField';
import { List } from '../../fields/List';
import { listSpec } from '../../fields/Schema';
-import { Cast, NumCast } from '../../fields/Types';
-import { setupMoveUpEvents, returnFalse } from '../../Utils';
-import { Transform } from '../util/Transform';
+import { Cast } from '../../fields/Types';
+import { returnFalse, setupMoveUpEvents } from '../../Utils';
+import { SelectionManager } from '../util/SelectionManager';
import { UndoManager } from '../util/UndoManager';
import { Colors } from './global/globalEnums';
import { InkingStroke } from './InkingStroke';
import { InkStrokeProperties } from './InkStrokeProperties';
-import { DocumentView } from './nodes/DocumentView';
-import { SelectionManager } from '../util/SelectionManager';
export interface InkControlProps {
inkDoc: Doc;
@@ -155,7 +153,7 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
cx={control.X}
cy={control.Y}
r={this.props.screenSpaceLineWidth * 2 * scale}
- opacity={this.props.inkView.controlUndo ? 0.15 : 1}
+ opacity={this.props.inkView.controlUndo ? 0.35 : 1}
height={this.props.screenSpaceLineWidth * 4 * scale}
width={this.props.screenSpaceLineWidth * 4 * scale}
strokeWidth={this.props.screenSpaceLineWidth / 2}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 9b52f5870..d619f5123 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -372,6 +372,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
}
render() {
TraceMobx();
+ if (!this.rootDoc._layout_isSvg) setTimeout(action(() => (this.rootDoc._layout_isSvg = true)));
const { inkData, inkStrokeWidth, inkLeft, inkTop, inkScaleX, inkScaleY, inkWidth, inkHeight } = this.inkScaledData();
const startMarker = StrCast(this.layoutDoc.stroke_startMarker);
@@ -390,7 +391,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
}
const highlight = !this.controlUndo && this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting);
const highlightIndex = highlight?.highlightIndex;
- const highlightColor = (!this.props.isSelected() || !isInkMask) && highlight?.highlightIndex ? highlight?.highlightColor : undefined;
+ const highlightColor = !this.props.isSelected() && !isInkMask && highlight?.highlightIndex ? highlight?.highlightColor : undefined;
const color = StrCast(this.layoutDoc.stroke_outlineColor, !closed && fillColor && fillColor !== 'transparent' ? StrCast(this.layoutDoc.color, 'transparent') : 'transparent');
// Visually renders the polygonal line made by the user.
@@ -419,15 +420,16 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
undefined,
color === 'transparent' ? highlightColor : undefined
);
+ const higlightMargin = Math.min(12, Math.max(2, 0.3 * inkStrokeWidth));
// Invisible polygonal line that enables the ink to be selected by the user.
const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, mask: boolean = false) =>
InteractionUtils.CreatePolyline(
inkData,
inkLeft,
inkTop,
- mask && color === 'transparent' ? this.strokeColor : color,
+ mask && color === 'transparent' ? this.strokeColor : highlightColor ?? color,
inkStrokeWidth,
- inkStrokeWidth + NumCast(this.layoutDoc.stroke_borderWidth) + (fillColor ? (closed ? 2 : (highlightIndex ?? 0) + 2) : 2),
+ inkStrokeWidth + NumCast(this.layoutDoc.stroke_borderWidth) + (fillColor ? (closed ? higlightMargin : (highlightIndex ?? 0) + higlightMargin) : higlightMargin),
StrCast(this.layoutDoc.stroke_lineJoin),
StrCast(this.layoutDoc.stroke_lineCap),
StrCast(this.layoutDoc.stroke_bezier),
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index d1561fd67..61aa616ec 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -61,8 +61,8 @@ export class PropertiesButtons extends React.Component<{}, {}> {
text={label(targetDoc?.[property])}
color={SettingsManager.userColor}
icon={icon(targetDoc?.[property] as any)}
- iconPlacement={'left'}
- align={'flex-start'}
+ iconPlacement="left"
+ align="flex-start"
fillWidth={true}
toggleType={ToggleType.BUTTON}
onClick={undoable(() => {
@@ -173,6 +173,16 @@ export class PropertiesButtons extends React.Component<{}, {}> {
);
}
+ @computed get verticalAlignButton() {
+ //select text
+ return this.propertyToggleBtn(
+ on => (on ? 'ALIGN TOP' : 'ALIGN CENTER'),
+ '_layout_centered',
+ on => `${on ? 'Text is aligned with top of document' : 'Text is aligned with center of document'} `,
+ on => <MdTouchApp /> // 'eye'
+ );
+ }
+
@computed get fitContentButton() {
return this.propertyToggleBtn(
on => (on ? 'PREVIOUS VIEW' : 'VIEW ALL'), //'View All',
@@ -512,6 +522,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
{toggle(this.layout_fitWidthButton)}
{/* {toggle(this.freezeThumb)} */}
{toggle(this.forceActiveButton)}
+ {toggle(this.verticalAlignButton, { display: !isText ? 'none' : '' })}
{toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
{/* {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })} */}
{toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 6ee96de5b..48c96d064 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -1,10 +1,11 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
-import { Dropdown, DropdownType, IconButton, IListItemProps, ListBox, ListItem, Popup, Shadows, Size, Type } from 'browndash-components';
+import { Dropdown, DropdownType, IconButton, IListItemProps, Shadows, Size, Type } from 'browndash-components';
import { action, runInAction, untracked } from 'mobx';
import { extname } from 'path';
import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs';
+import { FaFilter } from 'react-icons/fa';
import { Doc, Opt, StrListCast } from '../../fields/Doc';
import { BoolCast, Cast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types';
import { DashColor, lightOrDark, Utils } from '../../Utils';
@@ -13,18 +14,16 @@ import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager';
import { IsFollowLinkScript } from '../util/LinkFollower';
import { LinkManager } from '../util/LinkManager';
import { SelectionManager } from '../util/SelectionManager';
-import { ColorScheme, SettingsManager } from '../util/SettingsManager';
+import { SettingsManager } from '../util/SettingsManager';
import { undoBatch, UndoManager } from '../util/UndoManager';
import { TreeSort } from './collections/TreeSort';
import { Colors } from './global/globalEnums';
-import { InkingStroke } from './InkingStroke';
import { DocumentView, DocumentViewProps } from './nodes/DocumentView';
import { FieldViewProps } from './nodes/FieldView';
import { KeyValueBox } from './nodes/KeyValueBox';
+import { PropertiesView } from './PropertiesView';
import './StyleProvider.scss';
import React = require('react');
-import { PropertiesView } from './PropertiesView';
-import { FaFilter } from 'react-icons/fa';
export enum StyleProp {
TreeViewIcon = 'treeView_Icon',
@@ -85,7 +84,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
const isCaption = property.includes(':caption');
const isAnchor = property.includes(':anchor');
const isAnnotated = property.includes(':annotated');
- const isInk = () => doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name) && !props?.LayoutTemplateString;
+ const isInk = () => doc && doc._layout_isSvg && !props?.LayoutTemplateString;
const isOpen = property.includes(':open');
const isEmpty = property.includes(':empty');
const boxBackground = property.includes(':box');
@@ -124,7 +123,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
highlightStyle,
highlightColor,
highlightIndex,
- highlightStroke: doc.type === DocumentType.INK,
+ highlightStroke: doc.type === DocumentType.INK || doc.type === DocumentType.LINK,
};
}
}
@@ -171,7 +170,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
case StyleProp.BorderPath:
const borderPath = Doc.IsComicStyle(doc) &&
props?.renderDepth &&
- doc?.type !== DocumentType.INK && { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, 0.08), width: 3 };
+ ![DocumentType.INK, DocumentType.LINK].includes(doc?.type as any) && { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, 0.08), width: 3 };
return !borderPath
? null
: {
@@ -201,11 +200,11 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
case DocumentType.PRES: docColor = docColor || 'transparent'; break;
case DocumentType.FONTICON: docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break;
case DocumentType.RTF: docColor = docColor || Colors.LIGHT_GRAY; break;
+ case DocumentType.LINK: docColor = (isAnchor ? docColor : undefined); break;
case DocumentType.INK: docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined; break;
case DocumentType.EQUATION: docColor = docColor || 'transparent'; break;
case DocumentType.LABEL: docColor = docColor || Colors.LIGHT_GRAY; break;
case DocumentType.BUTTON: docColor = docColor || Colors.LIGHT_GRAY; break;
- case DocumentType.LINK: docColor = (isAnchor ? docColor : '') || 'transparent'; break;
case DocumentType.IMG:
case DocumentType.WEB:
case DocumentType.PDF:
@@ -258,13 +257,13 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
}
}
case StyleProp.PointerEvents:
+ if (!props?.treeViewDoc && doc?._isGroup && (!doc.backgroundColor || doc.backgroundColor === 'transparent') && !props?.DocumentView?.().docView?.isContentActive?.()) return 'none'; // groups with no backgrounds that are not active (eg selected, or child selected), don't directly respond to pointer events (but their children can to allow the group to be selected or dragged)
if (StrCast(doc?.pointerEvents) && !props?.LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc
if (docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.() !== undefined) return docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.();
if (DocumentView.ExploreMode || doc?.layout_unrendered) return isInk() ? 'visiblePainted' : 'all';
- if (props?.contentPointerEvents) return StrCast(props.contentPointerEvents);
if (props?.pointerEvents?.() === 'none') return 'none';
if (opacity() === 0) return 'none';
- if (props?.isDocumentActive?.()) return isInk() ? 'visiblePainted' : 'all';
+ if (props?.isDocumentActive?.() && !props.treeViewDoc) return isInk() ? 'visiblePainted' : 'all';
return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active
case StyleProp.Decorations:
const lock = () => {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index c7ad80f11..afeef5a8f 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -265,7 +265,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
addDocument={this.props.addDocument}
moveDocument={this.props.moveDocument}
removeDocument={this.props.removeDocument}
- contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)}
+ contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents) as any}
whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
addDocTab={this.props.addDocTab}
bringToFront={returnFalse}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index c43a9d2b8..0b29e7286 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -357,7 +357,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
addDocument={this.props.addDocument}
moveDocument={this.props.moveDocument}
removeDocument={this.props.removeDocument}
- contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)}
+ contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents) as any}
whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
addDocTab={this.props.addDocTab}
bringToFront={returnFalse}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 011bc1de5..09e7cdb32 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -217,7 +217,10 @@ export function CollectionSubView<X>(moreProps?: X) {
(de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.rootDoc));
const moved = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
added = canAdd || moved ? moved : undefined;
- } else {
+ } else if (addedDocs.length) {
+ added = this.addDocument(addedDocs);
+ }
+ if (!added && ScriptCast(this.rootDoc.dropConverter)) {
ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
added = addedDocs.length ? this.addDocument(addedDocs) : true;
}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index f89aa065b..b57402531 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -201,12 +201,10 @@ export class TreeView extends React.Component<TreeViewProps> {
if (!docView) {
this._editTitle = false;
} else if (docView.isSelected()) {
- const doc = docView.Document;
- SelectionManager.SelectSchemaViewDoc(doc);
this._editTitle = true;
this._disposers.selection = reaction(
- () => SelectionManager.SelectedSchemaDoc(),
- seldoc => seldoc !== doc && this.setEditTitle(undefined)
+ () => docView.isSelected(),
+ isSel => !isSel && this.setEditTitle(undefined)
);
} else {
docView.select(false);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index c1c01eacb..d93e44ab7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -42,6 +42,7 @@ export interface PoolData {
transition?: string;
highlight?: boolean;
replica: string;
+ pointerEvents?: string; // without this, toggling lockPosition of a group/collection in a freeform view won't update until something else invalidates the freeform view's documents forcing -- this is a problem with doLayoutComputation which makes a performance test to insure somethingChanged
pair: { layout: Doc; data?: Doc };
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index bfc61f601..5eff6b8e0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -16,17 +16,18 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro
import { ImageField } from '../../../../fields/URLField';
import { TraceMobx } from '../../../../fields/util';
import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, emptyFunction, intersectRect, lightOrDark, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { aggregateBounds, emptyFunction, intersectRect, lightOrDark, returnFalse, returnNone, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { InteractionUtils } from '../../../util/InteractionUtils';
+import { FollowLinkScript } from '../../../util/LinkFollower';
import { ReplayMovements } from '../../../util/ReplayMovements';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SelectionManager } from '../../../util/SelectionManager';
-import { ColorScheme, freeformScrollMode } from '../../../util/SettingsManager';
+import { freeformScrollMode } from '../../../util/SettingsManager';
import { SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
@@ -51,7 +52,6 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
import React = require('react');
-import { FollowLinkScript } from '../../../util/LinkFollower';
export type collectionFreeformViewProps = {
NativeWidth?: () => number;
@@ -156,12 +156,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const cb = Cast(this.rootDoc.contentBounds, listSpec('number'));
return cb
? { x: cb[0], y: cb[1], r: cb[2], b: cb[3] }
- : this.props.contentBounds?.() ??
- aggregateBounds(
- this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
- NumCast(this.layoutDoc._xPadding, 10),
- NumCast(this.layoutDoc._yPadding, 10)
- );
+ : aggregateBounds(
+ this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
+ NumCast(this.layoutDoc._xPadding, 10),
+ NumCast(this.layoutDoc._yPadding, 10)
+ );
}
@computed get nativeWidth() {
return this.props.NativeWidth?.() || (this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)));
@@ -1183,23 +1182,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
bringToFront = (doc: Doc, sendToBack?: boolean) => {
- if (sendToBack) {
- const docs = this.childLayoutPairs.map(pair => pair.layout).slice();
- docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
- let zfirst = docs.length ? NumCast(docs[0].zIndex) : 0;
- doc.zIndex = zfirst - 1;
- } else if (doc.stroke_isInkMask) {
+ if (doc.stroke_isInkMask) {
doc.zIndex = 5000;
} else {
- const docs = this.childLayoutPairs.map(pair => pair.layout).slice();
- docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
- let zlast = docs.length ? Math.max(docs.length, NumCast(docs.lastElement().zIndex)) : 1;
- if (docs.lastElement() !== doc) {
- if (zlast - docs.length > 100) {
- for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1;
- zlast = docs.length + 1;
+ // prettier-ignore
+ const docs = this.childLayoutPairs.map(pair => pair.layout)
+ .sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
+ if (sendToBack) {
+ const zfirst = docs.length ? NumCast(docs[0].zIndex) : 0;
+ doc.zIndex = zfirst - 1;
+ } else {
+ let zlast = docs.length ? Math.max(docs.length, NumCast(docs.lastElement().zIndex)) : 1;
+ if (docs.lastElement() !== doc) {
+ if (zlast - docs.length > 100) {
+ for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1;
+ zlast = docs.length + 1;
+ }
+ doc.zIndex = zlast + 1;
}
- doc.zIndex = zlast + 1;
}
}
};
@@ -1291,25 +1291,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return this.addDocument?.(newDoc);
}
};
- @computed get _pointerEvents() {
+ @computed get childPointerEvents() {
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
const pointerEvents = DocumentView.Interacting
? 'none'
: this.props.childPointerEvents?.() ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
return pointerEvents;
}
- pointerEvents = () => this._pointerEvents;
+ childPointerEventsFunc = () => this.childPointerEvents;
childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
+ groupChildPointerEvents = () => (this.props.isDocumentActive?.() ? 'all' : 'none');
getChildDocView(entry: PoolData) {
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
return (
<CollectionFreeFormDocumentView
key={childLayout[Id] + (entry.replica || '')}
+ GroupPointerEvents={(childLayout._isGroup && !this.childContentsActive()) || childLayout.pointerEvents === 'none' ? returnNone : this.rootDoc._isGroup ? this.groupChildPointerEvents : undefined}
DataDoc={childData}
Document={childLayout}
renderDepth={this.props.renderDepth + 1}
replica={entry.replica}
+ hideDecorations={BoolCast(childLayout._layout_isSvg && childLayout.type === DocumentType.LINK)}
suppressSetHeight={this.layoutEngine ? true : false}
renderCutoffProvider={this.renderCutoffProvider}
CollectionFreeFormView={this}
@@ -1327,7 +1330,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- isDocumentActive={this.props.childDocumentsActive?.() || this.rootDoc._isGroup ? this.props.isDocumentActive : this.isContentActive}
+ isDocumentActive={childLayout.pointerEvents === 'none' ? returnFalse : this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
isContentActive={this.childContentsActive}
focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
addDocTab={this.addDocTab}
@@ -1344,7 +1347,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
bringToFront={this.bringToFront}
layout_showTitle={this.props.childlayout_showTitle}
dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
- pointerEvents={this.pointerEvents}
+ pointerEvents={this.childPointerEventsFunc}
//fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeView_FreezeChildDimensions)} // bcz: check this
/>
);
@@ -1412,6 +1415,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
height: _height,
transition: StrCast(childDocLayout.dataTransition),
pair: params.pair,
+ pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
replica: '',
};
}
@@ -1531,7 +1535,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
newPos.z !== lastPos.z ||
newPos.rotation !== lastPos.rotation ||
newPos.zIndex !== lastPos.zIndex ||
- newPos.transition !== lastPos.transition
+ newPos.transition !== lastPos.transition ||
+ newPos.pointerEvents !== lastPos.pointerEvents
) {
this._layoutPoolData.set(entry[0], newPos);
somethingChanged = true;
@@ -1625,8 +1630,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
this._disposers.active = reaction(
- () => this.isContentActive(),
- active => this.rootDoc[this.autoResetFieldKey] && !active && this.resetView()
+ () => this.isContentActive(), // if autoreset is on, then whenever the view is selected, it will be restored to it default pan/zoom positions
+ active => this.rootDoc[this.autoResetFieldKey] && active && this.resetView()
);
this._disposers.fitContent = reaction(
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5c053fefc..a30ec5302 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -377,7 +377,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
newCollection._height = this.Bounds.height;
newCollection._isGroup = makeGroup;
newCollection._dragWhenActive = makeGroup;
- newCollection.forceActive = makeGroup;
newCollection.x = this.Bounds.left;
newCollection.y = this.Bounds.top;
newCollection.layout_fitWidth = true;
diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss
index 20ccd9ebd..c129d29eb 100644
--- a/src/client/views/global/globalCssVariables.scss
+++ b/src/client/views/global/globalCssVariables.scss
@@ -4,6 +4,7 @@ $white: #ffffff;
$off-white: #fdfdfd;
$light-gray: #dfdfdf;
$medium-gray: #9f9f9f;
+$medium-gray-dim: #9f9f9f30;
$dark-gray: #323232;
$black: #000000;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 4a438f826..199835dd9 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -6,13 +6,12 @@ import { listSpec } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { numberRange } from '../../../Utils';
+import { numberRange, OmitKeys } from '../../../Utils';
import { DocumentManager } from '../../util/DocumentManager';
import { SelectionManager } from '../../util/SelectionManager';
import { Transform } from '../../util/Transform';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { DocComponent } from '../DocComponent';
-import { InkingStroke } from '../InkingStroke';
import { StyleProp } from '../StyleProvider';
import './CollectionFreeFormDocumentView.scss';
import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
@@ -26,6 +25,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
dataTransition?: string;
replica: string;
CollectionFreeFormView: CollectionFreeFormView;
+ GroupPointerEvents?: () => 'none' | 'all' | undefined; // pointer events for this freeform doc view wrapper that are not passed to the docView. This allows items in a group to trigger the group to be selected, without selecting the items themselves
}
@observer
@@ -191,17 +191,18 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
panelHeight = () => this.sizeProvider?.height || this.props.PanelHeight?.();
screenToLocalTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.X, -this.Y);
returnThis = () => this;
+
render() {
TraceMobx();
const divProps: DocumentViewProps = {
- ...this.props,
+ ...OmitKeys(this.props, ['GroupPointerEvents']).omit,
CollectionFreeFormDocumentView: this.returnThis,
styleProvider: this.styleProvider,
ScreenToLocalTransform: this.screenToLocalTransform,
PanelWidth: this.panelWidth,
PanelHeight: this.panelHeight,
};
- const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString && !this.layoutDoc._stroke_isInkMask;
+ const isInk = this.layoutDoc._layout_isSvg && !this.props.LayoutTemplateString && !this.layoutDoc._stroke_isInkMask;
return (
<div
className="collectionFreeFormDocumentView-container"
@@ -213,7 +214,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
transition: this.dataProvider?.transition ?? (this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition)),
zIndex: this.ZInd,
display: this.sizeProvider?.width ? undefined : 'none',
- pointerEvents: isInk ? 'none' : undefined,
+ pointerEvents: this.props.GroupPointerEvents?.() ?? (isInk ? 'none' : undefined),
}}>
{this.props.renderCutoffProvider(this.props.Document) ? (
<div style={{ position: 'absolute', width: this.panelWidth(), height: this.panelHeight(), background: 'lightGreen' }} />
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index da665a502..c2355e4f7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,5 +1,5 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
@@ -53,6 +53,7 @@ import { PresEffect, PresEffectDirection } from './trails';
import { PinProps, PresBox } from './trails/PresBox';
import React = require('react');
import { KeyValueBox } from './KeyValueBox';
+import { LinkBox } from './LinkBox';
const { Howl } = require('howler');
interface Window {
@@ -142,6 +143,7 @@ export interface DocComponentView {
annotationKey?: string;
getTitle?: () => string;
getCenter?: (xf: Transform) => { X: number; Y: number };
+ screenBounds?: () => { left: number; top: number; right: number; bottom: number; center?:{X:number, Y:number} };
ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number };
@@ -154,7 +156,6 @@ export interface DocumentViewSharedProps {
renderDepth: number;
Document: Doc;
DataDoc?: Doc;
- contentBounds?: () => undefined | { x: number; y: number; r: number; b: number };
fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
suppressSetHeight?: boolean;
setContentView?: (view: DocComponentView) => any;
@@ -219,7 +220,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
hideLinkAnchors?: boolean;
isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
- contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
+ contentPointerEvents?: 'none' | 'all' | undefined; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
radialMenu?: String[];
LayoutTemplateString?: string;
dontCenter?: 'x' | 'y' | 'xy';
@@ -425,7 +426,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
public static addDocTabFunc: (doc: Doc, location: OpenWhere) => boolean = returnFalse;
onClick = action((e: React.MouseEvent | React.PointerEvent) => {
- if (!this.Document.ignoreClick && this.pointerEvents !== 'none' && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
+ if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
let stopPropagate = true;
let preventDefault = true;
!this.rootDoc._keepZWhenDragged && this.props.bringToFront(this.rootDoc);
@@ -536,7 +537,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
!this.props.onBrowseClick?.() &&
!this.Document.ignoreClick &&
e.button === 0 &&
- this.pointerEvents !== 'none' &&
!Doc.IsInMyOverlay(this.layoutDoc)
) {
e.stopPropagation();
@@ -635,6 +635,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.embedContainer) {
const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
+ if (de.complete.linkDocument) {
+ de.complete.linkDocument.layout_isSvg = true;
+ this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.addDocument(de.complete.linkDocument);
+ }
}
e.stopPropagation();
return true;
@@ -889,12 +893,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
/// disable pointer events on content when there's an enabled onClick script (but not the browse script) and the contents aren't forced active, or if contents are marked inactive
@computed get _contentPointerEvents() {
+ if (this.props.contentPointerEvents) return this.props.contentPointerEvents;
return (!this.disableClickScriptFunc && this.onClickHandler && !this.props.onBrowseClick?.() && this.isContentActive() !== true) || this.isContentActive() === false ? 'none' : this.pointerEvents;
}
contentPointerEvents = () => this._contentPointerEvents;
@computed get contents() {
TraceMobx();
- const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString;
+ const isInk = this.layoutDoc._layout_isSvg && !this.props.LayoutTemplateString;
return (
<div
className="documentView-contentsView"
@@ -1414,16 +1419,20 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.props.dontCenter?.includes('y') ? 0 : this.Yshift;
}
- public toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
+ public toggleNativeDimensions = () => this.docView && this.rootDoc.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
public getBounds = () => {
if (!this.docView?.ContentDiv || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
return undefined;
}
+ if (this.docView._componentView?.screenBounds) {
+ return this.docView._componentView.screenBounds();
+ }
const xf = this.docView.props
.ScreenToLocalTransform()
.scale(this.trueNativeWidth() ? this.nativeScaling : 1)
.inverse();
const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
+
if (this.docView.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
const docuBox = this.docView.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), center: undefined };
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index efb949a47..d871c88ba 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,11 +1,19 @@
import React = require('react');
+import { Bezier } from 'bezier-js';
+import { computed, action } from 'mobx';
import { observer } from 'mobx-react';
-import { emptyFunction, returnAlways, returnFalse, returnTrue } from '../../../Utils';
+import { Height, Width } from '../../../fields/DocSymbols';
+import { Id } from '../../../fields/FieldSymbols';
+import { DocCast, StrCast } from '../../../fields/Types';
+import { aggregateBounds, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils';
+import { DocumentManager } from '../../util/DocumentManager';
import { ViewBoxBaseComponent } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
import { ComparisonBox } from './ComparisonBox';
import { FieldView, FieldViewProps } from './FieldView';
import './LinkBox.scss';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
+import { Transform } from '../../util/Transform';
@observer
export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -17,8 +25,104 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
componentDidMount() {
this.props.setContentView?.(this);
}
+ @computed get anchor1() {
+ const anchor1 = DocCast(this.rootDoc.link_anchor_1);
+ const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1;
+ return DocumentManager.Instance.getDocumentView(anchor_1);
+ }
+ @computed get anchor2() {
+ const anchor2 = DocCast(this.rootDoc.link_anchor_2);
+ const anchor_2 = anchor2?.layout_unrendered ? DocCast(anchor2.annotationOn) : anchor2;
+ return DocumentManager.Instance.getDocumentView(anchor_2);
+ }
+ screenBounds = () => {
+ if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
+ const a_invXf = this.anchor1.props.ScreenToLocalTransform().inverse();
+ const b_invXf = this.anchor2.props.ScreenToLocalTransform().inverse();
+ const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(this.anchor1.rootDoc[Width](), this.anchor1.rootDoc[Height]()) };
+ const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(this.anchor2.rootDoc[Width](), this.anchor2.rootDoc[Height]()) };
+
+ const pts = [] as number[][];
+ pts.push([(a_scrBds.tl[0] + a_scrBds.br[0]) / 2, (a_scrBds.tl[1] + a_scrBds.br[1]) / 2]);
+ pts.push(Utils.getNearestPointInPerimeter(a_scrBds.tl[0], a_scrBds.tl[1], a_scrBds.br[0] - a_scrBds.tl[0], a_scrBds.br[1] - a_scrBds.tl[1], (b_scrBds.tl[0] + b_scrBds.br[0]) / 2, (b_scrBds.tl[1] + b_scrBds.br[1]) / 2));
+ pts.push(Utils.getNearestPointInPerimeter(b_scrBds.tl[0], b_scrBds.tl[1], b_scrBds.br[0] - b_scrBds.tl[0], b_scrBds.br[1] - b_scrBds.tl[1], (a_scrBds.tl[0] + a_scrBds.br[0]) / 2, (a_scrBds.tl[1] + a_scrBds.br[1]) / 2));
+ pts.push([(b_scrBds.tl[0] + b_scrBds.br[0]) / 2, (b_scrBds.tl[1] + b_scrBds.br[1]) / 2]);
+ const agg = aggregateBounds(
+ pts.map(pt => ({ x: pt[0], y: pt[1] })),
+ 0,
+ 0
+ );
+ return { left: agg.x, top: agg.y, right: agg.r, bottom: agg.b, center: undefined };
+ }
+ return { left: 0, top: 0, right: 0, bottom: 0, center: undefined };
+ };
render() {
- if (this.dataDoc.treeView_Open === undefined) setTimeout(() => (this.dataDoc.treeView_Open = true));
+ if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
+ const a = (this.anchor1 ?? this.anchor2)!;
+ const b = (this.anchor2 ?? this.anchor1)!;
+
+ const parxf = this.props.docViewPath()[this.props.docViewPath().length - 2].ComponentView as CollectionFreeFormView;
+ const this_xf = parxf?.getTransform() ?? Transform.Identity; //this.props.ScreenToLocalTransform();
+ const a_invXf = a.props.ScreenToLocalTransform().inverse();
+ const b_invXf = b.props.ScreenToLocalTransform().inverse();
+ const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(a.rootDoc[Width](), a.rootDoc[Height]()) };
+ const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(b.rootDoc[Width](), b.rootDoc[Height]()) };
+ const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) };
+ const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) };
+
+ const ppt1 = [(a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2];
+ const pt1 = Utils.getNearestPointInPerimeter(a_bds.tl[0], a_bds.tl[1], a_bds.br[0] - a_bds.tl[0], a_bds.br[1] - a_bds.tl[1], (b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2);
+ const pt2 = Utils.getNearestPointInPerimeter(b_bds.tl[0], b_bds.tl[1], b_bds.br[0] - b_bds.tl[0], b_bds.br[1] - b_bds.tl[1], (a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2);
+ const ppt2 = [(b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2];
+
+ const pts = [ppt1, pt1, pt2, ppt2].map(pt => [pt[0], pt[1]]);
+ const [lx, rx, ty, by] = [Math.min(pt1[0], pt2[0]), Math.max(pt1[0], pt2[0]), Math.min(pt1[1], pt2[1]), Math.max(pt1[1], pt2[1])];
+ setTimeout(
+ action(() => {
+ this.layoutDoc.x = lx;
+ this.layoutDoc.y = ty;
+ this.layoutDoc._width = rx - lx;
+ this.layoutDoc._height = by - ty;
+ })
+ );
+
+ const highlight = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting);
+ const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : undefined;
+
+ const bez = new Bezier(pts.map(p => ({ x: p[0], y: p[1] })));
+ const text = bez.get(0.5);
+ const linkDesc = StrCast(this.rootDoc.link_description) || 'description';
+ return (
+ <div style={{ pointerEvents: 'none', position: 'absolute', width: '100%', height: '100%' }}>
+ <svg width={Math.max(100, rx - lx)} height={Math.max(100, by - ty)} style={{ overflow: 'visible' }}>
+ <defs>
+ <filter x="0" y="0" width="1" height="1" id={`${this.rootDoc[Id] + 'background'}`}>
+ <feFlood floodColor={`${StrCast(this.rootDoc._backgroundColor, 'lightblue')}`} result="bg" />
+ <feMerge>
+ <feMergeNode in="bg" />
+ <feMergeNode in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ </defs>
+ <path
+ className="collectionfreeformlinkview-linkLine"
+ style={{ pointerEvents: this.props.pointerEvents?.() === 'none' ? 'none' : 'visibleStroke', stroke: highlightColor ?? 'lightblue', strokeWidth: 4 }}
+ d={`M ${pts[1][0] - lx} ${pts[1][1] - ty} C ${pts[1][0] + pts[1][0] - pts[0][0] - lx} ${pts[1][1] + pts[1][1] - pts[0][1] - ty},
+ ${pts[2][0] + pts[2][0] - pts[3][0] - lx} ${pts[2][1] + pts[2][1] - pts[3][1] - ty}, ${pts[2][0] - lx} ${pts[2][1] - ty}`}
+ />
+ <text
+ filter={`url(#${this.rootDoc[Id] + 'background'})`}
+ style={{ pointerEvents: this.props.pointerEvents?.() === 'none' ? 'none' : 'all', textAnchor: 'middle', fontSize: '12', stroke: 'black' }}
+ x={text.x - lx}
+ y={text.y - ty}>
+ <tspan>&nbsp;</tspan>
+ <tspan dy="2">{linkDesc.substring(0, 50) + (linkDesc.length > 50 ? '...' : '')}</tspan>
+ <tspan dy="2">&nbsp;</tspan>
+ </text>
+ </svg>
+ </div>
+ );
+ }
return (
<div className={`linkBox-container${this.props.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
<ComparisonBox
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 481e43feb..116069cbd 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -123,7 +123,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
Doc.AddToMyOverlay(value);
DocumentManager.Instance.AddViewRenderedCb(value, docView => {
Doc.UserDoc().currentRecording = docView.rootDoc;
- SelectionManager.SelectSchemaViewDoc(value);
+ docView.select(false);
RecordingBox.resumeWorkspaceReplaying(value);
});
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 348bdd79e..6765e1dea 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -5,6 +5,14 @@
height: 100%;
min-height: 100%;
}
+.formattedTextBox-inner.centered,
+.formattedTextBox-inner-rounded.centered {
+ align-items: center;
+ display: flex;
+ .ProseMirror {
+ min-height: unset;
+ }
+}
.ProseMirror:focus {
outline: none !important;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fcdfbdf2a..41b1c59b0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2149,7 +2149,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
onScroll={this.onScroll}
onDrop={this.ondrop}>
<div
- className={`formattedTextBox-inner${rounded}`}
+ className={`formattedTextBox-inner${rounded} ${this.layoutDoc.layout_centered ? 'centered' : ''}`}
ref={this.createDropTarget}
style={{
padding: StrCast(this.layoutDoc._textBoxPadding),
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 9e3eb28f9..feacdc9c5 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -687,16 +687,12 @@ export namespace Doc {
*/
export function ComputeContentBounds(docList: Doc[]) {
const bounds = docList.reduce(
- (bounds, doc) => {
- const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
- const [bptX, bptY] = [sptX + doc[Width](), sptY + doc[Height]()];
- return {
- x: Math.min(sptX, bounds.x),
- y: Math.min(sptY, bounds.y),
- r: Math.max(bptX, bounds.r),
- b: Math.max(bptY, bounds.b),
- };
- },
+ (bounds, doc) => ({
+ x: Math.min(NumCast(doc.x), bounds.x),
+ y: Math.min(NumCast(doc.y), bounds.y),
+ r: Math.max(NumCast(doc.x) + doc[Width](), bounds.r),
+ b: Math.max(NumCast(doc.y) + doc[Height](), bounds.b),
+ }),
{ x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE }
);
return bounds;