From 542a057edbdd4661cfb5ef5d07058aa93b47a113 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 24 Aug 2022 11:16:46 -0400
Subject: cleaned up some of the pin with view code.  added a pin button to
 menu bar when tab is selected to avoid needing to use context menu

---
 src/client/views/nodes/trails/PresBox.tsx | 358 +++++++-----------------------
 1 file changed, 86 insertions(+), 272 deletions(-)

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

diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 05e09361b..eb40089ec 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -358,20 +358,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (bestTarget) this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
     };
 
+    static pinDataTypes(target: Doc) {
+        const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(target.type as any) || target._viewType === CollectionViewType.Stacking;
+        const pannable = [DocumentType.IMG].includes(target.type as any) || (target.type === DocumentType.COL && target._viewType === CollectionViewType.Freeform);
+        const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(target.type as any);
+        const clippable = [DocumentType.COMPARISON].includes(target.type as any);
+        return { scrollable, pannable, temporal, clippable };
+    }
     // navigates to the bestTarget document by making sure it is on screen,
     // then it applies the view specs stored in activeItem to
     @action
     static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) {
-        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._scrollTop = activeItem.presPinViewScroll;
-        } else if (bestTarget.type === DocumentType.COMPARISON) {
-            bestTarget._clipWidth = activeItem.presPinClipWidth;
-        } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) {
-            bestTarget._currentTimecode = activeItem.presStartTime;
-        } else {
+        bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+        const { scrollable, pannable, temporal, clippable } = this.pinDataTypes(bestTarget);
+        if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth;
+        if (temporal) bestTarget._currentTimecode = activeItem.presStartTime;
+        if (scrollable) bestTarget._scrollTop = activeItem.presPinViewScroll;
+        if (pannable) {
             const contentBounds = Cast(activeItem.contentBounds, listSpec('number'));
-            bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
             if (contentBounds) {
                 bestTarget._panX = (contentBounds[0] + contentBounds[2]) / 2;
                 bestTarget._panY = (contentBounds[1] + contentBounds[3]) / 2;
@@ -388,6 +392,43 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return setTimeout(() => (bestTarget._viewTransition = undefined), activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
     }
 
+    /// copies values from the targetDoc (which is the prototype of the pinDoc) to
+    /// reserved fields on the pinDoc so that those values can be restored to the
+    /// target doc when navigating to it.
+    @action
+    static pinDocView(pinDoc: Doc, pinProps: PinProps | undefined) {
+        if (pinProps?.pinWithView) {
+            // If pinWithView option set then update scale and x / y props of slide
+            const bounds = pinProps.pinWithView.bounds;
+            pinDoc.presPinView = true;
+            pinDoc.presPinViewX = bounds.left + bounds.width / 2;
+            pinDoc.presPinViewY = bounds.top + bounds.height / 2;
+            pinDoc.presPinViewScale = pinProps.pinWithView.scale;
+            pinDoc.contentBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
+        }
+        if (pinProps?.pinDocView) {
+            const { scrollable, pannable, temporal, clippable } = this.pinDataTypes(pinDoc);
+            pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable;
+
+            if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop;
+            else if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth;
+            else if (temporal) pinDoc.presEndTime = NumCast((pinDoc.presStartTime = pinDoc._currentTimecode)) + 0.1;
+            else if (pannable) {
+                const panX = NumCast(pinDoc._panX);
+                const panY = NumCast(pinDoc._panY);
+                const pw = NumCast(pinProps.panelWidth);
+                const ph = NumCast(pinProps.panelHeight);
+                const ps = NumCast(pinDoc._viewScale);
+                if (pw && ph && ps) {
+                    pinDoc.contentBounds = new List<number>([panX - pw / 2 / ps, panY - ph / 2 / ps, panX + pw / 2 / ps, panY + ph / 2 / ps]);
+                }
+                pinDoc.presPinViewX = panX;
+                pinDoc.presPinViewY = panY;
+                pinDoc.presPinViewScale = ps;
+            }
+        }
+    }
+
     /**
      * This method makes sure that cursor navigates to the element that
      * has the option open and last in the group.
@@ -1540,26 +1581,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     }
 
-    @computed get effectDirection(): string {
-        let effect = '';
+    @computed get effectDirection() {
+        // prettier-ignore
         switch (this.activeItem.presEffectDirection) {
-            case 'left':
-                effect = 'Enter from left';
-                break;
-            case 'right':
-                effect = 'Enter from right';
-                break;
-            case 'top':
-                effect = 'Enter from top';
-                break;
-            case 'bottom':
-                effect = 'Enter from bottom';
-                break;
-            default:
-                effect = 'Enter from center';
-                break;
+            case 'left': return 'Enter from left'; 
+            case 'right': return 'Enter from right'; 
+            case 'top':  return'Enter from top';
+            case 'bottom': return 'Enter from bottom';
         }
-        return effect;
+        return 'Enter from center';
     }
 
     @undoBatch
@@ -1582,179 +1612,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
     };
 
-    @computed get presPinViewOptionsDropdown() {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: 'auto', width: 16, filter: 'invert(1)' }} />;
-        return (
-            <>
-                {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : null}
-                <div className="ribbon-doubleButton">
-                    <Tooltip
-                        title={
-                            <>
-                                <div className="dash-tooltip">{activeItem.presPinView ? 'Turn off pin with view' : 'Turn on pin with view'}</div>
-                            </>
-                        }>
-                        <div
-                            className="ribbon-toggle"
-                            style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : '' }}
-                            onClick={() => {
-                                activeItem.presPinView = !activeItem.presPinView;
-                                targetDoc.presPinView = activeItem.presPinView;
-                                if (activeItem.presPinView) {
-                                    if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) {
-                                        const scroll = targetDoc._scrollTop;
-                                        activeItem.presPinView = true;
-                                        activeItem.presPinViewScroll = scroll;
-                                    } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                                        activeItem.presStartTime = targetDoc._currentTimecode;
-                                        activeItem.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                                    } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
-                                        const x = targetDoc._panX;
-                                        const y = targetDoc._panY;
-                                        const scale = targetDoc._viewScale;
-                                        activeItem.presPinView = true;
-                                        activeItem.presPinViewX = x;
-                                        activeItem.presPinViewY = y;
-                                        activeItem.presPinViewScale = scale;
-                                    } else if (targetDoc.type === DocumentType.COMPARISON) {
-                                        const width = targetDoc._clipWidth;
-                                        activeItem.presPinClipWidth = width;
-                                        activeItem.presPinView = true;
-                                    }
-                                }
-                            }}>
-                            {presPinWithViewIcon}
-                        </div>
-                    </Tooltip>
-                    {activeItem.presPinView ? (
-                        <Tooltip
-                            title={
-                                <>
-                                    <div className="dash-tooltip">{'Update the pinned view with the view of the selected document'}</div>
-                                </>
-                            }>
-                            <div
-                                className="ribbon-button"
-                                onClick={() => {
-                                    if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
-                                        const scroll = targetDoc._scrollTop;
-                                        activeItem.presPinViewScroll = scroll;
-                                    } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                                        activeItem.presStartTime = targetDoc._currentTimecode;
-                                        activeItem.presStartTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                                    } else if (targetDoc.type === DocumentType.COMPARISON) {
-                                        const clipWidth = targetDoc._clipWidth;
-                                        activeItem.presPinClipWidth = clipWidth;
-                                    } else {
-                                        const x = targetDoc._panX;
-                                        const y = targetDoc._panY;
-                                        const scale = targetDoc._viewScale;
-                                        activeItem.presPinViewX = x;
-                                        activeItem.presPinViewY = y;
-                                        activeItem.presPinViewScale = scale;
-                                    }
-                                }}>
-                                Update
-                            </div>
-                        </Tooltip>
-                    ) : null}
-                </div>
-            </>
-        );
-    }
-
-    @computed get panOptionsDropdown() {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        return (
-            <>
-                {this.panable ? (
-                    <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}>
-                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                            <div className="presBox-subheading">Pan X</div>
-                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                                <input
-                                    className="presBox-input"
-                                    style={{ textAlign: 'left', width: 50 }}
-                                    type="number"
-                                    value={NumCast(activeItem.presPinViewX)}
-                                    onKeyDown={e => e.stopPropagation()}
-                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                        const val = e.target.value;
-                                        activeItem.presPinViewX = Number(val);
-                                    })}
-                                />
-                            </div>
-                        </div>
-                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                            <div className="presBox-subheading">Pan Y</div>
-                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                                <input
-                                    className="presBox-input"
-                                    style={{ textAlign: 'left', width: 50 }}
-                                    type="number"
-                                    value={NumCast(activeItem.presPinViewY)}
-                                    onKeyDown={e => e.stopPropagation()}
-                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                        const val = e.target.value;
-                                        activeItem.presPinViewY = Number(val);
-                                    })}
-                                />
-                            </div>
-                        </div>
-                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                            <div className="presBox-subheading">Scale</div>
-                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                                <input
-                                    className="presBox-input"
-                                    style={{ textAlign: 'left', width: 50 }}
-                                    type="number"
-                                    value={NumCast(activeItem.presPinViewScale)}
-                                    onKeyDown={e => e.stopPropagation()}
-                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                        const val = e.target.value;
-                                        activeItem.presPinViewScale = Number(val);
-                                    })}
-                                />
-                            </div>
-                        </div>
-                    </div>
-                ) : null}
-            </>
-        );
-    }
-
-    @computed get scrollOptionsDropdown() {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        return (
-            <>
-                {this.scrollable ? (
-                    <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}>
-                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                            <div className="presBox-subheading">Scroll</div>
-                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                                <input
-                                    className="presBox-input"
-                                    style={{ textAlign: 'left', width: 50 }}
-                                    type="number"
-                                    value={NumCast(activeItem.presPinViewScroll)}
-                                    onKeyDown={e => e.stopPropagation()}
-                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                        const val = e.target.value;
-                                        activeItem.presPinViewScroll = Number(val);
-                                    })}
-                                />
-                            </div>
-                        </div>
-                    </div>
-                ) : null}
-            </>
-        );
-    }
-
     @computed get mediaStopSlides() {
         const activeItem: Doc = this.activeItem;
         const list = this.childDocs.map((doc, i) => {
@@ -2102,42 +1959,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     createTemplate = (layout: string, input?: string) => {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        let x = 0;
-        let y = 0;
-        if (activeItem && targetDoc) {
-            x = NumCast(targetDoc.x);
-            y = NumCast(targetDoc.y) + NumCast(targetDoc._height) + 20;
-        }
-        let doc = undefined;
-        const title = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' });
-        const subtitle = Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' });
-        const header = Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' });
-        const contentTitle = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' });
-        const content = Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' });
-        const content1 = Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' });
-        const content2 = Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' });
+        const x = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.x) : 0;
+        const y = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.y) + NumCast(this.targetDoc._height) + 20 : 0;
+        const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' });
+        const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' });
+        const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' });
+        const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' });
+        const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' });
+        const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' });
+        const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' });
+        // prettier-ignore
         switch (layout) {
-            case 'blank':
-                doc = Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x: x, y: y });
-                break;
-            case 'title':
-                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
-                break;
-            case 'header':
-                doc = Docs.Create.FreeformDocument([header], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
-                break;
-            case 'content':
-                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
-                break;
-            case 'twoColumns':
-                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
-                break;
-            default:
-                break;
+            case 'blank': return Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x, y });
+            case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
+            case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
+            case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
+            case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x, y })
         }
-        return doc;
     };
 
     // Dropdown that appears when the user wants to begin presenting (either minimize or sidebar view)
@@ -2201,42 +2039,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * Returns the collection type as a string for headers
      */
-    @computed get stringType(): string {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        let type: string = '';
-        if (activeItem) {
-            switch (targetDoc.type) {
-                case DocumentType.PDF:
-                    type = 'PDF';
-                    break;
-                case DocumentType.RTF:
-                    type = 'Text node';
-                    break;
-                case DocumentType.COL:
-                    type = 'Collection';
-                    break;
-                case DocumentType.AUDIO:
-                    type = 'Audio';
-                    break;
-                case DocumentType.VID:
-                    type = 'Video';
-                    break;
-                case DocumentType.IMG:
-                    type = 'Image';
-                    break;
-                case DocumentType.WEB:
-                    type = 'Web page';
-                    break;
-                case DocumentType.MAP:
-                    type = 'Map';
-                    break;
-                default:
-                    type = 'Other node';
-                    break;
+    @computed get stringType() {
+        if (this.activeItem) {
+            // prettier-ignore
+            switch (this.targetDoc.type) {
+                case DocumentType.PDF: return 'PDF';
+                case DocumentType.RTF: return 'Text node';
+                case DocumentType.COL: return 'Collection';
+                case DocumentType.AUDIO: return 'Audio';
+                case DocumentType.VID: return 'Video';
+                case DocumentType.IMG: return 'Image';
+                case DocumentType.WEB: return 'Web page';
+                case DocumentType.MAP: return 'Map';
+                default: return 'Other node';
             }
         }
-        return type;
+        return '';
     }
 
     @observable private openActiveColorPicker: boolean = false;
@@ -2845,10 +2663,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             case DocumentType.PDF || DocumentType.RTF || DocumentType.WEB:
                 this.updateList(activeItem.frameList);
                 break;
-            case DocumentType.COL:
-                break;
-            default:
-                break;
         }
     };
 
-- 
cgit v1.2.3-70-g09d2

