From 6cf715a76dfb3f6a80fbf7c33e643681ea1a584c Mon Sep 17 00:00:00 2001 From: IEatChili Date: Tue, 30 Jul 2024 14:30:08 -0400 Subject: feat: adjusted ui: --- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/KeywordBox.tsx | 263 +++++++++++++++++++------------ src/client/views/StyleProvider.scss | 26 ++- 3 files changed, 189 insertions(+), 102 deletions(-) (limited to 'src') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index dc40562e8..bd6952620 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -832,7 +832,7 @@ export class DocumentDecorations extends ObservableReactComponent DocumentView.Selected()} /> diff --git a/src/client/views/KeywordBox.tsx b/src/client/views/KeywordBox.tsx index 321362299..68584a7fa 100644 --- a/src/client/views/KeywordBox.tsx +++ b/src/client/views/KeywordBox.tsx @@ -1,4 +1,4 @@ -import { Colors, IconButton } from 'browndash-components'; +import { Button, Colors, IconButton } from 'browndash-components'; import { action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import React from 'react'; @@ -6,14 +6,11 @@ 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 { NumCast, StrCast } from '../../fields/Types'; import { emptyFunction } from '../../Utils'; -import { Docs } from '../documents/Documents'; -import { DocUtils } from '../documents/DocUtils'; -import { DragManager, SetupDrag } from '../util/DragManager'; +import { DocumentType } from '../documents/DocumentTypes'; +import { DragManager } 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'; @@ -21,11 +18,13 @@ interface KeywordItemProps { doc: Doc; keyword: string; keywordDoc: Doc; - keywordCollection: Doc[]; setToEditing: () => void; isEditing: boolean; } +/** + * A component that handles individual keywords. + */ @observer export class KeywordItem extends ObservableReactComponent { constructor(props: any) { @@ -36,8 +35,12 @@ export class KeywordItem extends ObservableReactComponent { private ref: React.RefObject; + /** + * Gets the documents that a keyword is associated with. + * @returns An array of documents that contain the keyword. + */ getKeywordCollectionDocs = () => { - for (const doc of DocListCast(Doc.UserDoc().myKeywordCollections)) { + for (const doc of DocListCast(Doc.ActiveDashboard?.myKeywordCollections)) { if (doc.title === this._props.keyword) { return doc[DocData].docs; } @@ -45,9 +48,16 @@ export class KeywordItem extends ObservableReactComponent { return null; }; + /** + * 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)); + + // Create a new collection and set up configurations. const newCollection = ((doc: Doc) => { const docData = doc[DocData]; docData.data = new List(newEmbeddings); @@ -59,9 +69,12 @@ export class KeywordItem extends ObservableReactComponent { newCollection._width = 900; newCollection._height = 900; newCollection.layout_fitWidth = true; - //newCollection[DocData].smartCollection = this._props.keywordDoc; + // 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; return newCollection; }; @@ -89,16 +102,24 @@ export class KeywordItem extends ObservableReactComponent { @action removeLabel = () => { if (this._props.doc[DocData].data_labels) { - const filtered_docs = new List(DocListCast(this.getKeywordCollectionDocs()!).filter(doc => doc !== this._props.doc)); - this._props.keywordDoc[DocData].docs = filtered_docs; + 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; - 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; + 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))); + 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() { @@ -116,6 +137,9 @@ interface KeywordBoxProps { isEditing: boolean; } +/** + * A component that handles the keyword display for documents. + */ @observer export class KeywordBox extends ObservableReactComponent { @observable _currentInput: string = ''; @@ -127,30 +151,32 @@ export class KeywordBox extends ObservableReactComponent { 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; + } + constructor(props: any) { super(props); makeObservable(this); this.ref = React.createRef(); - } - - componentDidMount(): void { - this.height = this.ref.current?.getBoundingClientRect().height ? this.ref.current?.getBoundingClientRect().height : 0; - this._props.doc._keywordHeight = this.height; reaction( - () => this.currentScale, + () => this.cur_height, () => { - if (this.currentScale < 1) { - this.height = this.ref.current?.getBoundingClientRect().height ? this.ref.current?.getBoundingClientRect().height : 0; - this._props.doc._keywordHeight = this.height; - } + this._props.doc[DocData].keywordHeight = this.height; } ); } + componentDidMount(): void { + this.height = this.ref.current?.offsetHeight ? this.ref.current?.offsetHeight : 0; + this._props.doc[DocData].keywordHeight = this.height; + } + componentDidUpdate(prevProps: Readonly): void { - this.height = this.ref.current?.getBoundingClientRect().height ? this.ref.current?.getBoundingClientRect().height : 0; - this._props.doc._keywordHeight = this.height; + this.height = this.ref.current?.offsetHeight ? this.ref.current?.offsetHeight : 0; + this._props.doc[DocData].keywordHeight = this.height; } @action @@ -163,30 +189,44 @@ export class KeywordBox extends ObservableReactComponent { this._props.isEditing = false; }; + /** + * Gets the document associated with a keyword. + * @param keyword The keyword being searched for + * @returns A Doc containing keyword information + */ getKeywordCollection = (keyword: string) => { - for (const doc of DocListCast(Doc.UserDoc().myKeywordCollections)) { + // Look for the keyword document. + for (const doc of DocListCast(Doc.ActiveDashboard!.myKeywordCollections)) { if (doc.title === keyword) { return doc; } } + // 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(); - Doc.UserDoc().myKeywordCollections = new List([...DocListCast(Doc.UserDoc().myKeywordCollections), keywordCollection]); + if (Doc.ActiveDashboard) { + Doc.ActiveDashboard.myKeywordCollections = new List([...DocListCast(Doc.ActiveDashboard.myKeywordCollections), keywordCollection]); + } return keywordCollection; }; - submitLabel = () => { - if (!Doc.UserDoc().myKeywordCollections) { - Doc.UserDoc().myKeywordCollections = new List(); + /** + * Adds the keyword to the document. + * @param keyword + */ + submitLabel = (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 = 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 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. @@ -194,20 +234,31 @@ export class KeywordBox extends ObservableReactComponent { this._props.doc[DocData].data_labels = new List(); } - // Add this document to the keyword's collection of associated documents. - keywordCollection[DocData].docs = new List([...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).push(submittedLabel); - this._props.doc![DocData][`${this._currentInput}`] = true; + // 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 collection of DocListCast(keywordCollection.collections)) { - const newEmbedding = Doc.MakeEmbedding(this._props.doc); - collection[DocData].data = new List([...DocListCast(collection.data), newEmbedding]); - newEmbedding.embedContainer = collection; + // 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; this._currentInput = ''; // Clear the input box } }; @@ -223,9 +274,9 @@ export class KeywordBox extends ObservableReactComponent { if (SnappingManager.IsDragging || !(seldoc === this._props.doc) || !this._props.isEditing) { setTimeout( action(() => { - // if ((keywordsList as List).length === 0) { - // this._props.doc[DocData].showLabels = false; - // } + if ((keywordsList as List).length === 0) { + this._props.doc[DocData].showLabels = false; + } this.setToView(); }) ); @@ -237,59 +288,75 @@ export class KeywordBox extends ObservableReactComponent { ref={this.ref} style={{ transformOrigin: 'top left', + overflow: 'hidden', transform: `scale(${1 / this.currentScale})`, backgroundColor: this._props.isEditing ? Colors.LIGHT_GRAY : Colors.TRANSPARENT, borderColor: this._props.isEditing ? Colors.BLACK : Colors.TRANSPARENT, + maxWidth: `400px`, }}> -
- {(keywordsList as List).map(keyword => { - return ( - - ); - })} -
- {this._props.isEditing ? ( -
-
- { - e.key === 'Enter' ? this.submitLabel() : null; - e.stopPropagation(); - }} - type="text" - placeholder="Input keywords for document..." - aria-label="keyword-input" - className="keyword-input" - style={{ width: '100%', borderRadius: '5px' }} - /> -
-
- { - if ((keywordsList as List).length === 0) { - this._props.doc[DocData].showLabels = false; - } else { - this.setToView(); - } - }} - icon={'x'} - style={{ width: '4px' }} - /> -
+
+
+ {(keywordsList as List).map(keyword => { + return ; + })}
- ) : ( -
- )} + {this._props.isEditing ? ( +
+
+ { + e.key === 'Enter' ? this.submitLabel(this._currentInput) : null; + e.stopPropagation(); + }} + type="text" + placeholder="Input keywords for document..." + aria-label="keyword-input" + className="keyword-input" + style={{ width: '100%', borderRadius: '5px' }} + /> +
+ {Doc.ActiveDashboard?.myKeywordCollections ? ( +
+ {DocListCast(Doc.ActiveDashboard?.myKeywordCollections).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' }} + /> +
+
+ ) : ( +
+ )} +
); } diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss index 4267762aa..1d41697f5 100644 --- a/src/client/views/StyleProvider.scss +++ b/src/client/views/StyleProvider.scss @@ -77,14 +77,34 @@ align-items: center; } +.keyword-suggestions-box { + display: flex; + flex-wrap: wrap; + margin: auto; + align-self: center; + width: 90%; + border: 1px solid black; + border-radius: 2px; + margin-top: 8px; +} + +.keyword-suggestion { + cursor: pointer; + padding: 1px 1px; + margin: 2px 2px; + background-color: lightblue; + border: 1px solid black; + border-radius: 5px; + white-space: nowrap; + display: flex; + align-items: center; +} + .keyword-editing-box { margin-top: 8px; } .keyword-input-box { - // display: flex; - // align-items: center; - // align-content: center; margin: auto; align-self: center; width: 90%; -- cgit v1.2.3-70-g09d2