From 2e26c14fe82fa7dfdeb51398558101a0047743e6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 2 Feb 2021 13:53:50 -0500 Subject: playing with a general lightbox for Dash documents. --- src/client/views/DocumentDecorations.tsx | 5 ++- src/client/views/GlobalKeyHandler.ts | 1 + src/client/views/MainView.scss | 12 +++++++ src/client/views/MainView.tsx | 42 +++++++++++++++++++++- src/client/views/collections/TabDocView.tsx | 5 ++- .../collections/collectionFreeForm/MarqueeView.tsx | 6 +++- src/client/views/linking/LinkMenuItem.tsx | 6 ++-- 7 files changed, 71 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 8d8f4cd3b..b8500c263 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -26,6 +26,7 @@ import { InkStrokeProperties } from './InkStrokeProperties'; import { DocumentView } from "./nodes/DocumentView"; import React = require("react"); import e = require('express'); +import { MainView } from './MainView'; @observer export class DocumentDecorations extends React.Component<{ boundsLeft: number, boundsTop: number }, { value: string }> { @@ -171,8 +172,10 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b alias.x = -alias[WidthSym]() / 2; alias.y = -alias[HeightSym]() / 2; CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right"); - } else { // open same document in new tab + } else if (e.altKey) { // open same document in new tab CollectionDockingView.ToggleSplit(Cast(selectedDocs[0].props.Document._fullScreenView, Doc, null) || selectedDocs[0].props.Document, "right"); + } else { + runInAction(() => MainView.Instance.LightboxDoc = selectedDocs[0].props.Document); } } } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index e56ba38dd..de73004a0 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -114,6 +114,7 @@ export class KeyManager { DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = false); + MainView.Instance.LightboxDoc = undefined; Doc.SetSelectedTool(InkTool.None); var doDeselect = true; diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 8ccb64744..fc375f6a2 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -67,6 +67,18 @@ background: #cacaca; color: black; } + + .mainView-lightBoxFrame { + position: absolute; + top: 0; left: 0; + width: 100%; + height: 100%; + background: #000000bb; + z-index: 10000; + } + .mainView-lightBoxContents { + position: absolute; + } } .mainView-container-dark { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index bd5db62ef..08d590fd4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -68,6 +68,7 @@ export class MainView extends React.Component { private _docBtnRef = React.createRef(); private _mainViewRef = React.createRef(); @observable public LastButton: Opt; + @observable public LightboxDoc: Opt; @observable private _panelWidth: number = 0; @observable private _panelHeight: number = 0; @observable private _panelContent: string = "none"; @@ -593,6 +594,44 @@ export class MainView extends React.Component { ; } + lightboxWidth = () => window.innerWidth - Math.min(window.innerWidth / 4, 200) * 2; + lightboxHeight = () => window.innerHeight - Math.min(window.innerHeight / 4, 100) * 2; + @computed get lightboxView() { + return !this.LightboxDoc ? (null) : +
this.LightboxDoc = undefined)} > +
+ +
+
; + } + + render() { return (
((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)} ref={this._mainViewRef}> {this.inkResources} @@ -621,7 +660,8 @@ export class MainView extends React.Component { {this.snapLines}
-
); + {this.lightboxView} +
); } makeWebRef = (ele: HTMLDivElement) => { diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index c66734556..d291a703b 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -33,6 +33,7 @@ import { CollectionViewType } from './CollectionView'; import "./TabDocView.scss"; import React = require("react"); import Color = require('color'); +import { MainView } from '../MainView'; const _global = (window /* browser */ || global /* node */) as any; interface TabDocViewProps { @@ -279,7 +280,9 @@ export class TabDocView extends React.Component { case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); case "inPlace": case "add": - default: return CollectionDockingView.AddSplit(doc, locationParams, this.stack); + default: runInAction(() => MainView.Instance.LightboxDoc = doc); + if (MainView.Instance.LightboxDoc) return true; + return CollectionDockingView.AddSplit(doc, locationParams, this.stack); } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 0edbfe7a5..f7fb2b83d 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -643,7 +643,11 @@ export class MarqueeView extends React.Component e.preventDefault()} onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}> {this._visible ? this.marqueeDiv : null} diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 19ef03a31..e9a0486d1 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -106,13 +106,15 @@ export class LinkMenuItem extends React.Component { } } - onLinkButtonUp = (e: PointerEvent): void => { + onLinkButtonUp = action((e: PointerEvent): void => { document.removeEventListener("pointermove", this.onLinkButtonMoved); document.removeEventListener("pointerup", this.onLinkButtonUp); LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false); + LinkDocPreview.LinkInfo = undefined; + DocumentLinksButton.EditLink = undefined; e.stopPropagation(); - } + }); onLinkButtonMoved = async (e: PointerEvent) => { if (this._drag.current !== null && Math.abs((e.clientX - this._downX) * (e.clientX - this._downX) + (e.clientY - this._downY) * (e.clientY - this._downY)) > 5) { -- cgit v1.2.3-70-g09d2 From b1c83a54b8794c91bf2c218939891e87ad2a2776 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 2 Feb 2021 14:08:07 -0500 Subject: playing with translations in textbox sidebars. --- package-lock.json | 21 ++++++++- package.json | 1 + .../views/nodes/formattedText/FormattedTextBox.tsx | 55 ++++++++++++++++------ 3 files changed, 60 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index 42cbc1fd6..464fdfb51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6358,8 +6358,7 @@ "follow-redirects": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", - "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", - "dev": true + "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==" }, "for-each-property": { "version": "0.0.4", @@ -16843,6 +16842,24 @@ "punycode": "^2.1.0" } }, + "translate-google-api": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/translate-google-api/-/translate-google-api-1.0.4.tgz", + "integrity": "sha512-KVXmo4+64/H1vIbnzf2zNiJ2JLeEB3jrEnNRP2EFNAGNqna/5bmw/Cps3pCHu0n3BzTOoWh9u6wFvrRYdzQ6Iw==", + "requires": { + "axios": "^0.20.0" + }, + "dependencies": { + "axios": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz", + "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==", + "requires": { + "follow-redirects": "^1.10.0" + } + } + } + }, "traverse-chain": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz", diff --git a/package.json b/package.json index 13849c0f3..a18b11967 100644 --- a/package.json +++ b/package.json @@ -251,6 +251,7 @@ "standard-http-error": "^2.0.1", "styled-components": "^4.4.1", "textarea-caret": "^3.1.0", + "translate-google-api": "^1.0.4", "typescript-collections": "^1.3.3", "typescript-language-server": "^0.4.0", "url-loader": "^1.1.2", diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d24ccd9ad..634165bab 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -67,12 +67,14 @@ import { StyleProp } from '../../StyleProvider'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; import { DocumentManager } from '../../../util/DocumentManager'; +var translateGoogleApi = require("translate-google-api") export interface FormattedTextBoxProps { makeLink?: () => Opt; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text hideOnLeave?: boolean; // used by DocumentView for setting caption's hide on leave (bcz: would prefer to have caption-hideOnLeave field set or something similar) xMargin?: number; // used to override document's settings for xMargin --- see CollectionCarouselView yMargin?: number; + noSidebar?: boolean; dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded } export const GoogleRef = "googleDocId"; @@ -94,7 +96,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp private _ref: React.RefObject = React.createRef(); private _scrollRef: React.RefObject = React.createRef(); private _editorView: Opt; - private _applyingChange: boolean = false; + private _applyingChange: string = ""; private _searchIndex = 0; private _cachedLinks: Doc[] = []; private _undoTyping?: UndoManager.Batch; @@ -246,6 +248,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(Math.min(coordsT.left, coordsB.left), Math.max(coordsT.bottom, coordsB.bottom)); } + _lastText = ""; dispatchTransaction = (tx: Transaction) => { let timeStamp; clearTimeout(timeStamp); @@ -296,8 +299,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }; if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { - if (!this._applyingChange && removeSelection(json) !== removeSelection(curProto?.Data)) { - this._applyingChange = true; + if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) { + this._applyingChange = this.fieldKey; (curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()))); if ((!curTemp && !curProto) || curText || json.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) if (removeSelection(json) !== removeSelection(curLayout?.Data)) { @@ -325,7 +328,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.dataDoc[this.props.fieldKey + "-noTemplate"] = undefined; // mark the data field as not being split from any template it might have unchanged = false; } - this._applyingChange = false; + this._applyingChange = ""; if (!unchanged) { this.updateTitle(); this.tryUpdateHeight(); @@ -931,16 +934,20 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp () => { if (!this.dataDoc || !this.layoutDoc) return undefined; if (this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey]) { - return Cast(this.dataDoc[this.props.fieldKey], RichTextField, null)?.Data; + return { data: Cast(this.dataDoc[this.props.fieldKey], RichTextField, null), str: StrCast(this.dataDoc[this.props.fieldKey]) }; } - return Cast(this.layoutDoc[this.props.fieldKey], RichTextField, null)?.Data; + return { data: Cast(this.layoutDoc[this.props.fieldKey], RichTextField, null), str: StrCast(this.layoutDoc[this.props.fieldKey]) }; }, incomingValue => { - if (incomingValue !== undefined && this._editorView && !this._applyingChange) { - const updatedState = JSON.parse(incomingValue); - if (JSON.stringify(this._editorView.state.toJSON()) !== JSON.stringify(updatedState)) { - this._editorView.updateState(EditorState.fromJSON(this.config, updatedState)); - this.tryUpdateHeight(); + if (this._editorView && this._applyingChange !== this.fieldKey) { + if (incomingValue?.data) { + const updatedState = JSON.parse(incomingValue.data.Data); + if (JSON.stringify(this._editorView.state.toJSON()) !== JSON.stringify(updatedState)) { + this._editorView.updateState(EditorState.fromJSON(this.config, updatedState)); + this.tryUpdateHeight(); + } + } else if (incomingValue?.str) { + selectAll(this._editorView!.state, tx => this._editorView?.dispatch(tx.insertText(incomingValue.str))); } } }, @@ -1541,6 +1548,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp FormattedTextBox.LiveTextUndo?.end(); FormattedTextBox.LiveTextUndo = undefined; + + const state = this._editorView!.state; + const curText = state.doc.textBetween(0, state.doc.content.size, " \n"); + if (!this.fieldKey.includes("translation") && curText.endsWith(" ") && curText !== this._lastText) { + try { + translateGoogleApi(curText, { from: "en", to: "es", }).then(result => { + this.dataDoc[this.fieldKey + "-translation"] = result[0]; + }); + } catch (e) { + console.log(e.message); + } + this._lastText = curText; + } } _lastTimedMark: Mark | undefined = undefined; @@ -1636,7 +1656,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get sidebarHandle() { const annotated = DocListCast(this.dataDoc[this.annotationKey]).filter(d => d?.author).length; - return !this.props.isSelected() && !(annotated && !this.sidebarWidth()) ? (null) : + return this.props.noSidebar || (!this.props.isSelected() && !(annotated && !this.sidebarWidth())) ? (null) :
- {this.layoutDoc.sidebarViewType === CollectionViewType.Freeform ? + + {/* {this.layoutDoc.sidebarViewType === CollectionViewType.Freeform ? : - } + } */}
; } -- cgit v1.2.3-70-g09d2 From 5666aa3b921024c0f7e6ebb48e0e8f50bb770e79 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 2 Feb 2021 23:00:17 -0500 Subject: fixed document decorations on lightbox. made translations an option for text boxes. --- src/client/views/DocumentDecorations.scss | 1 + src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainView.tsx | 6 +++-- .../views/nodes/formattedText/FormattedTextBox.tsx | 27 ++++++++++------------ 4 files changed, 18 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 22e120167..461ea0fef 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -4,6 +4,7 @@ $linkGap : 3px; .documentDecorations { position: absolute; + z-index: 20000; } .documentDecorations-container { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index b8500c263..226eef658 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -604,7 +604,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b left: bounds.x - this._resizeBorderWidth / 2, top: bounds.y - this._resizeBorderWidth / 2, pointerEvents: KeyManager.Instance.ShiftPressed || this.Interacting ? "none" : "all", - zIndex: SelectionManager.Views().length > 1 ? 900 : 0, + display: SelectionManager.Views().length <= 1 ? "none" : undefined }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} > {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <> diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 08d590fd4..8598a7303 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -59,7 +59,7 @@ import { AnchorMenu } from './pdf/AnchorMenu'; import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { SearchBox } from './search/SearchBox'; -import { DefaultStyleProvider, StyleProp } from './StyleProvider'; +import { DefaultStyleProvider, StyleProp, DefaultLayerProvider } from './StyleProvider'; const _global = (window /* browser */ || global /* node */) as any; @observer @@ -596,6 +596,7 @@ export class MainView extends React.Component { lightboxWidth = () => window.innerWidth - Math.min(window.innerWidth / 4, 200) * 2; lightboxHeight = () => window.innerHeight - Math.min(window.innerHeight / 4, 100) * 2; + lightboxScreenToLocal = () => new Transform(-Math.min(window.innerWidth / 4, 200), -Math.min(window.innerHeight / 4, 100), 1); @computed get lightboxView() { return !this.LightboxDoc ? (null) :
this.LightboxDoc = undefined)} > @@ -614,7 +615,8 @@ export class MainView extends React.Component { rootSelected={returnTrue} removeDocument={undefined} styleProvider={DefaultStyleProvider} - ScreenToLocalTransform={Transform.Identity} + layerProvider={DefaultLayerProvider(this.LightboxDoc)} + ScreenToLocalTransform={this.lightboxScreenToLocal} PanelWidth={this.lightboxWidth} PanelHeight={this.lightboxHeight} focus={emptyFunction} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 634165bab..8b1e58ceb 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -67,7 +67,7 @@ import { StyleProp } from '../../StyleProvider'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; import { DocumentManager } from '../../../util/DocumentManager'; -var translateGoogleApi = require("translate-google-api") +const translateGoogleApi = require("translate-google-api") export interface FormattedTextBoxProps { makeLink?: () => Opt; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text @@ -1551,14 +1551,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const state = this._editorView!.state; const curText = state.doc.textBetween(0, state.doc.content.size, " \n"); - if (!this.fieldKey.includes("translation") && curText.endsWith(" ") && curText !== this._lastText) { + if (this.layoutDoc.sidebarViewType === "translation" && !this.fieldKey.includes("translation") && curText.endsWith(" ") && curText !== this._lastText) { try { - translateGoogleApi(curText, { from: "en", to: "es", }).then(result => { - this.dataDoc[this.fieldKey + "-translation"] = result[0]; + translateGoogleApi(curText, { from: "en", to: "es", }).then((result1: any) => { + setTimeout(() => translateGoogleApi(result1[0], { from: "es", to: "en", }).then((result: any) => { + this.dataDoc[this.fieldKey + "-translation"] = result1 + "\r\n\r\n" + result[0]; + }), 1000); }); - } catch (e) { - console.log(e.message); - } + } catch (e) { console.log(e.message); } this._lastText = curText; } } @@ -1693,14 +1693,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp return this.props.noSidebar || !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) :
- - {/* {this.layoutDoc.sidebarViewType === CollectionViewType.Freeform ? - : - } */} + {this.layoutDoc.sidebarViewType === "translation" ? + : + this.layoutDoc.sidebarViewType === CollectionViewType.Freeform ? + : + }
; } -- cgit v1.2.3-70-g09d2 From df23545cff9c612a91272c16fa819f8b53c310d0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 3 Feb 2021 10:35:29 -0500 Subject: fixed webBox forward/backward and in lightbox. --- src/client/views/AntimodeMenu.scss | 2 +- src/client/views/GestureOverlay.tsx | 6 +- src/client/views/GlobalKeyHandler.ts | 2 +- src/client/views/collections/CollectionMenu.scss | 9 +- src/client/views/collections/CollectionMenu.tsx | 157 +++++------------------ src/client/views/nodes/DocumentView.tsx | 14 +- src/client/views/nodes/WebBox.tsx | 45 +++---- 7 files changed, 73 insertions(+), 162 deletions(-) (limited to 'src') diff --git a/src/client/views/AntimodeMenu.scss b/src/client/views/AntimodeMenu.scss index be21cec12..6eeb83ba9 100644 --- a/src/client/views/AntimodeMenu.scss +++ b/src/client/views/AntimodeMenu.scss @@ -3,7 +3,7 @@ .antimodeMenu-cont { position: absolute; - z-index: 10000; + z-index: 10001; height: $antimodemenu-height; background: #323232; box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 524462401..0cf958681 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -607,7 +607,7 @@ export class GestureOverlay extends Touchable { this.makePolygon(this.InkShape, false); this.dispatchGesture(GestureUtils.Gestures.Stroke); this._points = []; - if (!CollectionFreeFormViewChrome.Instance._keepMode) { + if (!CollectionFreeFormViewChrome.Instance._keepPrimitiveMode) { this.InkShape = ""; } } @@ -654,9 +654,9 @@ export class GestureOverlay extends Touchable { this._points = []; } //get out of ink mode after each stroke= - if (CollectionFreeFormViewChrome.Instance && !CollectionFreeFormViewChrome.Instance?._keepMode) { + if (CollectionFreeFormViewChrome.Instance && !CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) { Doc.SetSelectedTool(InkTool.None); - CollectionFreeFormViewChrome.Instance._selected = CollectionFreeFormViewChrome.Instance._shapesNum; + CollectionFreeFormViewChrome.Instance._selectedPrimitive = CollectionFreeFormViewChrome.Instance._shapesPrimNum; SetActiveArrowStart("none"); GestureOverlay.Instance.SavedArrowStart = ActiveArrowStart(); SetActiveArrowEnd("none"); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index de73004a0..78905bfb2 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -133,7 +133,7 @@ export class KeyManager { GoogleAuthenticationManager.Instance.cancel(); SharingManager.Instance.close(); GroupManager.Instance.close(); - CollectionFreeFormViewChrome.Instance?.clearKeep(); + CollectionFreeFormViewChrome.Instance?.clearKeepPrimitiveMode(); window.getSelection()?.empty(); document.body.focus(); break; diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss index e36e5caa7..180eaba4e 100644 --- a/src/client/views/collections/CollectionMenu.scss +++ b/src/client/views/collections/CollectionMenu.scss @@ -315,6 +315,13 @@ } } +.collectionMenu-webUrlButtons { + margin-left: 44; + background: lightGray; + width: 100%; + display: flex; +} + .webBox-urlEditor { position: relative; opacity: 0.9; @@ -354,7 +361,7 @@ } } -.webpage-urlInput { +.collectionMenu-urlInput { padding: 12px 10px 11px 10px; border: 0px; color: black; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 5670d45f5..4e2d81e61 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -5,7 +5,7 @@ import { Tooltip } from "@material-ui/core"; import { action, computed, Lambda, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { ColorState } from "react-color"; -import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt } from "../../../fields/Doc"; import { Document } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { InkTool } from "../../../fields/InkField"; @@ -15,7 +15,6 @@ import { RichTextField } from "../../../fields/RichTextField"; import { listSpec } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; -import { WebField } from "../../../fields/URLField"; import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils"; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; @@ -549,17 +548,13 @@ export class CollectionFreeFormViewChrome extends React.Component { @@ -636,17 +628,17 @@ export class CollectionFreeFormViewChrome extends React.Component { - this._keepMode = keep; - if (this._selected !== i) { - this._selected = i; + this._keepPrimitiveMode = keep; + if (this._selectedPrimitive !== i) { + this._selectedPrimitive = i; Doc.SetSelectedTool(InkTool.Pen); SetActiveArrowStart(this._head[i]); SetActiveArrowEnd(this._end[i]); SetActiveBezierApprox("300"); - GestureOverlay.Instance.InkShape = this._shape[i]; + GestureOverlay.Instance.InkShape = this._shapePrims[i]; } else { - this._selected = this._shapesNum; + this._selectedPrimitive = this._shapePrims.length; Doc.SetSelectedTool(InkTool.None); SetActiveArrowStart(""); SetActiveArrowEnd(""); @@ -660,7 +652,7 @@ export class CollectionFreeFormViewChrome extends React.Component func(i, false)} onDoubleClick={() => func(i, true)} - style={{ backgroundColor: i === this._selected ? "525252" : "", fontSize: "20" }}> + style={{ backgroundColor: i === this._selectedPrimitive ? "525252" : "", fontSize: "20" }}> )} @@ -729,120 +721,35 @@ export class CollectionFreeFormViewChrome extends React.Component; } - onUrlDrop = (e: React.DragEvent) => { + onWebUrlDrop = (e: React.DragEvent) => { const { dataTransfer } = e; const html = dataTransfer.getData("text/html"); const uri = dataTransfer.getData("text/uri-list"); - const url = uri || html || this._url; + const url = uri || html || this.webBoxUrl || ""; const newurl = url.startsWith(window.location.origin) ? - url.replace(window.location.origin, this._url.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url; - this.submitURL(newurl); + url.replace(window.location.origin, this.webBoxUrl?.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url; + this.submitWebUrl(newurl); e.stopPropagation(); } - onUrlDragover = (e: React.DragEvent) => { - e.preventDefault(); - } - - @computed get _url() { - return this.selectedDoc?.data instanceof WebField ? Cast(this.selectedDoc.data, WebField, null)?.url.toString() : Field.toString(this.selectedDoc?.data as Field); - } - - set _url(value) { - if (this.selectedDoc) { - Doc.GetProto(this.selectedDoc).data = new WebField(value); - Doc.SetInPlace(this.selectedDoc, "title", value, true); - const annots = Doc.GetProto(this.selectedDoc)["data-annotations-" + this.urlHash(value)]; - Doc.GetProto(this.selectedDoc)["data-annotations"] = annots instanceof ObjectField ? ObjectField.MakeCopy(annots) : new List([]); - } - } - - @action - submitURL = (url: string) => { - if (!url.startsWith("http")) url = "http://" + url; - try { - const selectedDoc = this.selectedDoc; - if (selectedDoc) { - const URLy = new URL(url); - const future = Cast(selectedDoc["data-future"], listSpec("string"), null); - const history = Cast(selectedDoc["data-history"], listSpec("string"), null); - const annos = DocListCast(selectedDoc["data-annotations"]); - if (Field.toString(selectedDoc.data as Field) === Field.toString(new WebField(URLy))) { - Doc.GetProto(selectedDoc).data = new WebField(new URL("http://cs.brown.edu/~avd")); - setTimeout(action(() => Doc.GetProto(selectedDoc).data = new WebField(URLy)), 100); - } else { - if (url) { - Doc.GetProto(selectedDoc)["data-annotations-" + this.urlHash(this._url)] = new List(annos); - if (history === undefined) { - selectedDoc["data-history"] = new List([this._url]); - } else { - history.push(this._url); - } - this.props.docView.props.Document._scrollTop = 0; - future && (future.length = 0); - } - this._url = url; - } - } - } catch (e) { - console.log("WebBox URL error:" + url); - } - } - - urlHash(s: string) { - return s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0); - } - - onValueKeyDown = async (e: React.KeyboardEvent) => { - e.key === "Enter" && this.submitURL(this._keyInput.current!.value); + onWebUrlValueKeyDown = (e: React.KeyboardEvent) => { + e.key === "Enter" && this.submitWebUrl(this._keyInput.current!.value); e.stopPropagation(); } - - @action - forward = () => { - const selectedDoc = this.selectedDoc; - if (selectedDoc) { - const future = Cast(selectedDoc["data-future"], listSpec("string"), null); - const history = Cast(selectedDoc["data-history"], listSpec("string"), null); - if (future?.length) { - history?.push(this._url); - Doc.GetProto(selectedDoc)["data-annotations-" + this.urlHash(this._url)] = new List(DocListCast(selectedDoc["data-annotations"])); - const newurl = future.pop()!; - Doc.GetProto(selectedDoc).data = new WebField(new URL(this._url = newurl)); - Doc.GetProto(selectedDoc)["data-annotations"] = new List(DocListCast(selectedDoc["data-annotations-" + this.urlHash(newurl)])); - } - } - } - - @action - back = () => { - const selectedDoc = this.selectedDoc; - if (selectedDoc) { - const future = Cast(selectedDoc["data-future"], listSpec("string"), null); - const history = Cast(selectedDoc["data-history"], listSpec("string"), null); - if (history?.length) { - if (future === undefined) selectedDoc["data-future"] = new List([this._url]); - else future.push(this._url); - Doc.GetProto(selectedDoc)["data-annotations-" + this.urlHash(this._url)] = new List(DocListCast(selectedDoc["data-annotations"])); - const newurl = history.pop()!; - Doc.GetProto(selectedDoc).data = new WebField(new URL(this._url = newurl)); - Doc.GetProto(selectedDoc)["data-annotations"] = new List(DocListCast(selectedDoc["data-annotations-" + this.urlHash(newurl)])); - } - } - } + submitWebUrl = (url: string) => this.selectedDocumentView?.ComponentView?.submitURL?.(url); + webUrlForward = () => this.selectedDocumentView?.ComponentView?.forward?.(); + webUrlBack = () => this.selectedDocumentView?.ComponentView?.back?.(); private _keyInput = React.createRef(); @computed get urlEditor() { return ( -
- e.preventDefault()} > + e.preventDefault()} + onKeyDown={this.onWebUrlValueKeyDown} onClick={(e) => { this._keyInput.current!.select(); e.stopPropagation(); @@ -850,13 +757,13 @@ export class CollectionFreeFormViewChrome extends React.Component
- - -
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5ce8fcdcb..099433168 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -44,6 +44,13 @@ import React = require("react"); export type DocAfterFocusFunc = (notFocused: boolean) => boolean; export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => void; export type StyleProviderFunc = (doc: Opt, props: Opt, property: string) => any; +export interface DocComponentView { + getAnchor: () => Doc; + back?: () => boolean; + forward?: () => boolean; + url?: () => string; + submitURL?: (url: string) => boolean; +} export interface DocumentViewSharedProps { renderDepth: number; Document: Doc; @@ -51,7 +58,7 @@ export interface DocumentViewSharedProps { fitContentsToDoc?: boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; - setContentView?: (view: { getAnchor: () => Doc }) => any; + setContentView?: (view: DocComponentView) => any; CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView; PanelWidth: () => number; PanelHeight: () => number; @@ -122,6 +129,7 @@ export class DocumentViewInternal extends DocComponent; protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; private get topMost() { return this.props.renderDepth === 0; } @@ -701,10 +709,9 @@ export class DocumentViewInternal extends DocComponent this.ContentScale; onClickFunc = () => this.onClickHandler; makeLink = () => this.props.DocumentView.LinkBeingCreated; // 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. - setContentView = (view: { getAnchor: () => Doc }) => this._componentView = view; + setContentView = (view: { getAnchor: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view; @observable contentsActive: () => boolean = returnFalse; @action setContentsActive = (setActive: () => boolean) => this.contentsActive = setActive; - _componentView: { getAnchor: () => Doc } | undefined; @computed get contents() { TraceMobx(); return
{ get dataDoc() { return this.docView?.dataDoc || this.Document; } get finalLayoutKey() { return this.docView?.finalLayoutKey || "layout"; } get ContentDiv() { return this.docView?.ContentDiv; } + get ComponentView() { return this.docView?._componentView; } get allLinks() { return this.docView?.allLinks || []; } get LayoutFieldKey() { return this.docView?.LayoutFieldKey || "layout"; } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 4b7f0bf77..438395ab0 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -25,7 +25,6 @@ import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; import { Annotation } from "../pdf/Annotation"; -import { AnchorMenu } from "../pdf/AnchorMenu"; import { FieldView, FieldViewProps } from './FieldView'; import "./WebBox.scss"; import React = require("react"); @@ -57,6 +56,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this._url; constructor(props: any) { super(props); @@ -76,8 +76,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this.rootDoc; + + async componentDidMount() { + this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. + const urlField = Cast(this.dataDoc[this.props.fieldKey], WebField); runInAction(() => this._url = urlField?.url.toString() || ""); @@ -194,21 +199,6 @@ export class WebBox extends ViewBoxAnnotatableComponent { e.preventDefault(); }; - - @undoBatch - @action - onUrlDrop = (e: React.DragEvent) => { - const { dataTransfer } = e; - const html = dataTransfer.getData("text/html"); - const uri = dataTransfer.getData("text/uri-list"); - const url = uri || html || this._url; - this._url = url.startsWith(window.location.origin) ? - url.replace(window.location.origin, this._url.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url; - this.submitURL(); - e.stopPropagation(); - } - @action forward = () => { const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), null); @@ -218,7 +208,9 @@ export class WebBox extends ViewBoxAnnotatableComponent(DocListCast(this.dataDoc[this.annotationKey])); this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!)); this.dataDoc[this.annotationKey] = new List(DocListCast(this.dataDoc[this.annotationKey + "-" + this.urlHash(this._url)])); + return true; } + return false; } @action @@ -231,7 +223,9 @@ export class WebBox extends ViewBoxAnnotatableComponent(DocListCast(this.dataDoc[this.annotationKey])); this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!)); this.dataDoc[this.annotationKey] = new List(DocListCast(this.dataDoc[this.annotationKey + "-" + this.urlHash(this._url)])); + return true; } + return false; } urlHash(s: string) { @@ -239,10 +233,9 @@ export class WebBox extends ViewBoxAnnotatableComponent { - if (!this._url.startsWith("http")) this._url = "http://" + this._url; + submitURL = (newUrl: string) => { + if (!newUrl.startsWith("http")) newUrl = "http://" + newUrl; try { - const URLy = new URL(this._url); const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), null); const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), null); const annos = DocListCast(this.dataDoc[this.annotationKey]); @@ -250,25 +243,21 @@ export class WebBox extends ViewBoxAnnotatableComponent([url]); - } else { history.push(url); } + this.layoutDoc._scrollTop = 0; future && (future.length = 0); this.dataDoc[this.annotationKey + "-" + this.urlHash(url)] = new List(annos); } - this.dataDoc[this.fieldKey] = new WebField(URLy); + this._url = newUrl; + this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl)); this.dataDoc[this.annotationKey] = new List([]); } catch (e) { console.log("WebBox URL error:" + this._url); } } - onValueKeyDown = async (e: React.KeyboardEvent) => { - if (e.key === "Enter") this.submitURL(); - e.stopPropagation(); - } - editToggleBtn() { return {`${this.props.Document.isAnnotating ? "Exit" : "Enter"} annotation mode`}
}>
Date: Wed, 3 Feb 2021 12:08:27 -0500 Subject: added linkRelationship to linkEditor. fixed warnings. --- src/client/views/GestureOverlay.tsx | 18 +---- src/client/views/collections/CollectionMenu.tsx | 11 ++- src/client/views/linking/LinkEditor.tsx | 84 +++++++++++++++------- src/client/views/nodes/WebBox.tsx | 1 + .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- 5 files changed, 71 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 0cf958681..ae27e78d5 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -32,10 +32,6 @@ export class GestureOverlay extends Touchable { @observable public InkShape: string = ""; @observable public SavedColor?: string; @observable public SavedWidth?: string; - @observable public SavedFill?: string; - @observable public SavedArrowStart: string = "none"; - @observable public SavedArrowEnd: string = "none"; - @observable public SavedDash: String = "0"; @observable public Tool: ToolglassTools = ToolglassTools.None; @observable private _thumbX?: number; @@ -653,15 +649,7 @@ export class GestureOverlay extends Touchable { } else { this._points = []; } - //get out of ink mode after each stroke= - if (CollectionFreeFormViewChrome.Instance && !CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) { - Doc.SetSelectedTool(InkTool.None); - CollectionFreeFormViewChrome.Instance._selectedPrimitive = CollectionFreeFormViewChrome.Instance._shapesPrimNum; - SetActiveArrowStart("none"); - GestureOverlay.Instance.SavedArrowStart = ActiveArrowStart(); - SetActiveArrowEnd("none"); - GestureOverlay.Instance.SavedArrowEnd = ActiveArrowEnd(); - } + CollectionFreeFormViewChrome.Instance.primCreated(); } makePolygon = (shape: string, gesture: boolean) => { @@ -969,13 +957,9 @@ Scripting.addGlobal(function setPen(width: any, color: any, fill: any, arrowStar SetActiveInkColor(color); GestureOverlay.Instance.SavedWidth = ActiveInkWidth(); SetActiveInkWidth(width); - GestureOverlay.Instance.SavedFill = ActiveFillColor(); SetActiveFillColor(fill); - GestureOverlay.Instance.SavedArrowStart = ActiveArrowStart(); SetActiveArrowStart(arrowStart); - GestureOverlay.Instance.SavedArrowEnd = ActiveArrowEnd(); SetActiveArrowStart(arrowEnd); - GestureOverlay.Instance.SavedDash = ActiveDash(); SetActiveDash(dash); }); }); diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 4e2d81e61..7ba857449 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -25,7 +25,7 @@ import { undoBatch } from "../../util/UndoManager"; import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu"; import { EditableView } from "../EditableView"; import { GestureOverlay } from "../GestureOverlay"; -import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke"; +import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, ActiveArrowStart, ActiveArrowEnd } from "../InkingStroke"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocumentView } from "../nodes/DocumentView"; import { RichTextMenu } from "../nodes/formattedText/RichTextMenu"; @@ -591,12 +591,19 @@ export class CollectionFreeFormViewChrome extends React.Component { diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 435b9d904..f74b422d3 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -4,7 +4,6 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../fields/Doc"; import { DateCast, StrCast } from "../../../fields/Types"; -import { Utils } from "../../../Utils"; import { LinkManager } from "../../util/LinkManager"; import { undoBatch } from "../../util/UndoManager"; import './LinkEditor.scss'; @@ -21,31 +20,41 @@ interface LinkEditorProps { export class LinkEditor extends React.Component { @observable description = StrCast(LinkManager.currentLink?.description); + @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship); @observable openDropdown: boolean = false; @observable showInfo: boolean = false; @computed get infoIcon() { if (this.showInfo) { return "chevron-up"; } return "chevron-down"; } @observable private buttonColor: string = ""; - + @observable private relationshipButtonColor: string = ""; //@observable description = this.props.linkDoc.description ? StrCast(this.props.linkDoc.description) : "DESCRIPTION"; - @undoBatch @action + @undoBatch deleteLink = (): void => { LinkManager.Instance.deleteLink(this.props.linkDoc); this.props.showLinks(); } - @undoBatch @action - setDescripValue = (value: string) => { + @undoBatch + setRelationshipValue = action((value: string) => { + if (LinkManager.currentLink) { + LinkManager.currentLink.linkRelationship = value; + this.relationshipButtonColor = "rgb(62, 133, 55)"; + setTimeout(action(() => this.relationshipButtonColor = ""), 750); + return true; + } + }); + + @undoBatch + setDescripValue = action((value: string) => { if (LinkManager.currentLink) { LinkManager.currentLink.description = value; this.buttonColor = "rgb(62, 133, 55)"; setTimeout(action(() => this.buttonColor = ""), 750); return true; } - } + }); - @action onKey = (e: React.KeyboardEvent) => { if (e.key === "Enter") { this.setDescripValue(this.description); @@ -53,22 +62,48 @@ export class LinkEditor extends React.Component { } } - @action - onDown = () => { - this.setDescripValue(this.description); + onRelationshipKey = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + this.setRelationshipValue(this.relationship); + document.getElementById('input')?.blur(); + } } + onDown = () => this.setDescripValue(this.description); + onRelationshipDown = () => this.setRelationshipValue(this.description); + @action - handleChange = (e: React.ChangeEvent) => { - this.description = e.target.value; - } + handleChange = (e: React.ChangeEvent) => { this.description = e.target.value; } + @action + handleRelationshipChange = (e: React.ChangeEvent) => { this.relationship = e.target.value; } + @computed + get editRelationship() { + return
+
Link Relationship:
+
+
+ +
+
Set
+
+
; + } @computed get editDescription() { return
-
- Link Label:
+
Link Description:
{
Set
-
; +
+
; } @action - changeDropdown = () => { - this.openDropdown = !this.openDropdown; - } + changeDropdown = () => { this.openDropdown = !this.openDropdown; } @undoBatch changeFollowBehavior = action((follow: string) => { @@ -101,8 +135,7 @@ export class LinkEditor extends React.Component { @computed get followingDropdown() { return
-
- Follow Behavior:
+
Follow Behavior:
@@ -157,9 +190,7 @@ export class LinkEditor extends React.Component { } @action - changeInfo = () => { - this.showInfo = !this.showInfo; - } + changeInfo = () => { this.showInfo = !this.showInfo; } render() { const destination = LinkManager.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); @@ -186,8 +217,9 @@ export class LinkEditor extends React.Component { {DateCast(this.props.linkDoc.creationDate).toString()}
: null}
: null} -
{this.editDescription}
-
{this.followingDropdown}
+ {this.editDescription} + {this.editRelationship} + {this.followingDropdown}
); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 438395ab0..352abd1e7 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -256,6 +256,7 @@ export class WebBox extends ViewBoxAnnotatableComponent Opt; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text @@ -947,7 +947,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.tryUpdateHeight(); } } else if (incomingValue?.str) { - selectAll(this._editorView!.state, tx => this._editorView?.dispatch(tx.insertText(incomingValue.str))); + selectAll(this._editorView.state, tx => this._editorView?.dispatch(tx.insertText(incomingValue.str))); } } }, -- cgit v1.2.3-70-g09d2 From 2bc8cda2515c54120e2f22e80071e730d3b1f8f6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 3 Feb 2021 12:44:34 -0500 Subject: fixed updating annotations when navigating on a pdf --- src/client/views/nodes/WebBox.tsx | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 352abd1e7..a3afc96d4 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -36,13 +36,12 @@ const WebDocument = makeInterface(documentSchema); @observer export class WebBox extends ViewBoxAnnotatableComponent(WebDocument) { - private _annotationLayer: React.RefObject = React.createRef(); public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } private _mainCont: React.RefObject = React.createRef(); - private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); private _disposers: { [name: string]: IReactionDisposer } = {}; private _longPressSecondsHack?: NodeJS.Timeout; private _outerRef = React.createRef(); + private _annotationLayer: React.RefObject = React.createRef(); private _iframeIndicatorRef = React.createRef(); private _iframeDragRef = React.createRef(); @observable private _marqueeing: number[] | undefined; @@ -52,11 +51,10 @@ export class WebBox extends ViewBoxAnnotatableComponent = new Dictionary(); - get scrollHeight() { return this.webpage?.scrollHeight || 1000; } get _collapsed() { return StrCast(this.layoutDoc._chromeStatus) !== "enabled"; } set _collapsed(value) { this.layoutDoc._chromeStatus = !value ? "enabled" : "disabled"; } + get scrollHeight() { return this.webpage?.scrollHeight || 1000; } get webpage() { return this._iframe?.contentDocument?.children[0]; } - url = () => this._url; constructor(props: any) { super(props); @@ -64,6 +62,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { @@ -128,7 +127,6 @@ export class WebBox extends ViewBoxAnnotatableComponent void) => this._setPreviewCursor = func; iframeWheel = (e: any) => { if (this._forceSmoothScrollUpdate && e.target?.children) { this.webpage && setTimeout(action(() => { @@ -151,7 +149,6 @@ export class WebBox extends ViewBoxAnnotatableComponent this.rootDoc; - async componentDidMount() { this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. @@ -205,9 +202,8 @@ export class WebBox extends ViewBoxAnnotatableComponent(DocListCast(this.dataDoc[this.annotationKey])); this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!)); - this.dataDoc[this.annotationKey] = new List(DocListCast(this.dataDoc[this.annotationKey + "-" + this.urlHash(this._url)])); + this._annotationKey = this.fieldKey + "-annotations-" + this.urlHash(this._url); return true; } return false; @@ -220,9 +216,8 @@ export class WebBox extends ViewBoxAnnotatableComponent([this._url]); else future.push(this._url); - this.dataDoc[this.annotationKey + "-" + this.urlHash(this._url)] = new List(DocListCast(this.dataDoc[this.annotationKey])); this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!)); - this.dataDoc[this.annotationKey] = new List(DocListCast(this.dataDoc[this.annotationKey + "-" + this.urlHash(this._url)])); + this._annotationKey = this.fieldKey + "-annotations-" + this.urlHash(this._url); return true; } return false; @@ -238,7 +233,6 @@ export class WebBox extends ViewBoxAnnotatableComponent(annos); } this._url = newUrl; + this._annotationKey = this.fieldKey + "-annotations-" + this.urlHash(this._url); this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl)); - this.dataDoc[this.annotationKey] = new List([]); } catch (e) { console.log("WebBox URL error:" + this._url); } @@ -270,15 +263,10 @@ export class WebBox extends ViewBoxAnnotatableComponent { this._ignore = e.timeStamp; }; - onPrePointer = (e: React.PointerEvent) => { this._ignore = e.timeStamp; }; - onPostPointer = (e: React.PointerEvent) => { - if (this._ignore !== e.timeStamp) e.stopPropagation(); - } - - onPostWheel = (e: React.WheelEvent) => { - if (this._ignore !== e.timeStamp) e.stopPropagation(); - } + onPreWheel = (e: React.WheelEvent) => this._ignore = e.timeStamp; + onPrePointer = (e: React.PointerEvent) => this._ignore = e.timeStamp; + onPostPointer = (e: React.PointerEvent) => this._ignore !== e.timeStamp && e.stopPropagation(); + onPostWheel = (e: React.WheelEvent) => this._ignore !== e.timeStamp && e.stopPropagation(); onLongPressDown = (e: React.PointerEvent) => { this._pressX = e.clientX; @@ -389,8 +377,6 @@ export class WebBox extends ViewBoxAnnotatableComponent -- cgit v1.2.3-70-g09d2 From b3522a13641747a96cba9ada09ad74abed3faa41 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 3 Feb 2021 12:55:21 -0500 Subject: made link following in lightbox stay in lightbox. --- src/client/util/LinkManager.ts | 17 ++++++++++++----- src/client/views/linking/LinkMenu.scss | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index bf927c5b8..3dabe0ec0 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -7,6 +7,8 @@ import { LinkDocPreview } from "../views/nodes/LinkDocPreview"; import { CreateViewFunc, DocumentManager } from "./DocumentManager"; import { SharingManager } from "./SharingManager"; import { UndoManager } from "./UndoManager"; +import { MainView } from "../views/MainView"; +import { runInAction } from "mobx"; /* * link doc: @@ -145,11 +147,16 @@ export class LinkManager { doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") : (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number"))); if (target) { - const containerDoc = (await Cast(target.annotationOn, Doc)) || target; - containerDoc._currentTimecode = targetTimecode; - const targetContext = await target?.context as Doc; - const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; - DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished); + if (MainView.Instance.LightboxDoc) { + runInAction(() => MainView.Instance.LightboxDoc = (target.annotationOn as Doc) ?? target); + finished?.(); + } else { + const containerDoc = (await Cast(target.annotationOn, Doc)) || target; + containerDoc._currentTimecode = targetTimecode; + const targetContext = await target?.context as Doc; + const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; + DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished); + } } else { finished?.(); } diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss index 0e03b46db..57e8838be 100644 --- a/src/client/views/linking/LinkMenu.scss +++ b/src/client/views/linking/LinkMenu.scss @@ -4,7 +4,7 @@ width: auto; height: auto; position: absolute; - z-index: 999; + z-index: 10001; .linkMenu-list { -- cgit v1.2.3-70-g09d2 From 6b15f2498209a896fba50da5ae3085405324991b Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 3 Feb 2021 15:12:44 -0500 Subject: added lightbox nav buttons --- src/client/views/MainView.scss | 20 ++++++++++++++++---- src/client/views/MainView.tsx | 26 ++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index fc375f6a2..971e8f2ac 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -68,16 +68,28 @@ color: black; } - .mainView-lightBoxFrame { + .mainView-lightBox-frame { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #000000bb; z-index: 10000; - } - .mainView-lightBoxContents { - position: absolute; + .mainView-lightBox-contents { + position: absolute; + } + .mainView-lightBox-navBtn { + margin: auto; + position: relative; + background: transparent; + border-radius: 8; + color:white; + opacity: 0.7; + width: 30; + &:hover { + opacity: 1; + } + } } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8598a7303..1f96a26a4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -597,10 +597,13 @@ export class MainView extends React.Component { lightboxWidth = () => window.innerWidth - Math.min(window.innerWidth / 4, 200) * 2; lightboxHeight = () => window.innerHeight - Math.min(window.innerHeight / 4, 100) * 2; lightboxScreenToLocal = () => new Transform(-Math.min(window.innerWidth / 4, 200), -Math.min(window.innerHeight / 4, 100), 1); + lightboxHistory: (Opt)[] = []; + lightboxFuture: (Opt)[] = []; @computed get lightboxView() { + if (this.lightboxHistory.lastElement() !== this.LightboxDoc) this.lightboxHistory.push(this.LightboxDoc); return !this.LightboxDoc ? (null) : -
this.LightboxDoc = undefined)} > -
this.LightboxDoc = undefined)} > +
+
+
{ + e.stopPropagation(); + if (this.lightboxHistory.lastElement() !== this.lightboxFuture.lastElement()) this.lightboxFuture.push(this.lightboxHistory.pop()); + this.LightboxDoc = this.lightboxHistory.lastElement(); + })}> + +
+
+
+
{ + e.stopPropagation(); + this.LightboxDoc = this.lightboxFuture.pop(); + })}> + +
+
; } -- cgit v1.2.3-70-g09d2 From 9ae759a3c612bee4803e3714ef4ff141603a9341 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 3 Feb 2021 16:06:48 -0500 Subject: fixed selecting text in lightbox view. --- src/client/views/MainView.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 1f96a26a4..d72b2b409 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -601,8 +601,17 @@ export class MainView extends React.Component { lightboxFuture: (Opt)[] = []; @computed get lightboxView() { if (this.lightboxHistory.lastElement() !== this.LightboxDoc) this.lightboxHistory.push(this.LightboxDoc); + let downx = 0, downy = 0; return !this.LightboxDoc ? (null) : -
this.LightboxDoc = undefined)} > +
{ downx = e.clientX; downy = e.clientY; }} + onClick={action(e => { + if (Math.abs(downx - e.clientX) < 4 && Math.abs(downy - e.clientY) < 4) { + this.lightboxHistory = []; + this.lightboxFuture = []; + this.LightboxDoc = undefined; + } + })} >
-
+
{ e.stopPropagation(); @@ -643,7 +652,7 @@ export class MainView extends React.Component {
-
+
{ e.stopPropagation(); -- cgit v1.2.3-70-g09d2 From 7c1dd332dc444d2d34cc9cc142de5998ca4622b5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 4 Feb 2021 14:32:38 -0500 Subject: fixed scrolling animations in view bounds for webbox and pdfBox's particualrly for lightbox views. added a docViewPath prop for docuentViews. --- src/client/util/DocumentManager.ts | 19 ++++++++++++---- src/client/util/LinkManager.ts | 2 +- src/client/views/MainView.tsx | 25 +++++++++++++++++++--- .../views/collections/CollectionStackingView.tsx | 2 ++ .../views/collections/CollectionTreeView.tsx | 2 ++ src/client/views/collections/TabDocView.tsx | 5 ++++- .../collectionFreeForm/CollectionFreeFormView.tsx | 9 +++++--- .../views/nodes/CollectionFreeFormDocumentView.tsx | 3 ++- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 9 ++++++-- src/client/views/nodes/WebBox.tsx | 11 ++++++++-- src/client/views/pdf/PDFViewer.tsx | 12 +++++------ 12 files changed, 77 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 41c7a1409..08efdb36c 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,4 +1,4 @@ -import { action, observable } from 'mobx'; +import { action, observable, runInAction } from 'mobx'; import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { Cast, NumCast } from '../../fields/Types'; @@ -9,6 +9,7 @@ import { CollectionView } from '../views/collections/CollectionView'; import { DocumentView } from '../views/nodes/DocumentView'; import { LinkManager } from './LinkManager'; import { Scripting } from './Scripting'; +import { MainView } from '../views/MainView'; export type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void; @@ -116,6 +117,7 @@ export class DocumentManager { // heuristic to return the "best" documents first: // choose an exact match over an alias match // choose documents that have a PanelWidth() over those that don't (the treeview documents have no panelWidth) + docViews.map(view => view.docViewPath.includes(MainView.Instance.lightboxDocView.current!) && view.props.Document === toFind && toReturn.push(view)); docViews.map(view => view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view)); docViews.map(view => view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view)); docViews.map(view => view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view)); @@ -139,7 +141,10 @@ export class DocumentManager { originatingDoc: Opt = undefined, // doc that initiated the display of the target odoc finished?: () => void, ): Promise => { - const getFirstDocView = DocumentManager.Instance.getFirstDocumentView; + const getFirstDocView = (toFind: Doc, originatingDoc?: Doc) => { + const docView = DocumentManager.Instance.getFirstDocumentView(toFind, originatingDoc); + return (!MainView.Instance.LightboxDoc || docView?.docViewPath.includes(MainView.Instance.lightboxDocView.current!)) ? docView : undefined; + } const focusAndFinish = () => { finished?.(); return false; }; const highlight = () => { const finalDocView = getFirstDocView(targetDoc); @@ -214,8 +219,14 @@ export class DocumentManager { findView(0); } } else { // there's no context view so we need to create one first and try again when that finishes - createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target - () => this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished)); + const finishFunc = () => this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished); + if (MainView.Instance.LightboxDoc) { + runInAction(() => MainView.Instance.LightboxDoc = annotatedDoc ? annotatedDoc : targetDoc); + setTimeout(() => finishFunc, 250); + } else { + createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target + finishFunc); + } } } } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 3dabe0ec0..000017ebb 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -147,7 +147,7 @@ export class LinkManager { doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") : (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number"))); if (target) { - if (MainView.Instance.LightboxDoc) { + if (MainView.Instance.LightboxDoc && doc.annotationOn !== MainView.Instance.LightboxDoc) { // following a link should replace an existing lightboxDoc unless the target is an annotation on the lightbox document runInAction(() => MainView.Instance.LightboxDoc = (target.annotationOn as Doc) ?? target); finished?.(); } else { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d72b2b409..9d9698c9c 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -13,7 +13,7 @@ import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { BoolCast, PromiseValue, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils, emptyPath } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; import { Docs } from '../documents/Documents'; @@ -248,6 +248,9 @@ export class MainView extends React.Component { addDocument={undefined} addDocTab={this.addDocTabFunc} pinToPres={emptyFunction} + docViewPath={emptyPath} + layerProvider={undefined} + styleProvider={undefined} rootSelected={returnTrue} removeDocument={undefined} ScreenToLocalTransform={Transform.Identity} @@ -336,6 +339,9 @@ export class MainView extends React.Component { addDocument={undefined} addDocTab={this.addDocTabFunc} pinToPres={emptyFunction} + docViewPath={emptyPath} + layerProvider={undefined} + styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards ? this.DashboardStyleProvider : DefaultStyleProvider} rootSelected={returnTrue} removeDocument={returnFalse} ScreenToLocalTransform={this.mainContainerXf} @@ -344,7 +350,6 @@ export class MainView extends React.Component { renderDepth={0} scriptContext={CollectionDockingView.Instance.props.Document} focus={emptyFunction} - styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards ? this.DashboardStyleProvider : DefaultStyleProvider} parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} @@ -373,8 +378,10 @@ export class MainView extends React.Component { PanelWidth={this.menuPanelWidth} PanelHeight={this.getContentsHeight} renderDepth={0} + docViewPath={emptyPath} focus={emptyFunction} styleProvider={DefaultStyleProvider} + layerProvider={undefined} parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} @@ -476,11 +483,13 @@ export class MainView extends React.Component { dropAction={"alias"} parentActive={returnFalse} styleProvider={DefaultStyleProvider} + layerProvider={undefined} rootSelected={returnTrue} bringToFront={emptyFunction} select={emptyFunction} active={returnFalse} isSelected={returnFalse} + docViewPath={emptyPath} moveDocument={this.moveButtonDoc} CollectionView={undefined} addDocument={this.addButtonDoc} @@ -547,12 +556,14 @@ export class MainView extends React.Component { pinToPres={emptyFunction} rootSelected={returnTrue} styleProvider={DefaultStyleProvider} + layerProvider={undefined} removeDocument={undefined} ScreenToLocalTransform={Transform.Identity} PanelWidth={this.getPWidth} PanelHeight={this.getPHeight} renderDepth={0} focus={emptyFunction} + docViewPath={emptyPath} parentActive={returnFalse} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} @@ -573,6 +584,8 @@ export class MainView extends React.Component { ContainingCollectionDoc={undefined} Document={DocumentLinksButton.invisibleWebDoc} dropAction={"move"} + layerProvider={undefined} + styleProvider={undefined} isSelected={returnFalse} select={returnFalse} rootSelected={returnFalse} @@ -585,6 +598,7 @@ export class MainView extends React.Component { active={returnFalse} whenActiveChanged={returnFalse} focus={returnFalse} + docViewPath={emptyPath} PanelWidth={() => 500} PanelHeight={() => 800} docFilters={returnEmptyFilter} @@ -599,6 +613,7 @@ export class MainView extends React.Component { lightboxScreenToLocal = () => new Transform(-Math.min(window.innerWidth / 4, 200), -Math.min(window.innerHeight / 4, 100), 1); lightboxHistory: (Opt)[] = []; lightboxFuture: (Opt)[] = []; + lightboxDocView = React.createRef(); @computed get lightboxView() { if (this.lightboxHistory.lastElement() !== this.LightboxDoc) this.lightboxHistory.push(this.LightboxDoc); let downx = 0, downy = 0; @@ -618,13 +633,14 @@ export class MainView extends React.Component { width: window.innerWidth - Math.min(window.innerWidth / 4, 200) * 2, height: window.innerHeight - Math.min(window.innerHeight / 4, 100) * 2 }}> - { ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} parentActive={returnFalse} + docViewPath={emptyPath} childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this. noOverlay={true} // don't render overlay Docs since they won't scale active={returnTrue} @@ -334,6 +335,7 @@ export class TabDocView extends React.Component { whenActiveChanged={emptyFunction} focus={emptyFunction} styleProvider={TabDocView.miniStyleProvider} + layerProvider={undefined} addDocTab={this.addDocTab} pinToPres={TabDocView.PinDoc} docFilters={CollectionDockingView.Instance.docFilters} @@ -408,6 +410,7 @@ export class TabDocView extends React.Component { parentActive={this.active} whenActiveChanged={emptyFunction} focus={this.focusFunc} + docViewPath={emptyPath} bringToFront={emptyFunction} pinToPres={TabDocView.PinDoc} /> {this._document._viewType !== CollectionViewType.Freeform ? (null) : diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6c7512f7c..e25a46a5d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -904,13 +904,14 @@ export class CollectionFreeFormView extends CollectionSubView 5 ? 1000 : 0; !dontCenter && this.props.focus(this.props.Document); - afterFocus && setTimeout(afterFocus, delay); + afterFocus && setTimeout(() => afterFocus?.(delay ? true : false), delay); } else { !dontCenter && delay && this.props.focus(this.props.Document); afterFocus?.(!dontCenter && delay ? true : false); @@ -988,6 +989,7 @@ export class CollectionFreeFormView extends CollectionSubView { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined; sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined; - layerProvider?: (doc: Doc, assign?: boolean) => boolean; + layerProvider: ((doc: Doc, assign?: boolean) => boolean) | undefined zIndex?: number; highlight?: boolean; jitterRotation: number; @@ -162,6 +162,7 @@ export class CollectionFreeFormDocumentView extends DocComponent 12 || !layoutFrame || !this.layoutDoc || GetEffectiveAcl(this.layoutDoc) === AclPrivate) ? (null) : CollectionFreeFormDocumentView; PanelWidth: () => number; PanelHeight: () => number; - layerProvider?: (doc: Doc, assign?: boolean) => boolean; - styleProvider?: StyleProviderFunc; + docViewPath: DocumentView[]; + layerProvider: undefined | ((doc: Doc, assign?: boolean) => boolean); + styleProvider: Opt; focus: DocFocusFunc; docFilters: () => string[]; docRangeFilters: () => string[]; @@ -113,6 +114,7 @@ export interface DocumentViewInternalProps extends DocumentViewProps { isSelected: (outsideReaction?: boolean) => boolean; select: (ctrlPressed: boolean) => void; DocumentView: any; + viewPath: DocumentView[]; } @observer @@ -720,6 +722,7 @@ export class DocumentViewInternal extends DocComponent { get allLinks() { return this.docView?.allLinks || []; } get LayoutFieldKey() { return this.docView?.LayoutFieldKey || "layout"; } + @computed get docViewPath() { return this.props.docViewPath ? [...this.props.docViewPath, this] : [this]; } @computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); } @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions)); } @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions) || 0); } @@ -965,6 +969,7 @@ export class DocumentView extends React.Component { const internalProps = { ...this.props, DocumentView: this, + viewPath: this.docViewPath, PanelWidth: this.PanelWidth, PanelHeight: this.PanelHeight, NativeWidth: this.NativeWidth, diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index a3afc96d4..e80a78b87 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -92,12 +92,14 @@ export class WebBox extends ViewBoxAnnotatableComponent this.layoutDoc._scrollY = undefined, duration); + setTimeout(() => this.webpage && smoothScroll(duration, this.webpage as any as HTMLElement, Math.abs(scrollY || 0)), delay); setTimeout(() => this._outerRef.current && smoothScroll(duration, this._outerRef.current, Math.abs(scrollY || 0), () => this.layoutDoc._scrollTop = scrollY), delay); } if (scrollX !== undefined) { this._forceSmoothScrollUpdate = true; - this.layoutDoc._scrollX = undefined; + setTimeout(() => this.layoutDoc._scrollX = undefined, duration); + setTimeout(() => this.webpage && smoothScroll(duration, this.webpage as any as HTMLElement, Math.abs(scrollX || 0)), delay); setTimeout(() => this._outerRef.current && smoothScroll(duration, this._outerRef.current, Math.abs(scrollX || 0), () => this.layoutDoc._scrollLeft = scrollX), delay); } }, @@ -108,6 +110,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this._forceSmoothScrollUpdate = true); } else this._forceSmoothScrollUpdate = true; }, @@ -447,6 +450,8 @@ export class WebBox extends ViewBoxAnnotatableComponent this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); + panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc._scrollLeft), NumCast(this.layoutDoc._scrollTop)); render() { const inactiveLayer = this.props.layerProvider?.(this.layoutDoc) === false; @@ -490,6 +495,8 @@ export class WebBox extends ViewBoxAnnotatableComponent this._mainCont.current && smoothScroll(duration, this._mainCont.current, Math.abs(scrollY || 0)), delay); - setTimeout(() => { this.Document._scrollTop = scrollY; this.Document._scrollY = undefined; }, duration + delay); + setTimeout(() => this.Document._scrollY = undefined, duration + delay); + setTimeout(() => this._mainCont.current && smoothScroll(duration, this._mainCont.current, Math.abs(scrollY || 0), () => this.layoutDoc._scrollTop = scrollY), delay); } } }, @@ -518,8 +518,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent) => this._overlayAnnoInfo = anno); overlayTransform = () => this.scrollXf().scale(1 / this._zoomed); - panelWidth = () => (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); - panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); + panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); + panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); @computed get overlayLayer() { return
Date: Thu, 4 Feb 2021 14:36:00 -0500 Subject: linkMenu appears above docdecorations now. --- src/client/views/linking/LinkMenu.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss index 57e8838be..ebf8878cb 100644 --- a/src/client/views/linking/LinkMenu.scss +++ b/src/client/views/linking/LinkMenu.scss @@ -4,7 +4,7 @@ width: auto; height: auto; position: absolute; - z-index: 10001; + z-index: 20001; .linkMenu-list { -- cgit v1.2.3-70-g09d2 From 0590890a43a6ab4fe141324ad04e5cae59333f31 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 4 Feb 2021 14:44:18 -0500 Subject: fixed back/fwd behavior with lightbox --- src/client/views/MainView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9d9698c9c..5873ce650 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -662,7 +662,8 @@ export class MainView extends React.Component {
{ e.stopPropagation(); - if (this.lightboxHistory.lastElement() !== this.lightboxFuture.lastElement()) this.lightboxFuture.push(this.lightboxHistory.pop()); + const popped = this.lightboxHistory.pop(); + if (this.lightboxHistory.lastElement() !== this.lightboxFuture.lastElement()) this.lightboxFuture.push(popped); this.LightboxDoc = this.lightboxHistory.lastElement(); })}> -- cgit v1.2.3-70-g09d2 From 09e6a1f83de643437d03deac57e1cdebc68d6140 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 4 Feb 2021 16:18:35 -0500 Subject: fixed marquee annotations for web/pdf when scrolled --- src/client/views/MarqueeAnnotator.tsx | 3 ++- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 1 + src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 4ffccbc90..03afbe7bf 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -20,6 +20,7 @@ const _global = (window /* browser */ || global /* node */) as any; export interface MarqueeAnnotatorProps { rootDoc: Doc; down: number[]; + scrollTop: number; scaling?: () => number; containerOffset?: () => number[]; mainCont: HTMLDivElement; @@ -73,7 +74,7 @@ export class MarqueeAnnotator extends React.Component { const containerOffset = this.props.containerOffset?.() || [0, 0]; const mainAnnoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title }); if (anno.style.left) mainAnnoDoc.x = (parseInt(anno.style.left) - containerOffset[0]) / scale; - if (anno.style.top) mainAnnoDoc.y = (parseInt(anno.style.top) - containerOffset[1] + NumCast(this.props.rootDoc._scrollTop)) / scale; + if (anno.style.top) mainAnnoDoc.y = (parseInt(anno.style.top) - containerOffset[1]) / scale + NumCast(this.props.scrollTop); if (anno.style.height) mainAnnoDoc._height = parseInt(anno.style.height) / scale; if (anno.style.width) mainAnnoDoc._width = parseInt(anno.style.width) / scale; mainAnnoDoc.group = mainAnnoDoc; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 92d6e2612..59bc2e733 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -466,7 +466,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent {this.annotationLayer} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : - } + }
); } } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 8a1cefbd9..760620b19 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -574,6 +574,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent {this.annotationLayer} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : - } + }
{this.props.isSelected() ? this.editToggleBtn() : null}
); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index cffa9686b..eadf58027 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -576,7 +576,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent} + }
; } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 8e2d0aee8a577762a49078a4c0233919b5616585 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 4 Feb 2021 20:05:24 -0500 Subject: fixed default node type in text to be paragraph, not audioTag --- src/client/views/nodes/formattedText/nodes_rts.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts index 722c0a836..5d9c8b56d 100644 --- a/src/client/views/nodes/formattedText/nodes_rts.ts +++ b/src/client/views/nodes/formattedText/nodes_rts.ts @@ -22,6 +22,8 @@ export const nodes: { [index: string]: NodeSpec } = { content: "block+" }, + paragraph: ParagraphNodeSpec, + audiotag: { group: "block", attrs: { @@ -64,8 +66,6 @@ export const nodes: { [index: string]: NodeSpec } = { parseDOM: [{ tag: "footnote" }] }, - paragraph: ParagraphNodeSpec, - // :: NodeSpec A blockquote (`
`) wrapping one or more blocks. blockquote: { content: "block*", -- cgit v1.2.3-70-g09d2 From b846e1c28f92bb21063c15fe43c07d98335b4315 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 5 Feb 2021 11:59:13 -0500 Subject: use high res images on select --- src/client/views/nodes/ImageBox.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 59bc2e733..286b3bf5f 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -222,7 +222,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent Date: Fri, 5 Feb 2021 13:52:32 -0500 Subject: cleaned up lightbox. replaced old npm lightbox. --- src/client/util/DocumentManager.ts | 12 +-- src/client/util/LinkManager.ts | 10 +- src/client/views/DocumentDecorations.tsx | 4 +- src/client/views/GestureOverlay.tsx | 4 +- src/client/views/GlobalKeyHandler.ts | 5 +- src/client/views/LightboxView.scss | 26 ++++++ src/client/views/LightboxView.tsx | 103 +++++++++++++++++++++ src/client/views/MainView.scss | 24 ----- src/client/views/MainView.tsx | 82 +--------------- src/client/views/OverlayView.tsx | 4 +- src/client/views/Palette.tsx | 4 +- src/client/views/PropertiesView.tsx | 4 +- src/client/views/TemplateMenu.tsx | 6 +- .../views/collections/CollectionLinearView.tsx | 4 +- src/client/views/collections/CollectionMenu.tsx | 11 ++- .../views/collections/CollectionSchemaView.tsx | 4 +- src/client/views/collections/CollectionView.tsx | 41 +------- src/client/views/collections/SchemaTable.tsx | 6 +- src/client/views/collections/TabDocView.tsx | 5 +- src/client/views/collections/TreeView.tsx | 10 +- .../CollectionMulticolumnView.tsx | 4 +- .../CollectionMultirowView.tsx | 4 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocHolderBox.tsx | 4 + src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FilterBox.tsx | 2 + src/client/views/nodes/KeyValuePair.tsx | 6 +- src/client/views/nodes/LinkDocPreview.tsx | 7 +- .../views/nodes/formattedText/DashDocView.tsx | 4 +- .../formattedText/FormattedTextBoxComment.tsx | 6 +- .../views/nodes/formattedText/RichTextSchema.tsx | 1 + .../views/presentationview/PresElementBox.tsx | 4 +- src/mobile/AudioUpload.tsx | 4 +- src/mobile/MobileInterface.tsx | 2 + 34 files changed, 241 insertions(+), 180 deletions(-) create mode 100644 src/client/views/LightboxView.scss create mode 100644 src/client/views/LightboxView.tsx (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 08efdb36c..7af16ed6e 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -6,10 +6,10 @@ import { returnFalse } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionView } from '../views/collections/CollectionView'; +import { LightboxView } from '../views/LightboxView'; import { DocumentView } from '../views/nodes/DocumentView'; import { LinkManager } from './LinkManager'; import { Scripting } from './Scripting'; -import { MainView } from '../views/MainView'; export type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void; @@ -117,7 +117,7 @@ export class DocumentManager { // heuristic to return the "best" documents first: // choose an exact match over an alias match // choose documents that have a PanelWidth() over those that don't (the treeview documents have no panelWidth) - docViews.map(view => view.docViewPath.includes(MainView.Instance.lightboxDocView.current!) && view.props.Document === toFind && toReturn.push(view)); + docViews.map(view => view.docViewPath.includes(LightboxView.LightboxDocView.current!) && view.props.Document === toFind && toReturn.push(view)); docViews.map(view => view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view)); docViews.map(view => view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view)); docViews.map(view => view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view)); @@ -143,8 +143,8 @@ export class DocumentManager { ): Promise => { const getFirstDocView = (toFind: Doc, originatingDoc?: Doc) => { const docView = DocumentManager.Instance.getFirstDocumentView(toFind, originatingDoc); - return (!MainView.Instance.LightboxDoc || docView?.docViewPath.includes(MainView.Instance.lightboxDocView.current!)) ? docView : undefined; - } + return (!LightboxView.LightboxDoc || docView?.docViewPath.includes(LightboxView.LightboxDocView.current!)) ? docView : undefined; + }; const focusAndFinish = () => { finished?.(); return false; }; const highlight = () => { const finalDocView = getFirstDocView(targetDoc); @@ -220,8 +220,8 @@ export class DocumentManager { } } else { // there's no context view so we need to create one first and try again when that finishes const finishFunc = () => this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished); - if (MainView.Instance.LightboxDoc) { - runInAction(() => MainView.Instance.LightboxDoc = annotatedDoc ? annotatedDoc : targetDoc); + if (LightboxView.LightboxDoc) { + runInAction(() => LightboxView.LightboxDoc = annotatedDoc ? annotatedDoc : targetDoc); setTimeout(() => finishFunc, 250); } else { createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 000017ebb..afc0e76be 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,14 +1,14 @@ +import { runInAction } from "mobx"; import { computedFn } from "mobx-utils"; import { Doc, DocListCast, Opt } from "../../fields/Doc"; import { BoolCast, Cast, StrCast } from "../../fields/Types"; -import { DocFocusFunc, DocumentViewSharedProps } from "../views/nodes/DocumentView"; +import { LightboxView } from "../views/LightboxView"; +import { DocumentViewSharedProps } from "../views/nodes/DocumentView"; import { FormattedTextBoxComment } from "../views/nodes/formattedText/FormattedTextBoxComment"; import { LinkDocPreview } from "../views/nodes/LinkDocPreview"; import { CreateViewFunc, DocumentManager } from "./DocumentManager"; import { SharingManager } from "./SharingManager"; import { UndoManager } from "./UndoManager"; -import { MainView } from "../views/MainView"; -import { runInAction } from "mobx"; /* * link doc: @@ -147,8 +147,8 @@ export class LinkManager { doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") : (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number"))); if (target) { - if (MainView.Instance.LightboxDoc && doc.annotationOn !== MainView.Instance.LightboxDoc) { // following a link should replace an existing lightboxDoc unless the target is an annotation on the lightbox document - runInAction(() => MainView.Instance.LightboxDoc = (target.annotationOn as Doc) ?? target); + if (LightboxView.LightboxDoc && doc.annotationOn !== LightboxView.LightboxDoc) { // following a link should replace an existing lightboxDoc unless the target is an annotation on the lightbox document + runInAction(() => LightboxView.LightboxDoc = (target.annotationOn as Doc) ?? target); finished?.(); } else { const containerDoc = (await Cast(target.annotationOn, Doc)) || target; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 226eef658..0dfa0f8b2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -23,10 +23,10 @@ import { DocumentButtonBar } from './DocumentButtonBar'; import './DocumentDecorations.scss'; import { KeyManager } from './GlobalKeyHandler'; import { InkStrokeProperties } from './InkStrokeProperties'; +import { LightboxView } from './LightboxView'; import { DocumentView } from "./nodes/DocumentView"; import React = require("react"); import e = require('express'); -import { MainView } from './MainView'; @observer export class DocumentDecorations extends React.Component<{ boundsLeft: number, boundsTop: number }, { value: string }> { @@ -175,7 +175,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b } else if (e.altKey) { // open same document in new tab CollectionDockingView.ToggleSplit(Cast(selectedDocs[0].props.Document._fullScreenView, Doc, null) || selectedDocs[0].props.Document, "right"); } else { - runInAction(() => MainView.Instance.LightboxDoc = selectedDocs[0].props.Document); + runInAction(() => LightboxView.LightboxDoc = selectedDocs[0].props.Document); } } } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index ae27e78d5..76cb0112e 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -8,7 +8,7 @@ import { Cast, FieldValue, NumCast } from "../../fields/Types"; import MobileInkOverlay from "../../mobile/MobileInkOverlay"; import { GestureUtils } from "../../pen-gestures/GestureUtils"; import { MobileInkOverlayContent } from "../../server/Message"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from "../../Utils"; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, emptyPath } from "../../Utils"; import { CognitiveServices } from "../cognitive_services/CognitiveServices"; import { DocUtils } from "../documents/Documents"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; @@ -886,6 +886,8 @@ export class GestureOverlay extends Touchable { PanelHeight={this.return300} renderDepth={0} styleProvider={returnEmptyString} + layerProvider={undefined} + docViewPath={emptyPath} focus={emptyFunction} parentActive={returnTrue} whenActiveChanged={emptyFunction} diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 78905bfb2..e70dea0f6 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -19,12 +19,13 @@ import { SnappingManager } from "../util/SnappingManager"; import { undoBatch, UndoManager } from "../util/UndoManager"; import { CollectionDockingView } from "./collections/CollectionDockingView"; import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu"; +import { CollectionStackedTimeline } from "./collections/CollectionStackedTimeline"; import { ContextMenu } from "./ContextMenu"; import { DocumentDecorations } from "./DocumentDecorations"; import { InkStrokeProperties } from "./InkStrokeProperties"; +import { LightboxView } from "./LightboxView"; import { MainView } from "./MainView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; -import { CollectionStackedTimeline } from "./collections/CollectionStackedTimeline"; import { AnchorMenu } from "./pdf/AnchorMenu"; import { SearchBox } from "./search/SearchBox"; @@ -114,7 +115,7 @@ export class KeyManager { DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = false); - MainView.Instance.LightboxDoc = undefined; + LightboxView.LightboxDoc = undefined; Doc.SetSelectedTool(InkTool.None); var doDeselect = true; diff --git a/src/client/views/LightboxView.scss b/src/client/views/LightboxView.scss new file mode 100644 index 000000000..9847966bf --- /dev/null +++ b/src/client/views/LightboxView.scss @@ -0,0 +1,26 @@ +.lightboxView-frame { + position: absolute; + top: 0; left: 0; + width: 100%; + height: 100%; + background: #000000bb; + z-index: 10000; + .lightboxView-contents { + position: absolute; + } + .lightboxView-navBtn-frame { + position: absolute; + .lightboxView-navBtn { + margin: auto; + position: relative; + background: transparent; + border-radius: 8; + color:white; + opacity: 0.7; + width: 30; + &:hover { + opacity: 1; + } + } + } +} \ No newline at end of file diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx new file mode 100644 index 000000000..263117437 --- /dev/null +++ b/src/client/views/LightboxView.tsx @@ -0,0 +1,103 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import "normalize.css"; +import * as React from 'react'; +import { Doc, Opt } from '../../fields/Doc'; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils'; +import { Transform } from '../util/Transform'; +import "./LightboxView.scss"; +import { DocumentView } from './nodes/DocumentView'; +import { DefaultStyleProvider } from './StyleProvider'; + +interface LightboxViewProps { + PanelWidth: number; + PanelHeight: number; + minBorder: number[]; +} + +@observer +export class LightboxView extends React.Component { + @observable public static LightboxDoc: Opt; + public static LightboxHistory: (Opt)[] = []; + public static LightboxFuture: (Opt)[] = []; + public static LightboxDocView = React.createRef(); + public LightboxDocView = React.createRef(); + lightboxWidth = () => this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.minBorder[0]) * 2; + lightboxHeight = () => this.props.PanelHeight - Math.min(this.props.PanelHeight / 4, this.props.minBorder[1]) * 2; + lightboxScreenToLocal = () => new Transform(-Math.min(this.props.PanelWidth / 4, this.props.minBorder[0]), -Math.min(this.props.PanelHeight / 4, this.props.minBorder[1]), 1); + navBtn = (left: Opt, icon: string, display: () => string, click: (e: React.MouseEvent) => void) => { + return
+
+ +
+
; + } + + render() { + if (LightboxView.LightboxHistory.lastElement() !== LightboxView.LightboxDoc) LightboxView.LightboxHistory.push(LightboxView.LightboxDoc); + let downx = 0, downy = 0; + return !LightboxView.LightboxDoc ? (null) : +
{ downx = e.clientX; downy = e.clientY; }} + onClick={action(e => { + if (Math.abs(downx - e.clientX) < 4 && Math.abs(downy - e.clientY) < 4) { + LightboxView.LightboxHistory = []; + LightboxView.LightboxFuture = []; + LightboxView.LightboxDoc = undefined; + } + })} > +
+ +
+ {this.navBtn(undefined, "chevron-left", + () => LightboxView.LightboxDoc && LightboxView.LightboxHistory.length ? "" : "none", + action(e => { + e.stopPropagation(); + const popped = LightboxView.LightboxHistory.pop(); + if (LightboxView.LightboxHistory.lastElement() !== LightboxView.LightboxFuture.lastElement()) LightboxView.LightboxFuture.push(popped); + LightboxView.LightboxDoc = LightboxView.LightboxHistory.lastElement(); + }))} + {this.navBtn(this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.minBorder[0]), "chevron-right", + () => LightboxView.LightboxDoc && LightboxView.LightboxFuture.length ? "" : "none", + action(e => { + e.stopPropagation(); + LightboxView.LightboxDoc = LightboxView.LightboxFuture.pop(); + }))} + +
; + } +} \ No newline at end of file diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 971e8f2ac..8ccb64744 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -67,30 +67,6 @@ background: #cacaca; color: black; } - - .mainView-lightBox-frame { - position: absolute; - top: 0; left: 0; - width: 100%; - height: 100%; - background: #000000bb; - z-index: 10000; - .mainView-lightBox-contents { - position: absolute; - } - .mainView-lightBox-navBtn { - margin: auto; - position: relative; - background: transparent; - border-radius: 8; - color:white; - opacity: 0.7; - width: 30; - &:hover { - opacity: 1; - } - } - } } .mainView-container-dark { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5873ce650..b3f5cc54f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -13,7 +13,7 @@ import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { BoolCast, PromiseValue, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils, emptyPath } from '../../Utils'; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; import { Docs } from '../documents/Documents'; @@ -42,6 +42,7 @@ import { GestureOverlay } from './GestureOverlay'; import { MENU_PANEL_WIDTH, SEARCH_PANEL_HEIGHT } from './globalCssVariables.scss'; import { KeyManager } from './GlobalKeyHandler'; import { InkStrokeProperties } from './InkStrokeProperties'; +import { LightboxView } from './LightboxView'; import { LinkMenu } from './linking/LinkMenu'; import "./MainView.scss"; import { AudioBox } from './nodes/AudioBox'; @@ -59,7 +60,7 @@ import { AnchorMenu } from './pdf/AnchorMenu'; import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { SearchBox } from './search/SearchBox'; -import { DefaultStyleProvider, StyleProp, DefaultLayerProvider } from './StyleProvider'; +import { DefaultStyleProvider, StyleProp } from './StyleProvider'; const _global = (window /* browser */ || global /* node */) as any; @observer @@ -68,7 +69,6 @@ export class MainView extends React.Component { private _docBtnRef = React.createRef(); private _mainViewRef = React.createRef(); @observable public LastButton: Opt; - @observable public LightboxDoc: Opt; @observable private _panelWidth: number = 0; @observable private _panelHeight: number = 0; @observable private _panelContent: string = "none"; @@ -608,80 +608,6 @@ export class MainView extends React.Component {
; } - lightboxWidth = () => window.innerWidth - Math.min(window.innerWidth / 4, 200) * 2; - lightboxHeight = () => window.innerHeight - Math.min(window.innerHeight / 4, 100) * 2; - lightboxScreenToLocal = () => new Transform(-Math.min(window.innerWidth / 4, 200), -Math.min(window.innerHeight / 4, 100), 1); - lightboxHistory: (Opt)[] = []; - lightboxFuture: (Opt)[] = []; - lightboxDocView = React.createRef(); - @computed get lightboxView() { - if (this.lightboxHistory.lastElement() !== this.LightboxDoc) this.lightboxHistory.push(this.LightboxDoc); - let downx = 0, downy = 0; - return !this.LightboxDoc ? (null) : -
{ downx = e.clientX; downy = e.clientY; }} - onClick={action(e => { - if (Math.abs(downx - e.clientX) < 4 && Math.abs(downy - e.clientY) < 4) { - this.lightboxHistory = []; - this.lightboxFuture = []; - this.LightboxDoc = undefined; - } - })} > -
- -
-
-
{ - e.stopPropagation(); - const popped = this.lightboxHistory.pop(); - if (this.lightboxHistory.lastElement() !== this.lightboxFuture.lastElement()) this.lightboxFuture.push(popped); - this.LightboxDoc = this.lightboxHistory.lastElement(); - })}> - -
-
-
-
{ - e.stopPropagation(); - this.LightboxDoc = this.lightboxFuture.pop(); - })}> - -
-
-
; - } - - render() { return (
((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)} ref={this._mainViewRef}> {this.inkResources} @@ -710,7 +636,7 @@ export class MainView extends React.Component { {this.snapLines}
- {this.lightboxView} +
); } diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 78053be92..628003391 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -5,7 +5,7 @@ import ReactLoading from 'react-loading'; import { Doc } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { Cast, NumCast } from "../../fields/Types"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from "../../Utils"; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils, emptyPath } from "../../Utils"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { DragManager } from "../util/DragManager"; import { Scripting } from "../util/Scripting"; @@ -193,6 +193,8 @@ export class OverlayView extends React.Component { whenActiveChanged={emptyFunction} focus={emptyFunction} styleProvider={DefaultStyleProvider} + layerProvider={undefined} + docViewPath={emptyPath} addDocTab={returnFalse} pinToPres={emptyFunction} docFilters={returnEmptyFilter} diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index d181c7651..6268be31c 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -3,7 +3,7 @@ import { observer } from "mobx-react"; import * as React from "react"; import { Doc } from "../../fields/Doc"; import { NumCast } from "../../fields/Types"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue } from "../../Utils"; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, emptyPath } from "../../Utils"; import { Transform } from "../util/Transform"; import { DocumentView } from "./nodes/DocumentView"; import "./Palette.scss"; @@ -50,7 +50,9 @@ export default class Palette extends React.Component { PanelHeight={() => window.screen.height} renderDepth={0} focus={emptyFunction} + docViewPath={emptyPath} styleProvider={returnEmptyString} + layerProvider={undefined} parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 4aeb4e63a..a5ddf8044 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -11,7 +11,7 @@ import { InkField } from "../../fields/InkField"; import { ComputedField } from "../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../fields/Types"; import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from "../../fields/util"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse } from "../../Utils"; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, emptyPath } from "../../Utils"; import { DocumentType } from "../documents/DocumentTypes"; import { DocumentManager } from "../util/DocumentManager"; import { SelectionManager } from "../util/SelectionManager"; @@ -270,6 +270,8 @@ export class PropertiesView extends React.Component { renderDepth={1} rootSelected={returnFalse} styleProvider={DefaultStyleProvider} + layerProvider={undefined} + docViewPath={emptyPath} freezeDimensions={true} dontCenter={"y"} NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined} diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 841e8aef9..f9aae64f1 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -5,13 +5,14 @@ import { List } from "../../fields/List"; import { ScriptField } from "../../fields/ScriptField"; import { Cast, StrCast } from "../../fields/Types"; import { TraceMobx } from "../../fields/util"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils"; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils"; import { Docs, DocUtils } from "../documents/Documents"; import { Scripting } from "../util/Scripting"; import { Transform } from "../util/Transform"; import { undoBatch } from "../util/UndoManager"; import { CollectionTreeView } from "./collections/CollectionTreeView"; import { DocumentView } from "./nodes/DocumentView"; +import { DefaultStyleProvider } from "./StyleProvider"; import './TemplateMenu.scss'; import React = require("react"); @@ -130,6 +131,9 @@ export class TemplateMenu extends React.Component { CollectionView={undefined} ContainingCollectionDoc={undefined} ContainingCollectionView={undefined} + styleProvider={DefaultStyleProvider} + layerProvider={undefined} + docViewPath={emptyPath} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index 756346356..79cc1719c 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -7,7 +7,7 @@ import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { makeInterface } from '../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, returnTrue, Utils } from '../../../Utils'; +import { emptyFunction, returnTrue, Utils, emptyPath } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { Transform } from '../../util/Transform'; import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; @@ -152,6 +152,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { renderDepth={this.props.renderDepth + 1} focus={emptyFunction} styleProvider={this.props.styleProvider} + layerProvider={this.props.layerProvider} + docViewPath={emptyPath} parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 7ba857449..3f0b8eb3e 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -33,6 +33,7 @@ import { PresBox } from "../nodes/PresBox"; import "./CollectionMenu.scss"; import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView"; import { TabDocView } from "./TabDocView"; +import { LightboxView } from "../LightboxView"; @observer export class CollectionMenu extends AntimodeMenu { @@ -487,8 +488,14 @@ export class CollectionViewBaseChrome extends React.Component{"Show Lightbox of Images"}
} placement="top"> -
} placement="top"> + ; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index c39f8b255..e246dc020 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -11,7 +11,7 @@ import { listSpec } from "../../../fields/Schema"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { Cast, NumCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils"; +import { emptyFunction, emptyPath, returnFalse, setupMoveUpEvents } from "../../../Utils"; import { SelectionManager } from "../../util/SelectionManager"; import { SnappingManager } from "../../util/SnappingManager"; import { Transform } from "../../util/Transform"; @@ -414,6 +414,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { docRangeFilters={this.docRangeFilters} searchFilterDocs={this.searchFilterDocs} styleProvider={DefaultStyleProvider} + layerProvider={undefined} + docViewPath={emptyPath} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} moveDocument={this.props.moveDocument} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 03d8606d7..9ae469930 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,17 +1,16 @@ import { action, computed, observable } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; -import Lightbox from 'react-image-lightbox-with-rotate'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app import { DateField } from '../../../fields/DateField'; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, Field } from '../../../fields/Doc'; +import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; -import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types'; -import { ImageField } from '../../../fields/URLField'; +import { ScriptField } from '../../../fields/ScriptField'; +import { Cast, ScriptCast, StrCast } from '../../../fields/Types'; import { denormalizeEmail, distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; -import { returnFalse, Utils } from '../../../Utils'; +import { returnFalse } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; @@ -38,7 +37,6 @@ import { SubCollectionViewProps } from './CollectionSubView'; import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; import './CollectionView.scss'; -import { ScriptField } from '../../../fields/ScriptField'; export const COLLECTION_BORDER_WIDTH = 2; const path = require('path'); @@ -85,8 +83,6 @@ export class CollectionView extends Touchable { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } _isChildActive = false; //TODO should this be observable? - get _isLightboxOpen() { return BoolCast(this.props.Document._isLightboxOpen); } - set _isLightboxOpen(value) { this.props.Document._isLightboxOpen = value; } @observable private _curLightboxImg = 0; @observable private static _safeMode = false; public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; } @@ -280,7 +276,6 @@ export class CollectionView extends Touchable { !Doc.UserDoc().noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" }); !Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" }); subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" }); - subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" }); if (!Doc.IsSystem(this.props.Document) && !this.props.Document.annotationOn) { const existingVm = ContextMenu.Instance.findByDescription(category); @@ -342,27 +337,6 @@ export class CollectionView extends Touchable { } } - lightbox = (images: { image: string, title: string, caption: string }[]) => { - if (!images.length) return (null); - const mainPath = path.extname(images[this._curLightboxImg].image); - const nextPath = path.extname(images[(this._curLightboxImg + 1) % images.length].image); - const prevPath = path.extname(images[(this._curLightboxImg + images.length - 1) % images.length].image); - const main = images[this._curLightboxImg].image.replace(mainPath, "_o" + mainPath); - const title = images[this._curLightboxImg].title; - const caption = images[this._curLightboxImg].caption; - const next = images[(this._curLightboxImg + 1) % images.length].image.replace(nextPath, "_o" + nextPath); - const prev = images[(this._curLightboxImg + images.length - 1) % images.length].image.replace(prevPath, "_o" + prevPath); - return !this._isLightboxOpen ? (null) : ( this._isLightboxOpen = false)} - onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)} - onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />); - } - bodyPanelWidth = () => this.props.PanelWidth(); childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); @@ -389,13 +363,6 @@ export class CollectionView extends Touchable { style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined }}> {this.showIsTagged()} {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)} - {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => Cast(d.data, ImageField, null)).map(d => - ({ - image: (Cast(d.data, ImageField)!.url.href.indexOf(window.location.origin) === -1) ? - Utils.CorsProxy(Cast(d.data, ImageField)!.url.href) : Cast(d.data, ImageField)!.url.href, - title: StrCast(d.title), - caption: Field.toString(d.caption as Field) - })))}
); } } diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index d77f70607..3c83c241b 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -15,7 +15,7 @@ import { ComputedField } from "../../../fields/ScriptField"; import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types"; import { ImageField } from "../../../fields/URLField"; import { GetEffectiveAcl } from "../../../fields/util"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse } from "../../../Utils"; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse } from "../../../Utils"; import { Docs, DocumentOptions } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; import { CompileScript, Transformer, ts } from "../../util/Scripting"; @@ -25,6 +25,7 @@ import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/globa import { ContextMenu } from "../ContextMenu"; import '../DocumentDecorations.scss'; import { DocumentView } from "../nodes/DocumentView"; +import { DefaultStyleProvider } from "../StyleProvider"; import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells"; import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders"; import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC"; @@ -570,6 +571,9 @@ export class SchemaTable extends React.Component { ref="overlay"> { case "close": return CollectionDockingView.CloseSplit(doc, locationParams); case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); + case "lightbox": return runInAction(() => LightboxView.LightboxDoc = doc) ? true : false; case "inPlace": case "add": - default: runInAction(() => MainView.Instance.LightboxDoc = doc); - if (MainView.Instance.LightboxDoc) return true; + default: return CollectionDockingView.AddSplit(doc, locationParams, this.stack); } } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index c4934cf45..5deeb6997 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -1,7 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss'; import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -10,7 +9,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; @@ -21,17 +20,18 @@ import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { EditableView } from "../EditableView"; +import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss'; import { DocumentView, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; import { KeyValueBox } from '../nodes/KeyValueBox'; +import { SliderBox } from '../nodes/SliderBox'; import { StyleProp, testDocProps } from '../StyleProvider'; import { CollectionTreeView } from './CollectionTreeView'; import { CollectionView, CollectionViewType } from './CollectionView'; import "./TreeView.scss"; import React = require("react"); -import { SliderBox } from '../nodes/SliderBox'; export interface TreeViewProps { document: Doc; @@ -549,6 +549,8 @@ export class TreeView extends React.Component { Document={this.doc} DataDoc={undefined} styleProvider={this.titleStyleProvider} + layerProvider={undefined} + docViewPath={emptyPath} treeViewDoc={this.props.treeView.props.Document} addDocument={undefined} addDocTab={this.props.addDocTab} @@ -639,6 +641,8 @@ export class TreeView extends React.Component { renderDepth={this.props.renderDepth + 1} rootSelected={returnTrue} styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider} + layerProvider={undefined} + docViewPath={emptyPath} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 85013b960..3d9673438 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -6,7 +6,7 @@ import { documentSchema } from '../../../../fields/documentSchemas'; import { List } from '../../../../fields/List'; import { makeInterface } from '../../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { returnFalse } from '../../../../Utils'; +import { returnFalse, emptyPath } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; @@ -217,6 +217,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu Document={layout} DataDoc={layout.resolvedDataDoc as Doc} styleProvider={this.props.styleProvider} + layerProvider={undefined} + docViewPath={emptyPath} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} freezeDimensions={this.props.childFreezeDimensions} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 4f5c8af95..278ca20c1 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -6,7 +6,7 @@ import { documentSchema } from '../../../../fields/documentSchemas'; import { List } from '../../../../fields/List'; import { makeInterface } from '../../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { returnFalse } from '../../../../Utils'; +import { returnFalse, emptyPath } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; @@ -217,6 +217,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) Document={layout} DataDoc={layout.resolvedDataDoc as Doc} styleProvider={this.props.styleProvider} + layerProvider={undefined} + docViewPath={emptyPath} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} freezeDimensions={this.props.childFreezeDimensions} diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index d2dab4157..4b0422ed3 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -23,7 +23,7 @@ import React = require("react"); export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined; sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined; - layerProvider: ((doc: Doc, assign?: boolean) => boolean) | undefined + layerProvider: ((doc: Doc, assign?: boolean) => boolean) | undefined; zIndex?: number; highlight?: boolean; jitterRotation: number; diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx index 533a4931a..765751a65 100644 --- a/src/client/views/nodes/DocHolderBox.tsx +++ b/src/client/views/nodes/DocHolderBox.tsx @@ -124,6 +124,8 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent { const fullScreenDoc = Cast(this.props.Document._fullScreenView, Doc, null) || this.props.Document; - this.props.addDocTab(fullScreenDoc, "add"); + this.props.addDocTab(fullScreenDoc, "lightbox"); }, "double tap"); SelectionManager.DeselectAll(); } diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index 71196ea19..cc924ff7a 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -211,6 +211,8 @@ export class FilterBox extends ViewBoxBaseComponent { docFilters: returnEmptyFilter, docRangeFilters: returnEmptyFilter, searchFilterDocs: returnEmptyDoclist, + styleProvider: DefaultStyleProvider, + layerProvider: undefined, + docViewPath: emptyPath, ContainingCollectionView: undefined, ContainingCollectionDoc: undefined, fieldKey: this.props.keyName, diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 7934dba8e..75ca6059e 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -4,7 +4,7 @@ import wiki from "wikijs"; import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { Cast, FieldValue, NumCast } from "../../../fields/Types"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse } from "../../../Utils"; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, emptyPath } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { LinkManager } from '../../util/LinkManager'; import { Transform } from "../../util/Transform"; @@ -111,7 +111,10 @@ export class LinkDocPreview extends React.Component { focus={emptyFunction} whenActiveChanged={returnFalse} bringToFront={returnFalse} - styleProvider={this.props.docprops?.styleProvider} />; + styleProvider={this.props.docprops?.styleProvider} + layerProvider={this.props.docprops?.layerProvider} + docViewPath={emptyPath} + />; } render() { diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index 1fbd3af5c..91d123efe 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -236,7 +236,9 @@ export class DashDocView extends React.Component { PanelWidth={finalLayout[WidthSym]} PanelHeight={finalLayout[HeightSym]} focus={this.outerFocus} - styleProvider={returnEmptyString} + styleProvider={self._textBox.props.styleProvider} + layerProvider={self._textBox.props.layerProvider} + docViewPath={self._textBox.props.docViewPath} parentActive={returnFalse} whenActiveChanged={returnFalse} bringToFront={emptyFunction} diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 5371bd10a..9508f8034 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -8,7 +8,7 @@ import * as ReactDOM from 'react-dom'; import wiki from "wikijs"; import { Doc, DocCastAsync, DocListCast, Opt } from "../../../../fields/Doc"; import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, Utils } from "../../../../Utils"; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, Utils, emptyPath } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { Docs } from "../../../documents/Documents"; import { DocumentType } from "../../../documents/DocumentTypes"; @@ -22,6 +22,7 @@ import { FormattedTextBox } from "./FormattedTextBox"; import './FormattedTextBoxComment.scss'; import { schema } from "./schema_rts"; import React = require("react"); +import { DefaultStyleProvider } from "../../StyleProvider"; export let formattedTextBoxCommentPlugin = new Plugin({ view(editorView) { return new FormattedTextBoxComment(editorView); } @@ -298,6 +299,9 @@ export class FormattedTextBoxComment { Document={target} moveDocument={returnFalse} rootSelected={returnFalse} + styleProvider={DefaultStyleProvider} + layerProvider={undefined} + docViewPath={emptyPath} ScreenToLocalTransform={Transform.Identity} parentActive={returnFalse} addDocument={returnFalse} diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx index abbb89780..2252de3d6 100644 --- a/src/client/views/nodes/formattedText/RichTextSchema.tsx +++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx @@ -138,6 +138,7 @@ export class DashDocView { removeDocument={removeDoc} layerProvider={this._textBox.props.layerProvider} styleProvider={this._textBox.props.styleProvider} + docViewPath={this._textBox.props.docViewPath} ScreenToLocalTransform={this.getDocTransform} addDocTab={this._textBox.props.addDocTab} pinToPres={returnFalse} diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index c07e137c4..9f9200fa4 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -7,7 +7,7 @@ import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from "../../../fields/FieldSymbols"; import { createSchema, makeInterface } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; -import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils"; +import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, emptyPath } from "../../../Utils"; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { DocumentManager } from "../../util/DocumentManager"; @@ -92,6 +92,8 @@ export class PresElementBox extends ViewBoxBaseComponent 400} renderDepth={0} focus={emptyFunction} + layerProvider={undefined} styleProvider={() => "rgba(0,0,0,0)"} + docViewPath={emptyPath} parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index c7e2d02cb..f6c283c3a 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -212,6 +212,8 @@ export class MobileInterface extends React.Component { renderDepth={0} focus={emptyFunction} styleProvider={this.whitebackground} + layerProvider={undefined} + docViewPath={emptyPath} parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} -- cgit v1.2.3-70-g09d2 From 919483d4222de685c1062a1e715f57c09442b676 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 5 Feb 2021 13:57:34 -0500 Subject: from last --- src/client/views/DocumentDecorations.tsx | 15 ++++++++++----- src/client/views/collections/CollectionMenu.tsx | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 0dfa0f8b2..77b43db9b 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -175,7 +175,10 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b } else if (e.altKey) { // open same document in new tab CollectionDockingView.ToggleSplit(Cast(selectedDocs[0].props.Document._fullScreenView, Doc, null) || selectedDocs[0].props.Document, "right"); } else { - runInAction(() => LightboxView.LightboxDoc = selectedDocs[0].props.Document); + runInAction(() => { + LightboxView.LightboxDoc = selectedDocs[0].props.Document; + LightboxView.LightboxFuture = selectedDocs.slice(1).map(view => view.props.Document); + }); } } } @@ -572,10 +575,12 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
); - const openIcon = !canOpen ? (null) : Open in Tab (ctrl: as alias, shift: in new collection)
} placement="top">
{ e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}> - {SelectionManager.Views().length === 1 ? : "..."} -
- ; + const openIcon = !canOpen ? (null) : + Open in Tab (ctrl: as alias, shift: in new collection)
} placement="top"> +
{ e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}> + +
+ ; const titleArea = this._edtingTitle ? -- cgit v1.2.3-70-g09d2 From e39d157b06f815644f7cbd87179307efd9524623 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 5 Feb 2021 14:27:20 -0500 Subject: fixed lightbox frame sizing. --- src/client/views/LightboxView.tsx | 21 +++++++++++---------- src/client/views/MainView.tsx | 11 ++++++++--- 2 files changed, 19 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 263117437..a1047fafc 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -13,7 +13,7 @@ import { DefaultStyleProvider } from './StyleProvider'; interface LightboxViewProps { PanelWidth: number; PanelHeight: number; - minBorder: number[]; + maxBorder: number[]; } @observer @@ -23,14 +23,14 @@ export class LightboxView extends React.Component { public static LightboxFuture: (Opt)[] = []; public static LightboxDocView = React.createRef(); public LightboxDocView = React.createRef(); - lightboxWidth = () => this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.minBorder[0]) * 2; - lightboxHeight = () => this.props.PanelHeight - Math.min(this.props.PanelHeight / 4, this.props.minBorder[1]) * 2; - lightboxScreenToLocal = () => new Transform(-Math.min(this.props.PanelWidth / 4, this.props.minBorder[0]), -Math.min(this.props.PanelHeight / 4, this.props.minBorder[1]), 1); + lightboxWidth = () => this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]) * 2; + lightboxHeight = () => this.props.PanelHeight - Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]) * 2; + lightboxScreenToLocal = () => new Transform(-Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]), -Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]), 1); navBtn = (left: Opt, icon: string, display: () => string, click: (e: React.MouseEvent) => void) => { return
@@ -40,6 +40,7 @@ export class LightboxView extends React.Component { } render() { + console.log("ph = " + this.props.PanelHeight); if (LightboxView.LightboxHistory.lastElement() !== LightboxView.LightboxDoc) LightboxView.LightboxHistory.push(LightboxView.LightboxDoc); let downx = 0, downy = 0; return !LightboxView.LightboxDoc ? (null) : @@ -53,10 +54,10 @@ export class LightboxView extends React.Component { } })} >
{ if (LightboxView.LightboxHistory.lastElement() !== LightboxView.LightboxFuture.lastElement()) LightboxView.LightboxFuture.push(popped); LightboxView.LightboxDoc = LightboxView.LightboxHistory.lastElement(); }))} - {this.navBtn(this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.minBorder[0]), "chevron-right", + {this.navBtn(this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]), "chevron-right", () => LightboxView.LightboxDoc && LightboxView.LightboxFuture.length ? "" : "none", action(e => { e.stopPropagation(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index b3f5cc54f..44aa41f85 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -67,8 +67,9 @@ const _global = (window /* browser */ || global /* node */) as any; export class MainView extends React.Component { public static Instance: MainView; private _docBtnRef = React.createRef(); - private _mainViewRef = React.createRef(); @observable public LastButton: Opt; + @observable private _windowWidth: number = 0; + @observable private _windowHeight: number = 0; @observable private _panelWidth: number = 0; @observable private _panelHeight: number = 0; @observable private _panelContent: string = "none"; @@ -609,7 +610,11 @@ export class MainView extends React.Component { } render() { - return (
((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)} ref={this._mainViewRef}> + return (
((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)} + ref={r => { + r && new _global.ResizeObserver(action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; })).observe(r); + }}> {this.inkResources} @@ -636,7 +641,7 @@ export class MainView extends React.Component { {this.snapLines}
- +
); } -- cgit v1.2.3-70-g09d2 From 32a0d5be4ff7a49b554806c27dca5b63ea80277f Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 5 Feb 2021 17:59:41 -0500 Subject: fixed pushpin interactionson lightboxviews. fixed doc decorations on ligthboxviews --- src/client/views/LightboxView.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index a1047fafc..aa6e4d909 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable } from 'mobx'; +import { action, observable, computed } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; @@ -22,10 +22,11 @@ export class LightboxView extends React.Component { public static LightboxHistory: (Opt)[] = []; public static LightboxFuture: (Opt)[] = []; public static LightboxDocView = React.createRef(); - public LightboxDocView = React.createRef(); - lightboxWidth = () => this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]) * 2; - lightboxHeight = () => this.props.PanelHeight - Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]) * 2; - lightboxScreenToLocal = () => new Transform(-Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]), -Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]), 1); + @computed get leftBorder() { return Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]); } + @computed get topBorder() { return Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]); } + lightboxWidth = () => this.props.PanelWidth - this.leftBorder * 2; + lightboxHeight = () => this.props.PanelHeight - this.topBorder * 2; + lightboxScreenToLocal = () => new Transform(-this.leftBorder, -this.topBorder, 1); navBtn = (left: Opt, icon: string, display: () => string, click: (e: React.MouseEvent) => void) => { return
{ } render() { - console.log("ph = " + this.props.PanelHeight); if (LightboxView.LightboxHistory.lastElement() !== LightboxView.LightboxDoc) LightboxView.LightboxHistory.push(LightboxView.LightboxDoc); let downx = 0, downy = 0; return !LightboxView.LightboxDoc ? (null) : @@ -54,12 +54,12 @@ export class LightboxView extends React.Component { } })} >
- { docViewPath={emptyPath} removeDocument={undefined} styleProvider={DefaultStyleProvider} - layerProvider={undefined} + layerProvider={returnTrue} ScreenToLocalTransform={this.lightboxScreenToLocal} PanelWidth={this.lightboxWidth} PanelHeight={this.lightboxHeight} -- cgit v1.2.3-70-g09d2 From 0e5891eab7f53697b764b7e9da5163db0351a0a2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 8 Feb 2021 19:25:48 -0500 Subject: overhaul of link anchors on text boxes to use actual Documents to represent selected text. Also got rid of _scrollY and _scrollPreviewY so that all document regions can be focused on using focus() and the new scrollFocus() mechanisim --- src/client/documents/DocumentTypes.ts | 4 +- src/client/documents/Documents.ts | 7 + src/client/util/DocumentManager.ts | 8 +- src/client/views/GestureOverlay.tsx | 17 +- src/client/views/MainView.tsx | 2 +- src/client/views/StyleProvider.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +- src/client/views/nodes/DocumentLinksButton.tsx | 14 +- src/client/views/nodes/DocumentView.tsx | 14 +- src/client/views/nodes/LinkDocPreview.tsx | 15 +- src/client/views/nodes/PDFBox.tsx | 5 +- src/client/views/nodes/PresBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 184 ++++++++---------- .../views/nodes/formattedText/FormattedTextBox.tsx | 207 ++++++++++----------- .../formattedText/FormattedTextBoxComment.tsx | 18 +- .../views/nodes/formattedText/RichTextMenu.tsx | 24 +-- src/client/views/nodes/formattedText/marks_rts.ts | 20 +- src/client/views/pdf/PDFViewer.tsx | 116 +++++------- src/fields/documentSchemas.ts | 7 +- src/fields/util.ts | 4 +- 20 files changed, 306 insertions(+), 372 deletions(-) (limited to 'src') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 37a148e55..080657fd8 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -38,5 +38,7 @@ export enum DocumentType { LINKDB = "linkdb", // database of links ??? why do we have this SCRIPTDB = "scriptdb", // database of scripts - GROUPDB = "groupdb" // database of groups + GROUPDB = "groupdb", // database of groups + + TEXTANCHOR = "textanchor" // selection of text in a text box } \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1a4aae17e..7d6db06d5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -373,6 +373,9 @@ export namespace Docs { }], [DocumentType.GROUP, { layout: { view: EmptyBox, dataField: defaultDataKey } + }], + [DocumentType.TEXTANCHOR, { + layout: { view: EmptyBox, dataField: defaultDataKey } }] ]); @@ -785,6 +788,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.DOCHOLDER), document, { title: document ? document.title + "" : "container", targetDropAction: "move", ...options }); } + export function TextanchorDocument(options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.TEXTANCHOR), document, { targetDropAction: "move", ...options }); + } + export function FreeformDocument(documents: Array, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Freeform }, id); } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 7af16ed6e..f5f6b6f67 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -149,7 +149,8 @@ export class DocumentManager { const highlight = () => { const finalDocView = getFirstDocView(targetDoc); if (finalDocView) { - finalDocView.layoutDoc.scrollToLinkID = linkDoc?.[Id]; + const parent = targetDoc?.annotationOn as Doc; + if (parent) finalDocView.layoutDoc.scrollToAnchorID = targetDoc?.[Id]; Doc.linkFollowHighlight(finalDocView.props.Document); } }; @@ -159,7 +160,7 @@ export class DocumentManager { const first = getFirstDocView(annotatedDoc); if (first) { annotatedDoc = first.props.Document; - first.props.focus(annotatedDoc, false); + first.focus(targetDoc, false); } } if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? @@ -190,7 +191,6 @@ export class DocumentManager { highlight(); } else { // otherwise try to get a view of the context of the target const targetDocContextView = getFirstDocView(targetDocContext); - targetDocContext._scrollY = targetDocContext._scrollPreviewY = NumCast(targetDocContext._scrollTop, 0); // this will force PDFs to activate and load their annotations / allow scrolling if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first.. targetDocContext._viewTransition = "transform 500ms"; targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom); @@ -208,7 +208,7 @@ export class DocumentManager { } else if (delay > 1500) { // we didn't find the target, so it must have moved out of the context. Go back to just creating it. if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.props.Document); - if (targetDoc.layout) { + if (targetDoc.layout) { // there will no layout for a TEXTANCHOR type document Doc.SetInPlace(targetDoc, "annotationOn", undefined, false); createViewFunc(Doc.BrushDoc(targetDoc), finished); // create a new view of the target } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 76cb0112e..9306cf9ae 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -516,7 +516,7 @@ export class GestureOverlay extends Touchable { } handleLineGesture = (): boolean => { - let actionPerformed = false; + const actionPerformed = false; const B = this.svgBounds; // get the two targets at the ends of the line @@ -525,21 +525,6 @@ export class GestureOverlay extends Touchable { const target1 = document.elementFromPoint(ep1.X, ep1.Y); const target2 = document.elementFromPoint(ep2.X, ep2.Y); - // callback function to be called by each target - const callback = (doc: Doc) => { - if (!this._d1) { - this._d1 = doc; - } - // we don't want to create a link of both endpoints are the same document (doing so makes drawing an l very hard) - else if (this._d1 !== doc && !LinkManager.Instance.doesLinkExist(this._d1, doc)) { - // we don't want to create a link between ink strokes (doing so makes drawing a t very hard) - if (this._d1.type !== "ink" && doc.type !== "ink") { - DocUtils.MakeLink({ doc: this._d1 }, { doc: doc }, "gestural link", ""); - actionPerformed = true; - } - } - }; - const ge = new CustomEvent("dashOnGesture", { bubbles: true, diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 44aa41f85..bc3d05005 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -101,7 +101,7 @@ export class MainView extends React.Component { } new InkStrokeProperties(); this._sidebarContent.proto = undefined; - DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_delayAutoHeight", "_autoHeight", "_showSidebar", "_sidebarWidthPercent", "_width", "_height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollY", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's + DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_delayAutoHeight", "_autoHeight", "_showSidebar", "_sidebarWidthPercent", "_width", "_height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 3956b8c5b..058d21c92 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -12,7 +12,7 @@ import { SnappingManager } from '../util/SnappingManager'; import { UndoManager } from '../util/UndoManager'; import { CollectionViewType } from './collections/CollectionView'; import { MainView } from './MainView'; -import { DocumentViewProps } from "./nodes/DocumentView"; +import { DocumentViewProps, DocumentView } from "./nodes/DocumentView"; import { FieldViewProps } from './nodes/FieldView'; import "./StyleProvider.scss"; import React = require("react"); @@ -111,6 +111,8 @@ export function DefaultStyleProvider(doc: Opt, props: Opt { const [xp, yp] = this.getTransform().transformPoint(de.x, de.y); if (this.isAnnotationOverlay !== true && de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp); - if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalPdfAnnoDrop(e, de.complete.annoDragData, xp, yp); + if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData, xp, yp); if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, xp, yp); return false; } @@ -920,7 +919,6 @@ export class CollectionFreeFormView extends CollectionSubView 5 ? 1000 : 0; !dontCenter && this.props.focus(this.props.Document); afterFocus && setTimeout(() => afterFocus?.(delay ? true : false), delay); diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 2cac2d0b8..defa4dbf0 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -58,8 +58,6 @@ export class DocumentLinksButton extends React.Component this.props.View.LinkBeingCreated = dropEv.linkDocument); - setTimeout(action(() => this.props.View.LinkBeingCreated = undefined), 0); } linkDrag?.end(); }, @@ -118,7 +116,7 @@ export class DocumentLinksButton extends React.Component { - DocumentLinksButton.StartLinkView && (DocumentLinksButton.StartLinkView.LinkBeingCreated = undefined); - endLinkView.LinkBeingCreated = undefined; - }), 0); - } + LinkManager.currentLink = linkDoc; if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) { // if linking from a Hypothes.is annotation diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 2f56f5b00..463e59bd1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -40,12 +40,15 @@ import { LinkAnchorBox } from './LinkAnchorBox'; import { PresBox } from './PresBox'; import { RadialMenu } from './RadialMenu'; import React = require("react"); +import { LinkDocPreview } from "./LinkDocPreview"; +import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment"; export type DocAfterFocusFunc = (notFocused: boolean) => boolean; export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => void; export type StyleProviderFunc = (doc: Opt, props: Opt, property: string) => any; export interface DocComponentView { getAnchor: () => Doc; + scrollFocus?: (doc: Doc, smooth: boolean) => void; back?: () => boolean; forward?: () => boolean; url?: () => string; @@ -373,6 +376,10 @@ export class DocumentViewInternal extends DocComponent { + this._componentView?.scrollFocus?.(doc, !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc); // bcz: smooth parameter should really be passed into focus() instead of inferred here + return this.props.focus(doc, willZoom, scale, afterFocus, dontCenter, focused); + } onClick = action((e: React.MouseEvent | React.PointerEvent) => { if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 && (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { @@ -710,7 +717,6 @@ export class DocumentViewInternal extends DocComponent this.props.ScreenToLocalTransform().translate(0, -this.headerMargin); contentScaling = () => this.ContentScale; onClickFunc = () => this.onClickHandler; - makeLink = () => this.props.DocumentView.LinkBeingCreated; // 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. setContentView = (view: { getAnchor: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view; @observable contentsActive: () => boolean = returnFalse; @action setContentsActive = (setActive: () => boolean) => this.contentsActive = setActive; @@ -729,9 +735,9 @@ export class DocumentViewInternal extends DocComponent {this.layoutDoc.hideAllLinks ? (null) : this.allAnchors} {this.hideLinkButton ? (null) : @@ -865,7 +871,6 @@ export class DocumentView extends React.Component { public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive public ContentRef = React.createRef(); - @observable LinkBeingCreated: Opt; // see DocumentLinksButton for explanation of how this works @observable public docView: DocumentViewInternal | undefined | null; get Document() { return this.props.Document; } @@ -907,6 +912,9 @@ export class DocumentView extends React.Component { toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight()); contentsActive = () => this.docView?.contentsActive(); + focus = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => { + return this.docView?.focus(doc, willZoom, scale, afterFocus, dontCenter, focused); + } getBounds = () => { if (!this.docView || !this.docView.ContentDiv || this.docView.props.renderDepth === 0 || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { return undefined; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 75ca6059e..8051568ff 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -26,6 +26,7 @@ export class LinkDocPreview extends React.Component { @observable public static LinkInfo: Opt<{ linkDoc?: Doc; linkSrc: Doc; href?: string; Location: number[], docprops: DocumentViewSharedProps }>; @observable _targetDoc: Opt; @observable _toolTipText = ""; + _linkTarget: Opt; _editRef = React.createRef(); @action @@ -58,17 +59,13 @@ export class LinkDocPreview extends React.Component { runInAction(() => this._toolTipText = "external => " + this.props.href); } } else if (linkDoc && linkSrc) { - const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), linkSrc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc); - const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor; + const anchor1 = linkDoc.anchor1 as Doc; + const anchor2 = linkDoc.anchor2 as Doc; + this._linkTarget = Doc.AreProtosEqual(anchor1, linkSrc) || Doc.AreProtosEqual(anchor1.annotationOn as Doc, linkSrc) ? anchor2 : anchor1; + const target = this._linkTarget?.annotationOn ? await DocCastAsync(this._linkTarget.annotationOn) : this._linkTarget; runInAction(() => { this._toolTipText = ""; LinkDocPreview.TargetDoc = this._targetDoc = target; - if (this._targetDoc) { - this._targetDoc._scrollToPreviewLinkID = linkDoc?.[Id]; - if (anchor !== this._targetDoc && anchor) { - this._targetDoc._scrollPreviewY = NumCast(anchor?.y); - } - } }); } } @@ -89,7 +86,7 @@ export class LinkDocPreview extends React.Component {
: - this._linkTarget !== this._targetDoc && this._linkTarget && r?.focus(this._linkTarget)} Document={this._targetDoc} moveDocument={returnFalse} rootSelected={returnFalse} diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index ec9a75302..496caedaa 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -79,8 +79,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent doc !== this.rootDoc && this._pdfViewer?.scrollFocus(doc, smooth); + getAnchor = () => this.rootDoc; componentWillUnmount() { this._selectReactionDisposer?.(); } componentDidMount() { + this.props.setContentView?.(this); this._selectReactionDisposer = reaction(() => this.props.isSelected(), () => { document.removeEventListener("keydown", this.onKeyDown); @@ -218,7 +221,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent>(); render() { TraceMobx(); - if (true) {//this.props.isSelected() || (this.props.active() && this.props.renderDepth === 0) || this.props.Document._scrollY !== undefined) { + if (true) {//this.props.isSelected() || (this.props.active() && this.props.renderDepth === 0)) { this._displayPdfLive = true; } if (this._displayPdfLive) { diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index b6feace12..589a1c2ae 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -364,7 +364,7 @@ export class PresBox extends ViewBoxBaseComponent bestTarget && runInAction(() => { if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) { bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s'; - bestTarget._scrollY = activeItem.presPinViewScroll; + bestTarget._scrollTop = activeItem.presPinViewScroll; } else if (bestTarget.type === DocumentType.COMPARISON) { bestTarget._clipWidth = activeItem.presPinClipWidth; } else if (bestTarget.type === DocumentType.VID) { diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 1dfe88f78..ee152ddb3 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction import { observer } from "mobx-react"; import { Dictionary } from "typescript-collections"; import * as WebRequest from 'web-request'; -import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, WidthSym, Opt } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { HtmlField } from "../../../fields/HtmlField"; @@ -24,11 +24,11 @@ import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; +import { MarqueeAnnotator } from "../MarqueeAnnotator"; import { Annotation } from "../pdf/Annotation"; import { FieldView, FieldViewProps } from './FieldView'; import "./WebBox.scss"; import React = require("react"); -import { MarqueeAnnotator } from "../MarqueeAnnotator"; const htmlToText = require("html-to-text"); type WebDocument = makeInterface<[typeof documentSchema]>; @@ -44,15 +44,14 @@ export class WebBox extends ViewBoxAnnotatableComponent = React.createRef(); private _iframeIndicatorRef = React.createRef(); private _iframeDragRef = React.createRef(); + private _ignoreScroll = false; + private _initialScroll: Opt; @observable private _marqueeing: number[] | undefined; @observable private _url: string = "hello"; @observable private _pressX: number = 0; @observable private _pressY: number = 0; @observable private _iframe: HTMLIFrameElement | null = null; @observable private _savedAnnotations: Dictionary = new Dictionary(); - - get _collapsed() { return StrCast(this.layoutDoc._chromeStatus) !== "enabled"; } - set _collapsed(value) { this.layoutDoc._chromeStatus = !value ? "enabled" : "disabled"; } get scrollHeight() { return this.webpage?.scrollHeight || 1000; } get webpage() { return this._iframe?.contentDocument?.children[0]; } @@ -65,9 +64,14 @@ export class WebBox extends ViewBoxAnnotatableComponent { + iframeLoaded = (e: any) => { const iframe = this._iframe; if (iframe?.contentDocument) { + if (this._initialScroll !== undefined && this._outerRef.current && this.webpage) { + this.webpage.scrollTop = this._initialScroll; + this._outerRef.current.scrollTop = this._initialScroll; + this._initialScroll = undefined; + } iframe.setAttribute("enable-annotation", "true"); iframe.contentDocument.addEventListener("click", undoBatch(action(e => { let href = ""; @@ -76,81 +80,53 @@ export class WebBox extends ViewBoxAnnotatableComponent ({ scrollY: this.layoutDoc._scrollY, scrollX: this.layoutDoc._scrollX }), - ({ scrollY, scrollX }) => { - const delay = this._outerRef.current ? 0 : 250; // wait for mainCont and try again to scroll - const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/); - const duration = durationStr ? Number(durationStr[1]) : 1000; - if (scrollY !== undefined) { - this._forceSmoothScrollUpdate = true; - setTimeout(() => this.layoutDoc._scrollY = undefined, duration); - setTimeout(() => this.webpage && smoothScroll(duration, this.webpage as any as HTMLElement, Math.abs(scrollY || 0)), delay); - setTimeout(() => this._outerRef.current && smoothScroll(duration, this._outerRef.current, Math.abs(scrollY || 0), () => this.layoutDoc._scrollTop = scrollY), delay); - } - if (scrollX !== undefined) { - this._forceSmoothScrollUpdate = true; - setTimeout(() => this.layoutDoc._scrollX = undefined, duration); - setTimeout(() => this.webpage && smoothScroll(duration, this.webpage as any as HTMLElement, Math.abs(scrollX || 0)), delay); - setTimeout(() => this._outerRef.current && smoothScroll(duration, this._outerRef.current, Math.abs(scrollX || 0), () => this.layoutDoc._scrollLeft = scrollX), delay); - } - }, - { fireImmediately: true } - ); - this._disposers.scrollTop = reaction(() => this.layoutDoc._scrollTop, - scrollTop => { - const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/); - const duration = durationStr ? Number(durationStr[1]) : 1000; - if (scrollTop !== this._outerRef.current?.scrollTop && scrollTop !== undefined && this._forceSmoothScrollUpdate) { - this.webpage!.scrollTop = scrollTop; - this._outerRef.current && smoothScroll(duration, this._outerRef.current, Math.abs(scrollTop || 0), () => this._forceSmoothScrollUpdate = true); - } else this._forceSmoothScrollUpdate = true; - }, - { fireImmediately: true } - ); - }); - _forceSmoothScrollUpdate = true; - - updateScroll = (x: Opt, y: Opt) => { - if (y !== undefined) { - this._outerRef.current!.scrollTop = y; - this.layoutDoc._scrollY = undefined; - } - if (x !== undefined) { - this._outerRef.current!.scrollLeft = x; - this.layoutDoc.scrollX = undefined; } } - iframeWheel = (e: any) => { - if (this._forceSmoothScrollUpdate && e.target?.children) { - this.webpage && setTimeout(action(() => { - this.webpage!.scrollLeft = 0; - const scrollTop = this.webpage!.scrollTop; - const scrollLeft = this.webpage!.scrollLeft; - this._outerRef.current!.scrollTop = scrollTop; - this._outerRef.current!.scrollLeft = scrollLeft; - if (this.layoutDoc._scrollTop !== scrollTop) { - this._forceSmoothScrollUpdate = false; - this.layoutDoc._scrollTop = scrollTop; - } - if (this.layoutDoc._scrollLeft !== scrollLeft) { - this._forceSmoothScrollUpdate = false; - this.layoutDoc._scrollLeft = scrollLeft; - } - })); + @action + onWheelScroll = (scrollTop: number) => { + if (this.webpage && this._outerRef.current) { + this.webpage.scrollLeft = 0; + this._outerRef.current.scrollTop = scrollTop; + this._outerRef.current.scrollLeft = 0; + this._ignoreScroll = true; + if (this.layoutDoc._scrollTop !== scrollTop) { + this.layoutDoc._scrollTop = scrollTop; + } + this._ignoreScroll = false; } } + iframeWheel = (e: any) => this.webpage && e.target?.children && this.onWheelScroll(this.webpage.scrollTop); + onWheel = (e: React.WheelEvent) => { + this._outerRef.current && this.onWheelScroll(this._outerRef.current.scrollTop); + e.stopPropagation(); + } getAnchor = () => this.rootDoc; + scrollFocus = (doc: Doc, smooth: boolean) => { + if (doc !== this.rootDoc && this.webpage && this._outerRef.current) { + this._initialScroll !== undefined && (this._initialScroll = NumCast(doc.y)); + this._ignoreScroll = true; + if (smooth) { + smoothScroll(500, this.webpage as any as HTMLElement, NumCast(doc.y)); + smoothScroll(500, this._outerRef.current, NumCast(doc.y)); + } else { + this.webpage.scrollTop = NumCast(doc.y); + this._outerRef.current.scrollTop = NumCast(doc.y); + } + smooth && (this.layoutDoc._scrollTop = NumCast(doc.y)); + this._ignoreScroll = false; + } else { + this._initialScroll = NumCast(doc.y); + } + } async componentDidMount() { this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. @@ -158,9 +134,6 @@ export class WebBox extends ViewBoxAnnotatableComponent this._url = urlField?.url.toString() || ""); - this._disposers.scrollMove = reaction(() => this.layoutDoc.x || this.layoutDoc.y, - () => this.updateScroll(this.layoutDoc._scrollLeft, this.layoutDoc._scrollTop)); - this._disposers.selection = reaction(() => this.props.isSelected(), selected => { if (!selected) { @@ -190,6 +163,30 @@ export class WebBox extends ViewBoxAnnotatableComponent NumCast(this.layoutDoc._scrollTop), + (scrollTop) => { + if (quickScroll !== undefined) { + this._initialScroll = scrollTop; + } + else if (!this._ignoreScroll && this._outerRef.current && this.webpage) { + const viewTrans = StrCast(this.Document._viewTransition); + const durationMiliStr = viewTrans.match(/([0-9]*)ms/); + const durationSecStr = viewTrans.match(/([0-9.]*)s/); + const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0; + if (duration) { + smoothScroll(duration, this.webpage as any as HTMLElement, scrollTop); + smoothScroll(duration, this._outerRef.current, scrollTop); + } else { + this.webpage.scrollTop = scrollTop; + this._outerRef.current.scrollTop = scrollTop; + } + } + }, + { fireImmediately: true } + ); + quickScroll = undefined; } componentWillUnmount() { @@ -353,22 +350,14 @@ export class WebBox extends ViewBoxAnnotatableComponent { // this._pressX = e.clientX; // this._pressY = e.clientY; } - onLongPressUp = (e: PointerEvent) => { - if (this._longPressSecondsHack) { - clearTimeout(this._longPressSecondsHack); - } - if (this._iframeIndicatorRef.current) { - this._iframeIndicatorRef.current.classList.remove("active"); - } - if (this._iframeDragRef.current) { - while (this._iframeDragRef.current.firstChild) this._iframeDragRef.current.removeChild(this._iframeDragRef.current.firstChild); - } + this._longPressSecondsHack && clearTimeout(this._longPressSecondsHack); + this._iframeIndicatorRef.current?.classList.remove("active"); + while (this._iframeDragRef.current?.firstChild) this._iframeDragRef.current.removeChild(this._iframeDragRef.current.firstChild); } specificContextMenu = (e: React.MouseEvent): void => { @@ -377,7 +366,6 @@ export class WebBox extends ViewBoxAnnotatableComponent this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" }); funcs.push({ description: (this.layoutDoc[this.fieldKey + "-contentWidth"] ? "Unfreeze" : "Freeze") + " Content Width", event: () => this.layoutDoc[this.fieldKey + "-contentWidth"] = this.layoutDoc[this.fieldKey + "-contentWidth"] ? undefined : Doc.NativeWidth(this.layoutDoc), icon: "snowflake" }); cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); - } @computed @@ -389,7 +377,6 @@ export class WebBox extends ViewBoxAnnotatableComponent { e.currentTarget.before((e.currentTarget.contentDocument?.body || e.currentTarget.contentDocument)?.children[0]!); e.currentTarget.remove(); }} - view =