From 585f03bf45df4ac7ed61d22c9dbe10d8e453199d Mon Sep 17 00:00:00 2001
From: alyssaf16
Date: Wed, 5 Jun 2024 15:06:15 -0400
Subject: Flashcards changes
---
src/client/views/collections/CollectionCarouselView.scss | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
(limited to 'src/client/views/collections/CollectionCarouselView.scss')
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index f115bb40a..975b352cf 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -1,5 +1,7 @@
.collectionCarouselView-outer {
height: 100%;
+ position: relative;
+ overflow: hidden;
.collectionCarouselView-caption {
height: 50;
display: inline-block;
@@ -11,7 +13,16 @@
width: 100%;
user-select: none;
}
+ .message {
+ justify-content: center;
+ align-items: center;
+ display: flex;
+ height: 60%;
+ z-index: -1;
+ // margin: 15px;
+ }
}
+
.carouselView-back,
.carouselView-fwd,
.carouselView-star,
@@ -21,7 +32,7 @@
display: flex;
top: 42.5%;
width: 30;
- height: 15%;
+ height: 30;
align-items: center;
border-radius: 5px;
justify-content: center;
--
cgit v1.2.3-70-g09d2
From d1b7e29761fa0395263bff3521f148170659ff62 Mon Sep 17 00:00:00 2001
From: alyssaf16
Date: Fri, 7 Jun 2024 21:48:34 -0400
Subject: Flashcard menus
---
src/client/views/MainView.tsx | 1 +
.../views/collections/CollectionCarouselView.scss | 42 ++++++++++++-
.../views/collections/CollectionCarouselView.tsx | 53 ++++++++++++----
src/client/views/nodes/ComparisonBox.tsx | 73 +++++++++++++++++-----
src/client/views/nodes/DocumentView.tsx | 14 ++---
5 files changed, 147 insertions(+), 36 deletions(-)
(limited to 'src/client/views/collections/CollectionCarouselView.scss')
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 31d88fb87..541b15006 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -548,6 +548,7 @@ export class MainView extends ObservableReactComponent<{}> {
fa.faRobot,
fa.faSatellite,
fa.faStar,
+ fa.faFilePen,
]
);
}
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 975b352cf..c4679e888 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -50,7 +50,7 @@
}
.carouselView-star {
top: 0;
- right: 20;
+ left: 0;
}
.carouselView-remove {
top: 80%;
@@ -60,6 +60,46 @@
top: 80%;
right: 52%;
}
+.carouselView-quiz {
+ position: absolute;
+ display: flex;
+ top: 5px;
+ right: 8px;
+ &:hover {
+ color: white;
+ }
+}
+
+.carouselView-practice {
+ position: absolute;
+ display: flex;
+ top: 22px;
+ right: 8px;
+ &:hover {
+ color: white;
+ }
+}
+.carouselView-starFilter {
+ position: absolute;
+ display: flex;
+ top: 40px;
+ right: 7px;
+ &:hover {
+ color: white;
+ }
+}
+
+.carouselView-menu {
+ position: absolute;
+ display: flex;
+ top: 2px;
+ right: 2px;
+ width: 30;
+ height: 60;
+ border-radius: 5px;
+ color: rgba(255, 255, 255, 0.5);
+ background: rgba(0, 0, 0, 0.1);
+}
.carouselView-back:hover,
.carouselView-fwd:hover {
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 53d14e6e0..8da808065 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -19,6 +19,7 @@ import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView } from './CollectionSubView';
+import { Tooltip } from '@mui/material';
enum cardMode {
PRACTICE = 'practice',
@@ -143,22 +144,26 @@ export class CollectionCarouselView extends CollectionSubView() {
captionWidth = () => this._props.PanelWidth() - 2 * this.marginX;
setFilterMode = (mode: cardMode) => {
this.layoutDoc.filterOp = mode;
+ console.log('MODE' + mode);
+ console.log('FILT' + this.layoutDoc.filterOp + ';');
if (mode == cardMode.STAR) this.move(1);
- if (mode == cardMode.QUIZ) this.carouselItems?.map(doc => (doc.layout[this.sideField] = undefined));
+ if (mode == cardMode.QUIZ) {
+ this.carouselItems?.map(doc => (doc.layout[this.sideField] = undefined));
+ }
this.carouselItems?.map(doc => (doc.layout[this.practiceField] = undefined));
};
- specificMenu = (): void => {
- const cm = ContextMenu.Instance;
+ // specificMenu = (): void => {
+ // const cm = ContextMenu.Instance;
- const revealOptions = cm.findByDescription('Filter Flashcards');
- const revealItems: ContextMenuProps[] = revealOptions && 'subitems' in revealOptions ? revealOptions.subitems : [];
- revealItems.push({description: 'All', event: () => {this.setFilterMode(cardMode.ALL);}, icon: 'layer-group',}); // prettier-ignore
- revealItems.push({description: 'Star', event: () => {this.setFilterMode(cardMode.STAR);}, icon: 'star',}); // prettier-ignore
- revealItems.push({description: 'Practice Mode', event: () => {this.setFilterMode(cardMode.PRACTICE);}, icon: 'check',}); // prettier-ignore
- cm.addItem({description: 'Quiz Cards', event: () => {this.setFilterMode(cardMode.QUIZ);}, icon: 'pencil',}); // prettier-ignore
- !revealOptions && cm.addItem({ description: 'Filter Flashcards', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' });
- //cm.addItem({description: 'Quiz Cards', event: () => {this.layoutDoc.filterOp = cardMode.QUIZ; this.clearContent()});
- };
+ // const revealOptions = cm.findByDescription('Filter Flashcards');
+ // const revealItems: ContextMenuProps[] = revealOptions && 'subitems' in revealOptions ? revealOptions.subitems : [];
+ // revealItems.push({description: 'All', event: () => {this.setFilterMode(cardMode.ALL);}, icon: 'layer-group',}); // prettier-ignore
+ // revealItems.push({description: 'Star', event: () => {this.setFilterMode(cardMode.STAR);}, icon: 'star',}); // prettier-ignore
+ // revealItems.push({description: 'Practice Mode', event: () => {this.setFilterMode(cardMode.PRACTICE);}, icon: 'check',}); // prettier-ignore
+ // cm.addItem({description: 'Quiz Cards', event: () => {this.setFilterMode(cardMode.QUIZ);}, icon: 'pencil',}); // prettier-ignore
+ // !revealOptions && cm.addItem({ description: 'Filter Flashcards', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' });
+ // //cm.addItem({description: 'Quiz Cards', event: () => {this.layoutDoc.filterOp = cardMode.QUIZ; this.clearContent()});
+ // };
@computed get content() {
const index = NumCast(this.layoutDoc._carousel_index);
const curDoc = this.carouselItems?.[index];
@@ -226,6 +231,28 @@ export class CollectionCarouselView extends CollectionSubView() {
);
}
+ @computed get menu() {
+ return (
+
+
+ (this.layoutDoc.filterOp === cardMode.QUIZ ? this.setFilterMode(cardMode.ALL) : this.setFilterMode(cardMode.QUIZ))}>
+
+
+
+
+ (this.layoutDoc.filterOp === cardMode.PRACTICE ? this.setFilterMode(cardMode.ALL) : this.setFilterMode(cardMode.PRACTICE))}>
+
+
+
+
+ (!this.layoutDoc.filterOp ? this.setFilterMode(cardMode.ALL) : this.setFilterMode(cardMode.STAR))}>
+
+
+
+
+ );
+ }
+
render() {
return (
Recently missed!
+ {this.menu}
{this.Document._chromeHidden || !this.layoutDoc.filterOp ? null : this.buttons}
);
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index f844892c5..084723d56 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -45,6 +45,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
@observable private _loading = false;
@observable private _errorMessage = '';
@observable private _outputMessage = '';
+ @observable private _isEmpty = false;
+
+ public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
@action handleInputChange = (e: React.ChangeEvent) => {
this._inputValue = e.target.value;
@@ -162,23 +165,29 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
};
clearDoc = undoable((fieldKey: string) => {
- delete this.dataDoc[fieldKey];
- this.dataDoc[fieldKey] = 'empty';
+ // delete this.dataDoc[fieldKey];
+ this.dataDoc[fieldKey] = undefined;
+ this._isEmpty = true;
+ // this.dataDoc[fieldKey] = 'empty';
+ console.log('HERE' + fieldKey + ';');
}, 'clear doc');
// clearDoc = (fieldKey: string) => delete this.dataDoc[fieldKey];
moveDoc = (doc: Doc, addDocument: (document: Doc | Doc[]) => boolean, which: string) => this.remDoc(doc, which) && addDocument(doc);
addDoc = (doc: Doc, which: string) => {
- if (this.dataDoc[which] && this.dataDoc[which] !== 'empty') return false;
+ if (this.dataDoc[which] && !this._isEmpty) return false;
this.dataDoc[which] = doc;
return true;
};
remDoc = (doc: Doc, which: string) => {
if (this.dataDoc[which] === doc) {
- this.dataDoc[which] = 'empty';
+ this._isEmpty = true;
+ // this.dataDoc[which] = 'empty';
+ console.log('HEREEEE');
this.dataDoc[which] = undefined;
return true;
}
+ console.log('FALSE');
return false;
};
@@ -268,8 +277,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
if (!this.layoutDoc[`_${this._props.fieldKey}_revealOp`] || this.layoutDoc[`_${this._props.fieldKey}_revealOp`] === 'flip') {
this.flipFlashcard();
- console.log('Print Front of cards: ' + (RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text ?? ''));
- console.log('Print Back of cards: ' + (RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text ?? ''));
+ // console.log('Print Front of cards: ' + (RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text ?? ''));
+ // console.log('Print Back of cards: ' + (RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text ?? ''));
}
})
}
@@ -297,10 +306,31 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
Ask GPT to create an answer on the back side of the flashcard
)
}>
- (!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? this.askGPT(GPTCallType.CHATCARD) : null)}>
+
(!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? this.askGPT(GPTCallType.CHATCARD) : null)}>
+
Create a flashcard pile }>
+ {
+ const collectionArr: Doc[] = [];
+ collectionArr.push(this.Document);
+ const newCol = Docs.Create.CarouselDocument(collectionArr, {
+ _width: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 250),
+ _height: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 200),
+ _layout_fitWidth: false,
+ _layout_autoHeight: true,
+ });
+ newCol['x'] = e.clientX - 820;
+ newCol['y'] = e.clientY - 640;
+ this._props.addDocument?.(newCol);
+ this._props.removeDocument?.(this.Document);
+ this.Document.embedContainer = newCol;
+ }}>
+
+
+
Hover to reveal}>
()
- {this.overlayAlternateIcon}
+ {/* Remove this side of the flashcard}>
+ this.closeDown(e, this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? this._props.fieldKey + '_1' : this._props.fieldKey + '_0')}>
+
+
+ */}
+ {/* {this.overlayAlternateIcon} */}
);
}
-
+ // this.closeDown(e, this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? this.fieldKey + '_0' : this.fieldKey + '_1')}
@action activateContent = () => {
this.childActive = true;
};
@@ -434,7 +471,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
hideLinkButton
pointerEvents={this.childActive ? undefined : returnNone}
/>
- {layoutString ? null : clearButton(whichSlot)}
+ {/* {layoutString ? null : clearButton(whichSlot)}
*/}
> // placeholder image if doc is missingleft: `${NumCast(this.layoutDoc.width, 200) - 33}px`
) : (
@@ -452,7 +489,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
this.activateContent();
}}
ref={ele => this.createDropTarget(ele, which, index)}>
- {displayDoc(which)}
+ {!this._isEmpty ? displayDoc(which) : null}
+ {/* {this.dataDoc[this.fieldKey + '_0'] !== 'empty' ? displayDoc(which) : null} */}
);
@@ -460,7 +498,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
const side = this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 1 : 0;
// add text box to each side when comparison box is first created
- if (!(this.dataDoc[this.fieldKey + '_0'] || this.dataDoc[this.fieldKey + '_0'] === 'empty')) {
+ // (!this.dataDoc[this.fieldKey + '_0'] && this.dataDoc[this._props.fieldKey + '_0'] !== 'empty')
+ if (!this.dataDoc[this.fieldKey + '_0'] && !this._isEmpty) {
const dataSplit = StrCast(this.dataDoc.data).split('Answer');
const newDoc = Docs.Create.TextDocument(dataSplit[1]);
// if there is text from the pdf ai cards, put the question on the front side.
@@ -468,7 +507,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
newDoc[DocData].text = dataSplit[1];
this.addDoc(newDoc, this.fieldKey + '_0');
}
- if (!(this.dataDoc[this.fieldKey + '_1'] || this.dataDoc[this.fieldKey + '_1'] === 'empty')) {
+ if (!this.dataDoc[this.fieldKey + '_1'] && !this._isEmpty) {
const dataSplit = StrCast(this.dataDoc.data).split('Answer');
const newDoc = Docs.Create.TextDocument(dataSplit[0]);
// if there is text from the pdf ai cards, put the answer on the alternate side.
@@ -478,6 +517,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
}
// render the QuizCards
+ console.log('GERE' + DocCast(this.Document.embedContainer).filterOp);
if (DocCast(this.Document.embedContainer) && DocCast(this.Document.embedContainer).filterOp === 'quiz') {
const text = StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text);
return (
@@ -489,6 +529,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
value={this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? this._outputValue : this._inputValue}
onChange={this.handleInputChange}
onScroll={e => e.stopPropagation()}
+ placeholder={!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? 'Enter a response for GPT to evaluate.' : ''}
readOnly={this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate'}>
{this._loading ? (
@@ -516,7 +557,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
{
this.hoverFlip('alternate');
}}
@@ -531,8 +572,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
) : null}
- {this.flashcardMenu}
- {/* {this.overlayAlternateIcon} */}
+ {this._props.isContentActive() ? this.flashcardMenu : null}
+ {this.overlayAlternateIcon}
);
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2f3357791..d4c31a5b3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -566,13 +566,13 @@ export class DocumentViewInternal extends DocComponent { this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover'; }, icon: 'hand-point-up' }); // prettier-ignore
- revealItems.push({ description: 'Flip', event: () => { this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip'; }, icon: 'rotate' }); // prettier-ignore
- !revealOptions && cm.addItem({ description: 'Reveal Options', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' });
- }
+ // if (this.Document._layout_isFlashcard) {
+ // const revealOptions = cm.findByDescription('Reveal Options');
+ // const revealItems: ContextMenuProps[] = revealOptions && 'subitems' in revealOptions ? revealOptions.subitems : [];
+ // revealItems.push({ description: 'Hover', event: () => { this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover'; }, icon: 'hand-point-up' }); // prettier-ignore
+ // revealItems.push({ description: 'Flip', event: () => { this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip'; }, icon: 'rotate' }); // prettier-ignore
+ // !revealOptions && cm.addItem({ description: 'Reveal Options', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' });
+ // }
if (this._props.bringToFront) {
const zorders = cm.findByDescription('ZOrder...');
--
cgit v1.2.3-70-g09d2
From 39784e909c68f139bb537151294d8db56d021158 Mon Sep 17 00:00:00 2001
From: alyssaf16
Date: Wed, 12 Jun 2024 12:31:50 -0400
Subject: flashcard
---
.../views/collections/CollectionCarouselView.scss | 11 +++-
.../views/collections/CollectionCarouselView.tsx | 55 ++++++++++++++-----
src/client/views/nodes/ComparisonBox.scss | 13 ++++-
src/client/views/nodes/ComparisonBox.tsx | 62 +++++++++++++++-------
src/client/views/pdf/AnchorMenu.tsx | 21 ++++++--
src/client/views/pdf/PDFViewer.tsx | 1 +
6 files changed, 124 insertions(+), 39 deletions(-)
(limited to 'src/client/views/collections/CollectionCarouselView.scss')
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index c4679e888..b402a7a32 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -27,10 +27,10 @@
.carouselView-fwd,
.carouselView-star,
.carouselView-remove,
-.carouselView-check {
+.carouselView-check,
+.carouselView-add {
position: absolute;
display: flex;
- top: 42.5%;
width: 30;
height: 30;
align-items: center;
@@ -43,15 +43,22 @@
}
}
.carouselView-fwd {
+ top: 42.5%;
right: 20;
}
.carouselView-back {
+ top: 42.5%;
left: 20;
}
.carouselView-star {
top: 0;
left: 0;
}
+.carouselView-add {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+}
.carouselView-remove {
top: 80%;
left: 52%;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 9cf7765dd..b5b6e1f4b 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -4,6 +4,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import { Docs } from '../../documents/Documents';
import * as React from 'react';
import { StopEvent, returnFalse, returnOne, returnTrue, returnZero } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
@@ -48,7 +49,8 @@ export class CollectionCarouselView extends CollectionSubView() {
constructor(props: any) {
super(props);
makeObservable(this);
- this.layoutDoc.filterOp = cardMode.ALL;
+ // this.layoutDoc.filterOp = cardMode.ALL;
+ Doc.setDocFilter(this.Document, 'data_star', undefined, 'match');
this.layoutDoc.practiceMode = practiceMode.NORMAL;
this.layoutDoc._carousel_index = 0;
}
@@ -177,9 +179,12 @@ export class CollectionCarouselView extends CollectionSubView() {
this.layoutDoc.filterOp = mode;
switch (mode) {
case cardMode.STAR:
+ Doc.setDocFilter(this.Document, 'data_star', true, 'match');
this.move(1);
break;
- default: this.setFilterMessage(undefined); // prettier-ignore
+ default:
+ this.setFilterMessage(undefined); // prettier-ignore
+ Doc.setDocFilter(this.Document, 'data_star', true, 'remove');
}
};
@@ -197,6 +202,7 @@ export class CollectionCarouselView extends CollectionSubView() {
NativeHeight={returnZero}
fitWidth={undefined}
setContentViewBox={undefined}
+ childFilters={this.childDocFilters}
onDoubleClickScript={this.onContentDoubleClick}
onClickScript={this.onContentClick}
isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive}
@@ -227,6 +233,16 @@ export class CollectionCarouselView extends CollectionSubView() {
>
);
}
+
+ containsDifTypes = (): boolean => {
+ return this.carouselItems.filter(doc => !doc.layout.isFlashcard).length === 0;
+ };
+
+ addFlashcard() {
+ const newDoc = Docs.Create.ComparisonDocument('', { _layout_isFlashcard: true, _width: 300, _height: 300 });
+ this.addDocument?.(newDoc);
+ }
+
@computed get buttons() {
if (!this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)]) return null;
return (
@@ -237,17 +253,32 @@ export class CollectionCarouselView extends CollectionSubView() {
-
-
-
+ {!this.containsDifTypes() ? (
+
+ ) : null}
{this.layoutDoc.practiceMode === practiceMode.PRACTICE ? (
-
this.setPracticeVal(e, practiceVal.MISSED)}>
-
-
-
this.setPracticeVal(e, practiceVal.CORRECT)}>
-
-
+
+ this.setPracticeVal(e, practiceVal.MISSED)}>
+
+
+
+
+ this.setPracticeVal(e, practiceVal.CORRECT)}>
+
+
+
) : null}
>
@@ -320,7 +351,7 @@ export class CollectionCarouselView extends CollectionSubView() {
}}>
Recently missed!
- {this.menu}
+ {!this.containsDifTypes() ? this.menu : null}
{this.Document._chromeHidden || (!this._filterMessage && !this._practiceMessage) ? this.buttons : null}
);
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index dc107b576..0b2c356e5 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -14,9 +14,8 @@
-webkit-text-stroke-color: black;
-webkit-text-stroke-width: 0.2px;
}
-
.input-box {
- position: relative;
+ position: absolute;
padding: 10px;
width: 100%;
height: 100%;
@@ -145,6 +144,15 @@
}
}
+.explain {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ z-index: 200;
+ padding: 5px;
+ background: #dfdfdf;
+}
+
.comparisonBox-interactive {
pointer-events: unset;
cursor: ew-resize;
@@ -154,6 +162,7 @@
display: flex;
}
}
+
// .input-box {
// position: absolute;
// padding: 10px;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 02ab76c2a..9eb5f6ca2 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -43,9 +43,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
@observable private _inputValue = '';
@observable private _outputValue = '';
@observable private _loading = false;
- @observable private _errorMessage = '';
- @observable private _outputMessage = '';
@observable private _isEmpty = false;
+ @observable _yRelativeToTop: boolean = true;
public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
@@ -76,8 +75,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
componentDidMount() {
this._props.setContentViewBox?.(this);
reaction(
- () => this._props.isSelected(),
- selected => !selected && (this.childActive = false)
+ () => this._props.isSelected(), // when this reaction should update
+ selected => !selected && (this.childActive = false) // what it should update to
);
}
protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
@@ -283,8 +282,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
})
}
style={{
- background: usepath === 'alternate' ? 'white' : 'black',
- color: usepath === 'alternate' ? 'black' : 'white',
+ background: this.revealOp === 'hover' ? 'gray' : usepath === 'alternate' ? 'white' : 'black',
+ color: this.revealOp === 'hover' ? 'black' : usepath === 'alternate' ? 'black' : 'white',
display: 'inline-block',
}}>
@@ -303,7 +302,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? (
Flip to front side to use GPT
) : (
- Ask GPT to create an answer on the back side of the flashcard
+ Ask GPT to create an answer on the back side of the flashcard based on your question on the front
)
}>
(!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? this.askGPT(GPTCallType.CHATCARD) : null)}>
@@ -314,16 +313,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
{
+ // this.displayLabelHandler(e.target.value, e.clientY);
const collectionArr: Doc[] = [];
collectionArr.push(this.Document);
const newCol = Docs.Create.CarouselDocument(collectionArr, {
- _width: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 250),
- _height: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 200),
+ _width: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 250) + 50,
+ _height: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 200) + 50,
_layout_fitWidth: false,
_layout_autoHeight: true,
});
- newCol['x'] = e.clientX - 820;
- newCol['y'] = e.clientY - 640;
+ newCol['x'] = this.layoutDoc['x'];
+ newCol['y'] = NumCast(this.layoutDoc['y']) + 50;
this._props.addDocument?.(newCol);
this._props.removeDocument?.(this.Document);
this.Document.embedContainer = newCol;
@@ -332,9 +332,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
Hover to reveal }>
- (this.revealOp === 'hover' ? (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip') : (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover'))}>
+
this.handleHover()}>
@@ -361,6 +359,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
if (this._inputValue) this.askGPT(GPTCallType.QUIZ);
};
+ @action handleHover = () => {
+ if (this.revealOp === 'hover') {
+ this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip';
+ this.Document.forceActive = false;
+ } else {
+ this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover';
+ this.Document.forceActive = true;
+ }
+ //this.revealOp === 'hover' ? (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip') : (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover');
+ };
+
@action handleRenderClick = () => {
// Call the GPT model and get the output
this.layoutDoc[`_${this._props.fieldKey}_usePath`] = undefined;
@@ -448,6 +457,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
const whichDoc = DocCast(this.dataDoc[whichSlot]);
const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc);
const layoutString = targetDoc ? '' : this.testForTextFields(whichSlot);
+ // whichDoc['backgroundColor'] = this.layoutDoc['backgroundColor'];
return targetDoc || layoutString ? (
<>
@@ -500,20 +510,32 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
// add text box to each side when comparison box is first created
// (!this.dataDoc[this.fieldKey + '_0'] && this.dataDoc[this._props.fieldKey + '_0'] !== 'empty')
if (!this.dataDoc[this.fieldKey + '_0'] && !this._isEmpty) {
- const dataSplit = StrCast(this.dataDoc.data).split('Answer');
+ const dataSplit = StrCast(this.dataDoc.data).split('Answer: ');
const newDoc = Docs.Create.TextDocument(dataSplit[1]);
// if there is text from the pdf ai cards, put the question on the front side.
// eslint-disable-next-line prefer-destructuring
- newDoc[DocData].text = dataSplit[1];
+ // newDoc.text = dataSplit[1];
+ newDoc['backgroundColor'] = 'lightgray';
this.addDoc(newDoc, this.fieldKey + '_0');
+ // DocCast(this.dataDoc[this.fieldKey + '_0'])[DocData].text = dataSplit[1];
+ // DocCast(this.dataDoc[this.fieldKey + '_0']).text = dataSplit[1];
+ // console.log('HI' + DocCast(this.dataDoc[this.fieldKey + '_0']).text);
+ console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text));
}
+
if (!this.dataDoc[this.fieldKey + '_1'] && !this._isEmpty) {
- const dataSplit = StrCast(this.dataDoc.data).split('Answer');
+ const dataSplit = StrCast(this.dataDoc.data).split('Answer: ');
const newDoc = Docs.Create.TextDocument(dataSplit[0]);
+ this.addDoc(newDoc, this.fieldKey + '_1');
// if there is text from the pdf ai cards, put the answer on the alternate side.
// eslint-disable-next-line prefer-destructuring
- newDoc[DocData].text = dataSplit[0];
- this.addDoc(newDoc, this.fieldKey + '_1');
+
+ // newDoc[DocData].text = dataSplit[0];
+ // console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text));
+ // console.log('HI' + DocCast(this.dataDoc[this.fieldKey + '_1']).text);
+ // DocCast(this.dataDoc[this.props.fieldKey + '_1'])[DocData].text = dataSplit[0];
+ // console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text));
+ // DocCast(this.dataDoc[this.fieldKey + '_1'])[DocData].text = dataSplit[0];
}
// render the QuizCards
@@ -552,6 +574,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
);
}
+ console.log('HEREEE2' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text));
// render a normal flashcard when not a QuizCard
return (
()
}}
// onPointerUp={() => (this._isAnyChildContentActive = true)}
>
+ {StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text) === '' && !this.childActive ?
Enter text in the flashcard.
: null}
{displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)}
{this._loading ? (
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 2f6824466..cedd3c7c3 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -24,6 +24,8 @@ export class AnchorMenu extends AntimodeMenu
{
private _disposer: IReactionDisposer | undefined;
private _commentRef = React.createRef();
private _cropRef = React.createRef();
+ // @observable protected _top: number = -300;
+ // @observable protected _left: number = -300;
constructor(props: any) {
super(props);
@@ -38,10 +40,17 @@ export class AnchorMenu extends AntimodeMenu {
// GPT additions
@observable private _selectedText: string = '';
+ @observable private _x: number = 0;
+ @observable private _y: number = 0;
@action
public setSelectedText = (txt: string) => {
this._selectedText = txt.trim();
};
+ @action
+ public setLocation = (x: number, y: number) => {
+ this._x = x;
+ this._y = y;
+ };
public onMakeAnchor: () => Opt = () => undefined; // Method to get anchor from text search
@@ -99,7 +108,7 @@ export class AnchorMenu extends AntimodeMenu {
* Invokes the API with the selected text and stores it in the selected text.
* @param e pointer down event
*/
- gptFlashcards = async () => {
+ gptFlashcards = async (e: React.PointerEvent) => {
const queryText = this._selectedText;
try {
const res = await gptAPICall(queryText, GPTCallType.FLASHCARD);
@@ -117,8 +126,8 @@ export class AnchorMenu extends AntimodeMenu {
*/
transferToFlashcard = (text: string) => {
// put each question generated by GPT on the front of the flashcard
- const senArr = text.split('Question');
- const collectionArr: Doc[] = [];
+ var senArr = text.trim().split('Question: ');
+ var collectionArr: Doc[] = [];
for (let i = 1; i < senArr.length; i++) {
console.log('Arr ' + i + ': ' + senArr[i]);
const newDoc = Docs.Create.ComparisonDocument(senArr[i], { _layout_isFlashcard: true, _width: 300, _height: 300 });
@@ -133,6 +142,10 @@ export class AnchorMenu extends AntimodeMenu {
_layout_autoHeight: true,
});
+ newCol.x = this._x;
+ newCol.y = this._y;
+ newCol.zIndex = 100;
+
this.addToCollection?.(newCol);
};
@@ -221,7 +234,7 @@ export class AnchorMenu extends AntimodeMenu {
{/* Adds a create flashcards option to the anchor menu, which calls the gptFlashcard method. */}
this.gptFlashcards(e)}
icon={}
color={SettingsManager.userColor}
/>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6c1617c38..92f890e70 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -425,6 +425,7 @@ export class PDFViewer extends ObservableReactComponent {
const sel = window.getSelection();
if (sel) {
AnchorMenu.Instance.setSelectedText(sel.toString());
+ AnchorMenu.Instance.setLocation(NumCast(this._props.layoutDoc['x']), NumCast(this._props.layoutDoc['y']));
}
if (sel?.type === 'Range') {
--
cgit v1.2.3-70-g09d2
From fc3e2b18b9dad38920e1cec5e58bf9fbd06f4aaf Mon Sep 17 00:00:00 2001
From: bobzel
Date: Tue, 8 Oct 2024 03:30:21 -0400
Subject: changed carousel view to resize ui buttons based on screen scaling
and document dimensions. lint errors.
---
src/client/util/Import & Export/ImageUtils.ts | 1 -
.../views/collections/CollectionCarouselView.scss | 95 ++++++-----
.../views/collections/CollectionCarouselView.tsx | 176 +++++++++++----------
src/client/views/collections/TabDocView.tsx | 1 -
src/client/views/nodes/LabelBox.tsx | 2 +-
src/client/views/pdf/PDFViewer.tsx | 2 -
6 files changed, 142 insertions(+), 135 deletions(-)
(limited to 'src/client/views/collections/CollectionCarouselView.scss')
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 266e05f08..8d4eefa7e 100644
--- a/src/client/util/Import & Export/ImageUtils.ts
+++ b/src/client/util/Import & Export/ImageUtils.ts
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-namespace */
import { ClientUtils } from '../../../ClientUtils';
import { Doc } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index af617254a..97952822e 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -40,7 +40,6 @@
}
.carouselView-back,
.carouselView-fwd,
-.carouselView-star,
.carouselView-remove,
.carouselView-check,
.carouselView-add {
@@ -58,16 +57,14 @@
}
}
.carouselView-fwd {
- top: 42.5%;
- right: 20;
+ top: calc(50% - 15px);
+ right: 0;
+ transform-origin: right top;
}
.carouselView-back {
- top: 42.5%;
- left: 20;
-}
-.carouselView-star {
- top: 0;
+ top: calc(50% - 15px);
left: 0;
+ transform-origin: top left;
}
.carouselView-add {
position: absolute;
@@ -75,62 +72,60 @@
left: 0;
}
.carouselView-remove {
- top: 80%;
left: 52%;
}
.carouselView-check {
- top: 80%;
right: 52%;
}
-.carouselView-quiz {
- position: relative;
- display: flex;
- height: 20px;
- align-items: center;
- margin: auto;
- &:hover {
- color: white;
- }
-}
-
-.carouselView-practice {
- position: relative;
- display: flex;
- flex-direction: column;
- height: 20px;
- align-items: center;
- margin: auto;
- &:hover {
- color: white;
- }
-}
-.carouselView-starFilter {
- position: relative;
- display: flex;
- height: 20px;
- align-items: center;
- &:hover {
- color: white;
- }
-}
-
-.carouselView-practiceModes {
- width: 100%;
- height: 40px;
- display: flex;
- flex-direction: column;
-}
.carouselView-menu {
position: absolute;
flex-direction: column;
align-items: center;
display: flex;
- top: 2px;
- left: 2px;
+ top: 0px;
+ left: 0px;
width: 30;
+ transform-origin: top left;
border-radius: 5px;
color: rgba(255, 255, 255, 0.5);
background: rgba(0, 0, 0, 0.1);
+ .carouselView-practiceModes {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ top: 0;
+ position: relative;
+ .carouselView-quiz {
+ position: relative;
+ display: flex;
+ height: 20px;
+ align-items: center;
+ margin: auto;
+ &:hover {
+ color: white;
+ }
+ & > svg {
+ height: 100%;
+ width: 100%;
+ }
+ }
+
+ .carouselView-practice {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ height: 20px;
+ align-items: center;
+ margin: auto;
+ &:hover {
+ color: white;
+ }
+ & > svg {
+ height: 100%;
+ width: 100%;
+ }
+ }
+ }
}
.carouselView-back:hover,
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 (
);
};
@@ -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 (
- <>
-
-
-
-
-
-
-
- {this.practiceMode == practiceMode.PRACTICE ? (
-
-
- this.setPracticeVal(e, practiceVal.MISSED)}>
-
-
-
-
- this.setPracticeVal(e, practiceVal.CORRECT)}>
-
-
-
-
- ) : 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 (
-
+
{!this.filterDoc ? null : (
-
-
-
+
)}
-
+
- this.togglePracticeMode(practiceMode.QUIZ)}>
+
this.togglePracticeMode(practiceMode.QUIZ)}>
- this.togglePracticeMode(practiceMode.PRACTICE)}>
+
this.togglePracticeMode(practiceMode.PRACTICE)}>
@@ -357,6 +347,32 @@ export class CollectionCarouselView extends CollectionSubView() {
);
}
+ @computed get buttons() {
+ return (
+ <>
+
+
+
+
+
+
+ {this.practiceMode == practiceMode.PRACTICE ? (
+
+
+ this.setPracticeVal(e, practiceVal.MISSED)}>
+
+
+
+
+ this.setPracticeVal(e, practiceVal.CORRECT)}>
+
+
+
+
+ ) : null}
+ >
+ );
+ }
render() {
return (
@@ -388,7 +404,7 @@ export class CollectionCarouselView extends CollectionSubView() {
)}
{!this.Document._chromeHidden ? this.menu : null}
- {!this.Document._chromeHidden ? this.buttons : null}
+ {!this.Document._chromeHidden && this.carouselItems?.[this.carouselIndex] ? this.buttons : null}
);
}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f56ea9d76..5bfdee1f5 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -478,7 +478,6 @@ export class TabDocView extends ObservableReactComponent
{
componentDidMount() {
new ResizeObserver(
action(entries => {
- // eslint-disable-next-line no-restricted-syntax
for (const entry of entries) {
this._panelWidth = entry.contentRect.width;
this._panelHeight = entry.contentRect.height;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 696ba5697..94a9541f2 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -138,7 +138,7 @@ export class LabelBox extends ViewBoxBaseComponent() {
onBlur={() => {
this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? '';
}}
- contentEditable={this._props.onClickScript?.() ? false : true}
+ contentEditable={this._props.onClickScript?.() ? undefined : true}
ref={r => {
this._divRef = r;
this.fitTextToBox(r);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index fc74a480e..5743b17c6 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -498,8 +498,6 @@ export class PDFViewer extends ObservableReactComponent {
// }
this._loading = true;
try {
- if (this._selectionText === '') {
- }
const res = await gptAPICall(queryText, GPTCallType.FLASHCARD);
console.log(res);
--
cgit v1.2.3-70-g09d2
From 972839216c14baa5c9eaf80e1fb2fb2694bbb72c Mon Sep 17 00:00:00 2001
From: bobzel
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
---
src/client/views/PropertiesView.tsx | 7 +-
.../collections/CollectionCarousel3DView.scss | 1 +
.../views/collections/CollectionCarousel3DView.tsx | 70 ++++-
.../views/collections/CollectionCarouselView.scss | 86 +-----
.../views/collections/CollectionCarouselView.tsx | 316 ++++++---------------
.../views/collections/FlashcardPracticeUI.scss | 64 +++++
.../views/collections/FlashcardPracticeUI.tsx | 172 +++++++++++
src/client/views/nodes/ComparisonBox.scss | 15 +
src/client/views/nodes/ComparisonBox.tsx | 92 ++++--
src/client/views/nodes/FieldView.tsx | 1 +
.../nodes/formattedText/FormattedTextBox.scss | 6 -
.../views/nodes/formattedText/FormattedTextBox.tsx | 16 +-
12 files changed, 475 insertions(+), 371 deletions(-)
create mode 100644 src/client/views/collections/FlashcardPracticeUI.scss
create mode 100644 src/client/views/collections/FlashcardPracticeUI.tsx
(limited to 'src/client/views/collections/CollectionCarouselView.scss')
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 371d34173..442dab671 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -139,6 +139,9 @@ export class PropertiesView extends ObservableReactComponent disposer?.());
}
+ @computed get isText() {
+ return this.selectedDoc?.type === DocumentType.RTF;
+ }
@computed get isInk() {
return this.selectedDoc?.type === DocumentType.INK;
}
@@ -1199,8 +1202,8 @@ export class PropertiesView extends ObservableReactComponent
{!this.isStack ? null : this.getNumber('Gap', ' px', 0, 200, NumCast(this.selectedDoc!.gridGap), this.setVal((doc: Doc, val: number) => { doc.gridGap = val; })) }
- {!this.isStack ? null : this.getNumber('xMargin', ' px', 0, 500, NumCast(this.selectedDoc!.xMargin), this.setVal((doc: Doc, val: number) => { doc.xMargin = val; })) }
- {!this.isStack ? null : this.getNumber('yMargin', ' px', 0, 500, NumCast(this.selectedDoc!.yMargin), this.setVal((doc: Doc, val: number) => { doc.yMargin = val; })) }
+ {!this.isStack && !this.isText? null : this.getNumber('xMargin', ' px', 0, 500, NumCast(this.selectedDoc!.xMargin), this.setVal((doc: Doc, val: number) => { doc.xMargin = val; })) }
+ {!this.isStack && !this.isText? null : this.getNumber('yMargin', ' px', 0, 500, NumCast(this.selectedDoc!.yMargin), this.setVal((doc: Doc, val: number) => { doc.yMargin = val; })) }
{!this.isGroup ? null : this.getNumber('Padding', ' px', 0, 500, NumCast(this.selectedDoc!.xPadding), this.setVal((doc: Doc, val: number) => { doc.xPadding = doc.yPadding = val; })) }
{this.isInk ? this.controlPointsButton : null}
{this.getNumber('Width', ' px', 0, Math.max(1000, this.shapeWid), this.shapeWid, this.setVal((doc: Doc, val:number) => {this.shapeWid = val}), 1000, 1)}
diff --git a/src/client/views/collections/CollectionCarousel3DView.scss b/src/client/views/collections/CollectionCarousel3DView.scss
index a556d0fa7..42e112906 100644
--- a/src/client/views/collections/CollectionCarousel3DView.scss
+++ b/src/client/views/collections/CollectionCarousel3DView.scss
@@ -4,6 +4,7 @@
position: relative;
background-color: white;
overflow: hidden;
+ display: flex;
}
.carousel-wrapper {
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 (
{this.buttons}
{this.dots}
+
);
}
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 97952822e..757072453 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -12,23 +12,10 @@
display: inline-block;
width: 100%;
user-select: none;
+ position: absolute;
+ top: 0;
+ left: 0;
}
- .message {
- justify-content: center;
- align-items: center;
- display: flex;
- height: 60%;
- z-index: -1;
- // margin: 15px;
- }
-}
-
-.collectionCarouselView-addFlashcards {
- justify-content: center;
- align-items: center;
- height: 100%;
- z-index: -1;
- pointer-events: none;
}
.collectionCarouselView-recentlyMissed {
color: red;
@@ -39,10 +26,7 @@
pointer-events: none;
}
.carouselView-back,
-.carouselView-fwd,
-.carouselView-remove,
-.carouselView-check,
-.carouselView-add {
+.carouselView-fwd {
position: absolute;
display: flex;
width: 30;
@@ -66,68 +50,6 @@
left: 0;
transform-origin: top left;
}
-.carouselView-add {
- position: absolute;
- bottom: 0;
- left: 0;
-}
-.carouselView-remove {
- left: 52%;
-}
-.carouselView-check {
- right: 52%;
-}
-.carouselView-menu {
- position: absolute;
- flex-direction: column;
- align-items: center;
- display: flex;
- top: 0px;
- left: 0px;
- width: 30;
- transform-origin: top left;
- border-radius: 5px;
- color: rgba(255, 255, 255, 0.5);
- background: rgba(0, 0, 0, 0.1);
- .carouselView-practiceModes {
- width: 100%;
- display: flex;
- flex-direction: column;
- top: 0;
- position: relative;
- .carouselView-quiz {
- position: relative;
- display: flex;
- height: 20px;
- align-items: center;
- margin: auto;
- &:hover {
- color: white;
- }
- & > svg {
- height: 100%;
- width: 100%;
- }
- }
-
- .carouselView-practice {
- position: relative;
- display: flex;
- flex-direction: column;
- height: 20px;
- align-items: center;
- margin: auto;
- &:hover {
- color: white;
- }
- & > svg {
- height: 100%;
- width: 100%;
- }
- }
- }
-}
-
.carouselView-back:hover,
.carouselView-fwd:hover {
background: lightgray;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 559dcfe2a..64ddaac79 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -1,52 +1,35 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@mui/material';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Doc, Opt } from '../../../fields/Doc';
import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { StyleProp } from '../StyleProp';
-import { TagItem } from '../TagsView';
import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { FlashcardPracticeUI } from './FlashcardPracticeUI';
-enum cardMode {
- STAR = 'star',
- ALL = 'all',
-}
-enum practiceMode {
- PRACTICE = 'practice',
- QUIZ = 'quiz',
-}
-enum practiceVal {
- MISSED = 'missed',
- CORRECT = 'correct',
-}
@observer
export class CollectionCarouselView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
- get practiceField() { return this.fieldKey + "_practice"; } // prettier-ignore
- get sideField() { return "_" + this.fieldKey + "_usePath"; } // prettier-ignore
- get starField() { return "#star"; } // prettier-ignore
-
- _sideBtnWidth = 35;
_fadeTimer: NodeJS.Timeout | undefined;
+ _sideBtnWidth = 35;
+ @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
+ @observable _last_index = this.carouselIndex;
+ @observable _last_opacity = 1;
constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
}
- @observable _last_index = this.carouselIndex;
- @observable _last_opacity = 1;
-
componentWillUnmount() {
this._dropDisposer?.();
}
@@ -58,38 +41,31 @@ export class CollectionCarouselView extends CollectionSubView() {
}
};
- @computed get practiceMode() {
- return this.childDocs.some(doc => doc._layout_isFlashcard) ? StrCast(this.layoutDoc.practiceMode) : '';
- }
- @computed get practiceMessage() {
- const cardCount = this.carouselItems.length;
- if (this.practiceMode) {
- if (!Doc.hasDocFilter(this.layoutDoc, 'tags', Doc.FilterAny) && !cardCount) {
- return 'Finished! Click here to view all flashcards.';
- }
- }
- return '';
- }
-
- @computed get filterMessage() {
- const cardCount = this.carouselItems.length;
- if (!this.practiceMessage) {
- if (Doc.hasDocFilter(this.layoutDoc, 'tags', Doc.FilterAny) && !cardCount) {
- return 'No tagged items. Click here to view all flash cards.';
- }
- if (this.practiceMode) {
- if (!cardCount) return 'No flashcards to show! Click here to leave practice mode';
- }
- }
- return '';
- }
- @computed get marginX() { return NumCast(this.layoutDoc.caption_xMargin, 50); } // prettier-ignore
+ @computed get captionMarginX(){ return NumCast(this.layoutDoc.caption_xMargin, 50); } // prettier-ignore
@computed get carouselIndex() { return NumCast(this.layoutDoc._carousel_index) % this.carouselItems.length; } // prettier-ignore
@computed get carouselItems() { return this.childDocs
.filter(doc => doc.type !== DocumentType.LINK)
- .filter(doc => !this.practiceMode || (BoolCast(doc?._layout_isFlashcard) && doc[this.practiceField] !== practiceVal.CORRECT))// show only cards that aren't marked as correct
+ .filter(doc => !this._filterFunc?.(doc))
} // prettier-ignore
+ /**
+ * 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;
+
/**
* Move forward or backward the specified number of Docs
* @param dir signed number indicating Docs to move forward or backward
@@ -102,8 +78,8 @@ export class CollectionCarouselView extends CollectionSubView() {
/**
* Goes to the next Doc in the stack subject to the currently selected filter option.
*/
- advance = (e: React.MouseEvent) => {
- e.stopPropagation();
+ advance = (e?: React.MouseEvent) => {
+ e?.stopPropagation();
this.move(1);
};
@@ -115,55 +91,23 @@ export class CollectionCarouselView extends CollectionSubView() {
this.move(-1);
};
- /*
- * Toggles whether the 'star' metadata field is set on the current Doc
- */
- toggleStar = (e: React.MouseEvent) => {
- e.stopPropagation();
- const curDoc = this.carouselItems[this.carouselIndex];
- if (curDoc) {
- if (TagItem.docHasTag(curDoc, this.starField)) TagItem.removeTagFromDoc(curDoc, this.starField);
- else TagItem.addTagToDoc(curDoc, this.starField);
- }
- };
-
- /*
- * Sets a flashcard to either missed or correct depending on if they got the question right in practice mode.
- */
- setPracticeVal = (e: React.MouseEvent, val: string) => {
- e.stopPropagation();
- const curDoc = this.carouselItems[this.carouselIndex];
- curDoc && (curDoc[this.practiceField] = val);
- this.advance(e);
- };
-
- /**
- * Sets the practice mode answer style for flashcards
- * @param mode practiceMode or undefined for no practice
- */
- setPracticeMode = (mode: practiceMode | undefined) => {
- this.layoutDoc.practiceMode = mode;
- this.carouselItems?.map(doc => (doc[this.practiceField] = undefined));
- if (mode === practiceMode.QUIZ) this.carouselItems?.map(doc => (doc[this.sideField] = undefined));
- };
+ curDoc = () => this.carouselItems[this.carouselIndex];
captionStyleProvider = (doc: Doc | undefined, captionProps: Opt, property: string) => {
// first look for properties on the document in the carousel, then fallback to properties on the container
const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined;
return childValue ?? this._props.styleProvider?.(this.layoutDoc, captionProps, property);
};
+ contentPanelWidth = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc.xMargin);
contentPanelHeight = () => this._props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0) - 2 * NumCast(this.layoutDoc.yMargin);
onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
- captionWidth = () => this._props.PanelWidth() - 2 * this.marginX;
+ captionWidth = () => this._props.PanelWidth() - 2 * this.captionMarginX;
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);
-
isChildContentActive = () =>
this._props.isContentActive?.() === false
? false
@@ -172,10 +116,7 @@ export class CollectionCarouselView extends CollectionSubView() {
: this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
? false
: undefined;
-
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 (
);
};
@@ -214,7 +155,7 @@ export class CollectionCarouselView extends CollectionSubView() {
const fadeTime = 500;
const lastDoc = this.carouselItems?.[this._last_index];
return !lastDoc || this.carouselIndex === this._last_index ? null : (
-
+
{this.renderDoc(
lastDoc,
false, // hide captions if the carousel is configured to show the captions
@@ -235,15 +176,18 @@ export class CollectionCarouselView extends CollectionSubView() {
);
}
+ @computed get renderedDoc() {
+ const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption);
+ return this.renderDoc(this.curDoc(), !!carouselShowsCaptions);
+ }
+
@computed get content() {
- const index = this.carouselIndex;
- const curDoc = this.carouselItems?.[index];
const captionProps = { ...this._props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption);
- return !curDoc ? null : (
+ return !this.curDoc() ? null : (
<>
- {this.renderDoc(curDoc, !!carouselShowsCaptions)}
+ {this.renderedDoc}
{this.overlay}
{!carouselShowsCaptions ? null : (
@@ -253,158 +197,70 @@ export class CollectionCarouselView extends CollectionSubView() {
onWheel={StopEvent}
style={{
borderRadius: this._props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding) as string,
- marginRight: this.marginX,
- marginLeft: this.marginX,
- width: `calc(100% - ${this.marginX * 2}px)`,
+ marginRight: this.captionMarginX,
+ marginLeft: this.captionMarginX,
+ width: `calc(100% - ${this.captionMarginX * 2}px)`,
}}>
-
+
)}
>
);
}
- 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');
-
- @computed get filterDoc() {
- return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter');
- }
- 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 (
-
- {!this.filterDoc ? null : (
-
- )}
-
-
- this.togglePracticeMode(practiceMode.QUIZ)}>
-
-
-
-
- this.togglePracticeMode(practiceMode.PRACTICE)}>
-
-
-
-
-
- );
- }
- @computed get buttons() {
- return (
+ @computed get navButtons() {
+ return this.Document._chromeHidden || !this.curDoc() ? null : (
<>
-
+
-
+
- {this.practiceMode == practiceMode.PRACTICE ? (
-
-
- this.setPracticeVal(e, practiceVal.MISSED)}>
-
-
-
-
- this.setPracticeVal(e, practiceVal.CORRECT)}>
-
-
-
-
- ) : null}
>
);
}
+ docViewProps = () => ({
+ ...this._props, //
+ isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
+ isContentActive: this.isChildContentActive,
+ ScreenToLocalTransform: this.contentScreenToLocalXf,
+ });
+ carouselItemsFunc = () => this.carouselItems;
+ answered = () => this.advance();
+ @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
+
render() {
return (
-
-
- {!this.practiceMessage && !this.filterMessage ? (
- this.content
- ) : (
-
{
- if (this.filterMessage || this.practiceMessage) {
- this.setPracticeMode(undefined);
- Doc.setDocFilter(this.layoutDoc, 'tags', Doc.FilterAny, 'remove');
- }
- }}>
- {this.filterMessage || this.practiceMessage}
-
- )}
-
- {!this.Document._chromeHidden ? this.menu : null}
- {!this.Document._chromeHidden && this.carouselItems?.[this.carouselIndex] ? this.buttons : null}
+
+ {this.content}
+
+ {this.navButtons}
);
}
diff --git a/src/client/views/collections/FlashcardPracticeUI.scss b/src/client/views/collections/FlashcardPracticeUI.scss
new file mode 100644
index 000000000..53c26ad34
--- /dev/null
+++ b/src/client/views/collections/FlashcardPracticeUI.scss
@@ -0,0 +1,64 @@
+.FlashcardPracticeUI-remove,
+.FlashcardPracticeUI-check {
+ position: absolute;
+ display: flex;
+ width: 30;
+ height: 30;
+ align-items: center;
+ border-radius: 5px;
+ justify-content: center;
+ color: rgba(255, 255, 255, 0.5);
+ background: rgba(0, 0, 0, 0.1);
+ &:hover {
+ color: white;
+ }
+}
+.FlashcardPracticeUI-remove {
+ left: 52%;
+}
+.FlashcardPracticeUI-check {
+ right: 52%;
+}
+.FlashcardPracticeUI-menu {
+ position: absolute;
+ flex-direction: column;
+ align-items: center;
+ display: flex;
+ top: 0px;
+ left: 0px;
+ width: 30;
+ transform-origin: top left;
+ border-radius: 5px;
+ color: rgba(255, 255, 255, 0.5);
+ background: rgba(0, 0, 0, 0.1);
+ .FlashcardPracticeUI-practiceModes {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ top: 0;
+ position: relative;
+ .FlashcardPracticeUI-quiz,
+ .FlashcardPracticeUI-practice {
+ position: relative;
+ display: flex;
+ height: 20px;
+ align-items: center;
+ margin: auto;
+ padding: 3px;
+ &:hover {
+ color: white;
+ }
+ & > svg {
+ height: 100%;
+ width: 100%;
+ }
+ }
+ }
+}
+.FlashcardPracticeUI-message {
+ z-index: 100;
+ position: relative;
+ margin: auto;
+ align-content: center;
+ width: max-content;
+}
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
new file mode 100644
index 000000000..032a405bf
--- /dev/null
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -0,0 +1,172 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@mui/material';
+import { computed, makeObservable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { returnZero } from '../../../ClientUtils';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
+import { Transform } from '../../util/Transform';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import './FlashcardPracticeUI.scss';
+
+enum practiceMode {
+ PRACTICE = 'practice',
+ QUIZ = 'quiz',
+}
+enum practiceVal {
+ MISSED = 'missed',
+ CORRECT = 'correct',
+}
+
+interface PracticeUIProps {
+ fieldKey: string;
+ layoutDoc: Doc;
+ carouselItems: () => Doc[];
+ childDocs: Doc[];
+ curDoc: () => Doc;
+ advance: (correct: boolean) => void;
+ renderDepth: number;
+ sideBtnWidth: number;
+ uiBtnScaleTransform: number;
+ ScreenToLocalBoxXf: () => Transform;
+ maxWidgetScale: number;
+ docViewProps: () => DocumentViewProps;
+ setFilterFunc: (func?: (doc: Doc) => boolean) => void;
+ practiceBtnOffset?: number;
+}
+@observer
+export class FlashcardPracticeUI extends ObservableReactComponent
{
+ constructor(props: PracticeUIProps) {
+ super(props);
+ makeObservable(this);
+ this._props.setFilterFunc(this.tryFilterOut);
+ }
+
+ componentWillUnmount(): void {
+ this._props.setFilterFunc(undefined);
+ }
+
+ get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore
+
+ @computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
+ @computed get practiceMode() { return this._props.childDocs.some(doc => doc._layout_isFlashcard) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
+
+ btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale);
+ btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));
+
+ /**
+ * Sets the practice mode answer style for flashcards
+ * @param mode practiceMode or undefined for no practice
+ */
+ setPracticeMode = (mode: practiceMode | undefined) => {
+ this._props.layoutDoc.practiceMode = mode;
+ this._props.carouselItems().map(doc => (doc[this.practiceField] = undefined));
+ };
+
+ @computed get emptyMessage() {
+ const cardCount = this._props.carouselItems().length;
+ const practiceMessage = this.practiceMode && !Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !this._props.carouselItems().length ? 'Finished! Click here to view all flashcards.' : '';
+ const filterMessage = practiceMessage
+ ? ''
+ : Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !cardCount
+ ? 'No tagged items. Click here to view all flash cards.'
+ : this.practiceMode && !cardCount
+ ? 'No flashcards to show! Click here to leave practice mode'
+ : '';
+ return !practiceMessage && !filterMessage ? null : (
+ {
+ if (filterMessage || practiceMessage) {
+ this.setPracticeMode(undefined);
+ Doc.setDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny, 'remove');
+ }
+ }}>
+ {filterMessage || practiceMessage}
+
+ );
+ }
+
+ @computed get practiceButtons() {
+ /*
+ * Sets a flashcard to either missed or correct depending on if they got the question right in practice mode.
+ */
+ const setPracticeVal = (e: React.MouseEvent, val: string) => {
+ e.stopPropagation();
+ const curDoc = this._props.curDoc();
+ curDoc && (curDoc[this.practiceField] = val);
+ this._props.advance?.(val === practiceVal.CORRECT);
+ };
+
+ return this.practiceMode == practiceMode.PRACTICE ? (
+
+
+ setPracticeVal(e, practiceVal.MISSED)}>
+
+
+
+
+ setPracticeVal(e, practiceVal.CORRECT)}>
+
+
+
+
+ ) : null;
+ }
+ @computed get practiceModesMenu() {
+ const setColor = (mode: practiceMode) => (StrCast(this.practiceMode) === mode ? 'white' : 'light gray');
+ const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
+
+ return !this._props.curDoc()?._layout_isFlashcard ? null : (
+
+
+ togglePracticeMode(practiceMode.QUIZ)}>
+
+
+
+
+ togglePracticeMode(practiceMode.PRACTICE)}>
+
+
+
+
+ );
+ }
+ tryFilterOut = (doc: Doc) => (this.practiceMode && BoolCast(doc?._layout_isFlashcard) && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
+ render() {
+ return (
+ <>
+ {this.emptyMessage}
+ {this.practiceButtons}
+
+ {!this.filterDoc || this._props.layoutDoc._chromeHidden ? null : (
+
+ )}
+ {this.practiceModesMenu}
+
+ >
+ );
+ }
+}
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index b7307f3a3..8156c50f6 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -296,3 +296,18 @@
}
}
}
+.comparisonBox-bottomMenu {
+ transform-origin: bottom right;
+ width: max-content;
+ justify-content: space-between;
+ height: max-content;
+ position: absolute;
+ bottom: 0;
+ right: 2;
+ flex-direction: row-reverse;
+ display: flex;
+ cursor: pointer;
+ .comparisonBox-button {
+ padding-right: 8px;
+ }
+}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index ef66c2b11..81e223028 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -68,7 +68,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
return (
flip}>
setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, () => {
if (!this.revealOp || this.revealOp === 'flip') {
@@ -81,42 +81,74 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
color: this.revealOp === 'hover' ? 'black' : this._frontSide ? 'black' : 'white',
display: 'inline-block',
}}>
-
-
-
+
);
}
+ _sideBtnWidth = 30;
+ /**
+ * How much the content of the 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.25 * Math.min(NumCast(this.Document.width), NumCast(this.Document.height)));
+ 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);
+ }
+
@computed get flashcardMenu() {
return (
-
- Flip to front side to use GPT
:
Ask GPT to create an answer on the back side of the flashcard based on your question on the front
}>
-
(!this._frontSide ? this.findImageTags() : null)}>
-
-
-
- {DocCast(this.Document.embedContainer)?.type_collection === CollectionViewType.Carousel ? null : (
-
- Create a flashcard pile
}>
-
this.createFlashcardPile([this.Document], false)}>
-
-
-
-
Create new flashcard stack based on text}>
-
this.gptFlashcardPile()}>
-
+
+ {this.overlayAlternateIcon}
+ {!this._props.isContentActive() ? null : (
+ <>
+ {' '}
+ {!this._frontSide ? null : (
+ {
+ !this._frontSide ? "Flip to front side to use GPT":
+ "Ask GPT to create an answer on the back side of the flashcard based on your question on the front"}
+
// prettier-ignore
+ }>
+
(this._frontSide ? this.findImageTags() : null)}>
+
+
+
+ )}
+ {DocCast(this.Document.embedContainer)?.type_collection === CollectionViewType.Carousel ? null : (
+ <>
+
Create a flashcard pile}>
+
this.createFlashcardPile([this.Document], false)}>
+
+
+
+
Create new flashcard stack based on text }>
+
+
+
+
+ >
+ )}
+ Hover to reveal}>
+
+
-
+ >
)}
-
Hover to reveal }>
- this.handleHover()}>
-
-
-
);
}
@@ -478,7 +510,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
this._loading = false;
return;
}
- this.flipFlashcard();
}
try {
console.log(queryText);
@@ -655,6 +686,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
childActiveFunc = () => this._childActive;
+ contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
render() {
const clearButton = (which: string) => (
remove }>
@@ -687,6 +719,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
removeDocument={whichSlot.endsWith('1') ? this.remDoc1 : this.remDoc2}
NativeWidth={returnZero}
NativeHeight={returnZero}
+ ScreenToLocalTransform={this.contentScreenToLocalXf}
isContentActive={this.childActiveFunc}
isDocumentActive={returnFalse}
dontSelect={returnTrue}
@@ -808,8 +841,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
) : null}
- {this._props.isContentActive() ? this.flashcardMenu : null}
- {this.overlayAlternateIcon}
+ {this.flashcardMenu}
);
}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 170966471..c81631baa 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -51,6 +51,7 @@ export interface FieldViewSharedProps {
LayoutTemplate?: () => Opt;
renderDepth: number;
scriptContext?: unknown; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
+ screenXPadding?: () => number; // padding in screen space coordinates (used by text box to reflow around UI buttons in carouselView)
xPadding?: number;
yPadding?: number;
dontRegisterView?: boolean;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index d6f13d9ee..f1ae1151f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -101,12 +101,6 @@ audiotag:hover {
height: 22;
cursor: default;
}
-.formattedTextBox-flip {
- align-items: center;
- position: absolute;
- right: 2px;
- bottom: 4px;
-}
.formattedTextBox-outer {
position: relative;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c89737e1e..93153b453 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -763,10 +763,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
- const localDelta = this._props
- .ScreenToLocalTransform()
- .scale(this._props.NativeDimScaling?.() || 1)
- .transformDirection(delta[0], delta[1]);
+ const localDelta = this.DocumentView?.().screenToViewTransform().transformDirection(delta[0], delta[1]) ?? delta;
const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100;
const width = NumCast(this.layoutDoc._width) + localDelta[0];
this.layoutDoc._layout_sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
@@ -1264,7 +1261,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent Doc.RecordingEvent, this.breakupDictation);
this._disposers.layout_autoHeight = reaction(
- () => ({ autoHeight: this.layout_autoHeight, fontSize: this.fontSize, css: this.Document[DocCss] }),
+ () => ({ autoHeight: this.layout_autoHeight, fontSize: this.fontSize, css: this.Document[DocCss], xMargin: this.Document.xMargin, yMargin: this.Document.yMargin }),
autoHeight => setTimeout(() => autoHeight && this.tryUpdateScrollHeight())
);
this._disposers.highlights = reaction(
@@ -2088,8 +2085,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
- const paddingX = Math.max(this._props.xPadding ?? 0, NumCast(this.layoutDoc._xMargin));
- const paddingY = Math.max(this._props.yPadding ?? 0, NumCast(this.layoutDoc._yMargin));
+
+ const scrSize = (which: number, view = this._props.docViewPath().slice(-which)[0]) =>
+ [view._props.PanelWidth() / view.screenToLocalScale(), view._props.PanelHeight() / view.screenToLocalScale()]; // prettier-ignore
+ const scrMargin = [Math.max(0, (scrSize(2)[0] - scrSize(1)[0]) / 2), Math.max(0, (scrSize(2)[1] - scrSize(1)[1]) / 2)];
+ const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), this._props.xPadding ?? 0, 0, ((this._props.screenXPadding?.() ?? 0) - scrMargin[0]) * this.ScreenToLocalBoxXf().Scale);
+ const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, ((this._props.yPadding ?? 0) - scrMargin[1]) * this.ScreenToLocalBoxXf().Scale);
const styleFromLayout = styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props. e.g.,
return styleFromLayout?.height === '0px' ? null : (
Date: Wed, 9 Oct 2024 13:27:56 -0400
Subject: more refactoring to of collection flashcards into CollectioSubView to
simplify using it in diferent collection views.
---
.../views/collections/CollectionCardDeckView.scss | 13 +---
.../views/collections/CollectionCardDeckView.tsx | 81 ++++++----------------
.../views/collections/CollectionCarousel3DView.tsx | 72 +++++--------------
.../views/collections/CollectionCarouselView.scss | 1 +
.../views/collections/CollectionCarouselView.tsx | 53 ++------------
src/client/views/collections/CollectionSubView.tsx | 51 +++++++++++++-
.../views/collections/FlashcardPracticeUI.scss | 2 +-
.../views/collections/FlashcardPracticeUI.tsx | 27 ++++----
.../collectionFreeForm/CollectionFreeFormView.tsx | 2 +-
src/client/views/nodes/ComparisonBox.tsx | 18 ++---
10 files changed, 113 insertions(+), 207 deletions(-)
(limited to 'src/client/views/collections/CollectionCarouselView.scss')
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index 0520e38d4..0637cd4e9 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -6,6 +6,7 @@
position: relative;
background-color: white;
overflow: hidden;
+ display: flex;
button {
border-radius: 50%;
@@ -48,15 +49,3 @@
.card-item-active {
z-index: 100;
}
-
-.collectionCardDeckView-flashcards {
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- left: 0;
- display: flex;
- transform-origin: top left;
- pointer-events: none;
- z-index: 100;
-}
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 7272b22e2..8f351d8a7 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -22,7 +22,6 @@ import { DocumentView } from '../nodes/DocumentView';
import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
import './CollectionCardDeckView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { FlashcardPracticeUI } from './FlashcardPracticeUI';
enum cardSortings {
Time = 'time',
@@ -47,8 +46,6 @@ export class CollectionCardView extends CollectionSubView() {
private _textToDoc = new Map
();
private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
- _sideBtnWidth = 35;
- @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
@observable _forceChildXf = 0;
@observable _hoveredNodeIndex = -1;
@observable _docRefs = new ObservableMap();
@@ -119,15 +116,15 @@ export class CollectionCardView extends CollectionSubView() {
/**
* The child documents to be rendered-- everything other than ink/link docs (which are marks as being svg's)
*/
- @computed get childDocsWithoutLinks() {
- return this.childDocs.filter(l => !l.layout_isSvg).filter(doc => !this._filterFunc?.(doc));
+ @computed get childCards() {
+ return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg);
}
/**
* how much to scale down the contents of the view so that everything will fit
*/
@computed get fitContentScale() {
- const length = Math.min(this.childDocsWithoutLinks.length, this._maxRowCount);
+ const length = Math.min(this.childCards.length, this._maxRowCount);
return (this.childPanelWidth() * length) / this._props.PanelWidth();
}
@@ -280,7 +277,12 @@ export class CollectionCardView extends CollectionSubView() {
);
@computed get sortedDocs() {
- return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.Document.cardSort_isDesc), this._docDraggedIndex);
+ return this.sort(
+ this.childCards.map(card => card.layout),
+ this.cardSort,
+ BoolCast(this.Document.cardSort_isDesc),
+ this._docDraggedIndex
+ );
}
/**
@@ -428,12 +430,14 @@ export class CollectionCardView extends CollectionSubView() {
default: return StrCast(doc.title);
} // prettier-ignore
};
- const docTextPromises = this.childDocsWithoutLinks.map(async doc => {
- const docText = (await docToText(doc)) ?? '';
- doc.gptInputText = docText;
- this._textToDoc.set(docText.replace(/\n/g, ' ').trim(), doc);
- return `======${docText.replace(/\n/g, ' ').trim()}======`;
- });
+ const docTextPromises = this.childCards
+ .map(pair => pair.layout)
+ .map(async doc => {
+ const docText = (await docToText(doc)) ?? '';
+ doc.gptInputText = docText;
+ this._textToDoc.set(docText.replace(/\n/g, ' ').trim(), doc);
+ return `======${docText.replace(/\n/g, ' ').trim()}======`;
+ });
return Promise.all(docTextPromises);
};
@@ -567,7 +571,7 @@ export class CollectionCardView extends CollectionSubView() {
* Actually renders all the cards
*/
@computed get renderCards() {
- if (!this.childDocsWithoutLinks.length) {
+ if (!this.childCards.length) {
return null;
}
@@ -611,36 +615,16 @@ export class CollectionCardView extends CollectionSubView() {
}
contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
+ curDoc = () => this.childCards.find(card => DocumentView.getDocumentView(card.layout, this.DocumentView?.())?.IsSelected)?.layout;
docViewProps = () => ({
...this._props, //
isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
isContentActive: this.isChildContentActive,
ScreenToLocalTransform: this.contentScreenToLocalXf,
});
- carouselItemsFunc = () => this.childDocsWithoutLinks;
- @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
- answered = (correct: boolean) => !correct || !this.curDoc();
- curDoc = () => this.sortedDocs.find(doc => DocumentView.getDocumentView(doc, this.DocumentView?.())?.IsSelected);
- /**
- * 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
render() {
- const isEmpty = this.childDocsWithoutLinks.length === 0;
-
+ const isEmpty = this.childCards.length === 0;
return (
+ {this.flashCardUI(this.curDoc, this.docViewProps)}
);
}
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 3bcf3450f..9ccac0e0f 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 { action, computed, makeObservable, observable } from 'mobx';
+import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { returnZero } from '../../../ClientUtils';
@@ -15,26 +15,19 @@ import { DocumentView } from '../nodes/DocumentView';
import { FocusViewOptions } from '../nodes/FocusViewOptions';
import './CollectionCarousel3DView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-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');
@observer
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;
+ private _dropDisposer?: DragManager.DragDropDisposer;
constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
}
- private _dropDisposer?: DragManager.DragDropDisposer;
-
componentWillUnmount() {
this._dropDisposer?.();
}
@@ -46,8 +39,11 @@ export class CollectionCarousel3DView extends CollectionSubView() {
}
};
+ @computed get scrollSpeed() {
+ return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; // default scroll speed
+ }
@computed get carouselItems() {
- return this.childLayoutPairs.filter(pair => pair.layout.type !== DocumentType.LINK).filter(pair => !this._filterFunc?.(pair.layout));
+ return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg);
}
centerScale = Number(CAROUSEL3D_CENTER_SCALE);
@@ -86,11 +82,11 @@ export class CollectionCarousel3DView extends CollectionSubView() {
@computed get content() {
const currentIndex = NumCast(this.layoutDoc._carousel_index);
- const displayDoc = (childPair: { layout: Doc; data: Doc }, dxf: () => Transform) => (
+ const displayDoc = (child: Doc, dxf: () => Transform) => (
);
- return this.carouselItems.map((childPair, index) => (
-
- {displayDoc(childPair, index < currentIndex ? this.childScreenLeftToLocal : index === currentIndex ? this.childCenterScreenToLocal : this.childScreenRightToLocal)}
+ return this.carouselItems.map((child, index) => (
+
+ {displayDoc(child.layout, index < currentIndex ? this.childScreenLeftToLocal : index === currentIndex ? this.childCenterScreenToLocal : this.childScreenRightToLocal)}
));
}
@@ -191,35 +187,14 @@ 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;
-
+ curDoc = () => this.carouselItems[NumCast(this.layoutDoc._carousel_index)]?.layout;
+ answered = (correct: boolean) => (!correct || !this.curDoc()) && this.changeSlide(1);
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 (
{this.buttons}
-
+
{this.dots}
-
+ {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
);
}
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 757072453..544b3e262 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -2,6 +2,7 @@
height: 100%;
position: relative;
overflow: hidden;
+ display: flex;
.collectionCarouselView-caption {
height: 50;
display: inline-block;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 64ddaac79..538eba356 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -5,7 +5,6 @@ import * as React from 'react';
import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
import { Doc, Opt } from '../../../fields/Doc';
import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { StyleProp } from '../StyleProp';
import { DocumentView } from '../nodes/DocumentView';
@@ -13,15 +12,12 @@ import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { FlashcardPracticeUI } from './FlashcardPracticeUI';
@observer
export class CollectionCarouselView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
_fadeTimer: NodeJS.Timeout | undefined;
- _sideBtnWidth = 35;
- @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
@observable _last_index = this.carouselIndex;
@observable _last_opacity = 1;
@@ -43,28 +39,7 @@ export class CollectionCarouselView extends CollectionSubView() {
@computed get captionMarginX(){ return NumCast(this.layoutDoc.caption_xMargin, 50); } // prettier-ignore
@computed get carouselIndex() { return NumCast(this.layoutDoc._carousel_index) % this.carouselItems.length; } // prettier-ignore
- @computed get carouselItems() { return this.childDocs
- .filter(doc => doc.type !== DocumentType.LINK)
- .filter(doc => !this._filterFunc?.(doc))
- } // prettier-ignore
-
- /**
- * 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;
+ @computed get carouselItems() { return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg); } // prettier-ignore
/**
* Move forward or backward the specified number of Docs
@@ -91,7 +66,7 @@ export class CollectionCarouselView extends CollectionSubView() {
this.move(-1);
};
- curDoc = () => this.carouselItems[this.carouselIndex];
+ curDoc = () => this.carouselItems[this.carouselIndex]?.layout;
captionStyleProvider = (doc: Doc | undefined, captionProps: Opt
, property: string) => {
// first look for properties on the document in the carousel, then fallback to properties on the container
@@ -153,7 +128,7 @@ export class CollectionCarouselView extends CollectionSubView() {
*/
@computed get overlay() {
const fadeTime = 500;
- const lastDoc = this.carouselItems?.[this._last_index];
+ const lastDoc = this.carouselItems?.[this._last_index]?.layout;
return !lastDoc || this.carouselIndex === this._last_index ? null : (
{this.renderDoc(
@@ -211,10 +186,10 @@ export class CollectionCarouselView extends CollectionSubView() {
@computed get navButtons() {
return this.Document._chromeHidden || !this.curDoc() ? null : (
<>
-
+
-
+
>
@@ -227,9 +202,7 @@ export class CollectionCarouselView extends CollectionSubView() {
isContentActive: this.isChildContentActive,
ScreenToLocalTransform: this.contentScreenToLocalXf,
});
- carouselItemsFunc = () => this.carouselItems;
answered = () => this.advance();
- @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
render() {
return (
@@ -245,21 +218,7 @@ export class CollectionCarouselView extends CollectionSubView() {
top: NumCast(this.layoutDoc._yMargin),
}}>
{this.content}
-
+ {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
{this.navButtons}
);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 581201a20..c057d2402 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
@@ -25,7 +25,8 @@ import { SnappingManager } from '../../util/SnappingManager';
import { UndoManager } from '../../util/UndoManager';
import { ViewBoxBaseComponent } from '../DocComponent';
import { FieldViewProps } from '../nodes/FieldView';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { FlashcardPracticeUI } from './FlashcardPracticeUI';
export interface CollectionViewProps extends React.PropsWithChildren
{
isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
@@ -119,7 +120,8 @@ export function CollectionSubView() {
pair =>
// filter out any documents that have a proto that we don't have permissions to
!pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate))
- );
+ )
+ .filter(pair => !this._filterFunc?.(pair.layout!));
return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
}
/**
@@ -515,6 +517,49 @@ export function CollectionSubView() {
alert('Document upload failed - possibly an unsupported file type.');
}
};
+
+ protected _sideBtnWidth = 35;
+ @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
+ /**
+ * How much the content of the collection 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 in collection coordinates based on not wanting the widget to visually obscure too much of the collection
+ * This takes the desired screen space size and converts into collection coordinates. It then returns the smaller of the converted
+ * size or a fraction of the collection view.
+ */
+ @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.contentScaling, 0.25 * NumCast(this.layoutDoc.width, 1)); } // prettier-ignore
+ /**
+ * This computes a scale factor for UI elements so that they shrink and grow as the collection does in screen space.
+ * Note, the scale factor does not allow for elements to grow larger than their native screen space size.
+ */
+ @computed get uiBtnScaling() { return this.maxWidgetSize / this._sideBtnWidth; } // prettier-ignore
+
+ screenXPadding = () => (this.uiBtnScaling * this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) / this._props.ScreenToLocalTransform().Scale;
+ filteredChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
+ childDocsFunc = () => this.childDocs;
+ @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
+
+ public flashCardUI = (curDoc: () => Doc | undefined, docViewProps: () => DocumentViewProps, answered?: (correct: boolean) => void) => {
+ return (
+
+ );
+ };
}
return CollectionSubViewInternal;
diff --git a/src/client/views/collections/FlashcardPracticeUI.scss b/src/client/views/collections/FlashcardPracticeUI.scss
index 2f99500f8..c5252bbfa 100644
--- a/src/client/views/collections/FlashcardPracticeUI.scss
+++ b/src/client/views/collections/FlashcardPracticeUI.scss
@@ -50,7 +50,7 @@
height: 20px;
align-items: center;
margin: auto;
- padding: 3px;
+ // padding: 3px;
&:hover {
color: white;
}
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index a643c95b0..7bf4d86d1 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -23,15 +23,14 @@ enum practiceVal {
interface PracticeUIProps {
fieldKey: string;
layoutDoc: Doc;
- carouselItems: () => Doc[];
- childDocs: Doc[];
+ filteredChildDocs: () => Doc[];
+ allChildDocs: () => Doc[];
curDoc: () => Doc | undefined;
- advance: (correct: boolean) => void;
+ advance?: (correct: boolean) => void;
renderDepth: number;
sideBtnWidth: number;
- uiBtnScaleTransform: number;
+ uiBtnScaling: number;
ScreenToLocalBoxXf: () => Transform;
- maxWidgetScale: number;
docViewProps: () => DocumentViewProps;
setFilterFunc: (func?: (doc: Doc) => boolean) => void;
practiceBtnOffset?: number;
@@ -51,7 +50,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent doc.title === 'Filter'); } // prettier-ignore
- @computed get practiceMode() { return this._props.childDocs.some(doc => doc._layout_isFlashcard) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
+ @computed get practiceMode() { return this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale);
btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));
@@ -62,12 +61,12 @@ export class FlashcardPracticeUI extends ObservableReactComponent {
this._props.layoutDoc.practiceMode = mode;
- this._props.carouselItems().map(doc => (doc[this.practiceField] = undefined));
+ this._props.allChildDocs().map(doc => (doc[this.practiceField] = undefined));
};
@computed get emptyMessage() {
- const cardCount = this._props.carouselItems().length;
- const practiceMessage = this.practiceMode && !Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !this._props.carouselItems().length ? 'Finished! Click here to view all flashcards.' : '';
+ const cardCount = this._props.filteredChildDocs().length;
+ const practiceMessage = this.practiceMode && !Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !cardCount ? 'Finished! Click here to view all flashcards.' : '';
const filterMessage = practiceMessage
? ''
: Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !cardCount
@@ -78,7 +77,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent {
if (filterMessage || practiceMessage) {
this.setPracticeMode(undefined);
@@ -102,7 +101,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent
+
setPracticeVal(e, practiceVal.MISSED)}>
@@ -120,12 +119,12 @@ export class FlashcardPracticeUI extends ObservableReactComponent
(StrCast(this.practiceMode) === mode ? 'white' : 'light gray');
const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
- return !this._props.childDocs.some(doc => doc._layout_isFlashcard) ? null : (
+ return !this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? null : (
togglePracticeMode(practiceMode.QUIZ)}>
@@ -146,7 +145,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent
{this.emptyMessage}
{this.practiceButtons}
-
+
{!this.filterDoc || this._props.layoutDoc._chromeHidden ? null : (
()
);
}
- _sideBtnWidth = 30;
+ _sideBtnWidth = 35;
/**
* How much the content of the 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);
- }
+ @computed get viewScaling() { return this.ScreenToLocalBoxXf().Scale; } // 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.25 * Math.min(NumCast(this.Document.width), NumCast(this.Document.height)));
- return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
- }
+ @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.viewScaling, 0.25 * Math.min(NumCast(this.Document.width), NumCast(this.Document.height))); } // prettier-ignore
/**
* 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);
- }
+ @computed get uiBtnScaling() { return Math.max(this.maxWidgetSize / this._sideBtnWidth, 1) * Math.min(1, this.viewScaling)* (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
@computed get flashcardMenu() {
return (
-
+
{this.overlayAlternateIcon}
{!this._props.isContentActive() ? null : (
<>
- {' '}
{!this._frontSide ? null : (