diff options
author | bobzel <zzzman@gmail.com> | 2024-05-19 01:01:02 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2024-05-19 01:01:02 -0400 |
commit | 6e72f969029c22fe797397a6437836a0482260b6 (patch) | |
tree | e8ccde75702e557b2226c9069263e1bc3bd21a4b /src/client/views/nodes/ComparisonBox.tsx | |
parent | 5ff0bef5d3c4825aa7210a26c98aae3b24f4a835 (diff) | |
parent | 13dc6de0e0099f699ad0d2bb54401e6a0aa25018 (diff) |
Merge branch 'restoringEslint' into alyssa-starter
Diffstat (limited to 'src/client/views/nodes/ComparisonBox.tsx')
-rw-r--r-- | src/client/views/nodes/ComparisonBox.tsx | 163 |
1 files changed, 105 insertions, 58 deletions
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 9fd4d696a..84d14d4ef 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -1,30 +1,31 @@ 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 { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents, unimplementedFunction } from '../../../Utils'; -import { Doc, Opt, DocListCast } from '../../../fields/Doc'; -import { DocCast, NumCast, RTFCast, StrCast } from '../../../fields/Types'; -import { DocUtils, Docs } from '../../documents/Documents'; -import { DragManager, dropActionType } from '../../util/DragManager'; -import { undoBatch } from '../../util/UndoManager'; -import { SnappingManager } from '../../util/SnappingManager'; -import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; -import { StyleProp } from '../StyleProvider'; -import { Tooltip } from '@mui/material'; -import { CSSTransition } from 'react-transition-group'; +import { returnFalse, returnNone, setupMoveUpEvents } from '../../../ClientUtils'; +import { emptyFunction } from '../../../Utils'; +import { Doc, Opt } from '../../../fields/Doc'; +import { DocData } from '../../../fields/DocSymbols'; +import { RichTextField } from '../../../fields/RichTextField'; +import { DocCast, NumCast, RTFCast, StrCast, toList } from '../../../fields/Types'; +import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT'; +import { DocUtils } from '../../documents/DocUtils'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; +import { DragManager } from '../../util/DragManager'; +import { dropActionType } from '../../util/DropActionTypes'; +import { undoable } from '../../util/UndoManager'; +import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { PinDocView, PinProps } from '../PinFuncs'; +import { StyleProp } from '../StyleProp'; import './ComparisonBox.scss'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; -import { PinProps, PresBox } from './trails'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; -import { RichTextField } from '../../../fields/RichTextField'; -import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT'; -import { DocData } from '../../../fields/DocSymbols'; -import { KeyValueBox } from './KeyValueBox'; @observer -export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface { +export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); } @@ -71,17 +72,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() } }; - @undoBatch - private internalDrop = (e: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => { + private internalDrop = undoable((e: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => { if (dropEvent.complete.docDragData) { - const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments; - const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocs, this.Document, (doc: Doc | Doc[]) => this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey)); - Doc.SetContainer(droppedDocs.lastElement(), this.dataDoc); + const { droppedDocuments } = dropEvent.complete.docDragData; + const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocuments, this.Document, (doc: Doc | Doc[]) => this.addDoc(toList(doc).lastElement(), fieldKey)); + Doc.SetContainer(droppedDocuments.lastElement(), this.dataDoc); !added && e.preventDefault(); e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place return added; } - }; + return undefined; + }, 'internal drop'); private registerSliding = (e: React.PointerEvent<HTMLDivElement>, targetWidth: number) => { if (e.button !== 2) { @@ -90,11 +91,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() e, this.onPointerMove, emptyFunction, - action((e, doubleTap) => { + action((moveEv, doubleTap) => { if (doubleTap) { this._isAnyChildContentActive = true; - if (!this.dataDoc[this.fieldKey + '_1']) this.dataDoc[this.fieldKey + '_1'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc); - if (!this.dataDoc[this.fieldKey + '_2']) this.dataDoc[this.fieldKey + '_2'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc); + 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); } }), false, @@ -107,7 +108,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() this.layoutDoc[this.clipHeightKey] = (targetWidth * 100) / this._props.PanelHeight(); setTimeout( - action(() => (this._animating = '')), + action(() => { + this._animating = ''; + }), 200 ); }) @@ -133,18 +136,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() }); if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; - /* addAsAnnotation &&*/ this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.Document); + /* addAsAnnotation && */ this.addDocument(anchor); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.Document); return anchor; } return this.Document; }; - @undoBatch - clearDoc = (fieldKey: string) => { + clearDoc = undoable((fieldKey: string) => { delete this.dataDoc[fieldKey]; this.dataDoc[fieldKey] = 'empty'; - }; + }, '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); @@ -162,33 +164,63 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() return false; }; - whenChildContentsActiveChanged = action((isActive: boolean) => (this._isAnyChildContentActive = isActive)); - closeDown = (e: React.PointerEvent, which: string) => { setupMoveUpEvents( this, e, - e => { + moveEv => { const de = new DragManager.DocumentDragData([DocCast(this.dataDoc[which])], dropActionType.move); de.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { return addDocument(doc); }; de.canEmbed = true; - DragManager.StartDocumentDrag([this._closeRef.current!], de, e.clientX, e.clientY); + DragManager.StartDocumentDrag([this._closeRef.current!], de, moveEv.clientX, moveEv.clientY); return true; }, emptyFunction, - e => this.clearDoc(which) + () => this.clearDoc(which) ); }; docStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any => { if (property === StyleProp.PointerEvents) return 'none'; return this._props.styleProvider?.(doc, props, property); }; - moveDoc1 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_1'), true); - moveDoc2 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true); - remDoc1 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_1'), true); - remDoc2 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_2'), true); + moveDoc1 = (docs: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => toList(docs).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_1'), true); + moveDoc2 = (docs: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => toList(docs).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true); + remDoc1 = (docs: Doc | Doc[]) => toList(docs).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_1'), true); + remDoc2 = (docs: Doc | Doc[]) => toList(docs).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_2'), true); + + /** + * Tests for whether a comparison box slot (ie, before or after) has renderable text content + * @param whichSlot field key for start or end slot + * @returns a JSX layout string if a text field is found, othwerise undefined + */ + testForTextFields = (whichSlot: string) => { + const slotData = Doc.Get(this.dataDoc, whichSlot, true); + const slotHasText = slotData instanceof RichTextField || typeof slotData === 'string'; + const subjectText = RTFCast(this.Document[this.fieldKey])?.Text.trim(); + const altText = RTFCast(this.Document[this.fieldKey + '_alternate'])?.Text.trim(); + const layoutTemplateString = + slotHasText ? FormattedTextBox.LayoutString(whichSlot): + whichSlot.endsWith('1') ? (subjectText !== undefined ? FormattedTextBox.LayoutString(this.fieldKey) : undefined) : + altText !== undefined ? FormattedTextBox.LayoutString(this.fieldKey + '_alternate'): undefined; // prettier-ignore + + // A bit hacky to try out the concept of using GPT to fill in flashcards + // If the second slot doesn't have anything in it, but the fieldKey slot has text (e.g., this.text is a string) + // and the fieldKey + "_alternate" has text that includes a GPT query (indicated by (( && )) ) that is parameterized (optionally) by the fieldKey text (this) or other metadata (this.<field>). + // eg., this.text_alternate is + // "((Provide a one sentence definition for (this) that doesn't use any word in (this.excludeWords) ))" + // where (this) is replaced by the text in the fieldKey slot abd this.excludeWords is repalced by the conetnts of the excludeWords field + // The GPT call will put the "answer" in the second slot of the comparison (eg., text_2) + if (whichSlot.endsWith('2') && !layoutTemplateString?.includes(whichSlot)) { + const queryText = altText?.replace('(this)', subjectText); // TODO: this should be done in Doc.setField but it doesn't know about the fieldKey ... + if (queryText?.match(/\(\(.*\)\)/)) { + Doc.SetField(this.Document, whichSlot, ':=' + queryText, false); // make the second slot be a computed field on the data doc that calls ChatGpt + } + } + return layoutTemplateString; + }; + _closeRef = React.createRef<HTMLDivElement>(); /** @@ -283,22 +315,24 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() </Tooltip> ); }; - const displayDoc = (which: string) => { - const whichDoc = DocCast(this.dataDoc[which]); + const displayDoc = (whichSlot: string) => { + const whichDoc = DocCast(this.dataDoc[whichSlot]); const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc); // if there is no Doc in the first comparison slot, but the comparison box's fieldKey slot has a RichTextField, then render a text box to show the contents of the document's field key slot - const layoutTemplateString = !targetDoc && which.endsWith('1') && this.Document[this.fieldKey] instanceof RichTextField ? FormattedTextBox.LayoutString(this.fieldKey) : undefined; + const layoutString = !targetDoc && whichSlot.endsWith('1') && this.Document[this.fieldKey] instanceof RichTextField ? FormattedTextBox.LayoutString(this.fieldKey) : undefined; - return targetDoc || layoutTemplateString ? ( + return targetDoc || layoutString ? ( <> <DocumentView + // eslint-disable-next-line react/jsx-props-no-spreading {...this._props} + ignoreUsePath={layoutString ? true : undefined} renderDepth={this.props.renderDepth + 1} - LayoutTemplateString={layoutTemplateString} - Document={layoutTemplateString ? this.Document : targetDoc} + LayoutTemplateString={layoutString} + Document={layoutString ? this.Document : targetDoc} containerViewPath={this.DocumentView?.().docViewPath} - moveDocument={which.endsWith('1') ? this.moveDoc1 : this.moveDoc2} - removeDocument={which.endsWith('1') ? this.remDoc1 : this.remDoc2} + moveDocument={whichSlot.endsWith('1') ? this.moveDoc1 : this.moveDoc2} + removeDocument={whichSlot.endsWith('1') ? this.remDoc1 : this.remDoc2} NativeWidth={() => NumCast(this.layoutDoc.width, 200)} NativeHeight={(): number => { return NumCast(this.layoutDoc.height, 200); @@ -307,10 +341,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() isDocumentActive={returnFalse} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} styleProvider={this._isAnyChildContentActive ? this._props.styleProvider : this.docStyleProvider} - hideLinkButton={true} + hideLinkButton pointerEvents={this._isAnyChildContentActive ? undefined : returnNone} /> - {layoutTemplateString ? null : clearButton(which)} + {layoutString ? null : clearButton(whichSlot)} </> // placeholder image if doc is missing ) : ( <div className="placeholder"> @@ -318,13 +352,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() </div> ); }; - const displayBox = (which: string, index: number, cover: number) => { - return ( - <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)}> - {displayDoc(which)} - </div> - ); - }; + 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)}> + {displayDoc(which)} + </div> + ); const displayBoxReveal = (which: string, which2: string, index: number, cover: number) => { return ( @@ -434,3 +466,18 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() } } } + +Docs.Prototypes.TemplateMap.set(DocumentType.COMPARISON, { + data: '', + layout: { view: ComparisonBox, dataField: 'data' }, + options: { + acl: '', + backgroundColor: 'gray', + dropAction: dropActionType.move, + waitForDoubleClickToClick: 'always', + _layout_reflowHorizontal: true, + _layout_reflowVertical: true, + _layout_nativeDimEditable: true, + systemIcon: 'BsLayoutSplit', + }, +}); |