aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan-SR <144961007+Nathan-SR@users.noreply.github.com>2024-06-20 01:57:07 -0400
committerNathan-SR <144961007+Nathan-SR@users.noreply.github.com>2024-06-20 01:57:07 -0400
commitd207cf565968167c59b16baf6ca5ce2543c680ea (patch)
tree28b4757d490e5807201616c623c2bdc2c2f66ba6
parentb04015291de1d785ad54c0fad1f66903fb055bf4 (diff)
cursor position consistency for schema cell field
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx4
-rw-r--r--src/client/views/collections/collectionSchema/SchemaCellField.tsx86
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx3
3 files changed, 74 insertions, 19 deletions
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 1d9245206..c287b7d44 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -478,7 +478,6 @@ export class CollectionSchemaView extends CollectionSubView() {
this._colEles.forEach((colRef, i) => {
const edgeStyle = i === index ? `solid 2px ${Colors.MEDIUM_BLUE}` : '';
const sorted = i === this.columnKeys.indexOf(this.sortField);
- console.log(sorted)
const cellEles = [
colRef,
...this.docsWithDrag.docs
@@ -497,7 +496,7 @@ export class CollectionSchemaView extends CollectionSubView() {
highlightSortedColumn = (field?: string, descending?: boolean) => {
let index = -1;
let highlightColors: string[] = [];
- const rowCount: number = this._rowEles.size + 1;
+ const rowCount: number = this._docs.length + 1;
if (field || this.sortField){
index = this.columnKeys.indexOf(field || this.sortField);
const increment: number = 100/rowCount;
@@ -530,7 +529,6 @@ export class CollectionSchemaView extends CollectionSubView() {
getCellElement = (doc: Doc, fieldKey: string) => {
const index = this.columnKeys.indexOf(fieldKey);
- console.log(doc.title)
const cell = this._rowEles.get(doc).children[1].children[index];
return cell;
}
diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
index 5f758683d..3af3b1d61 100644
--- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -22,8 +22,10 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
private _disposers: { [name: string]: IReactionDisposer } = {};
private _inputref: HTMLDivElement | null = null;
+ private _content: string = '';
_overlayDisposer?: () => void;
@observable _editing: boolean = false;
+ @observable _displayedContent = '';
constructor(props: SchemaCellFieldProps) {
super(props);
@@ -50,12 +52,14 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
},
{ fireImmediately: true }
);
+ this._content = this.props.GetValue() ?? '';
+ this.setContent(this._content);
}
componentDidUpdate(prevProps: Readonly<SchemaCellFieldProps>) {
super.componentDidUpdate(prevProps);
if (this._editing && this._props.editing === false) {
- this._inputref?.innerText && this.finalizeEdit(this._inputref.innerText, false, true, false);
+ this.finalizeEdit(false, true, false);
} else
runInAction(() => {
if (this._props.editing !== undefined) this._editing = this._props.editing;
@@ -65,9 +69,14 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
componentWillUnmount(): void {
this._overlayDisposer?.();
this._disposers.editing?.();
- this._inputref?.innerText && this.finalizeEdit(this._inputref.innerText, false, true, false);
+ this.finalizeEdit(false, true, false);
}
+ @action
+ setContent = (content: string) => {
+ this._displayedContent = content;
+ };
+
@action
setIsFocused = (value: boolean) => {
const wasFocused = this._editing;
@@ -75,7 +84,52 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
return wasFocused !== this._editing;
};
- onChange = (e: FormEvent<HTMLInputElement>) => {
+ get cursorPosition() {
+ const selection = window.getSelection();
+ if (!selection || selection.rangeCount === 0 || !this._inputref) return null;
+
+ const range = selection.getRangeAt(0);
+ const adjRange = range.cloneRange();
+
+ adjRange.selectNodeContents(this._inputref);
+ adjRange.setEnd(range.startContainer, range.startOffset);
+
+ return adjRange.toString().length;
+ }
+
+ restoreCursorPosition = (position: number | null) => {
+ const selection = window.getSelection();
+ if (!selection || position === null || !this._inputref) return;
+
+ const range = document.createRange();
+ range.setStart(this._inputref, 0);
+ range.collapse(true);
+
+ let currentPos = 0;
+ const setRange = (nodes: NodeList) => {
+ for (let i = 0; i < nodes.length; ++i) {
+ const node = nodes[i];
+
+ if (node.nodeType === Node.TEXT_NODE) {
+ if (!node.textContent) return;
+ const nextPos = currentPos + node.textContent.length;
+ if (position <= nextPos) {
+ range.setStart(node, position - currentPos);
+ range.collapse(true);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ return true;
+ }
+ currentPos = nextPos;
+
+ } else if ((node.nodeType === Node.ELEMENT_NODE) && (setRange(node.childNodes))) return true;
+ }
+ return false;
+ }
+ };
+
+ onChange = (e: FormEvent<HTMLDivElement>) => {
+ const cursorPos = this.cursorPosition;
const targVal = e.currentTarget.innerText;
if (!(targVal.startsWith(':=') || targVal.startsWith('='))) {
this._overlayDisposer?.();
@@ -83,6 +137,9 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
} else if (!this._overlayDisposer) {
this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
}
+ this._content = targVal;
+ this.setContent(targVal);
+ setTimeout(() => this.restoreCursorPosition(cursorPos), 0);
this._props.highlightCells?.(targVal);
};
@@ -92,7 +149,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
switch (e.key) {
case 'Tab':
e.stopPropagation();
- this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, false);
+ this.finalizeEdit(e.shiftKey, false, false);
break;
case 'Backspace':
e.stopPropagation();
@@ -100,7 +157,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
case 'Enter':
e.stopPropagation();
if (!e.ctrlKey) {
- this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, true);
+ this.finalizeEdit(e.shiftKey, false, true);
}
break;
case 'Escape':
@@ -128,9 +185,9 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
};
@action
- finalizeEdit(value: string, shiftDown: boolean, lostFocus: boolean, enterKey: boolean) {
- //if (invalid) raise error
- if (this._props.SetValue(value, shiftDown, enterKey)) {
+ finalizeEdit(shiftDown: boolean, lostFocus: boolean, enterKey: boolean) {
+ this.setContent(this._content);
+ if (this._props.SetValue(this._content, shiftDown, enterKey)) {
this._editing = false;
} else {
this._editing = false;
@@ -151,31 +208,32 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
this._props.fieldContents ? <FieldView {...this._props.fieldContents} /> : this.props.contents ? this._props.contents?.valueOf() : ''
}
</span>
- }
+ };
renderEditor = () => {
return (
- <div
+ <div
contentEditable
- className="editableView-input"
+ className='editableView-static'
ref={r => { this._inputref = r; }}
style={{ overflow: 'auto', minHeight: `min(100%, ${(this._props.GetValue()?.split('\n').length || 1) * 15})`, minWidth: 20, }}
- onBlur={e => this.finalizeEdit(this._inputref ? this._inputref.innerText : '', false, true, false)}
+ onBlur={e => this.finalizeEdit(false, true, false)}
autoFocus
onInput={this.onChange}
onKeyDown={this.onKeyDown}
onPointerDown={e => e.stopPropagation}
onClick={e => e.stopPropagation}
onPointerUp={e => e.stopPropagation}
+ dangerouslySetInnerHTML={{ __html: `<span>${this._displayedContent}</span>` }}
>
</div>
);
- }
+ };
render() {
const gval = this._props.GetValue()?.replace(/\n/g, '\\r\\n');
if ((this._editing && gval !== undefined)) {
- return <div>{this.renderEditor()}</div>;
+ return <div className={`editableView-container-editing${this._props.oneLine ? '-oneLine' : ''}`}>{this.renderEditor()}</div>;
} else return (
this._props.contents instanceof ObjectField ? null : (
<div
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 17fff7bf1..74c001397 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -177,7 +177,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
width: '100%',
pointerEvents: this.lockedInteraction ? 'none' : pointerEvents,
}}>
- <EditableView
+ <SchemaCellField
highlightCells={this.adjustedHighlight}
ref={r => selectedCell(this._props) && this._props.autoFocus && r?.setIsFocused(true)}
oneLine={this._props.oneLine}
@@ -251,7 +251,6 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
e.stopPropagation();
} else !selectedCell(this._props) && this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
}
- console.log(this._props.Document.title);
})}
style={{ padding: this._props.padding,
maxWidth: this._props.maxWidth?.(),