aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-03-31 23:09:53 -0400
committerbobzel <zzzman@gmail.com>2024-03-31 23:09:53 -0400
commit108e27f0d08a8d6d3b3fcd456d6cca999c077e8b (patch)
tree1941a7b26f4cfe5abcb6c2c622c2e363a27b6a55
parent1e37adacfe5b44a71ed7d7dfd785c6c2d0808eae (diff)
fixed scrolling to targets in text views. fixed referencing fields on another doc from text box. fixed '@name' technique for publishing documents to also remove documents and work anywhere a title is set.
-rw-r--r--src/client/documents/Documents.ts5
-rw-r--r--src/client/util/Scripting.ts2
-rw-r--r--src/client/views/DocumentButtonBar.tsx2
-rw-r--r--src/client/views/DocumentDecorations.tsx6
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx25
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx8
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx34
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts13
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts1
-rw-r--r--src/fields/Doc.ts13
-rw-r--r--src/fields/util.ts16
12 files changed, 78 insertions, 49 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 6a0d45543..eb15c332f 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1516,6 +1516,9 @@ export namespace DocUtils {
return linkDoc;
});
+ const a = source.layout_unrendered ? 'link_anchor_1.annotationOn' : 'link_anchor_1';
+ const b = target.layout_unrendered ? 'link_anchor_2.annotationOn' : 'link_anchor_2';
+
return makeLink(
Docs.Create.LinkDocument(
source,
@@ -1529,6 +1532,8 @@ export namespace DocUtils {
link_displayLine: linkSettings.link_displayLine,
link_relationship: linkSettings.link_relationship,
link_description: linkSettings.link_description,
+ x: ComputedField.MakeFunction(`(this.${a}.x+this.${b}.x)/2`) as any,
+ y: ComputedField.MakeFunction(`(this.${a}.y+this.${b}.y)/2`) as any,
link_autoMoveAnchors: true,
_lockedPosition: true,
_layout_showCaption: '', // removed since they conflict with showing a link with a LinkBox (ie, line, not comparison box)
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 31222aa50..422e708bc 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -250,7 +250,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
const outputText = host.readFile('file.js');
const diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
-
+ if (script.startsWith('@')) options.typecheck = true; // need the compilation to fail so that the script will return itself as a string (instead of nothing)
const result = Run(outputText, paramNames, diagnostics, script, options);
if (options.globals) {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index d65e0b406..15ce4c15f 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -416,7 +416,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
)}
{DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== doc ? <div className="documentButtonBar-button">{this.endLinkButton} </div> : null}
- {Doc.noviceMode ? null : <div className="documentButtonBar-button">{this.templateButton}</div>}
+ <div className="documentButtonBar-button">{this.templateButton}</div>
{!SelectionManager.Views?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
<div className="documentButtonBar-button">{this.pinButton}</div>
<div className="documentButtonBar-button">{this.recordButton}</div>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 951e0912c..9e469ed1f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -131,12 +131,6 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
SelectionManager.Views.forEach(d => {
if (titleFieldKey === 'title') {
d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-');
- if (StrCast(d.Document.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) {
- Doc.RemFromMyPublished(d.Document);
- }
- if (!StrCast(d.Document.title).startsWith('@') && this._accumulatedTitle.startsWith('@')) {
- Doc.AddToMyPublished(d.Document);
- }
}
KeyValueBox.SetField(d.Document, titleFieldKey, this._accumulatedTitle);
}),
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 25bfdb588..b2897a9b7 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -3,14 +3,14 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import * as GoldenLayout from '../../../client/goldenLayout';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
import { AclAdmin, AclEdit, DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
-import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { FieldValue, ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
-import { GetEffectiveAcl, inheritParentAcls } from '../../../fields/util';
+import { GetEffectiveAcl, inheritParentAcls, SetPropSetterCb } from '../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivHeight, DivWidth, emptyFunction, incrementTitleCopy } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { Docs } from '../../documents/Documents';
@@ -32,6 +32,7 @@ import './CollectionDockingView.scss';
import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionSubView } from './CollectionSubView';
import { TabDocView } from './TabDocView';
+import { ComputedField } from '../../../fields/ScriptField';
const _global = (window /* browser */ || global) /* node */ as any;
@observer
@@ -313,8 +314,26 @@ export class CollectionDockingView extends CollectionSubView() {
}
};
+ /**
+ * This publishes Docs having titles starting with '@' to Doc.myPublishedDocs
+ * Once published, any text that uses the 'title' in its body will automatically
+ * be linked to this published document.
+ * @param target
+ * @param title
+ */
+ titleChanged = (target: any, value: any) => {
+ const title = Field.toString(value);
+ if (title.startsWith('@') && !title.substring(1).match(/[\(\)\[\]@]/) && title.length > 1) {
+ const embedding = DocListCast(target.proto_embeddings).lastElement();
+ embedding && Doc.AddToMyPublished(embedding);
+ } else if (!title.startsWith('@')) {
+ DocListCast(target.proto_embeddings).forEach(doc => Doc.RemFromMyPublished(doc));
+ }
+ };
+
componentDidMount: () => void = async () => {
this._unmounting = false;
+ SetPropSetterCb('title', this.titleChanged); // this overrides any previously assigned callback for the property
if (this._containerRef.current) {
this._lightboxReactionDisposer = reaction(
() => LightboxView.LightboxDoc,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 6b3a56b0b..6eca91e9d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -499,7 +499,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
_layout_showSidebar: true,
title: 'overview',
});
- const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
+ const portal = Docs.Create.FreeformDocument(selected, { title: 'summarized documents', x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
DocUtils.MakeLink(summary, portal, { link_relationship: 'summary of:summarized by' });
portal.hidden = true;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 22a0cbe5e..763939a68 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -87,7 +87,6 @@ export class DashFieldView {
hideValue={node.attrs.hideValue}
editable={node.attrs.editable}
nodeSelected={this.NodeSelected}
- dataDoc={node.attrs.dataDoc}
tbox={tbox}
/>
);
@@ -119,7 +118,6 @@ interface IDashFieldViewInternal {
height: number;
editable: boolean;
nodeSelected: () => boolean;
- dataDoc: boolean;
node: any;
getPos: any;
unclickable: () => boolean;
@@ -139,7 +137,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
makeObservable(this);
this._fieldKey = this._props.fieldKey;
this._textBoxDoc = this._props.tbox.Document;
- const setDoc = action((doc: Doc) => (this._dashDoc = this._props.dataDoc ? doc[DocData] : doc));
+ const setDoc = action((doc: Doc) => (this._dashDoc = doc));
if (this._props.docId) {
DocServer.GetRefField(this._props.docId).then(dashDoc => dashDoc instanceof Doc && setDoc(dashDoc));
@@ -168,7 +166,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
setTimeout(() => !this._props.tbox.ProseRef?.contains(document.activeElement) && this._props.tbox._props.onBlur?.());
}
});
- selectedCell = (): [Doc, number] => [this._dashDoc!, 0];
+ selectedCell = (): [Doc, number] | undefined => (this._dashDoc ? [this._dashDoc, 0] : undefined);
columnWidth = () => Math.min(this._props.tbox._props.PanelWidth(), Math.max(50, this._props.tbox._props.PanelWidth() - 100)); // try to leave room for the fieldKey
// set the display of the field's value (checkbox for booleans, span of text for strings)
@@ -284,7 +282,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
)}
{this._props.fieldKey.startsWith('#') || this._hideValue ? null : this.fieldValueContent}
{!this.values.length ? null : (
- <select className="dashFieldView-select" tabIndex={-1} defaultValue={Field.toKeyValueString(this._dashDoc!, this._fieldKey)} onChange={this.selectVal}>
+ <select className="dashFieldView-select" tabIndex={-1} defaultValue={this._dashDoc && Field.toKeyValueString(this._dashDoc, this._fieldKey)} onChange={this.selectVal}>
<option value="-unset-">-unset-</option>
{this.values.map(val => (
<option value={val.value}>{val.label}</option>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2deecb6ad..80e56efe0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -478,7 +478,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const title = StrCast(this.dataDoc.title, Cast(this.dataDoc.title, RichTextField, null)?.Text);
if (
!this._props.dontRegisterView && // (this.Document.isTemplateForField === "text" || !this.Document.isTemplateForField) && // only update the title if the data document's data field is changing
- (title.startsWith('-') || title.startsWith('@')) &&
+ title.startsWith('-') &&
this._editorView &&
!this.dataDoc.title_custom &&
(Doc.LayoutFieldKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
@@ -486,14 +486,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
let node = this._editorView.state.doc;
while (node.firstChild && node.firstChild.type.name !== 'text') node = node.firstChild;
const str = node.textContent;
- const prefix = str.startsWith('@') ? '' : '-';
+ const prefix = '-';
const cfield = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc.title));
if (!(cfield instanceof ComputedField)) {
this.dataDoc.title = (prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '')).trim();
- if (str.startsWith('@') && str.length > 1) {
- Doc.AddToMyPublished(this.Document);
- }
}
}
};
@@ -510,7 +507,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
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(/^@/, '');
+ const autoLinkTerm = Field.toString(target.title as Field).replace(/^@/, '');
var alink: Doc | undefined;
this.findInNode(editorView, editorView.state.doc, autoLinkTerm).forEach(sel => {
if (
@@ -1312,15 +1309,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this._disposers.scroll = reaction(
() => NumCast(this.layoutDoc._layout_scrollTop),
pos => {
- if (!this._ignoreScroll && this._scrollRef.current && !this._props.dontSelectOnLoad) {
+ if (!this._ignoreScroll && this.ProseRef && !this._props.dontSelectOnLoad) {
const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition);
const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
const durationSecStr = viewTrans.match(/([0-9.]*)s/);
const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
if (duration) {
- this._scrollStopper = smoothScroll(duration, this._scrollRef.current, Math.abs(pos || 0), 'ease', this._scrollStopper);
+ this._scrollStopper = smoothScroll(duration, this.ProseRef, Math.abs(pos || 0), 'ease', this._scrollStopper);
} else {
- this._scrollRef.current.scrollTo({ top: pos });
+ this.ProseRef.scrollTo({ top: pos });
}
}
},
@@ -1418,7 +1415,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
handleScrollToSelection: editorView => {
const docPos = editorView.coordsAtPos(editorView.state.selection.to);
const viewRect = self._ref.current!.getBoundingClientRect();
- const scrollRef = self._scrollRef.current;
+ const scrollRef = self.ProseRef;
const topOff = docPos.top < viewRect.top ? docPos.top - viewRect.top : undefined;
const botOff = docPos.bottom > viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined;
if (((topOff && Math.abs(Math.trunc(topOff)) > 0) || (botOff && Math.abs(Math.trunc(botOff)) > 0)) && scrollRef) {
@@ -1750,20 +1747,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
}
FormattedTextBox._hadSelection = window.getSelection()?.toString() !== '';
+
+ // this is the markdown for @<published name> document publishing to Doc.myPublishedDocs
+ const match = RTFCast(this.Document[this.fieldKey]).Text.match(/^(@[a-zA-Z][a-zA-Z_0-9 -]*[a-zA-Z_0-9-]+)/);
+ if (match) {
+ this.dataDoc.title_custom = true;
+ this.dataDoc.title = match[1]; // this triggers the collectionDockingView to publish this Doc
+ this.EditorView?.dispatch(this.EditorView?.state.tr.deleteRange(0, match[1].length + 1));
+ }
+
this.endUndoTypingBatch();
FormattedTextBox.LiveTextUndo?.end();
FormattedTextBox.LiveTextUndo = undefined;
const state = this._editorView!.state;
- if (StrCast(this.Document.title).startsWith('@') && !this.dataDoc.title_custom) {
- UndoManager.RunInBatch(() => {
- this.dataDoc.title_custom = true;
- this.dataDoc.layout_showTitle = 'title';
- const tr = this._editorView!.state.tr;
- this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.Document.title).length + 2))).deleteSelection());
- }, 'titler');
- }
// if the text box blurs and none of its contents are focused(), then pass the blur along
setTimeout(() => !this.ProseRef?.contains(document.activeElement) && this._props.onBlur?.());
};
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 5e53a019e..e8cf9e992 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -295,8 +295,8 @@ export class RichTextRules {
// create a hyperlink to a titled document
// @(<doctitle>)
- new InputRule(new RegExp(/(^|\s)@\(([a-zA-Z_@\.\? \-0-9]+)\)/), (state, match, start, end) => {
- const docTitle = match[2];
+ new InputRule(new RegExp(/@\(([a-zA-Z_@\.\? \-0-9]+)\)/), (state, match, start, end) => {
+ const docTitle = match[1];
const prefixLength = '@('.length;
if (docTitle) {
const linkToDoc = (target: Doc) => {
@@ -342,12 +342,7 @@ export class RichTextRules {
const assign = match[4] === ':' ? (match[4] = '') : match[4];
const value = match[5];
const dataDoc = value === undefined ? !fieldKey.startsWith('_') : !assign?.startsWith('=');
- const getTitledDoc = (docTitle: string) => {
- if (!DocServer.FindDocByTitle(docTitle)) {
- Doc.AddToMyPublished(Docs.Create.TextDocument('', { title: docTitle, _width: 400, _layout_autoHeight: true }));
- }
- return DocServer.FindDocByTitle(docTitle);
- };
+ const getTitledDoc = (docTitle: string) => DocServer.FindDocByTitle(docTitle);
// if the value has commas assume its an array (unless it's part of a chat gpt call indicated by '((' )
if (value?.includes(',') && !value.startsWith('((')) {
const values = value.split(',');
@@ -359,7 +354,7 @@ export class RichTextRules {
if (fieldKey === this.TextBox.fieldKey) return this.TextBox.EditorView!.state.tr;
}
const target = docTitle ? getTitledDoc(docTitle) : undefined;
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false, hideValue: false, dataDoc });
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false, hideValue: false });
return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
},
{ inCode: true }
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index e335044ea..cab3a6ef5 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -266,7 +266,6 @@ export const nodes: { [index: string]: NodeSpec } = {
hideKey: { default: false },
hideValue: { default: false },
editable: { default: true },
- dataDoc: { default: false },
},
leafText: node => Field.toString((DocServer.GetCachedRefField(node.attrs.docId as string) as Doc)?.[node.attrs.fieldKey as string] as Field),
group: 'inline',
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 200896e25..9973232bf 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -86,7 +86,8 @@ export namespace Field {
// this is a bit hacky, but we treat '^@' references to a published document
// as a kind of macro to include the content of those documents
Doc.MyPublishedDocs.forEach(doc => {
- const regex = new RegExp(`^\\^${doc.title}\\s`, 'm');
+ const regexMultilineFlag = 'm';
+ const regex = new RegExp(`^\\^${StrCast(doc.title).replace(/[\(\)]*/g, '')}\\s`, regexMultilineFlag); // need to remove characters that can cause the regular expression to be invalid
const sections = (Cast(doc.text, RichTextField, null)?.Text ?? '').split('--DOCDATA--');
if (script.match(regex)) {
script = script.replace(regex, sections[0]) + (sections.length > 1 ? sections[1] : '');
@@ -218,8 +219,14 @@ export class Doc extends RefField {
public static IsInMyOverlay(doc: Doc) { return Doc.MyOverlayDocs.includes(doc); } // prettier-ignore
public static AddToMyOverlay(doc: Doc) { Doc.ActiveDashboard?.myOverlayDocs ? Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
public static RemFromMyOverlay(doc: Doc) { Doc.ActiveDashboard?.myOverlayDocs ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myOverlayDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
- public static AddToMyPublished(doc: Doc) { Doc.ActiveDashboard?.myPublishedDocs ? Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
- public static RemFromMyPublished(doc: Doc){ Doc.ActiveDashboard?.myPublishedDocs ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myPublishedDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
+ public static AddToMyPublished(doc: Doc) {
+ doc[DocData].title_custom = true;
+ doc[DocData].layout_showTitle = 'title';
+ Doc.ActiveDashboard?.myPublishedDocs ? Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
+ public static RemFromMyPublished(doc: Doc){
+ doc[DocData].title_custom = false;
+ doc[DocData].layout_showTitle = undefined;
+ Doc.ActiveDashboard?.myPublishedDocs ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myPublishedDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
public static IsComicStyle(doc?: Doc) { return doc && Doc.ActiveDashboard && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === 'comic' ; } // prettier-ignore
constructor(id?: FieldId, forceSave?: boolean) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index c2ec3f13a..ad592391e 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -1,4 +1,4 @@
-import { $mobx, action, observable, runInAction, trace } from 'mobx';
+import { $mobx, action, observable, runInAction, trace, values } from 'mobx';
import { computedFn } from 'mobx-utils';
import { returnZero } from '../Utils';
import { DocServer } from '../client/DocServer';
@@ -16,6 +16,7 @@ import { RichTextField } from './RichTextField';
import { SchemaHeaderField } from './SchemaHeaderField';
import { ComputedField } from './ScriptField';
import { DocCast, ScriptCast, StrCast } from './Types';
+import { BaseException } from 'pdfjs-dist/types/src/shared/util';
function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
@@ -56,6 +57,8 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
delete curValue[FieldChanged];
}
+ if (typeof prop === 'string' && _propSetterCB.has(prop)) _propSetterCB.get(prop)!(target[SelfProxy], value);
+
const effectiveAcl = GetEffectiveAcl(target);
const writeMode = DocServer.getFieldWriteMode(prop as string);
@@ -282,6 +285,17 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
dataDocChanged && updateCachedAcls(dataDoc);
}
+export var _propSetterCB = new Map<string, ((target: any, value: any) => void) | undefined>();
+/**
+ * sets a callback function to be called whenever a value is assigned to the specified field key.
+ * For example, this is used to "publish" documents with titles that start with '@'
+ * @param prop
+ * @param setter
+ */
+export function SetPropSetterCb(prop: string, setter: ((target: any, value: any) => void) | undefined) {
+ _propSetterCB.set(prop, setter);
+}
+
//
// target should be either a Doc or ListImpl. receiver should be a Proxy<Doc> Or List.
//