diff options
Diffstat (limited to 'src/client/views/KeywordBox.tsx')
-rw-r--r-- | src/client/views/KeywordBox.tsx | 197 |
1 files changed, 158 insertions, 39 deletions
diff --git a/src/client/views/KeywordBox.tsx b/src/client/views/KeywordBox.tsx index d94f011f4..321362299 100644 --- a/src/client/views/KeywordBox.tsx +++ b/src/client/views/KeywordBox.tsx @@ -2,17 +2,26 @@ import { Colors, IconButton } from 'browndash-components'; import { action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import React from 'react'; -import { Doc } from '../../fields/Doc'; +import { returnFalse, setupMoveUpEvents } from '../../ClientUtils'; +import { Doc, DocListCast } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; +import { DocCast, NumCast } from '../../fields/Types'; +import { emptyFunction } from '../../Utils'; +import { Docs } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; import { DragManager, SetupDrag } from '../util/DragManager'; import { SnappingManager } from '../util/SnappingManager'; +import { CollectionFreeFormView } from './collections/collectionFreeForm'; +import { MainView } from './MainView'; import { DocumentView } from './nodes/DocumentView'; import { ObservableReactComponent } from './ObservableReactComponent'; interface KeywordItemProps { doc: Doc; - label: string; + keyword: string; + keywordDoc: Doc; + keywordCollection: Doc[]; setToEditing: () => void; isEditing: boolean; } @@ -25,28 +34,77 @@ export class KeywordItem extends ObservableReactComponent<KeywordItemProps> { this.ref = React.createRef(); } - private _dropDisposer?: DragManager.DragDropDisposer; private ref: React.RefObject<HTMLDivElement>; - protected createDropTarget = (ele: HTMLDivElement) => { - this._dropDisposer?.(); - SetupDrag(this.ref, () => undefined); - //ele && (this._dropDisposer = DragManager. (ele, this.onInternalDrop.bind(this), this.layoutDoc)); - //ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc)); + getKeywordCollectionDocs = () => { + for (const doc of DocListCast(Doc.UserDoc().myKeywordCollections)) { + if (doc.title === this._props.keyword) { + return doc[DocData].docs; + } + } + return null; + }; + + createCollection = () => { + const selected = DocListCast(this.getKeywordCollectionDocs()!); + const newEmbeddings = selected.map(doc => Doc.MakeEmbedding(doc)); + const newCollection = ((doc: Doc) => { + const docData = doc[DocData]; + docData.data = new List<Doc>(newEmbeddings); + docData.title = this._props.keyword; + doc._freeform_panX = doc._freeform_panY = 0; + return doc; + })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true)); + newEmbeddings.forEach(embed => (embed.embedContainer = newCollection)); + newCollection._width = 900; + newCollection._height = 900; + newCollection.layout_fitWidth = true; + //newCollection[DocData].smartCollection = this._props.keywordDoc; + + this._props.keywordDoc.collections = new List<Doc>([...DocListCast(this._props.keywordDoc.collections), newCollection]); + return newCollection; + }; + + @action + handleDragStart = (e: React.PointerEvent) => { + if (this._props.isEditing) { + const clone = this.ref.current?.cloneNode(true) as HTMLElement; + if (!clone) return; + + setupMoveUpEvents( + this, + e, + () => { + const dragData = new DragManager.DocumentDragData([this.createCollection()]); + DragManager.StartDocumentDrag([this.ref.current!], dragData, e.clientX, e.clientY, {}); + return true; + }, + returnFalse, + emptyFunction + ); + e.preventDefault(); + } }; @action removeLabel = () => { if (this._props.doc[DocData].data_labels) { - this._props.doc[DocData].data_labels = (this._props.doc[DocData].data_labels as List<string>).filter(label => label !== this._props.label) as List<string>; - this._props.doc![DocData][`${this._props.label}`] = false; + const filtered_docs = new List<Doc>(DocListCast(this.getKeywordCollectionDocs()!).filter(doc => doc !== this._props.doc)); + this._props.keywordDoc[DocData].docs = filtered_docs; + + this._props.doc[DocData].data_labels = (this._props.doc[DocData].data_labels as List<string>).filter(label => label !== this._props.keyword) as List<string>; + this._props.doc![DocData][`${this._props.keyword}`] = false; + + for (const collection of DocListCast(this._props.keywordDoc.collections)) { + collection[DocData].data = new List<Doc>(DocListCast(collection[DocData].data).filter(doc => !Doc.AreProtosEqual(this._props.doc, doc))); + } } }; render() { return ( - <div className="keyword" onClick={this._props.setToEditing} onPointerDown={() => {}} ref={this.ref}> - {this._props.label} + <div className="keyword" onClick={this._props.setToEditing} onPointerDown={this.handleDragStart} ref={this.ref}> + {this._props.keyword} {this.props.isEditing && <IconButton tooltip={'Remove label'} onPointerDown={this.removeLabel} icon={'X'} style={{ width: '8px', height: '8px', marginLeft: '10px' }} />} </div> ); @@ -61,31 +119,39 @@ interface KeywordBoxProps { @observer export class KeywordBox extends ObservableReactComponent<KeywordBoxProps> { @observable _currentInput: string = ''; - //private disposer: () => void; + private height: number = 0; + private ref: React.RefObject<HTMLDivElement>; + + @computed + get currentScale() { + return NumCast((this._props.doc.embedContainer as Doc)?._freeform_scale, 1); + } constructor(props: any) { super(props); makeObservable(this); + this.ref = React.createRef(); } - // componentDidMount(): void { - // reaction( - // () => ({ - // isDragging: SnappingManager.IsDragging, - // selectedDoc: DocumentView.SelectedDocs().lastElement(), - // isEditing: this._props.isEditing, - // }), - // ({ isDragging, selectedDoc, isEditing }) => { - // if (isDragging || selectedDoc !== this._props.doc || !isEditing) { - // this.setToView(); - // } - // } - // ); - // } - - // componentWillUnmount() { - // this.disposer(); - // } + componentDidMount(): void { + this.height = this.ref.current?.getBoundingClientRect().height ? this.ref.current?.getBoundingClientRect().height : 0; + this._props.doc._keywordHeight = this.height; + + reaction( + () => this.currentScale, + () => { + if (this.currentScale < 1) { + this.height = this.ref.current?.getBoundingClientRect().height ? this.ref.current?.getBoundingClientRect().height : 0; + this._props.doc._keywordHeight = this.height; + } + } + ); + } + + componentDidUpdate(prevProps: Readonly<KeywordBoxProps>): void { + this.height = this.ref.current?.getBoundingClientRect().height ? this.ref.current?.getBoundingClientRect().height : 0; + this._props.doc._keywordHeight = this.height; + } @action setToEditing = () => { @@ -97,14 +163,51 @@ export class KeywordBox extends ObservableReactComponent<KeywordBoxProps> { this._props.isEditing = false; }; + getKeywordCollection = (keyword: string) => { + for (const doc of DocListCast(Doc.UserDoc().myKeywordCollections)) { + if (doc.title === keyword) { + return doc; + } + } + + const keywordCollection = new Doc(); + keywordCollection.title = keyword; + keywordCollection[DocData].docs = new List<Doc>(); + keywordCollection.collections = new List<Doc>(); + Doc.UserDoc().myKeywordCollections = new List<Doc>([...DocListCast(Doc.UserDoc().myKeywordCollections), keywordCollection]); + + return keywordCollection; + }; + submitLabel = () => { - if (this._currentInput.trim()) { + if (!Doc.UserDoc().myKeywordCollections) { + Doc.UserDoc().myKeywordCollections = new List<Doc>(); + } + + const submittedLabel = this._currentInput.trim(); + if (submittedLabel) { + // If the keyword collection is not in the user doc, add it as a new doc, with the keyword as its title. + const keywordCollection = this.getKeywordCollection(submittedLabel); + + // If the document has no keywords field, create the field. if (!this._props.doc[DocData].data_labels) { this._props.doc[DocData].data_labels = new List<string>(); } - (this._props.doc![DocData].data_labels! as List<string>).push(this._currentInput.trim()); + // Add this document to the keyword's collection of associated documents. + keywordCollection[DocData].docs = new List<Doc>([...DocListCast(keywordCollection[DocData].docs), this._props.doc]); + + // Push the keyword to the document's keyword list field. + (this._props.doc![DocData].data_labels! as List<string>).push(submittedLabel); this._props.doc![DocData][`${this._currentInput}`] = true; + + // Iterate through the keyword document's collections and add a copy of the document to each collection + for (const collection of DocListCast(keywordCollection.collections)) { + const newEmbedding = Doc.MakeEmbedding(this._props.doc); + collection[DocData].data = new List<Doc>([...DocListCast(collection.data), newEmbedding]); + newEmbedding.embedContainer = collection; + } + this._currentInput = ''; // Clear the input box } }; @@ -120,19 +223,35 @@ export class KeywordBox extends ObservableReactComponent<KeywordBoxProps> { if (SnappingManager.IsDragging || !(seldoc === this._props.doc) || !this._props.isEditing) { setTimeout( action(() => { - if ((keywordsList as List<string>).length === 0) { - this._props.doc[DocData].showLabels = false; - } + // if ((keywordsList as List<string>).length === 0) { + // this._props.doc[DocData].showLabels = false; + // } this.setToView(); }) ); } return ( - <div className="keywords-container" style={{ backgroundColor: this._props.isEditing ? Colors.LIGHT_GRAY : Colors.TRANSPARENT, borderColor: this._props.isEditing ? Colors.BLACK : Colors.TRANSPARENT }}> + <div + className="keywords-container" + ref={this.ref} + style={{ + transformOrigin: 'top left', + transform: `scale(${1 / this.currentScale})`, + backgroundColor: this._props.isEditing ? Colors.LIGHT_GRAY : Colors.TRANSPARENT, + borderColor: this._props.isEditing ? Colors.BLACK : Colors.TRANSPARENT, + }}> <div className="keywords-list"> - {(keywordsList as List<string>).map(label => { - return <KeywordItem doc={this._props.doc} label={label} setToEditing={this.setToEditing} isEditing={this._props.isEditing}></KeywordItem>; + {(keywordsList as List<string>).map(keyword => { + return ( + <KeywordItem + doc={this._props.doc} + keyword={keyword} + keywordDoc={this.getKeywordCollection(keyword)} + keywordCollection={DocListCast(this.getKeywordCollection(keyword))} + setToEditing={this.setToEditing} + isEditing={this._props.isEditing}></KeywordItem> + ); })} </div> {this._props.isEditing ? ( |