From 4cce99ec8d0bb2a45e19dd967dbbb8348c68abe8 Mon Sep 17 00:00:00 2001
From: bobzel
Date: Wed, 28 Feb 2024 22:46:23 -0500
Subject: fixed double click of some icons to make them persist (circle,
rectangle, fitall).
---
src/client/views/nodes/FontIconBox/FontIconBox.tsx | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
(limited to 'src/client/views/nodes/FontIconBox')
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 3577cc8d9..91b6de80b 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -7,7 +7,7 @@ import * as React from 'react';
import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
import { ScriptField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { SelectionManager } from '../../../util/SelectionManager';
import { SettingsManager } from '../../../util/SettingsManager';
@@ -314,6 +314,7 @@ export class FontIconBox extends ViewBoxBaseComponent() {
const tooltip = StrCast(this.Document.toolTip);
const script = ScriptCast(this.Document.onClick);
+ const double = ScriptCast(this.Document.onDoubleClick);
const toggleStatus = script ? script.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result : false;
// Colors
const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
@@ -330,7 +331,12 @@ export class FontIconBox extends ViewBoxBaseComponent() {
//background={SettingsManager.userBackgroundColor}
icon={this.Icon(color)!}
label={this.label}
- onPointerDown={() => script.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false })}
+ onPointerDown={e =>
+ setupMoveUpEvents(this, e, returnTrue, emptyFunction, (e, doubleTap) => {
+ !doubleTap && script.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
+ doubleTap && double.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
+ })
+ }
/>
);
}
--
cgit v1.2.3-70-g09d2
From 5458574510fcda755ae23b4001e17efa799865a5 Mon Sep 17 00:00:00 2001
From: bobzel
Date: Wed, 6 Mar 2024 13:29:30 -0500
Subject: fixed plotly/mermaids loading from a new account
---
src/client/documents/Documents.ts | 6 ++--
src/client/util/CurrentUserUtils.ts | 38 ++++++++++++----------
src/client/util/DropConverter.ts | 1 -
src/client/views/DashboardView.tsx | 1 -
.../collections/collectionFreeForm/MarqueeView.tsx | 1 -
src/client/views/nodes/DocumentView.tsx | 2 +-
src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 +--
src/client/views/nodes/trails/PresBox.tsx | 8 ++---
src/fields/Doc.ts | 7 ++--
9 files changed, 36 insertions(+), 32 deletions(-)
(limited to 'src/client/views/nodes/FontIconBox')
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c9b5cb915..d41cd24d4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -273,7 +273,7 @@ export class DocumentOptions {
layout_hideResizeHandles?: BOOLt = new BoolInfo('whether to hide the resize handles when selected');
layout_hideLinkButton?: BOOLt = new BoolInfo('whether the blue link counter button should be hidden');
layout_hideDecorationTitle?: BOOLt = new BoolInfo('whether to suppress the document decortations title when selected');
- _layout_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown');
+ layout_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown');
layout_borderRounding?: string;
_layout_modificationDate?: DATEt = new DateInfo('last modification date of doc layout', false);
_layout_nativeDimEditable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers', false);
@@ -286,7 +286,6 @@ export class DocumentOptions {
_layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video) value is in seconds', false);
_layout_centered?: BOOLt = new BoolInfo('whether text should be vertically centered in Doc');
_layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
- _layout_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
_layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition', false);
_layout_enableAltContentUI?: BOOLt = new BoolInfo('whether to show alternate content button');
_layout_showTitle?: string; // field name to display in header (:hover is an optional suffix)
@@ -397,6 +396,7 @@ export class DocumentOptions {
_freeform_panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
_freeform_noAutoPan?: BOOLt = new BoolInfo('disables autopanning when this item is dragged');
_freeform_noZoom?: BOOLt = new BoolInfo('disables zooming (used by Pile docs)');
+ _freeform_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
//BUTTONS
buttonText?: string;
@@ -699,7 +699,7 @@ export namespace Docs {
DocumentType.FONTICON,
{
layout: { view: FontIconBox, dataField: 'icon' },
- options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideLinkButton: true, _width: 40, _height: 40 },
+ options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideContextMenu: true, layout_hideLinkButton: true, _width: 40, _height: 40 },
},
],
[
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index d75c717c9..310613bc9 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -66,7 +66,7 @@ export class CurrentUserUtils {
const userTemplates = DocListCast(userDocTemplates?.data).filter(doc => !Doc.IsSystem(doc));
const reqdOpts:DocumentOptions = {
title: "User Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true, hidden: false,
- _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, isSystem: true, _forceActive: true,
+ _dragOnlyWithinContainer: true, layout_hideContextMenu: true, isSystem: true, _forceActive: true,
_layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
};
const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
@@ -235,7 +235,7 @@ export class CurrentUserUtils {
MakeTemplate(Doc.GetProto(slide), true, "Untitled Slide View");
return slide;
}
- const plotlyView = (opts:DocumentOptions) => {
+ const plotlyApi = () => {
var plotly = Doc.MyPublishedDocs.find(doc => doc.title === "@plotly");
if (!plotly) {
const plotly = Docs.Create.TextDocument(
@@ -244,6 +244,8 @@ export class CurrentUserUtils {
, {title: "@plotly", title_custom: true, _layout_showTitle:"title", _width:300,_height:400});
Doc.AddToMyPublished(plotly);
}
+ }
+ const plotlyView = (opts:DocumentOptions) => {
const rtfield = new RichTextField(JSON.stringify(
{doc: {type:"doc",content:[
{type:"code_block",content:[
@@ -269,10 +271,10 @@ export class CurrentUserUtils {
slide[DocData].text = rtfield;
slide[DocData].layout_textPainted = ``;
slide[DocData]._type_collection = CollectionViewType.Freeform;
- slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted", "")`);
+ slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted", "")`, {documentView:"any"});
return slide;
}
- const mermaidsView = (opts:DocumentOptions) => {
+ const mermaidsApi = () => {
var mermaids = Doc.MyPublishedDocs.find(doc => doc.title === "@mermaids");
if (!mermaids) {
const mermaids = Docs.Create.TextDocument(
@@ -294,6 +296,8 @@ export class CurrentUserUtils {
, {title: "@mermaids", title_custom: true, _layout_showTitle:"title", _width:300,_height:400});
Doc.AddToMyPublished(mermaids);
}
+ }
+ const mermaidsView = (opts:DocumentOptions) => {
const rtfield = new RichTextField(JSON.stringify(
{doc: {type:"doc",content:[
{type:"code_block",content:[
@@ -321,9 +325,10 @@ pie title Minerals in my tap water
slide[DocData].text = rtfield;
slide[DocData].layout_textPainted = ``;
slide[DocData]._type_collection = CollectionViewType.Freeform;
- slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted", "")`);
+ slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted", "")`, {documentView:"any"});
return slide;
}
+ const apis = [plotlyApi(), mermaidsApi()]
const emptyThings:{key:string, // the field name where the empty thing will be stored
opts:DocumentOptions, // the document options that are required for the empty thing
funcs?:{[key:string]: any}, // computed fields that are rquired for the empth thing
@@ -396,14 +401,14 @@ pie title Minerals in my tap water
const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit,
- _width: 60, _height: 60, _layout_hideContextMenu: true, _dragOnlyWithinContainer: true,
+ _width: 60, _height: 60, _dragOnlyWithinContainer: true,
btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true,
};
return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
});
const reqdOpts:DocumentOptions = {
- title: "Document Creators", _layout_showTitle: "title", _xMargin: 0, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true,
+ title: "Document Creators", _layout_showTitle: "title", _xMargin: 0, _dragOnlyWithinContainer: true, layout_hideContextMenu: true, _chromeHidden: true, isSystem: true,
_layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
childDragAction: dropActionType.embed
};
@@ -441,7 +446,7 @@ pie title Minerals in my tap water
const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
const reqdBtnOpts:DocumentOptions = {
title, icon, target, toolTip, hidden, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List(['height', 'data_columnHeaders']), dontRegisterView: true,
- _width: 60, _height: 60, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true,
+ _width: 60, _height: 60, _dragOnlyWithinContainer: true,
};
return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
});
@@ -544,7 +549,7 @@ pie title Minerals in my tap water
const reqdToolOps:DocumentOptions = {
title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0",
layout_explainer: "This is a palette of documents that can be created.",
- _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true,
+ _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _dragOnlyWithinContainer: true, layout_hideContextMenu: true, _chromeHidden: true,
};
return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, userBtns]);
}
@@ -555,7 +560,7 @@ pie title Minerals in my tap water
const newDashboard = `createNewDashboard()`;
- const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true,
+ const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _dragOnlyWithinContainer: true,
title: "new Dash", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true };
const reqdBtnScript = {onClick: newDashboard,}
const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.layout_headerButton), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
@@ -596,7 +601,7 @@ pie title Minerals in my tap water
var myFilesystem = DocCast(doc[field]);
const newFolderOpts: DocumentOptions = {
- _forceActive: true, _dragOnlyWithinContainer: true, _embedContainer: Doc.MyFilesystem, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List(['treeView_SortCriterion']),
+ _forceActive: true, _dragOnlyWithinContainer: true, _embedContainer: Doc.MyFilesystem, _width: 30, _height: 30, undoIgnoreFields:new List(['treeView_SortCriterion']),
title: "New folder", color: Colors.BLACK, btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
};
const newFolderScript = { onClick: CollectionTreeView.AddTreeFunc};
@@ -624,7 +629,7 @@ pie title Minerals in my tap water
const recentlyClosed = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
- const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true,
+ const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _dragOnlyWithinContainer: true, layout_hideContextMenu: true,
title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, color: Colors.BLACK, buttonText: "Empty", icon: "trash", isSystem: true,
toolTip: "Empty recently closed",};
DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("this.target")});
@@ -658,8 +663,7 @@ pie title Minerals in my tap water
})
static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
- btnType: ButtonType.ToolButton, _layout_hideContextMenu: true,
- _dropPropertiesToRemove: new List([ "_layout_hideContextMenu"]),
+ btnType: ButtonType.ToolButton, _dropPropertiesToRemove: new List([ "layout_hideContextMenu"]),
/*_nativeWidth: 40, _nativeHeight: 40, */ _width: 40, _height: 40, isSystem: true, ...opts,
})
@@ -804,7 +808,7 @@ pie title Minerals in my tap water
_nativeWidth: params.width ?? 30, _width: params.width ?? 30,
_height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
toolType: params.toolType, expertMode: params.expertMode,
- _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _lockedPosition: true,
+ _dragOnlyWithinContainer: true, _lockedPosition: true,
};
const reqdFuncs:{[key:string]:any} = {
...params.funcs,
@@ -914,14 +918,14 @@ pie title Minerals in my tap water
static setupImportSidebar(doc: Doc, field:string) {
const reqdOpts:DocumentOptions = {
title: "My Imports", _forceActive: true, _layout_showTitle: "title", childLayoutString: ImportElementBox.LayoutString('data'),
- _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, childLimitHeight: 0, onClickScriptDisable:"never",
+ _dragOnlyWithinContainer: true, layout_hideContextMenu: true, childLimitHeight: 0, onClickScriptDisable:"never",
childDragAction: dropActionType.copy, _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
dontRegisterView: true, layout_explainer: "This is where documents that are Imported into Dash will go."
};
const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.MasonryDocument([], opts), reqdOpts, undefined, {onClick: "deselectAll()"});
const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
- _width: 30, _height: 30, color: Colors.BLACK, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton,
+ _width: 30, _height: 30, color: Colors.BLACK, _dragOnlyWithinContainer: true, title: "Import", btnType: ButtonType.ClickButton,
buttonText: "Import", icon: "upload", isSystem: true };
DocUtils.AssignDocField(myImports, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
return myImports;
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 54066d267..ba981145d 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -73,7 +73,6 @@ 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/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 439d250de..146ac5b01 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -428,7 +428,6 @@ export class DashboardView extends ObservableReactComponent<{}> {
_width: 30,
_height: 30,
_dragOnlyWithinContainer: true,
- _layout_hideContextMenu: true,
title: 'New trail',
toolTip: 'Create new trail',
color: Colors.BLACK,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index d0e59180d..e22b83307 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -494,7 +494,6 @@ export class MarqueeView extends ObservableReactComponent {
- if (e && this.layoutDoc._layout_hideContextMenu && Doc.noviceMode) {
+ if (e && this.layoutDoc.layout_hideContextMenu && Doc.noviceMode) {
e.preventDefault();
e.stopPropagation();
//!this._props.isSelected(true) && SelectionManager.SelectView(this.DocumentView(), false);
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 91b6de80b..d24c79b10 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -333,8 +333,8 @@ export class FontIconBox extends ViewBoxBaseComponent() {
label={this.label}
onPointerDown={e =>
setupMoveUpEvents(this, e, returnTrue, emptyFunction, (e, doubleTap) => {
- !doubleTap && script.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
- doubleTap && double.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
+ (!doubleTap || !double) && script.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
+ doubleTap && double && double.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
})
}
/>
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 918987034..0d4f9ec78 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -2223,10 +2223,10 @@ export class PresBox extends ViewBoxBaseComponent() {
// prettier-ignore
switch (layout) {
case 'blank': return Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x, y });
- case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y });
- case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y });
- case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y });
- case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y })
+ case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _freeform_fitContentsToBox: true, x, y });
+ case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _freeform_fitContentsToBox: true, x, y });
+ case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _freeform_fitContentsToBox: true, x, y });
+ case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _freeform_fitContentsToBox: true, x, y })
}
};
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 1df9d80a1..be1af3e6c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -172,7 +172,7 @@ export class Doc extends RefField {
public static get MyTrails() { return DocCast(Doc.ActiveDashboard?.myTrails); } // prettier-ignore
public static get MyCalendars() { return DocCast(Doc.ActiveDashboard?.myCalendars); } // prettier-ignore
public static get MyOverlayDocs() { return DocListCast(Doc.ActiveDashboard?.myOverlayDocs ?? DocCast(Doc.UserDoc().myOverlayDocs)?.data); } // prettier-ignore
- public static get MyPublishedDocs() { return DocListCast(Doc.ActiveDashboard?.myPublishedDocs ?? DocCast(Doc.UserDoc().myPublishedDocs)?.data); } // prettier-ignore
+ public static get MyPublishedDocs() { return DocListCast(Doc.ActiveDashboard?.myPublishedDocs).concat(DocListCast(DocCast(Doc.UserDoc().myPublishedDocs)?.data)); } // prettier-ignore
public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); } // prettier-ignore
public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); } // prettier-ignore
public static get MyImports() { return DocCast(Doc.UserDoc().myImports); } // prettier-ignore
@@ -583,6 +583,9 @@ export namespace Doc {
Doc.RemoveDocFromList(doc[DocData], 'proto_embeddings', embedding);
}
export function AddEmbedding(doc: Doc, embedding: Doc) {
+ if (embedding === null) {
+ console.log('WHAT?');
+ }
Doc.AddDocToList(doc[DocData], 'proto_embeddings', embedding, undefined, undefined, undefined, undefined, undefined, true);
}
export function GetEmbeddings(doc: Doc) {
@@ -607,7 +610,7 @@ export namespace Doc {
const dataDoc = doc[DocData];
const availableEmbeddings = Doc.GetEmbeddings(dataDoc);
const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail);
- bestEmbedding && Doc.AddDocToList(dataDoc, 'proto_embeddings', doc, undefined, undefined, undefined, undefined, undefined, true);
+ bestEmbedding && Doc.AddEmbedding(doc, doc);
return bestEmbedding ?? Doc.MakeEmbedding(doc);
}
--
cgit v1.2.3-70-g09d2
From b938ac2909290bd5dd11eeb0a0056f4679dbb25e Mon Sep 17 00:00:00 2001
From: bobzel
Date: Thu, 7 Mar 2024 10:24:56 -0500
Subject: fixed several things related to templates for labels (used as icons)
and FontIconboxes. Also made labels non uniformly resizable .
---
src/client/documents/Documents.ts | 16 +++++++---------
src/client/util/CurrentUserUtils.ts | 16 +++++++++-------
src/client/views/DocumentDecorations.tsx | 10 +++++-----
.../collectionFreeForm/CollectionFreeFormView.tsx | 12 ++++++++----
src/client/views/nodes/FontIconBox/FontIconBox.tsx | 8 ++++++--
src/client/views/nodes/LabelBox.tsx | 10 +++-------
6 files changed, 38 insertions(+), 34 deletions(-)
(limited to 'src/client/views/nodes/FontIconBox')
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d41cd24d4..b7d8222b9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -277,8 +277,8 @@ export class DocumentOptions {
layout_borderRounding?: string;
_layout_modificationDate?: DATEt = new DateInfo('last modification date of doc layout', false);
_layout_nativeDimEditable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers', false);
- _layout_reflowVertical?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
- _layout_reflowHorizontal?: BOOLt = new BoolInfo('whether a doc with a native size can be horizonally resized, causing some form of reflow');
+ _layout_reflowVertical?: BOOLt = new BoolInfo('permit vertical resizing with content "reflow"');
+ _layout_reflowHorizontal?: BOOLt = new BoolInfo('permit horizontal resizing with content reflow');
layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
layout_maxShown?: NUMt = new NumInfo('maximum number of children to display at one time (see multicolumnview)');
_layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
@@ -317,7 +317,6 @@ export class DocumentOptions {
_label_minFontSize?: NUMt = new NumInfo('minimum font size for labelBoxes', false);
_label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes', false);
stroke_width?: NUMt = new NumInfo('width of an ink stroke', false);
- icon_label?: STRt = new StrInfo('label to use for a fontIcon doc (otherwise, the title is used)', false);
mediaState?: STRt = new StrInfo(`status of audio/video media document: ${media_state.PendingRecording}, ${media_state.Recording}, ${media_state.Paused}, ${media_state.Playing}`, false);
recording?: BOOLt = new BoolInfo('whether WebCam is recording or not');
slides?: DOCt = new DocInfo('presentation slide associated with video recording (bcz: should be renamed!!)');
@@ -652,8 +651,8 @@ export namespace Docs {
[
DocumentType.LABEL,
{
- layout: { view: LabelBox, dataField: defaultDataKey },
- options: { _singleLine: true },
+ layout: { view: LabelBox, dataField: 'title' },
+ options: { _singleLine: true, layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true },
},
],
[
@@ -684,8 +683,8 @@ export namespace Docs {
[
DocumentType.BUTTON,
{
- layout: { view: LabelBox, dataField: 'onClick' },
- options: {},
+ layout: { view: LabelBox, dataField: 'title' },
+ options: { layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true },
},
],
[
@@ -1881,8 +1880,7 @@ export namespace DocUtils {
const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context));
if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
const pushpin = Docs.Create.FontIconDocument({
- title: 'pushpin',
- icon_label: '',
+ title: '',
annotationOn: Cast(doc.annotationOn, Doc, null),
followLinkToggle: true,
icon: 'map-pin',
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 310613bc9..cfba003fc 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -22,7 +22,7 @@ import { CollectionTreeView, TreeViewType } from "../views/collections/Collectio
import { Colors } from "../views/global/globalEnums";
import { media_state } from "../views/nodes/AudioBox";
import { OpenWhere } from "../views/nodes/DocumentView";
-import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox";
+import { ButtonType, FontIconBox } from "../views/nodes/FontIconBox/FontIconBox";
import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox";
import { DragManager, dropActionType } from "./DragManager";
import { MakeTemplate } from "./DropConverter";
@@ -32,6 +32,8 @@ import { ScriptingGlobals } from "./ScriptingGlobals";
import { ColorScheme } from "./SettingsManager";
import { SnappingManager } from "./SnappingManager";
import { UndoManager } from "./UndoManager";
+import { LabelBox } from "../views/nodes/LabelBox";
+import { ImageBox } from "../views/nodes/ImageBox";
interface Button {
// DocumentOptions fields a button can set
@@ -156,14 +158,14 @@ export class CurrentUserUtils {
}
const allopts = {isSystem: true, onClickScriptDisable: "never", ...opts};
return DocUtils.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ?
- DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
+ DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts, templateField), true, iconFieldName, templateField))),
{onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", });
};
- const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
- textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
+ const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({
+ layout: LabelBox.LayoutString(fieldKey), textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
});
- const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts });
- const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts });
+ const imageBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.ImageDocument( "http://www.cs.brown.edu/~bcz/noImage.png", { layout:ImageBox.LayoutString(fieldKey), "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts });
+ const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts });
const iconTemplates = [
makeIconTemplate(undefined, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}),
makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}),
@@ -174,7 +176,7 @@ export class CurrentUserUtils {
makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}),
makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}),
makeIconTemplate(DocumentType.VID, "icon", { iconTemplate:DocumentType.IMG}),
- makeIconTemplate(DocumentType.BUTTON,"data", { iconTemplate:DocumentType.FONTICON}),
+ makeIconTemplate(DocumentType.BUTTON,"title", { iconTemplate:DocumentType.FONTICON}),
//nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }),
//makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts))
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 87ee962a0..2fb9f0fc1 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -323,7 +323,7 @@ export class DocumentDecorations extends ObservableReactComponent {
const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2);
const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box
- doc.layout_borderRounding = `${radius}px`;
+ doc._layout_borderRounding = `${radius}px`;
});
return false;
},
@@ -533,9 +533,9 @@ export class DocumentDecorations extends ObservableReactComponent docCurveTVal) {
const localStartTVal = startSegmentT - Math.floor(i / 4);
t !== (localStartTVal < 0 ? 0 : localStartTVal) && segment.push(inkSegment.split(localStartTVal < 0 ? 0 : localStartTVal, t));
- segment.length && segments.push(segment);
+ if (segment.length && (Math.abs(segment[0].points[0].x - segment[0].points.lastElement().x) > 0.5 || Math.abs(segment[0].points[0].y - segment[0].points.lastElement().y) > 0.5)) segments.push(segment);
}
// start a new segment from the intersection t value
- segment = tVals.length - 1 === index ? [inkSegment.split(t).right] : [];
+ if (tVals.length - 1 === index) {
+ const split = inkSegment.split(t).right;
+ if (split && (Math.abs(split.points[0].x - split.points.lastElement().x) > 0.5 || Math.abs(split.points[0].y - split.points.lastElement().y) > 0.5)) segment = [split];
+ else segment = [];
+ } else segment = [];
startSegmentT = docCurveTVal;
});
} else {
@@ -1647,7 +1651,7 @@ export class CollectionFreeFormView extends CollectionSubView (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' });
- this._props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor)), icon: 'palette' });
+ this._props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor)), icon: 'palette' });
this._props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' });
if (!Doc.noviceMode) {
optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index d24c79b10..46043eefb 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -81,9 +81,13 @@ export class FontIconBox extends ViewBoxBaseComponent() {
}
};
- // Determining UI Specs
+ /**
+ * this chooses the appropriate title for the label
+ * if the Document is a template, then we use the title of the data doc that it renders
+ * otherwise, we use the Document's title itself.
+ */
@computed get label() {
- return StrCast(this.dataDoc.icon_label, StrCast(this.Document.title));
+ return StrCast(this.Document.isTemplateDoc ? this.dataDoc.title : this.Document.title);
}
Icon = (color: string, iconFalse?: boolean) => {
let icon;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index fd3074a88..be20b5934 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -17,12 +17,8 @@ import './LabelBox.scss';
import { PinProps, PresBox } from './trails';
import { Docs } from '../../documents/Documents';
-export interface LabelBoxProps extends FieldViewProps {
- label?: string;
-}
-
@observer
-export class LabelBox extends ViewBoxBaseComponent() {
+export class LabelBox extends ViewBoxBaseComponent() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(LabelBox, fieldKey);
}
@@ -32,7 +28,7 @@ export class LabelBox extends ViewBoxBaseComponent() {
private dropDisposer?: DragManager.DragDropDisposer;
private _timeout: any;
- constructor(props: LabelBoxProps) {
+ constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
}
@@ -45,7 +41,7 @@ export class LabelBox extends ViewBoxBaseComponent() {
}
@computed get Title() {
- return this.dataDoc.title_custom ? StrCast(this.Document.title) : this._props.label ? this._props.label : Field.toString(this.dataDoc[this.fieldKey] as Field);
+ return Field.toString(this.dataDoc[this.fieldKey] as Field);
}
protected createDropTarget = (ele: HTMLDivElement) => {
--
cgit v1.2.3-70-g09d2
From a9eb266296d1b71f1016c867f39e20299c011eea Mon Sep 17 00:00:00 2001
From: bobzel
Date: Sat, 9 Mar 2024 23:08:15 -0500
Subject: added a template button to top bar to set default layout. fixed show
title when value is a list. fixed typeahead for createing notes with
templates.
---
src/client/util/CurrentUserUtils.ts | 7 ++++---
src/client/views/GlobalKeyHandler.ts | 3 +++
src/client/views/MainView.tsx | 1 +
src/client/views/TemplateMenu.tsx | 13 -------------
src/client/views/global/globalScripts.ts | 19 +++++++++++++++++++
src/client/views/nodes/DocumentView.tsx | 2 +-
src/client/views/nodes/FontIconBox/FontIconBox.tsx | 21 +++++++++++++++------
7 files changed, 43 insertions(+), 23 deletions(-)
(limited to 'src/client/views/nodes/FontIconBox')
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 564a46514..15255641d 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -124,11 +124,11 @@ export class CurrentUserUtils {
{ title: "Postit", backgroundColor: "yellow", icon: "sticky-note"},
{ title: "Idea", backgroundColor: "pink", icon: "lightbulb" },
{ title: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
- const reqdNoteList = reqdTempOpts.map(opts => {
+ const reqdNoteList = [...reqdTempOpts.map(opts => {
const reqdOpts = {...opts, isSystem:true, width:200, layout_autoHeight: true, layout_fitWidth: true};
const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.title === opts.title): undefined;
return DocUtils.AssignOpts(noteTemp, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts));
- });
+ }), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))];
const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
@@ -788,7 +788,8 @@ pie title Minerals in my tap water
CollectionViewType.Grid, CollectionViewType.NoteTaking]),
title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}},
{ title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, width: 30, scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}},
- { title: "Header", icon: "heading", toolTip: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: true, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} },
+ { title: "Header", icon: "heading", toolTip: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} },
+ { title: "Template",icon: "scroll", toolTip: "Default Note Template",btnType: ButtonType.ToggleButton, expertMode: false, toolType:DocumentType.RTF, scripts: { onClick: '{ return setDefaultTemplate(_readOnly_); }'} },
{ title: "Fill", icon: "fill-drip", toolTip: "Fill/Background Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected
{ title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(this.toolType, this.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
{ title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 733383002..e800798ca 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -24,6 +24,7 @@ import { MainView } from './MainView';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { OpenWhereMod } from './nodes/DocumentView';
import { AnchorMenu } from './pdf/AnchorMenu';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
const modifiers = ['control', 'meta', 'shift', 'alt'];
type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo;
@@ -61,6 +62,8 @@ export class KeyManager {
});
public handle = action((e: KeyboardEvent) => {
+ // accumulate buffer of characters to insert in a new text note. once the note is created, it will stop keyboard events from reaching this function.
+ if (FormattedTextBox.SelectOnLoadChar) FormattedTextBox.SelectOnLoadChar = FormattedTextBox.SelectOnLoadChar + (e.key === 'Enter' ? '\n' : e.key);
e.key === 'Control' && (CtrlKey = true);
//if (!Doc.noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
const keyname = e.key && e.key.toLowerCase();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 3be52597a..e369e2876 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -397,6 +397,7 @@ export class MainView extends ObservableReactComponent<{}> {
fa.faPaintBrush,
fa.faTimes,
fa.faFlag,
+ fa.faScroll,
fa.faEye,
fa.faArrowsAlt,
fa.faQuoteLeft,
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 00195f7d7..eed197b0b 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -45,7 +45,6 @@ class OtherToggle extends React.Component<{ checked: boolean; name: string; togg
export interface TemplateMenuProps {
docViews: DocumentView[];
- templates?: Map;
}
@observer
@@ -61,17 +60,6 @@ export class TemplateMenu extends React.Component {
this.props.docViews.map(dv => dv.switchViews(false, 'layout'));
};
- @undoBatch
- @action
- toggleTemplate = (event: React.ChangeEvent, template: string): void => {
- this.props.docViews.forEach(d => (Doc.Layout(d.layoutDoc)['_show' + template] = event.target.checked ? template.toLowerCase() : ''));
- };
-
- @action
- toggleTemplateActivity = (): void => {
- this._hidden = !this._hidden;
- };
-
// todo: add brushes to brushMap to save with a style name
onCustomKeypress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
@@ -104,7 +92,6 @@ export class TemplateMenu extends React.Component {
const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data);
const addedTypes = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
const templateMenu: Array = [];
- this.props.templates?.forEach((checked, template) => templateMenu.push());
templateMenu.push();
addedTypes.concat(noteTypes).map(template => (template.treeView_Checked = this.templateIsUsed(firstDoc, template)));
this._addedKeys &&
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 33704e8fe..0579b07c7 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -21,6 +21,9 @@ import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
import { WebBox } from '../nodes/WebBox';
import { VideoBox } from '../nodes/VideoBox';
import { DocData } from '../../../fields/DocSymbols';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { PrefetchProxy } from '../../../fields/Proxy';
+import { MakeTemplate } from '../../util/DropConverter';
ScriptingGlobals.add(function IsNoneSelected() {
return SelectionManager.Views.length <= 0;
@@ -71,6 +74,22 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
}
});
+// toggle: Set overlay status of selected document
+ScriptingGlobals.add(function setDefaultTemplate(checkResult?: boolean) {
+ if (checkResult) {
+ return Doc.UserDoc().defaultTextLayout;
+ }
+ const view = SelectionManager.Views.length === 1 && SelectionManager.Views[0].ComponentView instanceof FormattedTextBox ? SelectionManager.Views[0] : undefined;
+
+ if (view) {
+ const tempDoc = view.Document;
+ if (!view.layoutDoc.isTemplateDoc) {
+ MakeTemplate(tempDoc);
+ }
+ Doc.UserDoc().defaultTextLayout = new PrefetchProxy(tempDoc);
+ tempDoc && Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', tempDoc);
+ } else Doc.UserDoc().defaultTextLayout = undefined;
+});
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
if (checkResult) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 431781d24..29266bd8e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -849,7 +849,7 @@ export class DocumentViewInternal extends DocComponent targetDoc[field.trim()]?.toString())
+ .map(field => Field.toString(targetDoc[field.trim()] as Field))
.join(' \\ ') || '-unset-'
}
display="block"
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 46043eefb..f02ad7300 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -1,7 +1,7 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { computed, makeObservable, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
@@ -312,6 +312,8 @@ export class FontIconBox extends ViewBoxBaseComponent() {
);
}
+ @observable _hackToRecompute = 0; // bcz: ugh ... 's toggleStatus initializes but doesn't track its value after a click. so a click that does nothing to the toggle state will toggle the button anyway. this forces the Toggle to re-read the ToggleStatus value.
+
@computed get toggleButton() {
// Determine the type of toggle button
const buttonText = StrCast(this.dataDoc.buttonText);
@@ -319,7 +321,7 @@ export class FontIconBox extends ViewBoxBaseComponent() {
const script = ScriptCast(this.Document.onClick);
const double = ScriptCast(this.Document.onDoubleClick);
- const toggleStatus = script ? script.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result : false;
+ const toggleStatus = script?.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result ?? false;
// Colors
const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
const backgroundColor = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor);
@@ -336,10 +338,17 @@ export class FontIconBox extends ViewBoxBaseComponent() {
icon={this.Icon(color)!}
label={this.label}
onPointerDown={e =>
- setupMoveUpEvents(this, e, returnTrue, emptyFunction, (e, doubleTap) => {
- (!doubleTap || !double) && script.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
- doubleTap && double && double.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
- })
+ setupMoveUpEvents(
+ this,
+ e,
+ returnTrue,
+ emptyFunction,
+ action((e, doubleTap) => {
+ (!doubleTap || !double) && script?.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
+ doubleTap && double?.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
+ this._hackToRecompute = this._hackToRecompute + 1;
+ })
+ )
}
/>
);
--
cgit v1.2.3-70-g09d2
From a974aa4e6573c8becf93f78610406747fec14c1c Mon Sep 17 00:00:00 2001
From: bobzel
Date: Tue, 19 Mar 2024 17:08:46 -0400
Subject: cleaned up user templates to not get changed on reload. made setting
a template add it to the template tools list and as a tools button. fixed
linking to parts of a template. fixed disappearing templates caused by
stacking view set a field with an empty key. updated field assignment
syntax in trees, dash field views, and key value box to all use :,:=,=,=:=
syntax. added text elide button. added @(title) syntax for hyperlinking.
made using a template both inherit from the template to get default values
and use the template to render. fixed submenu placement of context menu.
updated RTF markdown doc.
---
src/client/util/CurrentUserUtils.ts | 19 +++--
src/client/util/DropConverter.ts | 45 +++++-----
src/client/util/LinkManager.ts | 13 +--
src/client/util/RTFMarkup.tsx | 30 +++----
src/client/views/ContextMenuItem.tsx | 2 +-
src/client/views/TemplateMenu.scss | 1 +
src/client/views/TemplateMenu.tsx | 7 +-
.../collections/CollectionMasonryViewFieldRow.tsx | 10 +--
.../CollectionStackingViewFieldColumn.tsx | 6 +-
src/client/views/collections/TreeView.tsx | 14 +++-
src/client/views/global/globalScripts.ts | 33 +++-----
src/client/views/nodes/ComparisonBox.tsx | 71 +++++++++-------
src/client/views/nodes/DocumentView.tsx | 32 ++++++-
src/client/views/nodes/FieldView.tsx | 1 +
src/client/views/nodes/FontIconBox/FontIconBox.tsx | 22 ++---
src/client/views/nodes/KeyValueBox.tsx | 2 +-
src/client/views/nodes/KeyValuePair.tsx | 2 +-
.../views/nodes/formattedText/DashFieldView.tsx | 57 +++++++++----
.../views/nodes/formattedText/FormattedTextBox.tsx | 34 ++++----
.../views/nodes/formattedText/RichTextMenu.tsx | 13 +++
.../views/nodes/formattedText/RichTextRules.ts | 97 ++++++++++++----------
src/client/views/nodes/formattedText/marks_rts.ts | 5 +-
src/client/views/nodes/formattedText/nodes_rts.ts | 2 +
src/fields/Doc.ts | 42 +++++-----
src/fields/util.ts | 4 +
25 files changed, 328 insertions(+), 236 deletions(-)
(limited to 'src/client/views/nodes/FontIconBox')
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 84a33500d..bbee18707 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -73,7 +73,7 @@ export class CurrentUserUtils {
};
const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
const reqdFuncs = { /* hidden: "IsNoviceMode()" */ };
- return DocUtils.AssignScripts(DocUtils.AssignOpts(userDocTemplates, reqdOpts, userTemplates) ?? Docs.Create.MasonryDocument(userTemplates, reqdOpts), reqdScripts, reqdFuncs);
+ return DocUtils.AssignScripts(userDocTemplates ?? Docs.Create.MasonryDocument(userTemplates, reqdOpts), reqdScripts, reqdFuncs);
}
/// Initializes templates for editing click funcs of a document
@@ -133,11 +133,19 @@ export class CurrentUserUtils {
const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
}
+ static setupUserTemplates(doc: Doc, field="template_user") {
+ const tempUsers = DocCast(doc[field]);
+ const reqdUserList = DocListCast(tempUsers?.data);
+
+ const reqdOpts:DocumentOptions = { title: "User Layouts", _height: 75, isSystem: true };
+ return DocUtils.AssignOpts(tempUsers, reqdOpts, reqdUserList) ?? (doc[field] = Docs.Create.TreeDocument(reqdUserList, reqdOpts));
+ }
/// Initializes collection of templates for notes and click functions
static setupDocTemplates(doc: Doc, field="myTemplates") {
const templates = [
CurrentUserUtils.setupNoteTemplates(doc),
+ CurrentUserUtils.setupUserTemplates(doc),
CurrentUserUtils.setupClickEditorTemplates(doc)
];
CurrentUserUtils.setupChildClickEditors(doc)
@@ -375,9 +383,9 @@ pie title Minerals in my tap water
{ toolTip: "Tap or drag to create a flashcard", title: "Flashcard", icon: "id-card", dragFactory: doc.emptyFlashcard as Doc, clickFactory: DocCast(doc.emptyFlashcard)},
{ toolTip: "Tap or drag to create an equation", title: "Math", icon: "calculator", dragFactory: doc.emptyEquation as Doc, clickFactory: DocCast(doc.emptyEquation)},
{ toolTip: "Tap or drag to create a mermaid node", title: "Mermaids", icon: "rocket", dragFactory: doc.emptyMermaids as Doc, clickFactory: DocCast(doc.emptyMermaids)},
- { toolTip: "Tap or drag to create a plotly node", title: "Plotly", icon: "rocket", dragFactory: doc.emptyPlotly as Doc, clickFactory: DocCast(doc.emptyMermaids)},
+ { toolTip: "Tap or drag to create a plotly node", title: "Plotly", icon: "rocket", dragFactory: doc.emptyPlotly as Doc, clickFactory: DocCast(doc.emptyMermaids)},
{ toolTip: "Tap or drag to create a physics simulation",title: "Simulation", icon: "rocket",dragFactory: doc.emptySimulation as Doc, clickFactory: DocCast(doc.emptySimulation), funcs: { hidden: "IsNoviceMode()"}},
- { toolTip: "Tap or drag to create a note board", title: "Notes", icon: "book", dragFactory: doc.emptyNoteboard as Doc, clickFactory: DocCast(doc.emptyNoteboard)},
+ { toolTip: "Tap or drag to create a note board", title: "Notes", icon: "book", dragFactory: doc.emptyNoteboard as Doc, clickFactory: DocCast(doc.emptyNoteboard)},
{ toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc, clickFactory: DocCast(doc.emptyTab)},
{ toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, clickFactory: DocCast(doc.emptyWebpage)},
{ toolTip: "Tap or drag to create a comparison box", title: "Compare", icon: "columns", dragFactory: doc.emptyComparison as Doc, clickFactory: DocCast(doc.emptyComparison)},
@@ -385,10 +393,10 @@ pie title Minerals in my tap water
{ toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, clickFactory: DocCast(doc.emptyMap)},
{ toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreengrab as Doc, clickFactory: DocCast(doc.emptyScreengrab), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, clickFactory: DocCast(doc.emptyWebCam), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
- { toolTip: "Tap or drag to create a button", title: "Button", icon: "circle", dragFactory: doc.emptyButton as Doc, clickFactory: DocCast(doc.emptyButton)},
+ { toolTip: "Tap or drag to create a button", title: "Button", icon: "circle", dragFactory: doc.emptyButton as Doc, clickFactory: DocCast(doc.emptyButton)},
{ toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, clickFactory: DocCast(doc.emptyScript), funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "chart-bar", dragFactory: doc.emptyDataViz as Doc, clickFactory: DocCast(doc.emptyDataViz)},
- { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "person-chalkboard", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
+ { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "person-chalkboard", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a view slide", title: "View Slide", icon: "address-card", dragFactory: doc.emptyViewSlide as Doc,clickFactory: DocCast(doc.emptyViewSlide),openFactoryLocation: OpenWhere.overlay,funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize",dragFactory: doc.emptyHeader as Doc,clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true, funcs: { hidden: "IsNoviceMode()"} },
{ toolTip: "Toggle a Calculator REPL", title: "replviewer", icon: "calculator", clickFactory: '' as any, openFactoryLocation: OpenWhere.overlay}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
@@ -738,6 +746,7 @@ pie title Minerals in my tap water
{ title: "Center", toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
]},
+ { title: "Elide", toolTip: "Elide selection", btnType: ButtonType.ToggleButton, icon: "eye", toolType:"elide", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}},
{ title: "Dictate", toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", toolType:"dictation", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}},
{ title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", toolType:"noAutoLink", expertMode:true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}},
// { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}},
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 3df3e36c6..ed5749d06 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -28,6 +28,7 @@ export function MakeTemplate(doc: Doc) {
}
/**
+ *
* Recursively converts 'doc' into a template that can be used to render other documents.
*
* For recurive Docs in the template, their target fieldKey is defined by their title,
@@ -75,32 +76,36 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
remProps.map(prop => (dbox[prop] = undefined));
}
} else if (!doc.onDragStart && !doc.isButtonBar) {
- const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
- if (layoutDoc.type !== DocumentType.FONTICON) {
- !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc);
- }
- layoutDoc.isTemplateDoc = true;
- dbox = Docs.Create.FontIconDocument({
- _nativeWidth: 100,
- _nativeHeight: 100,
- _width: 100,
- _height: 100,
- backgroundColor: StrCast(doc.backgroundColor),
- title: StrCast(layoutDoc.title),
- btnType: ButtonType.ClickButton,
- icon: 'bolt',
- isSystem: false,
- });
- dbox.title = ComputedField.MakeFunction('this.dragFactory.title');
- dbox.dragFactory = layoutDoc;
- dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined;
- dbox.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory)');
+ dbox = makeUserTemplateButton(doc);
} else if (doc.isButtonBar) {
dbox.ignoreClick = true;
}
data.droppedDocuments[i] = dbox;
});
}
+export function makeUserTemplateButton(doc: Doc) {
+ const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
+ if (layoutDoc.type !== DocumentType.FONTICON) {
+ !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc);
+ }
+ layoutDoc.isTemplateDoc = true;
+ const dbox = Docs.Create.FontIconDocument({
+ _nativeWidth: 100,
+ _nativeHeight: 100,
+ _width: 100,
+ _height: 100,
+ backgroundColor: StrCast(doc.backgroundColor),
+ title: StrCast(layoutDoc.title),
+ btnType: ButtonType.ClickButton,
+ icon: 'bolt',
+ isSystem: false,
+ });
+ dbox.title = ComputedField.MakeFunction('this.dragFactory.title');
+ dbox.dragFactory = layoutDoc;
+ dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined;
+ dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory)');
+ return dbox;
+}
ScriptingGlobals.add(
function convertToButtons(dragData: any) {
convertDropDataToButtons(dragData as DragManager.DocumentDragData);
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 0c8d18a7a..cf16c4d6d 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -58,8 +58,8 @@ export class LinkManager {
link &&
action(lAnchProtoProtos => {
Doc.AddDocToList(Doc.UserDoc(), 'links', link);
- lAnchs[0] && lAnchs[0][DocData][DirectLinks].add(link);
- lAnchs[1] && lAnchs[1][DocData][DirectLinks].add(link);
+ lAnchs[0]?.[DocData][DirectLinks].add(link);
+ lAnchs[1]?.[DocData][DirectLinks].add(link);
})
)
)
@@ -170,10 +170,11 @@ export class LinkManager {
console.log('WAITING FOR DOC/PROTO IN LINKMANAGER');
return [];
}
- const dirLinks = Doc.GetProto(anchor)[DirectLinks];
- const annos = DocListCast(anchor[Doc.LayoutFieldKey(anchor) + '_annotations']);
- if (!annos) debugger;
- return annos.reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
+
+ const dirLinks = Array.from(anchor[DocData][DirectLinks]).filter(l => Doc.GetProto(anchor) === anchor[DocData] || ['1', '2'].includes(LinkManager.anchorIndex(l, anchor) as any));
+ const anchorRoot = DocCast(anchor.rootDocument, anchor); // template Doc fields store annotations on the topmost root of a template (not on themselves since the template layout items are only for layout)
+ const annos = DocListCast(anchorRoot[Doc.LayoutFieldKey(anchor) + '_annotations']);
+ return annos.reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks));
}, true);
// returns map of group type to anchor's links in that group type
diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
index f96d8a5df..315daad42 100644
--- a/src/client/util/RTFMarkup.tsx
+++ b/src/client/util/RTFMarkup.tsx
@@ -3,18 +3,12 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { MainViewModal } from '../views/MainViewModal';
import { SettingsManager } from './SettingsManager';
-import { Doc } from '../../fields/Doc';
-import { StrCast } from '../../fields/Types';
@observer
export class RTFMarkup extends React.Component<{}> {
static Instance: RTFMarkup;
@observable private isOpen = false; // whether the SharingManager modal is open or not
- // private get linkVisible() {
- // return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false;
- // }
-
@action
public open = () => (this.isOpen = true);
@@ -39,6 +33,10 @@ export class RTFMarkup extends React.Component<{}> {
{`wiki:phrase`}
{` display wikipedia page for entered text (terminate with carriage return)`}
+
+ {`(( any text ))`}
+ {` submit text to Chat GPT to have results appended afterward`}
+
{`#tag `}
{` add hashtag metadata to document. e.g, #idea`}
@@ -47,10 +45,6 @@ export class RTFMarkup extends React.Component<{}> {
{`#, ## ... ###### `}
{` set heading style based on number of '#'s between 1 and 6`}
{`>> `}
{` add a sidebar text document inline`}
@@ -61,7 +55,7 @@ export class RTFMarkup extends React.Component<{}> {
{`cmd-f `}
- {` collapse to an inline footnote)`}
+ {` collapse to an inline footnote`}
{`cmd-e `}
@@ -116,20 +110,20 @@ export class RTFMarkup extends React.Component<{}> {
{` start a block of text that begins with a hanging indent`}
- {`[:doctitle]] `}
+ {`@(doctitle) `}
{` hyperlink to document specified by it’s title`}
- {`[[fieldname]] `}
- {` display value of fieldname`}
+ {`[@(doctitle.)fieldname] `}
+ {` display value of fieldname of text document (unless (doctitle.) is used to indicate another document by it's title)`}
- {`[[fieldname=value]] `}
- {` assign value to fieldname of document and display it`}
+ {`[@fieldname:value] `}
+ {` assign value to fieldname to data document and display it (if '=' is used instead of ':' the value is set on the layout Doc. if value is wrapped in (()) then it will be sent to ChatGPT and the response will replace the value)`}
- {`[[fieldname:doctitle]] `}
- {` show value of fieldname from doc specified by it’s title`}
+ {`[@fieldname:=expression] `}
+ {` assign a computed expression to fieldname to data document and display it (if '=:=' is used instead of ':=' the expression is set on the layout Doc. if value is wrapped in (()) then it will be sent to ChatGPT and the prompt/response will replace the value)`}
);
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 0579b07c7..3fdc9a488 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -1,29 +1,26 @@
import { Colors } from 'browndash-components';
import { action, runInAction } from 'mobx';
+import { aggregateBounds } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { InkTool } from '../../../fields/InkField';
import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { aggregateBounds } from '../../../Utils';
import { DocumentType } from '../../documents/DocumentTypes';
import { LinkManager } from '../../util/LinkManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from '../../util/SelectionManager';
-import { undoable, UndoManager } from '../../util/UndoManager';
-import { CollectionFreeFormView } from '../collections/collectionFreeForm';
+import { UndoManager, undoable } from '../../util/UndoManager';
import { GestureOverlay } from '../GestureOverlay';
import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, SetActiveIsInkMask } from '../InkingStroke';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
// import { InkTranscription } from '../InkTranscription';
+import { DocData } from '../../../fields/DocSymbols';
import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
import { DocumentView } from '../nodes/DocumentView';
-import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { WebBox } from '../nodes/WebBox';
import { VideoBox } from '../nodes/VideoBox';
-import { DocData } from '../../../fields/DocSymbols';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { PrefetchProxy } from '../../../fields/Proxy';
-import { MakeTemplate } from '../../util/DropConverter';
+import { WebBox } from '../nodes/WebBox';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
ScriptingGlobals.add(function IsNoneSelected() {
return SelectionManager.Views.length <= 0;
@@ -76,19 +73,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setDefaultTemplate(checkResult?: boolean) {
- if (checkResult) {
- return Doc.UserDoc().defaultTextLayout;
- }
- const view = SelectionManager.Views.length === 1 && SelectionManager.Views[0].ComponentView instanceof FormattedTextBox ? SelectionManager.Views[0] : undefined;
-
- if (view) {
- const tempDoc = view.Document;
- if (!view.layoutDoc.isTemplateDoc) {
- MakeTemplate(tempDoc);
- }
- Doc.UserDoc().defaultTextLayout = new PrefetchProxy(tempDoc);
- tempDoc && Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', tempDoc);
- } else Doc.UserDoc().defaultTextLayout = undefined;
+ return DocumentView.setDefaultTemplate(checkResult);
});
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
@@ -197,7 +182,7 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
map.get(attr)?.setDoc?.();
});
-type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
+type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
type attrfuncs = [attrname, { checkResult: () => boolean; toggle: () => any }];
ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) {
@@ -221,6 +206,8 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
const attrs:attrfuncs[] = [
['dictation', { checkResult: () => textView?._recordingDictation ? true:false,
toggle: () => textView && runInAction(() => (textView._recordingDictation = !textView._recordingDictation)) }],
+ ['elide', { checkResult: () => false,
+ toggle: () => editorView ? RichTextMenu.Instance?.elideSelection(): 0}],
['noAutoLink',{ checkResult: () => (editorView ? RichTextMenu.Instance.noAutoLink : false),
toggle: () => editorView && RichTextMenu.Instance?.toggleNoAutoLinkAnchor()}],
['bold', { checkResult: () => (editorView ? RichTextMenu.Instance.bold : (Doc.UserDoc().fontWeight === 'bold') ? true:false),
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 715b23fb6..62f630c6c 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -159,6 +159,37 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
moveDoc2 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true);
remDoc1 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_1'), true);
remDoc2 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_2'), true);
+
+ /**
+ * Tests for whether a comparison box slot (ie, before or after) has renderable text content
+ * @param whichSlot field key for start or end slot
+ * @returns a JSX layout string if a text field is found, othwerise undefined
+ */
+ testForTextFields = (whichSlot: string) => {
+ const slotHasText = Doc.Get(this.dataDoc, whichSlot, true) instanceof RichTextField || typeof Doc.Get(this.dataDoc, whichSlot, true) === 'string';
+ const subjectText = RTFCast(this.Document[this.fieldKey])?.Text.trim();
+ const altText = RTFCast(this.Document[this.fieldKey + '_alternate'])?.Text.trim();
+ const layoutTemplateString =
+ slotHasText ? FormattedTextBox.LayoutString(whichSlot):
+ whichSlot.endsWith('1') ? (subjectText !== undefined ? FormattedTextBox.LayoutString(this.fieldKey) : undefined) :
+ altText !== undefined ? FormattedTextBox.LayoutString(this.fieldKey + '_alternate'): undefined; // prettier-ignore
+
+ // A bit hacky to try out the concept of using GPT to fill in flashcards
+ // If the second slot doesn't have anything in it, but the fieldKey slot has text (e.g., this.text is a string)
+ // and the fieldKey + "_alternate" has text that includes a GPT query (indicated by (( && )) ) that is parameterized (optionally) by the fieldKey text (this) or other metadata (this.).
+ // eg., this.text_alternate is
+ // "((Provide a one sentence definition for (this) that doesn't use any word in (this.excludeWords) ))"
+ // where (this) is replaced by the text in the fieldKey slot abd this.excludeWords is repalced by the conetnts of the excludeWords field
+ // The GPT call will put the "answer" in the second slot of the comparison (eg., text_2)
+ if (whichSlot.endsWith('2') && !layoutTemplateString?.includes(whichSlot)) {
+ var queryText = altText.replace('(this)', subjectText); // TODO: this should be done in KeyValueBox.setField but it doesn't know about the fieldKey ...
+ if (queryText && queryText.match(/\(\(.*\)\)/)) {
+ KeyValueBox.SetField(this.Document, whichSlot, ':=' + queryText, false); // make the second slot be a computed field on the data doc that calls ChatGpt
+ }
+ }
+ return layoutTemplateString;
+ };
+
_closeRef = React.createRef();
render() {
const clearButton = (which: string) => {
@@ -176,48 +207,24 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
/**
* Display the Docs in the before/after fields of the comparison. This also supports a GPT flash card use case
* where if there are no Docs in the slots, but the main fieldKey contains text, then
- * @param which
+ * @param whichSlot
* @returns
*/
- const displayDoc = (which: string) => {
- const whichDoc = DocCast(this.dataDoc[which]);
+ const displayDoc = (whichSlot: string) => {
+ const whichDoc = DocCast(this.dataDoc[whichSlot]);
const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc);
- const subjectText = RTFCast(this.Document[this.fieldKey])?.Text.trim();
- // if there is no Doc in the first comparison slot, but the comparison box's fieldKey slot has a RichTextField, then render a text box to show the contents of the document's field key slot
- // of if there is no Doc in the second comparison slot, but the second slot has a RichTextField, then render a text box to show the contents of the document's field key slot
- const layoutTemplateString = !targetDoc
- ? which.endsWith('1') && subjectText !== undefined
- ? FormattedTextBox.LayoutString(this.fieldKey)
- : which.endsWith('2') && (this.Document[which] instanceof RichTextField || typeof this.Document[which] === 'string')
- ? FormattedTextBox.LayoutString(which)
- : undefined
- : undefined;
-
- // A bit hacky to try out the concept of using GPT to fill in flashcards -- this whole process should probably be packaged into a script to be more generic.
- // If the second slot doesn't have anything in it, but the fieldKey slot has text (e.g., this.text is a string)
- // and the fieldKey + "_alternate" has text, then treat the _alternate's text as a GPT query (indicated by (( && )) ) that is parameterized (optionally)
- // by the field references in the text (eg., this.text_alternate is
- // "((Provide a one sentence definition for (this) that doesn't use any word in (this.excludeWords) ))"
- // where (this) is replaced by the text in the fieldKey slot abd this.excludeWords is repalced by the conetnts of the excludeWords field
- // A GPT call will put the "answer" in the second slot of the comparison (eg., text_2)
- if (which.endsWith('2') && !layoutTemplateString && !targetDoc) {
- var queryText = RTFCast(this.Document[this.fieldKey + '_alternate'])
- ?.Text.replace('(this)', subjectText) // TODO: this should be done in KeyValueBox.setField but it doesn't know about the fieldKey ...
- .trim();
- if (subjectText && queryText.match(/\(\(.*\)\)/)) {
- KeyValueBox.SetField(this.Document, which, ':=' + queryText, false); // make the second slot be a computed field on the data doc that calls ChatGpt
- }
- }
+ const layoutTemplateString = targetDoc ? '' : this.testForTextFields(whichSlot);
return targetDoc || layoutTemplateString ? (
<>
()
hideLinkButton={true}
pointerEvents={this._isAnyChildContentActive ? undefined : returnNone}
/>
- {layoutTemplateString ? null : clearButton(which)}
+ {layoutTemplateString ? null : clearButton(whichSlot)}
> // placeholder image if doc is missing
) : (
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9848f18e0..e9ce98583 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -10,6 +10,7 @@ import { AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fi
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
+import { PrefetchProxy } from '../../../fields/Proxy';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
@@ -19,10 +20,11 @@ import { DocServer } from '../../DocServer';
import { Networking } from '../../Network';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { DocOptions, DocUtils, Docs } from '../../documents/Documents';
+import { DocUtils, Docs } from '../../documents/Documents';
import { DictationManager } from '../../util/DictationManager';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType } from '../../util/DragManager';
+import { MakeTemplate, makeUserTemplateButton } from '../../util/DropConverter';
import { FollowLinkScript } from '../../util/LinkFollower';
import { LinkManager } from '../../util/LinkManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
@@ -36,6 +38,7 @@ import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent, ViewBoxInterface } from '../DocComponent';
import { EditableView } from '../EditableView';
+import { FieldsDropdown } from '../FieldsDropdown';
import { GestureOverlay } from '../GestureOverlay';
import { LightboxView } from '../LightboxView';
import { AudioAnnoState, StyleProp } from '../StyleProvider';
@@ -47,7 +50,6 @@ import { KeyValueBox } from './KeyValueBox';
import { LinkAnchorBox } from './LinkAnchorBox';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { PresEffect, PresEffectDirection } from './trails';
-import { FieldsDropdown } from '../FieldsDropdown';
interface Window {
MediaRecorder: MediaRecorder;
}
@@ -1271,6 +1273,32 @@ export class DocumentView extends DocComponent() {
custom && DocUtils.makeCustomViewClicked(this.Document, Docs.Create.StackingDocument, layout, undefined);
}, 'set custom view');
+ public static setDefaultTemplate(checkResult?: boolean) {
+ if (checkResult) {
+ return Doc.UserDoc().defaultTextLayout;
+ }
+ const view = SelectionManager.Views[0]?._props.renderDepth > 0 ? SelectionManager.Views[0] : undefined;
+ undoable(() => {
+ var tempDoc: Opt;
+ if (view) {
+ if (!view.layoutDoc.isTemplateDoc) {
+ tempDoc = view.Document;
+ MakeTemplate(tempDoc);
+ Doc.AddDocToList(Doc.UserDoc(), 'template_user', tempDoc);
+ Doc.AddDocToList(DocListCast(Doc.MyTools.data)[1], 'data', makeUserTemplateButton(tempDoc));
+ tempDoc && Doc.AddDocToList(Cast(Doc.UserDoc().template_user, Doc, null), 'data', tempDoc);
+ } else {
+ tempDoc = DocCast(view.Document[StrCast(view.Document.layout_fieldKey)]);
+ if (!tempDoc) {
+ tempDoc = view.Document;
+ while (tempDoc && !Doc.isTemplateDoc(tempDoc)) tempDoc = DocCast(tempDoc.proto);
+ }
+ }
+ }
+ Doc.UserDoc().defaultTextLayout = tempDoc ? new PrefetchProxy(tempDoc) : undefined;
+ }, 'set default template')();
+ }
+
/**
* This switches between the current view of a Doc and a specified alternate layout view.
* The current view of the Doc is stored in the layout_default field so that it can be restored.
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 4ecaaa283..5b47dd91d 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -55,6 +55,7 @@ export interface FieldViewSharedProps {
ignoreAutoHeight?: boolean;
disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
hideClickBehaviors?: boolean; // whether to suppress menu item options for changing click behaviors
+ ignoreUsePath?: boolean; // ignore the usePath field for selecting the fieldKey (eg., on text docs)
CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
containerViewPath?: () => DocumentView[];
fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index f02ad7300..57ae92359 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -5,8 +5,7 @@ import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
-import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { SelectionManager } from '../../../util/SelectionManager';
@@ -61,23 +60,12 @@ export class FontIconBox extends ViewBoxBaseComponent() {
}
@observable noTooltip = false;
- showTemplate = (): void => {
- const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
- dragFactory && this._props.addDocTab(dragFactory, OpenWhere.addRight);
- };
- dragAsTemplate = (): void => {
- this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
- };
- useAsPrototype = (): void => {
- this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)');
- };
+ showTemplate = (dragFactory: Doc) => this._props.addDocTab(dragFactory, OpenWhere.addRight);
specificContextMenu = (): void => {
- if (!Doc.noviceMode && Cast(this.layoutDoc.dragFactory, Doc, null)) {
- const cm = ContextMenu.Instance;
- cm.addItem({ description: 'Show Template', event: this.showTemplate, icon: 'tag' });
- cm.addItem({ description: 'Use as Render Template', event: this.dragAsTemplate, icon: 'tag' });
- cm.addItem({ description: 'Use as Prototype', event: this.useAsPrototype, icon: 'tag' });
+ const dragFactory = DocCast(this.layoutDoc.dragFactory);
+ if (!Doc.noviceMode && dragFactory) {
+ ContextMenu.Instance.addItem({ description: 'Show Template', event: () => this.showTemplate(dragFactory), icon: 'tag' });
}
};
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 2bcad806f..d85432631 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -115,7 +115,7 @@ export class KeyValueBox extends ObservableReactComponent {
field === undefined && (field = res.result);
}
}
- if (!key) return field;
+ if (!key) return false;
if (Field.IsField(field, true) && (key !== 'proto' || field !== target)) {
target[key] = field;
return true;
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index d59489a78..f9e8ce4f3 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -125,7 +125,7 @@ export class KeyValuePair extends ObservableReactComponent {
pinToPres: returnZero,
}}
GetValue={() => Field.toKeyValueString(this._props.doc, this._props.keyName)}
- SetValue={(value: string) => (KeyValueBox.SetField(this._props.doc, this._props.keyName, value) ? true : false)}
+ SetValue={(value: string) => KeyValueBox.SetField(this._props.doc, this._props.keyName, value)}
/>
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 62cb460c2..6b66d829c 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
@@ -8,12 +8,12 @@ import { Doc, DocListCast, Field } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { Cast } from '../../../../fields/Types';
+import { Cast, DocCast } from '../../../../fields/Types';
import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { CollectionViewType } from '../../../documents/DocumentTypes';
import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
+import { undoable, undoBatch } from '../../../util/UndoManager';
import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableCell';
import { FilterPanel } from '../../FilterPanel';
@@ -21,6 +21,7 @@ import { ObservableReactComponent } from '../../ObservableReactComponent';
import { OpenWhere } from '../DocumentView';
import './DashFieldView.scss';
import { FormattedTextBox } from './FormattedTextBox';
+import { DocData } from '../../../../fields/DocSymbols';
export class DashFieldView {
dom: HTMLDivElement; // container for label and value
@@ -62,6 +63,8 @@ export class DashFieldView {
height={node.attrs.height}
hideKey={node.attrs.hideKey}
editable={node.attrs.editable}
+ expanded={node.attrs.expanded}
+ dataDoc={node.attrs.dataDoc}
tbox={tbox}
/>
);
@@ -89,6 +92,8 @@ interface IDashFieldViewInternal {
width: number;
height: number;
editable: boolean;
+ expanded: boolean;
+ dataDoc: boolean;
node: any;
getPos: any;
unclickable: () => boolean;
@@ -101,18 +106,19 @@ export class DashFieldViewInternal extends ObservableReactComponent();
@observable _dashDoc: Doc | undefined = undefined;
- @observable _expanded = false;
+ @observable _expanded = this._props.expanded;
constructor(props: IDashFieldViewInternal) {
super(props);
makeObservable(this);
this._fieldKey = this._props.fieldKey;
- this._textBoxDoc = this._fieldKey.startsWith('_') ? this._props.tbox.Document : this._props.tbox.dataDoc;
+ this._textBoxDoc = this._props.tbox.Document;
+ const setDoc = (doc: Doc) => (this._dashDoc = this._props.dataDoc ? doc[DocData] : doc);
if (this._props.docId) {
- DocServer.GetRefField(this._props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
+ DocServer.GetRefField(this._props.docId).then(dashDoc => dashDoc instanceof Doc && setDoc(dashDoc));
} else {
- this._dashDoc = this._fieldKey.startsWith('_') ? this._props.tbox.Document : this._props.tbox.dataDoc;
+ setDoc(this._props.tbox.Document);
}
}
@@ -126,7 +132,9 @@ export class DashFieldViewInternal extends ObservableReactComponent 100;
+ isRowActive = () => this._expanded && this._props.editable;
+ finishEdit = action(() => (this._expanded = false));
+ selectedCell = (): [Doc, number] => [this._dashDoc!, 0];
// set the display of the field's value (checkbox for booleans, span of text for strings)
@computed get fieldValueContent() {
@@ -137,18 +145,18 @@ export class DashFieldViewInternal extends ObservableReactComponent this._props.tbox._props.PanelWidth() - 20 : returnZero}
- selectedCell={() => [this._dashDoc!, 0]}
+ maxWidth={this._props.hideKey || this._hideKey ? undefined : this._props.tbox._props.PanelWidth}
+ columnWidth={returnZero}
+ selectedCell={this.selectedCell}
fieldKey={this._fieldKey}
rowHeight={returnZero}
- isRowActive={() => this._expanded && this._props.editable}
+ isRowActive={this.isRowActive}
padding={0}
getFinfo={emptyFunction}
setColumnValues={returnFalse}
allowCRs={true}
oneLine={!this._expanded}
- finishEdit={action(() => (this._expanded = false))}
+ finishEdit={this.finishEdit}
transform={Transform.Identity}
menuTarget={null}
/>
@@ -173,11 +181,21 @@ export class DashFieldViewInternal extends ObservableReactComponent this._dashDoc && (this._dashDoc[this._fieldKey + '_hideKey'] = !this._dashDoc[this._fieldKey + '_hideKey'])),
+ 'hideKey'
+ );
+
+ @computed get _hideKey() {
+ return this._dashDoc && this._dashDoc[this._fieldKey + '_hideKey'];
+ }
+
// 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 => {
DashFieldViewMenu.createFieldView = this.createPivotForField;
+ DashFieldViewMenu.toggleFieldHide = this.toggleFieldHide;
DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16, this._fieldKey);
});
};
@@ -188,6 +206,7 @@ export class DashFieldViewInternal extends ObservableReactComponent ({ value: facet, label: facet }));
@@ -203,9 +222,9 @@ export class DashFieldViewInternal extends ObservableReactComponent
- {this._props.hideKey ? null : (
+ {this._props.hideKey || this._hideKey ? null : (
- {(this._textBoxDoc === this._dashDoc ? '' : this._dashDoc?.title + ':') + this._fieldKey}
+ {(Doc.AreProtosEqual(DocCast(this._textBoxDoc.rootDocument) ?? this._textBoxDoc, DocCast(this._dashDoc?.rootDocument) ?? this._dashDoc) ? '' : this._dashDoc?.title + ':') + this._fieldKey}
)}
{this._props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
@@ -224,6 +243,7 @@ export class DashFieldViewInternal extends ObservableReactComponent {
static Instance: DashFieldViewMenu;
static createFieldView: (e: React.MouseEvent) => void = emptyFunction;
+ static toggleFieldHide: () => void = emptyFunction;
constructor(props: any) {
super(props);
DashFieldViewMenu.Instance = this;
@@ -233,6 +253,10 @@ export class DashFieldViewMenu extends AntimodeMenu {
DashFieldViewMenu.createFieldView(e);
DashFieldViewMenu.Instance.fadeOut(true);
};
+ toggleFieldHide = (e: React.MouseEvent) => {
+ DashFieldViewMenu.toggleFieldHide();
+ DashFieldViewMenu.Instance.fadeOut(true);
+ };
@observable _fieldKey = '';
@@ -252,6 +276,9 @@ export class DashFieldViewMenu extends AntimodeMenu {
+
);
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fb709818c..2b48494f2 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -451,7 +451,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
+ Doc.MyPublishedDocs.filter(term => term.title).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
this._editorView?.dispatch(tr);
}
@@ -958,8 +958,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight),
icon: this.Document._layout_autoHeight ? 'lock' : 'unlock',
});
- optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: });
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
+ const help = cm.findByDescription('Help...');
+ const helpItems = help && 'subitems' in help ? help.subitems : [];
+ helpItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: });
+ !help && cm.addItem({ description: 'Help...', subitems: helpItems, icon: 'eye' });
this._downX = this._downY = Number.NaN;
};
@@ -1239,8 +1242,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this._editorView?.dispatch(tx.insertText(incomingValue.str)));
+ } else {
+ selectAll(this._editorView.state, tx => this._editorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
}
}
}
@@ -1339,15 +1342,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
if (pdfAnchor instanceof Doc) {
const dashField = view.state.schema.nodes.paragraph.create({}, [
- view.state.schema.nodes.dashField.create({ fieldKey: 'text', docId: pdfAnchor[Id], hideKey: true, editable: false }, undefined, [
+ view.state.schema.nodes.dashField.create({ fieldKey: 'text', docId: pdfAnchor[Id], hideKey: true, editable: false, expanded: true }, undefined, [
view.state.schema.marks.linkAnchor.create({
allAnchors: [{ href: `/doc/${this.Document[Id]}`, title: this.Document.title, anchorId: `${this.Document[Id]}` }],
- title: `from: ${DocCast(pdfAnchor.embedContainer).title}`,
+ title: StrCast(pdfAnchor.title),
noPreview: true,
- docref: false,
+ docref: true,
+ fontSize: '8px',
}),
- view.state.schema.marks.pFontSize.create({ fontSize: '8px' }),
- view.state.schema.marks.em.create({}),
]),
]);
@@ -1926,11 +1928,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
);
}
- cycleAlternateText = () => {
- if (this.layoutDoc._layout_enableAltContentUI) {
- const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
- this.layoutDoc[`_${this._props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
- }
+ cycleAlternateText = (skipHover?: boolean) => {
+ this.layoutDoc._layout_enableAltContentUI = true;
+ const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
+ this.layoutDoc[`_${this._props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' && !skipHover ? 'alternate:hover' : undefined;
};
@computed get overlayAlternateIcon() {
const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
@@ -1965,7 +1966,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
});
}
+ elideSelection = () => {
+ const state = this.view?.state;
+ if (!state) return;
+ if (state.selection.empty) return false;
+ const mark = state.schema.marks.summarize.create();
+ const tr = state.tr;
+ tr.addMark(state.selection.from, state.selection.to, mark);
+ const content = tr.selection.content();
+ const newNode = state.schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() });
+ this.view?.dispatch?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
+ return true;
+ };
+
toggleNoAutoLinkAnchor = () => {
if (this.view) {
const mark = this.view.state.schema.mark(this.view.state.schema.marks.noAutoLinkAnchor);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index c798ae4b3..b97141e92 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -267,7 +267,7 @@ export class RichTextRules {
// toggle alternate text UI %/
new InputRule(new RegExp(/%\//), (state, match, start, end) => {
- setTimeout(this.TextBox.cycleAlternateText);
+ setTimeout(() => this.TextBox.cycleAlternateText(true));
return state.tr.deleteRange(start, end);
}),
@@ -283,40 +283,26 @@ export class RichTextRules {
: tr;
}),
- new InputRule(new RegExp(/(^|[^=])(\(\(.*\)\))/), (state, match, start, end) => {
- var count = 0; // ignore first return value which will be the notation that chat is pending a result
- KeyValueBox.SetField(this.Document, '', match[2], false, (gptval: FieldResult) => {
- count && this.TextBox.EditorView?.dispatch(this.TextBox.EditorView!.state.tr.insertText(' ' + (gptval as string)));
- count++;
- });
- return null;
- }),
-
- // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
- // [[ : ]]
- // [[:docTitle]] => hyperlink
- // [[fieldKey]] => show field
- // [[fieldKey{:,=:}=value]] => show field and also set its value
- // [[fieldKey:docTitle]] => show field of doc
- new InputRule(
- new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)((=:|:)?=)([a-z,A-Z_@\?+\-*/\ 0-9\(\)]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/),
- (state, match, start, end) => {
- const fieldKey = match[1];
- const assign = match[2] === '=' ? '' : match[2];
- const value = match[4];
- const docTitle = match[5]?.replace(':', '');
+ // create a hyperlink to a titled document
+ // @()
+ new InputRule(new RegExp(/(^|\s)@\(([a-zA-Z_@:\.\? \-0-9]+)\)/), (state, match, start, end) => {
+ const docTitle = match[2];
+ const prefixLength = '@('.length;
+ if (docTitle) {
const linkToDoc = (target: Doc) => {
- const rstate = this.TextBox.EditorView?.state;
- const selection = rstate?.selection.$from.pos;
- if (rstate) {
- this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
+ const editor = this.TextBox.EditorView;
+ const selection = editor?.state?.selection.$from.pos;
+ if (editor) {
+ const estate = editor.state;
+ editor.dispatch(estate.tr.setSelection(new TextSelection(estate.doc.resolve(start), estate.doc.resolve(end - prefixLength))));
}
DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { link_relationship: 'portal to:portal from' });
- const fstate = this.TextBox.EditorView?.state;
- if (fstate && selection) {
- this.TextBox.EditorView?.dispatch(fstate.tr.setSelection(new TextSelection(fstate.doc.resolve(selection))));
+ const teditor = this.TextBox.EditorView;
+ if (teditor && selection) {
+ const tstate = teditor.state;
+ teditor.dispatch(tstate.tr.setSelection(new TextSelection(tstate.doc.resolve(selection))));
}
};
const getTitledDoc = (docTitle: string) => {
@@ -326,32 +312,57 @@ export class RichTextRules {
const titledDoc = DocServer.FindDocByTitle(docTitle);
return titledDoc ? Doc.BestEmbedding(titledDoc) : titledDoc;
};
- if (!fieldKey) {
- if (docTitle) {
- const target = getTitledDoc(docTitle);
- if (target) {
- setTimeout(() => linkToDoc(target));
- return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
- }
- }
- return state.tr;
+ const target = getTitledDoc(docTitle);
+ if (target) {
+ setTimeout(() => linkToDoc(target));
+ return state.tr.insertText(' ').deleteRange(start, start + prefixLength);
}
+ }
+ return state.tr;
+ }),
+
+ // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
+ // [@{this,doctitle,}.fieldKey{:,=,:=,=:=}value]
+ // [@{this,doctitle,}.fieldKey]
+ new InputRule(
+ new RegExp(/\[(@|@this\.|@[a-zA-Z_\? \-0-9]+\.)([a-zA-Z_\?\-0-9]+)((:|=|:=|=:=)([a-zA-Z,_@\?\+\-\*\/\ 0-9\(\)]*))?\]/),
+ (state, match, start, end) => {
+ const docTitle = match[1].substring(1).replace(/\.$/, '');
+ const fieldKey = match[2];
+ 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);
+ };
// 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(',');
const strs = values.some(v => !v.match(/^[-]?[0-9.]$/));
this.Document[DocData][fieldKey] = strs ? new List(values) : new List(values.map(v => Number(v)));
} else if (value) {
- KeyValueBox.SetField(this.Document, fieldKey, assign + value, Doc.IsDataProto(this.Document) ? true : undefined, assign ? undefined:
- (gptval: FieldResult) => this.Document[DocData][fieldKey] = gptval as string ); // prettier-ignore
+ KeyValueBox.SetField(this.Document, fieldKey, assign + value, Doc.IsDataProto(this.Document) ? true : undefined, assign.includes(":=") ? undefined:
+ (gptval: FieldResult) => (dataDoc ? this.Document[DocData]:this.Document)[fieldKey] = gptval as string ); // prettier-ignore
}
- const target = getTitledDoc(docTitle);
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false });
+ const target = docTitle ? getTitledDoc(docTitle) : undefined;
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false, dataDoc });
return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
},
{ inCode: true }
),
+ new InputRule(new RegExp(/(^|[^=])(\(\(.*\)\))/), (state, match, start, end) => {
+ var count = 0; // ignore first return value which will be the notation that chat is pending a result
+ KeyValueBox.SetField(this.Document, '', match[2], false, (gptval: FieldResult) => {
+ count && this.TextBox.EditorView?.dispatch(this.TextBox.EditorView!.state.tr.insertText(' ' + (gptval as string)));
+ count++;
+ });
+ return null;
+ }),
+
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
// wiki:title
new InputRule(new RegExp(/wiki:([a-zA-Z_@:\.\?\-0-9]+ )$/), (state, match, start, end) => {
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index a141ef041..b68acc8f8 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -74,6 +74,7 @@ export const marks: { [index: string]: MarkSpec } = {
allAnchors: { default: [] as { href: string; title: string; anchorId: string }[] },
title: { default: null },
noPreview: { default: false },
+ fontSize: { default: null },
docref: { default: false }, // flags whether the linked text comes from a document within Dash. If so, an attribution label is appended after the text
},
inclusive: false,
@@ -93,14 +94,16 @@ export const marks: { [index: string]: MarkSpec } = {
const anchorids = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.anchorId : item.anchorId), '');
return node.attrs.docref && node.attrs.title
? [
- 'div',
+ 'a',
['span', 0],
[
'span',
{
...node.attrs,
class: 'prosemirror-attribution',
+ 'data-targethrefs': targethrefs,
href: node.attrs.allAnchors[0].href,
+ style: `font-size: ${node.attrs.fontSize}`,
},
node.attrs.title,
],
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index c9115be90..905146ee2 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -265,6 +265,8 @@ export const nodes: { [index: string]: NodeSpec } = {
docId: { default: '' },
hideKey: { default: false },
editable: { default: true },
+ expanded: { default: null },
+ 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 daae32e9f..4b40d11b9 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -445,6 +445,12 @@ export namespace Doc {
export function GetT(doc: Doc, key: string, ctor: ToConstructor, ignoreProto: boolean = false): FieldResult {
return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult;
}
+ export function isTemplateDoc(doc: Doc) {
+ return GetT(doc, 'isTemplateDoc', 'boolean', true);
+ }
+ export function isTemplateForField(doc: Doc) {
+ return GetT(doc, 'isTemplateForField', 'string', true);
+ }
export function IsDataProto(doc: Doc) {
return GetT(doc, 'isDataDoc', 'boolean', true);
}
@@ -642,7 +648,7 @@ export namespace Doc {
cloneLinks: boolean,
cloneTemplates: boolean
): Promise {
- if (Doc.IsBaseProto(doc) || ((Doc.Get(doc, 'isTemplateDoc', true) || Doc.Get(doc, 'isTemplateForField', true)) && !cloneTemplates)) {
+ if (Doc.IsBaseProto(doc) || ((Doc.isTemplateDoc(doc) || Doc.isTemplateForField(doc)) && !cloneTemplates)) {
return doc;
}
if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
@@ -735,7 +741,7 @@ export namespace Doc {
const docAtKey = DocCast(clone[key]);
if (docAtKey && !Doc.IsSystem(docAtKey)) {
if (!Array.from(cloneMap.values()).includes(docAtKey)) {
- clone[key] = !cloneTemplates && (Doc.Get(docAtKey, 'isTemplateDoc', true) || Doc.Get(docAtKey, 'isTemplateForField', true)) ? docAtKey : cloneMap.get(docAtKey[Id]);
+ clone[key] = !cloneTemplates && (Doc.isTemplateDoc(docAtKey) || Doc.isTemplateForField(docAtKey)) ? docAtKey : cloneMap.get(docAtKey[Id]);
} else {
repairClone(docAtKey, cloneMap, cloneTemplates, visited);
}
@@ -857,7 +863,7 @@ export namespace Doc {
// of the original layout while allowing for individual layout properties to be overridden in the expanded layout.
export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc) {
// nothing to do if the layout isn't a template or we don't have a target that's different than the template
- if (!targetDoc || templateLayoutDoc === targetDoc || (!templateLayoutDoc.isTemplateForField && !templateLayoutDoc.isTemplateDoc)) {
+ if (!targetDoc || templateLayoutDoc === targetDoc || (!Doc.isTemplateForField(templateLayoutDoc) && !Doc.isTemplateDoc(templateLayoutDoc))) {
return templateLayoutDoc;
}
@@ -874,7 +880,7 @@ export namespace Doc {
expandedTemplateLayout = undefined;
_pendingMap.add(targetDoc[Id] + expandedLayoutFieldKey);
} else if (expandedTemplateLayout === undefined && !_pendingMap.has(targetDoc[Id] + expandedLayoutFieldKey)) {
- if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument ?? Doc.GetProto(targetDoc))) {
+ if (templateLayoutDoc.resolvedDataDoc === targetDoc[DocData]) {
expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
} else {
templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = DocCast(templateLayoutDoc.proto, templateLayoutDoc)); // if the template has already been applied (ie, a nested template), then use the template's prototype
@@ -910,8 +916,9 @@ export namespace Doc {
console.log('Warning: GetLayoutDataDocPair childDoc not defined');
return { layout: childDoc, data: childDoc };
}
- const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField) ? undefined : containerDataDoc;
- return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc), data: resolvedDataDoc };
+ const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!Doc.isTemplateDoc(childDoc) && !Doc.isTemplateForField(childDoc)) ? undefined : containerDataDoc;
+ const templateRoot = DocCast(containerDoc?.rootDocument);
+ return { layout: Doc.expandTemplateLayout(childDoc, templateRoot), data: resolvedDataDoc };
}
export function FindReferences(infield: Doc | List, references: Set, system: boolean | undefined) {
@@ -1035,20 +1042,13 @@ export namespace Doc {
// (ie, the 'data' doc), and then creates another delegate of that (ie, the 'layout' doc).
// This is appropriate if you're trying to create a document that behaves like all
// regularly created documents (e.g, text docs, pdfs, etc which all have data/layout docs)
- export function MakeDelegateWithProto(doc: Doc, id?: string, title?: string): Doc {
- const delegateProto = new Doc();
- delegateProto[Initializing] = true;
- delegateProto.proto = doc;
- delegateProto.author = Doc.CurrentUserEmail;
- delegateProto.isDataDoc = true;
- title && (delegateProto.title = title);
- const delegate = new Doc(id, true);
- delegate[Initializing] = true;
- delegate.proto = delegateProto;
- delegate.author = Doc.CurrentUserEmail;
- delegate[Initializing] = false;
- delegateProto[Initializing] = false;
- return delegate;
+ export function MakeDelegateWithProto(doc: Doc, id?: string, title?: string) {
+ const ndoc = Doc.ApplyTemplate(doc);
+ if (ndoc) {
+ Doc.GetProto(ndoc).isDataDoc = true;
+ ndoc && (Doc.GetProto(ndoc).proto = doc);
+ }
+ return ndoc;
}
let _applyCount: number = 0;
@@ -1671,7 +1671,7 @@ ScriptingGlobals.add(function getEmbedding(doc: any) {
return Doc.MakeEmbedding(doc);
});
ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) {
- return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto);
+ return doc.isTemplateDoc ? Doc.MakeDelegateWithProto(doc) : Doc.MakeCopy(doc, copyProto);
});
ScriptingGlobals.add(function copyField(field: any) {
return Field.Copy(field);
diff --git a/src/fields/util.ts b/src/fields/util.ts
index b73520999..c2ec3f13a 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -286,6 +286,10 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
// target should be either a Doc or ListImpl. receiver should be a Proxy Or List.
//
export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
+ if (!in_prop) {
+ console.log('WARNING: trying to set an empty property. This should be fixed. ');
+ return false;
+ }
let prop = in_prop;
const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : GetPropAcl(target, prop);
if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin) return true;
--
cgit v1.2.3-70-g09d2