aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/CollectionCarouselView.tsx
diff options
context:
space:
mode:
authoralyssaf16 <alyssa_feinberg@brown.edu>2024-10-08 10:49:31 -0400
committeralyssaf16 <alyssa_feinberg@brown.edu>2024-10-08 10:49:31 -0400
commitcaceff7f37b4e49621bc3495bf1d51fcc3a79957 (patch)
tree78b742655d8b362f78f23a54ab55a4b1c3db92ae /src/client/views/collections/CollectionCarouselView.tsx
parenta606005a5934913c38fba9b73886ee6e743aa635 (diff)
parentfc3e2b18b9dad38920e1cec5e58bf9fbd06f4aaf (diff)
Merge branch 'alyssa-starter' of https://github.com/brown-dash/Dash-Web into alyssa-starter
Diffstat (limited to 'src/client/views/collections/CollectionCarouselView.tsx')
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx176
1 files changed, 96 insertions, 80 deletions
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 936226baf..559dcfe2a 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -7,7 +7,6 @@ import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DocumentType } from '../../documents/DocumentTypes';
-import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { StyleProp } from '../StyleProp';
import { TagItem } from '../TagsView';
@@ -36,6 +35,8 @@ export class CollectionCarouselView extends CollectionSubView() {
get sideField() { return "_" + this.fieldKey + "_usePath"; } // prettier-ignore
get starField() { return "#star"; } // prettier-ignore
+ _sideBtnWidth = 35;
+
_fadeTimer: NodeJS.Timeout | undefined;
constructor(props: SubCollectionViewProps) {
@@ -155,7 +156,11 @@ export class CollectionCarouselView extends CollectionSubView() {
onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
captionWidth = () => this._props.PanelWidth() - 2 * this.marginX;
- contentScreentToLocalXf = () => this._props.ScreenToLocalTransform().translate(-NumCast(this.layoutDoc.xMargin), -NumCast(this.layoutDoc.yMargin));
+ contentScreenToLocalXf = () =>
+ this._props
+ .ScreenToLocalTransform()
+ .translate(-NumCast(this.layoutDoc.xMargin), -NumCast(this.layoutDoc.yMargin))
+ .scale(this._props.NativeDimScaling?.() || 1);
contentPanelWidth = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc.xMargin);
@@ -168,9 +173,9 @@ export class CollectionCarouselView extends CollectionSubView() {
? false
: undefined;
- childScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
-
renderDoc = (doc: Doc, showCaptions: boolean, overlayFunc?: (r: DocumentView | null) => void) => {
+ const screenScale = this.ScreenToLocalBoxXf().Scale;
+ const fitWidthScale = (NumCast(this.Document.width, 1) / NumCast(this.carouselItems[this.carouselIndex]?._width)) * (this._props.NativeDimScaling?.() || 1);
return (
<DocumentView
{...this._props}
@@ -195,10 +200,10 @@ export class CollectionCarouselView extends CollectionSubView() {
childFilters={this.childDocFilters}
hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)}
addDocument={this._props.addDocument}
- ScreenToLocalTransform={this.contentScreentToLocalXf}
+ ScreenToLocalTransform={this.contentScreenToLocalXf}
PanelWidth={this.contentPanelWidth}
PanelHeight={this.contentPanelHeight}
- xPadding={35}
+ xPadding={(this._sideBtnWidth * Math.min(this.maxWidgetScale, screenScale * screenScale)) / fitWidthScale} // padding shrinks based on screenScale to maintain its size, and then again by screenSize to get smaller
/>
);
};
@@ -259,46 +264,6 @@ export class CollectionCarouselView extends CollectionSubView() {
);
}
- addFlashcard() {
- const newDoc = Docs.Create.ComparisonDocument('', { _layout_isFlashcard: true, _width: 300, _height: 300 });
- this.addDocument?.(newDoc);
- }
-
- @computed get buttons() {
- if (!this.carouselItems?.[this.carouselIndex]) return null;
- return (
- <>
- <div>
- {/* <Tooltip title="add new flashcard to pile">
- <div key="add" className="carouselView-add" onClick={this.addFlashcard}>
- <FontAwesomeIcon icon="plus" color={this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)].layout[this.starField] ? 'yellow' : 'gray'} size="1x" />
- </div>
- </Tooltip> */}
- </div>
- <div key="back" className="carouselView-back" onClick={this.goback}>
- <FontAwesomeIcon icon="chevron-left" size="2x" />
- </div>
- <div key="fwd" className="carouselView-fwd" onClick={this.advance}>
- <FontAwesomeIcon icon="chevron-right" size="2x" />
- </div>
- {this.practiceMode == practiceMode.PRACTICE ? (
- <div>
- <Tooltip title="Incorrect. View again later.">
- <div key="remove" className="carouselView-remove" onClick={e => this.setPracticeVal(e, practiceVal.MISSED)}>
- <FontAwesomeIcon icon="xmark" color="red" size="1x" />
- </div>
- </Tooltip>
- <Tooltip title="Correct">
- <div key="check" className="carouselView-check" onClick={e => this.setPracticeVal(e, practiceVal.CORRECT)}>
- <FontAwesomeIcon icon="check" color="green" size="1x" />
- </div>
- </Tooltip>
- </div>
- ) : null}
- </>
- );
- }
-
togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
toggleFilterMode = () => Doc.setDocFilter(this.Document, 'tags', this.starField, 'check', true);
setColor = (mode: practiceMode | cardMode, which: string) => (which === mode ? 'white' : 'light gray');
@@ -306,50 +271,75 @@ export class CollectionCarouselView extends CollectionSubView() {
@computed get filterDoc() {
return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter');
}
- filterHeight = () => NumCast(this.filterDoc?.height);
+ filterHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this.ScreenToLocalBoxXf().Scale);
filterWidth = () => (!this.filterDoc ? 1 : (this.filterHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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.Document.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 `scale(${this.maxWidgetScale * Math.min(1, this.contentScaling)})`;
+ }
@computed get menu() {
const curDoc = this.carouselItems?.[this.carouselIndex];
return (
- <div className="carouselView-menu">
+ <div className="carouselView-menu" style={{ height: this.filterHeight(), width: this.filterHeight(), transform: this.uiBtnScaleTransform }}>
{!this.filterDoc ? null : (
- <div style={{ height: this.filterHeight(), width: this.filterHeight() }}>
- <DocumentView
- {...this._props}
- Document={this.filterDoc}
- TemplateDataDocument={undefined}
- LayoutTemplate={this._props.childLayoutTemplate}
- LayoutTemplateString={this._props.childLayoutString}
- renderDepth={this._props.renderDepth + 1}
- NativeWidth={returnZero}
- NativeHeight={returnZero}
- fitWidth={undefined}
- showTags={false}
- hideFilterStatus={true}
- containerViewPath={this.childContainerViewPath}
- setContentViewBox={undefined}
- onDoubleClickScript={this.onContentDoubleClick}
- onClickScript={this.onContentClick}
- isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive}
- isContentActive={this.isChildContentActive}
- hideCaptions={true}
- childFilters={this.childDocFilters}
- hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)}
- addDocument={this._props.addDocument}
- ScreenToLocalTransform={this.contentScreentToLocalXf}
- PanelWidth={this.filterWidth}
- PanelHeight={this.filterHeight}
- />
- </div>
+ <DocumentView
+ {...this._props}
+ Document={this.filterDoc}
+ TemplateDataDocument={undefined}
+ LayoutTemplate={this._props.childLayoutTemplate}
+ LayoutTemplateString={this._props.childLayoutString}
+ renderDepth={this._props.renderDepth + 1}
+ NativeWidth={returnZero}
+ NativeHeight={returnZero}
+ fitWidth={undefined}
+ showTags={false}
+ hideFilterStatus={true}
+ containerViewPath={this.childContainerViewPath}
+ setContentViewBox={undefined}
+ onDoubleClickScript={this.onContentDoubleClick}
+ onClickScript={this.onContentClick}
+ isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive}
+ isContentActive={this.isChildContentActive}
+ hideCaptions={true}
+ childFilters={this.childDocFilters}
+ hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)}
+ addDocument={this._props.addDocument}
+ ScreenToLocalTransform={this.contentScreenToLocalXf}
+ PanelWidth={this.filterWidth}
+ PanelHeight={this.filterHeight}
+ />
)}
- <div className="carouselView-practiceModes" style={{ display: BoolCast(curDoc?._layout_isFlashcard) ? undefined : 'none' }}>
+ <div
+ className="carouselView-practiceModes"
+ style={{
+ transformOrigin: `0px ${-this.filterHeight()}px`,
+ transform: `scale(${Math.max(1, 1 / this.ScreenToLocalBoxXf().Scale / this.maxWidgetScale)})`,
+ display: BoolCast(curDoc?._layout_isFlashcard) ? undefined : 'none',
+ }}>
<Tooltip title="Practice flashcards using GPT">
- <div key="back" className="carouselView-quiz" onClick={() => this.togglePracticeMode(practiceMode.QUIZ)}>
+ <div key="back" className="carouselView-quiz" style={{ width: this.filterWidth(), height: this.filterHeight() }} onClick={() => this.togglePracticeMode(practiceMode.QUIZ)}>
<FontAwesomeIcon icon="file-pen" color={this.setColor(practiceMode.QUIZ, StrCast(this.practiceMode))} size="1x" />
</div>
</Tooltip>
<Tooltip title={this.practiceMode === practiceMode.PRACTICE ? 'Exit practice mode' : 'Practice flashcards manually'}>
- <div key="back" className="carouselView-practice" onClick={() => this.togglePracticeMode(practiceMode.PRACTICE)}>
+ <div key="back" className="carouselView-practice" style={{ width: this.filterWidth(), height: this.filterHeight() }} onClick={() => this.togglePracticeMode(practiceMode.PRACTICE)}>
<FontAwesomeIcon icon="check" color={this.setColor(practiceMode.PRACTICE, StrCast(this.practiceMode))} size="1x" />
</div>
</Tooltip>
@@ -357,6 +347,32 @@ export class CollectionCarouselView extends CollectionSubView() {
</div>
);
}
+ @computed get buttons() {
+ return (
+ <>
+ <div key="back" className="carouselView-back" style={{ transform: this.uiBtnScaleTransform }} onClick={this.goback}>
+ <FontAwesomeIcon icon="chevron-left" size="2x" />
+ </div>
+ <div key="fwd" className="carouselView-fwd" style={{ transform: this.uiBtnScaleTransform }} onClick={this.advance}>
+ <FontAwesomeIcon icon="chevron-right" size="2x" />
+ </div>
+ {this.practiceMode == practiceMode.PRACTICE ? (
+ <div style={{ transform: this.uiBtnScaleTransform, bottom: `${this._sideBtnWidth}px`, height: `${this._sideBtnWidth}px`, position: 'absolute', width: `100%` }}>
+ <Tooltip title="Incorrect. View again later.">
+ <div key="remove" className="carouselView-remove" onClick={e => this.setPracticeVal(e, practiceVal.MISSED)}>
+ <FontAwesomeIcon icon="xmark" color="red" size="1x" />
+ </div>
+ </Tooltip>
+ <Tooltip title="Correct">
+ <div key="check" className="carouselView-check" onClick={e => this.setPracticeVal(e, practiceVal.CORRECT)}>
+ <FontAwesomeIcon icon="check" color="green" size="1x" />
+ </div>
+ </Tooltip>
+ </div>
+ ) : null}
+ </>
+ );
+ }
render() {
return (
@@ -388,7 +404,7 @@ export class CollectionCarouselView extends CollectionSubView() {
)}
</div>
{!this.Document._chromeHidden ? this.menu : null}
- {!this.Document._chromeHidden ? this.buttons : null}
+ {!this.Document._chromeHidden && this.carouselItems?.[this.carouselIndex] ? this.buttons : null}
</div>
);
}