diff options
Diffstat (limited to 'src')
25 files changed, 309 insertions, 173 deletions
diff --git a/src/Utils.ts b/src/Utils.ts index fc12c1e14..b60e9e023 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -159,6 +159,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/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 210fa40dc..66f47147f 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -463,7 +463,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> break; } - SelectionManager.SelectedDocuments().forEach(element => { + SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => { if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { let doc = PositionDocument(element.props.Document); let layoutDoc = PositionDocument(Doc.Layout(element.props.Document)); @@ -509,7 +509,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> dH && layoutDoc.autoHeight && (layoutDoc.autoHeight = false); } } - }); + })); } @action diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 74211cc90..723b8cac4 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,15 +47,12 @@ 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", - transform: `translate(${left}px, ${top}px) scale(${scaleX}, ${scaleY})`, - mixBlendMode: this.Document.tool === InkTool.Highlighter ? "multiply" : "unset" - }}> - {points} - </svg> - ); + return <svg width={width} height={height} style={{ + transformOrigin: "top left", + transform: `translate(${left}px, ${top}px) scale(${scaleX}, ${scaleY})`, + mixBlendMode: this.Document.tool === InkTool.Highlighter ? "multiply" : "unset" + }}> + {points} + </svg>; } }
\ No newline at end of file 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/Touchable.tsx b/src/client/views/Touchable.tsx index ba87025c4..0dd4f734c 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -45,7 +45,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> { this._touchDrag = true; switch (e.targetTouches.length) { case 1: - this.handle1PointerMove(e) + this.handle1PointerMove(e); break; case 2: this.handle2PointersMove(e); 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/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 48d330674..e1d23ddcb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -6,6 +6,8 @@ import { ScriptField } from "../../../../new_fields/ScriptField"; import { OverlayView, OverlayElementOptions } from "../../OverlayView"; import { emptyFunction } from "../../../../Utils"; import React = require("react"); +import { ObservableMap, runInAction } from "mobx"; +import { Id } from "../../../../new_fields/FieldSymbols"; interface PivotData { type: string; @@ -31,8 +33,7 @@ export interface ViewDefResult { bounds?: ViewDefBounds; } -export function computePivotLayout(pivotDoc: Doc, childDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], viewDefsToJSX: (views: any) => ViewDefResult[]) { - let layoutPoolData: Map<{ layout: Doc, data?: Doc }, any> = new Map(); +export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDoc: Doc, childDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], viewDefsToJSX: (views: any) => ViewDefResult[]) { const pivotAxisWidth = NumCast(pivotDoc.pivotWidth, 200); const pivotColumnGroups = new Map<FieldResult<Field>, Doc[]>(); @@ -49,6 +50,8 @@ export function computePivotLayout(pivotDoc: Doc, childDocs: Doc[], childPairs: const docMap = new Map<Doc, ViewDefBounds>(); const groupNames: PivotData[] = []; + const expander = 1.05; + const gap = .15; let x = 0; pivotColumnGroups.forEach((val, key) => { let y = 0; @@ -58,25 +61,31 @@ export function computePivotLayout(pivotDoc: Doc, childDocs: Doc[], childPairs: text: String(key), x, y: pivotAxisWidth + 50, - width: pivotAxisWidth * 1.25 * numCols, + width: pivotAxisWidth * expander * numCols, height: 100, fontSize: NumCast(pivotDoc.pivotFontSize, 10) }); for (const doc of val) { let layoutDoc = Doc.Layout(doc); + let wid = pivotAxisWidth; + let hgt = layoutDoc.nativeWidth ? (NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth)) * pivotAxisWidth : pivotAxisWidth; + if (hgt > pivotAxisWidth) { + hgt = pivotAxisWidth; + wid = layoutDoc.nativeHeight ? (NumCast(layoutDoc.nativeWidth) / NumCast(layoutDoc.nativeHeight)) * pivotAxisWidth : pivotAxisWidth; + } docMap.set(doc, { - x: x + xCount * pivotAxisWidth * 1.25, + x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2, y: -y, - width: pivotAxisWidth, - height: layoutDoc.nativeWidth ? (NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth)) * pivotAxisWidth : pivotAxisWidth + width: wid, + height: hgt }); xCount++; if (xCount >= numCols) { - xCount = 0; - y += pivotAxisWidth * 1.25; + xCount = (pivotAxisWidth - wid) / 2; + y += pivotAxisWidth * expander; } } - x += pivotAxisWidth * 1.25 * (numCols + 1); + x += pivotAxisWidth * (numCols * expander + gap); }); childPairs.map(pair => { @@ -88,9 +97,12 @@ export function computePivotLayout(pivotDoc: Doc, childDocs: Doc[], childPairs: height: NumCast(pair.layout.height) }; const pos = docMap.get(pair.layout) || defaultPosition; - layoutPoolData.set(pair, { transition: "transform 1s", ...pos }); + let data = poolData.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) { + runInAction(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...pos })); + } }); - return { map: layoutPoolData, elements: viewDefsToJSX(groupNames) }; + return { elements: viewDefsToJSX(groupNames) }; } export function AddCustomFreeFormLayout(doc: Doc, dataKey: string): () => void { 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.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index d2731703f..070d4aa65 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -1,5 +1,6 @@ @import "../../globalCssVariables"; +.collectionfreeformview-none, .collectionfreeformview-ease { position: inherit; top: 0; @@ -7,16 +8,14 @@ width: 100%; height: 100%; transform-origin: left top; + border-radius: inherit; +} + +.collectionfreeformview-ease { transition: transform 1s; } .collectionfreeformview-none { - position: inherit; - top: 0; - left: 0; - width: 100%; - height: 100%; - transform-origin: left top; touch-action: none; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8791a256c..9bbaa20e7 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); @@ -73,7 +74,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private _layoutPoolData = new ObservableMap<string, any>(); public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive - @observable _layoutElements: ViewDefResult[] = []; + @observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables @observable _clusterSets: (Doc[])[] = []; @computed get fitToContent() { return (this.props.fitToBox || this.Document.fitToBox) && !this.isAnnotationOverlay; } @@ -488,9 +489,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 @@ -619,12 +622,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") }; } @@ -651,25 +653,25 @@ 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)); - get doPivotLayout() { - return computePivotLayout(this.props.Document, this.childDocs, + doPivotLayout(poolData: ObservableMap<string, any>) { + return computePivotLayout(poolData, this.props.Document, this.childDocs, this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)), this.viewDefsToJSX); } - get doFreeformLayout() { + doFreeformLayout(poolData: ObservableMap<string, any>) { let layoutDocs = this.childLayoutPairs.map(pair => pair.layout); const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log); let state = initResult && initResult.success ? initResult.result.scriptState : undefined; 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(() => this._layoutPoolData.set(pair.layout[Id], pos)); + runInAction(() => poolData.set(pair.layout[Id], pos)); } }); return { elements: elements }; @@ -678,8 +680,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { get doLayoutComputation() { let computedElementData: { elements: ViewDefResult[] }; switch (this.Document.freeformLayoutEngine) { - case "pivot": computedElementData = this.doPivotLayout; break; - default: computedElementData = this.doFreeformLayout; break; + case "pivot": computedElementData = this.doPivotLayout(this._layoutPoolData); break; + default: computedElementData = this.doFreeformLayout(this._layoutPoolData); break; } this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).forEach(pair => computedElementData.elements.push({ @@ -693,14 +695,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } componentDidMount() { - this._layoutComputeReaction = reaction(() => 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(); } - @computed.struct get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } + @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } elementFunc = () => this._layoutElements; @action @@ -843,7 +845,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; @@ -873,9 +875,8 @@ interface CollectionFreeFormOverlayViewProps { @observer class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps>{ - @computed.struct get overlayViews() { return this.props.elements().filter(ele => ele.bounds && ele.bounds.z).map(ele => ele.ele); } render() { - return this.overlayViews; + return this.props.elements().filter(ele => ele.bounds && ele.bounds.z).map(ele => ele.ele); } } @@ -898,7 +899,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF const panx = -this.props.panX(); const pany = -this.props.panY(); const zoom = this.props.zoomScaling(); - return <div className={freeformclass} style={{ borderRadius: "inherit", transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)` }}> + return <div className={freeformclass} style={{ transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)` }}> {this.props.children()} </div>; } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index bcff91f4b..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 becb7bdfe..467fd4b32 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -41,6 +41,9 @@ import "./DocumentView.scss"; import { FormattedTextBox } from './FormattedTextBox'; 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, @@ -540,7 +543,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} @@ -584,7 +587,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"); @@ -614,9 +617,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 ? @@ -639,7 +642,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); } render() { @@ -665,9 +668,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 2f9e8d5f4..9910c9ecd 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()) { @@ -1050,29 +1056,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 86dc4fccc..a57059f77 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -26,6 +26,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'); @@ -267,7 +268,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(); @@ -290,7 +291,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; @@ -333,7 +334,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/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 5a5f784a1..46af63a7d 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -1,3 +1,11 @@ +.pdfBox-ui { + position: absolute; + width: 100%; + height:100%; + z-index: 1; + pointer-events: none; +} + .pdfBox-cont, .pdfBox-cont-interactive { display: inline-block; @@ -134,14 +142,13 @@ .pdfBox-overlayCont { position: absolute; - width: 100%; - height: 40px; + width: calc(100% - 40px); + height: 30px; background: #121721; bottom: 0; display: flex; justify-content: center; align-items: center; - padding: 20px; overflow: hidden; transition: left .5s; pointer-events: all; @@ -171,30 +178,24 @@ border-right: 15px solid #121721; transition: all 0.5s; } +} - .pdfBox-overlayButton-iconCont, - .pdfBox-nextIcon, - .pdfBox-prevIcon { - background: #121721; - height: 30px; - width: 70px; - display: flex; - justify-content: center; - align-items: center; - margin-left: -2px; - border-radius: 3px; - position: absolute; - pointer-events: all; - } +.pdfBox-overlayButton-iconCont, +.pdfBox-nextIcon, +.pdfBox-prevIcon { + padding: 0; + background: #121721; + height: 30px; + width: 25px; + display: inline-block; + position: relative; + justify-content: center; + align-items: center; + margin-left: -2px; + border-radius: 3px; + pointer-events: all; } .pdfBox-overlayButton:hover { background: none; } - -.pdfBox-nextIcon { - left: 20; top: 5; height: 30px; position: absolute; -} -.pdfBox-prevIcon { - left: 50; top: 5; height: 30px; position: absolute; -} diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index e7d8ac46c..23512543a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -21,7 +21,6 @@ import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); import { documentSchema } from '../../../new_fields/documentSchemas'; -import { SelectionManager } from '../../util/SelectionManager'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @@ -119,17 +118,17 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument> settingsPanel() { let pageBtns = <> <button className="pdfBox-overlayButton-iconCont" key="back" title="Page Back" - onPointerDown={e => e.stopPropagation()} onClick={this.backPage} style={{ left: 50, top: 5 }}> + onPointerDown={e => e.stopPropagation()} onClick={e => this.backPage()} style={{ left: 45, top: 5 }}> <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-left"} size="sm" /> </button> <button className="pdfBox-overlayButton-iconCont" key="fwd" title="Page Forward" - onPointerDown={e => e.stopPropagation()} onClick={this.forwardPage} style={{ left: 80, top: 5 }}> + onPointerDown={e => e.stopPropagation()} onClick={e => this.forwardPage()} style={{ left: 45, top: 5 }}> <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-right"} size="sm" /> </button> </>; return !this.active() ? (null) : (<div className="pdfBox-ui" onKeyDown={e => e.keyCode === KeyCodes.BACKSPACE || e.keyCode === KeyCodes.DELETE ? e.stopPropagation() : true} - onPointerDown={e => e.stopPropagation()} style={{ display: this.active() ? "flex" : "none", position: "absolute", width: "100%", height: "100%", zIndex: 1, pointerEvents: "none" }}> + onPointerDown={e => e.stopPropagation()} style={{ display: this.active() ? "flex" : "none" }}> <div className="pdfBox-overlayCont" key="cont" onPointerDown={(e) => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> <button className="pdfBox-overlayButton" title="Open Search Bar" /> <input className="pdfBox-searchBar" placeholder="Search" ref={this._searchRef} onChange={this.searchStringChanged} onKeyDown={e => e.keyCode === KeyCodes.ENTER && this.search(this._searchString, !e.shiftKey)} /> @@ -142,14 +141,14 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument> <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="sm" /> </button> </div> - <button className="pdfBox-overlayButton" key="search" onClick={action(() => this._searching = !this._searching)} title="Open Search Bar" style={{ bottom: 8, right: 0 }}> + <button className="pdfBox-overlayButton" key="search" onClick={action(() => this._searching = !this._searching)} title="Open Search Bar" style={{ bottom: 0, right: 0 }}> <div className="pdfBox-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div> <div className="pdfBox-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> <FontAwesomeIcon style={{ color: "white", padding: 5 }} icon={this._searching ? "times" : "search"} size="3x" /></div> </button> <input value={`${(this.Document.curPage || 1)}`} onChange={e => this.gotoPage(Number(e.currentTarget.value))} - style={{ left: 20, top: 5, height: "30px", width: "30px", position: "absolute", pointerEvents: "all" }} + style={{ left: 5, top: 5, height: "30px", width: "30px", position: "absolute", pointerEvents: "all" }} onClick={action(() => this._pageControls = !this._pageControls)} /> {this._pageControls ? pageBtns : (null)} <div className="pdfBox-settingsCont" key="settings" onPointerDown={(e) => e.stopPropagation()}> 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 1d1972841..0b0baeeb5 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -335,7 +335,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 de764346f..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,14 +673,15 @@ 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(); } render() { - // trace(); + TraceMobx(); return !this.extensionDoc ? (null) : <div className={"pdfViewer-viewer" + (this._zoomed !== 1 ? "-zoomed" : "")} ref={this._mainCont} style={{ - width: !this.props.Document.fitWidth ? NumCast(this.props.Document.nativeWidth) : `${100 / this.props.ContentScaling()}%`, - height: !this.props.Document.fitWidth ? NumCast(this.props.Document.nativeHeight) : `${100 / this.props.ContentScaling()}%`, - transform: `scale(${this.props.ContentScaling()})` + width: !this.props.Document.fitWidth ? NumCast(this.props.Document.nativeWidth) : `${100 / this.contentScaling}%`, + height: !this.props.Document.fitWidth ? NumCast(this.props.Document.nativeHeight) : `${100 / this.contentScaling}%`, + transform: `scale(${this.contentScaling})` }} onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick}> {this.pdfViewerDiv} {this.overlayLayer} 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; |