aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoralyssaf16 <alyssa_feinberg@brown.edu>2024-06-06 22:20:24 -0400
committeralyssaf16 <alyssa_feinberg@brown.edu>2024-06-06 22:20:24 -0400
commit7594f649890d27d558be35c9d40a0aa8211aec67 (patch)
treea37a4719f591037380e4ac9838aebf3e669bb968 /src
parentc76505f56a83625e3993427838aaee0c54c5fb81 (diff)
Flashcard changes - menu added
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/ComparisonBox.scss20
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx99
-rw-r--r--src/client/views/nodes/DocumentView.tsx3
-rw-r--r--src/client/views/nodes/FieldView.tsx1
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx1
6 files changed, 87 insertions, 43 deletions
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index f3dc0f68c..dc107b576 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -47,6 +47,7 @@
width: '91%';
height: '80%';
z-index: '-1';
+ overscroll-behavior: contain;
}
.clip-div {
@@ -241,24 +242,5 @@
}
}
}
-
- // .loading-circle {
- // position: relative;
- // width: 50px;
- // height: 50px;
- // border-radius: 50%;
- // border: 3px solid #ccc;
- // border-top-color: #333;
- // animation: spin 1s infinite linear;
- // }
-
- // @keyframes spin {
- // 0% {
- // transform: rotate(0deg);
- // }
- // 100% {
- // transform: rotate(360deg);
- // }
- // }
}
}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 6d4af2f3e..f844892c5 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -1,9 +1,9 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { action, computed, makeObservable, observable } from 'mobx';
+import { action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { returnFalse, returnNone, setupMoveUpEvents } from '../../../ClientUtils';
+import { returnFalse, returnNone, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
@@ -27,6 +27,7 @@ import { FormattedTextBox } from './formattedText/FormattedTextBox';
import ReactLoading from 'react-loading';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
+import { tickStep } from 'd3';
@observer
export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@@ -37,7 +38,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
- // this._isAnyChildContentActive = true;
}
@observable private _inputValue = '';
@@ -63,12 +63,19 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
@computed get clipHeight() {
return NumCast(this.layoutDoc[this.clipHeightKey], 200);
}
+ get revealOp() {
+ return this.layoutDoc[`_${this._props.fieldKey}_revealOp`];
+ }
get clipHeightKey() {
return '_' + this._props.fieldKey + '_clipHeight';
}
componentDidMount() {
this._props.setContentViewBox?.(this);
+ reaction(
+ () => this._props.isSelected(),
+ selected => !selected && (this.childActive = false)
+ );
}
protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
this._disposers[disposerId]?.();
@@ -98,7 +105,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
emptyFunction,
action((moveEv, doubleTap) => {
if (doubleTap) {
- this._isAnyChildContentActive = true;
+ this.childActive = true;
if (!this.dataDoc[this.fieldKey + '_1'] && !this.dataDoc[this.fieldKey]) this.dataDoc[this.fieldKey + '_1'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
if (!this.dataDoc[this.fieldKey + '_2'] && !this.dataDoc[this.fieldKey + '_alternate']) this.dataDoc[this.fieldKey + '_2'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
}
@@ -106,7 +113,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
false,
undefined,
action(() => {
- if (this._isAnyChildContentActive) return;
+ if (this.childActive) return;
this._animating = 'all 200ms';
// on click, animate slider movement to the targetWidth
this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this._props.PanelWidth();
@@ -247,7 +254,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
hoverFlip = (side: string | undefined) => {
if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] === 'hover') this.layoutDoc[`_${this._props.fieldKey}_usePath`] = side;
};
-
/**
* Creates the button used to flip the flashcards.
*/
@@ -259,7 +265,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
className="formattedTextBox-alternateButton"
onPointerDown={e =>
setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, () => {
- console.log(this.layoutDoc[`_${this._props.fieldKey}_revealOp`]);
if (!this.layoutDoc[`_${this._props.fieldKey}_revealOp`] || this.layoutDoc[`_${this._props.fieldKey}_revealOp`] === 'flip') {
this.flipFlashcard();
@@ -271,6 +276,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
style={{
background: usepath === 'alternate' ? 'white' : 'black',
color: usepath === 'alternate' ? 'black' : 'white',
+ display: 'inline-block',
}}>
<div key="alternate" className="formattedTextBox-flip">
<FontAwesomeIcon icon="turn-up" size="1x" />
@@ -280,6 +286,37 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
);
}
+ @computed get flashcardMenu() {
+ return (
+ <div>
+ <Tooltip
+ title={
+ this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? (
+ <div className="dash-tooltip">Flip to front side to use GPT</div>
+ ) : (
+ <div className="dash-tooltip">Ask GPT to create an answer on the back side of the flashcard</div>
+ )
+ }>
+ <div style={{ position: 'absolute', bottom: '3px', right: '55px', cursor: 'pointer' }} onPointerDown={e => (!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? this.askGPT(GPTCallType.CHATCARD) : null)}>
+ <FontAwesomeIcon icon="lightbulb" size="xl" />
+ </div>
+ </Tooltip>
+ <Tooltip title={<div className="dash-tooltip">Hover to reveal</div>}>
+ <div
+ style={{ position: 'absolute', bottom: '3px', right: '25px', cursor: 'pointer' }}
+ onClick={e => (this.revealOp === 'hover' ? (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip') : (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover'))}>
+ <FontAwesomeIcon color={this.revealOp === 'hover' ? 'blue' : 'black'} icon="layer-group" size="xl" />
+ </div>
+ </Tooltip>
+ {this.overlayAlternateIcon}
+ </div>
+ );
+ }
+
+ @action activateContent = () => {
+ this.childActive = true;
+ };
+
@action handleRenderGPTClick = () => {
// Call the GPT model and get the output
this.layoutDoc[`_${this._props.fieldKey}_usePath`] = 'alternate';
@@ -320,8 +357,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
const rubricText = ' Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text);
const queryText = questionText + ' UserAnswer: ' + this._inputValue + '. ' + rubricText;
this._loading = true;
+ const doc = DocCast(this.dataDoc[this.props.fieldKey + '_0']);
if (callType == GPTCallType.CHATCARD) {
- DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = '';
+ if (StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text) === '') {
+ this._loading = false;
+ return;
+ }
this.flipFlashcard();
}
try {
@@ -330,7 +371,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
console.error('GPT call failed');
return;
}
- this.animateRes(0, res, callType);
+ // this.animateRes(0, res, callType);
+ if (callType == GPTCallType.CHATCARD) {
+ DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
+ // this.flipFlashcard();
+ }
+ if (callType == GPTCallType.QUIZ) this._outputValue = res;
+ // DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
// this._outputValue = res;
console.log(res);
} catch (err) {
@@ -341,10 +388,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
layoutWidth = () => NumCast(this.layoutDoc.width, 200);
layoutHeight = () => NumCast(this.layoutDoc.height, 200);
- specificMenu = (): void => {
- const cm = ContextMenu.Instance;
- cm.addItem({ description: 'Create an Answer on the Back', event: () => this.askGPT(GPTCallType.CHATCARD), icon: 'pencil' });
- };
+ // specificMenu = (): void => {
+ // const cm = ContextMenu.Instance;
+ // cm.addItem({ description: 'Create an Answer on the Back', event: () => this.askGPT(GPTCallType.CHATCARD), icon: 'pencil' });
+ // };
+ @observable childActive = false;
render() {
const clearButton = (which: string) => (
@@ -378,12 +426,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
removeDocument={whichSlot.endsWith('1') ? this.remDoc1 : this.remDoc2}
NativeWidth={this.layoutWidth}
NativeHeight={this.layoutHeight}
- isContentActive={emptyFunction}
+ isContentActive={() => this.childActive}
isDocumentActive={returnFalse}
+ dontSelect={returnTrue}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- styleProvider={this._isAnyChildContentActive ? this._props.styleProvider : this.docStyleProvider}
+ styleProvider={this.childActive ? this._props.styleProvider : this.docStyleProvider}
hideLinkButton
- pointerEvents={this._isAnyChildContentActive ? undefined : returnNone}
+ pointerEvents={this.childActive ? undefined : returnNone}
/>
<div style={{ position: 'absolute', top: '-5px', left: '2px' }}>{layoutString ? null : clearButton(whichSlot)}</div>
</> // placeholder image if doc is missingleft: `${NumCast(this.layoutDoc.width, 200) - 33}px`
@@ -394,7 +443,15 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
);
};
const displayBox = (which: string, index: number, cover: number) => (
- <div className={`${index === 0 ? 'before' : 'after'}Box-cont`} key={which} style={{ width: this._props.PanelWidth() }} onPointerDown={e => this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}>
+ <div
+ className={`${index === 0 ? 'before' : 'after'}Box-cont`}
+ key={which}
+ style={{ width: this._props.PanelWidth() }}
+ onPointerDown={e => {
+ this.registerSliding(e, cover);
+ this.activateContent();
+ }}
+ ref={ele => this.createDropTarget(ele, which, index)}>
{displayDoc(which)}
</div>
);
@@ -431,6 +488,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
<textarea
value={this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? this._outputValue : this._inputValue}
onChange={this.handleInputChange}
+ onScroll={e => e.stopPropagation()}
readOnly={this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate'}></textarea>
{this._loading ? (
@@ -457,7 +515,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
return (
<div
className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}`} /* change className to easily disable/enable pointer events in CSS */
- onContextMenu={this.specificMenu}
+ // onContextMenu={this.specificMenu}
style={{ display: 'flex', flexDirection: 'column' }}
onMouseEnter={() => {
this.hoverFlip('alternate');
@@ -473,7 +531,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
<ReactLoading type="spin" height={30} width={30} color={'blue'} />
</div>
) : null}
- {this.overlayAlternateIcon}
+ {this.flashcardMenu}
+ {/* {this.overlayAlternateIcon} */}
</div>
);
}
@@ -491,7 +550,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
left: `calc(${this.clipWidth + '%'} - 0.5px)`,
cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this._props.PanelWidth() - 5) / this._props.PanelWidth() ? 'w-resize' : undefined,
}}
- onPointerDown={e => !this._isAnyChildContentActive && this.registerSliding(e, this._props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
+ onPointerDown={e => !this.childActive && this.registerSliding(e, this._props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
>
<div className="slide-handle" />
</div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a25249eac..2f3357791 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -503,6 +503,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
};
onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
+ if (this._props.dontSelect?.()) return;
if (e && this.layoutDoc.layout_hideContextMenu && Doc.noviceMode) {
e.preventDefault();
e.stopPropagation();
@@ -1378,7 +1379,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
screenToLocalScale = () => this._props.ScreenToLocalTransform().Scale;
isSelected = () => this.IsSelected;
select = (extendSelection: boolean, focusSelection?: boolean) => {
- DocumentView.SelectView(this, extendSelection);
+ if (!this._props.dontSelect?.()) DocumentView.SelectView(this, extendSelection);
if (focusSelection) {
DocumentView.showDocument(this.Document, {
willZoomCentered: true,
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 818d26956..138f00492 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -50,6 +50,7 @@ export interface FieldViewSharedProps {
PanelHeight: () => number;
isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
+ dontSelect: () => boolean | undefined;
childFilters: () => string[];
childFiltersByRanges: () => string[];
styleProvider: Opt<StyleProviderFuncType>;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 8ac8c2c5f..54643b4a5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -89,14 +89,14 @@ audiotag:hover {
right: 0;
bottom: 0;
width: 15;
- height: 15;
+ height: 22;
cursor: default;
}
.formattedTextBox-flip {
align-items: center;
position: absolute;
- right: 3px;
- bottom: 1px;
+ right: 2px;
+ bottom: 4px;
}
.formattedTextBox-outer {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 82133a06e..3e2befb5f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -816,6 +816,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
isTargetToggler = (anchor: Doc) => BoolCast(anchor.followLinkToggle);
specificContextMenu = (e: React.MouseEvent): void => {
+ if (this._props.dontSelect?.()) return;
const cm = ContextMenu.Instance;
let target = e.target as any; // hrefs are stored on the database of the <a> node that wraps the hyerlink <span>