aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-01-31 20:39:47 -0500
committerbobzel <zzzman@gmail.com>2024-01-31 20:39:47 -0500
commit0d8c05db9cbb5a23a94554c65015c347ff8c38b9 (patch)
treefa1cd6ba23262e81ee7b1b03a89783d0026cbba0 /src
parent8ac814bbb81b690a6a10f5a07aa5ce0e8cafe283 (diff)
cleaned up accessing/setting proto_embeddings with api on Doc. fixed some css related to IconButtons. added a paintView toggle button to dec decorations for text with code blocks. enabled text with code to modify Docs and get this and documentView as params.
Diffstat (limited to 'src')
-rw-r--r--src/client/util/DocumentManager.ts4
-rw-r--r--src/client/util/DropConverter.ts1
-rw-r--r--src/client/util/Scripting.ts3
-rw-r--r--src/client/views/DocComponent.tsx2
-rw-r--r--src/client/views/DocumentDecorations.tsx2
-rw-r--r--src/client/views/PropertiesDocContextSelector.tsx6
-rw-r--r--src/client/views/PropertiesView.tsx5
-rw-r--r--src/client/views/StyleProvider.scss4
-rw-r--r--src/client/views/StyleProvider.tsx18
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx2
-rw-r--r--src/client/views/collections/TreeView.scss4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx14
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss7
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx2
-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
-rw-r--r--src/fields/Doc.ts25
19 files changed, 122 insertions, 200 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index f730d17fe..53d472c66 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -355,8 +355,8 @@ export function DocFocusOrOpen(doc: Doc, options: FocusViewOptions = { willZoomC
});
}
};
- if (Doc.IsDataProto(doc) && DocListCast(doc.proto_embeddings).some(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))) {
- doc = DocListCast(doc.proto_embeddings).find(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))!;
+ if (Doc.IsDataProto(doc) && Doc.GetEmbeddings(doc).some(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))) {
+ doc = Doc.GetEmbeddings(doc).find(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))!;
}
if (doc.hidden) {
doc.hidden = false;
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index ba981145d..54066d267 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -73,6 +73,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
_nativeHeight: 100,
_width: 100,
_height: 100,
+ _layout_hideContextMenu: true,
backgroundColor: StrCast(doc.backgroundColor),
title: StrCast(layoutDoc.title),
btnType: ButtonType.ClickButton,
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index dfaacf318..f5e162d16 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -61,7 +61,8 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
const compiledFunction = (() => {
try {
return new Function(...paramNames, `return ${script}`);
- } catch {
+ } catch (e) {
+ console.log(e);
return undefined;
}
})();
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 73fa6709c..58be41f53 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -189,7 +189,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
toRemove.forEach(doc => {
leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc, true);
- Doc.RemoveDocFromList(doc[DocData], 'proto_embeddings', doc, true);
+ Doc.RemoveEmbedding(doc, doc);
doc.embedContainer = undefined;
if (recent && !dontAddToRemoved) {
doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2193acf62..dec109d7b 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -283,7 +283,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
} else {
var openDoc = selectedDocs[0].Document;
if (openDoc.layout_fieldKey === 'layout_icon') {
- openDoc = DocListCast(openDoc.proto_embeddings).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc);
+ openDoc = Doc.GetEmbeddings(openDoc).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc);
Doc.deiconifyView(openDoc);
}
LightboxView.Instance.SetLightboxDoc(
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 54f141a36..361451c4d 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -28,14 +28,14 @@ export class PropertiesDocContextSelector extends ObservableReactComponent<Prope
if (!this._props.DocView) return [];
const target = this._props.DocView._props.Document;
const targetContext = this._props.DocView.containerViewPath?.().lastElement()?.Document;
- const embeddings = DocListCast(target.proto_embeddings);
+ const embeddings = Doc.GetEmbeddings(target);
const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set<Doc>());
- const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.proto_embeddings));
+ const containerSets = Array.from(containerProtos.keys()).map(container => Doc.GetEmbeddings(container));
const containers = containerSets.reduce((p, set) => {
set.map(s => p.add(s));
return p;
}, new Set<Doc>());
- const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.proto_embeddings));
+ const doclayoutSets = Array.from(containers.keys()).map(dp => Doc.GetEmbeddings(dp));
const doclayouts = Array.from(
doclayoutSets
.reduce((p, set) => {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 208ed56c9..a7a065a95 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -173,7 +173,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
if (this.dataDoc && this.selectedDoc) {
const ids = new Set<string>(reqdKeys);
const docs: Doc[] = SelectionManager.Views.length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views.map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
- docs.forEach(doc => Object.keys(doc).forEach(key => doc[key] !== ComputedField.undefined && ids.add(key)));
+ docs.forEach(doc => Object.keys(doc).forEach(key => doc[key] !== ComputedField.undefined && key && ids.add(key)));
// prettier-ignore
Array.from(ids).filter(filter).sort().map(key => {
@@ -263,8 +263,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
@computed get contextCount() {
if (this.selectedDocumentView) {
const target = this.selectedDocumentView.Document;
- const embeddings = DocListCast(target.proto_embeddings);
- return embeddings.length - 1;
+ return Doc.GetEmbeddings(target).length - 1;
} else {
return 0;
}
diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss
index 4d3096f71..00bf503f5 100644
--- a/src/client/views/StyleProvider.scss
+++ b/src/client/views/StyleProvider.scss
@@ -1,5 +1,6 @@
.styleProvider-filter,
.styleProvider-audio,
+.styleProvider-paint,
.styleProvider-lock {
font-size: 10;
width: 15;
@@ -28,6 +29,9 @@
.styleProvider-audio {
right: 30;
}
+.styleProvider-paint {
+ top: 15;
+}
.styleProvider-lock:hover,
.styleProvider-audio:hover,
.styleProvider-filter:hover {
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 5a0167338..d3d13988f 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -9,7 +9,7 @@ import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs';
import { FaFilter } from 'react-icons/fa';
import { Doc, Opt, StrListCast } from '../../fields/Doc';
import { DocViews } from '../../fields/DocSymbols';
-import { BoolCast, Cast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
import { DashColor, lightOrDark, Utils } from '../../Utils';
import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager';
@@ -53,6 +53,16 @@ export enum StyleProp {
function toggleLockedPosition(doc: Doc) {
UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground');
}
+function togglePaintView(e: React.MouseEvent, doc: Opt<Doc>, props: Opt<FieldViewProps & DocumentViewProps>) {
+ const scriptProps = {
+ this: doc,
+ _readOnly_: false,
+ documentView: props?.DocumentView?.(),
+ value: undefined,
+ };
+ e.stopPropagation();
+ UndoManager.RunInBatch(() => doc && ScriptCast(doc.onPaint).script.run(scriptProps), 'togglePaintView');
+}
export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) {
return `M ${pw * 0.5} ${ph * inset} C ${pw * 0.6} ${ph * inset} ${pw * (1 - 2 * inset)} 0 ${pw * (1 - inset)} ${ph * inset} C ${pw} ${ph * (2 * inset)} ${pw * (1 - inset)} ${ph * 0.25} ${pw * (1 - inset)} ${ph * 0.3} C ${
@@ -267,6 +277,11 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
<FontAwesomeIcon icon='lock' size="lg" />
</div>
);
+ const paint = () => !doc?.onPaint ? null : (
+ <div className="styleProvider-paint" onClick={e => togglePaintView(e, doc, props)}>
+ <FontAwesomeIcon icon='pen' size="lg" />
+ </div>
+ );
const filter = () => {
const dashView = untracked(() => DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard));
const showFilterIcon =
@@ -329,6 +344,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
};
return (
<>
+ {paint()}
{lock()}
{filter()}
{audio()}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 87973fd81..31ca86f0f 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -490,7 +490,7 @@ export class CollectionDockingView extends CollectionSubView() {
// if you close a tab that is not embedded somewhere else (an embedded Doc can be opened simultaneously in a tab), then add the tab to recently closed
if (tab.DashDoc.embedContainer === this.Document) tab.DashDoc.embedContainer = undefined;
if (!tab.DashDoc.embedContainer) Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
- Doc.RemoveDocFromList(tab.DashDoc[DocData], 'proto_embeddings', tab.DashDoc);
+ Doc.RemoveEmbedding(tab.DashDoc, tab.DashDoc);
}
if (CollectionDockingView.Instance) {
const dview = CollectionDockingView.Instance.Document;
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 0a1946f09..09701ddb5 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -128,6 +128,10 @@
position: relative;
z-index: 1;
+ .treeView-rightButtons > .iconButton-container {
+ min-height: unset;
+ }
+
.treeView-background {
width: 100%;
height: 100%;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 54e8b08b6..9368560e9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -54,6 +54,7 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
import { RichTextField } from '../../../../fields/RichTextField';
+import { CompileScript } from '../../../util/Scripting';
export interface collectionFreeformViewProps {
NativeWidth?: () => number;
@@ -80,8 +81,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const paintFunc = StrCast(Field.toJavascriptString(Cast(field, RichTextField, null)?.Text as Field)).trim();
return !paintFunc
? ''
- : `const dashDiv = document.querySelector('#${this._paintedId}');
- (async () => { ${paintFunc} })()`;
+ : paintFunc.includes('dashDiv')
+ ? `const dashDiv = document.querySelector('#${this._paintedId}');
+ (async () => { ${paintFunc} })()`
+ : paintFunc;
}
constructor(props: any) {
super(props);
@@ -1496,7 +1499,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._disposers.paintFunc = reaction(
() => ({ code: this.paintFunc, first: this._firstRender, width: this.Document._width, height: this.Document._height }),
- ({ code, first }) => code && !first && eval(code),
+ ({ code, first }) => {
+ if (!code.includes('dashDiv')) {
+ const script = CompileScript(code, { params: { docView: 'any' }, typecheck: false, editable: true });
+ if (script.compiled) script.run({ this: this.Document, docView: this.DocumentView?.() });
+ } else code && !first && eval(code);
+ },
{ fireImmediately: true }
);
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 29d121974..29491569a 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -128,7 +128,7 @@
.row-menu {
display: flex;
- justify-content: flex-end;
+ justify-content: center;
}
}
@@ -224,7 +224,10 @@
display: flex;
flex-direction: row;
min-width: 50px;
- justify-content: flex-end;
+ justify-content: center;
+ .iconButton-container {
+ min-width: unset !important;
+ }
}
.row-cells {
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index f2fe0dde7..39fea2d2e 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -121,7 +121,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
pointerEvents: !this._props.isContentActive() ? 'none' : undefined,
}}>
<IconButton
- tooltip="whether document interations are enabled"
+ tooltip="whether document interactions are enabled"
icon={this.Document._lockedPosition ? <CgLockUnlock size="12px" /> : <CgLock size="12px" />}
size={Size.XSMALL}
onPointerDown={e =>
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) => {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 30e3aa5f0..f3fc51671 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -572,6 +572,16 @@ export namespace Doc {
return false;
}
+ export function RemoveEmbedding(doc: Doc, embedding: Doc) {
+ Doc.RemoveDocFromList(doc[DocData], 'proto_embeddings', embedding);
+ }
+ export function AddEmbedding(doc: Doc, embedding: Doc) {
+ Doc.AddDocToList(doc[DocData], 'proto_embeddings', embedding, undefined, undefined, undefined, undefined, undefined, true);
+ }
+ export function GetEmbeddings(doc: Doc) {
+ return DocListCast(Doc.Get(doc[DocData], 'proto_embeddings', true));
+ }
+
export function MakeEmbedding(doc: Doc, id?: string) {
const embedding = (!GetT(doc, 'isDataDoc', 'boolean', true) && doc.proto) || doc.type === DocumentType.CONFIG ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
const layout = Doc.LayoutField(embedding);
@@ -579,20 +589,18 @@ export namespace Doc {
Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
}
embedding.createdFrom = doc;
- embedding.proto_embeddingId = doc[DocData].proto_embeddingId = DocListCast(doc[DocData].proto_embeddings).length - 1;
+ embedding.proto_embeddingId = doc[DocData].proto_embeddingId = Doc.GetEmbeddings(doc).length - 1;
!Doc.GetT(embedding, 'title', 'string', true) && (embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`));
embedding.author = Doc.CurrentUserEmail;
- Doc.AddDocToList(doc[DocData], 'proto_embeddings', embedding);
-
return embedding;
}
export function BestEmbedding(doc: Doc) {
const dataDoc = doc[DocData];
- const availableEmbeddings = DocListCast(dataDoc.proto_embeddings);
+ const availableEmbeddings = Doc.GetEmbeddings(dataDoc);
const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail);
- bestEmbedding && Doc.AddDocToList(dataDoc, 'protoEmbeddings', doc);
+ bestEmbedding && Doc.AddDocToList(dataDoc, 'protoEmbeddings', doc, undefined, undefined, undefined, undefined, undefined, true);
return bestEmbedding ?? Doc.MakeEmbedding(doc);
}
@@ -951,7 +959,7 @@ export namespace Doc {
Doc.GetProto(copy).embedContainer = undefined;
Doc.GetProto(copy).proto_embeddings = new List<Doc>([copy]);
} else {
- Doc.AddDocToList(copy[DocData], 'proto_embeddings', copy);
+ Doc.AddEmbedding(copy, copy);
}
copy.embedContainer = undefined;
if (retitle) {
@@ -972,9 +980,10 @@ export namespace Doc {
Object.keys(doc)
.filter(key => key.startsWith('acl'))
.forEach(key => (delegate[key] = doc[key]));
- if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DocData], 'proto_embeddings', delegate);
+ if (!Doc.IsSystem(doc)) Doc.AddEmbedding(doc, delegate);
title && (delegate.title = title);
delegate[Initializing] = false;
+ Doc.AddEmbedding(doc, delegate);
return delegate;
}
return undefined;
@@ -995,7 +1004,7 @@ export namespace Doc {
delegate[Initializing] = true;
delegate.proto = delegateProto;
delegate.author = Doc.CurrentUserEmail;
- Doc.AddDocToList(delegateProto[DocData], 'proto_embeddings', delegate);
+ Doc.AddEmbedding(delegateProto, delegate);
delegate[Initializing] = false;
delegateProto[Initializing] = false;
return delegate;