aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/DocumentView.tsx6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx55
-rw-r--r--src/client/views/nodes/formattedText/PaintButtonView.tsx113
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts49
4 files changed, 50 insertions, 173 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 51f4b1a68..b5355fb99 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -456,8 +456,8 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
deleteClicked = undoable(() => this._props.removeDocument?.(this.Document), 'delete doc');
setToggleDetail = undoable(
- (defaultLayout: string) =>
- (this.Document.onClick = ScriptField.MakeScript(
+ (defaultLayout: string, scriptFieldKey: 'onClick') =>
+ (this.Document[scriptFieldKey] = ScriptField.MakeScript(
`toggleDetail(documentView, "${StrCast(this.Document.layout_fieldKey)
.replace('layout_', '')
.replace(/^layout$/, 'detail')}", "${defaultLayout}")`,
@@ -1212,7 +1212,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
};
public noOnClick = () => this._docViewInternal?.noOnClick();
public toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => this._docViewInternal?.toggleFollowLink(zoom, setTargetToggle);
- public setToggleDetail = (defaultLayout = '') => this._docViewInternal?.setToggleDetail(defaultLayout);
+ public setToggleDetail = (defaultLayout = '', scriptFieldKey = 'onClick') => this._docViewInternal?.setToggleDetail(defaultLayout, scriptFieldKey);
public onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => this._docViewInternal?.onContextMenu?.(e, pageX, pageY);
public cleanupPointerEvents = () => this._docViewInternal?.cleanupPointerEvents();
public startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this._docViewInternal?.startDragging(x, y, dropAction, hideSource);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 973f90501..b82ab4219 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -67,8 +67,6 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from './RichTextRules';
import { schema } from './schema_rts';
import { SummaryView } from './SummaryView';
-import { CollectionView } from '../../collections/CollectionView';
-import { PaintButtonView } from './PaintButtonView';
// import * as applyDevTools from 'prosemirror-dev-tools';
@observer
export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
@@ -488,14 +486,29 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
// creates links between terms in a document and published documents (myPublishedDocs) that have titles starting with an '@'
+ /**
+ * Searches the text for occurences of any strings that match the names of 'published' documents. These document
+ * names will begin with an '@' prefix. However, valid matches within the text can have any of the following formats:
+ * name, @<name>, or ^@<name>
+ * The last of these is interpreted as an include directive when converting the text into evaluated code in the paint
+ * function of a freeform view that is driven by the text box's text. The include directive will copy the code of the published
+ * document into the code being evaluated.
+ */
hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set<Doc>) => {
const editorView = this._editorView;
if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.Document)) {
const autoLinkTerm = StrCast(target.title).replace(/^@/, '');
var alink: Doc | undefined;
this.findInNode(editorView, editorView.state.doc, autoLinkTerm).forEach(sel => {
- const splitter = editorView.state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
- if (!sel.$anchor.pos || [autoLinkTerm, StrCast(target.title)].includes(editorView.state.doc.textBetween(sel.$anchor.pos - 1, sel.$to.pos).trim())) {
+ if (
+ !sel.$anchor.pos ||
+ autoLinkTerm ===
+ editorView.state.doc
+ .textBetween(sel.$anchor.pos - 1, sel.$to.pos)
+ .trim()
+ .replace(/[\^@]+/, '')
+ ) {
+ const splitter = editorView.state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
tr = tr.addMark(sel.from, sel.to, splitter);
tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
@@ -668,12 +681,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
let index = 0,
foundAt;
const ep = this.getNodeEndpoints(pm.state.doc, node);
- const regexp = new RegExp(find.replace('*', ''), 'i');
+ const regexp = new RegExp(find, 'i');
if (regexp) {
- while (ep && (foundAt = node.textContent.slice(index).search(regexp)) > -1) {
- const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
- ret.push(sel);
- index = index + foundAt + find.length;
+ var blockOffset = 0;
+ for (var i = 0; i < node.childCount; i++) {
+ var textContent = '';
+ while (i < node.childCount && node.child(i).type === pm.state.schema.nodes.text) {
+ textContent += node.child(i).textContent;
+ i++;
+ }
+ while (ep && (foundAt = textContent.slice(index).search(regexp)) > -1) {
+ const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + blockOffset + foundAt + 1), pm.state.doc.resolve(ep.from + index + blockOffset + foundAt + find.length + 1));
+ ret.push(sel);
+ index = index + foundAt + find.length;
+ }
+ blockOffset += textContent.length;
+ if (i < node.childCount) blockOffset += node.child(i).nodeSize;
}
}
} else {
@@ -934,17 +957,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR),
icon: !this.Document._createDocOnCR ? 'grip-lines' : 'bars',
});
- optionItems.push({
- description: 'Make Paint Function',
- event: () => {
- this.dataDoc.layout_painted = CollectionView.LayoutString('painted');
- this.layoutDoc.layout_fieldKey = 'layout_painted';
- this.layoutDoc.type_collection = CollectionViewType.Freeform;
- this.DocumentView?.().setToggleDetail();
- this.dataDoc.paintFunc = ComputedField.MakeFunction(`toJavascriptString(this['${this.fieldKey}']?.Text)`);
- },
- icon: !this.Document._layout_enableAltContentUI ? 'eye-slash' : 'eye',
- });
!Doc.noviceMode &&
optionItems.push({
description: `${this.Document._layout_autoHeight ? 'Lock' : 'Auto'} Height`,
@@ -1411,9 +1423,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
dashField(node: any, view: any, getPos: any) {
return new DashFieldView(node, view, getPos, self);
},
- paintButton(node: any, view: any, getPos: any) {
- return new PaintButtonView(node, view, getPos, self);
- },
equation(node: any, view: any, getPos: any) {
return new EquationView(node, view, getPos, self);
},
diff --git a/src/client/views/nodes/formattedText/PaintButtonView.tsx b/src/client/views/nodes/formattedText/PaintButtonView.tsx
deleted file mode 100644
index 74423c772..000000000
--- a/src/client/views/nodes/formattedText/PaintButtonView.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import { action, computed, IReactionDisposer, makeObservable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import * as ReactDOM from 'react-dom/client';
-import { Doc } from '../../../../fields/Doc';
-import { ObservableReactComponent } from '../../ObservableReactComponent';
-import './DashFieldView.scss';
-import { FormattedTextBox } from './FormattedTextBox';
-import { CollectionViewType } from '../../../documents/DocumentTypes';
-import { CollectionView } from '../../collections/CollectionView';
-import { StrCast } from '../../../../fields/Types';
-
-export class PaintButtonView {
- dom: HTMLDivElement; // container for label and value
- root: any;
- node: any;
- tbox: FormattedTextBox;
-
- constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
- this.node = node;
- this.tbox = tbox;
- 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) {
- 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();
- };
-
- this.root = ReactDOM.createRoot(this.dom);
- this.root.render(<PaintButtonViewInternal node={node} getPos={getPos} width={node.attrs.width} height={node.attrs.height} tbox={tbox} />);
- }
- destroy() {
- setTimeout(() => {
- try {
- this.root.unmount();
- } catch {}
- });
- }
- deselectNode() {
- this.dom.classList.remove('ProseMirror-selectednode');
- }
- selectNode() {
- this.dom.classList.add('ProseMirror-selectednode');
- }
-}
-
-interface IPaintButtonViewInternal {
- tbox: FormattedTextBox;
- width: number;
- height: number;
- node: any;
- getPos: any;
-}
-
-@observer
-export class PaintButtonViewInternal extends ObservableReactComponent<IPaintButtonViewInternal> {
- _reactionDisposer: IReactionDisposer | undefined;
- _textBoxDoc: Doc;
-
- constructor(props: IPaintButtonViewInternal) {
- super(props);
- makeObservable(this);
- this._textBoxDoc = this._props.tbox.Document;
- }
-
- return100 = () => 100;
- @computed get _checked() {
- return this._props.tbox.Document.onClick ? true : false;
- }
-
- onCheckClick = () => {
- const textView = this._props.tbox.DocumentView?.();
- if (textView) {
- const paintedField = 'layout_' + this._props.tbox.fieldKey + 'Painted';
- const layoutFieldKey = StrCast(textView.layoutDoc.layout_fieldKey, 'layout');
- if (textView.layoutDoc.onClick) {
- textView.layoutDoc[paintedField] = undefined;
- textView.layoutDoc.onClick = undefined;
- } else {
- textView.layoutDoc.type_collection = CollectionViewType.Freeform;
- textView.dataDoc[paintedField] = CollectionView.LayoutString(this._props.tbox.fieldKey);
- textView.layoutDoc.layout_fieldKey = paintedField;
- textView.setToggleDetail(layoutFieldKey.replace('layout_', '').replace('layout', ''));
- textView.layoutDoc.layout_fieldKey = layoutFieldKey;
- }
- }
- };
-
- render() {
- return (
- <div
- className="dashFieldView"
- style={{
- width: this._props.width,
- height: this._props.height,
- pointerEvents: this._props.tbox._props.isSelected() || this._props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
- }}>
- <input type="checkbox" value="paint" checked={this._checked} onChange={e => this.onCheckClick()} />
- </div>
- );
- }
-}
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 8f7bc5282..ce2c33fb4 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -68,40 +68,21 @@ export class RichTextRules {
),
// ``` create code block
- textblockTypeInputRule(/^```$/, schema.nodes.code_block),
- new InputRule(
- new RegExp(/(^|\n)\^@paint/), // for code blocks '^' means the beginning of the block, not the line, so need to test for \n
- (state, match, start, end) => {
- const { dataDoc, layoutDoc, fieldKey } = this.TextBox;
- layoutDoc.type_collection = CollectionViewType.Freeform;
- const paintedField = 'layout_' + this.TextBox.fieldKey + 'Painted';
- dataDoc[paintedField] = CollectionView.LayoutString(this.TextBox.fieldKey);
- const layoutFieldKey = StrCast(layoutDoc.layout_fieldKey);
- layoutDoc.layout_fieldKey = paintedField;
- this.TextBox.DocumentView?.().setToggleDetail(layoutFieldKey.replace('layout_', '').replace('layout', ''));
- layoutDoc.layout_fieldKey = layoutFieldKey;
- const comment = '/* enable as paint function ';
- const endComment = ' */\n';
- const inCode = state.tr.selection.$anchor.node().type === schema.nodes.code_block;
- if (inCode) {
- const tr = state.tr
- .deleteRange(start, end)
- .insertText(comment)
- .insert(start + comment.length, schema.nodes.paintButton.create())
- .insertText(endComment);
- return tr.setSelection(new TextSelection(tr.doc.resolve(start + comment.length + endComment.length + 1)));
- } else {
- const tr = state.tr
- .deleteRange(start, end)
- .insertText(comment)
- .insert(start + comment.length, schema.nodes.paintButton.create())
- .insertText(endComment)
- .insert(start + comment.length + endComment.length + 1, schema.nodes.code_block.create());
- return tr.setSelection(new TextSelection(tr.doc.resolve(start + comment.length + endComment.length + 3)));
- }
- },
- { inCode: true }
- ),
+ new InputRule(/^```$/, (state, match, start, end) => {
+ let $start = state.doc.resolve(start);
+ if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), schema.nodes.code_block)) return null;
+
+ // this enables text with code blocks to be used as a 'paint' function via a styleprovider button that is added to Docs that have an onPaint script
+ this.TextBox.layoutDoc.type_collection = CollectionViewType.Freeform; // make it a freeform when rendered as a collection since those are the only views that know about the paint function
+ const paintedField = 'layout_' + this.TextBox.fieldKey + 'Painted'; // make a layout field key for storing the CollectionView jsx string pointing to the textbox's text
+ this.TextBox.dataDoc[paintedField] = CollectionView.LayoutString(this.TextBox.fieldKey);
+ const layoutFieldKey = StrCast(this.TextBox.layoutDoc.layout_fieldKey); // save the current layout fieldkey
+ this.TextBox.layoutDoc.layout_fieldKey = paintedField; // setup the paint layout field key
+ this.TextBox.DocumentView?.().setToggleDetail(layoutFieldKey.replace('layout_', '').replace('layout', ''), 'onPaint'); // create the script to toggle between the painted and regular view
+ this.TextBox.layoutDoc.layout_fieldKey = layoutFieldKey; // restore the layout field key to text
+
+ return state.tr.delete(start, end).setBlockType(start, start, schema.nodes.code_block);
+ }),
// %<font-size> set the font size
new InputRule(new RegExp(/%([0-9]+)\s$/), (state, match, start, end) => {