From 972839216c14baa5c9eaf80e1fb2fb2694bbb72c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 8 Oct 2024 22:51:46 -0400
Subject: modified how buttons are laid out on carousel and comparison views so
 that text boxes can reflow around them.  extracted flashcard pratice into its
 own component and applied it to carousel3D and carousel

---
 .../views/collections/CollectionCarousel3DView.tsx | 70 ++++++++++++++++++----
 1 file changed, 57 insertions(+), 13 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index c5da8e037..1583f0e0c 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { computed, makeObservable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { returnZero } from '../../../ClientUtils';
@@ -9,12 +9,13 @@ import { Id } from '../../../fields/FieldSymbols';
 import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
+import { Transform } from '../../util/Transform';
 import { StyleProp } from '../StyleProp';
 import { DocumentView } from '../nodes/DocumentView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import './CollectionCarousel3DView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { Transform } from '../../util/Transform';
+import { FlashcardPracticeUI } from './FlashcardPracticeUI';
 
 // eslint-disable-next-line @typescript-eslint/no-require-imports
 const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss');
@@ -24,6 +25,9 @@ export class CollectionCarousel3DView extends CollectionSubView() {
     @computed get scrollSpeed() {
         return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; // default scroll speed
     }
+    _sideBtnWidth = 35;
+    @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
+
     constructor(props: SubCollectionViewProps) {
         super(props);
         makeObservable(this);
@@ -43,7 +47,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
     };
 
     @computed get carouselItems() {
-        return this.childLayoutPairs.filter(pair => pair.layout.type !== DocumentType.LINK);
+        return this.childLayoutPairs.filter(pair => pair.layout.type !== DocumentType.LINK).filter(pair => !this._filterFunc?.(pair.layout));
     }
 
     centerScale = Number(CAROUSEL3D_CENTER_SCALE);
@@ -53,22 +57,17 @@ export class CollectionCarousel3DView extends CollectionSubView() {
     onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
     isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
     isChildContentActive = () => !!this.isContentActive();
+    contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
     childScreenLeftToLocal = () =>
-        this._props
-            .ScreenToLocalTransform()
-            .scale(this._props.NativeDimScaling?.() || 1)
+        this.contentScreenToLocalXf()
             .translate(-(this.panelWidth() - this.panelWidth() * this.sideScale) / 2, -(this.panelHeight() - this.panelHeight() * this.sideScale) / 2 - (Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight())
             .scale(1 / this.sideScale);
     childScreenRightToLocal = () =>
-        this._props
-            .ScreenToLocalTransform()
-            .scale(this._props.NativeDimScaling?.() || 1)
+        this.contentScreenToLocalXf()
             .translate(-2 * this.panelWidth() - (this.panelWidth() - this.panelWidth() * this.sideScale) / 2, -(this.panelHeight() - this.panelHeight() * this.sideScale) / 2 - (Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight())
             .scale(1 / this.sideScale);
     childCenterScreenToLocal = () =>
-        this._props
-            .ScreenToLocalTransform()
-            .scale(this._props.NativeDimScaling?.() || 1)
+        this.contentScreenToLocalXf()
             .translate(
                 -this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, // Focused Doc is shifted right by 1/3 panel width then left by increased size percent of center * 1/2 * panel width / 3
                 -((Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2
@@ -119,7 +118,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
 
     changeSlide = (direction: number) => {
         DocumentView.DeselectAll();
-        this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + direction + this.carouselItems.length) % this.carouselItems.length;
+        this.layoutDoc._carousel_index = !this.curDoc() ? 0 : (NumCast(this.layoutDoc._carousel_index) + direction + this.carouselItems.length) % (this.carouselItems.length || 1);
     };
 
     onArrowClick = (direction: number) => {
@@ -192,6 +191,35 @@ export class CollectionCarousel3DView extends CollectionSubView() {
         return this.panelWidth() * (1 - index);
     }
 
+    /**
+     * How much the content of the carousel view is being scaled based on its nesting and its fit-to-width settings
+     */
+    @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
+
+    /**
+     * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
+     */
+    @computed get maxWidgetScale() {
+        const maxWidgetSize = Math.min(this._sideBtnWidth * this.contentScaling, 0.1 * NumCast(this.layoutDoc.width, 1));
+        return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
+    }
+    /**
+     * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
+     */
+    @computed get uiBtnScaleTransform() {  return this.maxWidgetScale * Math.min(1, this.contentScaling); } // prettier-ignore
+    screenXPadding = () => (this.uiBtnScaleTransform * this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) / this._props.ScreenToLocalTransform().Scale;
+
+    docViewProps = () => ({
+        ...this._props, //
+        isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
+        isContentActive: this.isChildContentActive,
+        ScreenToLocalTransform: this.contentScreenToLocalXf,
+    });
+    carouselItemsFunc = () => this.carouselItems.map(pair => pair.layout);
+    @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
+    answered = (correct: boolean) => (!correct || !this.curDoc()) && this.changeSlide(1);
+    curDoc = () => this.carouselItems[NumCast(this.layoutDoc._carousel_index)]?.layout;
+
     render() {
         return (
             <div
@@ -206,6 +234,22 @@ export class CollectionCarousel3DView extends CollectionSubView() {
                 </div>
                 {this.buttons}
                 <div className="dot-bar">{this.dots}</div>
+                <FlashcardPracticeUI
+                    setFilterFunc={this.setFilterFunc}
+                    fieldKey={this.fieldKey}
+                    sideBtnWidth={this._sideBtnWidth}
+                    carouselItems={this.carouselItemsFunc}
+                    childDocs={this.childDocs}
+                    advance={this.answered}
+                    curDoc={this.curDoc}
+                    practiceBtnOffset={this._sideBtnWidth * 4}
+                    layoutDoc={this.layoutDoc}
+                    maxWidgetScale={this.maxWidgetScale}
+                    uiBtnScaleTransform={this.uiBtnScaleTransform}
+                    ScreenToLocalBoxXf={this.ScreenToLocalBoxXf}
+                    renderDepth={this._props.renderDepth}
+                    docViewProps={this.docViewProps}
+                />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2

