aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts57
-rw-r--r--src/client/views/InkingStroke.tsx2
-rw-r--r--src/client/views/PreviewCursor.scss1
-rw-r--r--src/client/views/PreviewCursor.tsx30
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx55
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx26
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx13
-rw-r--r--src/client/views/nodes/DocumentView.tsx24
-rw-r--r--src/client/views/nodes/FontIconBox.tsx2
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss24
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx66
-rw-r--r--src/client/views/nodes/ImageBox.scss1
-rw-r--r--src/client/views/nodes/ImageBox.tsx7
-rw-r--r--src/client/views/nodes/VideoBox.scss1
-rw-r--r--src/client/views/nodes/VideoBox.tsx2
-rw-r--r--src/client/views/pdf/PDFViewer.tsx7
-rw-r--r--src/new_fields/documentSchemas.ts2
-rw-r--r--src/new_fields/util.ts6
19 files changed, 226 insertions, 101 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 12acda4bb..37b509370 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -153,6 +153,63 @@ export namespace Utils {
return Math.max(lower, Math.min(upper, n));
}
+ export function distanceBetweenHorizontalLines(xs: number, xe: number, y: number, xs2: number, xe2: number, y2: number): [number, number[]] {
+ if ((xs2 < xs && xe2 > xs) || (xs2 < xe && xe2 > xe) || (xs2 > xs && xe2 < xe)) return [Math.abs(y - y2), [Math.max(xs, xs2), y, Math.min(xe, xe2), y]];
+ if (xe2 < xs) return [Math.sqrt((xe2 - xs) * (xe2 - xs) + (y2 - y) * (y2 - y)), [xs, y, xs, y]];
+ //if (xs2 > xe)
+ return [Math.sqrt((xs2 - xe) * (xs2 - xe) + (y2 - y) * (y2 - y)), [xe, y, xe, y]];
+ }
+ export function distanceBetweenVerticalLines(x: number, ys: number, ye: number, x2: number, ys2: number, ye2: number): [number, number[]] {
+ if ((ys2 < ys && ye2 > ys) || (ys2 < ye && ye2 > ye) || (ys2 > ys && ye2 < ye)) return [Math.abs(x - x2), [x, Math.max(ys, ys2), x, Math.min(ye, ye2)]];
+ if (ye2 < ys) return [Math.sqrt((ye2 - ys) * (ye2 - ys) + (x2 - x) * (x2 - x)), [x, ys, x, ys]];
+ //if (ys2 > ye)
+ return [Math.sqrt((ys2 - ye) * (ys2 - ye) + (x2 - x) * (x2 - x)), [x, ye, x, ye]];
+ }
+
+ function project(px: number, py: number, ax: number, ay: number, bx: number, by: number) {
+
+ if (ax === bx && ay === by) return { point: { x: ax, y: ay }, left: false, dot: 0, t: 0 };
+ var atob = { x: bx - ax, y: by - ay };
+ var atop = { x: px - ax, y: py - ay };
+ var len = atob.x * atob.x + atob.y * atob.y;
+ var dot = atop.x * atob.x + atop.y * atob.y;
+ var t = Math.min(1, Math.max(0, dot / len));
+
+ dot = (bx - ax) * (py - ay) - (by - ay) * (px - ax);
+
+ return {
+ point: {
+ x: ax + atob.x * t,
+ y: ay + atob.y * t
+ },
+ left: dot < 1,
+ dot: dot,
+ t: t
+ };
+ }
+
+ export function closestPtBetweenRectangles(l: number, t: number, w: number, h: number,
+ l1: number, t1: number, w1: number, h1: number,
+ x: number, y: number) {
+ var r = l + w,
+ b = t + h;
+ var r1 = l1 + w1,
+ b1 = t1 + h1;
+ let hsegs = [[l, r, t, l1, r1, t1], [l, r, b, l1, r1, t1], [l, r, t, l1, r1, b1], [l, r, b, l1, r1, b1]];
+ let vsegs = [[l, t, b, l1, t1, b1], [r, t, b, l1, t1, b1], [l, t, b, r1, t1, b1], [r, t, b, r1, t1, b1]];
+ let res = hsegs.reduce((closest, seg) => {
+ let res = distanceBetweenHorizontalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
+ return (res[0] < closest[0]) ? res : closest;
+ }, [Number.MAX_VALUE, []] as [number, number[]]);
+ let fres = vsegs.reduce((closest, seg) => {
+ let res = distanceBetweenVerticalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
+ return (res[0] < closest[0]) ? res : closest;
+ }, res);
+
+ let near = project(x, y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
+ return project(near.point.x, near.point.y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
+ }
+
export function getNearestPointInPerimeter(l: number, t: number, w: number, h: number, x: number, y: number) {
var r = l + w,
b = t + h;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index e00ba91a4..a27f106e3 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -35,7 +35,6 @@ export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocu
@computed get PanelHeight() { return this.props.PanelHeight(); }
render() {
- // let pathData = this.parseData(this.props.line);
let data: InkData = Cast(this.Document.data, InkField)?.inkData ?? [];
let xs = data.map(p => p.x);
let ys = data.map(p => p.y);
@@ -48,7 +47,6 @@ export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocu
let height = bottom - top;
let scaleX = this.PanelWidth / width;
let scaleY = this.PanelHeight / height;
- // let pathlength = this.props.count; // bcz: this is needed to force reactions to the line's data changes
return (
<svg width={width} height={height} style={{
transformOrigin: "top left",
diff --git a/src/client/views/PreviewCursor.scss b/src/client/views/PreviewCursor.scss
index 20f9b9a49..d384fd284 100644
--- a/src/client/views/PreviewCursor.scss
+++ b/src/client/views/PreviewCursor.scss
@@ -6,4 +6,5 @@
top: 0;
left:0;
pointer-events: none;
+ opacity: 1;
} \ No newline at end of file
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index eed2cc5da..136a272ab 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -1,4 +1,4 @@
-import { action, observable, runInAction } from 'mobx';
+import { action, observable, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
@@ -7,21 +7,16 @@ import { Docs } from '../documents/Documents';
// import { Transform } from 'prosemirror-transform';
import { Doc } from '../../new_fields/Doc';
import { Transform } from "../util/Transform";
+import { TraceMobx } from '../../new_fields/util';
@observer
export class PreviewCursor extends React.Component<{}> {
- private _prompt = React.createRef<HTMLDivElement>();
static _onKeyPress?: (e: KeyboardEvent) => void;
static _getTransform: () => Transform;
static _addLiveTextDoc: (doc: Doc) => void;
static _addDocument: (doc: Doc) => boolean;
@observable static _clickPoint = [0, 0];
@observable public static Visible = false;
- //when focus is lost, this will remove the preview cursor
- @action onBlur = (): void => {
- PreviewCursor.Visible = false;
- }
-
constructor(props: any) {
super(props);
document.addEventListener("keydown", this.onKeyPress);
@@ -108,6 +103,12 @@ export class PreviewCursor extends React.Component<{}> {
}
}
}
+
+ //when focus is lost, this will remove the preview cursor
+ @action onBlur = (): void => {
+ PreviewCursor.Visible = false;
+ }
+
@action
public static Show(x: number, y: number,
onKeyPress: (e: KeyboardEvent) => void,
@@ -119,18 +120,13 @@ export class PreviewCursor extends React.Component<{}> {
this._addLiveTextDoc = addLiveText;
this._getTransform = getTransform;
this._addDocument = addDocument;
- setTimeout(action(() => this.Visible = true), (1));
+ this.Visible = true;
}
render() {
- if (!PreviewCursor._clickPoint) {
- return (null);
- }
- if (PreviewCursor.Visible && this._prompt.current) {
- this._prompt.current.focus();
- }
- return <div className="previewCursor" id="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={this._prompt}
- style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)`, opacity: PreviewCursor.Visible ? 1 : 0 }}>
- I
+ return (!PreviewCursor._clickPoint || !PreviewCursor.Visible) ? (null) :
+ <div className="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={e => e && e.focus()}
+ style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)` }}>
+ I
</div >;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index ba83630a4..4eb9e9d1e 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -14,6 +14,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEdit } from "@fortawesome/free-solid-svg-icons";
import { library } from "@fortawesome/fontawesome-svg-core";
import { MetadataEntryMenu } from "../MetadataEntryMenu";
+import { DocumentView } from "../nodes/DocumentView";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 837413842..73b45edc6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -6,7 +6,8 @@ import "./CollectionFreeFormLinkView.scss";
import React = require("react");
import v5 = require("uuid/v5");
import { DocumentType } from "../../../documents/DocumentTypes";
-import { observable, action } from "mobx";
+import { observable, action, reaction, IReactionDisposer } from "mobx";
+import { StrCast, Cast } from "../../../../new_fields/Types";
export interface CollectionFreeFormLinkViewProps {
A: DocumentView;
@@ -16,33 +17,57 @@ export interface CollectionFreeFormLinkViewProps {
@observer
export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
- @observable _alive: number = 0;
@observable _opacity: number = 1;
+ @observable _update: number = 0;
+ _anchorDisposer: IReactionDisposer | undefined;
@action
componentDidMount() {
- this._alive = 1;
- setTimeout(this.rerender, 50);
- setTimeout(action(() => this._opacity = 0.05), 50);
+ setTimeout(action(() => this._opacity = 0.05), 750);
+ this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform()],
+ () => {
+ let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+ let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+ let adiv = (acont.length ? acont[0] : this.props.A.ContentDiv!);
+ let bdiv = (bcont.length ? bcont[0] : this.props.B.ContentDiv!);
+ let a = adiv.getBoundingClientRect();
+ let b = bdiv.getBoundingClientRect();
+ let abounds = adiv.parentElement!.getBoundingClientRect();
+ let bbounds = bdiv.parentElement!.getBoundingClientRect();
+ let apt = Utils.closestPtBetweenRectangles(abounds.left, abounds.top, abounds.width, abounds.height,
+ bbounds.left, bbounds.top, bbounds.width, bbounds.height,
+ a.left + a.width / 2, a.top + a.height / 2);
+ let bpt = Utils.closestPtBetweenRectangles(bbounds.left, bbounds.top, bbounds.width, bbounds.height,
+ abounds.left, abounds.top, abounds.width, abounds.height,
+ apt.point.x, apt.point.y);
+ let afield = StrCast(this.props.A.props.Document[StrCast(this.props.A.props.layoutKey, "layout")]).indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
+ let bfield = afield === "anchor1" ? "anchor2" : "anchor1";
+ this.props.A.props.Document[afield + "_x"] = (apt.point.x - abounds.left) / abounds.width * 100;
+ this.props.A.props.Document[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100;
+ this.props.A.props.Document[bfield + "_x"] = (bpt.point.x - bbounds.left) / bbounds.width * 100;
+ this.props.A.props.Document[bfield + "_y"] = (bpt.point.y - bbounds.top) / bbounds.height * 100;
+ this._update++;
+ }
+ , { fireImmediately: true });
}
@action
componentWillUnmount() {
- this._alive = 0;
+ this._anchorDisposer?.();
}
- rerender = action(() => {
- if (this._alive) {
- setTimeout(this.rerender, 50);
- this._alive++;
- }
- });
render() {
- let y = this._alive;
+ let y = this._update;
let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
let a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
let b = (bcont.length ? bcont[0] : this.props.B.ContentDiv!).getBoundingClientRect();
- let pt1 = Utils.getNearestPointInPerimeter(a.left, a.top, a.width, a.height, b.left + b.width / 2, b.top + b.height / 2);
- let pt2 = Utils.getNearestPointInPerimeter(b.left, b.top, b.width, b.height, a.left + a.width / 2, a.top + a.height / 2);
+ let apt = Utils.closestPtBetweenRectangles(a.left, a.top, a.width, a.height,
+ b.left, b.top, b.width, b.height,
+ a.left + a.width / 2, a.top + a.height / 2);
+ let bpt = Utils.closestPtBetweenRectangles(b.left, b.top, b.width, b.height,
+ a.left, a.top, a.width, a.height,
+ apt.point.x, apt.point.y);
+ let pt1 = [apt.point.x, apt.point.y];
+ let pt2 = [bpt.point.x, bpt.point.y];
return (<line key="linkLine" className="collectionfreeformlinkview-linkLine"
style={{ opacity: this._opacity }}
x1={`${pt1[0]}`} y1={`${pt1[1]}`}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 03f860b32..9506ce084 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -40,6 +40,7 @@ import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import { computedFn, keepAlive } from "mobx-utils";
+import { TraceMobx } from "../../../../new_fields/util";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
@@ -512,9 +513,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let [x, y] = this.getTransform().transformPoint(pointX, pointY);
let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
- let safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
- this.props.Document.scale = Math.abs(safeScale);
- this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
+ if (localTransform.Scale >= 0.15) {
+ let safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
+ this.props.Document.scale = Math.abs(safeScale);
+ this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
+ }
}
@action
@@ -643,12 +646,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, z?: number, width?: number, height?: number, transition?: string, state?: any } {
- const script = this.Document.arrangeScript;
- const result = script && script.script.run(params, console.log);
- const layoutDoc = Doc.Layout(params.doc);
- if (result && result.success) {
+ const result = this.Document.arrangeScript?.script.run(params, console.log);
+ if (result?.success) {
return { ...result, transition: "transform 1s" };
}
+ const layoutDoc = Doc.Layout(params.doc);
return { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(layoutDoc.width, "number"), height: Cast(layoutDoc.height, "number") };
}
@@ -675,7 +677,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
}
- childDataProvider = computedFn((doc: Doc) => this._layoutPoolData.get(doc[Id]));
+ childDataProvider = computedFn(function childDataProvider(doc: Doc) { return (this as any)._layoutPoolData.get(doc[Id]); }.bind(this));
doPivotLayout(poolData: ObservableMap<string, any>) {
return computePivotLayout(poolData, this.props.Document, this.childDocs,
@@ -689,9 +691,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => {
+ const data = poolData.get(pair.layout[Id]);
const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: layoutDocs, state });
state = pos.state === undefined ? state : pos.state;
- let data = this._layoutPoolData.get(pair.layout[Id]);
if (!data || pos.x !== data.x || pos.y !== data.y || pos.z !== data.z || pos.width !== data.width || pos.height !== data.height || pos.transition !== data.transition) {
runInAction(() => poolData.set(pair.layout[Id], pos));
}
@@ -717,9 +719,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
componentDidMount() {
- this._layoutComputeReaction = reaction(() => { trace(); return this.doLayoutComputation },
+ this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation; },
action((computation: { elements: ViewDefResult[] }) => computation && (this._layoutElements = computation.elements)),
- { fireImmediately: true });
+ { fireImmediately: true, name: "doLayout" });
}
componentWillUnmount() {
this._layoutComputeReaction && this._layoutComputeReaction();
@@ -867,7 +869,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return eles;
}
render() {
- trace();
+ TraceMobx();
// update the actual dimensions of the collection so that they can inquired (e.g., by a minimap)
// this.Document.fitX = this.contentBounds && this.contentBounds.x;
// this.Document.fitY = this.contentBounds && this.contentBounds.y;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 0badbd3fe..c85b59488 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -10,6 +10,7 @@ import "./CollectionFreeFormDocumentView.scss";
import { DocumentView, DocumentViewProps } from "./DocumentView";
import React = require("react");
import { PositionDocument } from "../../../new_fields/documentSchemas";
+import { TraceMobx } from "../../../new_fields/util";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
dataProvider?: (doc: Doc) => { x: number, y: number, width: number, height: number, z: number, transition?: string } | undefined;
@@ -56,11 +57,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
this._disposer && this._disposer();
}
componentDidMount() {
- this._disposer = reaction(() => [this.props.Document.animateToPos, this.props.Document.isAnimating],
- () => {
- const target = this.props.Document.animateToPos ? Array.from(Cast(this.props.Document.animateToPos, listSpec("number"))!) : undefined;
- this._animPos = !target ? undefined : target[2] ? [NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y)] : this.props.ScreenToLocalTransform().transformPoint(target[0], target[1]);
- }, { fireImmediately: true });
+ this._disposer = reaction(() => this.props.Document.animateToPos ? Array.from(Cast(this.props.Document.animateToPos, listSpec("number"))!) : undefined,
+ target => this._animPos = !target ? undefined : target[2] ? [NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y)] : this.props.ScreenToLocalTransform().transformPoint(target[0], target[1]),
+ { fireImmediately: true });
}
contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect ? this.width / this.nativeWidth : 1;
@@ -88,7 +87,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
finalPanelHeight = () => this.dataProvider ? this.dataProvider.height : this.panelHeight();
render() {
- trace();
+ TraceMobx();
return <div className="collectionFreeFormDocumentView-container"
style={{
boxShadow:
@@ -99,7 +98,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
StrCast(this.layoutDoc.boxShadow, ""),
borderRadius: this.borderRounding(),
transform: this.transform,
- transition: this.Document.isAnimating !== undefined ? ".5s ease-in" : this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition),
+ transition: this.Document.isAnimating ? ".5s ease-in" : this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition),
width: this.width,
height: this.height,
zIndex: this.Document.zIndex || 0,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f8c592f07..39a68f51e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -43,6 +43,7 @@ import React = require("react");
import { InteractionUtils } from '../../util/InteractionUtils';
import { InkingControl } from '../InkingControl';
import { InkTool } from '../../../new_fields/InkField';
+import { TraceMobx } from '../../../new_fields/util';
library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -546,7 +547,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get finalLayoutKey() { return this.props.layoutKey || "layout"; }
childScaling = () => (this.layoutDoc.fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
@computed get contents() {
- trace();
+ TraceMobx();
return (<DocumentContentsView ContainingCollectionView={this.props.ContainingCollectionView}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
Document={this.props.Document}
@@ -590,7 +591,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@computed get innards() {
- trace();
+ TraceMobx();
const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle");
const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption");
@@ -620,9 +621,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
/>
</div>);
return <>
- {this.Document.links && DocListCast(this.Document.links).filter((d) => !DocListCast(this.layoutDoc.hiddenLinks).some(hidden => Doc.AreProtosEqual(hidden, d))).filter(this.isNonTemporalLink).map((d, i) =>
- <div className="documentView-docuLinkWrapper" key={`${d[Id]}`} style={{ transform: `scale(${this.layoutDoc.fitWidth ? 1 : 1 / 1})` }}>
- <DocumentView {...this.props} ContentScaling={returnOne} Document={d} layoutKey={this.linkEndpoint(d)} backgroundColor={returnTransparent} removeDocument={undoBatch(doc => Doc.AddDocToList(this.layoutDoc, "hiddenLinks", doc))} />
+ {this.Document.links && DocListCast(this.Document.links).filter(d => !d.hidden).filter(this.isNonTemporalLink).map((d, i) =>
+ <div className="documentView-docuLinkWrapper" key={`${d[Id]}`}>
+ <DocumentView {...this.props} ContentScaling={returnOne} Document={d} layoutKey={this.linkEndpoint(d)} backgroundColor={returnTransparent} removeDocument={undoBatch(doc => doc.hidden = true)} />
</div>)}
{!showTitle && !showCaption ?
this.Document.searchFields ?
@@ -645,7 +646,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
</>;
}
@computed get ignorePointerEvents() {
- return this.Document.isBackground && !this.isSelected();
+ return (this.Document.isBackground && !this.isSelected()) || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
}
@action
@@ -686,9 +687,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear;
return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont}
onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
- onPointerEnter={e => Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)}
+ onPointerEnter={e => {
+ console.log("Brush" + this.props.Document.title);
+ Doc.BrushDoc(this.props.Document);
+ }} onPointerLeave={e => {
+ console.log("UnBrush" + this.props.Document.title);
+ Doc.UnBrushDoc(this.props.Document);
+
+ }}
style={{
- transition: this.Document.isAnimating !== undefined ? ".5s linear" : StrCast(this.Document.transition),
+ transition: this.Document.isAnimating ? ".5s linear" : StrCast(this.Document.transition),
pointerEvents: this.ignorePointerEvents ? "none" : "all",
color: StrCast(this.Document.color),
outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 9a5de836f..960b55e3e 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -43,7 +43,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
background: StrCast(referenceLayout.backgroundColor),
boxShadow: this.props.Document.ischecked ? `4px 4px 12px black` : undefined
}}>
- <FontAwesomeIcon className="fontIconBox-icon" icon={this.Document.icon as any} color={this._foregroundColor} size="md" />
+ <FontAwesomeIcon className="fontIconBox-icon" icon={this.Document.icon as any} color={this._foregroundColor} size="sm" />
</button>;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 269a3ca68..77cdd3d42 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -27,6 +27,8 @@
pointer-events: all;
overflow-y: auto;
max-height: 100%;
+ display: flex;
+ flex-direction: row;
.formattedTextBox-dictation {
height: 20px;
@@ -48,10 +50,28 @@
width: 100%;
height: 100%;
}
-.formattedTextBox-sidebar,.formattedTextBox-sidebar-inking {
- border-left: solid 1px black;
+.formattedTextBox-sidebar-handle {
+ position: absolute;
+ top: calc(50% - 17.5px);
+ width: 10px;
+ height: 35px;
+ background: lightgray;
+ border-radius: 20px;
+}
+.formattedTextBox-cont > .formattedTextBox-sidebar-handle {
+ right: 0;
+ left: unset;
+}
+.formattedTextBox-sidebar, .formattedTextBox-sidebar-inking {
+ border-left: dashed 1px black;
height: 100%;
display: inline-block;
+ position: absolute;
+ right: 0;
+ > .formattedTextBox-sidebar-handle {
+ right:unset;
+ left:-5;
+ }
}
.formattedTextBox-sidebar-inking {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index f30422b66..d601e188d 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -47,6 +47,7 @@ import { documentSchema } from '../../../new_fields/documentSchemas';
import { AudioBox } from './AudioBox';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { InkTool } from '../../../new_fields/InkField';
+import { TraceMobx } from '../../../new_fields/util';
library.add(faEdit);
library.add(faSmile, faTextHeight, faUpload);
@@ -358,9 +359,11 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
}
+ toggleSidebar = () => this.props.Document.sidebarWidthPercent = StrCast(this.props.Document.sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%";
+
specificContextMenu = (e: React.MouseEvent): void => {
let funcs: ContextMenuProps[] = [];
- funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.props.Document.sidebarWidthPercent = StrCast(this.props.Document.sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%"; }, icon: "expand-arrows-alt" });
+ funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.toggleSidebar(); }, icon: "expand-arrows-alt" });
funcs.push({ description: "Record Bullet", event: () => { e.stopPropagation(); this.recordBullet(); }, icon: "expand-arrows-alt" });
["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
funcs.push({
@@ -997,13 +1000,16 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
if (!this._undoTyping) {
this._undoTyping = UndoManager.StartBatch("undoTyping");
}
- if (this._recording) { this.stopDictation(true); setTimeout(() => this.recordDictation(), 250); }
+ if (this._recording) {
+ this.stopDictation(true);
+ setTimeout(() => this.recordDictation(), 250);
+ }
}
@action
tryUpdateHeight() {
- let scrollHeight = this._ref.current ? this._ref.current.scrollHeight : 0;
- if (!this.layoutDoc.isAnimating && this.layoutDoc.autoHeight && scrollHeight !== 0 &&
+ const scrollHeight = this._ref.current?.scrollHeight;
+ if (!this.layoutDoc.animateToPos && this.layoutDoc.autoHeight && scrollHeight &&
getComputedStyle(this._ref.current!.parentElement!).top === "0px") { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
let nh = this.Document.isTemplateField ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
let dh = NumCast(this.layoutDoc.height, 0);
@@ -1016,7 +1022,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
@computed get sidebarWidth() { return Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth(); }
@computed get annotationsKey() { return "annotations"; }
render() {
- trace();
+ TraceMobx();
let rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
let interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground;
if (this.props.isSelected()) {
@@ -1051,29 +1057,33 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
<div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }}>
<div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: ((this.Document.isButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} />
</div>
- {this.sidebarWidthPercent === "0%" ? (null) : <div className={"formattedTextBox-sidebar" + (InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : "")} style={{ width: `${this.sidebarWidthPercent}` }}>
- <CollectionFreeFormView {...this.props}
- PanelHeight={() => this.props.PanelHeight()}
- PanelWidth={() => this.sidebarWidth}
- annotationsKey={this.annotationsKey}
- isAnnotationOverlay={true}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
- select={emptyFunction}
- active={this.annotationsActive}
- ContentScaling={returnOne}
- whenActiveChanged={this.whenActiveChanged}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocument}
- CollectionView={undefined}
- ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth), 0)}
- ruleProvider={undefined}
- renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- chromeCollapsed={true}>
- </CollectionFreeFormView>
- </div>}
+ {this.sidebarWidthPercent === "0%" ?
+ <div className="formattedTextBox-sidebar-handle" onPointerDown={e => e.stopPropagation()} onClick={e => this.toggleSidebar()} /> :
+ <div className={"formattedTextBox-sidebar" + (InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : "")}
+ style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${StrCast(this.extensionDoc?.backgroundColor, "transparent")}` }}>
+ <CollectionFreeFormView {...this.props}
+ PanelHeight={() => this.props.PanelHeight()}
+ PanelWidth={() => this.sidebarWidth}
+ annotationsKey={this.annotationsKey}
+ isAnnotationOverlay={true}
+ focus={this.props.focus}
+ isSelected={this.props.isSelected}
+ select={emptyFunction}
+ active={this.annotationsActive}
+ ContentScaling={returnOne}
+ whenActiveChanged={this.whenActiveChanged}
+ removeDocument={this.removeDocument}
+ moveDocument={this.moveDocument}
+ addDocument={this.addDocument}
+ CollectionView={undefined}
+ ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth), 0)}
+ ruleProvider={undefined}
+ renderDepth={this.props.renderDepth + 1}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ chromeCollapsed={true}>
+ </CollectionFreeFormView>
+ <div className="formattedTextBox-sidebar-handle" onPointerDown={e => e.stopPropagation()} onClick={e => this.toggleSidebar()} />
+ </div>}
<div className="formattedTextBox-dictation"
onClick={e => {
this._recording ? this.stopDictation(true) : this.recordDictation();
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index dcecbdc6e..ba4ef8879 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -16,6 +16,7 @@
width:100%;
height:100%;
position: absolute;
+ transform-origin: top left;
}
.imageBox-cont-interactive {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 5d40e274f..f21ce3bf2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -27,6 +27,7 @@ import React = require("react");
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { documentSchema } from '../../../new_fields/documentSchemas';
import { Id } from '../../../new_fields/FieldSymbols';
+import { TraceMobx } from '../../../new_fields/util';
var requestImageSize = require('../../util/request-image-size');
var path = require('path');
const { Howl } = require('howler');
@@ -268,7 +269,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
}
@computed get content() {
- trace();
+ TraceMobx();
const extensionDoc = this.extensionDoc;
if (!extensionDoc) return (null);
// let transform = this.props.ScreenToLocalTransform().inverse();
@@ -291,7 +292,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
if (field instanceof ImageField) paths = [this.choosePath(field.url)];
paths.push(...altpaths);
// }
- let interactive = InkingControl.Instance.selectedTool || this.Document.isBackground ? "" : "-interactive";
+ let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
let rotation = NumCast(this.Document.rotation, 0);
let aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1;
let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
@@ -334,7 +335,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
contentFunc = () => [this.content];
render() {
return (<div className={"imageBox-container"} onContextMenu={this.specificContextMenu}
- style={{ transformOrigin: "top left", transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
+ style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
<CollectionFreeFormView {...this.props}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 5829c1bd9..0a4c650a8 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,5 +1,6 @@
.videoBox-container {
pointer-events: all;
+ transform-origin: top left;
.inkingCanvas-paths-markers {
opacity : 0.4; // we shouldn't have to do this, but since chrome crawls to a halt with z-index unset in videoBox-content, this is a workaround
}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index d70f0474c..bd5bd918f 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -336,7 +336,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
render() {
return (<div className={"videoBox-container"} onContextMenu={this.specificContextMenu}
- style={{ transformOrigin: "top left", transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
+ style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
<CollectionFreeFormView {...this.props}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 22c3a29a8..f1c500391 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -29,6 +29,7 @@ import { documentSchema } from "../../../new_fields/documentSchemas";
import { DocumentDecorations } from "../DocumentDecorations";
import { InkingControl } from "../InkingControl";
import { InkTool } from "../../../new_fields/InkField";
+import { TraceMobx } from "../../../new_fields/util";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
const pdfjsLib = require("pdfjs-dist");
@@ -620,7 +621,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
}
@computed get annotationLayer() {
- trace();
+ TraceMobx();
return <div className="pdfViewer-annotationLayer" style={{ height: (this.Document.nativeHeight || 0), transform: `scale(${this._zoomed})` }} ref={this._annotationLayer}>
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) =>
<Annotation {...this.props} focus={this.props.focus} extensionDoc={this.extensionDoc!} anno={anno} key={`${anno[Id]}-annotation`} />)}
@@ -672,9 +673,9 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
marqueeing = () => this._marqueeing;
visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96;
contentZoom = () => this._zoomed;
- @computed get contentScaling() { return this.props.ContentScaling() }
+ @computed get contentScaling() { return this.props.ContentScaling(); }
render() {
- trace();
+ TraceMobx();
return !this.extensionDoc ? (null) :
<div className={"pdfViewer-viewer" + (this._zoomed !== 1 ? "-zoomed" : "")} ref={this._mainCont}
style={{
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 7592cdaa3..4c2f061a6 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -41,7 +41,7 @@ export const documentSchema = createSchema({
showTitle: "string", // whether an editable title banner is displayed at tht top of the document
isButton: "boolean", // whether document functions as a button (overiding native interactions of its content)
ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events)
- isAnimating: "boolean", // whether the document is in the midst of animating between two layouts (used by icons to de/iconify documents).
+ isAnimating: "string", // whether the document is in the midst of animating between two layouts (used by icons to de/iconify documents). value is undefined|"min"|"max"
animateToDimensions: listSpec("number"), // layout information about the target rectangle a document is animating towards
scrollToLinkID: "string", // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
strokeWidth: "number",
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 04194509c..4147be278 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -4,7 +4,7 @@ import { SerializationHelper } from "../client/util/SerializationHelper";
import { ProxyField } from "./Proxy";
import { RefField } from "./RefField";
import { ObjectField } from "./ObjectField";
-import { action } from "mobx";
+import { action, trace } from "mobx";
import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
import { DocServer } from "../client/DocServer";
@@ -12,6 +12,10 @@ function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
}
+export function TraceMobx() {
+ //trace();
+}
+
export interface GetterResult {
value: FieldResult;
shouldReturn?: boolean;