aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/collections/CollectionCardDeckView.scss12
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx66
-rw-r--r--src/client/views/collections/FlashcardPracticeUI.scss6
-rw-r--r--src/client/views/collections/FlashcardPracticeUI.tsx8
4 files changed, 81 insertions, 11 deletions
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index d1731c244..0520e38d4 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -48,3 +48,15 @@
.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 5d39dc1ca..7272b22e2 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -22,6 +22,7 @@ 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',
@@ -46,6 +47,8 @@ export class CollectionCardView extends CollectionSubView() {
private _textToDoc = new Map<string, Doc>();
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<Doc, DocumentView>();
@@ -117,7 +120,7 @@ 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);
+ return this.childDocs.filter(l => !l.layout_isSvg).filter(doc => !this._filterFunc?.(doc));
}
/**
@@ -565,11 +568,7 @@ export class CollectionCardView extends CollectionSubView() {
*/
@computed get renderCards() {
if (!this.childDocsWithoutLinks.length) {
- return (
- <span className="no-card-span" style={{ width: ` ${this._props.PanelWidth()}px`, height: ` ${this._props.PanelHeight()}px` }}>
- Sorry ! There are no cards in this group
- </span>
- );
+ return null;
}
// Map sorted documents to their rendered components
@@ -611,6 +610,34 @@ export class CollectionCardView extends CollectionSubView() {
});
}
+ contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
+ 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;
@@ -629,10 +656,35 @@ export class CollectionCardView extends CollectionSubView() {
className="card-wrapper"
style={{
...(!isEmpty && { transform: `scale(${1 / this.fitContentScale})` }),
- ...(!isEmpty && { height: `${100 * this.fitContentScale}%` }),
+ ...{ height: `${100 * (isEmpty ? 1 : this.fitContentScale)}%` },
+ ...{ width: `${100 * (isEmpty ? 1 : this.fitContentScale)}%` },
gridAutoRows: `${100 / this.numRows}%`,
}}>
{this.renderCards}
+ <div
+ className="collectionCardDeckView-flashcards"
+ style={{
+ transform: `scale(${this.fitContentScale || 1})`,
+ width: `${100 / (this.fitContentScale || 1)}%`,
+ height: `${100 / (this.fitContentScale || 1)}%`,
+ pointerEvents: !this.childDocsWithoutLinks.length ? 'unset' : undefined,
+ }}>
+ <FlashcardPracticeUI
+ setFilterFunc={this.setFilterFunc}
+ fieldKey={this.fieldKey}
+ sideBtnWidth={this._sideBtnWidth}
+ carouselItems={this.carouselItemsFunc}
+ childDocs={this.childDocs}
+ advance={this.answered}
+ curDoc={this.curDoc}
+ layoutDoc={this.layoutDoc}
+ maxWidgetScale={this.maxWidgetScale}
+ uiBtnScaleTransform={this.uiBtnScaleTransform}
+ ScreenToLocalBoxXf={this.ScreenToLocalBoxXf}
+ renderDepth={this._props.renderDepth}
+ docViewProps={this.docViewProps}
+ />
+ </div>
</div>
</div>
);
diff --git a/src/client/views/collections/FlashcardPracticeUI.scss b/src/client/views/collections/FlashcardPracticeUI.scss
index 53c26ad34..2f99500f8 100644
--- a/src/client/views/collections/FlashcardPracticeUI.scss
+++ b/src/client/views/collections/FlashcardPracticeUI.scss
@@ -13,6 +13,11 @@
color: white;
}
}
+.FlashcardPracticeUI-practice {
+ position: absolute;
+ width: 100%;
+ pointer-events: all;
+}
.FlashcardPracticeUI-remove {
left: 52%;
}
@@ -30,6 +35,7 @@
transform-origin: top left;
border-radius: 5px;
color: rgba(255, 255, 255, 0.5);
+ pointer-events: all;
background: rgba(0, 0, 0, 0.1);
.FlashcardPracticeUI-practiceModes {
width: 100%;
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index 032a405bf..072a2edef 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -25,7 +25,7 @@ interface PracticeUIProps {
layoutDoc: Doc;
carouselItems: () => Doc[];
childDocs: Doc[];
- curDoc: () => Doc;
+ curDoc: () => Doc | undefined;
advance: (correct: boolean) => void;
renderDepth: number;
sideBtnWidth: number;
@@ -100,8 +100,8 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
this._props.advance?.(val === practiceVal.CORRECT);
};
- return this.practiceMode == practiceMode.PRACTICE ? (
- <div style={{ transform: `scale(${this._props.uiBtnScaleTransform})`, bottom: `${this._props.practiceBtnOffset ?? this._props.sideBtnWidth}px`, height: `${this._props.sideBtnWidth}px`, position: 'absolute', width: `100%` }}>
+ return this.practiceMode == practiceMode.PRACTICE && this._props.curDoc() ? (
+ <div className="FlashcardPracticeUI-practice" style={{ transform: `scale(${this._props.uiBtnScaleTransform})`, bottom: `${this._props.practiceBtnOffset ?? this._props.sideBtnWidth}px`, height: `${this._props.sideBtnWidth}px` }}>
<Tooltip title="Incorrect. View again later.">
<div key="remove" className="FlashcardPracticeUI-remove" onClick={e => setPracticeVal(e, practiceVal.MISSED)}>
<FontAwesomeIcon icon="xmark" color="red" size="1x" />
@@ -119,7 +119,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
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 : (
+ return !this._props.childDocs.some(doc => doc._layout_isFlashcard) ? null : (
<div
className="FlashcardPracticeUI-practiceModes"
style={{