aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/formattedText/DashFieldView.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-05-03 10:23:46 -0400
committerbobzel <zzzman@gmail.com>2024-05-03 10:23:46 -0400
commit723c8b33ade753764d1d02b130c189fb65e20425 (patch)
treee278304fdace45a2c38562e72a3ccd2e8e91b759 /src/client/views/nodes/formattedText/DashFieldView.tsx
parentf410a7b314dd78244e18c9c52140b67b37ab0c87 (diff)
parent2caf7b7bb80b663b6ba585f88cdbd2d725f8505e (diff)
Merge branch 'master' into nathan-starter
Diffstat (limited to 'src/client/views/nodes/formattedText/DashFieldView.tsx')
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx136
1 files changed, 103 insertions, 33 deletions
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 6186b3d99..17b8b53e7 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
@@ -22,27 +22,49 @@ import { OpenWhere } from '../DocumentView';
import './DashFieldView.scss';
import { FormattedTextBox } from './FormattedTextBox';
import { DocData } from '../../../../fields/DocSymbols';
+import { NodeSelection } from 'prosemirror-state';
export class DashFieldView {
dom: HTMLDivElement; // container for label and value
root: any;
node: any;
tbox: FormattedTextBox;
+ getpos: any;
+ @observable _nodeSelected = false;
+ NodeSelected = () => this._nodeSelected;
unclickable = () => !this.tbox._props.rootSelected?.() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview);
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
+ makeObservable(this);
+ const self = this;
this.node = node;
this.tbox = tbox;
+ this.getpos = getPos;
this.dom = document.createElement('div');
this.dom.style.width = node.attrs.width;
this.dom.style.height = node.attrs.height;
this.dom.style.position = 'relative';
this.dom.style.display = 'inline-block';
- this.dom.onkeypress = function (e: any) {
+ const tBox = this.tbox;
+ this.dom.onkeypress = function (e: KeyboardEvent) {
e.stopPropagation();
};
- this.dom.onkeydown = function (e: any) {
+ this.dom.onkeydown = function (e: KeyboardEvent) {
e.stopPropagation();
+ if (e.key === 'Tab') {
+ e.preventDefault();
+ const editor = tbox.EditorView;
+ if (editor) {
+ const state = editor.state;
+ for (var i = self.getpos() + 1; i < state.doc.content.size; i++) {
+ if (state.doc.nodeAt(i)?.type.name === state.schema.nodes.dashField.name) {
+ editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(i))));
+ return;
+ }
+ }
+ // tBox.setFocus(state.selection.to);
+ }
+ }
};
this.dom.onkeyup = function (e: any) {
e.stopPropagation();
@@ -62,9 +84,9 @@ export class DashFieldView {
width={node.attrs.width}
height={node.attrs.height}
hideKey={node.attrs.hideKey}
+ hideValue={node.attrs.hideValue}
editable={node.attrs.editable}
- expanded={node.attrs.expanded}
- dataDoc={node.attrs.dataDoc}
+ nodeSelected={this.NodeSelected}
tbox={tbox}
/>
);
@@ -77,9 +99,11 @@ export class DashFieldView {
});
}
deselectNode() {
+ runInAction(() => (this._nodeSelected = false));
this.dom.classList.remove('ProseMirror-selectednode');
}
selectNode() {
+ setTimeout(() => runInAction(() => (this._nodeSelected = true)), 100);
this.dom.classList.add('ProseMirror-selectednode');
}
}
@@ -88,12 +112,12 @@ interface IDashFieldViewInternal {
fieldKey: string;
docId: string;
hideKey: boolean;
+ hideValue: boolean;
tbox: FormattedTextBox;
width: number;
height: number;
editable: boolean;
- expanded: boolean;
- dataDoc: boolean;
+ nodeSelected: () => boolean;
node: any;
getPos: any;
unclickable: () => boolean;
@@ -106,14 +130,14 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
_fieldKey: string;
_fieldRef = React.createRef<HTMLDivElement>();
@observable _dashDoc: Doc | undefined = undefined;
- @observable _expanded = this._props.expanded;
+ @observable _expanded = this._props.nodeSelected();
constructor(props: IDashFieldViewInternal) {
super(props);
makeObservable(this);
this._fieldKey = this._props.fieldKey;
this._textBoxDoc = this._props.tbox.Document;
- const setDoc = (doc: Doc) => (this._dashDoc = this._props.dataDoc ? doc[DocData] : doc);
+ const setDoc = action((doc: Doc) => (this._dashDoc = doc));
if (this._props.docId) {
DocServer.GetRefField(this._props.docId).then(dashDoc => dashDoc instanceof Doc && setDoc(dashDoc));
@@ -132,22 +156,29 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
componentWillUnmount() {
this._reactionDisposer?.();
}
- isRowActive = () => this._expanded && this._props.editable;
- finishEdit = action(() => (this._expanded = false));
- selectedCell = (): [Doc, number] => [this._dashDoc!, 0];
- selectedCells = () => [this._dashDoc!];
+ isRowActive = () => (this._props.nodeSelected() || this._expanded) && this._props.editable;
+ finishEdit = action(() => {
+ if (this._expanded) {
+ this._expanded = false;
+ // if the edit finishes, then we want to lose focus on the textBox unless something else in the textBox got focus
+ // the timeout allows switching focus from one dashFieldView to another in the same text box
+ setTimeout(() => !this._props.tbox.ProseRef?.contains(document.activeElement) && this._props.tbox._props.onBlur?.());
+ }
+ });
+ selectedCells = () => (this._dashDoc ? [this._dashDoc] : undefined);
+ columnWidth = () => Math.min(this._props.tbox._props.PanelWidth(), Math.max(50, this._props.tbox._props.PanelWidth() - 100)); // try to leave room for the fieldKey
// set the display of the field's value (checkbox for booleans, span of text for strings)
@computed get fieldValueContent() {
return !this._dashDoc ? null : (
- <div onClick={action(e => (this._expanded = !this._props.editable ? !this._expanded : true))} style={{ fontSize: 'smaller', width: this._props.hideKey ? this._props.tbox._props.PanelWidth() - 20 : undefined }}>
+ <div onClick={action(e => (this._expanded = !this._props.editable ? !this._expanded : true))} style={{ fontSize: 'smaller', width: !this._hideKey && this._expanded ? this.columnWidth() : undefined }}>
<SchemaTableCell
Document={this._dashDoc}
col={0}
deselectCell={emptyFunction}
selectCell={emptyFunction}
maxWidth={this._props.hideKey || this._hideKey ? undefined : this._props.tbox._props.PanelWidth}
- columnWidth={returnZero}
+ columnWidth={this._expanded || this._props.nodeSelected() ? this.columnWidth : returnZero}
selectedCells={this.selectedCells}
selectedCol={returnZero}
fieldKey={this._fieldKey}
@@ -158,10 +189,12 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
setColumnValues={returnFalse}
setSelectedColumnValues={returnFalse}
allowCRs={true}
- oneLine={!this._expanded}
+ oneLine={!this._expanded && !this._props.nodeSelected()}
finishEdit={this.finishEdit}
transform={Transform.Identity}
menuTarget={null}
+ autoFocus={true}
+ rootSelected={this._props.tbox._props.rootSelected}
/>
</div>
);
@@ -185,31 +218,49 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
};
toggleFieldHide = undoable(
- action(() => this._dashDoc && (this._dashDoc[this._fieldKey + '_hideKey'] = !this._dashDoc[this._fieldKey + '_hideKey'])),
+ action(() => {
+ const editor = this._props.tbox.EditorView!;
+ editor.dispatch(editor.state.tr.setNodeMarkup(this._props.getPos(), this._props.node.type, { ...this._props.node.attrs, hideKey: this._props.node.attrs.hideValue ? false : !this._props.node.attrs.hideKey ? true : false }));
+ }),
'hideKey'
);
+ toggleValueHide = undoable(
+ action(() => {
+ const editor = this._props.tbox.EditorView!;
+ editor.dispatch(editor.state.tr.setNodeMarkup(this._props.getPos(), this._props.node.type, { ...this._props.node.attrs, hideValue: this._props.node.attrs.hideKey ? false : !this._props.node.attrs.hideValue ? true : false }));
+ }),
+ 'hideValue'
+ );
+
@computed get _hideKey() {
- return this._dashDoc && this._dashDoc[this._fieldKey + '_hideKey'];
+ return this._props.hideKey && !this._expanded;
+ }
+
+ @computed get _hideValue() {
+ return this._props.hideValue && !this._props.nodeSelected();
}
// clicking on the label creates a pivot view collection of all documents
// in the same collection. The pivot field is the fieldKey of this label
- onPointerDownLabelSpan = (e: any) => {
+ onPointerDownLabelSpan = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, returnFalse, returnFalse, e => {
DashFieldViewMenu.createFieldView = this.createPivotForField;
DashFieldViewMenu.toggleFieldHide = this.toggleFieldHide;
+ DashFieldViewMenu.toggleValueHide = this.toggleValueHide;
DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16, this._fieldKey);
+ const editor = this._props.tbox.EditorView!;
+ setTimeout(() => editor.dispatch(editor.state.tr.setSelection(new NodeSelection(editor.state.doc.resolve(this._props.getPos())))), 100);
});
};
@undoBatch
selectVal = (event: React.ChangeEvent<HTMLSelectElement> | undefined) => {
- event && this._dashDoc && (this._dashDoc[this._fieldKey] = event.target.value);
+ event && this._dashDoc && (this._dashDoc[this._fieldKey] = event.target.value === '-unset-' ? undefined : event.target.value);
};
@computed get values() {
- if (this._props.expanded) return [];
+ if (this._props.nodeSelected()) return [];
const vals = FilterPanel.gatherFieldValues(DocListCast(Doc.ActiveDashboard?.data), this._fieldKey, []);
return vals.strings.map(facet => ({ value: facet, label: facet }));
@@ -218,21 +269,22 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
render() {
return (
<div
- className="dashFieldView"
+ className={`dashFieldView${this.isRowActive() ? '-active' : ''}`}
ref={this._fieldRef}
style={{
width: this._props.width,
height: this._props.height,
pointerEvents: this._props.tbox._props.rootSelected?.() || this._props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
}}>
- {this._props.hideKey || this._hideKey ? null : (
+ {this._hideKey ? null : (
<span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
{(Doc.AreProtosEqual(DocCast(this._textBoxDoc.rootDocument) ?? this._textBoxDoc, DocCast(this._dashDoc?.rootDocument) ?? this._dashDoc) ? '' : this._dashDoc?.title + ':') + this._fieldKey}
</span>
)}
- {this._props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
+ {this._props.fieldKey.startsWith('#') || this._hideValue ? null : this.fieldValueContent}
{!this.values.length ? null : (
- <select onChange={this.selectVal} style={{ height: '10px', width: '15px', fontSize: '12px', background: 'transparent' }}>
+ <select className="dashFieldView-select" tabIndex={-1} defaultValue={this._dashDoc && Field.toKeyValueString(this._dashDoc, this._fieldKey)} onChange={this.selectVal}>
+ <option value="-unset-">-unset-</option>
{this.values.map(val => (
<option value={val.value}>{val.label}</option>
))}
@@ -247,6 +299,7 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: DashFieldViewMenu;
static createFieldView: (e: React.MouseEvent) => void = emptyFunction;
static toggleFieldHide: () => void = emptyFunction;
+ static toggleValueHide: () => void = emptyFunction;
constructor(props: any) {
super(props);
DashFieldViewMenu.Instance = this;
@@ -260,6 +313,10 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
DashFieldViewMenu.toggleFieldHide();
DashFieldViewMenu.Instance.fadeOut(true);
};
+ toggleValueHide = (e: React.MouseEvent) => {
+ DashFieldViewMenu.toggleValueHide();
+ DashFieldViewMenu.Instance.fadeOut(true);
+ };
@observable _fieldKey = '';
@@ -275,14 +332,27 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
};
render() {
return this.getElement(
- <Tooltip key="trash" title={<div className="dash-tooltip">{`Show Pivot Viewer for '${this._fieldKey}'`}</div>}>
- <button className="antimodeMenu-button" onPointerDown={this.showFields}>
- <FontAwesomeIcon icon="eye" size="lg" />
- </button>
- <button className="antimodeMenu-button" onPointerDown={this.toggleFieldHide}>
- <FontAwesomeIcon icon="bullseye" size="lg" />
- </button>
- </Tooltip>
+ <>
+ <Tooltip key="trash" title={<div className="dash-tooltip">{`Show Pivot Viewer for '${this._fieldKey}'`}</div>}>
+ <button className="antimodeMenu-button" onPointerDown={this.showFields}>
+ <FontAwesomeIcon icon="eye" size="sm" />
+ </button>
+ </Tooltip>
+ {this._fieldKey.startsWith('#') ? null : (
+ <Tooltip key="key" title={<div className="dash-tooltip">Toggle view of field key</div>}>
+ <button className="antimodeMenu-button" onPointerDown={this.toggleFieldHide}>
+ <FontAwesomeIcon icon="bullseye" size="sm" />
+ </button>
+ </Tooltip>
+ )}
+ {this._fieldKey.startsWith('#') ? null : (
+ <Tooltip key="val" title={<div className="dash-tooltip">Toggle view of field value</div>}>
+ <button className="antimodeMenu-button" onPointerDown={this.toggleValueHide}>
+ <FontAwesomeIcon icon="hashtag" size="sm" />
+ </button>
+ </Tooltip>
+ )}
+ </>
);
}
}