aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorNathan-SR <144961007+Nathan-SR@users.noreply.github.com>2024-06-17 12:07:00 -0400
committerNathan-SR <144961007+Nathan-SR@users.noreply.github.com>2024-06-17 12:07:00 -0400
commit69153ed435cbbc10637563b8bb576a80f8c0693f (patch)
treee889ae54add45680b20e92d527df631e5a3b0cfe /src/client/views/collections
parentc472f9844ed2806f7965713cf618363210e37de1 (diff)
schema cell editable field class started
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/collectionSchema/SchemaCellField.tsx195
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx4
2 files changed, 197 insertions, 2 deletions
diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
new file mode 100644
index 000000000..5f758683d
--- /dev/null
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -0,0 +1,195 @@
+import { IReactionDisposer, action, makeObservable, observable, reaction, runInAction } from "mobx";
+import { ObservableReactComponent } from "../../ObservableReactComponent";
+import { observer } from "mobx-react";
+import { OverlayView } from "../../OverlayView";
+import { DocumentIconContainer } from "../../nodes/DocumentIcon";
+import React, { FormEvent } from "react";
+import { FieldView, FieldViewProps } from "../../nodes/FieldView";
+import { ObjectField } from "../../../../fields/ObjectField";
+
+export interface SchemaCellFieldProps {
+ contents: any;
+ fieldContents?: FieldViewProps;
+ editing?: boolean;
+ oneLine?: boolean;
+ highlightCells?: (text: string) => void;
+ GetValue(): string | undefined;
+ SetValue(value: string, shiftDown?: boolean, enterKey?: boolean): boolean;
+}
+
+@observer
+export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldProps> {
+
+ private _disposers: { [name: string]: IReactionDisposer } = {};
+ private _inputref: HTMLDivElement | null = null;
+ _overlayDisposer?: () => void;
+ @observable _editing: boolean = false;
+
+ constructor(props: SchemaCellFieldProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ componentDidMount(): void {
+ this._disposers.editing = reaction(
+ () => this._editing,
+ editing => {
+ if (editing) {
+ setTimeout(() => {
+ if (this._inputref?.innerText.startsWith('=') || this._inputref?.innerText.startsWith(':=')) {
+ this._overlayDisposer?.();
+ this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+ this._props.highlightCells?.(this._props.GetValue() ?? '');
+ }
+ });
+ } else {
+ this._overlayDisposer?.();
+ this._overlayDisposer = undefined;
+ this._props.highlightCells?.('');
+ }
+ },
+ { fireImmediately: true }
+ );
+ }
+
+ 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);
+ } else
+ runInAction(() => {
+ if (this._props.editing !== undefined) this._editing = this._props.editing;
+ });
+ }
+
+ componentWillUnmount(): void {
+ this._overlayDisposer?.();
+ this._disposers.editing?.();
+ this._inputref?.innerText && this.finalizeEdit(this._inputref.innerText, false, true, false);
+ }
+
+ @action
+ setIsFocused = (value: boolean) => {
+ const wasFocused = this._editing;
+ this._editing = value;
+ return wasFocused !== this._editing;
+ };
+
+ onChange = (e: FormEvent<HTMLInputElement>) => {
+ const targVal = e.currentTarget.innerText;
+ if (!(targVal.startsWith(':=') || targVal.startsWith('='))) {
+ this._overlayDisposer?.();
+ this._overlayDisposer = undefined;
+ } else if (!this._overlayDisposer) {
+ this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+ }
+ this._props.highlightCells?.(targVal);
+ };
+
+ @action
+ onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ if (e.nativeEvent.defaultPrevented) return; // hack .. DashFieldView grabs native events, but react ignores stoppedPropagation and preventDefault, so we need to check it here
+ switch (e.key) {
+ case 'Tab':
+ e.stopPropagation();
+ this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, false);
+ break;
+ case 'Backspace':
+ e.stopPropagation();
+ break;
+ case 'Enter':
+ e.stopPropagation();
+ if (!e.ctrlKey) {
+ this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, true);
+ }
+ break;
+ case 'Escape':
+ e.stopPropagation();
+ this._editing = false;
+ break;
+ case 'ArrowUp': case 'ArrowDown': case 'ArrowLeft': case 'ArrowRight': //prettier-ignore
+ e.stopPropagation();
+ break;
+ case 'Shift': case 'Alt': case 'Meta': case 'Control': case ':': //prettier-ignore
+ break;
+ // eslint-disable-next-line no-fallthrough
+ default:
+ break;
+ }
+ };
+
+
+ @action
+ onClick = (e?: React.MouseEvent) => {
+ if (this._props.editing !== false) {
+ e?.nativeEvent.stopPropagation();
+ this._editing = true;
+ }
+ };
+
+ @action
+ finalizeEdit(value: string, shiftDown: boolean, lostFocus: boolean, enterKey: boolean) {
+ //if (invalid) raise error
+ if (this._props.SetValue(value, shiftDown, enterKey)) {
+ this._editing = false;
+ } else {
+ this._editing = false;
+ !lostFocus &&
+ setTimeout(
+ action(() => {
+ this._editing = true;
+ }),
+ 0
+ );
+ }
+ }
+
+ staticDisplay = () => {
+ return <span className='editableView-static'>
+ {
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ this._props.fieldContents ? <FieldView {...this._props.fieldContents} /> : this.props.contents ? this._props.contents?.valueOf() : ''
+ }
+ </span>
+ }
+
+ renderEditor = () => {
+ return (
+ <div
+ contentEditable
+ className="editableView-input"
+ 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)}
+ autoFocus
+ onInput={this.onChange}
+ onKeyDown={this.onKeyDown}
+ onPointerDown={e => e.stopPropagation}
+ onClick={e => e.stopPropagation}
+ onPointerUp={e => e.stopPropagation}
+ >
+ </div>
+ );
+ }
+
+ render() {
+ const gval = this._props.GetValue()?.replace(/\n/g, '\\r\\n');
+ if ((this._editing && gval !== undefined)) {
+ return <div>{this.renderEditor()}</div>;
+ } else return (
+ this._props.contents instanceof ObjectField ? null : (
+ <div
+ className={`editableView-container-editing${this._props.oneLine ? '-oneLine' : ''}`}
+ style={{
+ minHeight: '10px',
+ whiteSpace: this._props.oneLine ? 'nowrap' : 'pre-line',
+ width: '100%',
+ }}
+ onClick={this.onClick}>
+ {this.staticDisplay()}
+ </div>
+ )
+ );
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 69880b280..79f9067e2 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -34,6 +34,7 @@ import { CollectionSchemaView, FInfotoColType } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
import { SchemaColumnHeader } from './SchemaColumnHeader';
import { ContextMenu } from '../../ContextMenu';
+import { SchemaCellField } from './SchemaCellField';
export interface SchemaTableCellProps {
Document: Doc;
@@ -176,11 +177,10 @@ 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}
- allowCRs={this._props.allowCRs}
contents={undefined}
fieldContents={fieldProps}
editing={selectedCell(this._props) ? undefined : false}