aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/formattedText/DashFieldView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/formattedText/DashFieldView.tsx')
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx229
1 files changed, 123 insertions, 106 deletions
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index bb3791f1e..940ed6386 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,52 +1,55 @@
-import { action, computed, IReactionDisposer, observable } from "mobx";
-import { observer } from "mobx-react";
+import { action, computed, IReactionDisposer, observable } from 'mobx';
+import { observer } from 'mobx-react';
import * as ReactDOM from 'react-dom';
-import { DataSym, Doc, DocListCast, 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 { DocServer } from "../../../DocServer";
-import { CollectionViewType } from "../../collections/CollectionView";
-import "./DashFieldView.scss";
-import { FormattedTextBox } from "./FormattedTextBox";
-import React = require("react");
-import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils";
-import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
-import { Tooltip } from "@material-ui/core";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { DataSym, Doc, DocListCast, 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 { DocServer } from '../../../DocServer';
+import { CollectionViewType } from '../../collections/CollectionView';
+import './DashFieldView.scss';
+import { FormattedTextBox } from './FormattedTextBox';
+import React = require('react');
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { Tooltip } from '@material-ui/core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
export class DashFieldView {
- _fieldWrapper: HTMLDivElement; // container for label and value
+ dom: HTMLDivElement; // container for label and value
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
const { boolVal, strVal } = DashFieldViewInternal.fieldContent(tbox.props.Document, tbox.rootDoc, node.attrs.fieldKey);
- this._fieldWrapper = document.createElement("div");
- this._fieldWrapper.style.width = node.attrs.width;
- this._fieldWrapper.style.height = node.attrs.height;
- this._fieldWrapper.style.fontWeight = "bold";
- this._fieldWrapper.style.position = "relative";
- this._fieldWrapper.style.display = "inline-block";
- this._fieldWrapper.textContent = node.attrs.fieldKey.startsWith("#") ? node.attrs.fieldKey : node.attrs.fieldKey + " " + strVal;
- this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); };
- this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); };
- this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); };
- this._fieldWrapper.onmousedown = function (e: any) { e.stopPropagation(); };
+ this.dom = document.createElement('div');
+ this.dom.style.width = node.attrs.width;
+ this.dom.style.height = node.attrs.height;
+ this.dom.style.fontWeight = 'bold';
+ this.dom.style.position = 'relative';
+ this.dom.style.display = 'inline-block';
+ this.dom.textContent = node.attrs.fieldKey.startsWith('#') ? node.attrs.fieldKey : node.attrs.fieldKey + ' ' + strVal;
+ this.dom.onkeypress = function (e: any) {
+ e.stopPropagation();
+ };
+ this.dom.onkeydown = function (e: any) {
+ e.stopPropagation();
+ };
+ this.dom.onkeyup = function (e: any) {
+ e.stopPropagation();
+ };
+ this.dom.onmousedown = function (e: any) {
+ e.stopPropagation();
+ };
- setTimeout(() => ReactDOM.render(<DashFieldViewInternal
- fieldKey={node.attrs.fieldKey}
- docid={node.attrs.docid}
- width={node.attrs.width}
- height={node.attrs.height}
- hideKey={node.attrs.hideKey}
- tbox={tbox}
- />, this._fieldWrapper));
- (this as any).dom = this._fieldWrapper;
+ setTimeout(() => ReactDOM.render(<DashFieldViewInternal fieldKey={node.attrs.fieldKey} docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} hideKey={node.attrs.hideKey} tbox={tbox} />, this.dom));
+ (this as any).dom = this.dom;
}
- destroy() { ReactDOM.unmountComponentAtNode(this._fieldWrapper); }
- selectNode() { }
+ destroy() {
+ ReactDOM.unmountComponentAtNode(this.dom);
+ }
+ selectNode() {}
}
interface IDashFieldViewInternal {
@@ -72,8 +75,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
this._textBoxDoc = this.props.tbox.props.Document;
if (this.props.docid) {
- DocServer.GetRefField(this.props.docid).
- then(action(async dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
+ DocServer.GetRefField(this.props.docid).then(action(async dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
} else {
this._dashDoc = this.props.tbox.rootDoc;
}
@@ -82,11 +84,11 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
this._reactionDisposer?.();
}
- public static multiValueDelimeter = ";";
+ public static multiValueDelimeter = ';';
public static fieldContent(textBoxDoc: Doc, dashDoc: Doc, fieldKey: string) {
- const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? (fieldKey === "PARAMS" ? textBoxDoc[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) || "" };
+ const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? (fieldKey === 'PARAMS' ? textBoxDoc[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)
@@ -95,30 +97,40 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
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);
- }}
- />;
- }
- else // field value is a string, so display it as an editable span
- {
+ 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={true}
- 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 => e.stopPropagation()));
- }} >
- {strVal}
- </span>;
+ return (
+ <span
+ className="dashFieldView-fieldSpan"
+ contentEditable={true}
+ 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 => e.stopPropagation())
+ );
+ }}>
+ {strVal}
+ </span>
+ );
}
}
}
@@ -126,11 +138,13 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
// 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 === "Enter") { // handle the enter key by "submitting" the current text to Dash's database.
+ 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
+ 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 (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);
@@ -139,44 +153,44 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
}
e.preventDefault(); //prevent default so that all the text in the prosemirror text box isn't selected
}
- e.stopPropagation(); // we need to handle all events or else they will propagate to prosemirror.
- }
+ 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;
+ 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)));
+ 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(":=")) {
+ else if (nodeText.startsWith(':=')) {
this._dashDoc![DataSym][this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(2));
- } else if (nodeText.startsWith("=:=")) {
+ } 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);
+ if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
Doc.SetInPlace(this._dashDoc!, this._fieldKey, newText, true);
} else {
const splits = newText.split(DashFieldViewInternal.multiValueDelimeter);
- if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
+ if (this._fieldKey !== 'PARAMS' || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
const strVal = splits.length > 1 ? new List<string>(splits) : newText;
- if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
+ 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.ContainingCollectionView;
@@ -190,36 +204,39 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
if (!list) {
alias._columnHeaders = list = new List<SchemaHeaderField>();
}
- list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb"));
- list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb"));
- alias._pivotField = this._fieldKey.startsWith("#") ? "#" : this._fieldKey;
- this.props.tbox.props.addDocTab(alias, "add:right");
+ list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, '#f1efeb'));
+ list.map(c => c.heading).indexOf('text') === -1 && list.push(new SchemaHeaderField('text', '#f1efeb'));
+ alias._pivotField = this._fieldKey.startsWith('#') ? '#' : this._fieldKey;
+ this.props.tbox.props.addDocTab(alias, 'add:right');
}
- }
-
+ };
// 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) => {
- setupMoveUpEvents(this, e, returnFalse, returnFalse, (e) => {
+ setupMoveUpEvents(this, e, returnFalse, returnFalse, e => {
DashFieldViewMenu.createFieldView = this.createPivotForField;
DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16);
});
- }
+ };
render() {
- return <div className="dashFieldView" style={{
- width: this.props.width,
- height: this.props.height,
- }}>
- {this.props.hideKey ? (null) :
- <span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
- {this._fieldKey}
- </span>}
-
- {this.props.fieldKey.startsWith("#") ? (null) : this.fieldValueContent}
+ return (
+ <div
+ className="dashFieldView"
+ style={{
+ width: this.props.width,
+ height: this.props.height,
+ }}>
+ {this.props.hideKey ? null : (
+ <span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
+ {this._fieldKey}
+ </span>
+ )}
- </div >;
+ {this.props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
+ </div>
+ );
}
}
@observer
@@ -234,19 +251,19 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
showFields = (e: React.MouseEvent) => {
DashFieldViewMenu.createFieldView(e);
DashFieldViewMenu.Instance.fadeOut(true);
- }
+ };
public show = (x: number, y: number) => {
this.jumpTo(x, y, true);
const hideMenu = () => {
this.fadeOut(true);
- document.removeEventListener("pointerdown", hideMenu);
+ document.removeEventListener('pointerdown', hideMenu);
};
- document.addEventListener("pointerdown", hideMenu);
- }
+ document.addEventListener('pointerdown', hideMenu);
+ };
render() {
const buttons = [
- <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}>
+ <Tooltip key="trash" title={<div className="dash-tooltip">{'Remove Link Anchor'}</div>}>
<button className="antimodeMenu-button" onPointerDown={this.showFields}>
<FontAwesomeIcon icon="eye" size="lg" />
</button>
@@ -255,4 +272,4 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
return this.getElement(buttons);
}
-} \ No newline at end of file
+}