aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/formattedText/DashFieldView.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-05-13 09:55:48 -0400
committerbobzel <zzzman@gmail.com>2023-05-13 09:55:48 -0400
commitdca61505ba138eef3819b16b760ec81becf9329e (patch)
treef1bdf777d263760eea058ae0b6df40db831574a4 /src/client/views/nodes/formattedText/DashFieldView.tsx
parent8b0fa2ca2c454468f04221f52951051253571556 (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.tsx150
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) {