diff options
-rw-r--r-- | src/ClientUtils.ts | 5 | ||||
-rw-r--r-- | src/client/util/LinkManager.ts | 2 | ||||
-rw-r--r-- | src/client/util/Scripting.ts | 8 | ||||
-rw-r--r-- | src/client/util/SelectionManager.ts | 1 | ||||
-rw-r--r-- | src/client/views/FieldsDropdown.tsx | 4 | ||||
-rw-r--r-- | src/client/views/PropertiesView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/CollectionSchemaView.tsx | 327 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx | 264 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/SchemaRowBox.tsx | 48 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/SchemaTableCell.tsx | 82 | ||||
-rw-r--r-- | src/client/views/global/globalScripts.ts | 1 | ||||
-rw-r--r-- | src/client/views/nodes/DataVizBox/DataVizBox.tsx | 177 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentIcon.tsx | 10 | ||||
-rw-r--r-- | src/client/views/nodes/FieldView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/LabelBox.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 |
16 files changed, 485 insertions, 454 deletions
diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts index 8ecbc55bf..3066499d8 100644 --- a/src/ClientUtils.ts +++ b/src/ClientUtils.ts @@ -317,7 +317,7 @@ export namespace ClientUtils { } export function lightenRGB(rVal: number, gVal: number, bVal: number, percent: number): [number, number, number] { - const amount = 1 + percent/100; + const amount = 1 + percent / 100; const r = rVal * amount; const g = gVal * amount; const b = bVal * amount; @@ -347,8 +347,6 @@ export namespace ClientUtils { return undefined; } - - export function GetClipboardText(): string { const textArea = document.createElement('textarea'); document.body.appendChild(textArea); @@ -725,7 +723,6 @@ export function UpdateIcon( const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; newDiv.style.width = width.toString(); newDiv.style.height = height.toString(); - console.log('width: ' + newDiv.style.width) replaceCanvases(docViewContent, newDiv); const htmlString = new XMLSerializer().serializeToString(newDiv); const nativeWidth = width; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index bbf8d9c11..e11482572 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -260,7 +260,7 @@ export function UPDATE_SERVER_CACHE() { Doc.MyDockedBtns.linearView_IsOpen && console.log('Set cached docs = '); const isFiltered = filtered.filter(doc => !Doc.IsSystem(doc)); const strings = isFiltered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)')); - //Doc.MyDockedBtns.linearView_IsOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str)); + Doc.MyDockedBtns.linearView_IsOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str)); rp.post(ClientUtils.prepend('/setCacheDocumentIds'), { body: { diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 3ba3ff4b5..b1db0bf39 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -81,6 +81,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: ts if (!options.editable) { batch = Doc.MakeReadOnly(); } + const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray); batch?.end(); return { success: true, result }; @@ -177,13 +178,8 @@ function forEachNode(node: ts.Node, onEnter: Traverser, onExit?: Traverser, inde ); } -// ScriptField.CompileScript(value, {}, true, undefined, DocumentIconContainer.getTransformer()); -// //addreturn = true -// //capturedvariables = undefined -// // - export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult { - const captured = options.capturedVariables ?? {}; + const captured = options.capturedVariables ?? {}; const signature = Object.keys(captured).reduce((p, v) => { const formatCapture = (obj: FieldType | undefined) => `${v}=${obj instanceof RefField ? 'XXX' : obj?.toString()}`; const captureVal = captured[v]; diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index c39d8ebc7..1ab84421c 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -58,7 +58,6 @@ export class SelectionManager { }); public static DeselectAll = (except?: Doc): void => { - const found = this.Instance.SelectedViews.find(dv => dv.Document === except); runInAction(() => { if (LinkManager.Instance) { diff --git a/src/client/views/FieldsDropdown.tsx b/src/client/views/FieldsDropdown.tsx index 176ac96b6..407031b40 100644 --- a/src/client/views/FieldsDropdown.tsx +++ b/src/client/views/FieldsDropdown.tsx @@ -34,7 +34,7 @@ export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps makeObservable(this); } - @computed get allDescendantDocs() { //!!! + @computed get allDescendantDocs() { const allDocs = new Set<Doc>(); SearchUtil.foreachRecursiveDoc([this._props.Document], (depth, doc) => allDocs.add(doc)); return Array.from(allDocs); @@ -57,7 +57,7 @@ export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps const filteredOptions = ['author', ...(this._newField ? [this._newField] : []), ...(this._props.addedFields ?? []), ...this.fieldsOfDocuments.filter(facet => facet[0] === facet.charAt(0).toUpperCase())]; Object.entries(DocOptions) - .filter(opts => opts[1].filterable) //!!! + .filter(opts => opts[1].filterable) .forEach((pair: [string, FInfo]) => filteredOptions.push(pair[0])); const options = filteredOptions.sort().map(facet => ({ value: facet, label: facet })); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 69c46052e..c6dccb4fb 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -884,7 +884,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps @computed get borderColor() { const doc = this.selectedDoc; const layoutDoc = doc ? Doc.Layout(doc) : doc; - return StrCast(layoutDoc); + return StrCast(layoutDoc.color); } set borderColor(value) { this.selectedDoc && (this.selectedDoc[DocData].color = value || undefined); } // prettier-ignore diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 0076caaf8..aef97e723 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,12 +1,11 @@ -/* eslint-disable no-restricted-syntax */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { IconButton, Popup, PopupTrigger, Size, Type } from 'browndash-components'; -import { IReactionDisposer, ObservableMap, action, autorun, computed, makeObservable, observable, observe, override, reaction, runInAction } from 'mobx'; +import { IconButton, Size } from 'browndash-components'; +import { IReactionDisposer, Lambda, ObservableMap, action, computed, makeObservable, observable, observe, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { ClientUtils, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../ClientUtils'; import { emptyFunction } from '../../../../Utils'; -import { Doc, DocListCast, Field, FieldType, IdToDoc, NumListCast, Opt, StrListCast } from '../../../../fields/Doc'; +import { Doc, DocListCast, Field, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc'; import { DocData } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; @@ -14,10 +13,13 @@ import { ColumnType } from '../../../../fields/SchemaHeaderField'; import { BoolCast, NumCast, StrCast } from '../../../../fields/Types'; import { DocUtils } from '../../../documents/DocUtils'; import { Docs, DocumentOptions, FInfo } from '../../../documents/Documents'; +import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; +import { SnappingManager } from '../../../util/SnappingManager'; import { undoBatch, undoable } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; +import { ContextMenuProps } from '../../ContextMenuItem'; import { EditableView } from '../../EditableView'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { StyleProp } from '../../StyleProp'; @@ -28,25 +30,23 @@ import { FieldViewProps } from '../../nodes/FieldView'; import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'; import './CollectionSchemaView.scss'; +import { SchemaCellField } from './SchemaCellField'; import { SchemaColumnHeader } from './SchemaColumnHeader'; import { SchemaRowBox } from './SchemaRowBox'; -import { ContextMenuProps } from '../../ContextMenuItem'; -import { DocumentManager } from '../../../util/DocumentManager'; -import { SchemaCellField } from './SchemaCellField'; -import { SnappingManager } from '../../../util/SnappingManager'; /** * The schema view offers a spreadsheet-like interface for users to interact with documents. Within the schema, - * each doc is represented by its own row. Each column represents a field, for example the author or title fields. + * each doc is represented by its own row. Each column represents a field, for example the author or title fields. * Users can apply varoius filters and sorts to columns to change what is displayed. The schemaview supports equations for * cell linking. - * + * * This class supports the main functionality for choosing which docs to render in the view, applying visual - * updates to rows and columns (such as user dragging or sort-related highlighting), applying edits to multiple cells + * updates to rows and columns (such as user dragging or sort-related highlighting), applying edits to multiple cells * at once, and applying filters and sorts to columns. It contains SchemaRowBoxes (which themselves contain SchemaTableCells, * and SchemaCellFields) and SchemaColumnHeaders. */ +// eslint-disable-next-line @typescript-eslint/no-require-imports const { SCHEMA_NEW_NODE_HEIGHT } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore export const FInfotoColType: { [key: string]: ColumnType } = { @@ -63,7 +63,7 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'author_date', ' @observer export class CollectionSchemaView extends CollectionSubView() { - private _keysDisposer: any; + private _keysDisposer?: Lambda; private _disposers: { [name: string]: IReactionDisposer } = {}; private _previewRef: HTMLDivElement | null = null; private _makeNewColumn: boolean = false; @@ -71,13 +71,14 @@ export class CollectionSchemaView extends CollectionSubView() { private _tableContentRef: HTMLDivElement | null = null; private _menuTarget = React.createRef<HTMLDivElement>(); private _headerRefs: SchemaColumnHeader[] = []; - private _eqHighlightColors: Array<[{r: number, g: number, b: number}, {r: number, g: number, b: number}]> = []; + private _eqHighlightColors: Array<[{ r: number; g: number; b: number }, { r: number; g: number; b: number }]> = []; + private _oldWheel: HTMLDivElement | null = null; constructor(props: SubCollectionViewProps) { super(props); makeObservable(this); const lightenedColor = (r: number, g: number, b:number) => { const lightened = ClientUtils.lightenRGB(r, g, b, 165); return {r: lightened[0], g: lightened[1], b: lightened[2]}} // prettier-ignore - const colors = (r: number, g: number, b: number): [any, any] => {return [{r: r, g: g, b: b}, lightenedColor(r, g, b)]} // prettier-ignore + const colors = (r: number, g: number, b: number):[{r:number,g:number,b:number},{r:number,g:number,b:number}] => ([{r, g, b}, lightenedColor(r, g, b)]); // prettier-ignore this._eqHighlightColors.push(colors(70, 150, 50)); this._eqHighlightColors.push(colors(180, 70, 20)); this._eqHighlightColors.push(colors(70, 50, 150)); @@ -122,8 +123,8 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _highlightedCellsInfo: Array<[doc: Doc, field: string]> = []; @observable _cellHighlightColors: ObservableMap = new ObservableMap<string, string[]>(); @observable _containedDocs: Doc[] = []; //all direct children of the schema - @observable _referenceSelectMode: {enabled: boolean, currEditing: SchemaCellField | undefined} = {enabled: false, currEditing: undefined} - + @observable _referenceSelectMode: { enabled: boolean; currEditing: SchemaCellField | undefined } = { enabled: false, currEditing: undefined }; + // target HTMLelement portal for showing a popup menu to edit cell values. public get MenuTarget() { return this._menuTarget.current; @@ -217,15 +218,17 @@ export class CollectionSchemaView extends CollectionSubView() { true ); this._disposers.docdata = reaction( - () => DocListCast(this.dataDoc[this.fieldKey]), - (docs) => this._containedDocs = docs, - {fireImmediately: true} - ) + () => DocListCast(this.dataDoc[this.fieldKey]), + docs => (this._containedDocs = docs), + { fireImmediately: true } + ); this._disposers.sortHighlight = reaction( - () => [this.sortField, this._containedDocs, this._selectedDocs, this._highlightedCellsInfo], - () => {this.sortField && setTimeout(() => this.highlightSortedColumn())}, - {fireImmediately: true} - ) + () => [this.sortField, this._containedDocs, this._selectedDocs, this._highlightedCellsInfo], + () => { + this.sortField && setTimeout(() => this.highlightSortedColumn()); + }, + { fireImmediately: true } + ); } componentWillUnmount() { @@ -239,8 +242,8 @@ export class CollectionSchemaView extends CollectionSubView() { removeDoc = (doc: Doc) => { this.removeDocument(doc); - this._containedDocs = this._containedDocs.filter(d => d !== doc) - } + this._containedDocs = this._containedDocs.filter(d => d !== doc); + }; rowIndex = (doc: Doc) => this.docsWithDrag.docs.indexOf(doc); @@ -301,7 +304,9 @@ export class CollectionSchemaView extends CollectionSubView() { } break; case 'Backspace': { - undoable(() => {this._selectedDocs.forEach(d => this._containedDocs.includes(d) && this.removeDoc(d));}, 'delete schema row'); + undoable(() => { + this._selectedDocs.forEach(d => this._containedDocs.includes(d) && this.removeDoc(d)); + }, 'delete schema row'); break; } case 'Escape': { @@ -319,7 +324,7 @@ export class CollectionSchemaView extends CollectionSubView() { addRow = (doc: Doc | Doc[]) => this.addDocument(doc); @undoBatch - changeColumnKey = (index: number, newKey: string, defaultVal?: any) => { + changeColumnKey = (index: number, newKey: string, defaultVal?: FieldType) => { if (!this.documentKeys.includes(newKey)) this.addNewKey(newKey, defaultVal); const currKeys = this.columnKeys.slice(); // copy the column key array first, then change it. @@ -328,9 +333,10 @@ export class CollectionSchemaView extends CollectionSubView() { }; @undoBatch - addColumn = (index: number = 0, key?: string, defaultVal?: any) => { + addColumn = (index: number = 0, keyIn?: string, defaultVal?: FieldType) => { + let key = keyIn; if (key && !this.documentKeys.includes(key)) this.addNewKey(key, defaultVal); - + const newColWidth = this.tableWidth / (this.storedColumnWidths.length + 1); const currWidths = this.storedColumnWidths.slice(); currWidths.splice(index, 0, newColWidth); @@ -345,11 +351,11 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - addNewKey = (key: string, defaultVal: any) => { + addNewKey = (key: string, defaultVal: FieldType | undefined) => { this.childDocs.forEach(doc => { doc[DocData][key] = defaultVal; }); - } + }; @undoBatch removeColumn = (index: number) => { @@ -365,13 +371,13 @@ export class CollectionSchemaView extends CollectionSubView() { const currKeys = this.columnKeys.slice(); currKeys.splice(index, 1); - this.layoutDoc.schema_columnKeys = new List<string>(currKeys); + this.layoutDoc.schema_columnKeys = new List<string>(currKeys); this._colEles.splice(index, 1); }; @action - startResize = (e: any, index: number, rightSide: boolean) => { + startResize = (e: React.PointerEvent, index: number, rightSide: boolean) => { this._displayColumnWidths = this.storedColumnWidths; setupMoveUpEvents(this, e, moveEv => this.resizeColumn(moveEv, index, rightSide), this.finishResize, emptyFunction); }; @@ -384,8 +390,8 @@ export class CollectionSchemaView extends CollectionSubView() { let change = e.movementX; - if (rightSide && (index !== this._displayColumnWidths.length - 1)) { - growing = change < 0 ? index + 1: index; + if (rightSide && index !== this._displayColumnWidths.length - 1) { + growing = change < 0 ? index + 1 : index; shrinking = change < 0 ? index : index + 1; } else if (index !== 0) { growing = change < 0 ? index : index - 1; @@ -432,7 +438,7 @@ export class CollectionSchemaView extends CollectionSubView() { this.closeNewColumnMenu(); this._headerRefs.forEach(ref => ref.toggleEditing(false)); this._draggedColIndex = index; - this.setColDrag(true); + this.setColDrag(true); const dragData = new DragManager.ColumnDragData(index); const dragEles = [this._colEles[index]]; this.childDocs.forEach(doc => dragEles.push(this._rowEles.get(doc).children[1].children[index])); @@ -446,7 +452,7 @@ export class CollectionSchemaView extends CollectionSubView() { * @returns column index */ findColDropIndex = (mouseX: number) => { - const xOffset: number = this._props.ScreenToLocalTransform().inverse().transformPoint(0,0)[0] + CollectionSchemaView._rowMenuWidth; + const xOffset: number = this._props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0] + CollectionSchemaView._rowMenuWidth; let index: number | undefined; this.displayColumnWidths.reduce((total, curr, i) => { if (total <= mouseX && total + curr >= mouseX) { @@ -507,12 +513,7 @@ export class CollectionSchemaView extends CollectionSubView() { this._colEles.forEach((colRef, i) => { const edgeStyle = i === index ? `solid 2px ${Colors.MEDIUM_BLUE}` : ''; const sorted = i === this.columnKeys.indexOf(this.sortField); - const cellEles = [ - colRef, - ...this.docsWithDrag.docs - .filter(doc => (i !== this._selectedCol || !this._selectedDocs.includes(doc)) && !sorted) - .map(doc => this._rowEles.get(doc).children[1].children[i]), - ]; + const cellEles = [colRef, ...this.docsWithDrag.docs.filter(doc => (i !== this._selectedCol || !this._selectedDocs.includes(doc)) && !sorted).map(doc => this._rowEles.get(doc).children[1].children[i])]; cellEles.forEach(ele => { if (sorted || this.highlightedCells.includes(ele)) return; ele.style.borderTop = ele === cellEles[0] ? edgeStyle : ''; @@ -533,14 +534,14 @@ export class CollectionSchemaView extends CollectionSubView() { this.childDocs.forEach(doc => { const cell = this._rowEles.get(doc).children[1].children[i]; - if (!(this._selectedDocs.includes(doc) && i === this._selectedCol) && !(this.highlightedCells.includes(cell)) && cell) { + if (!(this._selectedDocs.includes(doc) && i === this._selectedCol) && !this.highlightedCells.includes(cell) && cell) { cell.style.borderLeft = ''; cell.style.borderRight = ''; cell.style.borderBottom = ''; } }); }); - } + }; /** * Applies a gradient highlight to a sorted column. The direction of the gradient depends @@ -552,10 +553,10 @@ export class CollectionSchemaView extends CollectionSubView() { let index = -1; const highlightColors: string[] = []; const rowCount: number = this._containedDocs.length + 1; - if (field || this.sortField){ + if (field || this.sortField) { index = this.columnKeys.indexOf(field || this.sortField); - const increment: number = 110/rowCount; - for (let i = 1; i <= rowCount; ++i){ + const increment: number = 110 / rowCount; + for (let i = 1; i <= rowCount; ++i) { const adjColor = ClientUtils.lightenRGB(16, 66, 230, increment * i); highlightColors.push(`solid 2px rgb(${adjColor[0]}, ${adjColor[1]}, ${adjColor[2]})`); } @@ -564,25 +565,20 @@ export class CollectionSchemaView extends CollectionSubView() { this._colEles.forEach((colRef, i) => { const highlight: boolean = i === index; const desc: boolean = descending || this.sortDesc; - const cellEles = [ - colRef, - ...this.docsWithDrag.docs - .filter(doc => (i !== this._selectedCol || !this._selectedDocs.includes(doc))) - .map(doc => this._rowEles.get(doc).children[1].children[i]), - ]; + const cellEles = [colRef, ...this.docsWithDrag.docs.filter(doc => i !== this._selectedCol || !this._selectedDocs.includes(doc)).map(doc => this._rowEles.get(doc).children[1].children[i])]; const cellCount = cellEles.length; - for (let ele = 0; ele < cellCount; ++ele){ + for (let ele = 0; ele < cellCount; ++ele) { const currCell = cellEles[ele]; if (this.highlightedCells.includes(currCell)) continue; - const style = highlight ? desc ? `${highlightColors[cellCount - 1 - ele]}` : `${highlightColors[ele]}` : ''; + const style = highlight ? (desc ? `${highlightColors[cellCount - 1 - ele]}` : `${highlightColors[ele]}`) : ''; currCell.style.borderLeft = style; currCell.style.borderRight = style; } - cellEles[0].style.borderTop = highlight ? desc ? `${highlightColors[cellCount - 1]}` : `${highlightColors[0]}` : ''; - if (!(this._selectedDocs.includes(this.docsWithDrag.docs[this.docsWithDrag.docs.length - 1]) && this._selectedCol === index) && !this.highlightedCells.includes(cellEles[cellCount - 1])) cellEles[cellCount - 1].style.borderBottom = highlight ? desc ? `${highlightColors[0]}` : `${highlightColors[cellCount - 1]}` : ''; + cellEles[0].style.borderTop = highlight ? (desc ? `${highlightColors[cellCount - 1]}` : `${highlightColors[0]}`) : ''; + if (!(this._selectedDocs.includes(this.docsWithDrag.docs[this.docsWithDrag.docs.length - 1]) && this._selectedCol === index) && !this.highlightedCells.includes(cellEles[cellCount - 1])) + cellEles[cellCount - 1].style.borderBottom = highlight ? (desc ? `${highlightColors[0]}` : `${highlightColors[cellCount - 1]}`) : ''; }); - - } + }; /** * Gets the html element representing a cell so that styles can be applied on it. @@ -594,35 +590,40 @@ export class CollectionSchemaView extends CollectionSubView() { const index = this.columnKeys.indexOf(fieldKey); const cell = this._rowEles.get(doc).children[1].children[index]; return cell; - } + }; /** * Given text in a cell, find references to other cells (for equations). * @param text the text in the cell - * @returns the html cell elements referenced in the text. + * @returns the html cell elements referenced in the text. */ findCellRefs = (text: string) => { const pattern = /(this|d(\d+))\.(\w+)/g; - interface Match { docRef: string; field: string; } + interface Match { + docRef: string; + field: string; + } const matches: Match[] = []; let match: RegExpExecArray | null; while ((match = pattern.exec(text)) !== null) { - const docRef = match[1] === 'this' ? match[1] : match[2]; + const docRef = match[1] === 'this' ? match[1] : match[2]; matches.push({ docRef, field: match[3] }); } - const cells: Array<any> = []; - matches.forEach((match: Match) => { - const {docRef, field} = match; + const cells: [Doc, string][] = []; + matches.forEach((m: Match) => { + const { docRef, field } = m; const docView = DocumentManager.Instance.DocumentViews[Number(docRef)]; const doc = docView?.Document ?? undefined; - if (this.columnKeys.includes(field) && this._containedDocs.includes(doc)) {cells.push([doc, field])} - }) + if (this.columnKeys.includes(field) && this._containedDocs.includes(doc)) { + cells.push([doc, field]); + } + }); return cells; - } + }; /** * Determines whether the rows above or below a given row have been @@ -636,7 +637,7 @@ export class CollectionSchemaView extends CollectionSubView() { const selectedBelow: boolean = this._selectedDocs.includes(docs[index + 1]); const selectedAbove: boolean = this._selectedDocs.includes(docs[index - 1]); return [selectedAbove, selectedBelow]; - } + }; @action removeCellHighlights = () => { @@ -649,9 +650,10 @@ export class CollectionSchemaView extends CollectionSubView() { if (this.selectionOverlap(doc)[0]) cell.style.borderTop = ''; if (this.selectionOverlap(doc)[1]) cell.style.borderBottom = ''; } else cell.style.border = ''; - cell.style.backgroundColor = '';}); + cell.style.backgroundColor = ''; + }); this._highlightedCellsInfo = []; - } + }; restoreCellHighlights = () => { this._highlightedCellsInfo.forEach(info => { @@ -665,10 +667,10 @@ export class CollectionSchemaView extends CollectionSubView() { cell.style.borderRight = color; cell.style.borderBottom = color; }); - } + }; /** - * Highlights cells based on equation text in the cell currently being edited. + * Highlights cells based on equation text in the cell currently being edited. * Does not highlight selected cells (that's done directly in SchemaTableCell). * @param text the equation */ @@ -682,7 +684,7 @@ export class CollectionSchemaView extends CollectionSubView() { const info = this._highlightedCellsInfo[i]; const color = this._eqHighlightColors[i % 10]; const colorStrings = [`solid 2px rgb(${color[0].r}, ${color[0].g}, ${color[0].b})`, `rgb(${color[1].r}, ${color[1].g}, ${color[1].b})`]; - const doc = info[0]; + const doc = info[0]; const field = info[1]; const key = `${doc[Id]}_${field}`; const cell = this.getCellElement(doc, field); @@ -690,7 +692,7 @@ export class CollectionSchemaView extends CollectionSubView() { cell.style.border = colorStrings[0]; cell.style.backgroundColor = colorStrings[1]; } - } + }; //Used in SchemaRowBox @action @@ -718,7 +720,6 @@ export class CollectionSchemaView extends CollectionSubView() { this.deselectAllCells(); }; - selectRow = (doc: Doc, lastSelected: Doc) => { const index = this.rowIndex(doc); const lastSelectedRow = this.rowIndex(lastSelected); @@ -737,12 +738,12 @@ export class CollectionSchemaView extends CollectionSubView() { if (!doc) return; const docIndex = DocumentView.getDocViewIndex(doc); const field = this.columnKeys[col]; - const refToAdd = `d${docIndex}.${field}` - const editedField = this._referenceSelectMode.currEditing ? this._referenceSelectMode.currEditing as SchemaCellField : null; + const refToAdd = `d${docIndex}.${field}`; + const editedField = this._referenceSelectMode.currEditing ? (this._referenceSelectMode.currEditing as SchemaCellField) : null; editedField?.insertText(refToAdd, true); editedField?.setupRefSelect(false); return; - } + }; @action selectCell = (doc: Doc, col: number, shiftKey: boolean, ctrlKey: boolean) => { @@ -787,7 +788,9 @@ export class CollectionSchemaView extends CollectionSubView() { @action onInternalDrop = (e: Event, de: DragManager.DropEvent) => { if (de.complete.columnDragData) { - setTimeout(() => {this.setColDrag(false);}); + setTimeout(() => { + this.setColDrag(false); + }); e.stopPropagation(); return true; } @@ -849,9 +852,9 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - setKey = (key: string, defaultVal?: any, index?: number) => { + setKey = (key: string, defaultVal?: string, index?: number) => { if (this.columnKeys.includes(key)) return; - + if (this._makeNewColumn) { this.addColumn(this.columnKeys.indexOf(key), key, defaultVal); this._makeNewColumn = false; @@ -861,14 +864,14 @@ export class CollectionSchemaView extends CollectionSubView() { }; /** - * Used in SchemaRowBox to set - * @param key - * @param value - * @returns + * Used in SchemaRowBox to set + * @param key + * @param value + * @returns */ setCellValues = (key: string, value: string) => { - if (this._selectedCells.length === 1) this.docs.forEach(doc => !doc._lockedSchemaEditing && Doc.SetField(doc, key, value)); - else this._selectedCells.forEach(doc => !doc._lockedSchemaEditing && Doc.SetField(doc, key, value)); + if (this._selectedCells.length === 1) this.docs.forEach(doc => !doc._lockedSchemaEditing && Doc.SetField(doc, key, value)); + else this._selectedCells.forEach(doc => !doc._lockedSchemaEditing && Doc.SetField(doc, key, value)); return true; }; @@ -912,34 +915,36 @@ export class CollectionSchemaView extends CollectionSubView() { const cm = ContextMenu.Instance; cm.clearItems(); - const fieldSortedAsc = (this.sortField === this.columnKeys[index] && !this.sortDesc); - const fieldSortedDesc = (this.sortField === this.columnKeys[index] && this.sortDesc); - const revealOptions = cm.findByDescription('Sort column') - const sortOptions: ContextMenuProps[] = revealOptions && revealOptions && 'subitems' in revealOptions ? revealOptions.subitems ?? [] : []; + const fieldSortedAsc = this.sortField === this.columnKeys[index] && !this.sortDesc; + const fieldSortedDesc = this.sortField === this.columnKeys[index] && this.sortDesc; + const revealOptions = cm.findByDescription('Sort column'); + const sortOptions: ContextMenuProps[] = revealOptions && revealOptions && 'subitems' in revealOptions ? (revealOptions.subitems ?? []) : []; sortOptions.push({ - description: 'Sort A-Z', + description: 'Sort A-Z', event: () => { - this.setColumnSort(undefined); + this.setColumnSort(undefined); const field = this.columnKeys[index]; this._containedDocs = this.sortDocs(field, false); setTimeout(() => { this.highlightSortedColumn(field, false); - setTimeout(() => this.highlightSortedColumn(), 480); + setTimeout(() => this.highlightSortedColumn(), 480); }, 20); - }, - icon: 'arrow-down-a-z',}); + }, + icon: 'arrow-down-a-z', + }); sortOptions.push({ - description: 'Sort Z-A', + description: 'Sort Z-A', event: () => { - this.setColumnSort(undefined); + this.setColumnSort(undefined); const field = this.columnKeys[index]; this._containedDocs = this.sortDocs(field, true); setTimeout(() => { this.highlightSortedColumn(field, true); - setTimeout(() => this.highlightSortedColumn(), 480); + setTimeout(() => this.highlightSortedColumn(), 480); }, 20); - }, - icon: 'arrow-up-z-a'}); + }, + icon: 'arrow-up-z-a', + }); sortOptions.push({ description: 'Persistent Sort A-Z', event: () => { @@ -964,7 +969,7 @@ export class CollectionSchemaView extends CollectionSubView() { } }, icon: fieldSortedDesc ? 'lock' : 'lock-open'}); // prettier-ignore - + cm.addItem({ description: `Change field`, event: () => this.openNewColumnMenu(index, false), @@ -975,12 +980,12 @@ export class CollectionSchemaView extends CollectionSubView() { event: () => this.openFilterMenu(index), icon: 'filter', }); - cm.addItem({ - description: 'Sort column', - addDivider: false, - noexpand: true, - subitems: sortOptions, - icon: 'sort' + cm.addItem({ + description: 'Sort column', + addDivider: false, + noexpand: true, + subitems: sortOptions, + icon: 'sort', }); cm.addItem({ description: 'Add column to left', @@ -1068,7 +1073,7 @@ export class CollectionSchemaView extends CollectionSubView() { @computed get renderColumnMenu() { const x = this._columnMenuIndex! === -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth); return ( - <div className="schema-column-menu" style={{ left: x, maxWidth: `${Math.max(this._colEles[this._columnMenuIndex ?? 0].offsetWidth, 150)}px` }}> + <div className="schema-column-menu" style={{ left: x, maxWidth: `${Math.max(this._colEles[this._columnMenuIndex ?? 0].offsetWidth, 150)}px` }}> {this.keysDropdown} </div> ); @@ -1095,13 +1100,7 @@ export class CollectionSchemaView extends CollectionSubView() { } return ( <div key={key} className="schema-filter-option"> - <input - type="checkbox" - onPointerDown={e => e.stopPropagation()} - onClick={e => e.stopPropagation()} - onChange={e => Doc.setDocFilter(this.Document, columnKey, key, e.target.checked ? 'check' : 'remove')} - checked={bool} - /> + <input type="checkbox" onPointerDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()} onChange={e => Doc.setDocFilter(this.Document, columnKey, key, e.target.checked ? 'check' : 'remove')} checked={bool} /> <span style={{ paddingLeft: 4 }}>{key}</span> </div> ); @@ -1111,7 +1110,7 @@ export class CollectionSchemaView extends CollectionSubView() { @computed get renderFilterMenu() { const x = this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._filterColumnIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth); return ( - <div className="schema-filter-menu" style={{ left: x, maxWidth: `${Math.max(this._colEles[this._columnMenuIndex ?? 0].offsetWidth, 150)}px`}}> + <div className="schema-filter-menu" style={{ left: x, maxWidth: `${Math.max(this._colEles[this._columnMenuIndex ?? 0].offsetWidth, 150)}px` }}> <input className="schema-filter-input" type="text" value={this._filterSearchValue} onKeyDown={this.onFilterKeyDown} onChange={this.updateFilterSearch} onPointerDown={e => e.stopPropagation()} /> {this.renderFilterOptions} <div @@ -1129,13 +1128,13 @@ export class CollectionSchemaView extends CollectionSubView() { @action setColDrag = (beingDragged: boolean) => { this._colBeingDragged = beingDragged; !beingDragged && this.removeDragHighlight(); - } + }; @action updateMouseCoordinates = (e: React.PointerEvent<HTMLDivElement>) => { const prevX = this._mouseCoordinates.x; const prevY = this._mouseCoordinates.y; this._mouseCoordinates = { x: e.clientX, y: e.clientY, prevX: prevX, prevY: prevY }; - } + }; @action onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => { @@ -1158,9 +1157,9 @@ export class CollectionSchemaView extends CollectionSubView() { /** * Gets docs contained by collections within the schema. Currently defunct. - * @param doc - * @param displayed - * @returns + * @param doc + * @param displayed + * @returns */ // subCollectionDocs = (doc: Doc, displayed: boolean) => { // const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]); @@ -1203,8 +1202,7 @@ export class CollectionSchemaView extends CollectionSubView() { subDocs.forEach(t => { const docFieldKey = Doc.LayoutFieldKey(t); const isSubDocAnnotatable = t[docFieldKey] instanceof List && !(t[docFieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc)); - notFiltered = - notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !childFiltersByRanges.length) || DocUtils.FilterDocs([t], childDocFilters, childFiltersByRanges, d).length)); + notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !childFiltersByRanges.length) || DocUtils.FilterDocs([t], childDocFilters, childFiltersByRanges, d).length)); DocListCast(t[isSubDocAnnotatable ? docFieldKey + '_annotations' : docFieldKey]).forEach(newdoc => newarray.push(newdoc)); isSubDocAnnotatable && DocListCast(t[docFieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc)); }); @@ -1231,19 +1229,19 @@ export class CollectionSchemaView extends CollectionSubView() { // docsFromChildren = docsFromChildren.concat(docsNotAlreadyDisplayed); // }); - return this.filteredDocs;; + return this.filteredDocs; } /** - * Sorts docs first alphabetically and then numerically. + * Sorts docs first alphabetically and then numerically. * @param field the column being sorted * @param desc whether the sort is ascending or descending * @param persistent whether the sort is applied persistently or is one-shot - * @returns + * @returns */ sortDocs = (field: string, desc: boolean, persistent?: boolean) => { const numbers: Doc[] = []; - const strings: Doc[] = []; + const strings: Doc[] = []; this.docs.forEach(doc => { if (!isNaN(Number(Field.toString(doc[field] as FieldType)))) numbers.push(doc); @@ -1253,25 +1251,26 @@ export class CollectionSchemaView extends CollectionSubView() { const sortedNums = numbers.sort((numOne, numTwo) => { const numA = Number(Field.toString(numOne[field] as FieldType)); const numB = Number(Field.toString(numTwo[field] as FieldType)); - return desc? numA - numB : numB - numA; + return desc ? numA - numB : numB - numA; }); - const collator = new Intl.Collator(undefined, {sensitivity: 'base'}); + const collator = new Intl.Collator(undefined, { sensitivity: 'base' }); let sortedStrings; - if (!desc) {sortedStrings = strings.slice().sort((docA, docB) => collator.compare(Field.toString(docA[field] as FieldType), Field.toString(docB[field] as FieldType))); + if (!desc) { + sortedStrings = strings.slice().sort((docA, docB) => collator.compare(Field.toString(docA[field] as FieldType), Field.toString(docB[field] as FieldType))); } else sortedStrings = strings.slice().sort((docB, docA) => collator.compare(Field.toString(docA[field] as FieldType), Field.toString(docB[field] as FieldType))); const sortedDocs = desc ? sortedNums.concat(sortedStrings) : sortedStrings.concat(sortedNums); if (!persistent) this._containedDocs = sortedDocs; return sortedDocs; - } - + }; + /** * Returns all docs minus those currently being dragged by the user. */ @computed get docsWithDrag() { let docs = this.docs.slice(); - if (this.sortField){ + if (this.sortField) { const field = StrCast(this.layoutDoc.sortField); const desc = BoolCast(this.layoutDoc.sortDesc); // is this an ascending or descending sort docs = this.sortDocs(field, desc, true); @@ -1290,13 +1289,17 @@ export class CollectionSchemaView extends CollectionSubView() { previewWidthFunc = () => this.previewWidth; onPassiveWheel = (e: WheelEvent) => e.stopPropagation(); displayedDocsFunc = () => this.docsWithDrag.docs; - _oldWheel: any; render() { return ( - <div className="collectionSchemaView" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} - onDrop={this.onExternalDrop.bind(this)} - onPointerMove={e => this.onPointerMove(e)} - onPointerDown={() => {this.closeNewColumnMenu(); this.setColDrag(false)}}> + <div + className="collectionSchemaView" + ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} + onDrop={this.onExternalDrop.bind(this)} + onPointerMove={e => this.onPointerMove(e)} + onPointerDown={() => { + this.closeNewColumnMenu(); + this.setColDrag(false); + }}> <div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }} /> <div className="schema-table" @@ -1310,30 +1313,29 @@ export class CollectionSchemaView extends CollectionSubView() { <div className="schema-header-row" style={{ height: this.rowHeightFunc() }}> <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}> <IconButton - tooltip="Add a new key" - icon={ <FontAwesomeIcon icon="plus" size='lg'/>} - size={Size.XSMALL} - color={'black'} - onPointerDown={e => - setupMoveUpEvents( - this, - e, - returnFalse, - emptyFunction, - undoable(clickEv => { - clickEv.stopPropagation(); - this.addColumn() - }, 'add key to schema') - ) - } + tooltip="Add a new key" + icon={<FontAwesomeIcon icon="plus" size="lg" />} + size={Size.XSMALL} + color={'black'} + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + undoable(clickEv => { + clickEv.stopPropagation(); + this.addColumn(); + }, 'add key to schema') + ) + } /> </div> {this.columnKeys.map((key, index) => ( <SchemaColumnHeader - // eslint-disable-next-line react/no-array-index-key //cleanupField={this.cleanupComputedField} ref={r => r && this._headerRefs.push(r)} - keysDropdown={(this.keysDropdown)} + keysDropdown={this.keysDropdown} schemaView={this} columnWidth={() => CollectionSchemaView._minColWidth} //TODO: update Document={this.Document} @@ -1445,7 +1447,6 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV return ( <DocumentView key={this._props.doc[Id]} - // eslint-disable-next-line react/jsx-props-no-spreading {...this._props.schema._props} containerViewPath={this._props.schema.childContainerViewPath} LayoutTemplate={this._props.schema._props.childLayoutTemplate} @@ -1498,4 +1499,4 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP </div> ); } -}
\ No newline at end of file +} diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx index c5cdac8af..47918f9c1 100644 --- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx +++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/no-unused-prop-types */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -21,7 +20,8 @@ import { undoable } from '../../../util/UndoManager'; import { IconButton, Size } from 'browndash-components'; export enum SchemaFieldType { - Header, Cell + Header, + Cell, } export interface SchemaColumnHeaderProps { @@ -49,7 +49,6 @@ export interface SchemaColumnHeaderProps { @observer export class SchemaColumnHeader extends ObservableReactComponent<SchemaColumnHeaderProps> { - private _inputRef: EditableView | null = null; @observable _altTitle: string | undefined = undefined; @observable _showMenuIcon: boolean = false; @@ -58,18 +57,26 @@ export class SchemaColumnHeader extends ObservableReactComponent<SchemaColumnHea return this._props.columnKeys[this._props.columnIndex]; } - constructor(props: SchemaColumnHeaderProps){ + constructor(props: SchemaColumnHeaderProps) { super(props); makeObservable(this); } - + getFinfo = computedFn((fieldKey: string) => this._props.schemaView?.fieldInfos.get(fieldKey)); - setColumnValues = (field: string, defaultValue: string) => {this._props.schemaView?.setKey(field, defaultValue, this._props.columnIndex);} - @action updateAlt = (newAlt: string) => {this._altTitle = newAlt}; - updateKeyDropdown = (value: string) => {this._props.schemaView.updateKeySearch(value)}; - openKeyDropdown = () => {!this._props.schemaView._colBeingDragged && this._props.schemaView.openNewColumnMenu(this._props.columnIndex, false)}; + setColumnValues = (field: string, defaultValue: string) => { + this._props.schemaView?.setKey(field, defaultValue, this._props.columnIndex); + }; + @action updateAlt = (newAlt: string) => { + this._altTitle = newAlt; + }; + updateKeyDropdown = (value: string) => { + this._props.schemaView.updateKeySearch(value); + }; + openKeyDropdown = () => { + !this._props.schemaView._colBeingDragged && this._props.schemaView.openNewColumnMenu(this._props.columnIndex, false); + }; toggleEditing = (editing: boolean) => { - this._inputRef?.setIsEditing(editing); + this._inputRef?.setIsEditing(editing); this._inputRef?.setIsFocused(editing); }; @@ -110,139 +117,156 @@ export class SchemaColumnHeader extends ObservableReactComponent<SchemaColumnHea const cursor = !readOnly ? 'text' : 'default'; const pointerEvents: 'all' | 'none' = 'all'; return { color, fieldProps, cursor, pointerEvents }; - } + }; @computed get editableView() { - const { color, fieldProps, pointerEvents } = this.renderProps(this._props); - - return <div className='schema-column-edit-wrapper' onPointerUp={() => { - SchemaColumnHeader.isDefaultField(this.fieldKey) && this.openKeyDropdown(); - this._props.schemaView.deselectAllCells(); - }} - style={{ - color, - width: '100%', - pointerEvents, - }}> - <EditableView - ref={r => {this._inputRef = r; this._props.autoFocus && r?.setIsFocused(true)}} - oneLine={true} - allowCRs={false} - contents={''} - onClick={this.openKeyDropdown} - fieldContents={fieldProps} - editing={undefined} - placeholder={'Add key'} - updateAlt={this.updateAlt} // alternate title to display - updateSearch={this.updateKeyDropdown} - schemaFieldType={SchemaFieldType.Header} - GetValue={() => { - if (SchemaColumnHeader.isDefaultField(this.fieldKey)) return ''; - else if (this._altTitle) return this._altTitle; - else return this.fieldKey; + const { color, fieldProps, pointerEvents } = this.renderProps(this._props); + + return ( + <div + className="schema-column-edit-wrapper" + onPointerUp={() => { + SchemaColumnHeader.isDefaultField(this.fieldKey) && this.openKeyDropdown(); + this._props.schemaView.deselectAllCells(); }} - SetValue={undoable((value: string, shiftKey?: boolean, enterKey?: boolean) => { - if (enterKey) { // if shift & enter, set value of each cell in column - this.setColumnValues(value, ''); - this._altTitle = undefined; + style={{ + color, + width: '100%', + pointerEvents, + }}> + <EditableView + ref={r => { + this._inputRef = r; + this._props.autoFocus && r?.setIsFocused(true); + }} + oneLine={true} + allowCRs={false} + contents={''} + onClick={this.openKeyDropdown} + fieldContents={fieldProps} + editing={undefined} + placeholder={'Add key'} + updateAlt={this.updateAlt} // alternate title to display + updateSearch={this.updateKeyDropdown} + schemaFieldType={SchemaFieldType.Header} + GetValue={() => { + if (SchemaColumnHeader.isDefaultField(this.fieldKey)) return ''; + else if (this._altTitle) return this._altTitle; + else return this.fieldKey; + }} + SetValue={undoable((value: string, shiftKey?: boolean, enterKey?: boolean) => { + if (enterKey) { + // if shift & enter, set value of each cell in column + this.setColumnValues(value, ''); + this._altTitle = undefined; + this._props.finishEdit?.(); + return true; + } this._props.finishEdit?.(); return true; - } - this._props.finishEdit?.(); - return true; - }, 'edit column header')} - /> + }, 'edit column header')} + /> </div> + ); } public static isDefaultField = (key: string) => { const defaultPattern = /EmptyColumnKey/; - const isDefault: boolean = (defaultPattern.exec(key) != null); + const isDefault: boolean = defaultPattern.exec(key) != null; return isDefault; - } + }; - get headerButton(){ - const toRender = SchemaColumnHeader.isDefaultField(this.fieldKey) ? - (<IconButton - icon={ <FontAwesomeIcon icon="trash" size='sm'/>} - size={Size.XSMALL} - color={'black'} - onPointerDown={e => - setupMoveUpEvents( - this, - e, - returnFalse, - emptyFunction, - undoable(clickEv => { - clickEv.stopPropagation(); - this._props.schemaView.removeColumn(this._props.columnIndex); - }, 'open column menu') - ) - } - />) - : (<IconButton - icon={ <FontAwesomeIcon icon="caret-down" size='lg'/>} - size={Size.XSMALL} - color={'black'} - onPointerDown={e => - setupMoveUpEvents( - this, - e, - returnFalse, - emptyFunction, - undoable(clickEv => { - clickEv.stopPropagation(); - this._props.openContextMenu(e.clientX, e.clientY, this._props.columnIndex) - }, 'open column menu') - ) - } - />) + get headerButton() { + const toRender = SchemaColumnHeader.isDefaultField(this.fieldKey) ? ( + <IconButton + icon={<FontAwesomeIcon icon="trash" size="sm" />} + size={Size.XSMALL} + color={'black'} + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + undoable(clickEv => { + clickEv.stopPropagation(); + this._props.schemaView.removeColumn(this._props.columnIndex); + }, 'open column menu') + ) + } + /> + ) : ( + <IconButton + icon={<FontAwesomeIcon icon="caret-down" size="lg" />} + size={Size.XSMALL} + color={'black'} + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + undoable(clickEv => { + clickEv.stopPropagation(); + this._props.openContextMenu(e.clientX, e.clientY, this._props.columnIndex); + }, 'open column menu') + ) + } + /> + ); return toRender; } - @action handlePointerEnter = () => this._showMenuIcon = true; - @action handlePointerLeave = () => this._showMenuIcon = false; + @action handlePointerEnter = () => { this._showMenuIcon = true; } // prettier-ignore + @action handlePointerLeave = () => { this._showMenuIcon = false; } // prettier-ignore - @computed get displayButton() {return this._showMenuIcon;} + @computed get displayButton() { + return this._showMenuIcon; + } render() { return ( - <div - className="schema-column-header" - style={{ - width: this._props.columnWidths[this._props.columnIndex], - }} - onPointerEnter={() => {this.handlePointerEnter()}} - onPointerLeave={() => {this.handlePointerLeave()}} - onPointerDown={e => { - this.setupDrag(e); - setupMoveUpEvents( - this, - e, - () => {return this._inputRef?.setIsEditing(false) ?? false}, - emptyFunction, - emptyFunction, - ); - } + <div + className="schema-column-header" + style={{ + width: this._props.columnWidths[this._props.columnIndex], + }} + onPointerEnter={() => { + this.handlePointerEnter(); + }} + onPointerLeave={() => { + this.handlePointerLeave(); + }} + onPointerDown={e => { + this.setupDrag(e); + setupMoveUpEvents( + this, + e, + () => { + return this._inputRef?.setIsEditing(false) ?? false; + }, + emptyFunction, + emptyFunction + ); + }} + ref={col => { + if (col) { + this._props.setColRef(this._props.columnIndex, col); } - ref={col => { - if (col) { - this._props.setColRef(this._props.columnIndex, col); - } - }}> - <div className="schema-column-resizer left" onPointerDown={e => this._props.resizeColumn(e, this._props.columnIndex, false)} /> + }}> + <div className="schema-column-resizer left" onPointerDown={e => this._props.resizeColumn(e, this._props.columnIndex, false)} /> - <div className="schema-header-text">{this.editableView}</div> + <div className="schema-header-text">{this.editableView}</div> - <div className="schema-header-menu"> - <div className="schema-header-button" style={{opacity: this.displayButton ? '1.0' : '0.0'}}> - {this.headerButton} - </div> - </div> - - <div className="schema-column-resizer right" onPointerDown={e => this._props.resizeColumn(e, this._props.columnIndex, true)} /> + <div className="schema-header-menu"> + <div className="schema-header-button" style={{ opacity: this.displayButton ? '1.0' : '0.0' }}> + {this.headerButton} + </div> </div> + + <div className="schema-column-resizer right" onPointerDown={e => this._props.resizeColumn(e, this._props.columnIndex, true)} /> + </div> ); } -}
\ No newline at end of file +} diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index a8a4ef2c2..6ffb0865a 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -21,7 +21,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; /** * The SchemaRowBox renders a doc as a row of cells, with each cell representing - * one field value of the doc. It mostly handles communication from the SchemaView + * one field value of the doc. It mostly handles communication from the SchemaView * to each SchemaCell, passing down necessary functions are props. */ @@ -59,7 +59,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { ContextMenu.Instance.clearItems(); ContextMenu.Instance.addItem({ description: this.Document._lockedSchemaEditing ? 'Unlock field editing' : 'Lock field editing', - event: () => this.Document._lockedSchemaEditing = !this.Document._lockedSchemaEditing, + event: () => (this.Document._lockedSchemaEditing = !this.Document._lockedSchemaEditing), icon: this.Document._lockedSchemaEditing ? 'lock-open' : 'lock', }); ContextMenu.Instance.addItem({ @@ -78,17 +78,19 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { // ContextMenu.Instance.addItem({ // description: this.Document._childrenSharedWithSchema ? 'Remove children from schema' : 'Add children to schema', // event: () => { - // this.Document._childrenSharedWithSchema = !this.Document._childrenSharedWithSchema; + // this.Document._childrenSharedWithSchema = !this.Document._childrenSharedWithSchema; // }, // icon: this.Document._childrenSharedWithSchema ? 'minus' : 'plus', // }); // } ContextMenu.Instance.displayMenu(x, y, undefined, false); - } + }; - @computed get menuBackgroundColor(){ - if (this.Document._lockedSchemaEditing) {return '#F5F5F5'} - return '' + @computed get menuBackgroundColor() { + if (this.Document._lockedSchemaEditing) { + return '#F5F5F5'; + } + return ''; } @computed get menuInfos() { @@ -98,22 +100,28 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { return infos; } - isolatedSelection = (doc: Doc) => {return this.schemaView?.selectionOverlap(doc)}; + isolatedSelection = (doc: Doc) => { + return this.schemaView?.selectionOverlap(doc); + }; setCursorIndex = (mouseY: number) => this.schemaView?.setRelCursorIndex(mouseY); selectedCol = () => this.schemaView._selectedCol; getFinfo = computedFn((fieldKey: string) => this.schemaView?.fieldInfos.get(fieldKey)); selectCell = (doc: Doc, col: number, shift: boolean, ctrl: boolean) => this.schemaView?.selectCell(doc, col, shift, ctrl); deselectCell = () => this.schemaView?.deselectAllCells(); selectedCells = () => this.schemaView?._selectedDocs; - setColumnValues = (field: any, value: any) => this.schemaView?.setCellValues(field, value) ?? false; + setColumnValues = (field: string, value: string) => this.schemaView?.setCellValues(field, value) ?? false; columnWidth = computedFn((index: number) => () => this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth); computeRowIndex = () => this.schemaView?.rowIndex(this.Document); highlightCells = (text: string) => this.schemaView?.highlightCells(text); - selectReference = (doc: Doc, col: number) => {this.schemaView.selectReference(doc, col)} + selectReference = (doc: Doc, col: number) => { + this.schemaView.selectReference(doc, col); + }; eqHighlightFunc = (text: string) => { const info = this.schemaView.findCellRefs(text); const cells: HTMLDivElement[] = []; - info.forEach(info => {cells.push(this.schemaView.getCellElement(info[0], info[1]))}) + info.forEach(inf => { + cells.push(this.schemaView.getCellElement(inf[0], inf[1])); + }); return cells; }; render() { @@ -121,7 +129,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { <div className="schema-row" onPointerDown={e => this.setCursorIndex(e.clientY)} - style={{ height: this._props.PanelHeight()}} + style={{ height: this._props.PanelHeight() }} ref={(row: HTMLDivElement | null) => { row && this.schemaView?.addRowRef?.(this.Document, row); this._ref = row; @@ -131,11 +139,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { style={{ width: CollectionSchemaView._rowMenuWidth, pointerEvents: !this._props.isContentActive() ? 'none' : undefined, - backgroundColor: this.menuBackgroundColor + backgroundColor: this.menuBackgroundColor, }}> <IconButton tooltip="Open actions menu" - icon={ <FontAwesomeIcon icon="caret-right" size='lg'/>} + icon={<FontAwesomeIcon icon="caret-right" size="lg" />} size={Size.XSMALL} color={'black'} onPointerDown={e => @@ -146,14 +154,16 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { emptyFunction, undoable(clickEv => { clickEv.stopPropagation(); - this.openContextMenu(e.clientX, e.clientY) + this.openContextMenu(e.clientX, e.clientY); }, 'open actions menu') ) } /> - <div className="row-menu-infos"> - {this.menuInfos.map(icn => <FontAwesomeIcon className="row-infos-icon" icon={icn} size='2xs' />)} - </div> + <div className="row-menu-infos"> + {this.menuInfos.map(icn => ( + <FontAwesomeIcon key={icn.toString()} className="row-infos-icon" icon={icn} size="2xs" /> + ))} + </div> </div> <div className="row-cells"> {this.schemaView?.columnKeys?.map((key, index) => ( @@ -192,4 +202,4 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { </div> ); } -}
\ No newline at end of file +} diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index c05382ce0..f036ff843 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable no-use-before-define */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Popup, Size, Type } from 'browndash-components'; @@ -37,7 +36,7 @@ import { SchemaCellField } from './SchemaCellField'; /** * SchemaTableCells make up the majority of the visual representation of the SchemaView. * They are rendered for each cell in the SchemaView, and each represents one field value - * of a doc. Editing the content of the cell changes the corresponding doc's field value. + * of a doc. Editing the content of the cell changes the corresponding doc's field value. */ export interface SchemaTableCellProps { @@ -67,21 +66,16 @@ export interface SchemaTableCellProps { isolatedSelection: (doc: Doc) => [boolean, boolean]; highlightCells: (text: string) => void; eqHighlightFunc: (text: string) => HTMLDivElement[] | []; - refSelectModeInfo: {enabled: boolean, currEditing: SchemaCellField | undefined}; + refSelectModeInfo: { enabled: boolean; currEditing: SchemaCellField | undefined }; selectReference: (doc: Doc, col: number) => void; } function selectedCell(props: SchemaTableCellProps) { - return ( - props.isRowActive() && - props.selectedCol() === props.col && - props.selectedCells()?.filter(d => d === props.Document)?.length - ); + return props.isRowActive() && props.selectedCol() === props.col && props.selectedCells()?.filter(d => d === props.Document)?.length; } @observer export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellProps> { - // private _fieldRef: SchemaCellField | null = null; private _submittedValue: string = ''; @@ -96,9 +90,11 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro get lockedInteraction(){return (this.isDefault || this._props.Document._lockedSchemaEditing);} // prettier-ignore - get backgroundColor(){ - if (this.lockedInteraction) {return '#F5F5F5'} - return '' + get backgroundColor() { + if (this.lockedInteraction) { + return '#F5F5F5'; + } + return ''; } static addFieldDoc = (docs: Doc | Doc[] /* , where: OpenWhere */) => { @@ -110,7 +106,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro let protoCount = 0; let doc: Doc | undefined = Document; while (doc) { - if (Object.keys(doc).includes(fieldKey.replace(/^_/, ''))) break; + if (Object.keys(doc).includes(fieldKey.replace(/^_/, ''))) break; protoCount++; doc = DocCast(doc.proto); } @@ -149,28 +145,40 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro adjustSelfReference = (field: string) => { const modField = field.replace(/\bthis.\b/g, `d${this.docIndex}.`); return modField; - } + }; // parses a field from the "idToDoc(####)" format to DocumentId (d#) format for readability cleanupField = (field: string) => { let modField = field.slice(); let eqSymbol: string = ''; - if (modField.startsWith('=')) {modField = modField.substring(1); eqSymbol += '=';} - if (modField.startsWith(':=')) {modField = modField.substring(2); eqSymbol += ':=';} + if (modField.startsWith('=')) { + modField = modField.substring(1); + eqSymbol += '='; + } + if (modField.startsWith(':=')) { + modField = modField.substring(2); + eqSymbol += ':='; + } const idPattern = /idToDoc\((.*?)\)/g; let matches; const results = new Array<[id: string, func: string]>(); - while ((matches = idPattern.exec(field)) !== null) {results.push([matches[0], matches[1].replace(/"/g, '')]); } - results.forEach((idFuncPair) => {modField = modField.replace(idFuncPair[0], 'd' + (DocumentView.getDocViewIndex(IdToDoc(idFuncPair[1]))).toString());}) + while ((matches = idPattern.exec(field)) !== null) { + results.push([matches[0], matches[1].replace(/"/g, '')]); + } + results.forEach(idFuncPair => { + modField = modField.replace(idFuncPair[0], 'd' + DocumentView.getDocViewIndex(IdToDoc(idFuncPair[1])).toString()); + }); if (modField.endsWith(';')) modField = modField.substring(0, modField.length - 1); - const inQuotes = (field: string) => {return ((field.startsWith('`') && field.endsWith('`')) || (field.startsWith("'") && field.endsWith("'")) || (field.startsWith('"') && field.endsWith('"')))} + const inQuotes = (strField: string) => { + return (strField.startsWith('`') && strField.endsWith('`')) || (strField.startsWith("'") && strField.endsWith("'")) || (strField.startsWith('"') && strField.endsWith('"')); + }; if (!inQuotes(this._submittedValue) && inQuotes(modField)) modField = modField.substring(1, modField.length - 1); return eqSymbol + modField; - } + }; @computed get defaultCellContent() { const { color, textDecoration, fieldProps, pointerEvents } = SchemaTableCell.renderProps(this._props); @@ -203,7 +211,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro this._props.finishEdit?.(); return true; } - const hasNoLayout = Doc.IsDataProto(fieldProps.Document) ? true : undefined; // the "delegate" is a a data document so never write to it's proto + const hasNoLayout = Doc.IsDataProto(fieldProps.Document) ? true : undefined; // the "delegate" is a a data document so never write to it's proto const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, hasNoLayout); this._submittedValue = value; this._props.finishEdit?.(); @@ -245,8 +253,8 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro const sides: Array<string | undefined> = []; sides[0] = selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // left sides[1] = selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // right - sides[2] = (!this._props.isolatedSelection(this._props.Document)[0] && selectedCell(this._props)) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // top - sides[3] = (!this._props.isolatedSelection(this._props.Document)[1] && selectedCell(this._props)) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // bottom + sides[2] = !this._props.isolatedSelection(this._props.Document)[0] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // top + sides[3] = !this._props.isolatedSelection(this._props.Document)[1] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // bottom return sides; } @@ -256,9 +264,13 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro className="schema-table-cell" onContextMenu={e => StopEvent(e)} onPointerDown={action(e => { - if (this.lockedInteraction) { e.stopPropagation(); e.preventDefault(); return; } + if (this.lockedInteraction) { + e.stopPropagation(); + e.preventDefault(); + return; + } - if (this._props.refSelectModeInfo.enabled && !selectedCell(this._props)){ + if (this._props.refSelectModeInfo.enabled && !selectedCell(this._props)) { e.stopPropagation(); e.preventDefault(); this._props.selectReference(this._props.Document, this._props.col); @@ -274,14 +286,16 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro } else !selectedCell(this._props) && this._props.selectCell(this._props.Document, this._props.col, shift, ctrl); } })} - style={{ padding: this._props.padding, - maxWidth: this._props.maxWidth?.(), - width: this._props.columnWidth() || undefined, - borderLeft: this.borderColor[0], - borderRight: this.borderColor[1], - borderTop: this.borderColor[2], - borderBottom: this.borderColor[3], - backgroundColor: this.backgroundColor}}> + style={{ + padding: this._props.padding, + maxWidth: this._props.maxWidth?.(), + width: this._props.columnWidth() || undefined, + borderLeft: this.borderColor[0], + borderRight: this.borderColor[1], + borderTop: this.borderColor[2], + borderBottom: this.borderColor[3], + backgroundColor: this.backgroundColor, + }}> {this.isDefault ? '' : this.content} </div> ); @@ -524,4 +538,4 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC </div> ); } -}
\ No newline at end of file +} diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index 962a21dbb..6c3f4eaff 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -84,7 +84,6 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b } else { const dataKey = Doc.LayoutFieldKey(dv.Document); const alternate = (dv.layoutDoc[dataKey + '_usePath'] ? '_' + dv.layoutDoc[dataKey + '_usePath'] : '').replace(':hover', ''); - console.log('color: ' + dv.dataDoc[fieldKey + alternate] + ' to set to: ' + color) dv.layoutDoc[fieldKey + alternate] = undefined; dv.dataDoc[fieldKey + alternate] = color; } diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 12196f290..a44014444 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox } from '@mui/material'; import { Colors, Toggle, ToggleType, Type } from 'browndash-components'; @@ -8,42 +7,36 @@ import * as React from 'react'; import { ClientUtils, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents } from '../../../../ClientUtils'; import { emptyFunction } from '../../../../Utils'; import { Doc, DocListCast, Field, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc'; +import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols'; import { InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; +import { PrefetchProxy } from '../../../../fields/Proxy'; import { Cast, CsvCast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { CsvField } from '../../../../fields/URLField'; -import { GetEffectiveAcl, TraceMobx, inheritParentAcls } from '../../../../fields/util'; +import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; +import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT'; import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; +import { LinkManager } from '../../../util/LinkManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; import { MarqueeAnnotator } from '../../MarqueeAnnotator'; import { PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; +import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup'; import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { FocusViewOptions } from '../FocusViewOptions'; import './DataVizBox.scss'; +import { Col, DataVizTemplateInfo, DocCreatorMenu, LayoutType, TemplateFieldSize, TemplateFieldType } from './DocCreatorMenu'; import { Histogram } from './components/Histogram'; import { LineChart } from './components/LineChart'; import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; -import { LinkManager } from '../../../util/LinkManager'; -import { Col, DataVizTemplateInfo, DataVizTemplateLayout, DocCreatorMenu, TemplateFieldSize, LayoutType, TemplateFieldType } from './DocCreatorMenu'; -import { CollectionFreeFormView, MarqueeView } from '../../collections/collectionFreeForm'; -import { PrefetchProxy } from '../../../../fields/Proxy'; -import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols'; -import { template } from 'lodash'; -import { data } from 'jquery'; -import { listSpec } from '../../../../fields/Schema'; -import { ObjectField } from '../../../../fields/ObjectField'; -import { Id } from '../../../../fields/FieldSymbols'; -import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT'; -import { TbSortDescendingShapes } from 'react-icons/tb'; export enum DataVizView { TABLE = 'table', @@ -65,7 +58,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @observable _marqueeing: number[] | undefined = undefined; @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); @observable _specialHighlightedRow: number | undefined = undefined; - @observable GPTSummary: ObservableMap<string, {desc?: string, type?: TemplateFieldType, size?: TemplateFieldSize}> | undefined = undefined; + @observable GPTSummary: ObservableMap<string, { desc?: string; type?: TemplateFieldType; size?: TemplateFieldSize }> | undefined = undefined; @observable colsInfo: ObservableMap<string, Col> = new ObservableMap(); @observable _GPTLoading: boolean = false; @@ -121,12 +114,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const records = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href); this._urlError = false; return records?.filter(record => Object.keys(record).some(key => record[key])) ?? []; - } catch (e){ + } catch { this._urlError = true; - const data: { [key: string]: string; }[] = [ - { error: "Data not found"}, - ]; - return data; + return [{ error: 'Data not found' }] as { [key: string]: string }[]; } } @@ -153,70 +143,73 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @action setSpecialHighlightedRow = (row: number | undefined) => { this._specialHighlightedRow = row; - } + }; @action setColumnType = (colTitle: string, type: TemplateFieldType) => { const colInfo = this.colsInfo.get(colTitle); - if (colInfo) { + if (colInfo) { colInfo.type = type; } else { - this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: type, sizes: [TemplateFieldSize.MEDIUM]}) + this.colsInfo.set(colTitle, { title: colTitle, desc: '', type: type, sizes: [TemplateFieldSize.MEDIUM] }); } - } + }; @action modifyColumnSizes = (colTitle: string, size: TemplateFieldSize, valid: boolean) => { const column = this.colsInfo.get(colTitle); - if (column) { + if (column) { if (!valid && column.sizes.includes(size)) { column.sizes.splice(column.sizes.indexOf(size), 1); } else if (valid && !column.sizes.includes(size)) { column.sizes.push(size); } } else { - this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [size]}) + this.colsInfo.set(colTitle, { title: colTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [size] }); } - } + }; @action setColumnTitle = (colTitle: string, newTitle: string) => { const colInfo = this.colsInfo.get(colTitle); - if (colInfo) { + if (colInfo) { colInfo.title = newTitle; - console.log(colInfo.title) + console.log(colInfo.title); } else { - this.colsInfo.set(colTitle, {title: newTitle, desc: '', type: TemplateFieldType.UNSET, sizes: []}) + this.colsInfo.set(colTitle, { title: newTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [] }); } - } + }; @action setColumnDesc = (colTitle: string, desc: string) => { const colInfo = this.colsInfo.get(colTitle); if (colInfo) { - if (!desc) { colInfo.desc = this.GPTSummary?.get(colTitle)?.desc ?? ''; } - else { colInfo.desc = desc; } + if (!desc) { + colInfo.desc = this.GPTSummary?.get(colTitle)?.desc ?? ''; + } else { + colInfo.desc = desc; + } } else { - this.colsInfo.set(colTitle, {title: colTitle, desc: desc, type: TemplateFieldType.UNSET, sizes: []}) + this.colsInfo.set(colTitle, { title: colTitle, desc: desc, type: TemplateFieldType.UNSET, sizes: [] }); } - } + }; @action setColumnDefault = (colTitle: string, cont: string) => { const colInfo = this.colsInfo.get(colTitle); if (colInfo) { colInfo.defaultContent = cont; } else { - this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [], defaultContent: cont}) + this.colsInfo.set(colTitle, { title: colTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [], defaultContent: cont }); } - } + }; @action // pinned / linked anchor doc includes selected rows, graph titles, and graph colors - restoreView = (data: Doc) => { + restoreView = (viewData: Doc) => { // const changedView = data.config_dataViz && this.dataVizView !== data.config_dataViz && (this.layoutDoc._dataViz = data.config_dataViz); // const changedAxes = data.config_dataVizAxes && this.axes.join('') !== StrListCast(data.config_dataVizAxes).join('') && (this.layoutDoc._dataViz_axes = new List<string>(StrListCast(data.config_dataVizAxes))); - this.layoutDoc.dataViz_selectedRows = Field.Copy(data.dataViz_selectedRows); - this.layoutDoc.dataViz_histogram_barColors = Field.Copy(data.dataViz_histogram_barColors); - this.layoutDoc.dataViz_histogram_defaultColor = data.dataViz_histogram_defaultColor; - this.layoutDoc.dataViz_pie_sliceColors = Field.Copy(data.dataViz_pie_sliceColors); + this.layoutDoc.dataViz_selectedRows = Field.Copy(viewData.dataViz_selectedRows); + this.layoutDoc.dataViz_histogram_barColors = Field.Copy(viewData.dataViz_histogram_barColors); + this.layoutDoc.dataViz_histogram_defaultColor = viewData.dataViz_histogram_defaultColor; + this.layoutDoc.dataViz_pie_sliceColors = Field.Copy(viewData.dataViz_pie_sliceColors); Object.keys(this.layoutDoc).forEach(key => { if (key.startsWith('dataViz_histogram_title') || key.startsWith('dataViz_lineChart_title') || key.startsWith('dataViz_pieChart_title')) { - this.layoutDoc['_' + key] = data[key]; + this.layoutDoc['_' + key] = viewData[key]; } }); return true; @@ -354,7 +347,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { componentDidMount() { this._props.setContentViewBox?.(this); - if (!this._urlError) { if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey]).url.href)) this.fetchData() }; + if (!this._urlError) { + if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey]).url.href)) this.fetchData(); + } this._disposers.datavis = reaction( () => { if (this.layoutDoc.dataViz_schemaLive === undefined) this.layoutDoc.dataViz_schemaLive = true; @@ -516,14 +511,14 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { DocCreatorMenu.Instance.toggleDisplay(x, y); DocCreatorMenu.Instance.setDataViz(this); DocCreatorMenu.Instance.setTemplateDocs(this.getPossibleTemplates()); - } + }; - specificContextMenu = (x: number, y: number): void => { + specificContextMenu = (e: MouseEventHandler) => { const cm = ContextMenu.Instance; const options = cm.findByDescription('Options...'); const optionItems = options?.subitems ?? []; optionItems.push({ description: `Analyze with AI`, event: () => this.askGPT(), icon: 'lightbulb' }); - optionItems.push({ description: `Create documents`, event: () => this.openDocCreatorMenu(x, y), icon: 'table-cells' }); + optionItems.push({ description: `Create documents`, event: () => this.openDocCreatorMenu(e.pageX, e.pageY), icon: 'table-cells' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); }; @@ -533,19 +528,19 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { GPTPopup.Instance.createFilteredDoc = this.createFilteredDoc; GPTPopup.Instance.setDataJson(''); GPTPopup.Instance.setMode(GPTPopupMode.DATA); - const data = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href); - GPTPopup.Instance.setDataJson(JSON.stringify(data)); + const csvdata = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href); + GPTPopup.Instance.setDataJson(JSON.stringify(csvdata)); GPTPopup.Instance.generateDataAnalysis(); }); getColSummary = (): string => { - let possibleIds: number[] = this.records.map((_, index) => index); + const possibleIds: number[] = this.records.map((_, index) => index); for (let i = possibleIds.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [possibleIds[i], possibleIds[j]] = [possibleIds[j], possibleIds[i]]; } - + const rowsToCheck = possibleIds.slice(0, Math.min(10, this.records.length)); let prompt: string = 'Col titles: '; @@ -553,37 +548,37 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const cols = Array.from(Object.keys(this.records[0])).filter(header => header !== '' && header !== undefined); cols.forEach((col, i) => { - prompt += `Col #${i}: ${col} ------` - }) + prompt += `Col #${i}: ${col} ------`; + }); - prompt += '----------- Rows: ' + prompt += '----------- Rows: '; - rowsToCheck.forEach((row, i) => { - prompt += `Row #${row}: ` + rowsToCheck.forEach(row => { + prompt += `Row #${row}: `; cols.forEach(col => { - prompt += `${col}: ${this.records[row][col]} -----` - }) - }) + prompt += `${col}: ${this.records[row][col]} -----`; + }); + }); return prompt; - } + }; updateColDefaults = () => { - let possibleIds: number[] = this.records.map((_, index) => index); + const possibleIds: number[] = this.records.map((_, index) => index); for (let i = possibleIds.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [possibleIds[i], possibleIds[j]] = [possibleIds[j], possibleIds[i]]; } - + const rowToCheck = possibleIds[0]; const cols = Array.from(Object.keys(this.records[0])).filter(header => header !== '' && header !== undefined); - cols.forEach(col => { - this.setColumnDefault(col, `${this.records[rowToCheck][col]}`) + cols.forEach(col => { + this.setColumnDefault(col, `${this.records[rowToCheck][col]}`); }); - } + }; updateGPTSummary = async () => { this._GPTLoading = true; @@ -594,15 +589,12 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const cols = Array.from(Object.keys(this.records[0])).filter(header => header !== '' && header !== undefined); cols.forEach(col => { - if (!this.colsInfo.get(col)) this.colsInfo.set(col, {title: col, desc: '', sizes: [], type: TemplateFieldType.UNSET}); + if (!this.colsInfo.get(col)) this.colsInfo.set(col, { title: col, desc: '', sizes: [], type: TemplateFieldType.UNSET }); }); try { - const [res1, res2] = await Promise.all([ - gptAPICall(prompt, GPTCallType.VIZSUM), - gptAPICall('Info:' + prompt, GPTCallType.VIZSUM2) - ]); - + const [res1, res2] = await Promise.all([gptAPICall(prompt, GPTCallType.VIZSUM), gptAPICall('Info:' + prompt, GPTCallType.VIZSUM2)]); + if (res1) { this.GPTSummary = new ObservableMap(); const descs: { [col: string]: string } = JSON.parse(res1); @@ -611,10 +603,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { if (!this.colsInfo.get(key)?.desc) this.setColumnDesc(key, val); } } - + if (res2) { !this.GPTSummary && (this.GPTSummary = new ObservableMap()); - const info: { [col: string]: { type: TemplateFieldType, size: TemplateFieldSize } } = JSON.parse(res2); + const info: { [col: string]: { type: TemplateFieldType; size: TemplateFieldSize } } = JSON.parse(res2); for (const [key, val] of Object.entries(info)) { const colSummary = this.GPTSummary.get(key); if (colSummary) { @@ -628,30 +620,29 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } catch (err) { console.error(err); } - - } + }; getPossibleTemplates = (): Doc[] => { const linkedDocs: Doc[] = LinkManager.Instance.getAllRelatedLinks(this.Document).map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document))); const linkedCollections: Doc[] = linkedDocs.filter(doc => doc.type === 'config').map(doc => DocCast(doc.annotationOn)); const isColumnTitle = (title: string): boolean => { const colTitles: string[] = Object.keys(this.records[0]); - for (let i = 0; i < colTitles.length; ++i){ + for (let i = 0; i < colTitles.length; ++i) { if (colTitles[i] === title) { return true; } } return false; - } + }; const isValidTemplate = (collection: Doc) => { const childDocs = DocListCast(collection[Doc.LayoutFieldKey(collection)]); - for (let i = 0; i < childDocs.length; ++i){ + for (let i = 0; i < childDocs.length; ++i) { if (isColumnTitle(String(childDocs[i].title))) return true; } return false; - } + }; return linkedCollections.filter(col => isValidTemplate(col)); - } + }; ApplyTemplateTo = (templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined) => { if (!Doc.AreProtosEqual(target[targetKey] as Doc, templateDoc)) { @@ -664,14 +655,14 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } } return target; - } + }; applyLayout = (templateInfo: DataVizTemplateInfo, docs: Doc[]) => { if (templateInfo.layout.type === LayoutType.Stacked) return; const columns: number = templateInfo.columns; const xGap: number = templateInfo.layout.xMargin; const yGap: number = templateInfo.layout.yMargin; - const repeat: number = templateInfo.layout.repeat; + // const repeat: number = templateInfo.layout.repeat; const startX: number = templateInfo.referencePos.x; const startY: number = templateInfo.referencePos.y; const templWidth = Number(templateInfo.doc._width); @@ -682,8 +673,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { let curX: number = startX; let curY: number = startY; - while (docsChanged < docs.length){ - while (i < columns && docsChanged < docs.length){ + while (docsChanged < docs.length) { + while (i < columns && docsChanged < docs.length) { docs[docsChanged].x = curX; docs[docsChanged].y = curY; curX += templWidth + xGap; @@ -695,27 +686,29 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { curX = startX; curY += templHeight + yGap; } - } + }; // @action addSavedLayout = (layout: DataVizTemplateLayout) => { // const saved = Cast(this.layoutDoc.dataViz_savedTemplates, listSpec('RefField')); - + // } @action createDocsFromTemplate = (templateInfo: DataVizTemplateInfo) => { if (!templateInfo.doc) return; const mainCollection = this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; - const fields: string[] = Array.from(Object.keys(this.records[0])); + const fields: string[] = Array.from(Object.keys(this.records[0])); const selectedRows = NumListCast(this.layoutDoc.dataViz_selectedRows); const docs: Doc[] = selectedRows.map(row => { - const values: String[] = []; + const values: string[] = []; fields.forEach(col => values.push(this.records[row][col])); const proto = new Doc(); proto.author = ClientUtils.CurrentUserEmail(); - values.forEach((val, i) => {proto[fields[i]] = val as FieldType}); - + values.forEach((val, i) => { + proto[fields[i]] = val as FieldType; + }); + const target = Doc.MakeDelegate(proto); const targetKey = StrCast(templateInfo.doc!.layout_fieldKey, 'layout'); const applied = this.ApplyTemplateTo(templateInfo.doc!, target, targetKey, templateInfo.doc!.title + `${row}`); @@ -728,7 +721,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { docs.forEach(doc => mainCollection.addDocument(doc)); this.applyLayout(templateInfo, docs); - } + }; /** * creates a new dataviz document filter from this one @@ -783,7 +776,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { transform: `scale(${scale})`, position: 'absolute', }} - onContextMenu={(e) => this.specificContextMenu(e.pageX, e.pageY)} + onContextMenu={this.specificContextMenu} onWheel={e => e.stopPropagation()} ref={this._mainCont}> <div className="datatype-button"> diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx index 0b94ae4f7..9769ecb3d 100644 --- a/src/client/views/nodes/DocumentIcon.tsx +++ b/src/client/views/nodes/DocumentIcon.tsx @@ -1,5 +1,5 @@ import { Tooltip } from '@mui/material'; -import { action, makeObservable, observable } from 'mobx'; +import { makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { factory } from 'typescript'; @@ -18,14 +18,14 @@ interface DocumentIconProps { @observer export class DocumentIcon extends ObservableReactComponent<DocumentIconProps> { @observable _hovered = false; - constructor(props: any) { + constructor(props: DocumentIconProps) { super(props); makeObservable(this); } render() { const { view } = this._props; - const { left, top, right, bottom } = view.getBounds || { left: 0, top: 0, right: 0, bottom: 0 }; + const { left, top, bottom } = view.getBounds || { left: 0, top: 0, right: 0, bottom: 0 }; return ( <div @@ -34,7 +34,7 @@ export class DocumentIcon extends ObservableReactComponent<DocumentIconProps> { pointerEvents: 'all', position: 'absolute', background: SnappingManager.userBackgroundColor, - transform: `translate(${left}px, ${bottom - (bottom - top)/2}px)`, //**!** + transform: `translate(${left}px, ${bottom - (bottom - top) / 2}px)`, //**!** }}> <Tooltip title={<div>{StrCast(this._props.view.Document?.title)}</div>}> <p>d{this._props.index}</p> @@ -44,7 +44,7 @@ export class DocumentIcon extends ObservableReactComponent<DocumentIconProps> { } } -@observer +@observer export class DocumentIconContainer extends React.Component { public static getTransformer(): Transformer { const usedDocuments = new Set<number>(); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index ba85bfb68..741d63909 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -138,7 +138,7 @@ export class FieldView extends React.Component<FieldViewProps> { const field = this.fieldval; // prettier-ignore if (field instanceof Doc) return <p> <b>{field.title?.toString()}</b></p>; - if (field === undefined) return <p>{''}</p>; + if (field === undefined) return <p />; if (field instanceof DateField) return <p>{field.date.toLocaleString()}</p>; if (field instanceof List) return <div> {field.map(f => Field.toString(f)).join(', ')} </div>; if (field instanceof WebField) return <p>{Field.toString(field.url.href)}</p>; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index cfcf76b12..8974cccaf 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -2,7 +2,7 @@ import { Property } from 'csstype'; import { action, computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -//import * as textfit from 'textfit'; +import * as textfit from 'textfit'; import { Field, FieldType } from '../../../fields/Doc'; import { BoolCast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; @@ -96,7 +96,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() { this._timeout = setTimeout(() => this.fitTextToBox(r)); return textfitParams; } - //textfit(r, textfitParams); + textfit(r, textfitParams); } return textfitParams; }; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index c1367aa0b..0d7914a82 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1498,7 +1498,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB pdfAnchorId && this.addPdfReference(pdfAnchorId); } if (this._props.autoFocus) setTimeout(() => this._editorView!.focus()); // not sure why setTimeout is needed but editing dashFieldView's doesn't work without it. - } // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. @@ -2087,7 +2086,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB </div> </div> ); - } } |