diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/DocumentManager.ts | 4 | ||||
-rw-r--r-- | src/client/util/DragManager.ts | 1 | ||||
-rw-r--r-- | src/client/util/SelectionManager.ts | 3 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionNoteTakingView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/CollectionSchemaView.scss | 4 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/CollectionSchemaView.tsx | 302 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/SchemaRowBox.tsx | 82 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/SchemaTableCell.tsx | 39 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 11 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/DashFieldView.tsx | 6 | ||||
-rw-r--r-- | src/mobile/MobileInterface.tsx | 2 |
13 files changed, 309 insertions, 149 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 40d28c690..4cb56bd07 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -36,6 +36,7 @@ export class DocumentManager { } public DeleteDocumentView(dv: DocumentView) { this._documentViews.delete(dv); + console.log("deleted") } //private constructor so no other class can create a nodemanager @@ -94,6 +95,7 @@ export class DocumentManager { this.callAddViewFuncs(view); } // prettier-ignore }; + public RemoveView = action((view: DocumentView) => { if (!view._props.LayoutTemplateString?.includes(KeyValueBox.name) && !view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { this.DeleteDocumentView(view); @@ -125,6 +127,8 @@ export class DocumentManager { public getDocumentView(target: Doc | undefined, preferredCollection?: DocumentView): DocumentView | undefined { const docViewArray = DocumentManager.Instance.DocumentViews; + //console.log(docViewArray) + //console.log(this._documentViews) const passes = !target ? [] : preferredCollection ? [preferredCollection, undefined] : [undefined]; return passes.reduce( (toReturn, pass) => diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 9627c5df2..7676c3520 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -177,6 +177,7 @@ export namespace DragManager { constructor(colIndex: number) { this.colIndex = colIndex; } + colIndex: number; } // used by PDFs,Text,Image,Video,Web to conditionally (if the drop completes) create a text annotation when dragging the annotate button from the AnchorMenu when a text/region selection has been made. diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 36b926053..b6ee4d5c3 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -9,6 +9,7 @@ import { DocumentView } from '../views/nodes/DocumentView'; import { LinkManager } from './LinkManager'; import { ScriptingGlobals } from './ScriptingGlobals'; import { UndoManager } from './UndoManager'; +import { SchemaRowBox } from '../views/collections/collectionSchema/SchemaRowBox'; export class SelectionManager { private static _manager: SelectionManager; @@ -51,6 +52,7 @@ export class SelectionManager { }); public static DeselectAll = (except?: Doc): void => { + //console.log("deselect all") const found = this.Instance.SelectedViews.find(dv => dv.Document === except); runInAction(() => { LinkManager.Instance.currentLink = undefined; @@ -62,6 +64,7 @@ export class SelectionManager { dv._props.whenChildContentsActiveChanged(false); }); runInAction(() => (this.Instance.SelectedViews.length = 0)); + //not responsible for select onPointerDown if (found) this.SelectView(found, false); }; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 58b8d255a..2fb588e36 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -305,6 +305,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faSnowflake, fa.faStar, fa.faMicrophone, + fa.faCircleHalfStroke, fa.faKeyboard, fa.faQuestion, fa.faTasks, diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index d8a0aebb1..7fc415c23 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -141,6 +141,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { sections.get(existingHeader)!.push(d); } }); + //*!* // now we add back in the docs that we're dragging if (rowCol.length && columnHeaders.length > rowCol[1]) { const offset = 0; 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..3a3d4d206 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -56,7 +56,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 +87,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,7 +101,11 @@ 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 = SelectionManager.Docs.filter(doc => this.childDocs.includes(doc)); + // SelectionManager... filter(doc => this.childDocs.includes(doc)) + //DocCast(doc.embedContainer)[DocData] === this.dataDoc + //SelectionManager.Docs.forEach(doc => console.log("index: " + this.rowIndex(doc) + " equal: " + Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document))); if (!selected.length) { for (const sel of SelectionManager.Docs) { const contextPath = DocumentManager.GetContextPath(sel, true); @@ -125,6 +134,10 @@ export class CollectionSchemaView extends CollectionSubView() { return Cast(this.layoutDoc.schema_columnKeys, listSpec('string'), defaultColumnKeys); } + @computed get rowKeys() { + return Cast(this.layoutDoc.schema_rowKeys, listSpec('string'), []); + } + @computed get storedColumnWidths() { const widths = NumListCast( this.layoutDoc.schema_columnWidths, @@ -132,12 +145,18 @@ 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() { + const heights = this.childDocs.map(() => (this.rowHeightFunc())) + return heights; + } + @computed get displayColumnWidths() { return this._displayColumnWidths ?? this.storedColumnWidths; } @@ -190,13 +209,14 @@ 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)); + this.deselectCell(curDoc); } else { - this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1); - this._selectedCell && (this._selectedCell[0] = newDoc); + const shift: boolean = e.shiftKey; + const ctrl: boolean = e.ctrlKey; + this.selectCell(newDoc, this._selectedCol, shift, ctrl); this.scrollToDoc(newDoc, {}); } } @@ -210,12 +230,14 @@ 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)){ + SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)) + this.deselectCell(curDoc); + } else { + const shift: boolean = e.shiftKey; + const ctrl: boolean = e.ctrlKey; + this.selectCell(newDoc, this._selectedCol, shift, ctrl); this.scrollToDoc(newDoc, {}); } } @@ -224,17 +246,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 +264,17 @@ export class CollectionSchemaView extends CollectionSubView() { break; } case 'Escape': { - this.deselectCell(); + this.deselectAllCells(); } } } }; + @action + changeSelectedCellColumn = () => { + + } + @undoBatch setColumnSort = (field: string | undefined, desc: boolean = false) => { this.layoutDoc.sortField = field; @@ -342,6 +369,9 @@ export class CollectionSchemaView extends CollectionSubView() { @undoBatch moveColumn = (fromIndex: number, toIndex: number) => { + if (this._selectedCol === fromIndex) this._selectedCol = toIndex; + else if (toIndex === this._selectedCol) this._selectedCol = fromIndex; //keeps selected cell consistent + let currKeys = this.columnKeys.slice(); currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]); this.layoutDoc.schema_columnKeys = new List<string>(currKeys); @@ -349,27 +379,31 @@ export class CollectionSchemaView extends CollectionSubView() { let 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); + // 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,26 +411,79 @@ 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 + + let rowHeight = CollectionSchemaView._rowHeight; + let adjInitMouseY = mouseY - rowHeight - 100; //rowHeight: height of the column menu cells | 100: height of the top menu + let 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; + } + + //Uses current mouse position to calculate the indexes of actively dragged docs + findRowDropIndex = (mouseY: number) => { + let 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; + let 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; + }; + + @action + 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; - }); + let edgeStyle = ''; + if (i === index) edgeStyle = `solid 2px ${Colors.MEDIUM_BLUE}`; + + //border styles of menu cell + colRef.style.borderLeft = edgeStyle; + colRef.style.borderRight = edgeStyle; + colRef.style.borderTop = edgeStyle; + + for (let doc = 0; doc < this.childDocs.length; ++doc){ + if (i === this._selectedCol && this._selectedDocs.includes(this.childDocs[doc])){ + continue; + } else { + this._rowEles.get(this.childDocs[doc]).children[1].children[i].style.borderLeft = edgeStyle; + this._rowEles.get(this.childDocs[doc]).children[1].children[i].style.borderRight = edgeStyle; + if (doc === this.childDocs.length - 1){ + this._rowEles.get(this.childDocs[doc]).children[1].children[i].style.borderBottom = edgeStyle; + } + } + } }); }; @@ -419,7 +506,10 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - clearSelection = () => SelectionManager.DeselectAll(); + clearSelection = () => { + SelectionManager.DeselectAll(); + this.deselectAllCells(); + }; selectRows = (doc: Doc, lastSelected: Doc) => { const index = this.rowIndex(doc); @@ -428,50 +518,89 @@ 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, col: number, shiftKey: boolean, ctrlKey: boolean) => { + if (!shiftKey && !ctrlKey) this.clearSelection(); + !this._selectedCells && (this._selectedCells = []); + !shiftKey && this._selectedCells && this._selectedCells.push(doc); + let 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)){ + SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(doc)) + this.deselectCell(doc); + } else this.addDocToSelection(doc, true, index); } + else this.addDocToSelection(doc, false, index); + 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 - selectCell = (doc: Doc, index: number) => (this._selectedCell = [doc, index]); + 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(doc => this.rowIndex(doc))) + }; @action - deselectCell = () => (this._selectedCell = undefined); + 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]; + const index = this.findRowDropIndex(mouseY); + return index + } 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]); + let 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)); }); + this._lowestSelectedIndex = Math.min(...draggedDocs?.map(doc => this.rowIndex(doc))); return true; } return false; @@ -559,7 +688,30 @@ export class CollectionSchemaView extends CollectionSubView() { }; setColumnValues = (key: string, value: string) => { - this.childDocs.forEach(doc => KeyValueBox.SetField(doc, key, value)); + const selectedDocs: Doc[] = new Array; + this.childDocs.forEach(doc => { + let docIsSelected = this._selectedCells && !(this._selectedCells?.filter(d => d === doc).length === 0); + if (docIsSelected){ + selectedDocs.push(doc); + } + } + ); + if (selectedDocs.length === 1){ + this.childDocs.forEach(doc => KeyValueBox.SetField(doc, key, value)); + } else { + selectedDocs.forEach(doc => KeyValueBox.SetField(doc, key, value)); + } + return true; + }; + + setSelectedColumnValues = (key: string, value: string) => { + this.childDocs.forEach(doc => { + let docIsSelected = this._selectedCells && !(this._selectedCells?.filter(d => d === doc).length === 0); + if (docIsSelected){ + KeyValueBox.SetField(doc, key, value) + } + } + ); return true; }; @@ -750,7 +902,7 @@ export class CollectionSchemaView extends CollectionSubView() { ); } get renderKeysMenu() { - console.log('RNDERMENUT:' + this._columnMenuIndex); + //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()} /> @@ -817,12 +969,26 @@ 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){ + let newIndex = this.findColDropIndex(e.clientX); + if (newIndex != this._draggedColIndex) this.moveColumn(this._draggedColIndex, newIndex ?? this._draggedColIndex); + this._draggedColIndex = newIndex ? newIndex : this._draggedColIndex; + this.highlightDraggedColumn(newIndex ?? this._draggedColIndex) + } + } + @computed get sortedDocs() { 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 => !DragManager.docsBeingDragged.includes(d)); const docs = !field - ? this.childDocs - : [...this.childDocs].sort((docA, docB) => { + ? 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 Field); const bStr = Field.toString(docB[field] as Field); var out = 0; @@ -831,8 +997,11 @@ export class CollectionSchemaView extends CollectionSubView() { if (desc) out *= -1; return out; }); + + docs.splice(this.rowDropIndex, 0, ...DragManager.docsBeingDragged) return { docs }; } + rowHeightFunc = () => (BoolCast(this.layoutDoc._schema_singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight); sortedDocsFunc = () => this.sortedDocs; isContentActive = () => this._props.isSelected() || this._props.isContentActive(); @@ -842,7 +1011,10 @@ 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 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> <div className="schema-table" diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 39fea2d2e..7e40ef766 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -5,7 +5,7 @@ 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 { StopEvent, emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils'; import { Doc } from '../../../../fields/Doc'; import { BoolCast } from '../../../../fields/Types'; import { DragManager } from '../../../util/DragManager'; @@ -53,63 +53,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 +79,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( @@ -132,13 +90,14 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { emptyFunction, undoable(e => { e.stopPropagation(); - Doc.toggleLockedPosition(this.Document); + 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( @@ -148,11 +107,10 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { emptyFunction, undoable(e => { e.stopPropagation(); - this._props.removeDocument?.(this.Document); - }, 'Delete Row') + Doc.toggleLockedPosition(this.Document); + }, 'Delete Row') //(??) should this be something else? ) - } - /> + }></IconButton> <IconButton tooltip="open preview" icon={<FaExternalLinkAlt />} @@ -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..59cd7994b 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -7,7 +7,7 @@ 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 { StopEvent, Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero } from '../../../../Utils'; import { DateField } from '../../../../fields/DateField'; import { Doc, DocListCast, Field } from '../../../../fields/Doc'; import { RichTextField } from '../../../../fields/RichTextField'; @@ -35,8 +35,9 @@ 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 +46,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) @@ -78,7 +80,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, @@ -111,8 +113,9 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro } @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 selectedDocs: Doc[] | undefined = this._props.selectedCells(); + let isSelected = this._props.isRowActive() && (selectedDocs?.filter(doc => doc === this._props.Document).length !== 0) && this._props.selectedCol() === this._props.col; + return isSelected; } @computed get defaultCellContent() { @@ -180,11 +183,20 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro } } + render() { return ( <div className="schema-table-cell" - onPointerDown={action(e => !this.selected && this._props.selectCell(this._props.Document, this._props.col))} + onContextMenu={e => StopEvent(e)} + onPointerDown={action(e => { + const shift: boolean = e.shiftKey; + const ctrl: boolean = e.ctrlKey; + if (this.selected && ctrl) { + this._props.selectCell(this._props.Document, this._props.col, shift, ctrl); + e.stopPropagation(); + } else !this.selected && 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: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}> {this.content} </div> @@ -316,8 +328,9 @@ export class SchemaRTFCell extends ObservableReactComponent<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 = this._props.selectedCells(); + return this._props.isRowActive() && (selected && selected?.filter(doc => doc === this._props.Document).length !== 0) && this._props.selectedCol() === this._props.col; + //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 @@ -340,8 +353,8 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp } @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 = this._props.selectedCells(); + return this._props.isRowActive() && (selected && selected?.filter(doc => doc === this._props.Document).length !== 0) && this._props.selectedCol() === this._props.col; } render() { const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props); @@ -385,8 +398,8 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC } @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 = this._props.selectedCells(); + return this._props.isRowActive() && (selected && selected?.filter(doc => doc === this._props.Document).length !== 0) && this._props.selectedCol() === this._props.col; } render() { const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ee7bbbdba..581d43b94 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -279,6 +279,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document } startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) { + //console.log("docview drag") const docView = this._docView; if (this._mainCont.current && docView) { const views = SelectionManager.Views.filter(dv => dv.ContentDiv); @@ -362,7 +363,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document const sendToBack = e.altKey; this._singleClickFunc = // prettier-ignore - clickFunc ?? (() => (sendToBack ? documentView._props.bringToFront?.(this.Document, true) : + clickFunc ?? (() => (sendToBack ? documentView._props.bringToFront?.(this.Document, true) : this._props.select(e.ctrlKey||e.shiftKey, e.metaKey))); const waitFordblclick = this._props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick; if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') { @@ -380,7 +381,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document onPointerDown = (e: React.PointerEvent): void => { if (this._props.isGroupActive?.() === 'child' && !this._props.isDocumentActive?.()) return; - this._longPressSelector = setTimeout(() => DocumentView.LongPress && this._props.select(false), 1000); + this._longPressSelector = setTimeout(() => (DocumentView.LongPress && this._props.select(false), 1000)); //!!! if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this._docView; this._downX = e.clientX; @@ -1370,8 +1371,8 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { screenToLocalScale = () => this._props.ScreenToLocalTransform().Scale; isSelected = () => this.IsSelected; select = (extendSelection: boolean, focusSelection?: boolean) => { - if (this.IsSelected && SelectionManager.Views.length > 1) SelectionManager.DeselectView(this); - else { + /*if (this.IsSelected && SelectionManager.Views.length > 1) SelectionManager.DeselectView(this); + else {*/ SelectionManager.SelectView(this, extendSelection); if (focusSelection) { DocumentManager.Instance.showDocument(this.Document, { @@ -1380,7 +1381,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { zoomTime: 500, }); } - } + //} }; backgroundColor = () => this._docViewInternal?.backgroundBoxColor; DataTransition = () => this._props.DataTransition?.() || StrCast(this.Document.dataTransition); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index bb1f70f97..a79dda74e 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -315,7 +315,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray', color: usePath === undefined ? 'black' : 'white', }}> - <FontAwesomeIcon icon="turn-up" size="lg" /> + <FontAwesomeIcon icon="circle-half-stroke" size="lg" /> </div> </Tooltip> ); diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 439d4785e..499fc3457 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -179,14 +179,16 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi deselectCell={emptyFunction} selectCell={emptyFunction} maxWidth={this._props.hideKey || this._hideKey ? undefined : this._props.tbox._props.PanelWidth} - columnWidth={this._expanded || this._props.nodeSelected() ? this.columnWidth : returnZero} - selectedCell={this.selectedCell} + columnWidth={returnZero} + selectedCells={() => [this._dashDoc!]} + selectedCol={() => 0} fieldKey={this._fieldKey} rowHeight={returnZero} isRowActive={this.isRowActive} padding={0} getFinfo={emptyFunction} setColumnValues={returnFalse} + setSelectedColumnValues={returnFalse} allowCRs={true} oneLine={!this._expanded && !this._props.nodeSelected()} finishEdit={this.finishEdit} diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index dc4af8303..818e2b953 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -60,6 +60,7 @@ import { faLongArrowAltLeft, faLongArrowAltRight, faMicrophone, + faCircleHalfStroke, faMinus, faMobile, faMousePointer, @@ -197,6 +198,7 @@ library.add( faHighlighter, faLongArrowAltRight, faMicrophone, + faCircleHalfStroke, faMousePointer, faMusic, faObjectGroup, |