diff options
Diffstat (limited to 'src/client/views/collections/collectionSchema')
5 files changed, 192 insertions, 155 deletions
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss index 76bd392a5..02131ae22 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss @@ -1,4 +1,4 @@ -@import '../../global/globalCssVariables.scss'; +@import '../../global/globalCssVariables.module.scss'; .collectionSchemaView { cursor: default; diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 190e4ff2a..2546f5b02 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,7 +1,7 @@ -import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, ObservableMap, observe } from 'mobx'; +import { action, computed, makeObservable, observable, ObservableMap, observe } from 'mobx'; import { observer } from 'mobx-react'; +import * as React from 'react'; import { Doc, DocListCast, Field, NumListCast, Opt, StrListCast } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; @@ -18,6 +18,7 @@ import { EditableView } from '../../EditableView'; import { Colors } from '../../global/globalEnums'; import { DocFocusOptions, DocumentView, DocumentViewProps } from '../../nodes/DocumentView'; import { KeyValueBox } from '../../nodes/KeyValueBox'; +import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DefaultStyleProvider, StyleProp } from '../../StyleProvider'; import { CollectionSubView } from '../CollectionSubView'; import './CollectionSchemaView.scss'; @@ -57,6 +58,11 @@ export class CollectionSchemaView extends CollectionSubView() { private _tableContentRef: HTMLDivElement | null = null; private _menuTarget = React.createRef<HTMLDivElement>(); + constructor(props: any) { + super(props); + makeObservable(this); + } + static _rowHeight: number = 50; static _rowSingleLineHeight: number = 32; public static _minColWidth: number = 25; @@ -68,16 +74,16 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _menuKeys: string[] = []; @observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>(); @observable _colEles: HTMLDivElement[] = []; - @observable _displayColumnWidths: number[] | undefined; - @observable _columnMenuIndex: number | undefined; + @observable _displayColumnWidths: number[] | undefined = undefined; + @observable _columnMenuIndex: number | undefined = undefined; @observable _newFieldWarning: string = ''; @observable _makeNewField: boolean = false; @observable _newFieldDefault: any = 0; @observable _newFieldType: ColumnType = ColumnType.Number; @observable _menuValue: string = ''; - @observable _filterColumnIndex: number | undefined; + @observable _filterColumnIndex: number | undefined = undefined; @observable _filterSearchValue: string = ''; - @observable _selectedCell: [Doc, number] | undefined; + @observable _selectedCell: [Doc, number] | undefined = undefined; // target HTMLelement portal for showing a popup menu to edit cell values. public get MenuTarget() { @@ -85,12 +91,12 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get _selectedDocs() { - const selected = SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc)); + const selected = SelectionManager.Docs.filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document)); if (!selected.length) { - for (const sel of SelectionManager.Docs()) { + for (const sel of SelectionManager.Docs) { const contextPath = DocumentManager.GetContextPath(sel, true); - if (contextPath.includes(this.rootDoc)) { - const parentInd = contextPath.indexOf(this.rootDoc); + if (contextPath.includes(this.Document)) { + const parentInd = contextPath.indexOf(this.Document); return parentInd < contextPath.length - 1 ? [contextPath[parentInd + 1]] : []; } } @@ -107,7 +113,7 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get tableWidth() { - return this.props.PanelWidth() - this.previewWidth - (this.previewWidth === 0 ? 0 : CollectionSchemaView._previewDividerWidth); + return this._props.PanelWidth() - this.previewWidth - (this.previewWidth === 0 ? 0 : CollectionSchemaView._previewDividerWidth); } @computed get columnKeys() { @@ -139,14 +145,13 @@ export class CollectionSchemaView extends CollectionSubView() { return BoolCast(this.layoutDoc.sortDesc); } - @action componentDidMount() { - this.props.setContentView?.(this); + this._props.setContentView?.(this); document.addEventListener('keydown', this.onKeyDown); Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1])); this._keysDisposer = observe( - this.rootDoc[this.fieldKey ?? 'data'] as List<Doc>, + this.dataDoc[this.fieldKey ?? 'data'] as List<Doc>, change => { switch (change.type as any) { case 'splice': @@ -154,7 +159,7 @@ export class CollectionSchemaView extends CollectionSubView() { (change as any).added.forEach((doc: Doc) => // for each document added Doc.GetAllPrototypes(doc.value as Doc).forEach(proto => // for all of its prototypes (and itself) Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them - !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo('')))))); + !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo(key, key === 'author')))))); break; case 'update': //let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list } @@ -239,7 +244,6 @@ export class CollectionSchemaView extends CollectionSubView() { }; @undoBatch - @action setColumnSort = (field: string | undefined, desc: boolean = false) => { this.layoutDoc.sortField = field; this.layoutDoc.sortDesc = desc; @@ -248,7 +252,6 @@ export class CollectionSchemaView extends CollectionSubView() { addRow = (doc: Doc | Doc[]) => this.addDocument(doc); @undoBatch - @action changeColumnKey = (index: number, newKey: string, defaultVal?: any) => { if (!this.documentKeys.includes(newKey)) { this.addNewKey(newKey, defaultVal); @@ -260,7 +263,6 @@ export class CollectionSchemaView extends CollectionSubView() { }; @undoBatch - @action addColumn = (key: string, defaultVal?: any) => { if (!this.documentKeys.includes(key)) { this.addNewKey(key, defaultVal); @@ -281,7 +283,6 @@ export class CollectionSchemaView extends CollectionSubView() { addNewKey = (key: string, defaultVal: any) => this.childDocs.forEach(doc => (doc[key] = defaultVal)); @undoBatch - @action removeColumn = (index: number) => { if (this.columnKeys.length === 1) return; const currWidths = this.storedColumnWidths.slice(); @@ -320,8 +321,8 @@ export class CollectionSchemaView extends CollectionSubView() { change = this._displayColumnWidths[shrinking] - CollectionSchemaView._minColWidth; } - this._displayColumnWidths[shrinking] -= change * this.props.ScreenToLocalTransform().Scale; - this._displayColumnWidths[growing] += change * this.props.ScreenToLocalTransform().Scale; + this._displayColumnWidths[shrinking] -= change * this._props.ScreenToLocalTransform().Scale; + this._displayColumnWidths[growing] += change * this._props.ScreenToLocalTransform().Scale; return false; } @@ -335,7 +336,6 @@ export class CollectionSchemaView extends CollectionSubView() { }; @undoBatch - @action moveColumn = (fromIndex: number, toIndex: number) => { let currKeys = this.columnKeys.slice(); currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]); @@ -379,7 +379,7 @@ export class CollectionSchemaView extends CollectionSubView() { @action highlightDropColumn = (e: PointerEvent) => { e.stopPropagation(); - const mouseX = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0]; + const mouseX = this._props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0]; const index = this.findDropIndex(mouseX); this._colEles.forEach((colRef, i) => { let leftStyle = ''; @@ -416,8 +416,8 @@ export class CollectionSchemaView extends CollectionSubView() { @action clearSelection = () => SelectionManager.DeselectAll(); - selectRows = (rootDoc: Doc, lastSelected: Doc) => { - const index = this.rowIndex(rootDoc); + selectRows = (doc: Doc, lastSelected: Doc) => { + const index = this.rowIndex(doc); const lastSelectedRow = this.rowIndex(lastSelected); const startRow = Math.min(lastSelectedRow, index); const endRow = Math.max(lastSelectedRow, index); @@ -437,10 +437,9 @@ export class CollectionSchemaView extends CollectionSubView() { setDropIndex = (index: number) => (this._closestDropIndex = index); - @action onInternalDrop = (e: Event, de: DragManager.DropEvent) => { if (de.complete.columnDragData) { - const mouseX = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0]; + const mouseX = this._props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0]; const index = this.findDropIndex(mouseX); this.moveColumn(de.complete.columnDragData.colIndex, index ?? de.complete.columnDragData.colIndex); @@ -473,7 +472,6 @@ export class CollectionSchemaView extends CollectionSubView() { return false; }; - @action onExternalDrop = async (e: React.DragEvent): Promise<void> => { super.onExternalDrop(e, {}, undoBatch(action(docus => docus.map((doc: Doc) => this.addDocument(doc))))); }; @@ -485,7 +483,7 @@ export class CollectionSchemaView extends CollectionSubView() { const nativeWidth = this._previewRef!.getBoundingClientRect(); const minWidth = 40; const maxWidth = 1000; - const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]; + const movedWidth = this._props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]; const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth; this.layoutDoc.schema_previewWidth = width; return false; @@ -509,8 +507,8 @@ export class CollectionSchemaView extends CollectionSubView() { const found = this._tableContentRef && Array.from(this._tableContentRef.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); if (found) { const rect = found.getBoundingClientRect(); - const localRect = this.props.ScreenToLocalTransform().transformBounds(rect.left, rect.top, rect.width, rect.height); - if (localRect.y < this.rowHeightFunc() || localRect.y + localRect.height > this.props.PanelHeight()) { + const localRect = this._props.ScreenToLocalTransform().transformBounds(rect.left, rect.top, rect.width, rect.height); + if (localRect.y < this.rowHeightFunc() || localRect.y + localRect.height > this._props.PanelHeight()) { let focusSpeed = options.zoomTime ?? 50; smoothScroll(focusSpeed, this._tableContentRef!, localRect.y + this._tableContentRef!.scrollTop - this.rowHeightFunc(), options.easeFunc); return focusSpeed; @@ -757,7 +755,7 @@ export class CollectionSchemaView extends CollectionSubView() { @computed get renderFilterOptions() { const keyOptions: string[] = []; const columnKey = this.columnKeys[this._filterColumnIndex!]; - const allDocs = DocListCast(this.dataDoc[this.props.fieldKey]); + const allDocs = DocListCast(this.dataDoc[this._props.fieldKey]); allDocs.forEach(doc => { const value = StrCast(doc[columnKey]); if (!keyOptions.includes(value) && value !== '' && (this._filterSearchValue === '' || value.includes(this._filterSearchValue))) { @@ -781,9 +779,9 @@ export class CollectionSchemaView extends CollectionSubView() { onClick={e => e.stopPropagation()} onChange={action(e => { if (e.target.checked) { - Doc.setDocFilter(this.props.Document, columnKey, key, 'check'); + Doc.setDocFilter(this.Document, columnKey, key, 'check'); } else { - Doc.setDocFilter(this.props.Document, columnKey, key, 'remove'); + Doc.setDocFilter(this.Document, columnKey, key, 'remove'); } })} checked={bool} @@ -830,8 +828,8 @@ export class CollectionSchemaView extends CollectionSubView() { } rowHeightFunc = () => (BoolCast(this.layoutDoc._schema_singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight); sortedDocsFunc = () => this.sortedDocs; - isContentActive = () => this.props.isSelected() || this.props.isContentActive(); - screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0); + isContentActive = () => this._props.isSelected() || this._props.isContentActive(); + screenToLocal = () => this._props.ScreenToLocalTransform().translate(-this.tableWidth, 0); previewWidthFunc = () => this.previewWidth; render() { return ( @@ -840,7 +838,7 @@ export class CollectionSchemaView extends CollectionSubView() { <div className="schema-table" style={{ width: `calc(100% - ${this.previewWidth}px)` }} - onWheel={e => this.props.isContentActive() && e.stopPropagation()} + onWheel={e => this._props.isContentActive() && e.stopPropagation()} ref={r => { // prevent wheel events from passively propagating up through containers r?.addEventListener('wheel', (e: WheelEvent) => {}, { passive: false }); @@ -866,7 +864,7 @@ export class CollectionSchemaView extends CollectionSubView() { openContextMenu={this.openContextMenu} dragColumn={this.dragColumn} setColRef={this.setColRef} - isContentActive={this.props.isContentActive} + isContentActive={this._props.isContentActive} /> ))} </div> @@ -892,16 +890,15 @@ export class CollectionSchemaView extends CollectionSubView() { {Array.from(this._selectedDocs).lastElement() && ( <DocumentView Document={Array.from(this._selectedDocs).lastElement()} - DataDoc={undefined} fitContentsToBox={returnTrue} dontCenter={'y'} onClickScriptDisable="always" focus={emptyFunction} defaultDoubleClick={returnIgnore} - renderDepth={this.props.renderDepth + 1} + renderDepth={this._props.renderDepth + 1} rootSelected={this.rootSelected} PanelWidth={this.previewWidthFunc} - PanelHeight={this.props.PanelHeight} + PanelHeight={this._props.PanelHeight} isContentActive={returnTrue} isDocumentActive={returnFalse} ScreenToLocalTransform={this.screenToLocal} @@ -910,12 +907,12 @@ export class CollectionSchemaView extends CollectionSubView() { searchFilterDocs={this.searchFilterDocs} styleProvider={DefaultStyleProvider} docViewPath={returnEmptyDoclist} - moveDocument={this.props.moveDocument} + moveDocument={this._props.moveDocument} addDocument={this.addRow} - removeDocument={this.props.removeDocument} + removeDocument={this._props.removeDocument} whenChildContentsActiveChanged={returnFalse} - addDocTab={this.props.addDocTab} - pinToPres={this.props.pinToPres} + addDocTab={this._props.addDocTab} + pinToPres={this._props.pinToPres} bringToFront={returnFalse} /> )} @@ -939,7 +936,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP return ( <div className="schema-table-content" ref={this.props.setRef} style={{ height: `calc(100% - ${CollectionSchemaView._newNodeInputHeight + this.props.rowHeight()}px)` }}> {this.props.childDocs().docs.map((doc: Doc, index: number) => ( - <div className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}> + <div key={doc[Id]} className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}> <CollectionSchemaViewDoc doc={doc} schema={this.props.schema} index={index} rowHeight={this.props.rowHeight} /> </div> ))} @@ -956,9 +953,14 @@ interface CollectionSchemaViewDocProps { } @observer -class CollectionSchemaViewDoc extends React.Component<CollectionSchemaViewDocProps> { - tableWidthFunc = () => this.props.schema.tableWidth; - screenToLocalXf = () => this.props.schema.props.ScreenToLocalTransform().translate(0, -this.props.rowHeight() - this.props.index * this.props.rowHeight()); +class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaViewDocProps> { + constructor(props: any) { + super(props); + makeObservable(this); + } + + tableWidthFunc = () => this._props.schema.tableWidth; + screenToLocalXf = () => this._props.schema._props.ScreenToLocalTransform().translate(0, -this._props.rowHeight() - this._props.index * this._props.rowHeight()); noOpacityStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => { if (property === StyleProp.Opacity) return 1; return DefaultStyleProvider(doc, props, property); @@ -966,31 +968,30 @@ class CollectionSchemaViewDoc extends React.Component<CollectionSchemaViewDocPro render() { return ( <DocumentView - key={this.props.doc[Id]} - {...this.props.schema.props} - LayoutTemplate={this.props.schema.props.childLayoutTemplate} - LayoutTemplateString={SchemaRowBox.LayoutString(this.props.schema.props.fieldKey, this.props.index)} - Document={this.props.doc} - DataDoc={undefined} - renderDepth={this.props.schema.props.renderDepth + 1} + key={this._props.doc[Id]} + {...this._props.schema._props} + LayoutTemplate={this._props.schema._props.childLayoutTemplate} + LayoutTemplateString={SchemaRowBox.LayoutString(this._props.schema._props.fieldKey, this._props.index)} + Document={this._props.doc} + renderDepth={this._props.schema._props.renderDepth + 1} PanelWidth={this.tableWidthFunc} - PanelHeight={this.props.rowHeight} + PanelHeight={this._props.rowHeight} styleProvider={this.noOpacityStyleProvider} waitForDoubleClickToClick={returnNever} defaultDoubleClick={returnIgnore} dragAction="move" onClickScriptDisable="always" - focus={this.props.schema.focusDocument} - childFilters={this.props.schema.childDocFilters} - childFiltersByRanges={this.props.schema.childDocRangeFilters} - searchFilterDocs={this.props.schema.searchFilterDocs} - rootSelected={this.props.schema.rootSelected} + focus={this._props.schema.focusDocument} + childFilters={this._props.schema.childDocFilters} + childFiltersByRanges={this._props.schema.childDocRangeFilters} + searchFilterDocs={this._props.schema.searchFilterDocs} + rootSelected={this._props.schema.rootSelected} ScreenToLocalTransform={this.screenToLocalXf} bringToFront={emptyFunction} dragWhenActive={true} - isDocumentActive={this.props.schema.props.childDocumentsActive?.() ? this.props.schema.props.isDocumentActive : this.props.schema.isContentActive} + isDocumentActive={this._props.schema._props.childDocumentsActive?.() ? this._props.schema._props.isDocumentActive : this._props.schema.isContentActive} isContentActive={emptyFunction} - whenChildContentsActiveChanged={this.props.schema.props.whenChildContentsActiveChanged} + whenChildContentsActiveChanged={this._props.schema._props.whenChildContentsActiveChanged} hideDecorations={true} hideTitle={true} hideDocumentButtonBar={true} diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx index 65e47f441..04443b4a7 100644 --- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx +++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx @@ -1,4 +1,4 @@ -import React = require('react'); +import * as React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -26,7 +26,7 @@ export interface SchemaColumnHeaderProps { export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> { @observable _ref: HTMLDivElement | null = null; - @computed get fieldKey() { + get fieldKey() { return this.props.columnKeys[this.props.columnIndex]; } diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 7346c4f12..5a3be826b 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -1,13 +1,13 @@ -import React = require('react'); import { IconButton, Size } from 'browndash-components'; -import { computed } from 'mobx'; +import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; +import * as React from 'react'; import { CgClose } from 'react-icons/cg'; import { FaExternalLinkAlt } from 'react-icons/fa'; +import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils'; import { Doc } from '../../../../fields/Doc'; import { BoolCast } from '../../../../fields/Types'; -import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; @@ -28,38 +28,42 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo public static LayoutString(fieldKey: string, rowIndex: number) { return FieldView.LayoutString(SchemaRowBox, fieldKey).replace('fieldKey', `rowIndex={${rowIndex}} fieldKey`); } - private _ref: HTMLDivElement | null = null; + constructor(props: any) { + super(props); + makeObservable(this); + } + bounds = () => this._ref?.getBoundingClientRect(); @computed get schemaView() { - return this.props.DocumentView?.().props.docViewPath().lastElement()?.ComponentView as CollectionSchemaView; + return this._props.DocumentView?.()._props.docViewPath().lastElement()?.ComponentView as CollectionSchemaView; } @computed get schemaDoc() { - return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc; + return this._props.DocumentView?.()._props.docViewPath().lastElement()?.Document; } @computed get rowIndex() { - return this.schemaView?.rowIndex(this.rootDoc) ?? -1; + return this.schemaView?.rowIndex(this.Document) ?? -1; } componentDidMount(): void { - this.props.setContentView?.(this); + this._props.setContentView?.(this); } select = (ctrlKey: boolean, shiftKey: boolean) => { if (!this.schemaView) return; const lastSelected = Array.from(this.schemaView._selectedDocs).lastElement(); - if (shiftKey && lastSelected) this.schemaView.selectRows(this.rootDoc, lastSelected); + if (shiftKey && lastSelected) this.schemaView.selectRows(this.Document, lastSelected); else { - this.props.select?.(ctrlKey); + this._props.select?.(ctrlKey); } }; onPointerEnter = (e: any) => { - if (SnappingManager.GetIsDragging() && this.props.isContentActive()) { + if (SnappingManager.IsDragging && this._props.isContentActive()) { document.removeEventListener('pointermove', this.onPointerMove); document.addEventListener('pointermove', this.onPointerMove); } @@ -103,18 +107,18 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo return ( <div className="schema-row" - style={{ height: this.props.PanelHeight(), backgroundColor: this.props.isSelected() ? Colors.LIGHT_BLUE : undefined }} + style={{ height: this._props.PanelHeight(), backgroundColor: this._props.isSelected() ? Colors.LIGHT_BLUE : undefined }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} ref={(row: HTMLDivElement | null) => { - row && this.schemaView?.addRowRef?.(this.rootDoc, row); + row && this.schemaView?.addRowRef?.(this.Document, row); this._ref = row; }}> <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth, - pointerEvents: !this.props.isContentActive() ? 'none' : undefined, + pointerEvents: !this._props.isContentActive() ? 'none' : undefined, }}> <IconButton tooltip="close" @@ -128,7 +132,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo emptyFunction, undoable(e => { e.stopPropagation(); - this.props.removeDocument?.(this.rootDoc); + this._props.removeDocument?.(this.Document); }, 'Delete Row') ) } @@ -145,7 +149,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo emptyFunction, undoable(e => { e.stopPropagation(); - this.props.addDocTab(this.rootDoc, OpenWhere.addRight); + this._props.addDocTab(this.Document, OpenWhere.addRight); }, 'Open schema Doc preview') ) } @@ -155,13 +159,13 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo {this.schemaView?.columnKeys?.map((key, index) => ( <SchemaTableCell key={key} - Document={this.rootDoc} + Document={this.Document} col={index} fieldKey={key} allowCRs={false} // to enter text with new lines, must use \n columnWidth={this.columnWidth(index)} rowHeight={this.schemaView.rowHeightFunc} - isRowActive={this.props.isContentActive} + isRowActive={this._props.isContentActive} getFinfo={this.getFinfo} selectCell={this.selectCell} deselectCell={this.deselectCell} @@ -172,7 +176,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo transform={() => { const ind = index === this.schemaView.columnKeys.length - 1 ? this.schemaView.columnKeys.length - 3 : index; const x = this.schemaView?.displayColumnWidths.reduce((p, c, i) => (i <= ind ? p + c : p), 0); - const y = (this.props.rowIndex ?? 0) * this.props.PanelHeight(); + const y = (this._props.rowIndex ?? 0) * this._props.PanelHeight(); return new Transform(x + CollectionSchemaView._rowMenuWidth, y, 1); }} /> diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 9d5b533d1..85269028b 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -1,27 +1,28 @@ -import * as React from 'react'; -import Select, { MenuPlacement } from 'react-select'; -import { action, computed, observable } from 'mobx'; +import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import { extname } from 'path'; +import * as React from 'react'; import DatePicker from 'react-datepicker'; +import Select from 'react-select'; +import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils'; import { DateField } from '../../../../fields/DateField'; import { Doc, DocListCast, Field } from '../../../../fields/Doc'; import { RichTextField } from '../../../../fields/RichTextField'; import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils'; import { FInfo } from '../../../documents/Documents'; import { DocFocusOrOpen } from '../../../util/DocumentManager'; import { Transform } from '../../../util/Transform'; -import { undoable, undoBatch } from '../../../util/UndoManager'; +import { undoBatch, undoable } from '../../../util/UndoManager'; import { EditableView } from '../../EditableView'; +import { ObservableReactComponent } from '../../ObservableReactComponent'; +import { DefaultStyleProvider } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; import { OpenWhere } from '../../nodes/DocumentView'; -import { FieldView, FieldViewProps } from '../../nodes/FieldView'; -import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; +import { FieldViewProps } from '../../nodes/FieldView'; import { KeyValueBox } from '../../nodes/KeyValueBox'; -import { DefaultStyleProvider } from '../../StyleProvider'; -import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView'; +import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; +import { ColumnType, FInfotoColType } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; export interface SchemaTableCellProps { @@ -47,7 +48,12 @@ export interface SchemaTableCellProps { } @observer -export class SchemaTableCell extends React.Component<SchemaTableCellProps> { +export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellProps> { + constructor(props: any) { + super(props); + makeObservable(this); + } + static addFieldDoc = (doc: Doc, where: OpenWhere) => { DocFocusOrOpen(doc); return true; @@ -72,7 +78,6 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { searchFilterDocs: returnEmptyDoclist, styleProvider: DefaultStyleProvider, docViewPath: returnEmptyDoclist, - rootSelected: returnFalse, isSelected: returnFalse, setHeight: returnFalse, select: emptyFunction, @@ -97,12 +102,12 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { } @computed get selected() { - const selected: [Doc, number] | undefined = this.props.selectedCell(); - return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col; + const selected: [Doc, number] | undefined = this._props.selectedCell(); + return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col; } @computed get defaultCellContent() { - const { color, textDecoration, fieldProps } = SchemaTableCell.renderProps(this.props); + const { color, textDecoration, fieldProps, pointerEvents } = SchemaTableCell.renderProps(this._props); return ( <div @@ -111,19 +116,21 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { color, textDecoration, width: '100%', + pointerEvents, }}> <EditableView - oneLine={this.props.oneLine} - allowCRs={this.props.allowCRs} - contents={<FieldView {...fieldProps} />} + oneLine={this._props.oneLine} + allowCRs={this._props.allowCRs} + contents={undefined} + fieldContents={fieldProps} editing={this.selected ? undefined : false} - GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} + GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)} SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { - this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value); + this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value); } - const ret = KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value); - this.props.finishEdit?.(); + const ret = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value); + this._props.finishEdit?.(); return ret; }, 'edit schema cell')} /> @@ -132,8 +139,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { } get getCellType() { - const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType; - const cellValue = this.props.Document[this.props.fieldKey]; + const columnTypeStr = this._props.getFinfo(this._props.fieldKey)?.fieldType; + const cellValue = this._props.Document[this._props.fieldKey]; if (cellValue instanceof ImageField) return ColumnType.Image; if (cellValue instanceof DateField) return ColumnType.Date; if (cellValue instanceof RichTextField) return ColumnType.RTF; @@ -152,11 +159,11 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { const cellType: ColumnType = this.getCellType; // prettier-ignore switch (cellType) { - case ColumnType.Image: return <SchemaImageCell {...this.props} />; - case ColumnType.Boolean: return <SchemaBoolCell {...this.props} />; - case ColumnType.RTF: return <SchemaRTFCell {...this.props} />; - case ColumnType.Enumeration: return <SchemaEnumerationCell {...this.props} options={this.props.getFinfo(this.props.fieldKey)?.values?.map(val => val.toString())} />; - case ColumnType.Date: // return <SchemaDateCell {...this.props} />; + case ColumnType.Image: return <SchemaImageCell {...this._props} />; + case ColumnType.Boolean: return <SchemaBoolCell {...this._props} />; + case ColumnType.RTF: return <SchemaRTFCell {...this._props} />; + case ColumnType.Enumeration: return <SchemaEnumerationCell {...this._props} options={this._props.getFinfo(this._props.fieldKey)?.values?.map(val => val.toString())} />; + case ColumnType.Date: // return <SchemaDateCell {...this._props} />; default: return this.defaultCellContent; } } @@ -165,8 +172,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { return ( <div className="schema-table-cell" - onPointerDown={action(e => !this.selected && this.props.selectCell(this.props.Document, this.props.col))} - style={{ padding: this.props.padding, maxWidth: this.props.maxWidth?.(), width: this.props.columnWidth() || undefined, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}> + onPointerDown={action(e => !this.selected && this._props.selectCell(this._props.Document, this._props.col))} + style={{ padding: this._props.padding, maxWidth: this._props.maxWidth?.(), width: this._props.columnWidth() || undefined, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}> {this.content} </div> ); @@ -175,8 +182,13 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { // mj: most of this is adapted from old schema code so I'm not sure what it does tbh @observer -export class SchemaImageCell extends React.Component<SchemaTableCellProps> { - @observable _previewRef: HTMLImageElement | undefined; +export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellProps> { + constructor(props: any) { + super(props); + makeObservable(this); + } + + @observable _previewRef: HTMLImageElement | undefined = undefined; choosePath(url: URL) { if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href @@ -188,8 +200,8 @@ export class SchemaImageCell extends React.Component<SchemaTableCellProps> { } get url() { - const field = Cast(this.props.Document[this.props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc - const alts = DocListCast(this.props.Document[this.props.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images + const field = Cast(this._props.Document[this._props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc + const alts = DocListCast(this._props.Document[this._props.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images const altpaths = alts .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url) .filter(url => url) @@ -226,10 +238,10 @@ export class SchemaImageCell extends React.Component<SchemaTableCellProps> { }; render() { - const aspect = Doc.NativeAspect(this.props.Document); // aspect ratio - // let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px + const aspect = Doc.NativeAspect(this._props.Document); // aspect ratio + // let width = Math.max(75, this._props.columnWidth); // get a with that is no smaller than 75px // const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px - const height = this.props.rowHeight() ? this.props.rowHeight() - (this.props.padding || 6) * 2 : undefined; + const height = this._props.rowHeight() ? this._props.rowHeight() - (this._props.padding || 6) * 2 : undefined; const width = height ? height * aspect : undefined; // increase the width of the image if necessary to maintain proportionality return <img src={this.url} width={width ? width : undefined} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />; @@ -237,22 +249,26 @@ export class SchemaImageCell extends React.Component<SchemaTableCellProps> { } @observer -export class SchemaDateCell extends React.Component<SchemaTableCellProps> { - @observable _pickingDate: boolean = false; +export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProps> { + constructor(props: any) { + super(props); + makeObservable(this); + } + @observable _pickingDate: boolean = false; @computed get date(): DateField { // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined. - return DateCast(this.props.Document[this.props.fieldKey]); + return DateCast(this._props.Document[this._props.fieldKey]); } @action handleChange = (date: any) => { // const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } }); // if (script.compiled) { - // this.applyToDoc(this._document, this.props.row, this.props.col, script.run); + // this.applyToDoc(this._document, this._props.row, this._props.col, script.run); // } else { // ^ DateCast is always undefined for some reason, but that is what the field should be set to - this.props.Document[this.props.fieldKey] = new DateField(date as Date); + this._props.Document[this._props.fieldKey] = new DateField(date as Date); //} }; @@ -261,53 +277,64 @@ export class SchemaDateCell extends React.Component<SchemaTableCellProps> { } } @observer -export class SchemaRTFCell extends React.Component<SchemaTableCellProps> { +export class SchemaRTFCell extends ObservableReactComponent<SchemaTableCellProps> { + constructor(props: any) { + super(props); + makeObservable(this); + } + @computed get selected() { - const selected: [Doc, number] | undefined = this.props.selectedCell(); - return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col; + const selected: [Doc, number] | undefined = this._props.selectedCell(); + return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col; } selectedFunc = () => this.selected; render() { - const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props); + const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props); fieldProps.isContentActive = this.selectedFunc; return ( <div className="schemaRTFCell" style={{ display: 'flex', fontStyle: this.selected ? undefined : 'italic', width: '100%', height: '100%', position: 'relative', color, textDecoration, cursor, pointerEvents }}> - {this.selected ? <FormattedTextBox {...fieldProps} DataDoc={this.props.Document} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))} + {this.selected ? <FormattedTextBox {...fieldProps} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))} </div> ); } } @observer -export class SchemaBoolCell extends React.Component<SchemaTableCellProps> { +export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProps> { + constructor(props: any) { + super(props); + makeObservable(this); + } + @computed get selected() { - const selected: [Doc, number] | undefined = this.props.selectedCell(); - return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col; + const selected: [Doc, number] | undefined = this._props.selectedCell(); + return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col; } render() { - const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props); + const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props); return ( <div className="schemaBoolCell" style={{ display: 'flex', color, textDecoration, cursor, pointerEvents }}> <input style={{ marginRight: 4 }} type="checkbox" - checked={BoolCast(this.props.Document[this.props.fieldKey])} + checked={BoolCast(this._props.Document[this._props.fieldKey])} onChange={undoBatch((value: React.ChangeEvent<HTMLInputElement> | undefined) => { if ((value?.nativeEvent as any).shiftKey) { - this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString()); + this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString()); } - KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString()); + KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString()); })} /> <EditableView - contents={<FieldView {...fieldProps} />} + contents={undefined} + fieldContents={fieldProps} editing={this.selected ? undefined : false} - GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} + GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)} SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { - this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value); + this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value); } - const set = KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value); - this.props.finishEdit?.(); + const set = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value); + this._props.finishEdit?.(); return set; })} /> @@ -316,14 +343,19 @@ export class SchemaBoolCell extends React.Component<SchemaTableCellProps> { } } @observer -export class SchemaEnumerationCell extends React.Component<SchemaTableCellProps> { +export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableCellProps> { + constructor(props: any) { + super(props); + makeObservable(this); + } + @computed get selected() { - const selected: [Doc, number] | undefined = this.props.selectedCell(); - return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col; + const selected: [Doc, number] | undefined = this._props.selectedCell(); + return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col; } render() { - const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props); - const options = this.props.options?.map(facet => ({ value: facet, label: facet })); + const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props); + const options = this._props.options?.map(facet => ({ value: facet, label: facet })); return ( <div className="schemaSelectionCell" style={{ color, textDecoration, cursor, pointerEvents }}> <div style={{ width: '100%' }}> @@ -357,17 +389,17 @@ export class SchemaEnumerationCell extends React.Component<SchemaTableCellProps> ...base, left: 0, top: 0, - transform: `translate(${this.props.transform().TranslateX}px, ${this.props.transform().TranslateY}px)`, - width: Number(base.width) * this.props.transform().Scale, + transform: `translate(${this._props.transform().TranslateX}px, ${this._props.transform().TranslateY}px)`, + width: Number(base.width) * this._props.transform().Scale, zIndex: 9999, }), }} - menuPortalTarget={this.props.menuTarget} + menuPortalTarget={this._props.menuTarget} menuPosition={'absolute'} - placeholder={StrCast(this.props.Document[this.props.fieldKey], 'select...')} + placeholder={StrCast(this._props.Document[this._props.fieldKey], 'select...')} options={options} isMulti={false} - onChange={val => KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)} + onChange={val => KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)} /> </div> </div> |
