diff options
Diffstat (limited to 'src/client/views/nodes/formattedText/DashFieldView.tsx')
-rw-r--r-- | src/client/views/nodes/formattedText/DashFieldView.tsx | 326 |
1 files changed, 171 insertions, 155 deletions
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 439d4785e..9903d0e8a 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -1,15 +1,20 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable jsx-a11y/control-has-associated-label */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction, trace } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; +import { NodeSelection } from 'prosemirror-state'; import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; +import { returnFalse, returnZero, setupMoveUpEvents } from '../../../../ClientUtils'; import { Doc, DocListCast, Field } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { listSpec } from '../../../../fields/Schema'; import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; import { Cast, DocCast } from '../../../../fields/Types'; -import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils'; +import { emptyFunction } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { CollectionViewType } from '../../../documents/DocumentTypes'; import { Transform } from '../../../util/Transform'; @@ -18,96 +23,73 @@ import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableCell'; import { FilterPanel } from '../../FilterPanel'; import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { OpenWhere } from '../DocumentView'; +import { OpenWhere } from '../OpenWhere'; 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; +@observer +export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> { + // eslint-disable-next-line no-use-before-define + 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; + } - 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'; - const tBox = this.tbox; - this.dom.onkeypress = function (e: KeyboardEvent) { - e.stopPropagation(); - }; - 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(); - }; - this.dom.onmousedown = function (e: any) { - e.stopPropagation(); - }; + showFields = (e: React.MouseEvent) => { + DashFieldViewMenu.createFieldView(e); + DashFieldViewMenu.Instance.fadeOut(true); + }; + toggleFieldHide = () => { + DashFieldViewMenu.toggleFieldHide(); + DashFieldViewMenu.Instance.fadeOut(true); + }; + toggleValueHide = () => { + DashFieldViewMenu.toggleValueHide(); + DashFieldViewMenu.Instance.fadeOut(true); + }; - this.root = ReactDOM.createRoot(this.dom); - this.root.render( - <DashFieldViewInternal - node={node} - unclickable={this.unclickable} - getPos={getPos} - fieldKey={node.attrs.fieldKey} - docId={node.attrs.docId} - width={node.attrs.width} - height={node.attrs.height} - hideKey={node.attrs.hideKey} - hideValue={node.attrs.hideValue} - editable={node.attrs.editable} - nodeSelected={this.NodeSelected} - tbox={tbox} - /> + @observable _fieldKey = ''; + + @action + public show = (x: number, y: number, fieldKey: string) => { + this._fieldKey = fieldKey; + this.jumpTo(x, y, true); + const hideMenu = () => { + this.fadeOut(true); + document.removeEventListener('pointerdown', hideMenu, true); + }; + document.addEventListener('pointerdown', hideMenu, true); + }; + render() { + return this.getElement( + <> + <Tooltip key="trash" title={<div className="dash-tooltip">{`Show Pivot Viewer for '${this._fieldKey}'`}</div>}> + <button type="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 type="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 type="button" className="antimodeMenu-button" onPointerDown={this.toggleValueHide}> + <FontAwesomeIcon icon="hashtag" size="sm" /> + </button> + </Tooltip> + )} + </> ); } - destroy() { - setTimeout(() => { - try { - this.root.unmount(); - } catch {} - }); - } - deselectNode() { - runInAction(() => (this._nodeSelected = false)); - this.dom.classList.remove('ProseMirror-selectednode'); - } - selectNode() { - setTimeout(() => runInAction(() => (this._nodeSelected = true)), 100); - this.dom.classList.add('ProseMirror-selectednode'); - } } - interface IDashFieldViewInternal { fieldKey: string; docId: string; @@ -137,7 +119,9 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi makeObservable(this); this._fieldKey = this._props.fieldKey; this._textBoxDoc = this._props.tbox.Document; - const setDoc = action((doc: Doc) => (this._dashDoc = 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)); @@ -157,7 +141,6 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi this._reactionDisposer?.(); } isRowActive = () => (this._props.nodeSelected() || this._expanded) && this._props.editable; - finishEdit = action(() => { if (this._expanded) { this._expanded = false; @@ -166,13 +149,17 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi setTimeout(() => !this._props.tbox.ProseRef?.contains(document.activeElement) && this._props.tbox._props.onBlur?.()); } }); - selectedCell = (): [Doc, number] | undefined => (this._dashDoc ? [this._dashDoc, 0] : undefined); + 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._hideKey && this._expanded ? this.columnWidth() : undefined }}> + <div + onClick={action(() => { + 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} @@ -180,27 +167,29 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi selectCell={emptyFunction} maxWidth={this._props.hideKey || this._hideKey ? undefined : this._props.tbox._props.PanelWidth} columnWidth={this._expanded || this._props.nodeSelected() ? this.columnWidth : returnZero} - selectedCell={this.selectedCell} + selectedCells={this.selectedCells} + selectedCol={returnZero} fieldKey={this._fieldKey} rowHeight={returnZero} isRowActive={this.isRowActive} padding={0} getFinfo={emptyFunction} setColumnValues={returnFalse} - allowCRs={true} + setSelectedColumnValues={returnFalse} + allowCRs oneLine={!this._expanded && !this._props.nodeSelected()} finishEdit={this.finishEdit} transform={Transform.Identity} menuTarget={null} - autoFocus={true} + autoFocus rootSelected={this._props.tbox._props.rootSelected} /> </div> ); } - createPivotForField = (e: React.MouseEvent) => { - let container = this._props.tbox.DocumentView?.().containerViewPath?.().lastElement(); + createPivotForField = () => { + const container = this._props.tbox.DocumentView?.().containerViewPath?.().lastElement(); if (container) { const embedding = Doc.MakeEmbedding(container.Document); embedding._type_collection = CollectionViewType.Time; @@ -219,7 +208,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi toggleFieldHide = 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, hideKey: this._props.node.attrs.hideValue ? false : !this._props.node.attrs.hideKey ? true : false })); + 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 })); }), 'hideKey' ); @@ -227,7 +216,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi 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 })); + 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 })); }), 'hideValue' ); @@ -243,11 +232,11 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi // 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: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, returnFalse, e => { + setupMoveUpEvents(this, e, returnFalse, returnFalse, moveEv => { DashFieldViewMenu.createFieldView = this.createPivotForField; DashFieldViewMenu.toggleFieldHide = this.toggleFieldHide; DashFieldViewMenu.toggleValueHide = this.toggleValueHide; - DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16, this._fieldKey); + DashFieldViewMenu.Instance.show(moveEv.clientX, moveEv.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); }); @@ -277,7 +266,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi }}> {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} + {(Doc.AreProtosEqual(DocCast(this._textBoxDoc.rootDocument) ?? this._textBoxDoc, DocCast(this._dashDoc?.rootDocument) ?? this._dashDoc) ? '' : (this._dashDoc?.title ?? '') + ':') + this._fieldKey} </span> )} {this._props.fieldKey.startsWith('#') || this._hideValue ? null : this.fieldValueContent} @@ -293,65 +282,92 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi ); } } -@observer -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; - } - - showFields = (e: React.MouseEvent) => { - DashFieldViewMenu.createFieldView(e); - DashFieldViewMenu.Instance.fadeOut(true); - }; - toggleFieldHide = (e: React.MouseEvent) => { - DashFieldViewMenu.toggleFieldHide(); - DashFieldViewMenu.Instance.fadeOut(true); - }; - toggleValueHide = (e: React.MouseEvent) => { - DashFieldViewMenu.toggleValueHide(); - DashFieldViewMenu.Instance.fadeOut(true); - }; - - @observable _fieldKey = ''; +export class DashFieldView { + dom: HTMLDivElement; // container for label and value + root: any; + node: any; + tbox: FormattedTextBox; + getpos: any; + @observable _nodeSelected = false; + NodeSelected = () => this._nodeSelected; - @action - public show = (x: number, y: number, fieldKey: string) => { - this._fieldKey = fieldKey; - this.jumpTo(x, y, true); - const hideMenu = () => { - this.fadeOut(true); - document.removeEventListener('pointerdown', hideMenu, true); + 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); + 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: KeyboardEvent) { + e.stopPropagation(); }; - document.addEventListener('pointerdown', hideMenu, true); - }; - 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="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> - )} - </> + this.dom.onkeydown = (e: KeyboardEvent) => { + e.stopPropagation(); + if (e.key === 'Tab') { + e.preventDefault(); + const editor = tbox.EditorView; + if (editor) { + const { state } = editor; + for (let i = this.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; + } + } + } + } + }; + this.dom.onkeyup = function (e: any) { + e.stopPropagation(); + }; + this.dom.onmousedown = function (e: any) { + e.stopPropagation(); + }; + + this.root = ReactDOM.createRoot(this.dom); + this.root.render( + <DashFieldViewInternal + node={node} + unclickable={this.unclickable} + getPos={getPos} + fieldKey={node.attrs.fieldKey} + docId={node.attrs.docId} + width={node.attrs.width} + height={node.attrs.height} + hideKey={node.attrs.hideKey} + hideValue={node.attrs.hideValue} + editable={node.attrs.editable} + nodeSelected={this.NodeSelected} + tbox={tbox} + /> ); } + destroy() { + setTimeout(() => { + try { + this.root.unmount(); + } catch { + /* empty */ + } + }); + } + deselectNode() { + runInAction(() => { + this._nodeSelected = false; + }); + this.dom.classList.remove('ProseMirror-selectednode'); + } + selectNode() { + setTimeout( + action(() => { + this._nodeSelected = true; + }), + 100 + ); + this.dom.classList.add('ProseMirror-selectednode'); + } } |