aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-10-10 20:06:17 -0400
committerbobzel <zzzman@gmail.com>2024-10-10 20:06:17 -0400
commit962302d41ba5b086818f5db9ea5103c1e754b66f (patch)
treefe7b36ce2ac3c8276e4175e4dd8d5e223e1373a7 /src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
parent3a35e2687e3c7b0c864dd4f00b1002ff088b56d3 (diff)
parent040a1c5fd3e80606793e65be3ae821104460511b (diff)
Merge branch 'master' into alyssa-starter
Diffstat (limited to 'src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx')
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx253
1 files changed, 220 insertions, 33 deletions
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index e0ed8d01e..9ffdd812f 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -1,77 +1,264 @@
-/* eslint-disable react/no-unused-prop-types */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { setupMoveUpEvents } from '../../../../ClientUtils';
+import { returnEmptyFilter, returnFalse, returnZero, setupMoveUpEvents } from '../../../../ClientUtils';
import { emptyFunction } from '../../../../Utils';
-import { Colors } from '../../global/globalEnums';
import './CollectionSchemaView.scss';
+import { EditableView } from '../../EditableView';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { DefaultStyleProvider, returnEmptyDocViewList } from '../../StyleProvider';
+import { FieldViewProps } from '../../nodes/FieldView';
+import { Doc, returnEmptyDoclist } from '../../../../fields/Doc';
+import { dropActionType } from '../../../util/DropActionTypes';
+import { Transform } from '../../../util/Transform';
+import { SchemaTableCell } from './SchemaTableCell';
+import { DocCast } from '../../../../fields/Types';
+import { computedFn } from 'mobx-utils';
+import { CollectionSchemaView } from './CollectionSchemaView';
+import { undoable } from '../../../util/UndoManager';
+import { IconButton, Size } from 'browndash-components';
+
+export enum SchemaFieldType {
+ Header,
+ Cell,
+}
export interface SchemaColumnHeaderProps {
+ Document: Doc;
+ autoFocus?: boolean;
columnKeys: string[];
columnWidths: number[];
columnIndex: number;
- sortField: string;
- sortDesc: boolean;
+ schemaView: CollectionSchemaView;
+ keysDropdown: React.JSX.Element;
+ //cleanupField: (s: string) => string;
isContentActive: (outsideReaction?: boolean | undefined) => boolean | undefined;
setSort: (field: string | undefined, desc?: boolean) => void;
removeColumn: (index: number) => void;
rowHeight: () => number;
- resizeColumn: (e: React.PointerEvent, index: number) => void;
+ resizeColumn: (e: React.PointerEvent, index: number, rightSide: boolean) => void;
dragColumn: (e: PointerEvent, index: number) => boolean;
openContextMenu: (x: number, y: number, index: number) => void;
setColRef: (index: number, ref: HTMLDivElement) => void;
+ rootSelected?: () => boolean;
+ columnWidth: () => number;
+ finishEdit?: () => void; // notify container that edit is over (eg. to hide view in DashFieldView)
+ //transform: () => Transform;
}
@observer
-export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> {
- get fieldKey() {
- return this.props.columnKeys[this.props.columnIndex];
+export class SchemaColumnHeader extends ObservableReactComponent<SchemaColumnHeaderProps> {
+ private _inputRef: EditableView | null = null;
+ @observable _altTitle: string | undefined = undefined;
+ @observable _showMenuIcon: boolean = false;
+
+ @computed get fieldKey() {
+ return this._props.columnKeys[this._props.columnIndex];
}
- @action
- sortClicked = (e: React.PointerEvent) => {
- e.stopPropagation();
- e.preventDefault();
- if (this.props.sortField === this.fieldKey && this.props.sortDesc) {
- this.props.setSort(undefined);
- } else if (this.props.sortField === this.fieldKey) {
- this.props.setSort(this.fieldKey, true);
- } else {
- this.props.setSort(this.fieldKey, false);
- }
+ constructor(props: SchemaColumnHeaderProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ getFinfo = computedFn((fieldKey: string) => this._props.schemaView?.fieldInfos.get(fieldKey));
+ setColumnValues = (field: string, defaultValue: string) => {
+ this._props.schemaView?.setKey(field, defaultValue, this._props.columnIndex);
+ };
+ @action updateAlt = (newAlt: string) => {
+ this._altTitle = newAlt;
+ };
+ updateKeyDropdown = (value: string) => {
+ this._props.schemaView.updateKeySearch(value);
+ };
+ openKeyDropdown = () => {
+ !this._props.schemaView._colBeingDragged && this._props.schemaView.openNewColumnMenu(this._props.columnIndex, false);
+ };
+ toggleEditing = (editing: boolean) => {
+ this._inputRef?.setIsEditing(editing);
+ this._inputRef?.setIsFocused(editing);
};
@action
- onPointerDown = (e: React.PointerEvent) => {
- this.props.isContentActive(true) && setupMoveUpEvents(this, e, moveEv => this.props.dragColumn(moveEv, this.props.columnIndex), emptyFunction, emptyFunction);
+ setupDrag = (e: React.PointerEvent) => {
+ this._props.isContentActive(true) && setupMoveUpEvents(this, e, moveEv => this._props.dragColumn(moveEv, this._props.columnIndex), emptyFunction, emptyFunction);
+ };
+
+ renderProps = (props: SchemaColumnHeaderProps) => {
+ const { columnKeys, columnWidth, Document } = props;
+ const fieldKey = columnKeys[props.columnIndex];
+ const color = 'black';
+ const fieldProps: FieldViewProps = {
+ childFilters: returnEmptyFilter,
+ childFiltersByRanges: returnEmptyFilter,
+ docViewPath: returnEmptyDocViewList,
+ searchFilterDocs: returnEmptyDoclist,
+ styleProvider: DefaultStyleProvider,
+ isSelected: returnFalse,
+ setHeight: returnFalse,
+ select: emptyFunction,
+ dragAction: dropActionType.move,
+ renderDepth: 1,
+ noSidebar: true,
+ isContentActive: returnFalse,
+ whenChildContentsActiveChanged: emptyFunction,
+ ScreenToLocalTransform: Transform.Identity,
+ focus: emptyFunction,
+ addDocTab: SchemaTableCell.addFieldDoc,
+ pinToPres: returnZero,
+ Document: DocCast(Document.rootDocument, Document),
+ fieldKey: fieldKey,
+ PanelWidth: columnWidth,
+ PanelHeight: props.rowHeight,
+ rootSelected: props.rootSelected,
+ };
+ const readOnly = this.getFinfo(fieldKey)?.readOnly ?? false;
+ const cursor = !readOnly ? 'text' : 'default';
+ const pointerEvents: 'all' | 'none' = 'all';
+ return { color, fieldProps, cursor, pointerEvents };
};
+ @computed get editableView() {
+ const { color, fieldProps, pointerEvents } = this.renderProps(this._props);
+
+ return <div className='schema-column-edit-wrapper' onPointerUp={() => {
+ SchemaColumnHeader.isDefaultField(this.fieldKey) && this.openKeyDropdown();
+ this._props.schemaView.deselectAllCells();
+ }}
+ style={{
+ color,
+ width: '100%',
+ pointerEvents,
+ }}>
+ <EditableView
+ ref={r => {this._inputRef = r; this._props.autoFocus && r?.setIsFocused(true)}}
+ oneLine={true}
+ allowCRs={false}
+ contents={''}
+ onClick={this.openKeyDropdown}
+ fieldContents={fieldProps}
+ editing={undefined}
+ placeholder={'Add key'}
+ updateAlt={this.updateAlt} // alternate title to display
+ updateSearch={this.updateKeyDropdown}
+ inputString={true}
+ inputStringPlaceholder={'Add key'}
+ GetValue={() => {
+ if (SchemaColumnHeader.isDefaultField(this.fieldKey)) return '';
+ else if (this._altTitle) return this._altTitle;
+ else return this.fieldKey;
+ }}
+ SetValue={undoable((value: string, shiftKey?: boolean, enterKey?: boolean) => {
+ if (enterKey) {
+ // if shift & enter, set value of each cell in column
+ this.setColumnValues(value, '');
+ this._altTitle = undefined;
+ this._props.finishEdit?.();
+ return true;
+ }
+ this._props.finishEdit?.();
+ return true;
+ }, 'edit column header')}/>
+ </div>
+ }
+
+ public static isDefaultField = (key: string) => {
+ const defaultPattern = /EmptyColumnKey/;
+ const isDefault: boolean = defaultPattern.exec(key) != null;
+ return isDefault;
+ };
+
+ get headerButton() {
+ const toRender = SchemaColumnHeader.isDefaultField(this.fieldKey) ? (
+ <IconButton
+ icon={<FontAwesomeIcon icon="trash" size="sm" />}
+ size={Size.XSMALL}
+ color={'black'}
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ undoable(clickEv => {
+ clickEv.stopPropagation();
+ this._props.schemaView.removeColumn(this._props.columnIndex);
+ }, 'open column menu')
+ )
+ }
+ />
+ ) : (
+ <IconButton
+ icon={<FontAwesomeIcon icon="caret-down" size="lg" />}
+ size={Size.XSMALL}
+ color={'black'}
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ undoable(clickEv => {
+ clickEv.stopPropagation();
+ this._props.openContextMenu(e.clientX, e.clientY, this._props.columnIndex);
+ }, 'open column menu')
+ )
+ }
+ />
+ );
+
+ return toRender;
+ }
+
+ @action handlePointerEnter = () => { this._showMenuIcon = true; } // prettier-ignore
+ @action handlePointerLeave = () => { this._showMenuIcon = false; } // prettier-ignore
+
+ @computed get displayButton() {
+ return this._showMenuIcon;
+ }
+
render() {
return (
<div
className="schema-column-header"
style={{
- width: this.props.columnWidths[this.props.columnIndex],
+ width: this._props.columnWidths[this._props.columnIndex],
+ }}
+ onPointerEnter={() => {
+ this.handlePointerEnter();
+ }}
+ onPointerLeave={() => {
+ this.handlePointerLeave();
+ }}
+ onPointerDown={e => {
+ this.setupDrag(e);
+ setupMoveUpEvents(
+ this,
+ e,
+ () => {
+ return this._inputRef?.setIsEditing(false) ?? false;
+ },
+ emptyFunction,
+ emptyFunction
+ );
}}
- onPointerDown={this.onPointerDown}
ref={col => {
if (col) {
- this.props.setColRef(this.props.columnIndex, col);
+ this._props.setColRef(this._props.columnIndex, col);
}
}}>
- <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-column-resizer left" onPointerDown={e => this._props.resizeColumn(e, this._props.columnIndex, false)} />
+
+ <div className="schema-header-text">{this.editableView}</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-header-button" style={{ opacity: this.displayButton ? '1.0' : '0.0' }}>
+ {this.headerButton}
</div>
</div>
+
+ <div className="schema-column-resizer right" onPointerDown={e => this._props.resizeColumn(e, this._props.columnIndex, true)} />
</div>
);
}