From 5845674720497f87c5ccada7a188817f3c912c6e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 18 Mar 2021 15:42:13 -0400
Subject: preparing to break sidebar out into its own component

---
 src/client/views/nodes/WebBox.tsx | 258 ++++++++++++++++++--------------------
 1 file changed, 125 insertions(+), 133 deletions(-)

(limited to 'src/client/views/nodes/WebBox.tsx')

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 78bd6cbf6..df50f7b90 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -44,15 +44,15 @@ const WebDocument = makeInterface(documentSchema);
 @observer
 export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
+    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _keyInput = React.createRef<HTMLInputElement>();
-    @observable _scrollTimer: any;
-    @observable private _overlayAnnoInfo: Opt<Doc>;
     private _initialScroll: Opt<number>;
-    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
+    @observable private _scrollTimer: any;
+    @observable private _overlayAnnoInfo: Opt<Doc>;
     @observable private _marqueeing: number[] | undefined;
     @observable private _url: string = "hello";
     @observable private _isAnnotating = false;
@@ -60,11 +60,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable private _scrollHeight = 1500;
     @computed get scrollHeight() { return this._scrollHeight; }
+    @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
+    @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
 
     constructor(props: any) {
         super(props);
-        if (this.dataDoc[this.fieldKey] instanceof WebField) {
+        if (this.webField) {
             Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || 850);
             Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * 850);
         }
@@ -74,6 +76,58 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         this._annotationKey = "annotations-" + this.urlHash(this._url);
     }
 
+    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.
+
+        runInAction(() => this._url = this.webField?.toString() || "");
+
+        this._disposers.selection = reaction(() => this.props.isSelected(),
+            selected => !selected && setTimeout(() => {
+                Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
+                this._savedAnnotations.clear();
+            }));
+
+        if (this.webField?.href.indexOf("youtube") !== -1) {
+            const youtubeaspect = 400 / 315;
+            const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+            const nativeHeight = Doc.NativeHeight(this.layoutDoc);
+            if (this.webField) {
+                if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
+                    if (!nativeWidth) Doc.SetNativeWidth(this.layoutDoc, 600);
+                    Doc.SetNativeHeight(this.layoutDoc, (nativeWidth || 600) / youtubeaspect);
+                    this.layoutDoc._height = this.layoutDoc[WidthSym]() / youtubeaspect;
+                }
+            } // else it's an HTMLfield
+        } else if (this.webField && !this.dataDoc.text) {
+            const result = await WebRequest.get(Utils.CorsProxy(this.webField.href));
+            if (result) {
+                this.dataDoc.text = htmlToText.fromString(result.content);
+            }
+        }
+
+        var quickScroll = true;
+        this._disposers.scrollReaction = reaction(() => NumCast(this.layoutDoc._scrollTop),
+            (scrollTop) => {
+                if (quickScroll) {
+                    this._initialScroll = scrollTop;
+                }
+                else {
+                    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;
+                    this.goTo(scrollTop, duration);
+                }
+            },
+            { fireImmediately: true }
+        );
+        quickScroll = false;
+    }
+    componentWillUnmount() {
+        Object.values(this._disposers).forEach(disposer => disposer?.());
+        this._iframe?.removeEventListener('wheel', this.iframeWheel, true);
+    }
+
     @action
     createTextAnnotation = (sel: Selection, selRange: Range) => {
         if (this._mainCont.current) {
@@ -102,6 +156,32 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         }
     }
 
+    menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected
+
+    scrollFocus = (doc: Doc, smooth: boolean) => {
+        if (doc !== this.rootDoc && this._outerRef.current) {
+            const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
+            if (scrollTo !== undefined) {
+                const focusSpeed = smooth ? 500 : 0;
+                this._initialScroll !== undefined && (this._initialScroll = scrollTo);
+                this.goTo(scrollTo, focusSpeed);
+                return focusSpeed;
+            }
+        }
+        this._initialScroll = NumCast(doc.y);
+        return 0;
+    }
+
+    getAnchor = () => {
+        const anchor = Docs.Create.TextanchorDocument({
+            title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
+            annotationOn: this.rootDoc,
+            y: NumCast(this.layoutDoc._scrollTop),
+        });
+        this.addDocument(anchor);
+        return anchor;
+    }
+
     @action
     iframeUp = (e: PointerEvent) => {
         if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) {
@@ -151,8 +231,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 for (let ele = e.target; ele; ele = ele.parentElement) {
                     href = (typeof (ele.href) === "string" ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href;
                 }
-                if (href) {
-                    this.submitURL(href.replace(Utils.prepend(""), Cast(this.dataDoc[this.fieldKey], WebField, null)?.url.origin));
+                if (href && this.webField?.origin) {
+                    this.submitURL(href.replace(Utils.prepend(""), this.webField?.origin));
                     if (this._outerRef.current) {
                         this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop);
                         this._outerRef.current.scrollLeft = 0;
@@ -164,6 +244,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         }
     }
 
+    @action
+    iframeWheel = (e: any) => {
+        if (!this._scrollTimer) {
+            this._scrollTimer = setTimeout(action(() => this._scrollTimer = undefined), 250); // this turns events off on the iframe which allows scrolling to change direction smoothly
+        }
+    }
+
     @action
     setDashScrollTop = (scrollTop: number, timeout: number = 250) => {
         const iframeHeight = Math.max(1000, this._scrollHeight - this.panelHeight());
@@ -177,90 +264,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             }
         }), timeout);
     }
-    @action
-    iframeWheel = (e: any) => {
-        if (!this._scrollTimer) {
-            this._scrollTimer = setTimeout(action(() => this._scrollTimer = undefined), 250); // this turns events off on the iframe which allows scrolling to change direction smoothly
-        }
-    }
-    onWheel = (e: any) => {
-        e.stopPropagation();
-        e.preventDefault();
-    }
-    onScroll = (e: any) => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0);
-    scrollFocus = (doc: Doc, smooth: boolean) => {
-        if (doc !== this.rootDoc && this._outerRef.current) {
-            const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
-            if (scrollTo !== undefined) {
-                const focusSpeed = smooth ? 500 : 0;
-                this._initialScroll !== undefined && (this._initialScroll = scrollTo);
-                this.goTo(scrollTo, focusSpeed);
-                return focusSpeed;
-            }
-        }
-        this._initialScroll = NumCast(doc.y);
-        return 0;
-    }
-
-    getAnchor = () => {
-        const anchor = Docs.Create.TextanchorDocument({
-            title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
-            annotationOn: this.rootDoc,
-            y: NumCast(this.layoutDoc._scrollTop),
-        });
-        this.addDocument(anchor);
-        return anchor;
-    }
-
-    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() || "");
-
-        this._disposers.selection = reaction(() => this.props.isSelected(),
-            selected => !selected && setTimeout(() => {
-                Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
-                this._savedAnnotations.clear();
-            }));
-
-        const field = Cast(this.rootDoc[this.props.fieldKey], WebField);
-        if (field?.url.href.indexOf("youtube") !== -1) {
-            const youtubeaspect = 400 / 315;
-            const nativeWidth = Doc.NativeWidth(this.layoutDoc);
-            const nativeHeight = Doc.NativeHeight(this.layoutDoc);
-            if (field) {
-                if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
-                    if (!nativeWidth) Doc.SetNativeWidth(this.layoutDoc, 600);
-                    Doc.SetNativeHeight(this.layoutDoc, (nativeWidth || 600) / youtubeaspect);
-                    this.layoutDoc._height = this.layoutDoc[WidthSym]() / youtubeaspect;
-                }
-            } // else it's an HTMLfield
-        } else if (field?.url && !this.dataDoc.text) {
-            const result = await WebRequest.get(Utils.CorsProxy(field.url.href));
-            if (result) {
-                this.dataDoc.text = htmlToText.fromString(result.content);
-            }
-        }
-
-        var quickScroll = true;
-        this._disposers.scrollReaction = reaction(() => NumCast(this.layoutDoc._scrollTop),
-            (scrollTop) => {
-                if (quickScroll) {
-                    this._initialScroll = scrollTop;
-                }
-                else {
-                    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;
-                    this.goTo(scrollTop, duration);
-                }
-            },
-            { fireImmediately: true }
-        );
-        quickScroll = false;
-    }
 
     goTo = (scrollTop: number, duration: number) => {
         if (this._outerRef.current) {
@@ -275,11 +278,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         }
     }
 
-    componentWillUnmount() {
-        Object.values(this._disposers).forEach(disposer => disposer?.());
-        this._iframe?.removeEventListener('wheel', this.iframeWheel, true);
-    }
-
     @action
     forward = () => {
         const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), null);
@@ -312,12 +310,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     }
 
     @action
-    submitURL = (newUrl: string) => {
+    submitURL = (newUrl?: string) => {
+        if (!newUrl) return;
         if (!newUrl.startsWith("http")) newUrl = "http://" + newUrl;
         try {
             const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), null);
             const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), null);
-            const url = Cast(this.dataDoc[this.fieldKey], WebField, null)?.url.toString();
+            const url = this.webField?.toString();
             if (url) {
                 if (history === undefined) {
                     this.dataDoc[this.fieldKey + "-history"] = new List<string>([url]);
@@ -336,7 +335,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         return true;
     }
 
-    menuControls = () => this.urlEditor;
     onWebUrlDrop = (e: React.DragEvent) => {
         const { dataTransfer } = e;
         const html = dataTransfer.getData("text/html");
@@ -371,12 +369,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                     <button className="submitUrl" onClick={() => this.submitURL(this._keyInput.current!.value)} onDragOver={e => e.stopPropagation()} onDrop={this.onWebUrlDrop}>
                         GO
                     </button>
-                    <button className="submitUrl" onClick={this.back}>
-                        <FontAwesomeIcon icon="caret-left" size="lg"></FontAwesomeIcon>
-                    </button>
-                    <button className="submitUrl" onClick={this.forward}>
-                        <FontAwesomeIcon icon="caret-right" size="lg"></FontAwesomeIcon>
-                    </button>
+                    <button className="submitUrl" onClick={this.back}> <FontAwesomeIcon icon="caret-left" size="lg" /> </button>
+                    <button className="submitUrl" onClick={this.forward}> <FontAwesomeIcon icon="caret-right" size="lg" />  </button>
                 </div>
             </div>
         );
@@ -391,6 +385,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
 
+    @action
+    onMarqueeDown = (e: React.PointerEvent) => {
+        if (!e.altKey && e.button === 0 && this.active(true)) {
+            this._marqueeing = [e.clientX, e.clientY];
+            this.props.select(false);
+        }
+    }
+    @action finishMarquee = (x?: number, y?: number) => {
+        this._marqueeing = undefined;
+        this._isAnnotating = false;
+        x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false);
+    }
+
     @computed
     get urlContent() {
         const field = this.dataDoc[this.props.fieldKey];
@@ -424,7 +431,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         });
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
-        this.allTags.map(tag => target[tag] = tag);
+        this.sidebarAllTags.map(tag => target[tag] = tag);
         DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline markup", "annotation");
         this.sidebarAddDocument(target);
     }
@@ -445,19 +452,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.sidebarKey());
     sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.sidebarKey());
     sidebarDocFilters = () => [...StrListCast(this.layoutDoc._docFilters), ...StrListCast(this.layoutDoc[this.sidebarKey() + "-docFilters"])];
-    @computed get allTags() {
+    @computed get sidebarAllTags() {
         const keys = new Set<string>();
         DocListCast(this.rootDoc[this.sidebarKey()]).forEach(doc => SearchBox.documentKeys(doc).forEach(key => keys.add(key)));
         return Array.from(keys.keys()).filter(key => key[0]).filter(key => !key.startsWith("_") && (key[0] === "#" || key[0] === key[0].toUpperCase())).sort();
     }
-    renderTag = (tag: string) => {
-        const active = StrListCast(this.rootDoc[this.sidebarKey() + "-docFilters"]).includes(`${tag}:${tag}:check`);
-        return <div className={`webBox-filterTag${active ? "-active" : ""}`}
-            onClick={e => Doc.setDocFilter(this.rootDoc, tag, tag, "check", true, this.sidebarKey(), e.shiftKey)}>
-            {tag}
-        </div>;
-    }
     @computed get sidebarOverlay() {
+        const renderTag = (tag: string) => {
+            const active = StrListCast(this.rootDoc[this.sidebarKey() + "-docFilters"]).includes(`${tag}:${tag}:check`);
+            return <div className={`webBox-filterTag${active ? "-active" : ""}`}
+                onClick={e => Doc.setDocFilter(this.rootDoc, tag, tag, "check", true, this.sidebarKey(), e.shiftKey)}>
+                {tag}
+            </div>;
+        }
         return !this.layoutDoc._showSidebar ? (null) :
             <div style={{
                 position: "absolute", pointerEvents: this.active() ? "all" : undefined, top: 0, right: 0,
@@ -494,21 +501,18 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                     />
                 </div>
                 <div className="webBox-tagList" style={{ height: this.sidebarFiltersHeight(), width: this.sidebarWidth() }}>
-                    {this.allTags.map(tag => this.renderTag(tag))}
+                    {this.sidebarAllTags.map(renderTag)}
                 </div>
             </div>;
     }
 
-    @computed
-    get content() {
+    @computed get content() {
         return <div className={"webBox-cont" + (this.active() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")}
             style={{ width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / (this.props.scaling?.() || 1)}%`, }}>
             {this.urlContent}
         </div>;
     }
 
-    showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
-    @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get annotationLayer() {
         TraceMobx();
         return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
@@ -518,20 +522,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         </div>;
     }
 
-    @action
-    onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.active(true)) {
-            this._marqueeing = [e.clientX, e.clientY];
-            this.props.select(false);
-        }
-    }
+    showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func;
-    @action finishMarquee = (x?: number, y?: number) => {
-        this._marqueeing = undefined;
-        this._isAnnotating = false;
-        x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false);
-    }
-
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (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(0, NumCast(this.layoutDoc._scrollTop));
@@ -551,8 +543,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                             transform: `scale(${scale})`,
                             pointerEvents: inactiveLayer ? "none" : undefined
                         }}
-                        onWheel={this.onWheel}
-                        onScroll={this.onScroll}
+                        onWheel={e => { e.stopPropagation(); e.preventDefault(); }} // block wheel events from propagating since they're handled by the iframe
+                        onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
                         onPointerDown={this.onMarqueeDown}
                     >
                         <div className={"webBox-innerContent"} style={{
-- 
cgit v1.2.3-70-g09d2

