From b05c44385cc81889ee94a4bbb034237142438bdc Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Wed, 21 Feb 2024 17:44:25 -0500 Subject: branch --- src/.DS_Store | Bin 10244 -> 10244 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'src') diff --git a/src/.DS_Store b/src/.DS_Store index c363efb13..9b5118d8a 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ -- cgit v1.2.3-70-g09d2 From 7f4f5d5d4779a3ff8efd5a64e2cef9f6758e29ae Mon Sep 17 00:00:00 2001 From: "A.J. Shulman" Date: Wed, 21 Feb 2024 17:45:49 -0500 Subject: changed logo for Notes and PPT Slide in tool menu --- src/client/util/CurrentUserUtils.ts | 4 ++-- src/client/views/MainView.tsx | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 714e33d25..b0890b674 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -273,7 +273,7 @@ export class CurrentUserUtils { { 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 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: "folder", 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)}, @@ -284,7 +284,7 @@ export class CurrentUserUtils { { toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", 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: "file", 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 diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index b6cb845a6..fe0e1d1ab 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -288,6 +288,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faFolderOpen, fa.faFolderPlus, fa.faFolderClosed, + fa.faBook, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, @@ -488,6 +489,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faUpload, fa.faBorderAll, fa.faBraille, + fa.faPersonChalkboard, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, -- cgit v1.2.3-70-g09d2 From 3735fd5e8c0f5315d73a28ef59dcff4bc6158c88 Mon Sep 17 00:00:00 2001 From: Zachary Zhang Date: Wed, 21 Feb 2024 17:45:56 -0500 Subject: replace button icon with circle instead of bold --- src/.DS_Store | Bin 10244 -> 10244 bytes src/client/util/CurrentUserUtils.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/.DS_Store b/src/.DS_Store index c363efb13..bb607fbc3 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 714e33d25..d61a40ffa 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -281,7 +281,7 @@ export class CurrentUserUtils { { 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: "bolt", 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: "file", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}}, -- cgit v1.2.3-70-g09d2 From 2279f029ce19c9f7f886a6966ea5f23be9468890 Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Wed, 21 Feb 2024 20:00:11 -0500 Subject: presentation trail tab icon changed --- src/client/views/MainView.tsx | 1 + src/fields/Doc.ts | 2 +- src/mobile/MobileInterface.tsx | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index b6cb845a6..496cb0bdd 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -452,6 +452,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faAngleDown, fa.faPlayCircle, fa.faClock, + fa.faRoute, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 56d50846a..17cc5ce97 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1432,7 +1432,7 @@ export namespace Doc { case DocumentType.WEBCAM: return 'video'; case DocumentType.AUDIO: return 'microphone'; case DocumentType.BUTTON: return 'bolt'; - case DocumentType.PRES: return 'tv'; + case DocumentType.PRES: return 'route'; case DocumentType.SCRIPTING: return 'terminal'; case DocumentType.IMPORT: return 'cloud-upload-alt'; case DocumentType.VID: return 'video'; diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index c31e73b42..dc4af8303 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -92,6 +92,7 @@ import { faTrash, faTrashAlt, faTree, + faRoute, faTv, faUndoAlt, faVideo, -- cgit v1.2.3-70-g09d2 From 43770615603c9d0f0aa19a3a676822737157c161 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 6 Mar 2024 09:44:38 -0500 Subject: enabled copy and paste to work with template docs (and not copy template). Fixed tempaltes of collections to allow new nodes to be added/removed. --- src/client/views/DocComponent.tsx | 8 ++--- src/client/views/TemplateMenu.tsx | 9 ++++-- src/client/views/nodes/ScriptingBox.tsx | 32 ++++++++++---------- src/fields/Doc.ts | 52 ++++++++++++++++++++++----------- 4 files changed, 60 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 2a527eca1..a25a8f77a 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -182,7 +182,7 @@ export function ViewBoxAnnotatableComponent

() { const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin); // docs.forEach(doc => doc.annotationOn === this.Document && Doc.SetInPlace(doc, 'annotationOn', undefined, true)); - const targetDataDoc = this.dataDoc; + const targetDataDoc = this.Document[DocData]; // this.dataDoc; // we want to write to the template, not the actual data doc const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); const toRemove = value.filter(v => docs.includes(v)); @@ -228,7 +228,7 @@ export function ViewBoxAnnotatableComponent

() { if (this._props.filterAddDocument?.(docs) === false || docs.find(doc => Doc.AreProtosEqual(doc, this.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.Document))) { return false; } - const targetDataDoc = this.dataDoc; + const targetDataDoc = this.Document[DocData]; // this.dataDoc; // we want to write to the template, not the actual data doc const effectiveAcl = GetEffectiveAcl(targetDataDoc); if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) { @@ -245,9 +245,9 @@ export function ViewBoxAnnotatableComponent

() { inheritParentAcls(targetDataDoc, doc, true); }); - const annoDocs = Doc.Get(targetDataDoc, annotationKey ?? this.annotationKey, true) as List; // get the dataDoc directly ... when using templates there may be some default items already there, but we can't change them. maybe we should copy them over, though... + const annoDocs = Doc.Get(targetDataDoc, annotationKey ?? this.annotationKey, true) as List; // get the dataDoc directly ... when using templates there may be some default items already there, but we can't change them, so we copy them below (should really be some kind of inheritance since the template contents could change) if (annoDocs instanceof List) annoDocs.push(...added.filter(add => !annoDocs.includes(add))); - else targetDataDoc[annotationKey ?? this.annotationKey] = new List(added); + else targetDataDoc[annotationKey ?? this.annotationKey] = new List([...added, ...(annoDocs === undefined ? DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]) : [])]); targetDataDoc[(annotationKey ?? this.annotationKey) + '_modificationDate'] = new DateField(); } } diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index f7729dbc7..5d9abb460 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -3,7 +3,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast } from '../../fields/Doc'; import { ScriptField } from '../../fields/ScriptField'; -import { Cast, StrCast } from '../../fields/Types'; +import { Cast, DocCast, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils'; import { Docs, DocUtils } from '../documents/Documents'; @@ -81,8 +81,11 @@ export class TemplateMenu extends React.Component { componentDidMount() { !this._addedKeys && (this._addedKeys = new ObservableSet()); [...Array.from(Object.keys(this.props.docViews[0].Document[DocData])), ...Array.from(Object.keys(this.props.docViews[0].Document))] - .filter(key => key.startsWith('layout_') && key !== 'layout_fieldKey') - .map(key => runInAction(() => this._addedKeys.add(key.replace('layout_', '')))); + .filter(key => key.startsWith('layout_') && ( + StrCast(this.props.docViews[0].Document[key]).startsWith("<") || + DocCast(this.props.docViews[0].Document[key])?.isTemplateDoc + )) + .map(key => runInAction(() => this._addedKeys.add(key.replace('layout_', '')))); // prettier-ignore } return100 = () => 300; diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 89650889d..d9d0dbe3e 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -130,7 +130,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent() } }) ); - observer.observe(document.getElementsByClassName('scriptingBox')[0]); + observer.observe(document.getElementsByClassName('scriptingBox-outerDiv')[0]); } @action @@ -811,22 +811,20 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent() render() { TraceMobx(); return ( -

-
this._props.isSelected() && e.stopPropagation()}> - {this._paramSuggestion ? ( -
- {' '} - {this._scriptSuggestedParams}{' '} -
- ) : null} - {!this._applied && !this._function ? this.renderScriptingInputs : null} - {this._applied && !this._function ? this.renderParamsInputs() : null} - {!this._applied && this._function ? this.renderFunctionInputs() : null} - - {!this._applied && !this._function ? this.renderScriptingTools() : null} - {this._applied && !this._function ? this.renderTools('Run', () => this.onRun()) : null} - {!this._applied && this._function ? this.renderTools('Create Function', () => this.onCreate()) : null} -
+
this._props.isSelected() && e.stopPropagation()}> + {this._paramSuggestion ? ( +
+ {' '} + {this._scriptSuggestedParams}{' '} +
+ ) : null} + {!this._applied && !this._function ? this.renderScriptingInputs : null} + {this._applied && !this._function ? this.renderParamsInputs() : null} + {!this._applied && this._function ? this.renderFunctionInputs() : null} + + {!this._applied && !this._function ? this.renderScriptingTools() : null} + {this._applied && !this._function ? this.renderTools('Run', () => this.onRun()) : null} + {!this._applied && this._function ? this.renderTools('Create Function', () => this.onCreate()) : null}
); } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index b1bdd50a7..1df9d80a1 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -614,8 +614,19 @@ export namespace Doc { // this lists out all the tag ids that can be in a RichTextField that might contain document ids. // if a document is cloned, we need to make sure to clone all of these referenced documents as well; export const DocsInTextFieldIds = ['audioId', 'textId', 'anchorId', 'docId']; - export async function makeClone(doc: Doc, cloneMap: Map, linkMap: Map, rtfs: { copy: Doc; key: string; field: RichTextField }[], exclusions: string[], pruneDocs: Doc[], cloneLinks: boolean): Promise { - if (Doc.IsBaseProto(doc)) return doc; + export async function makeClone( + doc: Doc, + cloneMap: Map, + linkMap: Map, + rtfs: { copy: Doc; key: string; field: RichTextField }[], + exclusions: string[], + pruneDocs: Doc[], + cloneLinks: boolean, + cloneTemplates: boolean + ): Promise { + if (Doc.IsBaseProto(doc) || ((Doc.Get(doc, 'isTemplateDoc', true) || Doc.Get(doc, 'isTemplateForField', true)) && !cloneTemplates)) { + return doc; + } if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!; const copy = new Doc(undefined, true); cloneMap.set(doc[Id], copy); @@ -630,7 +641,7 @@ export namespace Doc { const list = await Cast(doc[key], listSpec(Doc)); const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc); if (docs !== undefined && docs.length) { - const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks))); + const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates))); assignKey(new List(clones)); } else { assignKey(ObjectField.MakeCopy(field)); @@ -645,7 +656,7 @@ export namespace Doc { ); const results = docids && (await DocServer.GetRefFields(docids)); const docs = results && Array.from(Object.keys(results)).map(key => DocCast(results[key])); - docs?.map(doc => doc && Doc.makeClone(doc, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks)); + docs?.map(doc => doc && Doc.makeClone(doc, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)); rtfs.push({ copy, key, field }); } } @@ -657,8 +668,14 @@ export namespace Doc { } else if (docAtKey instanceof Doc) { if (pruneDocs.includes(docAtKey)) { // prune doc and do nothing - } else if (!Doc.IsSystem(docAtKey) && (key.startsWith('layout') || ['embedContainer', 'annotationOn', 'proto'].includes(key) || ((key === 'link_anchor_1' || key === 'link_anchor_2') && doc.author === Doc.CurrentUserEmail))) { - assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks)); + } else if ( + !Doc.IsSystem(docAtKey) && + (key.includes('layout[') || + key.startsWith('layout') || // + ['embedContainer', 'annotationOn', 'proto'].includes(key) || + (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === Doc.CurrentUserEmail)) + ) { + assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)); } else { assignKey(docAtKey); } @@ -681,16 +698,17 @@ export namespace Doc { ((cloneMap.has(DocCast(link.link_anchor_1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id])) && (cloneMap.has(DocCast(link.link_anchor_2)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_2)?.annotationOn)?.[Id]))) ) { - linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks)); + linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)); } }); - Doc.SetInPlace(copy, 'title', '>:' + doc.title, true); + if (Doc.Get(copy, 'title', true)) copy.title = '>:' + doc.title; + // Doc.SetInPlace(copy, 'title', '>:' + doc.title, true); copy.cloneOf = doc; cloneMap.set(doc[Id], copy); return copy; } - export function repairClone(clone: Doc, cloneMap: Map, visited: Set) { + export function repairClone(clone: Doc, cloneMap: Map, cloneTemplates: boolean, visited: Set) { if (visited.has(clone)) return; visited.add(clone); Object.keys(clone) @@ -699,22 +717,22 @@ export namespace Doc { const docAtKey = DocCast(clone[key]); if (docAtKey && !Doc.IsSystem(docAtKey)) { if (!Array.from(cloneMap.values()).includes(docAtKey)) { - clone[key] = cloneMap.get(docAtKey[Id]); + clone[key] = !cloneTemplates && (Doc.Get(docAtKey, 'isTemplateDoc', true) || Doc.Get(docAtKey, 'isTemplateForField', true)) ? docAtKey : cloneMap.get(docAtKey[Id]); } else { - repairClone(docAtKey, cloneMap, visited); + repairClone(docAtKey, cloneMap, cloneTemplates, visited); } } }); } - export function MakeClones(docs: Doc[], cloneLinks: boolean) { + export function MakeClones(docs: Doc[], cloneLinks: boolean, cloneTemplates: boolean) { const cloneMap = new Map(); - return docs.map(doc => Doc.MakeClone(doc, cloneLinks, cloneMap)); + return docs.map(doc => Doc.MakeClone(doc, cloneLinks, cloneTemplates, cloneMap)); } - export async function MakeClone(doc: Doc, cloneLinks = true, cloneMap: Map = new Map()) { + export async function MakeClone(doc: Doc, cloneLinks = true, cloneTemplates = true, cloneMap: Map = new Map()) { const linkMap = new Map(); const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = []; - const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks); + const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks, cloneTemplates); const repaired = new Set(); const linkedDocs = Array.from(linkMap.values()); linkedDocs.map((link: Doc) => LinkManager.Instance.addLink(link, true)); @@ -732,7 +750,7 @@ export namespace Doc { copy[key] = new RichTextField(field.Data.replace(docidsearch, replacer).replace(re, replacer2), field.Text); }); const clonedDocs = [...Array.from(cloneMap.values()), ...linkedDocs]; - clonedDocs.map(clone => Doc.repairClone(clone, cloneMap, repaired)); + clonedDocs.map(clone => Doc.repairClone(clone, cloneMap, cloneTemplates, repaired)); return { clone: copy, map: cloneMap, linkMap }; } @@ -1395,7 +1413,7 @@ export namespace Doc { const list = Array.from(Object.values(fieldlist)) .map(d => DocCast(d)) .filter(d => d); - const docs = clone ? (await Promise.all(Doc.MakeClones(list, false))).map(res => res.clone) : list; + const docs = clone ? (await Promise.all(Doc.MakeClones(list, false, false))).map(res => res.clone) : list; if (ptx !== undefined && pty !== undefined && newPoint !== undefined) { const firstx = list.length ? NumCast(list[0].x) + ptx - newPoint[0] : 0; const firsty = list.length ? NumCast(list[0].y) + pty - newPoint[1] : 0; -- cgit v1.2.3-70-g09d2 From bd9170eaa21a2fbccd0912f50d40cefaf0bbe47f Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 6 Mar 2024 12:28:24 -0500 Subject: cleaned up some dropActionType strings. fixed stackingview scrolling when always active. fixed schemaView keys dropdown menu to be a popup. cleaned up display of key Finfo's --- src/client/documents/Documents.ts | 46 +++---- src/client/views/DashboardView.tsx | 9 +- src/client/views/TemplateMenu.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 15 ++- src/client/views/collections/CollectionSubView.tsx | 5 +- src/client/views/collections/TreeView.tsx | 6 +- .../collectionSchema/CollectionSchemaView.scss | 149 +++++++++++---------- .../collectionSchema/CollectionSchemaView.tsx | 68 ++++++---- src/client/views/nodes/LinkAnchorBox.tsx | 4 +- .../views/nodes/RecordingBox/RecordingBox.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- 11 files changed, 170 insertions(+), 142 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 031560886..c9b5cb915 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -70,14 +70,14 @@ class EmptyBox { } export enum FInfoFieldType { - string, - boolean, - number, - Doc, - enumeration, - date, - list, - rtf, + string = 'string', + boolean = 'boolean', + number = 'number', + Doc = 'Doc', + enumeration = 'enum', + date = 'date', + list = 'list', + rtf = 'rich text', } export class FInfo { description: string = ''; @@ -198,20 +198,20 @@ type DATEt = DateInfo | number; type DTYPEt = DTypeInfo | string; export class DocumentOptions { // coordinate and dimensions depending on view - x?: NUMt = new NumInfo('x coordinate of document in a freeform view', false); - y?: NUMt = new NumInfo('y coordinate of document in a freeform view', false); + x?: NUMt = new NumInfo('horizontal coordinate in freeform view', false); + y?: NUMt = new NumInfo('vertical coordinate in freeform view', false); z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, false, [1, 0]); - overlayX?: NUMt = new NumInfo('x coordinate of document in a overlay view', false); - overlayY?: NUMt = new NumInfo('y coordinate of document in a overlay view', false); - text?: RTFt = new RtfInfo('rich text of a text doc', true); + overlayX?: NUMt = new NumInfo('horizontal coordinate in overlay view', false); + overlayY?: NUMt = new NumInfo('vertical coordinate in overlay view', false); + text?: RTFt = new RtfInfo('plain or rich text', true); _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", false); _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units"); - latitude?: NUMt = new NumInfo('latitude coordinate for map views', false); - longitude?: NUMt = new NumInfo('longitude coordinate for map views', false); + latitude?: NUMt = new NumInfo('latitude coordinate', false); + longitude?: NUMt = new NumInfo('longitude coordinate', false); routeCoordinates?: STRt = new StrInfo("stores a route's/direction's coordinates (stringified version)"); // for a route document, this stores the route's coordinates - markerType?: STRt = new StrInfo('Defines the marker type for a pushpin document'); - markerColor?: STRt = new StrInfo('Defines the marker color for a pushpin document'); - map?: STRt = new StrInfo('text location of map'); + markerType?: STRt = new StrInfo('marker type for a pushpin document'); + markerColor?: STRt = new StrInfo('marker color for a pushpin document'); + map?: STRt = new StrInfo('map location name'); map_type?: STRt = new StrInfo('type of map view', false); map_zoom?: NUMt = new NumInfo('zoom of a map view', false); map_pitch?: NUMt = new NumInfo('pitch of a map view', false); @@ -221,11 +221,11 @@ export class DocumentOptions { date_range?: STRt = new StrInfo('date range for calendar', false); wikiData?: STRt = new StrInfo('WikiData ID related to map location'); - description?: STRt = new StrInfo('A description of the document'); - _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)', false); - _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden', false); - _width?: NUMt = new NumInfo('displayed width of a document'); - _height?: NUMt = new NumInfo('displayed height of document'); + description?: STRt = new StrInfo('description of document'); + _timecodeToShow?: NUMt = new NumInfo('media timecode when document should appear (e.g., when an annotation shows up as a video plays)', false); + _timecodeToHide?: NUMt = new NumInfo('media timecode when document should disappear', false); + _width?: NUMt = new NumInfo("width of document in container's coordinates"); + _height?: NUMt = new NumInfo("height of document in container's coordiantes"); data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)', false); data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)', false); linearBtnWidth?: NUMt = new NumInfo('unexpanded width of a linear menu button (button "width" changes when it expands)', false); diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 472d419fc..439d250de 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -28,6 +28,7 @@ import { Colors } from './global/globalEnums'; import { MainViewModal } from './MainViewModal'; import { ButtonType } from './nodes/FontIconBox/FontIconBox'; import { ObservableReactComponent } from './ObservableReactComponent'; +import { dropActionType } from '../util/DragManager'; enum DashboardGroup { MyDashboards, @@ -403,7 +404,7 @@ export class DashboardView extends ObservableReactComponent<{}> { _layout_fitWidth: true, _gridGap: 5, _forceActive: true, - childDragAction: 'embed', + childDragAction: dropActionType.embed, treeView_TruncateTitleWidth: 150, ignoreClick: true, contextMenuIcons: new List(['plus']), @@ -411,7 +412,7 @@ export class DashboardView extends ObservableReactComponent<{}> { _lockedPosition: true, layout_boxShadow: '0 0', childDontRegisterViews: true, - dropAction: 'same', + dropAction: dropActionType.same, isSystem: true, layout_explainer: 'All of the calendars that you have created will appear here.', }; @@ -450,7 +451,7 @@ export class DashboardView extends ObservableReactComponent<{}> { _layout_fitWidth: true, _gridGap: 5, _forceActive: true, - childDragAction: 'embed', + childDragAction: dropActionType.embed, treeView_TruncateTitleWidth: 150, ignoreClick: true, layout_headerButton: myTrailsBtn, @@ -459,7 +460,7 @@ export class DashboardView extends ObservableReactComponent<{}> { _lockedPosition: true, layout_boxShadow: '0 0', childDontRegisterViews: true, - dropAction: 'same', + dropAction: dropActionType.same, isSystem: true, layout_explainer: 'All of the trails that you have created will appear here.', }; diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 5d9abb460..00195f7d7 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -105,7 +105,7 @@ export class TemplateMenu extends React.Component { const addedTypes = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data); const templateMenu: Array = []; this.props.templates?.forEach((checked, template) => templateMenu.push()); - templateMenu.push(); + templateMenu.push(); addedTypes.concat(noteTypes).map(template => (template.treeView_Checked = this.templateIsUsed(firstDoc, template))); this._addedKeys && Array.from(this._addedKeys) diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index dc391631a..ea1caf58f 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -233,10 +233,6 @@ export class CollectionStackingView extends CollectionSubView boolean): boolean => { return this._props.removeDocument?.(doc) && addDocument?.(doc) ? true : false; }; - createRef = (ele: HTMLDivElement | null) => { - this._masonryGridRef = ele; - this.createDashEventsTarget(ele!); //so the whole grid is the drop target? - }; onChildClickHandler = () => this._props.childClickScript || ScriptCast(this.Document.onChildClick); @computed get onChildDoubleClickHandler() { @@ -679,6 +675,8 @@ export class CollectionStackingView extends CollectionSubView this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0)))); + onPassiveWheel = (e: WheelEvent) => e.stopPropagation(); + _oldWheel: any; render() { TraceMobx(); const editableViewProps = { @@ -699,7 +697,14 @@ export class CollectionStackingView extends CollectionSubView
{ + this._masonryGridRef = ele; + this.createDashEventsTarget(ele); //so the whole grid is the drop target? + this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel); + this._oldWheel = ele; + // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling + ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); + }} style={{ overflowY: this.isContentActive() ? 'auto' : 'hidden', background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor), diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index fdbd1cc90..59a695a3d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -219,12 +219,13 @@ export function CollectionSubView(moreProps?: X) { const targetDocments = DocListCast(this.dataDoc[this._props.fieldKey]); const someMoved = !dropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag)); if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => (targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop)); - if ((!dropAction || dropAction === 'inSame' || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) { + if ((!dropAction || dropAction === dropActionType.inPlace || dropAction === dropActionType.same || dropAction === dropActionType.move || someMoved) && docDragData.moveDocument) { const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d); const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d); if (movedDocs.length) { const canAdd = - (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.Document)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.Document)); + (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.Document)) && + (dropAction !== dropActionType.inPlace || docDragData.draggedDocuments.every(d => d.embedContainer === this.Document)); const moved = docDragData.moveDocument(movedDocs, this.Document, canAdd ? this.addDocument : returnFalse); added = canAdd || moved ? moved : undefined; } else if (addedDocs.length) { diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 4f2d8b9c0..285a789e6 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -449,10 +449,10 @@ export class TreeView extends ObservableReactComponent { const addDoc = inside ? this.localAdd : parentAddDoc; const canAdd = !StrCast((inside ? this.Document : this._props.treeViewParent)?.treeView_FreezeChildren).includes('add') || forceAdd; - if (canAdd && (dropAction !== 'inSame' || droppedDocuments.every(d => d.embedContainer === this._props.parentTreeView?.Document))) { - const move = (!dropAction || canEmbed || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same' || dropAction === 'inSame') && moveDocument; + if (canAdd && (dropAction !== dropActionType.inPlace || droppedDocuments.every(d => d.embedContainer === this._props.parentTreeView?.Document))) { + const move = (!dropAction || canEmbed || dropAction === dropActionType.proto || dropAction === dropActionType.move || dropAction === dropActionType.same || dropAction === dropActionType.inPlace) && moveDocument; this._props.parentTreeView instanceof TreeView && (this._props.parentTreeView.dropping = true); - const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false); + const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === dropActionType.proto ? addDoc(d) : false) : addDoc(d)) || added, false); this._props.parentTreeView instanceof TreeView && (this._props.parentTreeView.dropping = false); return res; } diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss index fed4e89cf..ac0bd2378 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss @@ -38,91 +38,97 @@ background: yellow; } } + } - .schema-column-menu, - .schema-filter-menu { - background: $light-gray; - position: absolute; - min-width: 200px; - max-width: 400px; - display: flex; - flex-direction: column; - align-items: flex-start; - z-index: 1; - - .schema-key-search-input { - width: calc(100% - 20px); - margin: 10px; - } + .schema-preview-divider { + height: 100%; + background: black; + cursor: ew-resize; + } +} - .schema-search-result { - cursor: pointer; - padding: 5px 10px; - width: 100%; +.schema-column-menu, +.schema-filter-menu { + background: $light-gray; + position: relative; + min-width: 200px; + max-width: 400px; + display: flex; + flex-direction: column; + align-items: flex-start; + z-index: 1; - &:hover { - background-color: $medium-gray; - } + .schema-key-search-input { + width: calc(100% - 20px); + margin: 10px; + } - .schema-search-result-type, - .schema-search-result-desc { - color: $dark-gray; - font-size: $body-text; - } - } + .schema-search-result { + cursor: pointer; + padding: 5px 10px; + width: 100%; - .schema-key-search, - .schema-new-key-options { - width: 100%; - display: flex; - flex-direction: column; - align-items: flex-start; - } + &:hover { + background-color: $medium-gray; + } + .schema-search-result-type { + font-family: 'Courier New', Courier, monospace; + } - .schema-new-key-options { - margin: 10px; - .schema-key-warning { - color: red; - font-weight: normal; - align-self: center; - } - } + .schema-search-result-type, + .schema-search-result-desc { + color: $dark-gray; + font-size: $body-text; + } + .schema-search-result-desc { + font-style: italic; + } + } - .schema-key-list { - width: 100%; - max-height: 300px; - overflow-y: auto; - } + .schema-key-search, + .schema-new-key-options { + width: 100%; + display: flex; + flex-direction: column; + align-items: flex-start; + } - .schema-key-type-option { - margin: 2px 0px; + .schema-new-key-options { + margin: 10px; + .schema-key-warning { + color: red; + font-weight: normal; + align-self: center; + } + } - input { - margin-right: 5px; - } - } + .schema-key-list { + width: 100%; + max-height: 300px; + overflow-y: auto; + } - .schema-key-default-val { - margin: 5px 0; - } + .schema-key-type-option { + margin: 2px 0px; - .schema-column-menu-button { - cursor: pointer; - padding: 2px 5px; - background: $medium-blue; - border-radius: 9999px; - color: $white; - width: fit-content; - margin: 5px; - align-self: center; - } + input { + margin-right: 5px; } } - .schema-preview-divider { - height: 100%; - background: black; - cursor: ew-resize; + .schema-key-default-val { + margin: 5px 0; + } + + .schema-column-menu-button { + cursor: pointer; + padding: 2px 5px; + background: $medium-blue; + border-radius: 9999px; + color: $white; + width: fit-content; + margin: 5px; + align-self: center; } } @@ -133,6 +139,7 @@ .row-menu { display: flex; justify-content: center; + cursor: pointer; } } diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index eee3836d2..12f0ad5e9 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, makeObservable, observable, ObservableMap, observe } from 'mobx'; +import { action, computed, makeObservable, observable, ObservableMap, observe, trace } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Field, NumListCast, Opt, StrListCast } from '../../../../fields/Doc'; @@ -25,6 +25,8 @@ import { CollectionSubView } from '../CollectionSubView'; import './CollectionSchemaView.scss'; import { SchemaColumnHeader } from './SchemaColumnHeader'; import { SchemaRowBox } from './SchemaRowBox'; +import { Popup, PopupTrigger, Type } from 'browndash-components'; +import { SettingsManager } from '../../../util/SettingsManager'; const { default: { SCHEMA_NEW_NODE_HEIGHT } } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore export enum ColumnType { @@ -161,7 +163,7 @@ export class CollectionSchemaView extends CollectionSubView() { (change as any).added.forEach((doc: Doc) => // for each document added Doc.GetAllPrototypes(doc.value as Doc).forEach(proto => // for all of its prototypes (and itself) Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them - !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo(key, key === 'author')))))); + !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo("-no description-", key === 'author')))))); break; case 'update': //let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list } @@ -681,6 +683,7 @@ export class CollectionSchemaView extends CollectionSubView() { } else { this.setKey(this._menuValue, this._newFieldDefault); } + this._columnMenuIndex = undefined; })}> done
@@ -688,12 +691,12 @@ export class CollectionSchemaView extends CollectionSubView() { ); } - onPassiveWheel = (e: WheelEvent) => { + onKeysPassiveWheel = (e: WheelEvent) => { // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) - if (!this._oldWheel.scrollTop && e.deltaY <= 0) e.preventDefault(); + if (!this._oldKeysWheel.scrollTop && e.deltaY <= 0) e.preventDefault(); e.stopPropagation(); }; - _oldWheel: any; + _oldKeysWheel: any; @computed get keysDropdown() { return (
@@ -708,9 +711,9 @@ export class CollectionSchemaView extends CollectionSubView() {
{ - this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel); - this._oldWheel = r; - r?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); + this._oldKeysWheel?.removeEventListener('wheel', this.onKeysPassiveWheel); + this._oldKeysWheel = r; + r?.addEventListener('wheel', this.onKeysPassiveWheel, { passive: false }); }}> {this._menuKeys.map(key => (

- {key} - {this.fieldInfos.get(key)!.fieldType ? ', ' : ''} + {key} + {this.fieldInfos.get(key)!.fieldType ? ':' : ''} {this.fieldInfos.get(key)!.fieldType} +   {this.fieldInfos.get(key)!.description}

-

{this.fieldInfos.get(key)!.description}

))}
@@ -740,16 +743,17 @@ export class CollectionSchemaView extends CollectionSubView() { const x = this._columnMenuIndex! == -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth); return (
- e.stopPropagation()} /> + e.stopPropagation()} /> + {this._makeNewField ? this.newFieldMenu : this.keysDropdown} +
+ ); + } + get renderKeysMenu() { + console.log('RNDERMENUT:' + this._columnMenuIndex); + return ( +
+ e.stopPropagation()} /> {this._makeNewField ? this.newFieldMenu : this.keysDropdown} -
{ - e.stopPropagation(); - this.closeColumnMenu(); - })}> - cancel -
); } @@ -833,6 +837,8 @@ export class CollectionSchemaView extends CollectionSubView() { isContentActive = () => this._props.isSelected() || this._props.isContentActive(); screenToLocal = () => this.ScreenToLocalBoxXf().translate(-this.tableWidth, 0); previewWidthFunc = () => this.previewWidth; + onPassiveWheel = (e: WheelEvent) => e.stopPropagation(); + _oldWheel: any; render() { return (
this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)}> @@ -841,15 +847,23 @@ export class CollectionSchemaView extends CollectionSubView() { className="schema-table" style={{ width: `calc(100% - ${this.previewWidth}px)` }} onWheel={e => this._props.isContentActive() && e.stopPropagation()} - ref={r => { - // prevent wheel events from passively propagating up through containers - r?.addEventListener('wheel', (e: WheelEvent) => {}, { passive: false }); + ref={ele => { + // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling + this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel); + (this._oldWheel = ele)?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); }}>
-
(this._columnMenuIndex === -1 ? this.closeColumnMenu() : this.openColumnMenu(-1, true))}> - -
+ this.openColumnMenu(-1, true)} icon="plus" />} + trigger={PopupTrigger.CLICK} + type={Type.TERT} + isOpen={this._columnMenuIndex !== -1 ? false : undefined} + popup={this.renderKeysMenu} + />
{this.columnKeys.map((key, index) => ( ))}
- {this._columnMenuIndex !== undefined && this.renderColumnMenu} + {this._columnMenuIndex !== undefined && this._columnMenuIndex !== -1 && this.renderColumnMenu} {this._filterColumnIndex !== undefined && this.renderFilterMenu} (this._tableContentRef = ref)} /> {this.layoutDoc.chromeHidden ? null : ( diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 00e1f04c5..0a4325d8c 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -5,7 +5,7 @@ import { Utils, emptyFunction, setupMoveUpEvents } from '../../../Utils'; import { Doc } from '../../../fields/Doc'; import { NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { DragManager } from '../../util/DragManager'; +import { DragManager, dropActionType } from '../../util/DragManager'; import { LinkFollower } from '../../util/LinkFollower'; import { SelectionManager } from '../../util/SelectionManager'; import { ViewBoxBaseComponent } from '../DocComponent'; @@ -54,7 +54,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent() { const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY)); if (separation > 100) { const dragData = new DragManager.DocumentDragData([this.Document]); - dragData.dropAction = 'embed'; + dragData.dropAction = dropActionType.embed; dragData.dropPropertiesToRemove = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick']; DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]); return true; diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index f6d94ce05..e38a42b29 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -11,7 +11,7 @@ import { Upload } from '../../../../server/SharedMediaTypes'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; -import { DragManager } from '../../../util/DragManager'; +import { DragManager, dropActionType } from '../../../util/DragManager'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { Presentation } from '../../../util/TrackMovements'; import { undoBatch } from '../../../util/UndoManager'; @@ -251,7 +251,7 @@ ScriptingGlobals.add(function resumeWorkspaceReplaying(value: Doc, _readOnly_: b ScriptingGlobals.add(function startRecordingDrag(value: { doc: Doc | string; e: React.PointerEvent }) { if (DocCast(value.doc)) { - DragManager.StartDocumentDrag([value.e.target as HTMLElement], new DragManager.DocumentDragData([DocCast(value.doc)], 'embed'), value.e.clientX, value.e.clientY); + DragManager.StartDocumentDrag([value.e.target as HTMLElement], new DragManager.DocumentDragData([DocCast(value.doc)], dropActionType.embed), value.e.clientX, value.e.clientY); value.e.preventDefault(); return true; } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 2d5239f06..4a1a51558 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -30,7 +30,7 @@ import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType } from '../../../documents/DocumentTypes'; import { DictationManager } from '../../../util/DictationManager'; import { DocumentManager } from '../../../util/DocumentManager'; -import { DragManager } from '../../../util/DragManager'; +import { DragManager, dropActionType } from '../../../util/DragManager'; import { MakeTemplate } from '../../../util/DropConverter'; import { LinkManager } from '../../../util/LinkManager'; import { RTFMarkup } from '../../../util/RTFMarkup'; @@ -624,7 +624,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent 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') 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 ecf3bf771156b3935d8c87f1fbaf5c4e294c6aee Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 6 Mar 2024 13:36:45 -0500 Subject: fixed backgroundColors to be set on dataDoc in infoUI --- .../collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index 8628ca3c3..43b877705 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -34,7 +34,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent (this._currState = val)); } // prettier-ignore componentWillUnmount(): void { - this._props.Freeform.layoutDoc.backgroundColor = this._originalbackground; + this._props.Freeform.dataDoc.backgroundColor = this._originalbackground; } setCurrState = (state: infoState) => { @@ -45,9 +45,9 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent { - this._originalbackground = StrCast(this._props.Freeform.layoutDoc.backgroundColor); + this._originalbackground = StrCast(this._props.Freeform.dataDoc.backgroundColor); // state entry functions - const setBackground = (colour: string) => () => (this._props.Freeform.layoutDoc.backgroundColor = colour); + const setBackground = (colour: string) => () => (this._props.Freeform.dataDoc.backgroundColor = colour); const setOpacity = (opacity: number) => () => (this._props.Freeform.layoutDoc.opacity = opacity); // arc transition trigger conditions const firstDoc = () => (this._props.Freeform.childDocs.length ? this._props.Freeform.childDocs[0] : undefined); -- 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') 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 7164179264d057986bfd5f303c61248f1c189406 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 8 Mar 2024 16:17:02 -0500 Subject: cleaned up MakeTemplate api and currentUserUtils templates for clicks etc. --- src/client/documents/Documents.ts | 9 ++++ src/client/util/CurrentUserUtils.ts | 35 ++++++++-------- src/client/util/DropConverter.ts | 48 ++++++++++++++-------- src/client/views/collections/TreeView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- 5 files changed, 60 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b7d8222b9..e6969d1f3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1024,6 +1024,15 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List(), options); } + /** + * Creates a Doc to edit a script and write the compiled script into the specified field. + * Typically, this would be used to create a template that can then be applied to some other Doc + * in order to customize a behavior, such as onClick. + * @param script + * @param options + * @param fieldKey the field that the compiled script is written into. + * @returns the Scripting Doc + */ export function ScriptingDocument(script: Opt | null, options: DocumentOptions = {}, fieldKey?: string) { return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined }); } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index cfba003fc..564a46514 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -105,9 +105,12 @@ export class CurrentUserUtils { { opts: { title: "onCheckedClick"}, script: "console.log(heading, checked, containingTreeView)"}, ]; const reqdClickList = reqdTempOpts.map(opts => { + const title = opts.opts.title?.toString(); const allOpts = {...reqdClickOpts, ...opts.opts}; - const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === opts.opts.title): undefined; - return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}), allOpts), true, opts.opts.title?.toString()); + const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === title): undefined; + const script = ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}); + const scriptDoc = Docs.Create.ScriptingDocument(script, allOpts, title) + return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(scriptDoc); }); const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, isSystem: true}; @@ -118,13 +121,13 @@ export class CurrentUserUtils { static setupNoteTemplates(doc: Doc, field="template_notes") { const tempNotes = DocCast(doc[field]); const reqdTempOpts:DocumentOptions[] = [ - { noteType: "Postit", backgroundColor: "yellow", icon: "sticky-note"}, - { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" }, - { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }]; + { 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 reqdOpts = {...opts, isSystem:true, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true}; - const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined; - return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note"); + 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)); }); const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true }; @@ -149,16 +152,16 @@ export class CurrentUserUtils { const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => { - const iconFieldName = "icon" + (type ? "_" + type : ""); - const curIcon = DocCast(templateIconsDoc[iconFieldName]); + const title = "icon" + (type ? "_" + type : ""); + const curIcon = DocCast(templateIconsDoc[title]); let creator = labelBox; switch (opts.iconTemplate) { case DocumentType.IMG : creator = imageBox; break; case DocumentType.FONTICON: creator = fontBox; break; } - const allopts = {isSystem: true, onClickScriptDisable: "never", ...opts}; + const allopts = {isSystem: true, onClickScriptDisable: "never", ...opts, title}; return DocUtils.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ? - DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts, templateField), true, iconFieldName, templateField))), + DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[title] = MakeTemplate(creator(allopts, templateField)))), {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", }); }; const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({ @@ -211,7 +214,7 @@ export class CurrentUserUtils { }; const headerBtnHgt = 10; const headerTemplate = (opts:DocumentOptions) => { - const header = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "text", + const header = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "Untitled Header", layout: "" + ` ` + @@ -224,7 +227,7 @@ export class CurrentUserUtils { // " " + // " " + // "
"; - MakeTemplate(Doc.GetProto(header), true, "Untitled Header"); + MakeTemplate(Doc.GetProto(header)); return header; } const slideView = (opts:DocumentOptions) => { @@ -232,9 +235,9 @@ export class CurrentUserUtils { [ Docs.Create.MulticolumnDocument([], { title: "hero", _height: 200, isSystem: true }), Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true, _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) }) - ], opts); + ], {...opts, title: "Untitled Slide View"}); - MakeTemplate(Doc.GetProto(slide), true, "Untitled Slide View"); + MakeTemplate(Doc.GetProto(slide)); return slide; } const plotlyApi = () => { diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index ba981145d..3df3e36c6 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -12,43 +12,55 @@ import { ButtonType, FontIconBox } from '../views/nodes/FontIconBox/FontIconBox' import { DragManager } from './DragManager'; import { ScriptingGlobals } from './ScriptingGlobals'; -export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt = undefined, templateField: string = '') { - if (templateField) doc[DocData].title = templateField; /// the title determines which field is being templated - doc.isTemplateDoc = makeTemplate(doc, first, rename); +/** + * Converts a Doc to a render template that can be applied to other Docs to customize how they render while + * still using the other Doc as the backing data store (ie, dataDoc). During rendering, if a layout Doc is provided + * with 'isTemplateDoc' set, then the layout Doc is treated as a template for the rendered Doc. The template Doc is + * "expanded" to create an template instance for the rendered Doc. + * + * + * @param doc the doc to convert to a template + * @returns 'doc' + */ +export function MakeTemplate(doc: Doc) { + doc.isTemplateDoc = makeTemplate(doc, true); return doc; } -// -// converts 'doc' into a template that can be used to render other documents. -// the title of doc is used to determine which field is being templated, so -// passing a value for 'rename' allows the doc to be given a meangingful name -// after it has been converted to -function makeTemplate(doc: Doc, first: boolean = true, rename: Opt = undefined): boolean { + +/** + * 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, + * not by whatever fieldKey they used in their layout. + * @param doc + * @param first whether this is the topmost root of the recursive template + * @returns whether a template was successfully created + */ +function makeTemplate(doc: Doc, first: boolean = true): boolean { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; if (layoutDoc.layout instanceof Doc) { - // its already a template - return true; + return true; // its already a template } const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)![0]; const fieldKey = layout.replace("fieldKey={'", '').replace(/'}$/, ''); const docs = DocListCast(layoutDoc[fieldKey]); - let any = false; + let isTemplate = false; docs.forEach(d => { if (!StrCast(d.title).startsWith('-')) { - any = Doc.MakeMetadataFieldTemplate(d, layoutDoc[DocData]) || any; + isTemplate = Doc.MakeMetadataFieldTemplate(d, layoutDoc[DocData]) || isTemplate; } else if (d.type === DocumentType.COL || d.data instanceof RichTextField) { - any = makeTemplate(d, false) || any; + isTemplate = makeTemplate(d, false) || isTemplate; } }); if (first && !docs.length) { // bcz: feels hacky : if the root level document has items, it's not a field template - any = Doc.MakeMetadataFieldTemplate(doc, layoutDoc[DocData], true) || any; + isTemplate = Doc.MakeMetadataFieldTemplate(doc, layoutDoc[DocData], true) || isTemplate; } else if (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] instanceof ImageField) { if (!StrCast(layoutDoc.title).startsWith('-')) { - any = Doc.MakeMetadataFieldTemplate(layoutDoc, layoutDoc[DocData], true); + isTemplate = Doc.MakeMetadataFieldTemplate(layoutDoc, layoutDoc[DocData], true); } } - rename && (doc.title = rename); - return any; + return isTemplate; } export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data?.draggedDocuments.map((doc, i) => { diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 285a789e6..0c0e49411 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -631,7 +631,7 @@ export class TreeView extends ObservableReactComponent { } return (
- {!docs?.length || this._props.AddToMap /* hack to identify pres box trees */ ? null : ( + {!docs?.length || this.treeView.outlineMode || this._props.AddToMap /* hack to identify pres box trees */ ? null : (
{ if (!this.layoutDoc.isTemplateDoc) { - const title = StrCast(this.Document.title); - this.Document.title = 'text'; - MakeTemplate(this.Document, true, title); + MakeTemplate(this.Document); } Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.Document); Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', this.Document); -- cgit v1.2.3-70-g09d2 From f1ed1cff137c06afc4d4db8a8778f7827404889b Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 9 Mar 2024 19:05:30 -0500 Subject: fixed up default text that uses a template to process an initial carriage return properly. fixed text with inherited templates to be able to show fields with a default dashField value from template that can be overidden on instance. --- src/client/documents/Documents.ts | 11 +++--- src/client/views/MarqueeAnnotator.tsx | 4 +-- src/client/views/PreviewCursor.tsx | 2 +- src/client/views/collections/TreeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 36 +++++++++++-------- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- .../collectionSchema/SchemaTableCell.tsx | 2 +- src/client/views/nodes/MapBox/MapBox.tsx | 4 +-- .../views/nodes/MapboxMapBox/MapboxContainer.tsx | 4 +-- .../views/nodes/formattedText/FormattedTextBox.tsx | 40 ++++++++++------------ 10 files changed, 55 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e6969d1f3..7a3b965fe 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -3,7 +3,7 @@ import { action, reaction, runInAction } from 'mobx'; import { basename } from 'path'; import { DateField } from '../../fields/DateField'; import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc'; -import { Initializing } from '../../fields/DocSymbols'; +import { DocData, Initializing } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { HtmlField } from '../../fields/HtmlField'; import { InkField, PointData } from '../../fields/InkField'; @@ -1981,10 +1981,8 @@ export namespace DocUtils { } } - export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, backgroundColor?: string) { + export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) { const tbox = Docs.Create.TextDocument('', { - _xMargin: noMargins ? 0 : undefined, - _yMargin: noMargins ? 0 : undefined, annotationOn, backgroundColor, _width: width || 200, @@ -1997,11 +1995,14 @@ export namespace DocUtils { _layout_enableAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards), title, }); + const template = Doc.UserDoc().defaultTextLayout; if (template instanceof Doc) { + // if a default text template is specified tbox._width = NumCast(template._width); tbox.layout_fieldKey = 'layout_' + StrCast(template.title); - Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template; + Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template; // set the text doc's layout to render with the text template + tbox[DocData].proto = template; // and also set the text doc to inherit from the template (this allows the template to specify default field values) } return tbox; } diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index a4303c3aa..f59042b04 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -88,7 +88,7 @@ export class MarqueeAnnotator extends ObservableReactComponent this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color const targetCreator = (annotationOn: Doc | undefined) => { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow'); + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); FormattedTextBox.SetSelectOnLoad(target); return target; }; diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 456b753b4..a94c18295 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -85,7 +85,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { } else { FormattedTextBox.PasteOnLoad = e; if (e.clipboardData.getData('dash/pdfAnchor')) e.preventDefault(); - UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined, undefined)), 'paste'); + UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined)), 'paste'); } } //pasting in images diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 0c0e49411..c6bbcb0a5 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -884,7 +884,7 @@ export class TreeView extends ObservableReactComponent { // prettier-ignore switch (property.split(':')[0]) { case StyleProp.Opacity: return this.treeView.outlineMode ? undefined : 1; - case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor)); + case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : undefined;//StrCast(doc._backgroundColor, StrCast(doc.backgroundColor)); case StyleProp.Highlighting: if (this.treeView.outlineMode) return undefined; case StyleProp.BoxShadow: return undefined; case StyleProp.DocContents: diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 4eb946939..9500b918a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -30,7 +30,7 @@ import { SelectionManager } from '../../../util/SelectionManager'; import { freeformScrollMode } from '../../../util/SettingsManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; -import { undoBatch, UndoManager } from '../../../util/UndoManager'; +import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; import { Timeline } from '../../animationtimeline/Timeline'; import { ContextMenu } from '../../ContextMenu'; import { GestureOverlay } from '../../GestureOverlay'; @@ -1146,23 +1146,29 @@ export class CollectionFreeFormView extends CollectionSubView this._props.isContentActive(); - @undoBatch + /** + * Create a new text note of the same style as the one being typed into. + * If the text doc is be part of a larger templated doc, the new Doc will be a copy of the templated Doc + * + * @param fieldProps render props for the text doc being typed into + * @param below whether to place the new text Doc below or to the right of the one being typed into. + * @returns whether the new text doc was created and added successfully + */ + createTextDocCopy = undoable((fieldProps: FieldViewProps, below: boolean) => { + const textDoc = DocCast(fieldProps.Document.rootDocument, fieldProps.Document); + const newDoc = Doc.MakeCopy(textDoc, true); + newDoc[DocData][Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style + newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10); + newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0); + FormattedTextBox.SetSelectOnLoad(newDoc); + FormattedTextBox.DontSelectInitialText = true; + return this.addDocument?.(newDoc); + }, 'copied text note'); + onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) { e.stopPropagation?.(); - const below = !e.altKey && e.key !== 'Tab'; - const layout_fieldKey = StrCast(fieldProps.fieldKey); - const newDoc = Doc.MakeCopy(fieldProps.Document, true); - const dataField = fieldProps.Document[Doc.LayoutFieldKey(newDoc)]; - newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List([]) : undefined; - if (below) newDoc.y = NumCast(fieldProps.Document.y) + NumCast(fieldProps.Document._height) + 10; - else newDoc.x = NumCast(fieldProps.Document.x) + NumCast(fieldProps.Document._width) + 10; - if (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) { - newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey]; - } - newDoc[DocData].text = undefined; - FormattedTextBox.SetSelectOnLoad(newDoc); - return this.addDocument?.(newDoc); + return this.createTextDocCopy(fieldProps, !e.altKey && e.key !== 'Tab'); } }; @computed get childPointerEvents() { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index e22b83307..b913e05ad 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -178,7 +178,7 @@ export class MarqueeView extends ObservableReactComponent() implem }); const targetCreator = (annotationOn: Doc | undefined) => { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow'); + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); FormattedTextBox.SetSelectOnLoad(target); return target; }; @@ -592,7 +592,7 @@ export class MapBox extends ViewBoxAnnotatableComponent() implem /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER const anchor = Docs.Create.ConfigDocument({ title: 'MapAnchor:' + this.Document.title, - text: StrCast(this.selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location', + text: (StrCast(this.selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location') as any, config_latitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.latitude ?? this.dataDoc.latitude), config_longitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.longitude ?? this.dataDoc.longitude), config_map_zoom: NumCast(this.dataDoc.map_zoom), diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx index 8a5bd7ce6..3eb051dbf 100644 --- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx +++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx @@ -232,7 +232,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent }); const targetCreator = (annotationOn: Doc | undefined) => { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow'); + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); FormattedTextBox.SetSelectOnLoad(target); return target; }; @@ -466,7 +466,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER const anchor = Docs.Create.ConfigDocument({ title: 'MapAnchor:' + this.Document.title, - text: StrCast(this.selectedPin?.map) || StrCast(this.Document.map) || 'map location', + text: (StrCast(this.selectedPin?.map) || StrCast(this.Document.map) || 'map location') as any, config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude), config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude), config_map_zoom: NumCast(this.dataDoc.map_zoom), diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 54e3e7b44..1ff7274f8 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -67,7 +67,6 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu'; import { RichTextRules } from './RichTextRules'; import { schema } from './schema_rts'; import { SummaryView } from './SummaryView'; -import Select from 'react-select'; // import * as applyDevTools from 'prosemirror-dev-tools'; @observer export class FormattedTextBox extends ViewBoxAnnotatableComponent() implements ViewBoxInterface { @@ -100,7 +99,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent; private _keymap: any = undefined; @@ -306,7 +304,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn); + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn); FormattedTextBox.SetSelectOnLoad(target); return target; }; @@ -1457,39 +1455,37 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent m.type !== mark.type), mark]; - const tr = this._editorView.state.tr - .setStoredMarks(storedMarks) - .insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size) - .setStoredMarks(storedMarks); + const tr1 = this._editorView.state.tr.setStoredMarks(storedMarks); + const tr2 = selLoadChar === 'Enter' ? tr1.insert(this._editorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this._editorView.state.doc.content.size - 1); + const tr = tr2.setStoredMarks(storedMarks); + this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size)))); } else if (curText && !FormattedTextBox.DontSelectInitialText) { selectAll(this._editorView.state, this._editorView?.dispatch); } } - selectOnLoad && this._editorView!.focus(); + if (selectOnLoad) { + FormattedTextBox.DontSelectInitialText = false; + this._editorView!.focus(); + } if (this._props.isContentActive()) this.prepareForTyping(); - if (this._editorView) { - const tr = this._editorView.state.tr; - const { from, to } = tr.selection; - // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the selection after the document has ben fully instantiated. - if (FormattedTextBox.DontSelectInitialText) setTimeout(() => this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)))), 250); - - if (FormattedTextBox.PasteOnLoad) { - const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor'); - FormattedTextBox.PasteOnLoad = undefined; - pdfAnchorId && this.addPdfReference(pdfAnchorId); - } + if (this._editorView && FormattedTextBox.PasteOnLoad) { + const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor'); + FormattedTextBox.PasteOnLoad = undefined; + pdfAnchorId && this.addPdfReference(pdfAnchorId); } - FormattedTextBox.DontSelectInitialText = false; } // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. -- 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') 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 ff594dffaf804e9597ad43d01d157d00e5f20504 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 10 Mar 2024 10:18:03 -0400 Subject: fixed copying a Doc with an expanded render template to copy the expanded render template in order not to lose instance data (eg, height, width) --- src/fields/Doc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 1433e5ffb..e47bfcbed 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -974,7 +974,7 @@ export namespace Doc { } else if (field instanceof ObjectField) { copy[key] = doc[key] instanceof Doc && key.includes('layout[') - ? undefined // remove expanded template field documents + ? new ProxyField(Doc.MakeCopy(doc[key] as any)) // copy the expanded render template : ObjectField.MakeCopy(field); } else if (field instanceof Promise) { debugger; //This shouldn't happend... -- cgit v1.2.3-70-g09d2