diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/ColorBox.tsx | 45 | ||||
| -rw-r--r-- | src/client/views/nodes/ComparisonBox.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentLinksButton.tsx | 24 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 312 | ||||
| -rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/ScriptingBox.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.scss | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.tsx | 59 | ||||
| -rw-r--r-- | src/client/views/nodes/formattedText/DashFieldView.tsx | 4 | ||||
| -rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 11 | ||||
| -rw-r--r-- | src/client/views/nodes/formattedText/RichTextRules.ts | 7 |
11 files changed, 170 insertions, 300 deletions
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index 57028b0ca..b186d9ffc 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -54,27 +54,30 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument } render() { const selDoc = SelectionManager.SelectedDocuments()?.[0]?.rootDoc; - return <div className={`colorBox-container${this.active() ? "-interactive" : ""}`} - onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} - style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} > + // return <div className={`colorBox-container${this.active() ? "-interactive" : ""}`} + // onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} + // style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} > - <SketchPicker onChange={ColorBox.switchColor} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']} - color={StrCast(ActiveInkPen()?.backgroundColor, - StrCast(selDoc?._backgroundColor, StrCast(selDoc?.backgroundColor, "black")))} /> - <div style={{ display: "grid", gridTemplateColumns: "20% 80%", paddingTop: "10px" }}> - <div> {ActiveInkWidth() ?? 2}</div> - <input type="range" defaultValue={ActiveInkWidth() ?? 2} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => { - SetActiveInkWidth(e.target.value); - SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(e.target.value)); - }} /> - <div> {ActiveInkBezierApprox() ?? 2}</div> - <input type="range" defaultValue={ActiveInkBezierApprox() ?? 2} min={0} max={300} onChange={(e: React.ChangeEvent<HTMLInputElement>) => { - SetActiveBezierApprox(e.target.value); - SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeBezier = e.target.value); - }} /> - <br /> - <br /> - </div> - </div>; + // <SketchPicker onChange={ColorBox.switchColor} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']} + // color={StrCast(ActiveInkPen()?.backgroundColor, + // StrCast(selDoc?._backgroundColor, StrCast(selDoc?.backgroundColor, "black")))} /> + + // <div style={{ display: "grid", gridTemplateColumns: "20% 80%", paddingTop: "10px" }}> + // <div> {ActiveInkWidth() ?? 2}</div> + // <input type="range" defaultValue={ActiveInkWidth() ?? 2} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => { + // SetActiveInkWidth(e.target.value); + // SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(e.target.value)); + // }} /> + // <div> {ActiveInkBezierApprox() ?? 2}</div> + // <input type="range" defaultValue={ActiveInkBezierApprox() ?? 2} min={0} max={300} onChange={(e: React.ChangeEvent<HTMLInputElement>) => { + // SetActiveBezierApprox(e.target.value); + // SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeBezier = e.target.value); + // }} /> + // <br /> + // <br /> + // </div> + // </div> + // ; + return <></>; } } diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index f140cc6e5..616cddfcf 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -24,7 +24,7 @@ const ComparisonDocument = makeInterface(comparisonSchema, documentSchema); @observer export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, ComparisonDocument>(ComparisonDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); } - protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; + protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined]; @observable _animating = ""; diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index c9d23ff3a..ddfa77967 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -1,19 +1,19 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Tooltip } from "@material-ui/core"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../fields/Doc"; -import { emptyFunction, setupMoveUpEvents, returnFalse } from "../../../Utils"; +import { Doc } from "../../../fields/Doc"; +import { TraceMobx } from "../../../fields/util"; +import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils"; +import { DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; -import { UndoManager, undoBatch } from "../../util/UndoManager"; +import { LinkManager } from "../../util/LinkManager"; +import { undoBatch, UndoManager } from "../../util/UndoManager"; import './DocumentLinksButton.scss'; import { DocumentView } from "./DocumentView"; -import React = require("react"); -import { DocUtils } from "../../documents/Documents"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { LinkDocPreview } from "./LinkDocPreview"; -import { TaskCompletionBox } from "./TaskCompletedBox"; import { LinkDescriptionPopup } from "./LinkDescriptionPopup"; -import { LinkManager } from "../../util/LinkManager"; -import { Tooltip } from "@material-ui/core"; +import { TaskCompletionBox } from "./TaskCompletedBox"; +import React = require("react"); const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -24,6 +24,7 @@ interface DocumentLinksButtonProps { AlwaysOn?: boolean; InMenu?: boolean; StartLink?: boolean; + links: Doc[]; } @observer export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> { @@ -158,7 +159,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp @computed get linkButton() { - const links = DocListCast(this.props.View.props.Document.links); + TraceMobx(); + const links = this.props.links; const menuTitle = this.props.StartLink ? "Drag or tap to start link" : "Tap to complete link"; const buttonTitle = "Tap to view links"; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1d89f3d24..c47edefd6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,31 +1,31 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym, AclPrivate, AclEdit, AclAdmin } from "../../../fields/Doc"; +import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Document } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { listSpec } from "../../../fields/Schema"; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../fields/Types"; -import { TraceMobx, GetEffectiveAcl, SharingPermissions } from '../../../fields/util'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; +import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; +import { MobileInterface } from '../../../mobile/MobileInterface'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; -import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, emptyPath } from "../../../Utils"; +import { emptyFunction, emptyPath, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { ClientRecommender } from '../../ClientRecommender'; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from "../../util/DocumentManager"; -import { SnappingManager } from '../../util/SnappingManager'; import { DragManager, dropActionType } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; +import { LinkManager } from '../../util/LinkManager'; import { Scripting } from '../../util/Scripting'; import { SearchUtil } from '../../util/SearchUtil'; import { SelectionManager } from "../../util/SelectionManager"; import SharingManager from '../../util/SharingManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from "../../util/Transform"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { CollectionView, CollectionViewType } from '../collections/CollectionView'; @@ -35,19 +35,13 @@ import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; import { KeyphraseQueryView } from '../KeyphraseQueryView'; import { DocumentContentsView } from "./DocumentContentsView"; +import { DocumentLinksButton } from './DocumentLinksButton'; import "./DocumentView.scss"; import { LinkAnchorBox } from './LinkAnchorBox'; +import { LinkDescriptionPopup } from './LinkDescriptionPopup'; import { RadialMenu } from './RadialMenu'; -import React = require("react"); -import { DocumentLinksButton } from './DocumentLinksButton'; -import { MobileInterface } from '../../../mobile/MobileInterface'; import { TaskCompletionBox } from './TaskCompletedBox'; -import { LinkDescriptionPopup } from './LinkDescriptionPopup'; -import { LinkManager } from '../../util/LinkManager'; - -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, - fa.faCopy, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard, fa.faQuestion); +import React = require("react"); export type DocFocusFunc = () => boolean; @@ -104,24 +98,25 @@ export interface DocumentViewProps { @observer export class DocumentView extends DocComponent<DocumentViewProps, Document>(Document) { + @observable _animateScalingTo = 0; private _downX: number = 0; private _downY: number = 0; + private _firstX: number = -1; + private _firstY: number = -1; private _lastTap: number = 0; private _doubleTap = false; private _mainCont = React.createRef<HTMLDivElement>(); private _dropDisposer?: DragManager.DragDropDisposer; private _showKPQuery: boolean = false; private _queries: string = ""; - private _gestureEventDisposer?: GestureUtils.GestureEventDisposer; private _titleRef = React.createRef<EditableView>(); + private _gestureEventDisposer?: GestureUtils.GestureEventDisposer; + private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer; + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - private holdDisposer?: InteractionUtils.MultiTouchEventDisposer; - - public get title() { return this.props.Document.title; } public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive public get ContentDiv() { return this._mainCont.current; } - get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); } + private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); } @computed get topMost() { return this.props.renderDepth === 0; } @computed get freezeDimensions() { return this.props.FreezeDimensions; } @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); } @@ -135,9 +130,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu onClickFunc = () => this.onClickHandler; onDoubleClickFunc = () => this.onDoubleClickHandler; - private _firstX: number = -1; - private _firstY: number = -1; - handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => { this.removeMoveListeners(); this.removeEndListeners(); @@ -151,11 +143,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu this._firstX = pt.pageX; this._firstY = pt.pageY; } - } handle1PointerHoldMove = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => { - const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1]; if (this._firstX === -1 || this._firstY === -1) { @@ -199,7 +189,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu componentDidMount() { this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document)); this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this))); - this._mainCont.current && (this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this))); + this._mainCont.current && (this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this))); // this._mainCont.current && (this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this))); if (!this.props.dontRegisterView) { @@ -211,13 +201,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu componentDidUpdate() { this._dropDisposer?.(); this._gestureEventDisposer?.(); - this.multiTouchDisposer?.(); - this.holdDisposer?.(); + this._multiTouchDisposer?.(); + this._holdDisposer?.(); if (this._mainCont.current) { this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document); this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this)); - this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)); - this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)); + this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)); + this._holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)); } } @@ -225,8 +215,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu componentWillUnmount() { this._dropDisposer?.(); this._gestureEventDisposer?.(); - this.multiTouchDisposer?.(); - this.holdDisposer?.(); + this._multiTouchDisposer?.(); + this._holdDisposer?.(); Doc.UnBrushDoc(this.props.Document); if (!this.props.dontRegisterView) { const index = DocumentManager.Instance.DocumentViews.indexOf(this); @@ -241,7 +231,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); dragData.dropAction = dropAction; dragData.removeDocument = this.props.removeDocument; - dragData.moveDocument = this.props.moveDocument;// this.layoutDoc.onDragStart ? undefined : this.props.moveDocument; + dragData.moveDocument = this.props.moveDocument; dragData.dragDivName = this.props.dragDivName; dragData.treeViewDoc = this.props.treeViewDoc; DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.layoutDoc.onDragStart }); @@ -253,7 +243,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu const de = new DragManager.DocumentDragData([topDoc]); de.dragDivName = topDocView.props.dragDivName; de.moveDocument = topDocView.props.moveDocument; - undoBatch(action(() => topDoc.z = topDoc.z ? 0 : 1))(); setTimeout(() => { const newDocView = DocumentManager.Instance.getDocumentView(topDoc); if (newDocView) { @@ -262,6 +251,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu DragManager.StartDocumentDrag([contentDiv], de, x, y, { offsetX: x - xf.left, offsetY: y - xf.top, hideSource: true }); } }, 0); + UndoManager.RunInBatch(action(() => topDoc.z = topDoc.z ? 0 : 1), "float"); } onKeyDown = (e: React.KeyboardEvent) => { @@ -300,41 +290,39 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu const func = () => this.onDoubleClickHandler.script.run({ this: this.layoutDoc, self: this.rootDoc, - thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey + thisContainer: this.props.ContainingCollectionDoc, + shiftKey: e.shiftKey }, console.log); func(); } else { UndoManager.RunInBatch(() => { + let fullScreenDoc = this.props.Document; if (StrCast(this.props.Document.layoutKey) !== "layout_fullScreen" && this.props.Document.layout_fullScreen) { - const fullScreenAlias = Doc.MakeAlias(this.props.Document); - fullScreenAlias.layoutKey = "layout_fullScreen"; - this.props.addDocTab(fullScreenAlias, "inTab"); - } else { - this.props.addDocTab(this.props.Document, "inTab"); + fullScreenDoc = Doc.MakeAlias(this.props.Document); + fullScreenDoc.layoutKey = "layout_fullScreen"; } + this.props.addDocTab(fullScreenDoc, "inTab"); }, "double tap"); SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); } } } else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself - //SelectionManager.DeselectAll(); const func = () => this.onClickHandler.script.run({ this: this.layoutDoc, self: this.rootDoc, - thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey + thisContainer: this.props.ContainingCollectionDoc, + shiftKey: e.shiftKey }, console.log); if (this.props.Document !== Doc.UserDoc()["dockedBtn-undo"] && this.props.Document !== Doc.UserDoc()["dockedBtn-redo"]) { UndoManager.RunInBatch(func, "on click"); } else func(); } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself - const alias = Doc.MakeAlias(this.props.Document); - DocUtils.makeCustomViewClicked(alias, undefined, "onClick"); - this.props.addDocTab(alias, "onRight"); - } else if (this.props.Document.links && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { - DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey); + this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "onRight"); + } else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { + this.allLinks.length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey); } else { - if ((this.layoutDoc.onDragStart || (this.props.Document.rootDocument)) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part + if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template } else { SelectionManager.SelectDoc(this, e.ctrlKey || e.shiftKey); @@ -406,7 +394,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); - } } @@ -444,12 +431,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu const oldPoint2 = this.prevPoints.get(pt2.identifier); const pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!); if (pinching !== 0 && oldPoint1 && oldPoint2) { - // let dX = (Math.min(pt1.clientX, pt2.clientX) - Math.min(oldPoint1.clientX, oldPoint2.clientX)); - // let dY = (Math.min(pt1.clientY, pt2.clientY) - Math.min(oldPoint1.clientY, oldPoint2.clientY)); - // let dX = Math.sign(Math.abs(pt1.clientX - oldPoint1.clientX) - Math.abs(pt2.clientX - oldPoint2.clientX)); - // let dY = Math.sign(Math.abs(pt1.clientY - oldPoint1.clientY) - Math.abs(pt2.clientY - oldPoint2.clientY)); - // let dW = -dX; - // let dH = -dY; const dW = (Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX)); const dH = (Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY)); const dX = -1 * Math.sign(dW); @@ -532,7 +513,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } onPointerMove = (e: PointerEvent): void => { - if ((e as any).formattedHandled) { e.stopPropagation(); return; } if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) return; if (e.cancelBubble && this.active) { @@ -553,17 +533,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu onPointerUp = (e: PointerEvent): void => { this.cleanUpInteractions(); + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log); - document.removeEventListener("pointerup", this.onPointerUp); - return; + } else { + this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2); + this._lastTap = Date.now(); } - - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2); - this._lastTap = Date.now(); } onGesture = (e: Event, ge: GestureUtils.GestureEvent) => { @@ -585,42 +563,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } } - - @undoBatch - toggleLinkButtonBehavior = (): void => { - this.Document.ignoreClick = false; - if (this.Document.isLinkButton || this.onClickHandler || this.Document.ignoreClick) { - this.Document.isLinkButton = false; - this.Document.onClick = this.layoutDoc.onClick = undefined; - } else { - this.Document.isLinkButton = true; - this.Document.followLinkZoom = false; - this.Document.followLinkLocation = undefined; - } - } - - @undoBatch - toggleFollowInPlace = (): void => { + @undoBatch @action + toggleFollowLink = (location: Opt<string>, zoom: boolean, setPushpin: boolean): void => { this.Document.ignoreClick = false; this.Document.isLinkButton = !this.Document.isLinkButton; - if (this.Document.isLinkButton) { - this.Document.followLinkZoom = true; - this.Document.followLinkLocation = "inPlace"; - } - } - - @undoBatch - toggleFollowOnRight = (): void => { - this.Document.ignoreClick = false; - this.Document.isLinkButton = !this.Document.isLinkButton; - if (this.Document.isLinkButton) { - this.Document.followLinkZoom = false; - this.Document.followLinkLocation = "onRight"; + setPushpin && (this.Document.isPushpin = this.Document.isLinkButton); + if (this.Document.isLinkButton && !this.onClickHandler) { + this.Document.followLinkZoom = zoom; + this.Document.followLinkLocation = location; + } else { + this.Document.onClick = this.layoutDoc.onClick = undefined; } } - @undoBatch - @action + @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { if (this.props.Document === Doc.UserDoc().activeWorkspace) { alert("linking to document tabs not yet supported. Drop link on document content."); @@ -650,11 +606,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } if (de.complete.linkDragData) { e.stopPropagation(); - if (de.complete.linkDragData.linkSourceDocument !== this.props.Document) { - const linkDoc = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument }, - { doc: this.props.Document }, `link`); - de.complete.linkDragData.linkSourceDocument !== this.props.Document && - (de.complete.linkDragData.linkDocument = linkDoc); // TODODO this is where in text links get passed + const linkSource = de.complete.linkDragData.linkSourceDocument; + if (linkSource !== this.props.Document) { + const linkDoc = DocUtils.MakeLink({ doc: linkSource }, { doc: this.props.Document }, `link`); + linkSource !== this.props.Document && (de.complete.linkDragData.linkDocument = linkDoc); // TODODO this is where in text links get passed linkDoc && makeLink(linkDoc); } @@ -669,8 +624,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu @undoBatch @action + toggleLockPosition = (): void => { + this.Document.lockedPosition = this.Document.lockedPosition ? undefined : true; + } + + @undoBatch + @action makeIntoPortal = async () => { - const portalLink = DocListCast(this.Document.links).find(d => d.anchor1 === this.props.Document); + const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document); if (!portalLink) { const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), title: StrCast(this.props.Document.title) + ".portal" }); DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to"); @@ -681,9 +642,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu @undoBatch @action - toggleBackground = (temporary: boolean): void => { - this.Document._overflow = temporary ? "visible" : "hidden"; - this.Document.isBackground = !temporary ? !this.Document.isBackground : (this.Document.isBackground ? undefined : true); + toggleBackground = () => { + this.Document.isBackground = (this.Document.isBackground ? undefined : true); + this.Document._overflow = this.Document.isBackground ? "visible" : undefined; if (this.Document.isBackground) { this.props.bringToFront(this.props.Document, true); this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeWidth"] = this.Document[WidthSym](); @@ -691,12 +652,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } } - @undoBatch - @action - toggleLockPosition = (): void => { - this.Document.lockedPosition = this.Document.lockedPosition ? undefined : true; - } - @action onContextMenu = async (e: React.MouseEvent | Touch): Promise<void> => { // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 @@ -743,9 +698,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "window-restore" }); onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); - onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" }); - onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link on Right", event: this.toggleFollowOnRight, icon: "concierge-bell" }); - onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" }); + onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "concierge-bell" }); + !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "concierge-bell" }); + onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "concierge-bell" }); + onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "snowflake" }); onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" }); !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "hand-point-right" }); @@ -759,11 +715,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu const more = cm.findByDescription("More..."); const moreItems = more && "subitems" in more ? more.subitems : []; - moreItems.push({ - description: "Download document", icon: "download", event: async () => { - Doc.Zip(this.props.Document); - } - }); + moreItems.push({ description: "Download document", icon: "download", event: async () => Doc.Zip(this.props.Document) }); if (!Doc.UserDoc().noviceMode) { moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" }); moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); @@ -790,111 +742,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu runInAction(() => { if (!this.topMost && !(e instanceof Touch)) { - // DocumentViews should stop propagation of this event - e.stopPropagation(); + e.stopPropagation(); // DocumentViews should stop propagation of this event } cm.displayMenu(e.pageX - 15, e.pageY - 15); !SelectionManager.IsSelected(this, true) && SelectionManager.SelectDoc(this, false); }); - const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc()["tabs-button-library"] as Doc).sourcePanel as Doc) ? "" : d.title), ""); - const item = ({ - description: `path: ${path}`, event: () => { - if (this.props.LibraryPath !== emptyPath) { - this.props.LibraryPath.map(lp => Doc.GetProto(lp).treeViewOpen = lp.treeViewOpen = true); - Doc.linkFollowHighlight(this.props.Document); - } else { - Doc.AddDocToList(Doc.GetProto(Doc.UserDoc().myCatalog as Doc), "data", this.props.Document[DataSym]); - } - }, icon: "check" - }); - //cm.addItem(item); - } - - recommender = async () => { - if (!ClientRecommender.Instance) new ClientRecommender({ title: "Client Recommender" }); - const documents: Doc[] = []; - const allDocs = await SearchUtil.GetAllDocs(); - // clears internal representation of documents as vectors - ClientRecommender.Instance.reset_docs(); - //ClientRecommender.Instance.arxivrequest("electrons"); - await Promise.all(allDocs.map((doc: Doc) => { - let isMainDoc: boolean = false; - const dataDoc = Doc.GetProto(doc); - if (doc.type === DocumentType.RTF) { - if (dataDoc === Doc.GetProto(this.props.Document)) { - isMainDoc = true; - } - if (!documents.includes(dataDoc)) { - documents.push(dataDoc); - const extdoc = doc.data_ext as Doc; - return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, true, "", isMainDoc); - } - } - if (doc.type === DocumentType.IMG) { - if (dataDoc === Doc.GetProto(this.props.Document)) { - isMainDoc = true; - } - if (!documents.includes(dataDoc)) { - documents.push(dataDoc); - const extdoc = doc.data_ext as Doc; - return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, true, "", isMainDoc, true); - } - } - })); - const doclist = ClientRecommender.Instance.computeSimilarities("cosine"); - const recDocs: { preview: Doc, score: number }[] = []; - // tslint:disable-next-line: prefer-for-of - for (let i = 0; i < doclist.length; i++) { - recDocs.push({ preview: doclist[i].actualDoc, score: doclist[i].score }); - } - - const data = recDocs.map(unit => { - unit.preview.score = unit.score; - return unit.preview; - }); - - console.log(recDocs.map(doc => doc.score)); - - const title = `Showing ${data.length} recommendations for "${StrCast(this.props.Document.title)}"`; - const recommendations = Docs.Create.RecommendationsDocument(data, { title }); - recommendations.documentIconHeight = 150; - recommendations.sourceDoc = this.props.Document; - recommendations.sourceDocContext = this.props.ContainingCollectionView!.props.Document; - this.props.addDocTab(recommendations, "onRight"); - - // RecommendationsBox.Instance.displayRecommendations(e.pageX + 100, e.pageY); - } - - @action - externalRecommendation = async (api: string) => { - if (!ClientRecommender.Instance) new ClientRecommender({ title: "Client Recommender" }); - ClientRecommender.Instance.reset_docs(); - const doc = Doc.GetDataDoc(this.props.Document); - const extdoc = doc.data_ext as Doc; - const recs_and_kps = await ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, false, api); - let recs: any; - let kps: any; - if (recs_and_kps) { - recs = recs_and_kps.recs; - kps = recs_and_kps.keyterms; - } - else { - console.log("recommender system failed :("); - return; - } - console.log("ibm keyterms: ", kps.toString()); - const headers = [new SchemaHeaderField("title"), new SchemaHeaderField("href")]; - const bodies: Doc[] = []; - const titles = recs.title_vals; - const urls = recs.url_vals; - for (let i = 0; i < 5; i++) { - const body = Docs.Create.FreeformDocument([], { title: titles[i] }); - body.href = urls[i]; - bodies.push(body); - } - this.props.addDocTab(Docs.Create.SchemaDocument(headers, bodies, { title: `Showing External Recommendations for "${StrCast(doc.title)}"` }), "onRight"); - this._showKPQuery = true; - this._queries = kps.toString(); } // does Document set a layout prop @@ -924,6 +776,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false; } childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); + @computed.struct get linkOffset() { return [-15, 0]; } @computed get contents() { TraceMobx(); return (<div style={{ position: "absolute", width: "100%", height: "100%" }}> @@ -967,13 +820,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu {this.layoutDoc.hideAllLinks ? (null) : this.allAnchors} {/* {this.allAnchors} */} {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton || this.props.dontRegisterView ? (null) : - <DocumentLinksButton View={this} Offset={[-15, 0]} />} + <DocumentLinksButton View={this} links={this.allLinks} Offset={this.linkOffset} />} </div> ); } // used to decide whether a link anchor view should be created or not. - // if it's a tempoarl link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here. + // if it's a temporal link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here. // would be good to generalize this some way. isNonTemporalLink = (linkDoc: Doc) => { const anchor = Cast(Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1 : linkDoc.anchor2, Doc) as Doc; @@ -981,7 +834,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu return anchor.type === DocumentType.AUDIO && NumCast(ept) ? false : true; } - @observable _link: Opt<Doc>; // see DocumentButtonBar for explanation of how this works makeLink = () => this._link; // pass the link placeholde to child views so they can react to make a specialized anchor. This is essentially a function call to the descendants since the value of the _link variable will immediately get set back to undefined. @@ -990,13 +842,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu anchorPanelWidth = () => this.props.PanelWidth() || 1; anchorPanelHeight = () => this.props.PanelHeight() || 1; - @computed get allAnchors() { + @computed.struct get directLinks() { return LinkManager.Instance.getAllDirectLinks(this.Document); } + @computed.struct get allLinks() { return DocListCast(this.Document.links); } + @computed.struct get allAnchors() { TraceMobx(); if (this.props.LayoutTemplateString?.includes("LinkAnchorBox")) return null; return (this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots this.layoutDoc.presBox || // presentationbox nodes this.props.dontRegisterView ? (null) : // view that are not registered - DocUtils.FilterDocs(LinkManager.Instance.getAllDirectLinks(this.Document), this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => + DocUtils.FilterDocs(this.directLinks, this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => <DocumentView {...this.props} key={i + 1} Document={d} ContainingCollectionView={this.props.ContainingCollectionView} @@ -1069,7 +923,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined); } } - @observable _animateScalingTo = 0; + switchViews = action((custom: boolean, view: string) => { this._animateScalingTo = 0.1; // shrink doc setTimeout(action(() => { @@ -1083,7 +937,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu return (this.Document.isBackground !== undefined || this.isSelected(false)) && ((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.IMG) && this.props.renderDepth > 0 && !this.props.treeViewDoc ? - <div className="documentView-lock" onClick={() => this.toggleBackground(true)}> + <div className="documentView-lock" onClick={this.toggleBackground}> <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} style={{ color: this.Document.isBackground ? "red" : undefined }} size="lg" /> </div> : (null); @@ -1107,7 +961,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"]; let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc._viewType !== CollectionViewType.Linear && this.props.Document.type !== DocumentType.INK; highlighting = highlighting && this.props.focus !== emptyFunction; // bcz: hack to turn off highlighting onsidebar panel documents. need to flag a document as not highlightable in a more direct way - return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} + const topmost = this.topMost ? "-topmost" : ""; + return <div className={`documentView-node${topmost}`} id={this.props.Document[Id]} ref={this._mainCont} onKeyDown={this.onKeyDown} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} @@ -1149,7 +1004,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu this.innards} {this.renderLock()} </div>; - { this._showKPQuery ? <KeyphraseQueryView keyphrases={this._queries}></KeyphraseQueryView> : undefined; } } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 5f689624c..d668d332b 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -66,7 +66,7 @@ const uploadIcons = { @observer export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageDocument>(ImageDocument) { - protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; + protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } private _imgRef: React.RefObject<HTMLImageElement> = React.createRef(); private _dropDisposer?: DragManager.DragDropDisposer; diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index bc43cd473..1a5edc1d9 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -32,7 +32,7 @@ const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema); export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, ScriptingDocument>(ScriptingDocument) { private dropDisposer?: DragManager.DragDropDisposer; - protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); } private _overlayDisposer?: () => void; private _caretPos = 0; diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index f5c8745e7..875142169 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -23,7 +23,7 @@ position: absolute; background-color: rgba(245, 230, 95, 0.616); } - .webBox-container, .webBox-container-dragging { + .webBox-container { transform-origin: top left; width: 100%; height: 100%; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index bc30b15e6..d30f1499e 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -14,7 +14,7 @@ import { listSpec, makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { WebField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; -import { addStyleSheet, clearStyleSheetRules, emptyFunction, returnOne, returnZero, Utils } from "../../../Utils"; +import { addStyleSheet, clearStyleSheetRules, emptyFunction, returnOne, returnZero, Utils, returnTrue } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; @@ -57,13 +57,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum @observable private _pressY: number = 0; @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>(); private _selectionReactionDisposer?: IReactionDisposer; + private _scrollReactionDisposer?: IReactionDisposer; + private _moveReactionDisposer?: IReactionDisposer; private _keyInput = React.createRef<HTMLInputElement>(); private _longPressSecondsHack?: NodeJS.Timeout; private _outerRef = React.createRef<HTMLDivElement>(); private _iframeRef = React.createRef<HTMLIFrameElement>(); private _iframeIndicatorRef = React.createRef<HTMLDivElement>(); private _iframeDragRef = React.createRef<HTMLDivElement>(); - private _reactionDisposer?: IReactionDisposer; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); iframeLoaded = action((e: any) => { @@ -76,22 +77,24 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum iframe.contentDocument.children[0].scrollTop = NumCast(this.layoutDoc._scrollTop); iframe.contentDocument.children[0].scrollLeft = NumCast(this.layoutDoc._scrollLeft); } - this._reactionDisposer?.(); - this._reactionDisposer = reaction(() => ({ y: this.layoutDoc._scrollY, x: this.layoutDoc._scrollX }), - ({ x, y }) => { - if (y !== undefined) { - this._outerRef.current!.scrollTop = y; - this.layoutDoc._scrollY = undefined; - } - if (x !== undefined) { - this._outerRef.current!.scrollLeft = x; - this.layoutDoc.scrollX = undefined; - } - }, + this._scrollReactionDisposer?.(); + this._scrollReactionDisposer = reaction(() => ({ y: this.layoutDoc._scrollY, x: this.layoutDoc._scrollX }), + ({ x, y }) => this.updateScroll(x, y), { fireImmediately: true } ); }); + updateScroll = (x: Opt<number>, y: Opt<number>) => { + if (y !== undefined) { + this._outerRef.current!.scrollTop = y; + this.layoutDoc._scrollY = undefined; + } + if (x !== undefined) { + this._outerRef.current!.scrollLeft = x; + this.layoutDoc.scrollX = undefined; + } + } + setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; iframedown = (e: PointerEvent) => { this._setPreviewCursor?.(e.screenX, e.screenY, false); @@ -106,6 +109,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum const urlField = Cast(this.dataDoc[this.props.fieldKey], WebField); runInAction(() => this._url = urlField?.url.toString() || ""); + this._moveReactionDisposer = reaction(() => this.layoutDoc.x || this.layoutDoc.y, + () => this.updateScroll(this.layoutDoc._scrollLeft, this.layoutDoc._scrollTop)); this._selectionReactionDisposer = reaction(() => this.props.isSelected(), selected => { @@ -140,8 +145,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } componentWillUnmount() { + this._moveReactionDisposer?.(); this._selectionReactionDisposer?.(); - this._reactionDisposer?.(); + this._scrollReactionDisposer?.(); document.removeEventListener("pointerup", this.onLongPressUp); document.removeEventListener("pointermove", this.onLongPressMove); this._iframeRef.current?.contentDocument?.removeEventListener('pointerdown', this.iframedown); @@ -547,7 +553,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum clipDoc._width = this.marqueeWidth(); clipDoc._height = this.marqueeHeight(); clipDoc._scrollTop = this.marqueeY(); - const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title }); + const targetDoc = Docs.Create.TextDocument("", { _width: 125, _height: 125, title: "Note linked to " + this.props.Document.title }); Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]); clipDoc.rootDocument = targetDoc; targetDoc.layoutKey = "layout"; @@ -558,8 +564,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) { DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation"); annotationDoc.isLinkButton = true; - e.annoDragData.dropDocument.isPushpin = true; - e.annoDragData.dropDocument.isLinkButton = true; } } }); @@ -582,8 +586,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum else if (this._mainCont.current) { // set marquee x and y positions to the spatially transformed position const boundingRect = this._mainCont.current.getBoundingClientRect(); + const boundingHeight = (this.Document._nativeHeight || 1) / (this.Document._nativeWidth || 1) * boundingRect.width; this._startX = (e.clientX - boundingRect.left) / boundingRect.width * (this.Document._nativeWidth || 1); - this._startY = (e.clientY - boundingRect.top) / boundingRect.height * (this.Document._nativeHeight || 1); + this._startY = (e.clientY - boundingRect.top) / boundingHeight * (this.Document._nativeHeight || 1); this._marqueeHeight = this._marqueeWidth = 0; this._marqueeing = true; } @@ -598,8 +603,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum if (this._marqueeing && this._mainCont.current) { // transform positions and find the width and height to set the marquee to const boundingRect = this._mainCont.current.getBoundingClientRect(); + const boundingHeight = (this.Document._nativeHeight || 1) / (this.Document._nativeWidth || 1) * boundingRect.width; const curX = (e.clientX - boundingRect.left) / boundingRect.width * (this.Document._nativeWidth || 1); - const curY = (e.clientY - boundingRect.top) / boundingRect.height * (this.Document._nativeHeight || 1); + const curY = (e.clientY - boundingRect.top) / boundingHeight * (this.Document._nativeHeight || 1); this._marqueeWidth = curX - this._startX; this._marqueeHeight = curY - this._startY; this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth); @@ -651,17 +657,25 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum document.removeEventListener("pointermove", this.onSelectMove); document.removeEventListener("pointerup", this.onSelectEnd); } - marqueeWidth = () => this._marqueeWidth; marqueeHeight = () => this._marqueeHeight; marqueeX = () => this._marqueeX; marqueeY = () => this._marqueeY; marqueeing = () => this._marqueeing; + visibleHeiht = () => { + if (this._mainCont.current) { + const boundingRect = this._mainCont.current.getBoundingClientRect(); + const scalin = (this.Document._nativeWidth || 0) / boundingRect.width; + return Math.min(boundingRect.height * scalin, this.props.PanelHeight() * scalin); + } + return this.props.PanelHeight(); + } scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc._scrollLeft), NumCast(this.layoutDoc._scrollTop)); render() { return (<div className="webBox" ref={this._mainCont} > <div className={`webBox-container`} style={{ + position: undefined, transform: `scale(${this.props.ContentScaling()})`, width: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%", height: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%", @@ -672,7 +686,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum {this.content} <div className={"webBox-outerContent"} ref={this._outerRef} style={{ - width: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%", + width: Number.isFinite(this.props.ContentScaling()) ? `${Math.max(100, 100 / this.props.ContentScaling())}% ` : "100%", pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc.isBackground ? "all" : "none" }} onWheel={e => e.stopPropagation()} @@ -700,6 +714,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum annotationsKey={this.annotationKey} NativeHeight={returnZero} NativeWidth={returnZero} + VisibleHeight={this.visibleHeiht} focus={this.props.focus} setPreviewCursor={this.setPreviewCursor} isSelected={this.props.isSelected} diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 8718bf329..958a37568 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -205,9 +205,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna {this._fieldKey} </span>} - {/* <div className="dashFieldView-fieldSpan"> */} - {this.fieldValueContent} - {/* </div> */} + {this.props.fieldKey.startsWith("#") ? (null) : this.fieldValueContent} {!this._showEnumerables ? (null) : <div className="dashFieldView-enumerables" onPointerDown={this.onPointerDownEnumerables} />} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 6b6fc5da2..627c6e363 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -223,7 +223,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } const state = this._editorView.state.apply(tx); this._editorView.updateState(state); - (tx.storedMarks && !this._editorView.state.storedMarks) && (this._editorView.state.storedMarks = tx.storedMarks); const tsel = this._editorView.state.selection.$from; tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 1000))); @@ -741,7 +740,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._disposers.search = reaction(() => this.rootDoc.searchMatch, search => search ? this.highlightSearchTerms([Doc.SearchQuery()]) : this.unhighlightSearchTerms(), - { fireImmediately: true }); + { fireImmediately: this.rootDoc.searchMatch ? true : false }); this._disposers.record = reaction(() => this._recording, () => { @@ -994,7 +993,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp clipboardTextSerializer: this.clipboardTextSerializer, handlePaste: this.handlePaste, }); - !Doc.UserDoc().noviceMode && applyDevTools.applyDevTools(this._editorView); + // !Doc.UserDoc().noviceMode && applyDevTools.applyDevTools(this._editorView); const startupText = !rtfField && this._editorView && Field.toString(this.dataDoc[fieldKey] as Field); if (startupText) { const { state: { tr }, dispatch } = this._editorView; @@ -1011,10 +1010,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp FormattedTextBox.SelectOnLoadChar = ""; } - (selectOnLoad /* || !rtfField?.Text*/) && this._editorView!.focus(); + selectOnLoad && this._editorView!.focus(); // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. - if (!this._editorView!.state.storedMarks || !this._editorView!.state.storedMarks.some(mark => mark.type === schema.marks.user_mark)) { - this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })]; + if (!this._editorView!.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) { + this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ?? []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })]; } } getFont(font: string) { diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index ef0fead4a..dc1d8a2c8 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -317,13 +317,12 @@ export class RichTextRules { // create an inline view of a tag stored under the '#' field new InputRule( - new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_;\-0-9]*)\s$/), + new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_\-0-9]*)\s$/), (state, match, start, end) => { const tag = match[1]; if (!tag) return state.tr; - const multiple = tag.split(";"); - this.Document[DataSym]["#"] = multiple.length > 1 ? new List(multiple) : tag; - const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" }); + this.Document[DataSym]["#" + tag] = "."; + const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" + tag }); return state.tr.deleteRange(start, end).insert(start, fieldView); }), |
