aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/KeywordBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/KeywordBox.tsx')
-rw-r--r--src/client/views/KeywordBox.tsx316
1 files changed, 0 insertions, 316 deletions
diff --git a/src/client/views/KeywordBox.tsx b/src/client/views/KeywordBox.tsx
deleted file mode 100644
index 841c5394b..000000000
--- a/src/client/views/KeywordBox.tsx
+++ /dev/null
@@ -1,316 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, Colors, IconButton } from 'browndash-components';
-import { action, computed, makeObservable, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import React from 'react';
-import ResizeObserver from 'resize-observer-polyfill';
-import { returnFalse, setupMoveUpEvents } from '../../ClientUtils';
-import { Utils, emptyFunction } from '../../Utils';
-import { Doc, DocListCast, StrListCast } from '../../fields/Doc';
-import { DocData, KeywordsHeight } from '../../fields/DocSymbols';
-import { List } from '../../fields/List';
-import { NumCast, StrCast } from '../../fields/Types';
-import { DocumentType } from '../documents/DocumentTypes';
-import { DragManager } from '../util/DragManager';
-import { SnappingManager } from '../util/SnappingManager';
-import { undoable } from '../util/UndoManager';
-import { ObservableReactComponent } from './ObservableReactComponent';
-import { DocumentView } from './nodes/DocumentView';
-import { FontIconBox } from './nodes/FontIconBox/FontIconBox';
-
-interface KeywordItemProps {
- doc: Doc;
- keyword: string;
- keywordDoc?: Doc;
- setToEditing: () => void;
- isEditing: boolean;
-}
-
-/**
- * A component that handles individual keywords.
- */
-@observer
-export class KeywordItem extends ObservableReactComponent<KeywordItemProps> {
- /**
- * 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);
-
- /**
- * 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<Doc>();
- newKeywordCol[DocData].docs = new List<Doc>();
- // 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>();
- Doc.AddDocToList(Doc.ActiveDashboard, 'myKeywordCollections', newKeywordCol);
- }
-
- return newKeywordCol;
- };
- /**
- * Gets all Docs that have the specified keyword
- * @param keyword keyword string
- * @returns An array of documents that contain the keyword.
- */
- 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);
- }
- }
- }
-
- if (!doc[DocData].tags) doc[DocData].tags = new List<string>();
- const tagList = doc[DocData].tags as List<string>;
- if (!tagList.includes(keyword)) tagList.push(keyword);
- };
-
- public static RemoveLabel = (doc: Doc, keyword: string, keywordDoc?: Doc) => {
- if (doc[DocData].tags) {
- if (doc.type === DocumentType.COL) {
- keywordDoc && Doc.RemoveDocFromList(keywordDoc[DocData], 'collections', doc);
-
- for (const cur_doc of KeywordItem.allDocsWithKeyword(keyword)) {
- doc[DocData].data = new List<Doc>(DocListCast(doc[DocData].data).filter(d => !Doc.AreProtosEqual(cur_doc, d)));
- }
- } else {
- keywordDoc && Doc.RemoveDocFromList(keywordDoc[DocData], 'docs', doc);
-
- for (const collection of DocListCast(keywordDoc?.collections)) {
- collection[DocData].data = new List<Doc>(DocListCast(collection[DocData].data).filter(d => !Doc.AreProtosEqual(doc, d)));
- }
- }
- }
- doc[DocData].tags = new List<string>((doc[DocData].tags as List<string>).filter(label => label !== keyword));
- };
-
- private _ref: React.RefObject<HTMLDivElement>;
-
- 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 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<Doc>(newEmbeddings);
- docData.title = this._props.keyword;
- docData.tags = new List<string>([this._props.keyword]);
- docData.showLabels = true;
- docData.freeform_fitContentsToBox = 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 => Doc.SetContainer(embed, newCollection));
-
- // Add the collection to the keyword document's list of associated smart collections.
- this._props.keywordDoc && Doc.AddDocToList(this._props.keywordDoc, 'collections', newCollection);
- return newCollection;
- };
-
- @action
- handleDragStart = (e: React.PointerEvent) => {
- 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();
- };
-
- render() {
- setTimeout(() => KeywordItem.addLabelToDoc(this._props.doc, this._props.keyword)); // bcz: hack to make sure that Docs are added to their keyword Doc collection since metadata can get set anywhere without a guard triggering an add to the collection
- const keyword = this._props.keyword.replace(/^#/, '');
- const metadata = keyword.startsWith('@') ? keyword.replace(/^@/, '') : '';
- return (
- <div className="keyword" onClick={this._props.setToEditing} onPointerDown={this.handleDragStart} ref={this._ref} key={Utils.GenerateGuid()}>
- {metadata ? (
- <span>
- <b style={{ fontSize: 'smaller' }}>{keyword}&nbsp;</b>
- {this._props.doc[metadata] as string}
- </span>
- ) : (
- keyword
- )}
- {this.props.isEditing && (
- <IconButton
- tooltip="Remove label"
- onPointerDown={undoable(() => KeywordItem.RemoveLabel(this._props.doc, this._props.keyword, this._props.keywordDoc), `remove label ${this._props.keyword}`)}
- icon={<FontAwesomeIcon icon="times" size="sm" />}
- style={{ width: '8px', height: '8px', marginLeft: '10px' }}
- />
- )}
- </div>
- );
- }
-}
-
-interface KeywordBoxProps {
- View: DocumentView;
-}
-
-/**
- * A component that handles the keyword display for documents.
- */
-@observer
-export class KeywordBox extends ObservableReactComponent<KeywordBoxProps> {
- private _ref: React.RefObject<HTMLDivElement>;
-
- constructor(props: any) {
- super(props);
- makeObservable(this);
- this._ref = React.createRef();
- }
-
- @observable _currentInput = '';
- @observable _isEditing = !StrListCast(this._props.View.dataDoc.tags).length;
-
- @computed get currentScale() {
- return NumCast((this._props.View.Document.embedContainer as Doc)?._freeform_scale, 1);
- }
- @computed get isEditing() {
- return this._isEditing && DocumentView.SelectedDocs().includes(this._props.View.Document);
- }
-
- @action
- setToEditing = (editing = true) => {
- this._isEditing = editing;
- editing && this._props.View.select(false);
- };
-
- /**
- * Adds the keyword to the document.
- * @param keyword
- */
- submitLabel = undoable((keyword: string) => {
- const submittedLabel = keyword.trim();
- submittedLabel && KeywordItem.addLabelToDoc(this._props.View.Document, '#' + submittedLabel.replace(/^#/, ''));
- this._currentInput = ''; // Clear the input box
- }, 'added doc label');
-
- render() {
- const keywordsList = StrListCast(this._props.View.dataDoc.tags);
-
- return !this._props.View.Document.showLabels ? null : (
- <div
- className="keywords-container"
- ref={r => r && new ResizeObserver(action(() => (this._props.View.Document[KeywordsHeight] = r?.getBoundingClientRect().height ?? 0))).observe(r)}
- style={{
- transformOrigin: 'top left',
- maxWidth: `${100 * this.currentScale}%`,
- width: 'max-content',
- transform: `scale(${1 / this.currentScale})`,
- backgroundColor: this.isEditing ? Colors.LIGHT_GRAY : Colors.TRANSPARENT,
- borderColor: this.isEditing ? Colors.BLACK : Colors.TRANSPARENT,
- }}>
- <div className="keywords-content" style={{ width: '100%' }}>
- <div className="keywords-list">
- {!keywordsList.length ? null : ( //
- <IconButton style={{ width: '8px' }} tooltip="Close Menu" onPointerDown={() => this.setToEditing(!this._isEditing)} icon={<FontAwesomeIcon icon={this._isEditing ? 'chevron-up' : 'chevron-down'} size="sm" />} />
- )}
- {keywordsList.map(keyword => (
- <KeywordItem key={keyword} doc={this._props.View.Document} keyword={keyword} keywordDoc={KeywordItem.findKeywordCollectionDoc(keyword)} setToEditing={this.setToEditing} isEditing={this.isEditing} />
- ))}
- </div>
- {this.isEditing ? (
- <div className="keyword-editing-box">
- <div className="keyword-input-box">
- <input
- value={this._currentInput}
- autoComplete="off"
- onChange={action(e => (this._currentInput = e.target.value))}
- onKeyDown={e => {
- 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' }}
- />
- </div>
- <div className="keyword-suggestions-box">
- {KeywordItem.AllKeywordCollections.map(doc => {
- const keyword = StrCast(doc.title);
- return (
- <Button
- style={{ margin: '2px 2px', border: '1px solid black', backgroundColor: 'lightblue', color: 'black' }}
- text={keyword}
- color={SnappingManager.userVariantColor}
- tooltip={'Add existing keyword'}
- onClick={() => this.submitLabel(keyword)}
- key={Utils.GenerateGuid()}
- />
- );
- })}
- </div>
- </div>
- ) : null}
- </div>
- </div>
- );
- }
-}