From ba3f889a61715b715a230d0d24894a5951fbbb3b Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 21 Nov 2019 13:14:39 -0500 Subject: fixed inking on top of existing ink. fixed zooming maximally out. --- src/client/views/InkingStroke.tsx | 18 +++++++----------- .../collectionFreeForm/CollectionFreeFormView.tsx | 8 +++++--- src/client/views/nodes/DocumentView.tsx | 4 +++- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src') 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 p.x); let ys = data.map(p => p.y); @@ -48,15 +47,12 @@ export class InkingStroke extends DocExtendableComponent - {points} - - ); + return + {points} + ; } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3dd655b96..dc98e662d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -488,9 +488,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 diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index cf5a94f01..a985f3069 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -41,6 +41,8 @@ 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'; 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, @@ -639,7 +641,7 @@ export class DocumentView extends DocComponent(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() { -- cgit v1.2.3-70-g09d2 From 17474b9b840c78df4dc2601e82cf63a85e0bcbf7 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 21 Nov 2019 13:57:54 -0500 Subject: added names to make mobx tracking easier for freeform reactions --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index dc98e662d..8325ffe99 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -653,7 +653,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]); }); doPivotLayout(poolData: ObservableMap) { return computePivotLayout(poolData, this.props.Document, this.childDocs, @@ -697,7 +697,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { componentDidMount() { this._layoutComputeReaction = reaction(() => { trace(); return this.doLayoutComputation }, action((computation: { elements: ViewDefResult[] }) => computation && (this._layoutElements = computation.elements)), - { fireImmediately: true }); + { fireImmediately: true, name: "doLayout" }); } componentWillUnmount() { this._layoutComputeReaction && this._layoutComputeReaction(); -- cgit v1.2.3-70-g09d2 From 7ef52a87d1731770c6e1a8cd1aef31cb384fff05 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 21 Nov 2019 16:27:56 -0500 Subject: made textbox sidebar toggle a widget. fixed isAnimating stuff. made TraceMobx to simplify tracing --- .../collectionFreeForm/CollectionFreeFormView.tsx | 16 +++--- .../views/nodes/CollectionFreeFormDocumentView.tsx | 13 ++--- src/client/views/nodes/DocumentView.tsx | 7 ++- src/client/views/nodes/FormattedTextBox.scss | 24 +++++++- src/client/views/nodes/FormattedTextBox.tsx | 66 +++++++++++++--------- src/client/views/nodes/ImageBox.tsx | 3 +- src/client/views/pdf/PDFViewer.tsx | 5 +- src/new_fields/documentSchemas.ts | 2 +- src/new_fields/util.ts | 6 +- 9 files changed, 89 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8325ffe99..b2344771d 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); @@ -621,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") }; } @@ -653,7 +653,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - childDataProvider = computedFn(function childDataProvider(doc: Doc) { return (this as any)._layoutPoolData.get(doc[Id]); }); + childDataProvider = computedFn(function childDataProvider(doc: Doc) { return (this as any)._layoutPoolData.get(doc[Id]); }.bind(this)); doPivotLayout(poolData: ObservableMap) { return computePivotLayout(poolData, this.props.Document, this.childDocs, @@ -667,9 +667,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)); } @@ -695,7 +695,7 @@ 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, name: "doLayout" }); } @@ -845,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; 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 [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 this.dataProvider ? this.dataProvider.height : this.panelHeight(); render() { - trace(); + TraceMobx(); return
(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 ((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"); @@ -669,7 +670,7 @@ export class DocumentView extends DocComponent(Docu 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)} 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/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 5201f9bdc..86a32eb22 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 &
- {this.sidebarWidthPercent === "0%" ? (null) :
- 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}> - -
} + {this.sidebarWidthPercent === "0%" ? +
e.stopPropagation()} onClick={e => this.toggleSidebar()} /> : +
+ 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}> + +
e.stopPropagation()} onClick={e => this.toggleSidebar()} /> +
}
{ this._recording ? this.stopDictation(true) : this.recordDictation(); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 5d40e274f..d102c9600 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 {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => )} @@ -674,7 +675,7 @@ export class PDFViewer extends DocAnnotatableComponent this._zoomed; @computed get contentScaling() { return this.props.ContentScaling() } render() { - trace(); + TraceMobx(); return !this.extensionDoc ? (null) :
Date: Fri, 22 Nov 2019 08:59:43 -0500 Subject: cleaned up previewcursor warnings --- src/client/views/PreviewCursor.scss | 1 + src/client/views/PreviewCursor.tsx | 30 +++++++++++++----------------- 2 files changed, 14 insertions(+), 17 deletions(-) (limited to 'src') 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(); 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
- I + return (!PreviewCursor._clickPoint || !PreviewCursor.Visible) ? (null) : +
e && e.focus()} + style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)` }}> + I
; } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 3b37cc31bb09b11238868c34a38a8e99f508479f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 22 Nov 2019 13:22:21 -0500 Subject: fixed link anchors to move to be nearest alternate anchor. --- src/Utils.ts | 57 ++++++++++++++++++++++ src/client/views/Touchable.tsx | 2 +- .../views/collections/ParentDocumentSelector.tsx | 1 + .../CollectionFreeFormLinkView.tsx | 55 +++++++++++++++------ .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 15 ++++-- src/client/views/nodes/FontIconBox.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/ImageBox.scss | 1 + src/client/views/nodes/ImageBox.tsx | 4 +- src/client/views/nodes/VideoBox.scss | 1 + src/client/views/nodes/VideoBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 2 +- 13 files changed, 119 insertions(+), 27 deletions(-) (limited to 'src') 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/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 extends React.Component { 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/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 { - @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 ( { TraceMobx(); return this.doLayoutComputation }, + this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation; }, action((computation: { elements: ViewDefResult[] }) => computation && (this._layoutElements = computation.elements)), { fireImmediately: true, name: "doLayout" }); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9ec339686..467fd4b32 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -617,9 +617,9 @@ export class DocumentView extends DocComponent(Docu />
); 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) => -
- Doc.AddDocToList(this.layoutDoc, "hiddenLinks", doc))} /> + {this.Document.links && DocListCast(this.Document.links).filter(d => !d.hidden).filter(this.isNonTemporalLink).map((d, i) => +
+ doc.hidden = true)} />
)} {!showTitle && !showCaption ? this.Document.searchFields ? @@ -668,7 +668,14 @@ export class DocumentView extends DocComponent(Docu let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear; return
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 ? ".5s linear" : StrCast(this.Document.transition), pointerEvents: this.ignorePointerEvents ? "none" : "all", 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( background: StrCast(referenceLayout.backgroundColor), boxShadow: this.props.Document.ischecked ? `4px 4px 12px black` : undefined }}> - + ; } } \ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 86a32eb22..9910c9ecd 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -363,7 +363,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & specificContextMenu = (e: React.MouseEvent): void => { let funcs: ContextMenuProps[] = []; - funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.toggleSidebar() }, 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({ 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 d102c9600..f21ce3bf2 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -292,7 +292,7 @@ export class ImageBox extends DocAnnotatableComponent [this.content]; render() { return (
+ style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} > [this.youtubeVideoId ? this.youtubeContent : this.content]; render() { return (
+ style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} > 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() { TraceMobx(); return !this.extensionDoc ? (null) : -- cgit v1.2.3-70-g09d2