From add4926f62b397fc6c655be31a711dd7b83b469a Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 23 Aug 2024 00:45:10 -0400 Subject: starting to cleanup keywordsBox --- src/client/views/KeywordBox.tsx | 390 +++++++++++++++++-------------------- src/client/views/StyleProvider.tsx | 8 +- 2 files changed, 180 insertions(+), 218 deletions(-) (limited to 'src') diff --git a/src/client/views/KeywordBox.tsx b/src/client/views/KeywordBox.tsx index 20cb63d66..703299ae6 100644 --- a/src/client/views/KeywordBox.tsx +++ b/src/client/views/KeywordBox.tsx @@ -3,17 +3,17 @@ import { action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import React from 'react'; import { returnFalse, setupMoveUpEvents } from '../../ClientUtils'; -import { Doc, DocListCast } from '../../fields/Doc'; +import { Utils, emptyFunction } from '../../Utils'; +import { Doc, DocListCast, StrListCast } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; import { NumCast, StrCast } from '../../fields/Types'; -import { emptyFunction, Utils } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; +import { SelectionManager } from '../util/SelectionManager'; import { SnappingManager } from '../util/SnappingManager'; -import { DocumentView } from './nodes/DocumentView'; -import { ObservableReactComponent } from './ObservableReactComponent'; import { undoable } from '../util/UndoManager'; +import { ObservableReactComponent } from './ObservableReactComponent'; interface KeywordItemProps { doc: Doc; @@ -28,69 +28,149 @@ interface KeywordItemProps { */ @observer export class KeywordItem extends ObservableReactComponent { - constructor(props: any) { - super(props); - makeObservable(this); - this.ref = React.createRef(); + /** + * return list of all Docs that collect Docs with specified keywords + */ + public static get AllKeywordCollections() { + return DocListCast(Doc.ActiveDashboard?.myKeywordCollections); } + /** + * Find Doc that collects all Docs with given keyword + * @param keyword keyword string + * @returns keyword collection Doc or undefined + */ + public static findKeywordCollectionDoc = (keyword: String) => KeywordItem.AllKeywordCollections.find(doc => doc.title === keyword); - private ref: React.RefObject; + /** + * Creates a Doc that collects Docs with the specified keyword + * @param keyword keyword string + * @returns collection Doc + */ + public static createKeywordCollectionDoc = (keyword: string) => { + const newKeywordCol = new Doc(); + newKeywordCol.title = keyword; + newKeywordCol.collections = new List(); + newKeywordCol[DocData].docs = new List(); + // If the active Dashboard does not have a keyword collection, create it. + if (Doc.ActiveDashboard) { + if (!Doc.ActiveDashboard.myKeywordCollections) Doc.ActiveDashboard.myKeywordCollections = new List(); + Doc.AddDocToList(Doc.ActiveDashboard, 'myKeywordCollections', newKeywordCol); + } + return newKeywordCol; + }; /** - * Gets the documents that a keyword is associated with. + * Gets all Docs that have the specified keyword + * @param keyword keyword string * @returns An array of documents that contain the keyword. */ - getKeywordCollectionDocs = () => { - for (const doc of DocListCast(Doc.ActiveDashboard?.myKeywordCollections)) { - if (doc.title === this._props.keyword) { - return doc[DocData].docs; + public static allDocsWithKeyword = (keyword: string) => DocListCast(KeywordItem.findKeywordCollectionDoc(keyword)?.[DocData].docs); + + /** + * Adds a keyword to the metadata of this document + * @param keyword keyword string + */ + public static addLabelToDoc = (doc: Doc, keyword: string) => { + // If the keyword collection is not in active Dashboard, add it as a new doc, with the keyword as its title. + const keywordCollection = KeywordItem.findKeywordCollectionDoc(keyword) ?? KeywordItem.createKeywordCollectionDoc(keyword); + + // If the document is of type COLLECTION, make it a smart collection, otherwise, add the keyword to the document. + if (doc.type === DocumentType.COL) { + Doc.AddDocToList(keywordCollection[DocData], 'collections', doc); + + // Iterate through the keyword Doc collections and add a copy of the document to each collection + for (const cdoc of DocListCast(keywordCollection[DocData].docs)) { + if (!DocListCast(doc[DocData].data).find(d => Doc.AreProtosEqual(d, cdoc))) { + const newEmbedding = Doc.MakeEmbedding(cdoc); + Doc.AddDocToList(doc[DocData], 'data', newEmbedding); + Doc.SetContainer(newEmbedding, doc); + } + } + } else { + // Add this document to the keyword's collection of associated documents. + Doc.AddDocToList(keywordCollection[DocData], 'docs', doc); + + // Iterate through the keyword document's collections and add a copy of the document to each collection + for (const collection of DocListCast(keywordCollection.collections)) { + if (!DocListCast(collection[DocData].data).find(d => Doc.AreProtosEqual(d, doc))) { + const newEmbedding = Doc.MakeEmbedding(doc); + Doc.AddDocToList(collection[DocData], 'data', newEmbedding); + Doc.SetContainer(newEmbedding, collection); + } } } - return null; + + if (!doc[DocData].data_labels) doc[DocData].data_labels = new List(); + (doc[DocData].data_labels as List).push(keyword); + doc[DocData][keyword] = true; }; + public static RemoveLabel = (doc: Doc, keyword: string, keywordDoc: Doc) => { + if (doc[DocData].data_labels) { + if (doc.type === DocumentType.COL) { + Doc.RemoveDocFromList(keywordDoc[DocData], 'collections', doc); + + for (const cur_doc of KeywordItem.allDocsWithKeyword(keyword)) { + doc[DocData].data = new List(DocListCast(doc[DocData].data).filter(d => !Doc.AreProtosEqual(cur_doc, d))); + } + } else { + Doc.RemoveDocFromList(keywordDoc[DocData], 'docs', doc); + + for (const collection of DocListCast(keywordDoc.collections)) { + collection[DocData].data = new List(DocListCast(collection[DocData].data).filter(d => !Doc.AreProtosEqual(doc, d))); + } + } + } + doc[DocData].data_labels = new List((doc[DocData].data_labels as List).filter(label => label !== keyword)); + doc[DocData][keyword] = undefined; + }; + + private _ref: React.RefObject; + + constructor(props: any) { + super(props); + makeObservable(this); + this._ref = React.createRef(); + } + /** * Creates a smart collection. * @returns */ createCollection = () => { // Get the documents that contain the keyword. - const selected = DocListCast(this.getKeywordCollectionDocs()!); - const newEmbeddings = selected.map(doc => Doc.MakeEmbedding(doc)); + const newEmbeddings = KeywordItem.allDocsWithKeyword(this._props.keyword).map(doc => Doc.MakeEmbedding(doc)); // Create a new collection and set up configurations. const newCollection = ((doc: Doc) => { const docData = doc[DocData]; docData.data = new List(newEmbeddings); docData.title = this._props.keyword; + docData.data_labels = new List([this._props.keyword]); + docData[`${this._props.keyword}`] = true; + docData.showLabels = true; doc._freeform_panX = doc._freeform_panY = 0; + doc._width = 900; + doc._height = 900; + doc.layout_fitWidth = true; 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; + newEmbeddings.forEach(embed => Doc.SetContainer(embed, newCollection)); // Add the collection to the keyword document's list of associated smart collections. - this._props.keywordDoc.collections = new List([...DocListCast(this._props.keywordDoc.collections), newCollection]); - newCollection[DocData].data_labels = new List([this._props.keyword]); - newCollection[DocData][`${this._props.keyword}`] = true; - newCollection[DocData].showLabels = true; + Doc.AddDocToList(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, {}); + DragManager.StartDocumentDrag([this._ref.current!], dragData, e.clientX, e.clientY, {}); return true; }, returnFalse, @@ -100,51 +180,34 @@ export class KeywordItem extends ObservableReactComponent { } }; - @action - removeLabel = () => { - if (this._props.doc[DocData].data_labels) { - if (this._props.doc.type === DocumentType.COL) { - const filtered_collections = new List(DocListCast(this._props.keywordDoc.collections).filter(doc => doc !== this._props.doc)); - this._props.keywordDoc.collections = filtered_collections; - - for (const cur_doc of DocListCast(this.getKeywordCollectionDocs()!, [])) { - this._props.doc[DocData].data = new List(DocListCast(this._props.doc[DocData].data).filter(doc => !Doc.AreProtosEqual(cur_doc, doc))); - } - } else { - const filtered_docs = new List(DocListCast(this.getKeywordCollectionDocs()!).filter(doc => doc !== this._props.doc)); - this._props.keywordDoc[DocData].docs = filtered_docs; - - for (const collection of DocListCast(this._props.keywordDoc.collections)) { - collection[DocData].data = new List(DocListCast(collection[DocData].data).filter(doc => !Doc.AreProtosEqual(this._props.doc, doc))); - } - } - } - this._props.doc[DocData].data_labels = (this._props.doc[DocData].data_labels as List).filter(label => label !== this._props.keyword) as List; - this._props.doc![DocData][`${this._props.keyword}`] = false; - }; - render() { const keyword = this._props.keyword.replace(/^@/, ''); const metadata = this._props.keyword.startsWith('@'); return ( -
+
{metadata ? ( {keyword}  - {this._props.doc[keyword] as string}{' '} + {this._props.doc[keyword] as string} ) : ( keyword )} - {this.props.isEditing && } + {this.props.isEditing && ( + KeywordItem.RemoveLabel(this._props.doc, this._props.keyword, this._props.keywordDoc), `remove label ${this._props.keyword}`)} + icon={'X'} + style={{ width: '8px', height: '8px', marginLeft: '10px' }} + /> + )}
); } } interface KeywordBoxProps { - doc: Doc; - isEditing: boolean; + Document: Doc; } /** @@ -152,76 +215,50 @@ interface KeywordBoxProps { */ @observer export class KeywordBox extends ObservableReactComponent { - @observable _currentInput: string = ''; - private height: number = 0; - private ref: React.RefObject; - - @computed - get currentScale() { - return NumCast((this._props.doc.embedContainer as Doc)?._freeform_scale, 1); - } - - @computed - get cur_height() { - return this.ref.current?.offsetHeight ? this.ref.current?.offsetHeight : 0; - } + private _height: number = 0; + private _ref: React.RefObject; constructor(props: any) { super(props); makeObservable(this); - this.ref = React.createRef(); + this._ref = React.createRef(); reaction( () => this.cur_height, () => { - this._props.doc[DocData].keywordHeight = this.height; + this._props.Document[DocData].keywordHeight = this._height; } ); } - componentDidMount(): void { - this.height = this.ref.current?.offsetHeight ? this.ref.current?.offsetHeight : 0; - this._props.doc[DocData].keywordHeight = this.height; - } + @observable _currentInput = ''; + @observable _isEditing = !StrListCast(this._props.Document[DocData].data_labels).length; - componentDidUpdate(prevProps: Readonly): void { - this.height = this.ref.current?.offsetHeight ? this.ref.current?.offsetHeight : 0; - this._props.doc[DocData].keywordHeight = this.height; + @computed get currentScale() { + return NumCast((this._props.Document.embedContainer as Doc)?._freeform_scale, 1); } - @action - setToEditing = () => { - this._props.isEditing = true; - }; + @computed get cur_height() { + return this._ref.current?.offsetHeight ?? 0; + } - @action - setToView = () => { - this._props.isEditing = false; - }; + @computed get isEditing() { + return this._isEditing && SelectionManager.Docs().includes(this._props.Document); + } - /** - * Gets the document associated with a keyword. - * @param keyword The keyword being searched for - * @returns A Doc containing keyword information - */ - getKeywordCollection = (keyword: string) => { - // Look for the keyword document. - for (const doc of DocListCast(Doc.ActiveDashboard!.myKeywordCollections)) { - if (doc.title === keyword) { - return doc; - } - } + componentDidMount() { + this._height = this._ref.current?.offsetHeight ?? 0; + this._props.Document[DocData].keywordHeight = this._height; + } - // If not contained, create a new document and add it to the active Dashboard's keyword list. - const keywordCollection = new Doc(); - keywordCollection.title = keyword; - keywordCollection[DocData].docs = new List(); - keywordCollection.collections = new List(); - if (Doc.ActiveDashboard) { - Doc.ActiveDashboard.myKeywordCollections = new List([...DocListCast(Doc.ActiveDashboard.myKeywordCollections), keywordCollection]); - } + componentDidUpdate(prevProps: Readonly): void { + this._height = this._ref.current?.offsetHeight ?? 0; + this._props.Document[DocData].keywordHeight = this._height; + } - return keywordCollection; + @action + setToEditing = () => { + this._isEditing = true; }; /** @@ -229,94 +266,42 @@ export class KeywordBox extends ObservableReactComponent { * @param keyword */ submitLabel = undoable((keyword: string) => { - // If the active Dashboard does not have a keyword collection, create it. - if (Doc.ActiveDashboard && !Doc.ActiveDashboard.myKeywordCollections) { - Doc.ActiveDashboard.myKeywordCollections = new List(); - } - const submittedLabel = keyword.trim(); - if (submittedLabel && !this._props.doc![DocData][`${submittedLabel}`]) { - // If the keyword collection is not in active Dashboard, 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(); - } - - // If the document is of type COLLECTION, make it a smart collection, otherwise, add the keyword to the document. - if (this._props.doc.type === DocumentType.COL) { - keywordCollection.collections = new List([...DocListCast(keywordCollection.collections), this._props.doc]); - - // Iterate through the keyword document's collections and add a copy of the document to each collection - for (const doc of DocListCast(keywordCollection[DocData].docs)) { - const newEmbedding = Doc.MakeEmbedding(doc); - this._props.doc[DocData].data = new List([...DocListCast(this._props.doc[DocData].data), newEmbedding]); - newEmbedding.embedContainer = this._props.doc; - } - } else { - // Add this document to the keyword's collection of associated documents. - keywordCollection[DocData].docs = new List([...DocListCast(keywordCollection[DocData].docs), this._props.doc]); - - // 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([...DocListCast(collection.data), newEmbedding]); - newEmbedding.embedContainer = collection; - } - } - - // Push the keyword to the document's keyword list field. - (this._props.doc![DocData].data_labels! as List).push(submittedLabel); - this._props.doc![DocData][`${submittedLabel}`] = true; + if (submittedLabel && !this._props.Document[DocData][submittedLabel]) { + KeywordItem.addLabelToDoc(this._props.Document, submittedLabel); this._currentInput = ''; // Clear the input box } }, 'added doc label'); - @action - onInputChange = (e: React.ChangeEvent) => { - this._currentInput = e.target.value; - }; - render() { - const keywordsList = this._props.doc[DocData].data_labels ? this._props.doc[DocData].data_labels : new List(); - const seldoc = DocumentView.SelectedDocs().lastElement(); - if (SnappingManager.IsDragging || !(seldoc === this._props.doc) || !this._props.isEditing) { - setTimeout( - action(() => { - if ((keywordsList as List).length === 0) { - this._props.doc[DocData].showLabels = false; - } - this.setToView(); - }) - ); - } + const keywordsList = StrListCast(this._props.Document[DocData].data_labels); - return ( + return !this._props.Document.showLabels ? null : (
-
+
- {(keywordsList as List).map(keyword => { - return ; + {keywordsList.map(keyword => { + const keywordDoc = KeywordItem.findKeywordCollectionDoc(keyword); + return !keywordDoc ? null : ; })}
- {this._props.isEditing ? ( + {this.isEditing ? (
(this._currentInput = e.target.value))} onKeyDown={e => { e.key === 'Enter' ? this.submitLabel(this._currentInput) : null; e.stopPropagation(); @@ -328,45 +313,28 @@ export class KeywordBox extends ObservableReactComponent { style={{ width: '100%', borderRadius: '5px' }} />
- {Doc.ActiveDashboard?.myKeywordCollections ? ( -
- {DocListCast(Doc.ActiveDashboard?.myKeywordCollections).map(doc => { - const keyword = StrCast(doc.title); - return ( -
- ) : ( -
- )} +
+ {KeywordItem.AllKeywordCollections.map(doc => { + const keyword = StrCast(doc.title); + return ( +
- { - if ((keywordsList as List).length === 0) { - this._props.doc[DocData].showLabels = false; - } else { - this.setToView(); - } - }} - icon={'x'} - style={{ width: '4px' }} - /> + {!keywordsList.length ? null : ( // + (this._isEditing = false))} icon="x" /> + )}
- ) : ( -
- )} + ) : null}
); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 374399445..1e80e7ee5 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -364,13 +364,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt ); }; - const keywords = () => { - if (doc && doc![DocData].showLabels && (!doc[DocData].data_labels || (doc[DocData].data_labels as List).length === 0)){ - return () - } else if (doc && doc![DocData].data_labels && doc![DocData].showLabels) { - return () - } - } + const keywords = () => doc ? : null; return ( <> {paint()} -- cgit v1.2.3-70-g09d2