diff options
Diffstat (limited to 'src/client/views/collections/collectionSchema')
5 files changed, 480 insertions, 331 deletions
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss index 9768877ff..6fb8e40db 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss @@ -176,12 +176,12 @@ } } - .schema-column-resizer.left { + /*.schema-column-resizer.left { min-width: 5px; transform: translate(-3px, 0px); align-self: flex-start; background-color: $medium-gray; - } + }*/ // creates awkward thick gray borders between colheaders } .schema-header-menu { diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 6a956f2ac..7c2cfd15f 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,45 +1,38 @@ +/* eslint-disable no-restricted-syntax */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Popup, PopupTrigger, Type } from 'browndash-components'; -import { ObservableMap, action, computed, makeObservable, observable, observe } from 'mobx'; +import { ObservableMap, action, computed, makeObservable, observable, observe, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { emptyFunction, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils'; -import { Doc, DocListCast, Field, NumListCast, Opt, StrListCast } from '../../../../fields/Doc'; +import { returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../ClientUtils'; +import { emptyFunction } from '../../../../Utils'; +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'; -import { listSpec } from '../../../../fields/Schema'; -import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { DocUtils, Docs, DocumentOptions, FInfo } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; -import { DragManager, dropActionType } from '../../../util/DragManager'; -import { SelectionManager } from '../../../util/SelectionManager'; +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 { DragManager } from '../../../util/DragManager'; +import { dropActionType } from '../../../util/DropActionTypes'; import { SettingsManager } from '../../../util/SettingsManager'; import { undoBatch, undoable } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { EditableView } from '../../EditableView'; import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { DefaultStyleProvider, StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; +import { DefaultStyleProvider } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../../nodes/DocumentView'; -import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView'; -import { KeyValueBox } from '../../nodes/KeyValueBox'; +import { FieldViewProps } from '../../nodes/FieldView'; +import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { CollectionSubView } from '../CollectionSubView'; import './CollectionSchemaView.scss'; import { SchemaColumnHeader } from './SchemaColumnHeader'; import { SchemaRowBox } from './SchemaRowBox'; -const { SCHEMA_NEW_NODE_HEIGHT } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore -export enum ColumnType { - Number, - String, - Boolean, - Date, - Image, - RTF, - Enumeration, - Any, -} +const { SCHEMA_NEW_NODE_HEIGHT } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore export const FInfotoColType: { [key: string]: ColumnType } = { string: ColumnType.String, @@ -56,7 +49,6 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'author_date', ' @observer export class CollectionSchemaView extends CollectionSubView() { private _keysDisposer: any; - private _closestDropIndex: number = 0; private _previewRef: HTMLDivElement | null = null; private _makeNewColumn: boolean = false; private _documentOptions: DocumentOptions = new DocumentOptions(); @@ -88,7 +80,13 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _menuValue: string = ''; @observable _filterColumnIndex: number | undefined = undefined; @observable _filterSearchValue: string = ''; - @observable _selectedCell: [Doc, number] | undefined = undefined; + @observable _selectedCol: number = 0; + @observable _selectedCells: Array<Doc> = []; + @observable _mouseCoordinates = { x: 0, y: 0 }; + @observable _lowestSelectedIndex = -1; // lowest index among selected rows; used to properly sync dragged docs with cursor position + @observable _relCursorIndex = -1; // cursor index relative to the current selected cells + @observable _draggedColIndex = 0; + @observable _colBeingDragged = false; // target HTMLelement portal for showing a popup menu to edit cell values. public get MenuTarget() { @@ -96,14 +94,14 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get _selectedDocs() { - const selected = SelectionManager.Docs.filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document)); + // get all selected documents then filter out any whose parent is not this schema document + const selected = DocumentView.SelectedDocs().filter(doc => this.childDocs.includes(doc)); if (!selected.length) { - for (const sel of SelectionManager.Docs) { - const contextPath = DocumentManager.GetContextPath(sel, true); - if (contextPath.includes(this.Document)) { - const parentInd = contextPath.indexOf(this.Document); - return parentInd < contextPath.length - 1 ? [contextPath[parentInd + 1]] : []; - } + // if no schema doc is directly selected, test if a child of a schema doc is selected (such as in the preview window) + const childOfSchemaDoc = DocumentView.SelectedDocs().find(sel => DocumentView.getContextPath(sel, true).includes(this.Document)); + if (childOfSchemaDoc) { + const contextPath = DocumentView.getContextPath(childOfSchemaDoc, true); + return [contextPath[contextPath.indexOf(childOfSchemaDoc) - 1]]; // the schema doc that is "selected" by virtue of one of its children being selected } } return selected; @@ -122,7 +120,7 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get columnKeys() { - return Cast(this.layoutDoc.schema_columnKeys, listSpec('string'), defaultColumnKeys); + return StrListCast(this.layoutDoc.schema_columnKeys, defaultColumnKeys); } @computed get storedColumnWidths() { @@ -132,12 +130,17 @@ export class CollectionSchemaView extends CollectionSubView() { ); const totalWidth = widths.reduce((sum, width) => sum + width, 0); + // If the total width of all columns is not the width of the schema table minus the width of the row menu, resize them appropriately if (totalWidth !== this.tableWidth - CollectionSchemaView._rowMenuWidth) { return widths.map(w => (w / totalWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)); } return widths; } + @computed get rowHeights() { + return this.childDocs.map(() => this.rowHeightFunc()); + } + @computed get displayColumnWidths() { return this._displayColumnWidths ?? this.storedColumnWidths; } @@ -157,8 +160,8 @@ export class CollectionSchemaView extends CollectionSubView() { Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1])); this._keysDisposer = observe( this.dataDoc[this.fieldKey ?? 'data'] as List<Doc>, - change => { - switch (change.type as any) { + (change: any) => { + switch (change.type) { case 'splice': // prettier-ignore (change as any).added.forEach((doc: Doc) => // for each document added @@ -166,7 +169,9 @@ export class CollectionSchemaView extends CollectionSubView() { 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("-no description-", 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 + case 'update': // let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list + break; + default: } }, true @@ -178,6 +183,9 @@ export class CollectionSchemaView extends CollectionSubView() { document.removeEventListener('keydown', this.onKeyDown); } + // ViewBoxInterface overrides + override isUnstyledView = returnTrue; // used by style provider : turns off opacity, animation effects, scaling + rowIndex = (doc: Doc) => this.sortedDocs.docs.indexOf(doc); @action @@ -190,13 +198,12 @@ export class CollectionSchemaView extends CollectionSubView() { const lastIndex = this.rowIndex(lastDoc); const curDoc = this.sortedDocs.docs[lastIndex]; if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) { - !e.shiftKey && this.clearSelection(); const newDoc = this.sortedDocs.docs[lastIndex + 1]; if (this._selectedDocs.includes(newDoc)) { - SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)); + DocumentView.DeselectView(DocumentView.getFirstDocumentView(curDoc)); + this.deselectCell(curDoc); } else { - this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1); - this._selectedCell && (this._selectedCell[0] = newDoc); + this.selectCell(newDoc, this._selectedCol, e.shiftKey, e.ctrlKey); this.scrollToDoc(newDoc, {}); } } @@ -210,12 +217,12 @@ export class CollectionSchemaView extends CollectionSubView() { const firstIndex = this.rowIndex(firstDoc); const curDoc = this.sortedDocs.docs[firstIndex]; if (firstIndex > 0 && firstIndex < this.childDocs.length) { - !e.shiftKey && this.clearSelection(); const newDoc = this.sortedDocs.docs[firstIndex - 1]; - if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)); - else { - this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1); - this._selectedCell && (this._selectedCell[0] = newDoc); + if (this._selectedDocs.includes(newDoc)) { + DocumentView.DeselectView(DocumentView.getFirstDocumentView(curDoc)); + this.deselectCell(curDoc); + } else { + this.selectCell(newDoc, this._selectedCol, e.shiftKey, e.ctrlKey); this.scrollToDoc(newDoc, {}); } } @@ -224,17 +231,17 @@ export class CollectionSchemaView extends CollectionSubView() { } break; case 'ArrowRight': - if (this._selectedCell) { - this._selectedCell[1] = Math.min(this._selectedCell[1] + 1, this.columnKeys.length - 1); + if (this._selectedCells) { + this._selectedCol = Math.min(this._colEles.length - 1, this._selectedCol + 1); } else if (this._selectedDocs.length > 0) { - this.selectCell(this._selectedDocs[0], 0); + this.selectCell(this._selectedDocs[0], 0, false, false); } break; case 'ArrowLeft': - if (this._selectedCell) { - this._selectedCell[1] = Math.max(this._selectedCell[1] - 1, 0); + if (this._selectedCells) { + this._selectedCol = Math.max(0, this._selectedCol - 1); } else if (this._selectedDocs.length > 0) { - this.selectCell(this._selectedDocs[0], 0); + this.selectCell(this._selectedDocs[0], 0, false, false); } break; case 'Backspace': { @@ -242,12 +249,17 @@ export class CollectionSchemaView extends CollectionSubView() { break; } case 'Escape': { - this.deselectCell(); + this.deselectAllCells(); + break; } + default: } } }; + @action + changeSelectedCellColumn = () => {}; + @undoBatch setColumnSort = (field: string | undefined, desc: boolean = false) => { this.layoutDoc.sortField = field; @@ -262,7 +274,7 @@ export class CollectionSchemaView extends CollectionSubView() { this.addNewKey(newKey, defaultVal); } - let currKeys = [...this.columnKeys]; + const currKeys = this.columnKeys.slice(); // copy the column key array first, then change it. currKeys[index] = newKey; this.layoutDoc.schema_columnKeys = new List<string>(currKeys); }; @@ -279,13 +291,16 @@ export class CollectionSchemaView extends CollectionSubView() { const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0); this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth))); - let currKeys = this.columnKeys.slice(); + const currKeys = this.columnKeys.slice(); currKeys.splice(0, 0, key); this.layoutDoc.schema_columnKeys = new List<string>(currKeys); }; @action - addNewKey = (key: string, defaultVal: any) => this.childDocs.forEach(doc => (doc[DocData][key] = defaultVal)); + addNewKey = (key: string, defaultVal: any) => + this.childDocs.forEach(doc => { + doc[DocData][key] = defaultVal; + }); @undoBatch removeColumn = (index: number) => { @@ -295,7 +310,7 @@ export class CollectionSchemaView extends CollectionSubView() { const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0); this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth))); - let currKeys = this.columnKeys.slice(); + const currKeys = this.columnKeys.slice(); currKeys.splice(index, 1); this.layoutDoc.schema_columnKeys = new List<string>(currKeys); }; @@ -303,7 +318,7 @@ export class CollectionSchemaView extends CollectionSubView() { @action startResize = (e: any, index: number) => { this._displayColumnWidths = this.storedColumnWidths; - setupMoveUpEvents(this, e, (e, delta) => this.resizeColumn(e, index), this.finishResize, emptyFunction); + setupMoveUpEvents(this, e, moveEv => this.resizeColumn(moveEv, index), this.finishResize, emptyFunction); }; @action @@ -342,34 +357,32 @@ export class CollectionSchemaView extends CollectionSubView() { @undoBatch moveColumn = (fromIndex: number, toIndex: number) => { - let currKeys = this.columnKeys.slice(); + if (this._selectedCol === fromIndex) this._selectedCol = toIndex; + else if (toIndex === this._selectedCol) this._selectedCol = fromIndex; // keeps selected cell consistent + + const currKeys = this.columnKeys.slice(); currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]); this.layoutDoc.schema_columnKeys = new List<string>(currKeys); - let currWidths = this.storedColumnWidths.slice(); + const currWidths = this.storedColumnWidths.slice(); currWidths.splice(toIndex, 0, currWidths.splice(fromIndex, 1)[0]); this.layoutDoc.schema_columnWidths = new List<number>(currWidths); + + this._draggedColIndex = toIndex; }; @action dragColumn = (e: PointerEvent, index: number) => { + this._draggedColIndex = index; + this._colBeingDragged = 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])); DragManager.StartColumnDrag(dragEles, dragData, e.x, e.y); - - document.removeEventListener('pointermove', this.highlightDropColumn); - document.addEventListener('pointermove', this.highlightDropColumn); - let stopHighlight = (e: PointerEvent) => { - document.removeEventListener('pointermove', this.highlightDropColumn); - document.removeEventListener('pointerup', stopHighlight); - }; - document.addEventListener('pointerup', stopHighlight); - return true; }; - findDropIndex = (mouseX: number) => { + findColDropIndex = (mouseX: number) => { let index: number | undefined; this.displayColumnWidths.reduce((total, curr, i) => { if (total <= mouseX && total + curr >= mouseX) { @@ -377,28 +390,72 @@ export class CollectionSchemaView extends CollectionSubView() { else index = i + 1; } return total + curr; - }, CollectionSchemaView._rowMenuWidth); + }, 2 * CollectionSchemaView._rowMenuWidth); // probably prone to issues; find better implementation (!!!) return index; }; + /** + * Calculates the relative index of the cursor in the group of selected rows, ie. + * if five rows are selected and the cursor is in the middle row, its relative index would be 2. + * Used to align actively dragged documents properly with the cursor. + * @param mouseY the initial Y position of the cursor on drag + */ @action - highlightDropColumn = (e: PointerEvent) => { - e.stopPropagation(); - const mouseX = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY)[0]; - const index = this.findDropIndex(mouseX); + setRelCursorIndex = (mouseY: number) => { + this._mouseCoordinates.y = mouseY; // updates this.rowDropIndex computed value to overwrite the old cached value + + const rowHeight = CollectionSchemaView._rowHeight; + const adjInitMouseY = mouseY - rowHeight - 100; // rowHeight: height of the column menu cells | 100: height of the top menu + const yOffset = this._lowestSelectedIndex * rowHeight; + + const heights = this._selectedDocs.map(() => this.rowHeightFunc()); + let index: number = 0; + heights.reduce((total, curr, i) => { + if (total <= adjInitMouseY && total + curr >= adjInitMouseY) { + if (adjInitMouseY <= total + curr) index = i; + else index = i + 1; + } + return total + curr; + }, yOffset); + this._relCursorIndex = index; + }; + + findRowDropIndex = (mouseY: number) => { + const rowHeight = CollectionSchemaView._rowHeight; + let index: number = 0; + this.rowHeights.reduce((total, curr, i) => { + if (total <= mouseY && total + curr >= mouseY) { + if (mouseY <= total + curr) index = i; + else index = i + 1; + } + return total + curr; + }, rowHeight); + + // fix index if selected rows are dragged out of bounds + let adjIndex = index - this._relCursorIndex; + const maxY = this.rowHeights.reduce((total, curr) => total + curr, 0) + rowHeight; + if (mouseY > maxY) adjIndex = this.childDocs.length - 1; + else if (adjIndex <= 0) adjIndex = 0; + + return adjIndex; + }; + + highlightDraggedColumn = (index: number) => this._colEles.forEach((colRef, i) => { - let leftStyle = ''; - let rightStyle = ''; - if (i + 1 === index) rightStyle = `solid 12px ${Colors.MEDIUM_BLUE}`; - if (i === index && i === 0) leftStyle = `solid 12px ${Colors.MEDIUM_BLUE}`; - colRef.style.borderLeft = leftStyle; - colRef.style.borderRight = rightStyle; - this.childDocs.forEach(doc => { - this._rowEles.get(doc).children[1].children[i].style.borderLeft = leftStyle; - this._rowEles.get(doc).children[1].children[i].style.borderRight = rightStyle; + const edgeStyle = i === index ? `solid 2px ${Colors.MEDIUM_BLUE}` : ''; + const cellEles = [ + colRef, + ...this.childDocs // + .filter(doc => i !== this._selectedCol || !this._selectedDocs.includes(doc)) + .map(doc => this._rowEles.get(doc).children[1].children[i]), + ]; + cellEles[0].style.borderTop = edgeStyle; + cellEles.forEach(ele => { + ele.style.borderLeft = edgeStyle; + ele.style.borderRight = edgeStyle; }); + cellEles.slice(-1)[0].style.borderBottom = edgeStyle; }); - }; @action addRowRef = (doc: Doc, ref: HTMLDivElement) => this._rowEles.set(doc, ref); @@ -413,13 +470,16 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - addDocToSelection = (doc: Doc, extendSelection: boolean, index: number) => { - const rowDocView = DocumentManager.Instance.getDocumentView(doc); - if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection); + addDocToSelection = (doc: Doc, extendSelection: boolean) => { + const rowDocView = DocumentView.getDocumentView(doc); + if (rowDocView) DocumentView.SelectView(rowDocView, extendSelection); }; @action - clearSelection = () => SelectionManager.DeselectAll(); + clearSelection = () => { + DocumentView.DeselectAll(); + this.deselectAllCells(); + }; selectRows = (doc: Doc, lastSelected: Doc) => { const index = this.rowIndex(doc); @@ -428,63 +488,97 @@ export class CollectionSchemaView extends CollectionSubView() { const endRow = Math.max(lastSelectedRow, index); for (let i = startRow; i <= endRow; i++) { const currDoc = this.sortedDocs.docs[i]; - if (!this._selectedDocs.includes(currDoc)) this.addDocToSelection(currDoc, true, i); + if (!this._selectedDocs.includes(currDoc)) { + this.selectCell(currDoc, this._selectedCol, false, true); + } } }; @action - selectCell = (doc: Doc, index: number) => (this._selectedCell = [doc, index]); + selectCell = (doc: Doc, col: number, shiftKey: boolean, ctrlKey: boolean) => { + if (!shiftKey && !ctrlKey) this.clearSelection(); + !this._selectedCells && (this._selectedCells = []); + !shiftKey && this._selectedCells && this._selectedCells.push(doc); + const index = this.rowIndex(doc); + + if (!this) return; + const lastSelected = Array.from(this._selectedDocs).lastElement(); + if (shiftKey && lastSelected && !this._selectedDocs.includes(doc)) this.selectRows(doc, lastSelected); + else if (ctrlKey) { + if (lastSelected && this._selectedDocs.includes(doc)) { + DocumentView.DeselectView(DocumentView.getFirstDocumentView(doc)); + this.deselectCell(doc); + } else this.addDocToSelection(doc, true); + } else this.addDocToSelection(doc, false); + this._selectedCol = col; + + if (this._lowestSelectedIndex === -1 || index < this._lowestSelectedIndex) this._lowestSelectedIndex = index; + + // let selectedIndexes: Array<Number> = this._selectedCells.map(doc => this.rowIndex(doc)); + }; @action - deselectCell = () => (this._selectedCell = undefined); + deselectCell = (doc: Doc) => { + this._selectedCells && (this._selectedCells = this._selectedCells.filter(d => d !== doc)); + if (this.rowIndex(doc) === this._lowestSelectedIndex) this._lowestSelectedIndex = Math.min(...this._selectedDocs.map(d => this.rowIndex(d))); + }; + + @action + deselectAllCells = () => { + this._selectedCells = []; + this._lowestSelectedIndex = -1; + }; sortedSelectedDocs = () => this.sortedDocs.docs.filter(doc => this._selectedDocs.includes(doc)); - setDropIndex = (index: number) => (this._closestDropIndex = index); + @computed + get rowDropIndex() { + const mouseY = this.ScreenToLocalBoxXf().transformPoint(this._mouseCoordinates.x, this._mouseCoordinates.y)[1]; + return this.findRowDropIndex(mouseY); + } onInternalDrop = (e: Event, de: DragManager.DropEvent) => { if (de.complete.columnDragData) { - const mouseX = this.ScreenToLocalBoxXf().transformPoint(de.x, de.y)[0]; - const index = this.findDropIndex(mouseX); - this.moveColumn(de.complete.columnDragData.colIndex, index ?? de.complete.columnDragData.colIndex); + this._colBeingDragged = false; + e.stopPropagation(); this._colEles.forEach((colRef, i) => { + // style for menu cell colRef.style.borderLeft = ''; colRef.style.borderRight = ''; + colRef.style.borderTop = ''; + this.childDocs.forEach(doc => { - this._rowEles.get(doc).children[1].children[i].style.borderLeft = ''; - this._rowEles.get(doc).children[1].children[i].style.borderRight = ''; + if (!(this._selectedDocs.includes(doc) && i === this._selectedCol)) { + this._rowEles.get(doc).children[1].children[i].style.borderLeft = ''; + this._rowEles.get(doc).children[1].children[i].style.borderRight = ''; + this._rowEles.get(doc).children[1].children[i].style.borderBottom = ''; + } }); }); - - e.stopPropagation(); return true; } + const draggedDocs = de.complete.docDragData?.draggedDocuments; if (draggedDocs && super.onInternalDrop(e, de) && !this.sortField) { - const pushedDocs = this.childDocs.filter((doc, index) => index >= this._closestDropIndex && !draggedDocs.includes(doc)); - const pushedAndDraggedDocs = [...pushedDocs, ...draggedDocs]; - const removed = this.childDocs.slice().filter(doc => !pushedAndDraggedDocs.includes(doc)); - this.dataDoc[this.fieldKey ?? 'data'] = new List<Doc>([...removed, ...draggedDocs, ...pushedDocs]); + const map = draggedDocs?.map(doc => this.rowIndex(doc)); + console.log(map); + this.dataDoc[this.fieldKey ?? 'data'] = new List<Doc>([...this.sortedDocs.docs]); this.clearSelection(); draggedDocs.forEach(doc => { - const draggedView = DocumentManager.Instance.getFirstDocumentView(doc); - if (draggedView) DocumentManager.Instance.RemoveView(draggedView); - DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true)); + DocumentView.addViewRenderedCb(doc, dv => dv.select(true)); }); + this._lowestSelectedIndex = Math.min(...(draggedDocs?.map(doc => this.rowIndex(doc)) ?? [])); return true; } return false; }; - onExternalDrop = async (e: React.DragEvent): Promise<void> => { - super.onExternalDrop(e, {}, undoBatch(action(docus => docus.map((doc: Doc) => this.addDocument(doc))))); - }; - + onExternalDrop = (e: React.DragEvent) => super.onExternalDrop(e, {}, docs => docs.map(doc => this.addDocument(doc))); onDividerDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, emptyFunction); @action - onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => { + onDividerMove = (e: PointerEvent) => { const nativeWidth = this._previewRef!.getBoundingClientRect(); const minWidth = 40; const maxWidth = 1000; @@ -514,37 +608,76 @@ export class CollectionSchemaView extends CollectionSubView() { const rect = found.getBoundingClientRect(); const localRect = this.ScreenToLocalBoxXf().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; + const focusSpeed = options.zoomTime ?? 50; smoothScroll(focusSpeed, this._tableContentRef!, localRect.y + this._tableContentRef!.scrollTop - this.rowHeightFunc(), options.easeFunc); return focusSpeed; } } + return undefined; }; @computed get fieldDefaultInput() { switch (this._newFieldType) { case ColumnType.Number: - return <input type="number" name="" id="" value={this._newFieldDefault ?? 0} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />; + return ( + <input + type="number" + name="" + id="" + value={this._newFieldDefault ?? 0} + onPointerDown={e => e.stopPropagation()} + onChange={action((e: any) => { + this._newFieldDefault = e.target.value; + })} + /> + ); case ColumnType.Boolean: return ( <> - <input type="checkbox" name="" id="" value={this._newFieldDefault} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.checked))} /> + <input + type="checkbox" + name="" + id="" + value={this._newFieldDefault} + onPointerDown={e => e.stopPropagation()} + onChange={action((e: any) => { + this._newFieldDefault = e.target.checked; + })} + /> {this._newFieldDefault ? 'true' : 'false'} </> ); case ColumnType.String: - return <input type="text" name="" id="" value={this._newFieldDefault ?? ''} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />; + return ( + <input + type="text" + name="" + id="" + value={this._newFieldDefault ?? ''} + onPointerDown={e => e.stopPropagation()} + onChange={action((e: any) => { + this._newFieldDefault = e.target.value; + })} + /> + ); + default: + return undefined; } } onSearchKeyDown = (e: React.KeyboardEvent) => { switch (e.key) { case 'Enter': - this._menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuKeys[0]) : action(() => (this._makeNewField = true))(); + this._menuKeys.length > 0 && this._menuValue.length > 0 + ? this.setKey(this._menuKeys[0]) + : runInAction(() => { + this._makeNewField = true; + }); break; case 'Escape': this.closeColumnMenu(); break; + default: } }; @@ -559,7 +692,28 @@ export class CollectionSchemaView extends CollectionSubView() { }; setColumnValues = (key: string, value: string) => { - this.childDocs.forEach(doc => KeyValueBox.SetField(doc, key, value)); + const selectedDocs: Doc[] = []; + this.childDocs.forEach(doc => { + const docIsSelected = this._selectedCells && !(this._selectedCells?.filter(d => d === doc).length === 0); + if (docIsSelected) { + selectedDocs.push(doc); + } + }); + if (selectedDocs.length === 1) { + this.childDocs.forEach(doc => Doc.SetField(doc, key, value)); + } else { + selectedDocs.forEach(doc => Doc.SetField(doc, key, value)); + } + return true; + }; + + setSelectedColumnValues = (key: string, value: string) => { + this.childDocs.forEach(doc => { + const docIsSelected = this._selectedCells && !(this._selectedCells?.filter(d => d === doc).length === 0); + if (docIsSelected) { + Doc.SetField(doc, key, value); + } + }); return true; }; @@ -576,7 +730,9 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - closeColumnMenu = () => (this._columnMenuIndex = undefined); + closeColumnMenu = () => { + this._columnMenuIndex = undefined; + }; @action openFilterMenu = (index: number) => { @@ -585,7 +741,9 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - closeFilterMenu = () => (this._filterColumnIndex = undefined); + closeFilterMenu = () => { + this._filterColumnIndex = undefined; + }; openContextMenu = (x: number, y: number, index: number) => { this.closeColumnMenu(); @@ -615,7 +773,7 @@ export class CollectionSchemaView extends CollectionSubView() { this._menuKeys = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase())); }; - getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] == field); + getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] === field); removeFieldFilters = (field: string) => { this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(Doc.FilterSep)[1], 'remove')); @@ -627,11 +785,14 @@ export class CollectionSchemaView extends CollectionSubView() { case 'Escape': this.closeFilterMenu(); break; + default: } }; @action - updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => (this._filterSearchValue = e.target.value); + updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => { + this._filterSearchValue = e.target.value; + }; @computed get newFieldMenu() { return ( @@ -640,7 +801,7 @@ export class CollectionSchemaView extends CollectionSubView() { <input type="radio" name="newFieldType" - checked={this._newFieldType == ColumnType.Number} + checked={this._newFieldType === ColumnType.Number} onChange={action(() => { this._newFieldType = ColumnType.Number; this._newFieldDefault = 0; @@ -652,7 +813,7 @@ export class CollectionSchemaView extends CollectionSubView() { <input type="radio" name="newFieldType" - checked={this._newFieldType == ColumnType.Boolean} + checked={this._newFieldType === ColumnType.Boolean} onChange={action(() => { this._newFieldType = ColumnType.Boolean; this._newFieldDefault = false; @@ -664,7 +825,7 @@ export class CollectionSchemaView extends CollectionSubView() { <input type="radio" name="newFieldType" - checked={this._newFieldType == ColumnType.String} + checked={this._newFieldType === ColumnType.String} onChange={action(() => { this._newFieldType = ColumnType.String; this._newFieldDefault = ''; @@ -676,7 +837,7 @@ export class CollectionSchemaView extends CollectionSubView() { <div className="schema-key-warning">{this._newFieldWarning}</div> <div className="schema-column-menu-button" - onPointerDown={action(e => { + onPointerDown={action(() => { if (this.documentKeys.includes(this._menuValue)) { this._newFieldWarning = 'Field already exists'; } else if (this._menuValue.length === 0) { @@ -703,7 +864,7 @@ export class CollectionSchemaView extends CollectionSubView() { <div className="schema-key-search"> <div className="schema-column-menu-button" - onPointerDown={action(e => { + onPointerDown={action((e: any) => { e.stopPropagation(); this._makeNewField = true; })}> @@ -741,7 +902,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); + 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, minWidth: CollectionSchemaView._minColWidth }}> <input className="schema-key-search-input" type="text" onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} /> @@ -750,7 +911,6 @@ export class CollectionSchemaView extends CollectionSubView() { ); } get renderKeysMenu() { - console.log('RNDERMENUT:' + this._columnMenuIndex); return ( <div className="schema-column-menu" style={{ left: 0, minWidth: CollectionSchemaView._minColWidth }}> <input className="schema-key-search-input" type="text" onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} /> @@ -780,17 +940,11 @@ export class CollectionSchemaView extends CollectionSubView() { } return ( <div key={key} className="schema-filter-option"> - <input + <input // type="checkbox" onPointerDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()} - onChange={action(e => { - if (e.target.checked) { - Doc.setDocFilter(this.Document, columnKey, key, 'check'); - } else { - Doc.setDocFilter(this.Document, columnKey, key, 'remove'); - } - })} + onChange={e => Doc.setDocFilter(this.Document, columnKey, key, e.target.checked ? 'check' : 'remove')} checked={bool} /> <span style={{ paddingLeft: 4 }}>{key}</span> @@ -807,7 +961,7 @@ export class CollectionSchemaView extends CollectionSubView() { {this.renderFilterOptions} <div className="schema-column-menu-button" - onPointerDown={action(e => { + onPointerDown={action((e: any) => { e.stopPropagation(); this.closeFilterMenu(); })}> @@ -817,22 +971,41 @@ export class CollectionSchemaView extends CollectionSubView() { ); } + @action + onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => { + if (DragManager.docsBeingDragged.length) { + this._mouseCoordinates = { x: e.clientX, y: e.clientY }; + } + if (this._colBeingDragged) { + const newIndex = this.findColDropIndex(e.clientX); + if (newIndex !== this._draggedColIndex) this.moveColumn(this._draggedColIndex, newIndex ?? this._draggedColIndex); + this._draggedColIndex = newIndex || this._draggedColIndex; + this.highlightDraggedColumn(newIndex ?? this._draggedColIndex); + } + }; + @computed get sortedDocs() { + const draggedDocs = this.isContentActive() ? DragManager.docsBeingDragged : []; const field = StrCast(this.layoutDoc.sortField); - const desc = BoolCast(this.layoutDoc.sortDesc); + const desc = BoolCast(this.layoutDoc.sortDesc); // is this an ascending or descending sort + const staticDocs = this.childDocs.filter(d => !draggedDocs.includes(d)); const docs = !field - ? this.childDocs - : [...this.childDocs].sort((docA, docB) => { - const aStr = Field.toString(docA[field] as Field); - const bStr = Field.toString(docB[field] as Field); - var out = 0; + ? staticDocs + : [...staticDocs].sort((docA, docB) => { + // this sorts the documents based on the selected field. returning -1 for a before b, 0 for a = b, 1 for a > b + const aStr = Field.toString(docA[field] as FieldType); + const bStr = Field.toString(docB[field] as FieldType); + let out = 0; if (aStr < bStr) out = -1; if (aStr > bStr) out = 1; if (desc) out *= -1; return out; }); + + docs.splice(this.rowDropIndex, 0, ...draggedDocs); return { docs }; } + rowHeightFunc = () => (BoolCast(this.layoutDoc._schema_singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight); sortedDocsFunc = () => this.sortedDocs; isContentActive = () => this._props.isSelected() || this._props.isContentActive(); @@ -842,8 +1015,8 @@ export class CollectionSchemaView extends CollectionSubView() { _oldWheel: any; render() { return ( - <div className="collectionSchemaView" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)}> - <div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }}></div> + <div className="collectionSchemaView" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)} onPointerMove={e => this.onPointerMove(e)}> + <div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }} /> <div className="schema-table" style={{ width: `calc(100% - ${this.previewWidth}px)` }} @@ -859,7 +1032,7 @@ export class CollectionSchemaView extends CollectionSubView() { placement="right" background={SettingsManager.userBackgroundColor} color={SettingsManager.userColor} - toggle={<FontAwesomeIcon onPointerDown={e => this.openColumnMenu(-1, true)} icon="plus" />} + toggle={<FontAwesomeIcon onPointerDown={() => this.openColumnMenu(-1, true)} icon="plus" />} trigger={PopupTrigger.CLICK} type={Type.TERT} isOpen={this._columnMenuIndex !== -1 ? false : undefined} @@ -868,6 +1041,7 @@ export class CollectionSchemaView extends CollectionSubView() { </div> {this.columnKeys.map((key, index) => ( <SchemaColumnHeader + // eslint-disable-next-line react/no-array-index-key key={index} columnIndex={index} columnKeys={this.columnKeys} @@ -887,28 +1061,42 @@ export class CollectionSchemaView extends CollectionSubView() { </div> {this._columnMenuIndex !== undefined && this._columnMenuIndex !== -1 && this.renderColumnMenu} {this._filterColumnIndex !== undefined && this.renderFilterMenu} - <CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} rowHeight={this.rowHeightFunc} setRef={(ref: HTMLDivElement | null) => (this._tableContentRef = ref)} /> + { + // eslint-disable-next-line no-use-before-define + <CollectionSchemaViewDocs + schema={this} + childDocs={this.sortedDocsFunc} + rowHeight={this.rowHeightFunc} + setRef={(ref: HTMLDivElement | null) => { + this._tableContentRef = ref; + }} + /> + } {this.layoutDoc.chromeHidden ? null : ( <div className="schema-add"> <EditableView GetValue={returnEmptyString} SetValue={undoable(value => (value ? this.addRow(Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true })) : false), 'add text doc')} placeholder={"Type text to create note or ':' to create specific type"} - contents={'+ New Node'} + contents="+ New Node" menuCallback={this.menuCallback} height={CollectionSchemaView._newNodeInputHeight} /> </div> )} </div> - {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>} + {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown} />} {this.previewWidth > 0 && ( - <div style={{ width: `${this.previewWidth}px` }} ref={ref => (this._previewRef = ref)}> + <div + style={{ width: `${this.previewWidth}px` }} + ref={ref => { + this._previewRef = ref; + }}> {Array.from(this._selectedDocs).lastElement() && ( <DocumentView Document={Array.from(this._selectedDocs).lastElement()} fitContentsToBox={returnTrue} - dontCenter={'y'} + dontCenter="y" onClickScriptDisable="always" focus={emptyFunction} defaultDoubleClick={returnIgnore} @@ -939,28 +1127,6 @@ export class CollectionSchemaView extends CollectionSubView() { } } -interface CollectionSchemaViewDocsProps { - schema: CollectionSchemaView; - setRef: (ref: HTMLDivElement | null) => void; - childDocs: () => { docs: Doc[] }; - rowHeight: () => number; -} - -@observer -class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsProps> { - render() { - 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 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> - ))} - </div> - ); - } -} - interface CollectionSchemaViewDocProps { schema: CollectionSchemaView; index: number; @@ -981,10 +1147,12 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV if (property === StyleProp.Opacity) return 1; return DefaultStyleProvider(doc, props, property); }; + isRowContentActive = () => this._props.schema.isContentActive() || this._props.schema._props.isSelected() || this._props.schema._props.isAnyChildContentActive(); render() { 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} @@ -1004,16 +1172,37 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV searchFilterDocs={this._props.schema.searchFilterDocs} rootSelected={this._props.schema.rootSelected} ScreenToLocalTransform={this.screenToLocalXf} - dragWhenActive={true} + dragWhenActive isDocumentActive={this._props.schema._props.childDocumentsActive?.() ? this._props.schema._props.isDocumentActive : this._props.schema.isContentActive} - isContentActive={emptyFunction} + isContentActive={this.isRowContentActive} whenChildContentsActiveChanged={this._props.schema._props.whenChildContentsActiveChanged} - hideDecorations={true} - hideTitle={true} - hideDocumentButtonBar={true} - hideLinkAnchors={true} - layout_fitWidth={returnTrue} + hideDecorations + hideTitle + hideDocumentButtonBar + hideLinkAnchors + fitWidth={returnTrue} /> ); } } +interface CollectionSchemaViewDocsProps { + schema: CollectionSchemaView; + setRef: (ref: HTMLDivElement | null) => void; + childDocs: () => { docs: Doc[] }; + rowHeight: () => number; +} + +@observer +class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsProps> { + render() { + 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 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> + ))} + </div> + ); + } +} diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx index 5f8b412be..6b5a34ec0 100644 --- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx +++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx @@ -1,8 +1,10 @@ +/* eslint-disable react/no-unused-prop-types */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable } from 'mobx'; +import { action } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { emptyFunction, setupMoveUpEvents } from '../../../../Utils'; +import { setupMoveUpEvents } from '../../../../ClientUtils'; +import { emptyFunction } from '../../../../Utils'; import { Colors } from '../../global/globalEnums'; import './CollectionSchemaView.scss'; @@ -24,8 +26,6 @@ export interface SchemaColumnHeaderProps { @observer export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> { - @observable _ref: HTMLDivElement | null = null; - get fieldKey() { return this.props.columnKeys[this.props.columnIndex]; } @@ -34,9 +34,9 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> sortClicked = (e: React.PointerEvent) => { e.stopPropagation(); e.preventDefault(); - if (this.props.sortField == this.fieldKey && this.props.sortDesc) { + if (this.props.sortField === this.fieldKey && this.props.sortDesc) { this.props.setSort(undefined); - } else if (this.props.sortField == this.fieldKey) { + } else if (this.props.sortField === this.fieldKey) { this.props.setSort(this.fieldKey, true); } else { this.props.setSort(this.fieldKey, false); @@ -45,7 +45,7 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> @action onPointerDown = (e: React.PointerEvent) => { - this.props.isContentActive(true) && setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction, false); + this.props.isContentActive(true) && setupMoveUpEvents(this, e, moveEv => this.props.dragColumn(moveEv, this.props.columnIndex), emptyFunction, emptyFunction); }; render() { @@ -58,19 +58,18 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> onPointerDown={this.onPointerDown} ref={col => { if (col) { - this._ref = col; this.props.setColRef(this.props.columnIndex, col); } }}> - <div className="schema-column-resizer left" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex)}></div> + <div className="schema-column-resizer left" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex)} /> <div className="schema-column-title">{this.fieldKey}</div> <div className="schema-header-menu"> <div className="schema-header-button" onPointerDown={e => this.props.openContextMenu(e.clientX, e.clientY, this.props.columnIndex)}> <FontAwesomeIcon icon="ellipsis-h" /> </div> - <div className="schema-sort-button" onPointerDown={this.sortClicked} style={this.props.sortField == this.fieldKey ? { backgroundColor: Colors.MEDIUM_BLUE } : {}}> - <FontAwesomeIcon icon="caret-right" style={this.props.sortField == this.fieldKey ? { transform: `rotate(${this.props.sortDesc ? '270deg' : '90deg'})` } : {}} /> + <div className="schema-sort-button" onPointerDown={this.sortClicked} style={this.props.sortField === this.fieldKey ? { backgroundColor: Colors.MEDIUM_BLUE } : {}}> + <FontAwesomeIcon icon="caret-right" style={this.props.sortField === this.fieldKey ? { transform: `rotate(${this.props.sortDesc ? '270deg' : '90deg'})` } : {}} /> </div> </div> </div> diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 39fea2d2e..760089ffb 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -5,17 +5,16 @@ import { computedFn } from 'mobx-utils'; import * as React from 'react'; import { CgClose, CgLock, CgLockUnlock } from 'react-icons/cg'; import { FaExternalLinkAlt } from 'react-icons/fa'; -import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils'; +import { returnFalse, setupMoveUpEvents } from '../../../../ClientUtils'; +import { emptyFunction } from '../../../../Utils'; import { Doc } from '../../../../fields/Doc'; import { BoolCast } from '../../../../fields/Types'; -import { DragManager } from '../../../util/DragManager'; -import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoable } from '../../../util/UndoManager'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; -import { OpenWhere } from '../../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; +import { OpenWhere } from '../../nodes/OpenWhere'; import { CollectionSchemaView } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; import { SchemaTableCell } from './SchemaTableCell'; @@ -42,7 +41,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { } @computed get schemaDoc() { - return this.DocumentView?.().containerViewPath?.().lastElement()?.Document; + return this.schemaView.Document; } @computed get rowIndex() { @@ -53,63 +52,21 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { this._props.setContentViewBox?.(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.Document, lastSelected); - else { - this._props.select?.(ctrlKey); - } - }; - - onPointerEnter = (e: any) => { - if (SnappingManager.IsDragging && this._props.isContentActive()) { - document.removeEventListener('pointermove', this.onPointerMove); - document.addEventListener('pointermove', this.onPointerMove); - } - }; - - onPointerMove = (e: any) => { - const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.embedContainer === this.schemaDoc); // this.schemaView?._selectedDocs.has(doc) ?? false; - - if (this._ref && dragIsRow) { - const rect = this._ref.getBoundingClientRect(); - const y = e.clientY - rect.top; //y position within the element. - const height = this._ref.clientHeight; - const halfLine = height / 2; - if (y <= halfLine) { - this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`; - this._ref.style.borderBottom = '0px'; - this.schemaView?.setDropIndex(this.rowIndex); - } else if (y > halfLine) { - this._ref.style.borderTop = '0px'; - this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`; - this.schemaView?.setDropIndex(this.rowIndex + 1); - } - } - }; - - onPointerLeave = (e: any) => { - if (this._ref) { - this._ref.style.borderTop = '0px'; - this._ref.style.borderBottom = '0px'; - } - document.removeEventListener('pointermove', this.onPointerMove); - }; - + 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) => this.schemaView?.selectCell(doc, col); - deselectCell = () => this.schemaView?.deselectCell(); - selectedCell = () => this.schemaView?._selectedCell; + 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?.setColumnValues(field, value) ?? false; + setSelectedColumnValues = (field: any, value: any) => this.schemaView?.setSelectedColumnValues(field, value) ?? false; columnWidth = computedFn((index: number) => () => this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth); render() { return ( <div className="schema-row" + onPointerDown={e => this.setCursorIndex(e.clientY)} 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.Document, row); this._ref = row; @@ -121,8 +78,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { pointerEvents: !this._props.isContentActive() ? 'none' : undefined, }}> <IconButton - tooltip="whether document interactions are enabled" - icon={this.Document._lockedPosition ? <CgLockUnlock size="12px" /> : <CgLock size="12px" />} + tooltip="close" + icon={<CgClose size="16px" />} size={Size.XSMALL} onPointerDown={e => setupMoveUpEvents( @@ -130,15 +87,16 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { e, returnFalse, emptyFunction, - undoable(e => { - e.stopPropagation(); - Doc.toggleLockedPosition(this.Document); + undoable(clickEv => { + clickEv.stopPropagation(); + this._props.removeDocument?.(this.Document); }, 'Delete Row') ) - }></IconButton> + } + /> <IconButton - tooltip="close" - icon={<CgClose size={'16px'} />} + tooltip="whether document interactions are enabled" + icon={this.Document._lockedPosition ? <CgLockUnlock size="12px" /> : <CgLock size="12px" />} size={Size.XSMALL} onPointerDown={e => setupMoveUpEvents( @@ -146,10 +104,10 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { e, returnFalse, emptyFunction, - undoable(e => { - e.stopPropagation(); - this._props.removeDocument?.(this.Document); - }, 'Delete Row') + undoable(clickEv => { + clickEv.stopPropagation(); + Doc.toggleLockedPosition(this.Document); + }, 'toggle document lock') ) } /> @@ -163,8 +121,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { e, returnFalse, emptyFunction, - undoable(e => { - e.stopPropagation(); + undoable(clickEv => { + clickEv.stopPropagation(); this._props.addDocTab(this.Document, OpenWhere.addRight); }, 'Open schema Doc preview') ) @@ -185,8 +143,10 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { getFinfo={this.getFinfo} selectCell={this.selectCell} deselectCell={this.deselectCell} - selectedCell={this.selectedCell} + selectedCells={this.selectedCells} + selectedCol={this.selectedCol} setColumnValues={this.setColumnValues} + setSelectedColumnValues={this.setSelectedColumnValues} oneLine={BoolCast(this.schemaDoc?._singleLine)} menuTarget={this.schemaView.MenuTarget} transform={() => { diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index bf36b2668..5874364e0 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -1,3 +1,6 @@ +/* eslint-disable jsx-a11y/alt-text */ +/* 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'; import { action, computed, makeObservable, observable } from 'mobx'; @@ -7,36 +10,36 @@ import * as React from 'react'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import Select from 'react-select'; -import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils'; +import { ClientUtils, StopEvent, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../ClientUtils'; +import { emptyFunction } 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 { ColumnType } from '../../../../fields/SchemaHeaderField'; +import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast, toList } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { FInfo, FInfoFieldType } from '../../../documents/Documents'; -import { DocFocusOrOpen } from '../../../util/DocumentManager'; -import { dropActionType } from '../../../util/DragManager'; -import { SettingsManager } from '../../../util/SettingsManager'; +import { dropActionType } from '../../../util/DropActionTypes'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoBatch, undoable } from '../../../util/UndoManager'; import { EditableView } from '../../EditableView'; import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { DefaultStyleProvider } from '../../StyleProvider'; +import { DefaultStyleProvider, returnEmptyDocViewList } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; -import { OpenWhere, returnEmptyDocViewList } from '../../nodes/DocumentView'; +import { DocumentView } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; -import { KeyValueBox } from '../../nodes/KeyValueBox'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; -import { ColumnType, FInfotoColType } from './CollectionSchemaView'; +import { FInfotoColType } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; export interface SchemaTableCellProps { Document: Doc; col: number; deselectCell: () => void; - selectCell: (doc: Doc, col: number) => void; - selectedCell: () => [Doc, number] | undefined; + selectCell: (doc: Doc, col: number, shift: boolean, ctrl: boolean) => void; + selectedCells: () => Doc[] | undefined; + selectedCol: () => number; fieldKey: string; maxWidth?: () => number; columnWidth: () => number; @@ -45,6 +48,7 @@ export interface SchemaTableCellProps { isRowActive: () => boolean | undefined; getFinfo: (fieldKey: string) => FInfo | undefined; setColumnValues: (field: string, value: string) => boolean; + setSelectedColumnValues: (field: string, value: string) => boolean; oneLine?: boolean; // whether all input should fit on one line vs allowing textare multiline inputs allowCRs?: boolean; // allow carriage returns in text input (othewrise CR ends the edit) finishEdit?: () => void; // notify container that edit is over (eg. to hide view in DashFieldView) @@ -55,6 +59,14 @@ export interface SchemaTableCellProps { rootSelected?: () => boolean; } +function selectedCell(props: SchemaTableCellProps) { + return ( + props.isRowActive() && + props.selectedCol() === props.col && // + props.selectedCells()?.filter(d => d === props.Document)?.length + ); +} + @observer export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellProps> { constructor(props: SchemaTableCellProps) { @@ -62,8 +74,8 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro makeObservable(this); } - static addFieldDoc = (doc: Doc, where: OpenWhere) => { - DocFocusOrOpen(doc); + static addFieldDoc = (docs: Doc | Doc[] /* , where: OpenWhere */) => { + DocumentView.FocusOrOpen(toList(docs)[0]); return true; }; public static renderProps(props: SchemaTableCellProps) { @@ -78,7 +90,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro doc = DocCast(doc.proto); } const parenCount = Math.max(0, protoCount - 1); - const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue'; + const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue'; // color of text in cells const textDecoration = color !== 'black' && parenCount ? 'underline' : ''; const fieldProps: FieldViewProps = { childFilters: returnEmptyFilter, @@ -110,11 +122,6 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro return { color, textDecoration, fieldProps, cursor, pointerEvents }; } - @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; - } - @computed get defaultCellContent() { const { color, textDecoration, fieldProps, pointerEvents } = SchemaTableCell.renderProps(this._props); @@ -128,12 +135,12 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro pointerEvents, }}> <EditableView - ref={r => this.selected && this._props.autoFocus && r?.setIsFocused(true)} + ref={r => selectedCell(this._props) && this._props.autoFocus && r?.setIsFocused(true)} oneLine={this._props.oneLine} allowCRs={this._props.allowCRs} contents={undefined} fieldContents={fieldProps} - editing={this.selected ? undefined : false} + editing={selectedCell(this._props) ? undefined : false} GetValue={() => Field.toKeyValueString(fieldProps.Document, this._props.fieldKey, SnappingManager.MetaKey)} SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { @@ -141,7 +148,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro this._props.finishEdit?.(); return true; } - const ret = KeyValueBox.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(fieldProps.Document) ? true : undefined); + const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(fieldProps.Document) ? true : undefined); this._props.finishEdit?.(); return ret; }, 'edit schema cell')} @@ -153,30 +160,27 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro get getCellType() { 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; if (typeof cellValue === 'number') return ColumnType.Any; if (typeof cellValue === 'string' && columnTypeStr !== FInfoFieldType.enumeration) return ColumnType.Any; if (typeof cellValue === 'boolean') return ColumnType.Boolean; - - if (columnTypeStr && columnTypeStr in FInfotoColType) { - return FInfotoColType[columnTypeStr]; - } + if (columnTypeStr && columnTypeStr in FInfotoColType) return FInfotoColType[columnTypeStr]; return ColumnType.Any; } get content() { - 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} />; - default: return this.defaultCellContent; + switch (this.getCellType) { + 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 => Field.toString(val))} />; + case ColumnType.Date: return <SchemaDateCell {...this._props} />; + default: return this.defaultCellContent; } } @@ -184,8 +188,18 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro 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 }}> + onContextMenu={e => StopEvent(e)} + onPointerDown={action(e => { + const shift: boolean = e.shiftKey; + const ctrl: boolean = e.ctrlKey; + if (this._props.isRowActive?.() !== false) { + if (selectedCell(this._props) && ctrl) { + this._props.selectCell(this._props.Document, this._props.col, shift, ctrl); + e.stopPropagation(); + } 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, border: selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}> {this.content} </div> ); @@ -204,8 +218,8 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro choosePath(url: URL) { if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href - if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver - if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question + if (url.href.indexOf(window.location.origin) === -1) return ClientUtils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver + if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; // Why is this here — good question const ext = extname(url.href); return url.href.replace(ext, '_s' + ext); @@ -220,7 +234,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths; // If there is a path, follow it; otherwise, follow a link to a default image icon - const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')]; + const url = paths.length ? paths : [ClientUtils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')]; return url[0]; } @@ -244,7 +258,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro }; @action - removeHoverPreview = (e: React.PointerEvent) => { + removeHoverPreview = () => { if (!this._previewRef) return; document.body.removeChild(this._previewRef); }; @@ -256,7 +270,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro 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} />; + return <img src={this.url} width={width || undefined} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />; } } @@ -280,26 +294,26 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp // } else { // ^ DateCast is always undefined for some reason, but that is what the field should be set to date && (this._props.Document[this._props.fieldKey] = new DateField(date)); - //} + // } }, 'date change'); render() { - const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props); + const { pointerEvents } = SchemaTableCell.renderProps(this._props); return ( <> <div style={{ pointerEvents: 'none' }}> - <DatePicker dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={e => {}} /> + <DatePicker dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={emptyFunction} /> </div> {pointerEvents === 'none' ? null : ( <Popup icon={<FontAwesomeIcon size="sm" icon="caret-down" />} size={Size.XSMALL} type={Type.TERT} - color={SettingsManager.userColor} - background={SettingsManager.userBackgroundColor} + color={SnappingManager.userColor} + background={SnappingManager.userBackgroundColor} popup={ <div style={{ width: 'fit-content', height: '200px' }}> - <DatePicker open={true} dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={this.handleChange} /> + <DatePicker open dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={this.handleChange} /> </div> } /> @@ -315,19 +329,14 @@ export class SchemaRTFCell extends ObservableReactComponent<SchemaTableCellProps 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; - } - // if the text box blurs and none of its contents are focused(), then the edit finishes - selectedFunc = () => this.selected; + selectedFunc = () => !!selectedCell(this._props); render() { const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props); fieldProps.isContentActive = this.selectedFunc; return ( - <div className="schemaRTFCell" style={{ fontStyle: this.selected ? undefined : 'italic', color, textDecoration, cursor, pointerEvents }}> - {this.selected ? <FormattedTextBox {...fieldProps} autoFocus={true} onBlur={() => this._props.finishEdit?.()} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))} + <div className="schemaRTFCell" style={{ fontStyle: selectedCell(this._props) ? undefined : 'italic', color, textDecoration, cursor, pointerEvents }}> + {selectedCell(this._props) ? <FormattedTextBox {...fieldProps} autoFocus onBlur={() => this._props.finishEdit?.()} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))} </div> ); } @@ -339,10 +348,6 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp 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; - } render() { const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props); return ( @@ -353,14 +358,14 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp 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()); - } else KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString()); + this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? '')); + } else Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? '')); })} /> <EditableView contents={undefined} fieldContents={fieldProps} - editing={this.selected ? undefined : false} + editing={selectedCell(this._props) ? undefined : false} GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)} SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { @@ -368,7 +373,7 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp this._props.finishEdit?.(); return true; } - const set = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined); + const set = Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined); this._props.finishEdit?.(); return set; })} @@ -384,12 +389,8 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC 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; - } render() { - const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props); + const { color, textDecoration, 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 }}> @@ -434,7 +435,7 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC 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 => Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)} /> </div> </div> |
