aboutsummaryrefslogtreecommitdiff
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
parentc472f9844ed2806f7965713cf618363210e37de1 (diff)
schema cell editable field class started
-rw-r--r--src/client/views/EditableView.tsx13
-rw-r--r--src/client/views/collections/collectionSchema/SchemaCellField.tsx195
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx4
3 files changed, 206 insertions, 6 deletions
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index f94f4be86..f5271f749 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -58,6 +58,7 @@ export interface EditableProps {
wrap?: string; // nowrap, pre-wrap, etc
schemaFieldType?: SchemaFieldType;
+ prohibitedText?: Array<string>;
onClick?: () => void;
updateAlt?: (newAlt: string) => void;
updateSearch?: (value: string) => void;
@@ -203,8 +204,13 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
}
};
+ // checkForInvalidText = (text: string) => {
+ // const regX = new RegExp(new Array<string>(...this._props.prohibitedText), 'g')
+ // }
+
@action
finalizeEdit(value: string, shiftDown: boolean, lostFocus: boolean, enterKey: boolean) {
+ //if (invalid) raise error
if (this._props.SetValue(value, shiftDown, enterKey)) {
this._editing = false;
this._props.isEditingCallback?.(false);
@@ -252,7 +258,7 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
onChange: this._props.autosuggestProps.onChange,
}}
/>
- ) : this._props.oneLine !== false && this._props.GetValue()?.toString().indexOf('\n') === -1 ? (
+ ) : ( this._props.oneLine !== false && this._props.GetValue()?.toString().indexOf('\n') === -1 ? (
<input
className="editableView-input"
ref={r => { this._inputref = r; }} // prettier-ignore
@@ -284,7 +290,7 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
onClick={this.stopPropagation}
onPointerUp={this.stopPropagation}
/>
- );
+ ));
}
staticDisplay = () => {
@@ -319,8 +325,7 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
if ((this._editing && gval !== undefined)) {
return this._props.sizeToContent ? (
<div style={{ display: 'grid', minWidth: 100 }}>
- <div style={{ display: 'inline-block', position: 'relative', height: 0, width: '100%', overflow: 'hidden' }}>{gval}</div>
- {this.renderEditor()}
+ <div style={{ display: 'inline-block', position: 'relative', height: 0, width: '100%', overflow: 'hidden' }}>{this.renderEditor()}</div>
</div>
) : (
<div >
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}