From 66f904921ef519e1b54cb013ea7fafc640ae3dc7 Mon Sep 17 00:00:00 2001
From: aidahosa1 <aisosa_idahosa@brown.edu>
Date: Tue, 19 Mar 2024 16:21:34 -0400
Subject: working on implementing card logic

---
 .../views/collections/CollectionCardDeckView.tsx   | 281 +++++++++++----------
 1 file changed, 151 insertions(+), 130 deletions(-)

(limited to 'src/client/views/collections/CollectionCardDeckView.tsx')

diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index feb9e61cc..e35f7b421 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,164 +1,185 @@
-import { action, computed, IReactionDisposer, makeObservable } from 'mobx';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { Utils, emptyFunction, returnFalse, returnZero } from '../../../Utils';
 import { Doc, DocListCast } from '../../../fields/Doc';
-import { ScriptField } from '../../../fields/ScriptField';
-import { NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
-import { DocUtils } from '../../documents/Documents';
+import { Id } from '../../../fields/FieldSymbols';
+import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { DragManager } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
-import { OpenWhere } from '../nodes/DocumentView';
-import { computePassLayout, computeStarburstLayout, computeCardDeckLayout } from './collectionFreeForm';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import './CollectionPileView.scss';
+import { StyleProp } from '../StyleProvider';
+import { DocumentView } from '../nodes/DocumentView';
+import { FocusViewOptions } from '../nodes/FieldView';
+import './CollectionCarousel3DView.scss';
 import { CollectionSubView } from './CollectionSubView';
-import { dropActionType } from '../../util/DragManager';
-
+const { default: { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
 @observer
 export class CollectionCardView extends CollectionSubView() {
-    _originalChrome: any = '';
-    _disposers: { [name: string]: IReactionDisposer } = {};
-
+    @computed get scrollSpeed() {
+        return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; //default scroll speed
+    }
     constructor(props: any) {
         super(props);
         makeObservable(this);
     }
 
-    componentDidMount() {
-        if (this.layoutEngine() !== computePassLayout.name && this.layoutEngine() !== computeCardDeckLayout.name) {
-            this.Document._freeform_cardEngine = computePassLayout.name;
-        }
-        this._originalChrome = this.layoutDoc._chromeHidden;
-        this.layoutDoc._chromeHidden = true;
-    }
+    private _dropDisposer?: DragManager.DragDropDisposer;
+
     componentWillUnmount() {
-        this.layoutDoc._chromeHidden = this._originalChrome; 
-        Object.values(this._disposers).forEach(disposer => disposer?.());
+        this._dropDisposer?.();
     }
 
-    layoutEngine = () => StrCast(this.Document._freeform_cardEngine);
-
-    @undoBatch
-    addCardDoc = (doc: Doc | Doc[]) => {
-        (doc instanceof Doc ? [doc] : doc).map(d => DocUtils.iconify(d));
-        return this._props.addDocument?.(doc) || false;
+    protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+        this._dropDisposer?.();
+        if (ele) {
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
+        }
     };
 
-    @undoBatch
-    removeCardDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(d => Doc.deiconifyView(d));
-        const ret = this._props.moveDocument?.(doc, targetCollection, addDoc) || false;
-        if (ret && !DocListCast(this.dataDoc[this.fieldKey ?? 'data']).length) this.DocumentView?.()._props.removeDocument?.(this.Document);
-        return ret;
+    centerScale = Number(CAROUSEL3D_CENTER_SCALE);
+    panelWidth = () => this._props.PanelWidth() / 3;
+    panelHeight = () => this._props.PanelHeight() * Number(CAROUSEL3D_SIDE_SCALE);
+    onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
+    isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
+    isChildContentActive = () => (this.isContentActive() ? true : false);
+    childScreenToLocal = () =>
+        this._props // document's left is the panel shifted by the doc's index * panelWidth/#docs.  But it scales by centerScale around its center, so it's left moves left by the distance of the left from the center (panelwidth/2) * the scale delta (centerScale-1)
+            .ScreenToLocalTransform() // the top behaves the same way ecept it's shifted by the 'top' amount specified for the panel in css and then by the scale factor.
+            .translate(-this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, -((Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2)
+            .scale(1 / this.centerScale);
+
+    focus = (anchor: Doc, options: FocusViewOptions) => {
+        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
+        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return;
+        options.didMove = true;
+        const target = DocCast(anchor.annotationOn) ?? anchor;
+        const index = docs.indexOf(target);
+        index !== -1 && (this.layoutDoc._carousel_index = index); //if index is not -1, then assign index to this.layoutDoc._carousel_index
+        return undefined;
     };
 
-    @computed get toggleIcon() {
-        return ScriptField.MakeScript('documentView.iconify()', { documentView: 'any' });
-    }
-    @computed get contentEvents() {
-        const isCard = this.layoutEngine() === computeCardDeckLayout.name;
-        return this._props.isContentActive() && isCard ? undefined : 'none';
+    
+    @computed get content() {
+        const currentIndex = NumCast(this.layoutDoc._carousel_index);
+        const displayDoc = (childPair: { layout: Doc; data: Doc }) => {
+            return (
+                <DocumentView
+                    {...this._props}
+                    Document={childPair.layout}
+                    TemplateDataDocument={childPair.data}
+                    //suppressSetHeight={true}
+                    NativeWidth={returnZero}
+                    NativeHeight={returnZero}
+                    layout_fitWidth={undefined}
+                    onDoubleClickScript={this.onChildDoubleClick}
+                    renderDepth={this._props.renderDepth + 1}
+                    LayoutTemplate={this._props.childLayoutTemplate}
+                    LayoutTemplateString={this._props.childLayoutString}
+                    focus={this.focus}
+                    ScreenToLocalTransform={this.childScreenToLocal} //makes sure the box wrapper thing is in the right spot 
+                    isContentActive={this.isChildContentActive}
+                    isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
+                    PanelWidth={this.panelWidth}
+                    PanelHeight={this.panelHeight}
+                />
+            );
+        };
+
+        return this.childLayoutPairs.map((childPair, index) => {
+            return (
+                <div key={childPair.layout[Id]} className={`collectionCarousel3DView-item${index === currentIndex ? '-active' : ''} ${index}`} style={{ width: this.panelWidth() }}>
+                    {displayDoc(childPair)}
+                </div>
+            );
+        });
     }
 
-    // returns the contents of the cardSpread in a CollectionFreeFormView
-    @computed get contents() {
+    changeSlide = (direction: number) => {
+        SelectionManager.DeselectAll();
+        this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + direction + this.childLayoutPairs.length) % this.childLayoutPairs.length;
+    };
+
+    onArrowClick = (direction: number) => {
+        this.changeSlide(direction);
+        !this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = direction === 1 ? 'fwd' : 'back'); // while autoscroll is on, keep the other autoscroll button hidden
+        !this.layoutDoc.autoScrollOn && this.fadeScrollButton(); // keep pause button visible while autoscroll is on
+    };
+
+    interval?: number;
+    startAutoScroll = (direction: number) => {
+        this.interval = window.setInterval(() => {
+            this.changeSlide(direction);
+        }, this.scrollSpeed);
+    };
+
+    stopAutoScroll = () => {
+        window.clearInterval(this.interval);
+        this.interval = undefined;
+        this.fadeScrollButton();
+    };
+
+    toggleAutoScroll = (direction: number) => {
+        this.layoutDoc.autoScrollOn = this.layoutDoc.autoScrollOn ? false : true;
+        this.layoutDoc.autoScrollOn ? this.startAutoScroll(direction) : this.stopAutoScroll();
+    };
+
+    fadeScrollButton = () => {
+        window.setTimeout(() => {
+            !this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = 'none'); //fade away after 1.5s if it's not clicked.
+        }, 1500);
+    };
+
+    @computed get buttons() {
         return (
-            <div className="collectionPileView-innards" style={{ pointerEvents: this.contentEvents }}>
-                <CollectionFreeFormView
-                    {...this._props} //
-                    layoutEngine={this.layoutEngine}
-                    addDocument={this.addCardDoc}
-                    moveDocument={this.removeCardDoc}
-                    // pile children never have their contents active, but will be document active whenever the entire pile is.
-                    childContentsActive={returnFalse}
-                    childDocumentsActive={this._props.isDocumentActive}
-                    childDragAction={dropActionType.move}
-                    childClickScript={this.toggleIcon}
-                />
+            <div className="arrow-buttons">
+                <div title="click to go back" key="back" className="carousel3DView-back" onClick={e => this.onArrowClick(-1)} />
+                <div title="click to advance" key="fwd" className="carousel3DView-fwd" onClick={e => this.onArrowClick(1)} />
+                {/* {this.autoScrollButton} */}
             </div>
         );
     }
 
-    // // toggles the pileup between starburst to compact
-    // toggleStarburst = action(() => {
-    //     this.layoutDoc._freeform_scale = undefined;
-    //     if (this.layoutEngine() === computeStarburstLayout.name) {
-    //         if (NumCast(this.layoutDoc._width) !== NumCast(this.Document._starburstDiameter, 500)) {
-    //             this.Document._starburstDiameter = NumCast(this.layoutDoc._width);
-    //         }
-    //         const defaultSize = 110;
-    //         this.Document.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width) / 2 - NumCast(this.layoutDoc._freeform_pileWidth, defaultSize) / 2;
-    //         this.Document.y = NumCast(this.Document.y) + NumCast(this.layoutDoc._height) / 2 - NumCast(this.layoutDoc._freeform_pileHeight, defaultSize) / 2;
-    //         this.layoutDoc._width = NumCast(this.layoutDoc._freeform_pileWidth, defaultSize);
-    //         this.layoutDoc._height = NumCast(this.layoutDoc._freeform_pileHeight, defaultSize);
-    //         DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
-    //         this.layoutDoc._freeform_panX = 0;
-    //         this.layoutDoc._freeform_panY = -10;
-    //         this.Document._freeform_pileEngine = computePassLayout.name;
-    //     } else {
-    //         const defaultSize = NumCast(this.Document._starburstDiameter, 400);
-    //         this.Document.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width) / 2 - defaultSize / 2;
-    //         this.Document.y = NumCast(this.Document.y) + NumCast(this.layoutDoc._height) / 2 - defaultSize / 2;
-    //         this.layoutDoc._freeform_pileWidth = NumCast(this.layoutDoc._width);
-    //         this.layoutDoc._freeform_pileHeight = NumCast(this.layoutDoc._height);
-    //         this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0;
-    //         this.layoutDoc._width = this.layoutDoc._height = defaultSize;
-    //         this.layoutDoc.background;
-    //         this.Document._freeform_pileEngine = computeStarburstLayout.name;
-    //     }
-    // });
-
-    // for dragging documents out of the pileup view
-    _undoBatch: UndoManager.Batch | undefined;
-    pointerDown = (e: React.PointerEvent) => {
-        let dist = 0;
-        setupMoveUpEvents(
-            this,
-            e,
-            (e: PointerEvent, down: number[], delta: number[]) => {
-                if (this.layoutEngine() === 'pass' && this.childDocs.length && e.shiftKey) {
-                    dist += Math.sqrt(delta[0] * delta[0] + delta[1] * delta[1]);
-                    if (dist > 100) {
-                        if (!this._undoBatch) {
-                            this._undoBatch = UndoManager.StartBatch('layout pile');
-                        }
-                        const doc = this.childDocs[0];
-                        doc.x = e.clientX;
-                        doc.y = e.clientY;
-                        this._props.addDocTab(doc, OpenWhere.inParentFromScreen) && (this._props.removeDocument?.(doc) || false);
-                        dist = 0;
-                    }
-                }
-                return false;
-            },
-            () => {
-                this._undoBatch?.end();
-                this._undoBatch = undefined;
-            },
-            emptyFunction,
-            e.shiftKey && this.layoutEngine() === computePassLayout.name,
-            this.layoutEngine() === computePassLayout.name && e.shiftKey
-        ); // this sets _doubleTap
-    };
+    @computed get autoScrollButton() {
+        const whichButton = this.layoutDoc.showScrollButton;
+        return (
+            <>
+                <div className={`carousel3DView-back-scroll${whichButton === 'back' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.Document.backgroundColor)}` }} onClick={() => this.toggleAutoScroll(-1)}>
+                    {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon={'pause'} size={'1x'} /> : <FontAwesomeIcon icon={'angle-double-left'} size={'1x'} />}
+                </div>
+                <div className={`carousel3DView-fwd-scroll${whichButton === 'fwd' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.Document.backgroundColor)}` }} onClick={() => this.toggleAutoScroll(1)}>
+                    {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon={'pause'} size={'1x'} /> : <FontAwesomeIcon icon={'angle-double-right'} size={'1x'} />}
+                </div>
+            </>
+        );
+    }
 
-    // onClick for toggling the pileup view
-    // @undoBatch
-    // onClick = (e: React.MouseEvent) => {
-    //     if (e.button === 0) {
-    //         SelectionManager.DeselectAll();
-    //         this.toggleStarburst();
-    //         e.stopPropagation();
-    //     }
-    // };
+    @computed get dots() {
+        return this.childLayoutPairs.map((_child, index) => <div key={Utils.GenerateGuid()} className={`dot${index === NumCast(this.layoutDoc._carousel_index) ? '-active' : ''}`} onClick={() => (this.layoutDoc._carousel_index = index)} />);
+    }
+    @computed get translateX() {
+        const index = NumCast(this.layoutDoc._carousel_index);
+        return this.panelWidth() * (1 - index);
+    }
 
     render() {
         return (
-            <div className={`collectionPileView`} 
-            // onClick={this.onClick} 
-            onPointerDown={this.pointerDown} style={{ width: this._props.PanelWidth(), height: '100%' }}>
-                {this.contents}
+            <div
+                className="collectionCarousel3DView-outer"
+                ref={this.createDashEventsTarget}
+                style={{
+                    background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor),
+                    color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color),
+                }}>
+                <div className="carousel-wrapper" style={{ transform: `translateX(${this.translateX}px)` }}>
+                    {this.content}
+                </div>
+                {this.buttons}
+                <div className="dot-bar">{this.dots}</div>
             </div>
         );
     }
+
+
 }
-- 
cgit v1.2.3-70-g09d2

