/* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/click-events-have-key-events */ /* eslint-disable react/jsx-props-no-spreading */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { emptyFunction } from '../../../Utils'; import { StopEvent, returnFalse, returnOne, returnTrue, returnZero } from '../../../ClientUtils'; import { Doc, Opt } from '../../../fields/Doc'; import { 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'; import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import './CollectionCarouselView.scss'; import { CollectionSubView } from './CollectionSubView'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; @observer export class CollectionCarouselView extends CollectionSubView() { private _dropDisposer?: DragManager.DragDropDisposer; constructor(props: any) { super(props); makeObservable(this); } componentWillUnmount() { this._dropDisposer?.(); } protected createDashEventsTarget = (ele: HTMLDivElement | null) => { this._dropDisposer?.(); if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); } }; @computed get carouselItems() { return this.childLayoutPairs.filter(pair => pair.layout.type !== DocumentType.LINK); } move = (dir: number) => { const moveToCardWithField = (match: (doc: Doc) => boolean): boolean => { let startInd = (NumCast(this.layoutDoc._carousel_index) + dir) % this.carouselItems.length; while (!match(this.carouselItems?.[startInd].layout) && (startInd + dir + this.carouselItems.length) % this.carouselItems.length !== this.layoutDoc._carousel_index) { startInd = (startInd + dir + this.carouselItems.length) % this.carouselItems.length; } this.layoutDoc._carousel_index = startInd; return match(this.carouselItems?.[startInd].layout); }; switch (StrCast(this.layoutDoc.filterOp)) { case 'star': // go to a flashcard that is starred, skip the ones that aren't if (!moveToCardWithField((doc: Doc) => !!doc[`${this.fieldKey}_star`])) { this.layoutDoc.filterOp = undefined; // if there aren't any starred, show all cards } break; case 'practice': // go to a new index that is missed, skip the ones that are correct if (!moveToCardWithField((doc: Doc) => doc[`${this.fieldKey}_missed`] !== 'correct')) { this.layoutDoc.filterOp = undefined; // if all of the cards are correct, show all cards and exit practice mode // set all the cards to missed this.carouselItems.forEach(item => { item.layout[`${this.fieldKey}_missed`] = undefined; }); } break; default: moveToCardWithField( returnTrue); } // prettier-ignore }; /** * Goes to the next Doc in the stack subject to the currently selected filter option. */ advance = (e: React.MouseEvent) => { e.stopPropagation(); this.move(1); }; /** * Goes to the previous Doc in the stack subject to the currently selected filter option. */ goback = (e: React.MouseEvent) => { e.stopPropagation(); this.move(-1); }; /* * Stars the document when the star button is pressed. */ star = (e: React.MouseEvent) => { e.stopPropagation(); const curDoc = this.carouselItems[NumCast(this.layoutDoc._carousel_index)]; curDoc.layout[`${this.fieldKey}_star`] = curDoc.layout[`${this.fieldKey}_star`] ? undefined : true; }; /* * Sets a flashcard to either missed or correct depending on if they got the question right in practice mode. */ missed = (e: React.MouseEvent, val: string) => { e.stopPropagation(); const curDoc = this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)]; curDoc.layout[`${this.fieldKey}_missed`] = val; this.advance(e); }; captionStyleProvider = (doc: Doc | undefined, captionProps: Opt, property: string): any => { // 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); }; panelHeight = () => this._props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0); onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick); onContentClick = () => ScriptCast(this.layoutDoc.onChildClick); @computed get marginX() { return NumCast(this.layoutDoc.caption_xMargin, 50); } captionWidth = () => this._props.PanelWidth() - 2 * this.marginX; @computed get content() { const index = NumCast(this.layoutDoc._carousel_index); 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?.layout instanceof Doc) ? null : ( <>
{!carouselShowsCaptions ? null : (
)} ); } @computed get buttons() { if (!this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)]) return null; return ( <>
this.missed(e, 'missed')} style={{ visibility: this.layoutDoc.filterOp === 'practice' ? 'visible' : 'hidden' }}>
this.missed(e, 'correct')} style={{ visibility: this.layoutDoc.filterOp === 'practice' ? 'visible' : 'hidden' }}>
); } 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.layoutDoc.filterOp = undefined;}, icon: 'layer-group',}); // prettier-ignore revealItems.push({description: 'Star', event: () => {this.layoutDoc.filterOp = 'star';}, icon: 'star',}); // prettier-ignore revealItems.push({description: 'Practice Mode', event: () => {this.layoutDoc.filterOp = 'practice';}, icon: 'check',}); // prettier-ignore revealItems.push({description: 'Quiz Cards', event: () => {this.layoutDoc.filterOp = 'quiz';}, icon: 'pencil',}); // prettier-ignore !revealOptions && cm.addItem({ description: 'Filter Flashcards', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' }); }; render() { return (
{this.content} {/* Displays a message to the user to add more flashcards if they are in practice mode and no flashcards are there. */}

Add flashcards!

{/* Displays a message to the user that a flashcard was recently missed if they had previously gotten it wrong. */}

Recently missed!

{this.Document._chromeHidden ? null : this.buttons}
); } }