diff options
author | bobzel <zzzman@gmail.com> | 2023-05-13 09:55:48 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2023-05-13 09:55:48 -0400 |
commit | dca61505ba138eef3819b16b760ec81becf9329e (patch) | |
tree | f1bdf777d263760eea058ae0b6df40db831574a4 /src/client/views/nodes/formattedText/DashFieldView.tsx | |
parent | 8b0fa2ca2c454468f04221f52951051253571556 (diff) |
changed EditableViews to support oneline and multiline. Also added transformer UI to allow documents to be entered. changed transformer to write doc id's, not variables.. made schema view support oneline and fixed bug with docdecoration hader occluding things invisibly. updated web pages to be zoomable and for its anchors to update web page and scroll location properly. made autolinkanchor directly go to target on click.
Diffstat (limited to 'src/client/views/nodes/formattedText/DashFieldView.tsx')
-rw-r--r-- | src/client/views/nodes/formattedText/DashFieldView.tsx | 150 |
1 files changed, 28 insertions, 122 deletions
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index bf6fa2ec6..a9e59ab1c 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -2,18 +2,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable } from 'mobx'; import { observer } from 'mobx-react'; -import { NodeSelection } from 'prosemirror-state'; import * as ReactDOM from 'react-dom/client'; -import { DataSym, Doc, DocListCast, Field } from '../../../../fields/Doc'; +import { DataSym, Doc, Field } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { listSpec } from '../../../../fields/Schema'; import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; -import { ComputedField } from '../../../../fields/ScriptField'; import { Cast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils'; +import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { CollectionViewType } from '../../../documents/DocumentTypes'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; +import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableCell'; import { OpenWhere } from '../DocumentView'; import './DashFieldView.scss'; import { FormattedTextBox } from './FormattedTextBox'; @@ -70,10 +69,10 @@ export class DashFieldView { } catch {} }); } - deselectNode() { + @action deselectNode() { this.dom.classList.remove('ProseMirror-selectednode'); } - selectNode() { + @action selectNode() { this.dom.classList.add('ProseMirror-selectednode'); } } @@ -98,6 +97,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna _fieldKey: string; _fieldStringRef = React.createRef<HTMLSpanElement>(); @observable _dashDoc: Doc | undefined; + @observable _expanded = false; constructor(props: IDashFieldViewInternal) { super(props); @@ -114,126 +114,32 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna this._reactionDisposer?.(); } - public static multiValueDelimeter = ';'; - public static fieldContent(textBoxDoc: Doc, dashDoc: Doc, fieldKey: string) { - const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? ''; - const fval = dashVal instanceof List ? dashVal.join(DashFieldViewInternal.multiValueDelimeter) : StrCast(dashVal).startsWith(':=') || dashVal === '' ? Doc.Layout(textBoxDoc)[fieldKey] : dashVal; - return { boolVal: Cast(fval, 'boolean', null), strVal: Field.toString(fval as Field) || '' }; - } - // set the display of the field's value (checkbox for booleans, span of text for strings) @computed get fieldValueContent() { - if (this._dashDoc) { - const { boolVal, strVal } = DashFieldViewInternal.fieldContent(this._textBoxDoc, this._dashDoc, this._fieldKey); - // field value is a boolean, so use a checkbox or similar widget to display it - if (boolVal === true || boolVal === false) { - return ( - <input - className="dashFieldView-fieldCheck" - type="checkbox" - checked={boolVal} - onChange={e => { - if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = e.target.checked; - Doc.SetInPlace(this._dashDoc!, this._fieldKey, e.target.checked, true); - }} - /> - ); - } // field value is a string, so display it as an editable span - else { - // bcz: this is unfortunate, but since this React component is nested within a non-React text box (prosemirror), we can't - // use React events. Essentially, React events occur after native events have been processed, so corresponding React events - // will never fire because Prosemirror has handled the native events. So we add listeners for native events here. - return ( - <span - className="dashFieldView-fieldSpan" - contentEditable={!this.props.unclickable()} - style={{ display: strVal.length < 2 ? 'inline-block' : undefined }} - suppressContentEditableWarning={true} - defaultValue={strVal} - ref={r => { - r?.addEventListener('keydown', e => this.fieldSpanKeyDown(e, r)); - r?.addEventListener('blur', e => r && this.updateText(r.textContent!, false)); - r?.addEventListener( - 'pointerdown', - action(e => { - // let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span> - // while (target && !target.dataset?.targethrefs) target = target.parentElement; - this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new NodeSelection(this.props.tbox.EditorView!.state.doc.resolve(this.props.getPos())))); - // FormattedTextBoxComment.update(this.props.tbox, this.props.tbox.EditorView!, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc); - // e.stopPropagation(); - }) - ); - }}> - {strVal} - </span> - ); - } - } + 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 }}> + <SchemaTableCell + Document={this._dashDoc} + col={0} + deselectCell={emptyFunction} + selectCell={emptyFunction} + maxWidth={this.props.hideKey ? undefined : () => 100} + columnWidth={this.props.hideKey ? () => this.props.tbox.props.PanelWidth() - 20 : returnZero} + selectedCell={() => [this._dashDoc!, 0]} + fieldKey={this._fieldKey} + rowHeight={returnZero} + isRowActive={() => this._expanded && this.props.editable} + padding={0} + getFinfo={emptyFunction} + setColumnValues={returnFalse} + allowCRs={true} + oneLine={!this._expanded} + finishEdit={action(() => (this._expanded = false))} + /> + </div> + ); } - // we need to handle all key events on the input span or else they will propagate to prosemirror. - @action - fieldSpanKeyDown = (e: KeyboardEvent, span: HTMLSpanElement) => { - if (e.key === 'c' && (e.ctrlKey || e.metaKey)) { - navigator.clipboard.writeText(window.getSelection()?.toString() || ''); - return; - } - if (e.key === 'Enter') { - // handle the enter key by "submitting" the current text to Dash's database. - this.updateText(span.textContent!, true); - e.preventDefault(); // prevent default to avoid a newline from being generated and wiping out this field view - } - if (e.key === 'a' && (e.ctrlKey || e.metaKey)) { - // handle ctrl-A to select all the text within the span - if (window.getSelection) { - const range = document.createRange(); - range.selectNodeContents(span); - window.getSelection()!.removeAllRanges(); - window.getSelection()!.addRange(range); - } - e.preventDefault(); //prevent default so that all the text in the prosemirror text box isn't selected - } - if (!this.props.editable) { - e.preventDefault(); - } - e.stopPropagation(); // we need to handle all events or else they will propagate to prosemirror. - }; - - @action - updateText = (nodeText: string, forceMatch: boolean) => { - if (nodeText) { - const newText = nodeText.startsWith(':=') || nodeText.startsWith('=:=') ? ':=-computed-' : nodeText; - // look for a document whose id === the fieldKey being displayed. If there's a match, then that document - // holds the different enumerated values for the field in the titles of its collected documents. - // if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down. - DocServer.GetRefField(this._fieldKey).then(options => { - let modText = ''; - options instanceof Doc && DocListCast(options.data).forEach(opt => (forceMatch ? StrCast(opt.title).startsWith(newText) : StrCast(opt.title) === newText) && (modText = StrCast(opt.title))); - if (modText) { - // elementfieldSpan.innerHTML = this._dashDoc![this._fieldKey as string] = modText; - Doc.SetInPlace(this._dashDoc!, this._fieldKey, modText, true); - } // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key - else if (nodeText.startsWith(':=')) { - this._dashDoc![DataSym][this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(2)); - } else if (nodeText.startsWith('=:=')) { - Doc.Layout(this._textBoxDoc)[this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(3)); - } else { - if (Number(newText).toString() === newText) { - if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText); - Doc.SetInPlace(this._dashDoc!, this._fieldKey, Number(newText), true); - } else { - const splits = newText.split(DashFieldViewInternal.multiValueDelimeter); - if (!this._textBoxDoc[this._fieldKey]) { - const strVal = splits.length > 1 ? new List<string>(splits) : newText; - if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal; - Doc.SetInPlace(this._dashDoc!, this._fieldKey, strVal, true); - } - } - } - }); - } - }; - createPivotForField = (e: React.MouseEvent) => { let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement(); if (container) { |