From 2a313f28fcb8675223708b0657de7517a3281095 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 17 Apr 2024 12:27:21 -0400 Subject: restoring eslint - updates not complete yet --- src/fields/ScriptField.ts | 96 ++++++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 38 deletions(-) (limited to 'src/fields/ScriptField.ts') diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 8b51088b2..8a3787768 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -1,17 +1,16 @@ import { action, makeObservable, observable } from 'mobx'; import { computedFn } from 'mobx-utils'; -import { createSimpleSchema, custom, map, object, primitive, PropSchema, serializable, SKIP } from 'serializr'; -import { DocServer } from '../client/DocServer'; -import { CompiledScript, CompileScript, ScriptOptions, Transformer } from '../client/util/Scripting'; -import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals'; -import { autoObject, Deserializable } from '../client/util/SerializationHelper'; +import { PropSchema, SKIP, createSimpleSchema, custom, map, object, primitive, serializable } from 'serializr'; import { numberRange } from '../Utils'; -import { Doc, Field, FieldResult, Opt } from './Doc'; +import { GPTCallType, gptAPICall } from '../client/apis/gpt/GPT'; +import { CompileScript, CompiledScript, ScriptOptions, Transformer } from '../client/util/Scripting'; +import { ScriptingGlobals, scriptingGlobal } from '../client/util/ScriptingGlobals'; +import { Deserializable, autoObject } from '../client/util/SerializationHelper'; +import { Doc, Field, FieldType, FieldResult, Opt } from './Doc'; import { Copy, FieldChanged, Id, ToJavascriptString, ToScriptString, ToString, ToValue } from './FieldSymbols'; import { List } from './List'; -import { ObjectField } from './ObjectField'; +import { ObjGetRefField, ObjectField } from './ObjectField'; import { Cast, StrCast } from './Types'; -import { GPTCallType, gptAPICall } from '../client/apis/gpt/GPT'; function optional(propSchema: PropSchema) { return custom( @@ -44,7 +43,9 @@ const scriptSchema = createSimpleSchema({ originalScript: true, }); -function finalizeScript(script: ScriptField) { +// eslint-disable-next-line no-use-before-define +function finalizeScript(scriptIn: ScriptField) { + const script = scriptIn; const comp = CompileScript(script.script.originalScript, script.script.options); if (!comp.compiled) { throw new Error("Couldn't compile loaded script"); @@ -54,11 +55,13 @@ function finalizeScript(script: ScriptField) { if (!compset.compiled) { throw new Error("Couldn't compile setter script"); } - (script as any).setterscript = compset; + script.setterscript = compset; } return comp; } -async function deserializeScript(script: ScriptField) { +// eslint-disable-next-line no-use-before-define +async function deserializeScript(scriptIn: ScriptField) { + const script = scriptIn; if (script.captures) { const captured: any = {}; (script.script.options as ScriptOptions).capturedVariables = captured; @@ -68,13 +71,16 @@ async function deserializeScript(script: ScriptField) { const val = capture.split(':')[1]; if (val === 'true') captured[key] = true; else if (val === 'false') captured[key] = false; - else if (val.startsWith('ID->')) captured[key] = await DocServer.GetRefField(val.replace('ID->', '')); + else if (val.startsWith('ID->')) captured[key] = await ObjGetRefField(val.replace('ID->', '')); else if (!isNaN(Number(val))) captured[key] = Number(val); else captured[key] = val; }) - ).then(() => ((script as any).script = finalizeScript(script))); + ).then(() => { + script.script = finalizeScript(script); + }); } else { - (script as any).script = ScriptField.GetScriptFieldCache(script.script.originalScript) ?? finalizeScript(script); + // eslint-disable-next-line no-use-before-define + script.script = ScriptField.GetScriptFieldCache(script.script.originalScript) ?? finalizeScript(script); } } @@ -84,9 +90,9 @@ export class ScriptField extends ObjectField { @serializable readonly rawscript: string | undefined; @serializable(object(scriptSchema)) - readonly script: CompiledScript; + script: CompiledScript; @serializable(object(scriptSchema)) - readonly setterscript: CompiledScript | undefined; + setterscript: CompiledScript | undefined; @serializable @observable _cachedResult: FieldResult = undefined; @@ -131,6 +137,7 @@ export class ScriptField extends ObjectField { [ToString]() { return this.script.originalScript; } + // eslint-disable-next-line default-param-last public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Doc | string | number | boolean }, transformer?: Transformer) { return CompileScript(script, { params: { @@ -150,20 +157,23 @@ export class ScriptField extends ObjectField { }); } + // eslint-disable-next-line default-param-last public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) { const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); return compiled.compiled ? new ScriptField(compiled) : undefined; } + // eslint-disable-next-line default-param-last public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) { const compiled = ScriptField.CompileScript(script, params, false, capturedVariables); return compiled.compiled ? new ScriptField(compiled) : undefined; } - public static CallGpt(queryText: string, setVal: (val: FieldResult) => void, target: Doc) { + public static CallGpt(queryTextIn: string, setVal: (val: FieldResult) => void, target: Doc) { + let queryText = queryTextIn; if (typeof queryText === 'string' && setVal) { while (queryText.match(/\(this\.[a-zA-Z_]*\)/)?.length) { const fieldRef = queryText.split('(this.')[1].replace(/\).*/, ''); - queryText = queryText.replace(/\(this\.[a-zA-Z_]*\)/, Field.toString(target[fieldRef] as Field)); + queryText = queryText.replace(/\(this\.[a-zA-Z_]*\)/, Field.toString(target[fieldRef] as FieldType)); } setVal(`Chat Pending: ${queryText}`); gptAPICall(queryText, GPTCallType.COMPLETION).then(result => { @@ -198,24 +208,29 @@ export class ComputedField extends ScriptField { } _lastComputedResult: FieldResult; - value = (doc:Doc) => (this._lastComputedResult = this._cachedResult ?? - computedFn((doc: Doc) => - this.script.compiled && - this.script.run( { - this: doc, - //value: '', - _setCacheResult_: this.setCacheResult, - _last_: this._lastComputedResult, - _readOnly_: true, - }, - console.log - ).result - )(doc) - ); // prettier-ignore + value = (doc: Doc) => { + this._lastComputedResult = + this._cachedResult ?? + computedFn(() => + this.script.compiled && + this.script.run( + { + this: doc, + // value: '', + _setCacheResult_: this.setCacheResult, + _last_: this._lastComputedResult, + _readOnly_: true, + }, + console.log + ).result + )(); // prettier-ignore + return this._lastComputedResult; + }; - [ToValue](doc: Doc) { if (ComputedField.useComputed) return { value: this.value(doc) }; } // prettier-ignore + [ToValue](doc: Doc) { return ComputedField.useComputed ? { value: this.value(doc) } : undefined; } // prettier-ignore [Copy](): ObjectField { return new ComputedField(this.script, this.setterscript, this.rawscript); } // prettier-ignore + // eslint-disable-next-line default-param-last public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) { const compiled = ScriptField.CompileScript(script, params, true, { value: '', ...capturedVariables }); const compiledsetter = setterscript ? ScriptField.CompileScript(setterscript, { ...params, value: 'any' }, false, capturedVariables) : undefined; @@ -225,7 +240,7 @@ export class ComputedField extends ScriptField { public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number, defaultVal: Opt) { if (!doc[`${fieldKey}_indexed`]) { - const flist = new List(numberRange(curTimecode + 1).map(i => undefined) as any as number[]); + const flist = new List(numberRange(curTimecode + 1).map(() => undefined) as any as number[]); flist[curTimecode] = Cast(doc[fieldKey], 'number', null); doc[`${fieldKey}_indexed`] = flist; } @@ -235,7 +250,7 @@ export class ComputedField extends ScriptField { } public static MakeInterpolatedString(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) { if (!doc[`${fieldKey}_`]) { - const flist = new List(numberRange(curTimecode + 1).map(i => undefined) as any as string[]); + const flist = new List(numberRange(curTimecode + 1).map(() => undefined) as any as string[]); flist[curTimecode] = StrCast(doc[fieldKey]); doc[`${fieldKey}_indexed`] = flist; } @@ -244,9 +259,9 @@ export class ComputedField extends ScriptField { return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; } public static MakeInterpolatedDataField(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) { - if (doc[`${fieldKey}`] instanceof List) return; + if (doc[`${fieldKey}`] instanceof List) return undefined; if (!doc[`${fieldKey}_indexed`]) { - const flist = new List(numberRange(curTimecode + 1).map(i => undefined) as any as Field[]); + const flist = new List(numberRange(curTimecode + 1).map(() => undefined) as any as FieldType[]); flist[curTimecode] = Field.Copy(doc[fieldKey]); doc[`${fieldKey}_indexed`] = flist; } @@ -257,11 +272,13 @@ export class ComputedField extends ScriptField { false, {} ); - return (doc[`${fieldKey}`] = getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined); + doc[fieldKey] = getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; + return doc[fieldKey]; } } ScriptingGlobals.add( + // eslint-disable-next-line prefer-arrow-callback function setIndexVal(list: any[], index: number, value: any) { while (list.length <= index) list.push(undefined); list[index] = value; @@ -271,6 +288,7 @@ ScriptingGlobals.add( ); ScriptingGlobals.add( + // eslint-disable-next-line prefer-arrow-callback function getIndexVal(list: any[], index: number, defaultVal: Opt = undefined) { return list?.reduce((p, x, i) => ((i <= index && x !== undefined) || p === undefined ? x : p), defaultVal); }, @@ -279,12 +297,14 @@ ScriptingGlobals.add( ); ScriptingGlobals.add( + // eslint-disable-next-line prefer-arrow-callback function makeScript(script: string) { return ScriptField.MakeScript(script); }, 'returns the value at a given index of a list', '(list: any[], index: number)' ); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function dashCallChat(setVal: (val: FieldResult) => void, target: Doc, queryText: string) { ScriptField.CallGpt(queryText, setVal, target); }, 'calls chat gpt for the query string and then calls setVal with the result'); -- cgit v1.2.3-70-g09d2 From 098deaa68c8b9bb781748fbe0c1bd0104bab3596 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 30 Apr 2024 23:35:18 -0400 Subject: unwinding more import loops by splitting up Documents.ts into DocUtils.ts and moving crate functions to <>Box functions --- .eslintrc.json | 2 +- .../apis/google_docs/GooglePhotosClientUtils.ts | 3 +- src/client/documents/DocFromField.ts | 50 + src/client/documents/DocUtils.ts | 883 ++++++++++++++ src/client/documents/Documents.ts | 1270 +------------------- src/client/util/CurrentUserUtils.ts | 8 +- src/client/util/DictationManager.ts | 5 +- src/client/util/DocumentManager.ts | 5 +- src/client/util/DragManager.ts | 21 +- src/client/util/GroupManager.tsx | 6 +- src/client/util/GroupMemberView.tsx | 4 +- src/client/util/LinkFollower.ts | 16 +- src/client/util/RTFMarkup.tsx | 22 +- src/client/util/ReplayMovements.ts | 2 +- src/client/util/SettingsManager.tsx | 17 +- src/client/util/SharingManager.tsx | 19 +- src/client/views/DashboardView.tsx | 47 +- src/client/views/DocComponent.tsx | 7 +- src/client/views/DocumentButtonBar.tsx | 7 +- src/client/views/DocumentDecorations.tsx | 5 +- src/client/views/GestureOverlay.tsx | 43 +- src/client/views/GlobalKeyHandler.ts | 11 +- src/client/views/InkStrokeProperties.ts | 3 +- src/client/views/InkingStroke.tsx | 79 +- src/client/views/LightboxView.tsx | 10 +- src/client/views/MainView.tsx | 10 +- src/client/views/MarqueeAnnotator.tsx | 8 +- src/client/views/PreviewCursor.tsx | 3 +- src/client/views/PropertiesButtons.tsx | 5 +- .../views/PropertiesDocBacklinksSelector.tsx | 2 +- src/client/views/PropertiesDocContextSelector.tsx | 3 +- src/client/views/PropertiesView.tsx | 5 +- src/client/views/SidebarAnnos.tsx | 5 +- src/client/views/TemplateMenu.tsx | 3 +- .../views/collections/CollectionCarousel3DView.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 26 +- .../collections/CollectionMasonryViewFieldRow.tsx | 2 +- .../views/collections/CollectionNoteTakingView.tsx | 12 +- .../collections/CollectionNoteTakingViewColumn.tsx | 12 +- .../views/collections/CollectionPileView.tsx | 4 +- .../collections/CollectionStackedTimeline.tsx | 12 +- .../views/collections/CollectionStackingView.tsx | 11 +- .../CollectionStackingViewFieldColumn.tsx | 10 +- src/client/views/collections/CollectionSubView.tsx | 12 +- .../views/collections/CollectionTreeView.tsx | 5 +- src/client/views/collections/CollectionView.tsx | 23 +- src/client/views/collections/TabDocView.tsx | 5 +- src/client/views/collections/TreeView.tsx | 8 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 17 +- .../collections/collectionFreeForm/MarqueeView.tsx | 9 +- .../collectionGrid/CollectionGridView.tsx | 5 +- .../collectionSchema/CollectionSchemaView.tsx | 6 +- .../collections/collectionSchema/SchemaRowBox.tsx | 4 +- src/client/views/global/globalScripts.ts | 6 +- src/client/views/linking/LinkMenuItem.tsx | 3 +- .../views/newlightbox/ButtonMenu/ButtonMenu.tsx | 2 +- src/client/views/newlightbox/NewLightboxView.tsx | 10 +- src/client/views/nodes/AudioBox.tsx | 11 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 6 +- src/client/views/nodes/ComparisonBox.tsx | 21 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 27 +- src/client/views/nodes/DocumentLinksButton.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 39 +- src/client/views/nodes/EquationBox.tsx | 18 +- src/client/views/nodes/FieldView.tsx | 27 +- src/client/views/nodes/FocusViewOptions.ts | 24 + src/client/views/nodes/FontIconBox/FontIconBox.tsx | 17 +- src/client/views/nodes/FunctionPlotBox.tsx | 11 +- src/client/views/nodes/ImageBox.tsx | 13 +- src/client/views/nodes/KeyValueBox.tsx | 10 +- src/client/views/nodes/KeyValuePair.tsx | 3 +- src/client/views/nodes/LabelBox.tsx | 12 +- src/client/views/nodes/LinkBox.tsx | 18 + src/client/views/nodes/LinkDocPreview.tsx | 3 +- src/client/views/nodes/LoadingBox.tsx | 6 + src/client/views/nodes/MapBox/MapBox.tsx | 16 +- src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx | 2 +- src/client/views/nodes/MapBox/MapPushpinBox.tsx | 7 + .../views/nodes/MapboxMapBox/MapboxContainer.tsx | 11 +- src/client/views/nodes/OpenWhere.ts | 25 + src/client/views/nodes/PDFBox.tsx | 14 +- .../nodes/PhysicsBox/PhysicsSimulationBox.tsx | 383 +++--- .../views/nodes/RecordingBox/RecordingBox.tsx | 5 + src/client/views/nodes/ScreenshotBox.tsx | 10 +- src/client/views/nodes/ScriptingBox.tsx | 7 + src/client/views/nodes/VideoBox.tsx | 17 +- src/client/views/nodes/WebBox.tsx | 15 +- src/client/views/nodes/calendarBox/CalendarBox.tsx | 6 + .../views/nodes/formattedText/DashDocView.tsx | 5 +- .../views/nodes/formattedText/DashFieldView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 56 +- .../formattedText/ProsemirrorExampleTransfer.ts | 4 +- .../views/nodes/formattedText/RichTextRules.ts | 3 +- .../views/nodes/generativeFill/GenerativeFill.tsx | 5 +- src/client/views/nodes/trails/PresBox.tsx | 15 +- src/client/views/nodes/trails/PresElementBox.tsx | 7 +- src/client/views/pdf/Annotation.tsx | 2 +- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 3 +- src/client/views/pdf/PDFViewer.tsx | 11 +- src/client/views/search/SearchBox.tsx | 12 +- src/client/views/selectedDoc/SelectedDocView.tsx | 2 +- src/fields/Doc.ts | 76 ++ src/fields/RichTextUtils.ts | 3 +- src/fields/ScriptField.ts | 4 + src/mobile/MobileInterface.tsx | 3 +- 105 files changed, 1900 insertions(+), 1896 deletions(-) create mode 100644 src/client/documents/DocFromField.ts create mode 100644 src/client/documents/DocUtils.ts create mode 100644 src/client/views/nodes/FocusViewOptions.ts create mode 100644 src/client/views/nodes/OpenWhere.ts (limited to 'src/fields/ScriptField.ts') diff --git a/.eslintrc.json b/.eslintrc.json index 6d851a64a..2e4da56b8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -57,7 +57,7 @@ "react/destructuring-assignment": 0, "no-restricted-globals": ["error", "event"], "no-param-reassign": ["error", { "props": false }], - "import/no-cycle": 0, + // "import/no-cycle": 0, "no-alert": 0, "radix": "off" }, diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts index 1eb977813..fdc185a8e 100644 --- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts +++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts @@ -10,7 +10,8 @@ import { RichTextUtils } from '../../../fields/RichTextUtils'; import { Cast, ImageCast, StrCast } from '../../../fields/Types'; import { MediaItem, NewMediaItemResult } from '../../../server/apis/google/SharedTypes'; import { Networking } from '../../Network'; -import { DocUtils, Docs, DocumentOptions } from '../../documents/Documents'; +import { Docs, DocumentOptions } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; import { FormattedTextBox } from '../../views/nodes/formattedText/FormattedTextBox'; import { GoogleAuthenticationManager } from '../GoogleAuthenticationManager'; diff --git a/src/client/documents/DocFromField.ts b/src/client/documents/DocFromField.ts new file mode 100644 index 000000000..1c0a9755b --- /dev/null +++ b/src/client/documents/DocFromField.ts @@ -0,0 +1,50 @@ +/* eslint-disable prefer-destructuring */ +/* eslint-disable default-param-last */ +/* eslint-disable no-use-before-define */ +import { Doc, DocListCast } from '../../fields/Doc'; +import { InkField } from '../../fields/InkField'; +import { List } from '../../fields/List'; +import { AudioField, ImageField, PdfField, VideoField } from '../../fields/URLField'; +import { InkingStroke } from '../views/InkingStroke'; +import { CollectionView } from '../views/collections/CollectionView'; +import { AudioBox } from '../views/nodes/AudioBox'; +import { ImageBox } from '../views/nodes/ImageBox'; +import { PDFBox } from '../views/nodes/PDFBox'; +import { VideoBox } from '../views/nodes/VideoBox'; +import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; +import { Docs, DocumentOptions } from './Documents'; + +export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined { + let created: Doc | undefined; + const field = target[fieldKey]; + const resolved = options ?? {}; + if (field instanceof ImageField) { + created = Docs.Create.ImageDocument(field.url.href, resolved); + created.layout = ImageBox.LayoutString(fieldKey); + } else if (field instanceof Doc) { + created = field; + } else if (field instanceof VideoField) { + created = Docs.Create.VideoDocument(field.url.href, resolved); + created.layout = VideoBox.LayoutString(fieldKey); + } else if (field instanceof PdfField) { + created = Docs.Create.PdfDocument(field.url.href, resolved); + created.layout = PDFBox.LayoutString(fieldKey); + } else if (field instanceof AudioField) { + created = Docs.Create.AudioDocument(field.url.href, resolved); + created.layout = AudioBox.LayoutString(fieldKey); + } else if (field instanceof InkField) { + created = Docs.Create.InkDocument(field.inkData, resolved); + created.layout = InkingStroke.LayoutString(fieldKey); + } else if (field instanceof List && field[0] instanceof Doc) { + created = Docs.Create.StackingDocument(DocListCast(field), resolved); + created.layout = CollectionView.LayoutString(fieldKey); + } else { + created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _layout_autoHeight: true }, ...resolved }); + created.layout = FormattedTextBox.LayoutString(fieldKey); + } + if (created) { + created.title = fieldKey; + proto && created.proto && (created.proto = Doc.GetProto(proto)); + } + return created; +} diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts new file mode 100644 index 000000000..e93bbc41b --- /dev/null +++ b/src/client/documents/DocUtils.ts @@ -0,0 +1,883 @@ +/* eslint-disable prefer-destructuring */ +/* eslint-disable default-param-last */ +/* eslint-disable no-use-before-define */ +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { saveAs } from 'file-saver'; +import * as JSZip from 'jszip'; +import { action, runInAction } from 'mobx'; +import { ClientUtils } from '../../ClientUtils'; +import * as JSZipUtils from '../../JSZipUtils'; +import { decycle } from '../../decycler/decycler'; +import { DateField } from '../../fields/DateField'; +import { Doc, DocListCast, Field, FieldType, LinkedTo, Opt, SetActiveAudioLinker, StrListCast } from '../../fields/Doc'; +import { DocData } from '../../fields/DocSymbols'; +import { Id } from '../../fields/FieldSymbols'; +import { InkDataFieldName, InkField } from '../../fields/InkField'; +import { List, ListFieldName } from '../../fields/List'; +import { ProxyField } from '../../fields/Proxy'; +import { RichTextField } from '../../fields/RichTextField'; +import { ComputedField, FollowLinkScript, ScriptField } from '../../fields/ScriptField'; +import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types'; +import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from '../../fields/URLField'; +import { SharingPermissions } from '../../fields/util'; +import { Upload } from '../../server/SharedMediaTypes'; +import { DocServer } from '../DocServer'; +import { Networking } from '../Network'; +import { LinkManager } from '../util/LinkManager'; +import { ScriptingGlobals } from '../util/ScriptingGlobals'; +import { SerializationHelper } from '../util/SerializationHelper'; +import { UndoManager, undoable } from '../util/UndoManager'; +import { ContextMenu } from '../views/ContextMenu'; +import { ContextMenuProps } from '../views/ContextMenuItem'; +import { FieldViewProps } from '../views/nodes/FieldView'; +import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup'; +import { LoadingBox } from '../views/nodes/LoadingBox'; +import { OpenWhere } from '../views/nodes/OpenWhere'; +import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; +import { DocumentType } from './DocumentTypes'; +import { Docs, DocumentOptions } from './Documents'; + +const { DFLT_IMAGE_NATIVE_DIM } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore + +const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', '')); + +export namespace DocUtils { + function matchFieldValue(doc: Doc, key: string, valueIn: any): boolean { + let value = valueIn; + const hasFunctionFilter = ClientUtils.HasFunctionFilter(value); + if (hasFunctionFilter) { + return hasFunctionFilter(StrCast(doc[key])); + } + if (key === LinkedTo) { + // links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("...")) + const allLinks = LinkManager.Instance.getAllRelatedLinks(doc); + const matchLink = (val: string, anchor: Doc) => { + const linkedToExp = (val ?? '').split('='); + if (linkedToExp.length === 1) return Field.toScriptString(anchor) === val; + return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1]; + }; + // prettier-ignore + return (value === Doc.FilterNone && !allLinks.length) || + (value === Doc.FilterAny && !!allLinks.length) || + (allLinks.some(link => matchLink(value,DocCast(link.link_anchor_1)) || + matchLink(value,DocCast(link.link_anchor_2)) )); + } + if (typeof value === 'string') { + value = value.replace(`,${ClientUtils.noRecursionHack}`, ''); + } + const fieldVal = doc[key]; + // prettier-ignore + if ((value === Doc.FilterAny && fieldVal !== undefined) || + (value === Doc.FilterNone && fieldVal === undefined)) { + return true; + } + const vals = StrListCast(fieldVal); // list typing is very imperfect. casting to a string list doesn't mean that the entries will actually be strings + if (vals.length) { + return vals.some(v => typeof v === 'string' && v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring + } + return Field.toString(fieldVal as FieldType).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring + } + /** + * @param docs + * @param childFilters + * @param childFiltersByRanges + * @param parentCollection + * Given a list of docs and childFilters, @returns the list of Docs that match those filters + */ + export function FilterDocs(childDocs: Doc[], childFilters: string[], childFiltersByRanges: string[], parentCollection?: Doc) { + if (!childFilters?.length && !childFiltersByRanges?.length) { + return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one + } + + const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields + childFilters.forEach(filter => { + const fields = filter.split(Doc.FilterSep); + const key = fields[0]; + const value = fields[1]; + const modifiers = fields[2]; + if (!filterFacets[key]) { + filterFacets[key] = {}; + } + filterFacets[key][value] = modifiers; + }); + + const filteredDocs = childFilters.length + ? childDocs.filter(d => { + if (d.z) return true; + // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter + if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) { + return false; + } + const facetKeys = Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== ClientUtils.noDragDocsFilter.split(Doc.FilterSep)[0]); + // eslint-disable-next-line no-restricted-syntax + for (const facetKey of facetKeys) { + const facet = filterFacets[facetKey]; + + // facets that match some value in the field of the document (e.g. some text field) + const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match'); + + // facets that have a check next to them + const checks = Object.keys(facet).filter(value => facet[value] === 'check'); + + // metadata facets that exist + const exists = Object.keys(facet).filter(value => facet[value] === 'exists'); + + // facets that unset metadata (a hack for making cookies work) + const unsets = Object.keys(facet).filter(value => facet[value] === 'unset'); + + // facets that specify that a field must not match a specific value + const xs = Object.keys(facet).filter(value => facet[value] === 'x'); + + if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; + const failsNotEqualFacets = !xs.length ? false : xs.some(value => matchFieldValue(d, facetKey, value)); + const satisfiesCheckFacets = !checks.length ? true : checks.some(value => matchFieldValue(d, facetKey, value)); + const satisfiesExistsFacets = !exists.length ? true : facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length; + const satisfiesUnsetsFacets = !unsets.length ? true : d[facetKey] === undefined; + const satisfiesMatchFacets = !matches.length + ? true + : matches.some(value => { + if (facetKey.startsWith('*')) { + // fields starting with a '*' are used to match families of related fields. ie, *modificationDate will match text_modificationDate, data_modificationDate, etc + const allKeys = Array.from(Object.keys(d)); + allKeys.push(...Object.keys(Doc.GetProto(d))); + const keys = allKeys.filter(key => key.includes(facetKey.substring(1))); + return keys.some(key => Field.toString(d[key] as FieldType).includes(value)); + } + return Field.toString(d[facetKey] as FieldType).includes(value); + }); + // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria + if (parentCollection?.childFilters_boolean === 'OR') { + if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; + } + // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria + else if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; + } + return parentCollection?.childFilters_boolean !== 'OR'; + }) + : childDocs; + const rangeFilteredDocs = filteredDocs.filter(d => { + for (let i = 0; i < childFiltersByRanges.length; i += 3) { + const key = childFiltersByRanges[i]; + const min = Number(childFiltersByRanges[i + 1]); + const max = Number(childFiltersByRanges[i + 2]); + const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null); + if (val === undefined) { + // console.log("Should 'undefined' pass range filter or not?") + } else if (val < min || val > max) return false; + } + return true; + }); + return rangeFilteredDocs; + } + + export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; + + export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { + broadcastEvent && runInAction(() => { Doc.RecordingEvent += 1; }); // prettier-ignore + return DocUtils.ActiveRecordings.map(audio => { + const sourceDoc = getSourceDoc(); + return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); + }); + } + + SetActiveAudioLinker(MakeLinkToActiveAudio); + + export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; link_displayLine?: boolean }, id?: string, showPopup?: number[]) { + if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; + if (target.doc === Doc.UserDoc()) return undefined; + + const makeLink = action((linkDoc: Doc, showAt?: number[]) => { + if (showAt) { + LinkManager.Instance.currentLink = linkDoc; + + TaskCompletionBox.textDisplayed = 'Link Created'; + TaskCompletionBox.popupX = showAt[0]; + TaskCompletionBox.popupY = showAt[1] - 33; + TaskCompletionBox.taskCompleted = true; + + LinkDescriptionPopup.Instance.popupX = showAt[0]; + LinkDescriptionPopup.Instance.popupY = showAt[1]; + LinkDescriptionPopup.Instance.display = true; + + const rect = document.body.getBoundingClientRect(); + if (LinkDescriptionPopup.Instance.popupX + 200 > rect.width) { + LinkDescriptionPopup.Instance.popupX -= 190; + TaskCompletionBox.popupX -= 40; + } + if (LinkDescriptionPopup.Instance.popupY + 100 > rect.height) { + LinkDescriptionPopup.Instance.popupY -= 40; + TaskCompletionBox.popupY -= 40; + } + + setTimeout( + action(() => { + TaskCompletionBox.taskCompleted = false; + }), + 2500 + ); + } + return linkDoc; + }); + + const a = source.layout_unrendered ? 'link_anchor_1?.annotationOn' : 'link_anchor_1'; + const b = target.layout_unrendered ? 'link_anchor_2?.annotationOn' : 'link_anchor_2'; + + return makeLink( + Docs.Create.LinkDocument( + source, + target, + { + acl_Guest: SharingPermissions.Augment, + _acl_Guest: SharingPermissions.Augment, + title: ComputedField.MakeFunction('generateLinkTitle(this)') as any, + link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined, + link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined, + link_displayLine: linkSettings.link_displayLine, + link_relationship: linkSettings.link_relationship, + link_description: linkSettings.link_description, + x: ComputedField.MakeFunction(`((this.${a}?.x||0)+(this.${b}?.x||0))/2`) as any, + y: ComputedField.MakeFunction(`((this.${a}?.y||0)+(this.${b}?.y||0))/2`) as any, + link_autoMoveAnchors: true, + _lockedPosition: true, + _layout_showCaption: '', // removed since they conflict with showing a link with a LinkBox (ie, line, not comparison box) + _layout_showTitle: '', + // _layout_showCaption: 'link_description', + // _layout_showTitle: 'link_relationship', + }, + id + ), + showPopup + ); + } + + export function AssignScripts(doc: Doc, scripts?: { [key: string]: string | undefined }, funcs?: { [key: string]: string }) { + scripts && + Object.keys(scripts).forEach(key => { + const script = scripts[key]; + if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) { + (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = ScriptField.MakeScript(script, { + self: Doc.name, + this: Doc.name, + dragData: Doc.DocDragDataName, + value: 'any', + _readOnly_: 'boolean', + scriptContext: 'any', + documentView: Doc.name, + heading: Doc.name, + checked: 'boolean', + containingTreeView: Doc.name, + altKey: 'boolean', + ctrlKey: 'boolean', + shiftKey: 'boolean', + }); + } + }); + funcs && + Object.keys(funcs) + .filter(key => !key.endsWith('-setter')) + .forEach(key => { + const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); + if (ScriptCast(cfield)?.script.originalScript !== funcs[key]) { + const setFunc = Cast(funcs[key + '-setter'], 'string', null); + (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = funcs[key] ? ComputedField.MakeFunction(funcs[key], { dragData: Doc.DocDragDataName }, { _readOnly_: true }, setFunc) : undefined; + } + }); + return doc; + } + export function AssignOpts(doc: Doc | undefined, reqdOpts: DocumentOptions, items?: Doc[]) { + if (doc) { + const compareValues = (val1: any, val2: any) => { + if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) { + return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v)); + } + return val1 === val2; + }; + Object.entries(reqdOpts).forEach(pair => { + const targetDoc = pair[0].startsWith('_') ? doc : Doc.GetProto(doc as Doc); + if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/, '')) || !compareValues(pair[1], targetDoc[pair[0]])) { + targetDoc[pair[0]] = pair[1]; + } + }); + items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), 'data', item)); + items && DocListCast(doc.data).forEach(item => Doc.IsSystem(item) && !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item)); + } + return doc; + } + export function AssignDocField(doc: Doc, field: string, creator: (reqdOpts: DocumentOptions, items?: Doc[]) => Doc, reqdOpts: DocumentOptions, items?: Doc[], scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) { + // eslint-disable-next-line no-return-assign + return DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs); + } + + /** + * + * @param type the type of file. + * @param path the path to the file. + * @param options the document options. + * @param overwriteDoc the placeholder loading doc. + * @returns + */ + export async function DocumentFromType(type: string, path: string, options: DocumentOptions, overwriteDoc?: Doc): Promise> { + let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined; + if (type.indexOf('image') !== -1) { + ctor = Docs.Create.ImageDocument; + if (!options._width) options._width = 300; + } + if (type.indexOf('video') !== -1) { + ctor = Docs.Create.VideoDocument; + if (!options._width) options._width = 600; + if (!options._height) options._height = ((options._width as number) * 2) / 3; + } + if (type.indexOf('audio') !== -1) { + ctor = Docs.Create.AudioDocument; + } + if (type.indexOf('pdf') !== -1) { + ctor = Docs.Create.PdfDocument; + if (!options._width) options._width = 400; + if (!options._height) options._height = ((options._width as number) * 1200) / 927; + } + if (type.indexOf('csv') !== -1) { + ctor = Docs.Create.DataVizDocument; + if (!options._width) options._width = 400; + if (!options._height) options._height = ((options._width as number) * 1200) / 927; + } + // TODO:al+glr + // if (type.indexOf("map") !== -1) { + // ctor = Docs.Create.MapDocument; + // if (!options._width) options._width = 800; + // if (!options._height) options._height = (options._width as number) * 3 / 4; + // } + if (type.indexOf('html') !== -1) { + if (path.includes(window.location.hostname)) { + const s = path.split('/'); + const id = s[s.length - 1]; + return DocServer.GetRefField(id).then(field => { + if (field instanceof Doc) { + const embedding = Doc.MakeEmbedding(field); + embedding.x = (options.x as number) || 0; + embedding.y = (options.y as number) || 0; + embedding._width = (options._width as number) || 300; + embedding._height = (options._height as number) || (options._width as number) || 300; + return embedding; + } + return undefined; + }); + } + ctor = Docs.Create.WebDocument; + // eslint-disable-next-line no-param-reassign + options = { ...options, _width: 400, _height: 512, title: path }; + } + + return ctor ? ctor(path, overwriteDoc ? { ...options, title: StrCast(overwriteDoc.title, path) } : options, overwriteDoc) : undefined; + } + + export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false, pivotField?: string, pivotValue?: string): void { + const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data) + .filter(btnDoc => !btnDoc.hidden) + .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) + .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title) + .map(dragDoc => ({ + description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), + event: undoable(() => { + const newDoc = DocUtils.copyDragFactory(dragDoc); + if (newDoc) { + newDoc.author = ClientUtils.CurrentUserEmail(); + newDoc.x = x; + newDoc.y = y; + Doc.SetSelectOnLoad(newDoc); + if (pivotField) { + newDoc[pivotField] = pivotValue; + } + docAdder?.(newDoc); + } + }, StrCast(dragDoc.title)), + icon: Doc.toIcon(dragDoc), + })) as ContextMenuProps[]; + ContextMenu.Instance.addItem({ + description: 'Create document', + subitems: documentList, + icon: 'file', + }); + !simpleMenu && + ContextMenu.Instance.addItem({ + description: 'Styled Notes', + subitems: DocListCast((Doc.UserDoc().template_notes as Doc).data).map(note => ({ + description: ':' + StrCast(note.title), + event: undoable(() => { + const textDoc = Docs.Create.TextDocument('', { + _width: 200, + x, + y, + _layout_autoHeight: note._layout_autoHeight !== false, + title: StrCast(note.title) + '#' + (note.embeddingCount = NumCast(note.embeddingCount) + 1), + }); + textDoc.layout_fieldKey = 'layout_' + note.title; + textDoc[textDoc.layout_fieldKey] = note; + if (pivotField) { + textDoc[pivotField] = pivotValue; + } + docTextAdder(textDoc); + }, 'create quick note'), + icon: StrCast(note.icon) as IconProp, + })) as ContextMenuProps[], + icon: 'sticky-note', + }); + const userDocList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[1]?.data) + .filter(btnDoc => !btnDoc.hidden) + .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) + .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title) + .map(dragDoc => ({ + description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), + event: undoable(() => { + const newDoc = DocUtils.delegateDragFactory(dragDoc); + if (newDoc) { + newDoc.author = ClientUtils.CurrentUserEmail(); + newDoc.x = x; + newDoc.y = y; + Doc.SetSelectOnLoad(newDoc); + if (pivotField) { + newDoc[pivotField] = pivotValue; + } + docAdder?.(newDoc); + } + }, StrCast(dragDoc.title)), + icon: Doc.toIcon(dragDoc), + })) as ContextMenuProps[]; + ContextMenu.Instance.addItem({ + description: 'User Templates', + subitems: userDocList, + icon: 'file', + }); + } + + // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) + + /** + * Applies a template to a Doc and logs the action with the UndoManager + * If the template already exists and has been registered, it can be specified by it's signature name (e.g., 'icon' not 'layout_icon'). + * Alternatively, the signature can be omitted and the template can be provided. + * @param doc the Doc to apply the template to. + * @param creator a function that will create the template if it doesn't exist + * @param templateSignature the signature name for a template that has already been created and registered on the userDoc. (can be "" if template is provide) + * @param template the template to use (optional if templateSignature is provided) + * @returns doc + */ + export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', template?: Doc) { + const batch = UndoManager.StartBatch('makeCustomViewClicked'); + createCustomView(doc, creator, templateSignature || StrCast(template?.title), template); + batch.end(); + return doc; + } + export function findTemplate(templateName: string, type: string) { + let docLayoutTemplate: Opt; + const iconViews = DocListCast(Cast(Doc.UserDoc().template_icons, Doc, null)?.data); + const templBtns = DocListCast(Cast(Doc.UserDoc().template_buttons, Doc, null)?.data); + const noteTypes = DocListCast(Cast(Doc.UserDoc().template_notes, Doc, null)?.data); + const userTypes = DocListCast(Cast(Doc.UserDoc().template_user, Doc, null)?.data); + const clickFuncs = DocListCast(Cast(Doc.UserDoc().template_clickFuncs, Doc, null)?.data); + const allTemplates = iconViews + .concat(templBtns) + .concat(noteTypes) + .concat(userTypes) + .concat(clickFuncs) + .map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc) + .filter(doc => doc.isTemplateDoc); + // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized + // first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on + !docLayoutTemplate && + allTemplates.forEach(tempDoc => { + StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc); + }); + !docLayoutTemplate && + allTemplates.forEach(tempDoc => { + StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc); + }); + return docLayoutTemplate; + } + export function createCustomView(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) { + const templateName = templateSignature.replace(/\(.*\)/, ''); + doc.layout_fieldKey = 'layout_' + (templateSignature || (docLayoutTemplate?.title ?? '')); + // eslint-disable-next-line no-param-reassign + docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.isGroup && doc.transcription ? 'transcription' : doc.type)); + + const customName = 'layout_' + templateSignature; + const _width = NumCast(doc._width); + const _height = NumCast(doc._height); + const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _layout_autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _layout_showSidebar: false }; + + if (docLayoutTemplate) { + if (docLayoutTemplate !== doc[customName]) { + Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); + } + } else { + let fieldTemplate: Opt; + if (doc.data instanceof RichTextField || typeof doc.data === 'string') { + fieldTemplate = Docs.Create.TextDocument('', options); + } else if (doc.data instanceof PdfField) { + fieldTemplate = Docs.Create.PdfDocument('http://www.msn.com', options); + } else if (doc.data instanceof VideoField) { + fieldTemplate = Docs.Create.VideoDocument('http://www.cs.brown.edu', options); + } else if (doc.data instanceof AudioField) { + fieldTemplate = Docs.Create.AudioDocument('http://www.cs.brown.edu', options); + } else if (doc.data instanceof ImageField) { + fieldTemplate = Docs.Create.ImageDocument('http://www.cs.brown.edu', options); + } + const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + '(' + doc.title + ')', isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); + fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); + docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); + } + } + export function makeCustomView(doc: Doc, custom: boolean, layout: string) { + Doc.setNativeView(doc); + if (custom) { + makeCustomViewClicked(doc, Docs.Create.StackingDocument, layout, undefined); + } + } + export function iconify(doc: Doc) { + const layoutFieldKey = Cast(doc.layout_fieldKey, 'string', null); + DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined); + if (layoutFieldKey && layoutFieldKey !== 'layout' && layoutFieldKey !== 'layout_icon') doc.deiconifyLayout = layoutFieldKey.replace('layout_', ''); + } + + export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) { + runInAction(() => { + docList.forEach((doc, i) => { + const d = doc; + DocUtils.iconify(d); + d.x = Math.cos((Math.PI * 2 * i) / docList.length) * size - size; + d.y = Math.sin((Math.PI * 2 * i) / docList.length) * size - size; + d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + }); + }); + if (create) { + const newCollection = Docs.Create.PileDocument(docList, { title: 'pileup', _freeform_noZoom: true, x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, dragWhenActive: true, _layout_fitWidth: false }); + newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size; + newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size; + newCollection._width = newCollection._height = size * 2; + return newCollection; + } + return undefined; + } + export function makeIntoPortal(doc: Doc, layoutDoc: Doc, allLinks: Doc[]) { + const portalLink = allLinks.find(d => d.link_anchor_1 === doc && d.link_relationship === 'portal to:portal from'); + if (!portalLink) { + DocUtils.MakeLink( + doc, + Docs.Create.FreeformDocument([], { + _width: NumCast(layoutDoc._width) + 10, + _height: Math.max(NumCast(layoutDoc._height), NumCast(layoutDoc._width) + 10), + _isLightbox: true, + _layout_fitWidth: true, + title: StrCast(doc.title) + ' [Portal]', + }), + { link_relationship: 'portal to:portal from' } + ); + } + doc.followLinkLocation = OpenWhere.lightbox; + doc.onClick = FollowLinkScript(); + } + + export function LeavePushpin(doc: Doc, annotationField: string) { + if (doc.followLinkToggle) return undefined; + const context = Cast(doc.embedContainer, Doc, null) ?? Cast(doc.annotationOn, Doc, null); + 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: '', + annotationOn: Cast(doc.annotationOn, Doc, null), + followLinkToggle: true, + icon: 'map-pin', + x: Cast(doc.x, 'number', null), + y: Cast(doc.y, 'number', null), + backgroundColor: '#ACCEF7', + layout_hideAllLinks: true, + _width: 15, + _height: 15, + _xPadding: 0, + onClick: FollowLinkScript(), + _timecodeToShow: Cast(doc._timecodeToShow, 'number', null), + }); + Doc.AddDocToList(context, annotationField, pushpin); + DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, ''); + doc._timecodeToShow = undefined; + return pushpin; + } + return undefined; + } + + // /** + // * + // * @param dms Degree Minute Second format exif gps data + // * @param ref ref that determines negativity of decimal coordinates + // * @returns a decimal format of gps latitude / longitude + // */ + // function getDecimalfromDMS(dms?: number[], ref?: string) { + // if (dms && ref) { + // let degrees = dms[0] / dms[1]; + // let minutes = dms[2] / dms[3] / 60.0; + // let seconds = dms[4] / dms[5] / 3600.0; + + // if (['S', 'W'].includes(ref)) { + // degrees = -degrees; minutes = -minutes; seconds = -seconds + // } + // return (degrees + minutes + seconds).toFixed(5); + // } + // } + + function ConvertDMSToDD(degrees: number, minutes: number, seconds: number, direction: string) { + let dd = degrees + minutes / 60 + seconds / (60 * 60); + if (direction === 'S' || direction === 'W') { + dd *= -1; + } // Don't do anything for N or E + return dd; + } + + export function assignImageInfo(result: Upload.FileInformation, protoIn: Doc) { + const proto = protoIn; + if (Upload.isImageInformation(result)) { + const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim); + const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase(); + proto.data_nativeOrientation = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined); + proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; + proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); + if (NumCast(proto.data_nativeOrientation) >= 5) { + proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; + proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); + } + proto.data_exif = JSON.stringify(result.exifData?.data); + proto.data_contentSize = result.contentSize; + // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates + const latitude = result.exifData?.data?.GPSLatitude; + const latitudeDirection = result.exifData?.data?.GPSLatitudeRef; + const longitude = result.exifData?.data?.GPSLongitude; + const longitudeDirection = result.exifData?.data?.GPSLongitudeRef; + if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) { + proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection); + proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection); + } + } + } + + async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions, overwriteDoc?: Doc) { + if (result instanceof Error) { + alert(`Upload failed: ${result.message}`); + return; + } + const full = { ...options, _width: 400, title: name }; + const pathname = result.accessPaths.agnostic.client; + const doc = await DocUtils.DocumentFromType(type, pathname, full, overwriteDoc); + if (doc) { + const proto = Doc.GetProto(doc); + proto.text = result.rawText; + !(result instanceof Error) && DocUtils.assignImageInfo(result, proto); + if (Upload.isVideoInformation(result)) { + proto.data_duration = result.duration; + } + if (overwriteDoc) { + LoadingBox.removeCurrentlyLoading(overwriteDoc); + } + generatedDocuments.push(doc); + } + } + + export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) { + const defaultTextTemplate = DocCast(Doc.UserDoc().defaultTextLayout); + const tbox = Docs.Create.TextDocument('', { + annotationOn, + backgroundColor, + x, + y, + title, + ...(defaultTextTemplate + ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance + : { + _width: width || 200, + _height: 35, + _layout_centered: BoolCast(Doc.UserDoc()._layout_centered), + _layout_fitWidth: true, + _layout_autoHeight: true, + }), + }); + + if (defaultTextTemplate) { + tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title); + Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = defaultTextTemplate; // set the text doc's layout to render with the text template + tbox[DocData].proto = defaultTextTemplate; // and also set the text doc to inherit from the template (this allows the template to specify default field values) + } + return tbox; + } + + export function uploadYoutubeVideoLoading(videoId: string, options: DocumentOptions, overwriteDoc?: Doc) { + const generatedDocuments: Doc[] = []; + Networking.UploadYoutubeToServer(videoId, overwriteDoc?.[Id]).then(upfiles => { + const { + source: { newFilename, mimetype }, + result, + } = upfiles.lastElement(); + if ((result as any).message) { + if (overwriteDoc) { + overwriteDoc.isLoading = false; + overwriteDoc.loadingError = (result as any).message; + LoadingBox.removeCurrentlyLoading(overwriteDoc); + } + } else newFilename && processFileupload(generatedDocuments, newFilename, mimetype ?? '', result, options, overwriteDoc); + }); + } + + /** + * uploadFilesToDocs will take in an array of Files, and creates documents for the + * new files. + * + * @param files an array of files that will be uploaded + * @param options options to use while uploading + * @returns + */ + export async function uploadFilesToDocs(files: File[], options: DocumentOptions) { + const generatedDocuments: Doc[] = []; + + // These files do not have overwriteDocs, so we do not set the guid and let the client generate one. + const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({ file })); + + const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs); + upfiles.forEach(({ source: { newFilename, mimetype }, result }) => { + newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options); + }); + return generatedDocuments; + } + + export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) { + const generatedDocuments: Doc[] = []; + // Since this file has an overwriteDoc, we can set the client tracking guid to the overwriteDoc's guid. + Networking.UploadFilesToServer([{ file, guid: overwriteDoc[Id] }]).then(upfiles => { + const { + source: { newFilename, mimetype }, + result, + } = upfiles.lastElement() ?? { source: { newFilename: '', mimetype: '' }, result: { message: 'upload failed' } }; + if ((result as any).message) { + if (overwriteDoc) { + overwriteDoc.loadingError = (result as any).message; + LoadingBox.removeCurrentlyLoading(overwriteDoc); + } + } else newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options, overwriteDoc); + }); + } + + // copies the specified drag factory document + export function copyDragFactory(dragFactory: Doc) { + if (!dragFactory) return undefined; + const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); + if (ndoc && dragFactory.dragFactory_count !== undefined) { + dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1; + Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(), true); + } + + return ndoc; + } + export function delegateDragFactory(dragFactory: Doc) { + const ndoc = Doc.MakeDelegateWithProto(dragFactory); + if (ndoc && dragFactory.dragFactory_count !== undefined) { + dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1; + Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(); + } + return ndoc; + } + + export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') { + const { clone, map, linkMap } = await Doc.MakeClone(doc); + const proms = new Set(); + function replacer(key: any, value: any) { + if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined; + if (value?.__type === 'image') { + const extension = value.url.replace(/.*\./, ''); + proms.add(value.url.replace('.' + extension, '_o.' + extension)); + return SerializationHelper.Serialize(new ImageField(value.url)); + } + if (value?.__type === 'pdf') { + proms.add(value.url); + return SerializationHelper.Serialize(new PdfField(value.url)); + } + if (value?.__type === 'audio') { + proms.add(value.url); + return SerializationHelper.Serialize(new AudioField(value.url)); + } + if (value?.__type === 'video') { + proms.add(value.url); + return SerializationHelper.Serialize(new VideoField(value.url)); + } + if ( + value instanceof Doc || + value instanceof ScriptField || + value instanceof RichTextField || + value instanceof InkField || + value instanceof CsvField || + value instanceof WebField || + value instanceof DateField || + value instanceof ProxyField || + value instanceof ComputedField + ) { + return SerializationHelper.Serialize(value); + } + if (value instanceof Array && key !== ListFieldName && key !== InkDataFieldName) return { fields: value, __type: 'list' }; + return value; + } + + const docs: { [id: string]: any } = {}; + const links: { [id: string]: any } = {}; + Array.from(map.entries()).forEach(f => { + docs[f[0]] = f[1]; + }); + Array.from(linkMap.entries()).forEach(l => { + links[l[0]] = l[1]; + }); + const jsonDocs = JSON.stringify({ id: clone[Id], docs, links }, decycle(replacer)); + + const zip = new JSZip(); + let count = 0; + const promArr = Array.from(proms) + .filter(url => url?.startsWith('/files')) + .map(url => url.replace('/', '')); // window.location.origin)); + console.log(promArr.length); + if (!promArr.length) { + zip.file('docs.json', jsonDocs); + zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename)); + } else + promArr.forEach((url, i) => { + // loading a file and add it in a zip file + JSZipUtils.getBinaryContent(window.location.origin + '/' + url, (err: any, data: any) => { + if (err) throw err; // or handle the error + // // Generate a directory within the Zip file structure + // const assets = zip.folder("assets"); + // assets.file(filename, data, {binary: true}); + const assetPathOnServer = promArr[i].replace(window.location.origin + '/', '').replace(/\//g, '%%%'); + zip.file(assetPathOnServer, data, { binary: true }); + console.log(' => ' + url); + if (++count === promArr.length) { + zip.file('docs.json', jsonDocs); + zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename)); + // const a = document.createElement("a"); + // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`); + // a.href = url; + // a.download = `DocExport-${this.props.Document[Id]}.zip`; + // a.click(); + } + }); + }); + } +} + +ScriptingGlobals.add('Docs', Docs); +// eslint-disable-next-line prefer-arrow-callback +ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc, asDelegate?: boolean) { + return dragFactory instanceof Doc ? (asDelegate ? DocUtils.delegateDragFactory(dragFactory) : DocUtils.copyDragFactory(dragFactory)) : dragFactory; +}); +// eslint-disable-next-line prefer-arrow-callback +ScriptingGlobals.add(function makeDelegate(proto: any) { + const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title }); + return d; +}); +// eslint-disable-next-line prefer-arrow-callback +ScriptingGlobals.add(function generateLinkTitle(link: Doc) { + const linkAnchor1title = link.link_anchor_1 && link.link_anchor_1 !== link ? Cast(link.link_anchor_1, Doc, null)?.title : ''; + const linkAnchor2title = link.link_anchor_2 && link.link_anchor_2 !== link ? Cast(link.link_anchor_2, Doc, null)?.title : ''; + const relation = link.link_relationship || 'to'; + return `${linkAnchor1title} (${relation}) ${linkAnchor2title}`; +}); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0e7f911c7..a3fea1ce4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,79 +1,27 @@ /* eslint-disable prefer-destructuring */ /* eslint-disable default-param-last */ /* eslint-disable no-use-before-define */ -import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { saveAs } from 'file-saver'; -import * as JSZip from 'jszip'; -import { action, reaction, runInAction } from 'mobx'; +import { reaction } from 'mobx'; import { basename } from 'path'; import { ClientUtils, OmitKeys } from '../../ClientUtils'; -import * as JSZipUtils from '../../JSZipUtils'; -import { decycle } from '../../decycler/decycler'; import { DateField } from '../../fields/DateField'; -import { Doc, DocListCast, Field, FieldType, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc'; -import { DocData, Initializing } from '../../fields/DocSymbols'; -import { Id } from '../../fields/FieldSymbols'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, CreateLinkToActiveAudio, Doc, FieldType, Opt, updateCachedAcls } from '../../fields/Doc'; +import { Initializing } from '../../fields/DocSymbols'; import { HtmlField } from '../../fields/HtmlField'; -import { InkDataFieldName, InkField } from '../../fields/InkField'; -import { List, ListFieldName } from '../../fields/List'; -import { ProxyField } from '../../fields/Proxy'; +import { InkField } from '../../fields/InkField'; +import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; import { SchemaHeaderField } from '../../fields/SchemaHeaderField'; import { ComputedField, ScriptField } from '../../fields/ScriptField'; -import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types'; +import { ScriptCast, StrCast } from '../../fields/Types'; import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from '../../fields/URLField'; -import { SharingPermissions, inheritParentAcls } from '../../fields/util'; +import { SharingPermissions } from '../../fields/util'; import { PointData } from '../../pen-gestures/GestureTypes'; -import { Upload } from '../../server/SharedMediaTypes'; import { DocServer } from '../DocServer'; -import { Networking } from '../Network'; -import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; -import { FollowLinkScript } from '../util/LinkFollower'; import { LinkManager } from '../util/LinkManager'; -import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { SerializationHelper } from '../util/SerializationHelper'; -import { UndoManager, undoable } from '../util/UndoManager'; -import { ContextMenu } from '../views/ContextMenu'; -import { ContextMenuProps } from '../views/ContextMenuItem'; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke } from '../views/InkingStroke'; -import { CollectionDockingView } from '../views/collections/CollectionDockingView'; -import { CollectionView } from '../views/collections/CollectionView'; -import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView'; -import { AudioBox, mediaState } from '../views/nodes/AudioBox'; -import { ComparisonBox } from '../views/nodes/ComparisonBox'; -import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox'; -import { OpenWhere } from '../views/nodes/DocumentView'; -import { EquationBox } from '../views/nodes/EquationBox'; -import { FieldViewProps } from '../views/nodes/FieldView'; -import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; -import { FunctionPlotBox } from '../views/nodes/FunctionPlotBox'; -import { ImageBox } from '../views/nodes/ImageBox'; -import { KeyValueBox } from '../views/nodes/KeyValueBox'; -import { LabelBox } from '../views/nodes/LabelBox'; -import { LinkBox } from '../views/nodes/LinkBox'; -import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup'; -import { LoadingBox } from '../views/nodes/LoadingBox'; -import { MapBox } from '../views/nodes/MapBox/MapBox'; -import { MapPushpinBox } from '../views/nodes/MapBox/MapPushpinBox'; -import { PDFBox } from '../views/nodes/PDFBox'; -import { PhysicsSimulationBox } from '../views/nodes/PhysicsBox/PhysicsSimulationBox'; -import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox'; -import { ScreenshotBox } from '../views/nodes/ScreenshotBox'; -import { ScriptingBox } from '../views/nodes/ScriptingBox'; -import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; -import { VideoBox } from '../views/nodes/VideoBox'; -import { WebBox } from '../views/nodes/WebBox'; -import { CalendarBox } from '../views/nodes/calendarBox/CalendarBox'; -import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; -import { PresBox } from '../views/nodes/trails/PresBox'; -import { PresElementBox } from '../views/nodes/trails/PresElementBox'; -import { SearchBox } from '../views/search/SearchBox'; import { CollectionViewType, DocumentType } from './DocumentTypes'; -const { DFLT_IMAGE_NATIVE_DIM } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore - -const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', '')); class EmptyBox { public static LayoutString() { return ''; @@ -146,7 +94,7 @@ class DocInfo extends FInfo { } class DimInfo extends FInfo { fieldType? = FInfoFieldType.enumeration; - values? = [DimUnit.Pixel, DimUnit.Ratio]; + values? = []; // DimUnit.Pixel, DimUnit.Ratio]; readOnly = false; filterable = false; override searchable = () => false; @@ -202,7 +150,7 @@ type STRt = StrInfo | string; type LISTt = ListInfo | List; type DOCt = DocInfo | Doc; type RTFt = RtfInfo | RichTextField; -type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio; +type DIMt = DimInfo; // | typeof DimUnit.Pixel | typeof DimUnit.Ratio; type PEVt = PEInfo | 'none' | 'all'; type COLLt = CTypeInfo | CollectionViewType; type DROPt = DAInfo | dropActionType; @@ -245,6 +193,7 @@ export class DocumentOptions { _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)', false); _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)', false); + acl?: STRt = new StrInfo('unused except as a display category in KeyValueBox'); acl_Guest?: STRt = new StrInfo("permissions granted to users logged in as 'guest' (either view, or private)"); // public permissions _acl_Guest?: string; // public permissions type?: DTYPEt = new DTypeInfo('type of document', true); @@ -253,6 +202,7 @@ export class DocumentOptions { title?: STRt = new StrInfo('title of document', true); title_custom?: BOOLt = new BoolInfo('whether title is a default or has been intentionally set'); caption?: RichTextField; + systemIcon?: STRt = new StrInfo("name of icon to use to represent document's type"); author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable author_date?: DATEt = new DateInfo('date the document was created', true); annotationOn?: DOCt = new DocInfo('document annotated by this document', false); @@ -272,6 +222,12 @@ export class DocumentOptions { _lockedPosition?: BOOLt = new BoolInfo("lock the x,y coordinates of the document so that it can't be dragged"); _lockedTransform?: BOOLt = new BoolInfo('lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed'); + dataViz_title?: string; + dataViz_line?: string; + dataViz_pie?: string; + dataViz_histogram?: string; + dataViz?: string; + layout?: string | Doc; // default layout string or template document layout_isSvg?: BOOLt = new BoolInfo('whether document decorations and other selections should handle pointerEvents for svg content or use doc bounding box'); layout_keyValue?: STRt = new StrInfo('layout definition for showing keyValue view of document', false); @@ -325,6 +281,7 @@ export class DocumentOptions { _text_fontSize?: string; _text_fontFamily?: string; _text_fontWeight?: string; + fontSize?: string; _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views infoWindowOpen?: BOOLt = new BoolInfo('whether info window corresponding to pin is open (on MapDocuments)'); @@ -333,7 +290,7 @@ export class DocumentOptions { _label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes', false); stroke_width?: NUMt = new NumInfo('width of an ink stroke', false); stroke_showLabel?: BOOLt = new BoolInfo('show label inside of stroke'); - mediaState?: STRt = new StrInfo(`status of audio/video media document: ${mediaState.PendingRecording}, ${mediaState.Recording}, ${mediaState.Paused}, ${mediaState.Playing}`, false); + mediaState?: STRt = new StrInfo(`status of audio/video media document:`); // ${mediaState.PendingRecording}, ${mediaState.Recording}, ${mediaState.Paused}, ${mediaState.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!!)'); autoPlayAnchors?: BOOLt = new BoolInfo('whether to play audio/video when an anchor is clicked in a stackedTimeline.'); @@ -403,6 +360,7 @@ export class DocumentOptions { // STOPPING HERE // freeform properties + freeform?: STRt = new StrInfo(''); _freeform_backgroundGrid?: BOOLt = new BoolInfo('whether background grid is shown on freeform collections'); _freeform_scale_min?: NUMt = new NumInfo('how far out a view can zoom (used by image/videoBoxes that are clipped'); _freeform_scale_max?: NUMt = new NumInfo('how far in a view can zoom (used by sidebar freeform views'); @@ -433,6 +391,7 @@ export class DocumentOptions { flexGap?: NUMt = new NumInfo('Linear view flex gap'); flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse'; + link?: string; link_description?: string; // added for links link_relationship?: string; // type of relatinoship a link represents link_displayLine?: BOOLt = new BoolInfo('whether a link line should be dipslayed between the two link anchors'); @@ -528,120 +487,12 @@ export namespace Docs { type PrototypeMap = Map; const defaultDataKey = 'data'; - const TemplateMap: TemplateMap = new Map([ - [ - DocumentType.RTF, - { - layout: { view: FormattedTextBox, dataField: 'text' }, - options: { - acl: '', - _height: 35, - _xMargin: 10, - _yMargin: 10, - layout_nativeDimEditable: true, - layout_reflowVertical: true, - layout_reflowHorizontal: true, - defaultDoubleClick: 'ignore', - systemIcon: 'BsFileEarmarkTextFill', - }, - }, - ], - [ - DocumentType.SEARCH, - { - layout: { view: SearchBox, dataField: defaultDataKey }, - options: { acl: '', _width: 400 }, - }, - ], - [ - DocumentType.IMG, - { - layout: { view: ImageBox, dataField: defaultDataKey }, - options: { acl: '', freeform: '', systemIcon: 'BsFileEarmarkImageFill' }, - }, - ], - [ - DocumentType.WEB, - { - layout: { view: WebBox, dataField: defaultDataKey }, - options: { acl: '', _height: 300, _layout_fitWidth: true, layout_nativeDimEditable: true, layout_reflowVertical: true, waitForDoubleClickToClick: 'always', systemIcon: 'BsGlobe' }, - }, - ], + export const TemplateMap: TemplateMap = new Map([ [ - DocumentType.COL, - { - layout: { view: CollectionView, dataField: defaultDataKey }, - options: { - acl: '', - _layout_fitWidth: true, - freeform: '', - _freeform_panX: 0, - _freeform_panY: 0, - _freeform_scale: 1, - layout_nativeDimEditable: true, - layout_reflowHorizontal: true, - layout_reflowVertical: true, - systemIcon: 'BsFillCollectionFill', - }, - }, - ], - [ - DocumentType.KVP, - { - layout: { view: KeyValueBox, dataField: defaultDataKey }, - options: { acl: '', _layout_fitWidth: true, _height: 150 }, - }, - ], - [ - DocumentType.VID, - { - layout: { view: VideoBox, dataField: defaultDataKey }, - options: { acl: '', _layout_currentTimecode: 0, systemIcon: 'BsFileEarmarkPlayFill' }, - }, - ], - [ - DocumentType.AUDIO, - { - layout: { view: AudioBox, dataField: defaultDataKey }, - options: { acl: '', _height: 100, layout_fitWidth: true, layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsFillVolumeUpFill' }, - }, - ], - [ - DocumentType.REC, - { - layout: { view: VideoBox, dataField: defaultDataKey }, - options: { acl: '', _height: 100, backgroundColor: 'pink', systemIcon: 'BsFillMicFill' }, - }, - ], - [ - DocumentType.PDF, - { - layout: { view: PDFBox, dataField: defaultDataKey }, - options: { acl: '', _layout_curPage: 1, _layout_fitWidth: true, layout_nativeDimEditable: true, layout_reflowVertical: true, systemIcon: 'BsFileEarmarkPdfFill' }, - }, - ], - [ - DocumentType.MAP, - { - layout: { view: MapBox, dataField: defaultDataKey }, - options: { acl: '', map: '', _height: 600, _width: 800, layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsFillPinMapFill' }, - }, - ], - [ - DocumentType.LINK, + DocumentType.GROUPDB, { - layout: { view: LinkBox, dataField: 'link' }, - options: { - acl: '', - childDontRegisterViews: true, - layout_hideLinkAnchors: true, - _height: 1, - _width: 1, - link: '', - link_description: '', - color: 'lightBlue', // lightblue is default color for linking dot and link documents text comment area - _dropPropertiesToRemove: new List(['onClick']), - }, + layout: { view: EmptyBox, dataField: defaultDataKey }, + options: { acl: '', title: 'Global Group Database' }, }, ], [ @@ -652,175 +503,18 @@ export namespace Docs { options: { acl: '', title: 'Global Script Database' }, }, ], - [ - DocumentType.SCRIPTING, - { - layout: { view: ScriptingBox, dataField: defaultDataKey }, - options: { acl: '', systemIcon: 'BsFileEarmarkCodeFill' }, - }, - ], - [ - DocumentType.LABEL, - { - layout: { view: LabelBox, dataField: 'title' }, - options: { acl: '', _singleLine: true, layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true }, - }, - ], - [ - DocumentType.EQUATION, - { - layout: { view: EquationBox, dataField: 'text' }, - options: { acl: '', fontSize: '14px', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, layout_hideDecorationTitle: true, systemIcon: 'BsCalculatorFill' }, // systemIcon: 'BsSuperscript' + BsSubscript - }, - ], - [ - DocumentType.FUNCPLOT, - { - layout: { view: FunctionPlotBox, dataField: defaultDataKey }, - options: { acl: '', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true }, - }, - ], - [ - DocumentType.BUTTON, - { - layout: { view: LabelBox, dataField: 'title' }, - options: { acl: '', layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true }, - }, - ], - [ - DocumentType.PRES, - { - layout: { view: PresBox, dataField: defaultDataKey }, - options: { acl: '', defaultDoubleClick: 'ignore', hideClickBehaviors: true, layout_hideLinkAnchors: true }, - }, - ], - [ - DocumentType.FONTICON, - { - layout: { view: FontIconBox, dataField: 'icon' }, - options: { acl: '', defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideContextMenu: true, layout_hideLinkButton: true, _width: 40, _height: 40 }, - }, - ], - [ - DocumentType.WEBCAM, - { - layout: { view: RecordingBox, dataField: defaultDataKey }, - options: { acl: '', systemIcon: 'BsFillCameraVideoFill' }, - }, - ], - [ - DocumentType.PRESELEMENT, - { - layout: { view: PresElementBox, dataField: defaultDataKey }, - options: { acl: '', title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' }, - }, - ], + [ DocumentType.CONFIG, - { - layout: { view: CollectionView, dataField: defaultDataKey }, - options: { acl: '', config: '', layout_hideLinkButton: true, layout_unrendered: true }, - }, - ], - [ - DocumentType.INK, - { - // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method - layout: { view: InkingStroke, dataField: 'stroke' }, - options: { - acl: '', - systemIcon: 'BsFillPencilFill', // - layout_nativeDimEditable: true, - layout_reflowVertical: true, - layout_reflowHorizontal: true, - layout_hideDecorationTitle: true, // don't show title when selected - fitWidth: false, - layout_isSvg: true, - }, - }, - ], - [ - DocumentType.SCREENSHOT, - { - layout: { view: ScreenshotBox, dataField: defaultDataKey }, - options: { acl: '', layout_nativeDimEditable: true, systemIcon: 'BsCameraFill' }, - }, - ], - [ - DocumentType.COMPARISON, - { - data: '', - layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { - acl: '', - backgroundColor: 'gray', - dropAction: dropActionType.move, - waitForDoubleClickToClick: 'always', - layout_reflowHorizontal: true, - layout_reflowVertical: true, - layout_nativeDimEditable: true, - systemIcon: 'BsLayoutSplit', - }, - }, - ], - [ - DocumentType.GROUPDB, { layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { acl: '', title: 'Global Group Database' }, - }, - ], - [ - DocumentType.DATAVIZ, - { - layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { - acl: '', - dataViz_title: '', - dataViz_line: '', - dataViz_pie: '', - dataViz_histogram: '', - dataViz: 'table', - _layout_fitWidth: true, - layout_reflowHorizontal: true, - layout_reflowVertical: true, - layout_nativeDimEditable: true, - }, - }, - ], - [ - DocumentType.LOADING, - { - layout: { view: LoadingBox, dataField: '' }, - options: { acl: '', _layout_fitWidth: true, _fitHeight: true, layout_nativeDimEditable: true }, - }, - ], - [ - DocumentType.SIMULATION, - { - data: '', - layout: { view: PhysicsSimulationBox, dataField: defaultDataKey, _width: 1000, _height: 800 }, - options: { acl: '', _height: 100, mass1: '', mass2: '', layout_nativeDimEditable: true, position: '', acceleration: '', pendulum: '', spring: '', wedge: '', simulation: '', review: '', systemIcon: 'BsShareFill' }, - }, - ], - [ - DocumentType.PUSHPIN, - { - layout: { view: MapPushpinBox, dataField: defaultDataKey }, - options: { acl: '' }, + options: { acl: '', config: '', layout_hideLinkButton: true, layout_unrendered: true }, }, ], [ DocumentType.MAPROUTE, { - layout: { view: CollectionView, dataField: defaultDataKey }, - options: { acl: '' }, - }, - ], - [ - DocumentType.CALENDAR, - { - layout: { view: CalendarBox, dataField: defaultDataKey }, + layout: { view: EmptyBox, dataField: defaultDataKey }, options: { acl: '' }, }, ], @@ -1005,7 +699,7 @@ export namespace Docs { } Doc.assign(viewDoc, viewProps, true, true); if (![DocumentType.LINK, DocumentType.CONFIG, DocumentType.LABEL].includes(viewDoc.type as any)) { - DocUtils.MakeLinkToActiveAudio(() => viewDoc); + CreateLinkToActiveAudio(() => viewDoc); } updateCachedAcls(dataDoc); updateCachedAcls(viewDoc); @@ -1039,7 +733,7 @@ export namespace Docs { */ // eslint-disable-next-line default-param-last export function ScriptingDocument(script: Opt | null, options: DocumentOptions = {}, fieldKey?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script || undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined }); + return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script || undefined, { ...options, layout: fieldKey ? `` /* ScriptingBox.LayoutString(fieldKey) */ : undefined }); } // eslint-disable-next-line default-param-last @@ -1309,34 +1003,6 @@ export namespace Docs { return ret; } - export type DocConfig = { - doc: Doc; - initialWidth?: number; - path?: Doc[]; - }; - - export function StandardCollectionDockingDocument(configs: Array, options: DocumentOptions, id?: string, type: string = 'row') { - const layoutConfig = { - content: [ - { - type: type, - content: [...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))], - }, - ], - }; - const doc = DockDocument( - configs.map(c => c.doc), - JSON.stringify(layoutConfig), - ClientUtils.CurrentUserEmail() === 'guest' ? options : { acl_Guest: SharingPermissions.View, ...options }, - id - ); - configs.forEach(c => { - Doc.SetContainer(c.doc, doc); - inheritParentAcls(doc, c.doc, false); - }); - return doc; - } - export function DelegateDocument(proto: Doc, options: DocumentOptions = {}) { return InstanceFromProto(proto, undefined, options); } @@ -1346,879 +1012,3 @@ export namespace Docs { } } } - -export namespace DocUtils { - function matchFieldValue(doc: Doc, key: string, valueIn: any): boolean { - let value = valueIn; - const hasFunctionFilter = ClientUtils.HasFunctionFilter(value); - if (hasFunctionFilter) { - return hasFunctionFilter(StrCast(doc[key])); - } - if (key === LinkedTo) { - // links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("...")) - const allLinks = LinkManager.Instance.getAllRelatedLinks(doc); - const matchLink = (val: string, anchor: Doc) => { - const linkedToExp = (val ?? '').split('='); - if (linkedToExp.length === 1) return Field.toScriptString(anchor) === val; - return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1]; - }; - // prettier-ignore - return (value === Doc.FilterNone && !allLinks.length) || - (value === Doc.FilterAny && !!allLinks.length) || - (allLinks.some(link => matchLink(value,DocCast(link.link_anchor_1)) || - matchLink(value,DocCast(link.link_anchor_2)) )); - } - if (typeof value === 'string') { - value = value.replace(`,${ClientUtils.noRecursionHack}`, ''); - } - const fieldVal = doc[key]; - // prettier-ignore - if ((value === Doc.FilterAny && fieldVal !== undefined) || - (value === Doc.FilterNone && fieldVal === undefined)) { - return true; - } - const vals = StrListCast(fieldVal); // list typing is very imperfect. casting to a string list doesn't mean that the entries will actually be strings - if (vals.length) { - return vals.some(v => typeof v === 'string' && v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring - } - return Field.toString(fieldVal as FieldType).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring - } - /** - * @param docs - * @param childFilters - * @param childFiltersByRanges - * @param parentCollection - * Given a list of docs and childFilters, @returns the list of Docs that match those filters - */ - export function FilterDocs(childDocs: Doc[], childFilters: string[], childFiltersByRanges: string[], parentCollection?: Doc) { - if (!childFilters?.length && !childFiltersByRanges?.length) { - return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one - } - - const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields - childFilters.forEach(filter => { - const fields = filter.split(Doc.FilterSep); - const key = fields[0]; - const value = fields[1]; - const modifiers = fields[2]; - if (!filterFacets[key]) { - filterFacets[key] = {}; - } - filterFacets[key][value] = modifiers; - }); - - const filteredDocs = childFilters.length - ? childDocs.filter(d => { - if (d.z) return true; - // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter - if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) { - return false; - } - const facetKeys = Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== ClientUtils.noDragDocsFilter.split(Doc.FilterSep)[0]); - // eslint-disable-next-line no-restricted-syntax - for (const facetKey of facetKeys) { - const facet = filterFacets[facetKey]; - - // facets that match some value in the field of the document (e.g. some text field) - const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match'); - - // facets that have a check next to them - const checks = Object.keys(facet).filter(value => facet[value] === 'check'); - - // metadata facets that exist - const exists = Object.keys(facet).filter(value => facet[value] === 'exists'); - - // facets that unset metadata (a hack for making cookies work) - const unsets = Object.keys(facet).filter(value => facet[value] === 'unset'); - - // facets that specify that a field must not match a specific value - const xs = Object.keys(facet).filter(value => facet[value] === 'x'); - - if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; - const failsNotEqualFacets = !xs.length ? false : xs.some(value => matchFieldValue(d, facetKey, value)); - const satisfiesCheckFacets = !checks.length ? true : checks.some(value => matchFieldValue(d, facetKey, value)); - const satisfiesExistsFacets = !exists.length ? true : facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length; - const satisfiesUnsetsFacets = !unsets.length ? true : d[facetKey] === undefined; - const satisfiesMatchFacets = !matches.length - ? true - : matches.some(value => { - if (facetKey.startsWith('*')) { - // fields starting with a '*' are used to match families of related fields. ie, *modificationDate will match text_modificationDate, data_modificationDate, etc - const allKeys = Array.from(Object.keys(d)); - allKeys.push(...Object.keys(Doc.GetProto(d))); - const keys = allKeys.filter(key => key.includes(facetKey.substring(1))); - return keys.some(key => Field.toString(d[key] as FieldType).includes(value)); - } - return Field.toString(d[facetKey] as FieldType).includes(value); - }); - // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria - if (parentCollection?.childFilters_boolean === 'OR') { - if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; - } - // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria - else if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; - } - return parentCollection?.childFilters_boolean !== 'OR'; - }) - : childDocs; - const rangeFilteredDocs = filteredDocs.filter(d => { - for (let i = 0; i < childFiltersByRanges.length; i += 3) { - const key = childFiltersByRanges[i]; - const min = Number(childFiltersByRanges[i + 1]); - const max = Number(childFiltersByRanges[i + 2]); - const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null); - if (val === undefined) { - // console.log("Should 'undefined' pass range filter or not?") - } else if (val < min || val > max) return false; - } - return true; - }); - return rangeFilteredDocs; - } - - export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; - - export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { - broadcastEvent && runInAction(() => { Doc.RecordingEvent += 1; }); // prettier-ignore - return DocUtils.ActiveRecordings.map(audio => { - const sourceDoc = getSourceDoc(); - return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); - }); - } - - export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; link_displayLine?: boolean }, id?: string, showPopup?: number[]) { - if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; - if (target.doc === Doc.UserDoc()) return undefined; - - const makeLink = action((linkDoc: Doc, showAt?: number[]) => { - if (showAt) { - LinkManager.Instance.currentLink = linkDoc; - - TaskCompletionBox.textDisplayed = 'Link Created'; - TaskCompletionBox.popupX = showAt[0]; - TaskCompletionBox.popupY = showAt[1] - 33; - TaskCompletionBox.taskCompleted = true; - - LinkDescriptionPopup.Instance.popupX = showAt[0]; - LinkDescriptionPopup.Instance.popupY = showAt[1]; - LinkDescriptionPopup.Instance.display = true; - - const rect = document.body.getBoundingClientRect(); - if (LinkDescriptionPopup.Instance.popupX + 200 > rect.width) { - LinkDescriptionPopup.Instance.popupX -= 190; - TaskCompletionBox.popupX -= 40; - } - if (LinkDescriptionPopup.Instance.popupY + 100 > rect.height) { - LinkDescriptionPopup.Instance.popupY -= 40; - TaskCompletionBox.popupY -= 40; - } - - setTimeout( - action(() => { - TaskCompletionBox.taskCompleted = false; - }), - 2500 - ); - } - return linkDoc; - }); - - const a = source.layout_unrendered ? 'link_anchor_1?.annotationOn' : 'link_anchor_1'; - const b = target.layout_unrendered ? 'link_anchor_2?.annotationOn' : 'link_anchor_2'; - - return makeLink( - Docs.Create.LinkDocument( - source, - target, - { - acl_Guest: SharingPermissions.Augment, - _acl_Guest: SharingPermissions.Augment, - title: ComputedField.MakeFunction('generateLinkTitle(this)') as any, - link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined, - link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined, - link_displayLine: linkSettings.link_displayLine, - link_relationship: linkSettings.link_relationship, - link_description: linkSettings.link_description, - x: ComputedField.MakeFunction(`((this.${a}?.x||0)+(this.${b}?.x||0))/2`) as any, - y: ComputedField.MakeFunction(`((this.${a}?.y||0)+(this.${b}?.y||0))/2`) as any, - link_autoMoveAnchors: true, - _lockedPosition: true, - _layout_showCaption: '', // removed since they conflict with showing a link with a LinkBox (ie, line, not comparison box) - _layout_showTitle: '', - // _layout_showCaption: 'link_description', - // _layout_showTitle: 'link_relationship', - }, - id - ), - showPopup - ); - } - - export function AssignScripts(doc: Doc, scripts?: { [key: string]: string | undefined }, funcs?: { [key: string]: string }) { - scripts && - Object.keys(scripts).forEach(key => { - const script = scripts[key]; - if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) { - (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = ScriptField.MakeScript(script, { - self: Doc.name, - this: Doc.name, - dragData: DragManager.DocumentDragData.name, - value: 'any', - _readOnly_: 'boolean', - scriptContext: 'any', - documentView: Doc.name, - heading: Doc.name, - checked: 'boolean', - containingTreeView: Doc.name, - altKey: 'boolean', - ctrlKey: 'boolean', - shiftKey: 'boolean', - }); - } - }); - funcs && - Object.keys(funcs) - .filter(key => !key.endsWith('-setter')) - .forEach(key => { - const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); - if (ScriptCast(cfield)?.script.originalScript !== funcs[key]) { - const setFunc = Cast(funcs[key + '-setter'], 'string', null); - (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = funcs[key] ? ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, { _readOnly_: true }, setFunc) : undefined; - } - }); - return doc; - } - export function AssignOpts(doc: Doc | undefined, reqdOpts: DocumentOptions, items?: Doc[]) { - if (doc) { - const compareValues = (val1: any, val2: any) => { - if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) { - return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v)); - } - return val1 === val2; - }; - Object.entries(reqdOpts).forEach(pair => { - const targetDoc = pair[0].startsWith('_') ? doc : Doc.GetProto(doc as Doc); - if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/, '')) || !compareValues(pair[1], targetDoc[pair[0]])) { - targetDoc[pair[0]] = pair[1]; - } - }); - items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), 'data', item)); - items && DocListCast(doc.data).forEach(item => Doc.IsSystem(item) && !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item)); - } - return doc; - } - export function AssignDocField(doc: Doc, field: string, creator: (reqdOpts: DocumentOptions, items?: Doc[]) => Doc, reqdOpts: DocumentOptions, items?: Doc[], scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) { - // eslint-disable-next-line no-return-assign - return DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs); - } - - export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined { - let created: Doc | undefined; - const field = target[fieldKey]; - const resolved = options ?? {}; - if (field instanceof ImageField) { - created = Docs.Create.ImageDocument(field.url.href, resolved); - created.layout = ImageBox.LayoutString(fieldKey); - } else if (field instanceof Doc) { - created = field; - } else if (field instanceof VideoField) { - created = Docs.Create.VideoDocument(field.url.href, resolved); - created.layout = VideoBox.LayoutString(fieldKey); - } else if (field instanceof PdfField) { - created = Docs.Create.PdfDocument(field.url.href, resolved); - created.layout = PDFBox.LayoutString(fieldKey); - } else if (field instanceof AudioField) { - created = Docs.Create.AudioDocument(field.url.href, resolved); - created.layout = AudioBox.LayoutString(fieldKey); - } else if (field instanceof InkField) { - created = Docs.Create.InkDocument(field.inkData, resolved); - created.layout = InkingStroke.LayoutString(fieldKey); - } else if (field instanceof List && field[0] instanceof Doc) { - created = Docs.Create.StackingDocument(DocListCast(field), resolved); - created.layout = CollectionView.LayoutString(fieldKey); - } else { - created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _layout_autoHeight: true }, ...resolved }); - created.layout = FormattedTextBox.LayoutString(fieldKey); - } - if (created) { - created.title = fieldKey; - proto && created.proto && (created.proto = Doc.GetProto(proto)); - } - return created; - } - - /** - * - * @param type the type of file. - * @param path the path to the file. - * @param options the document options. - * @param overwriteDoc the placeholder loading doc. - * @returns - */ - export async function DocumentFromType(type: string, path: string, options: DocumentOptions, overwriteDoc?: Doc): Promise> { - let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined; - if (type.indexOf('image') !== -1) { - ctor = Docs.Create.ImageDocument; - if (!options._width) options._width = 300; - } - if (type.indexOf('video') !== -1) { - ctor = Docs.Create.VideoDocument; - if (!options._width) options._width = 600; - if (!options._height) options._height = ((options._width as number) * 2) / 3; - } - if (type.indexOf('audio') !== -1) { - ctor = Docs.Create.AudioDocument; - } - if (type.indexOf('pdf') !== -1) { - ctor = Docs.Create.PdfDocument; - if (!options._width) options._width = 400; - if (!options._height) options._height = ((options._width as number) * 1200) / 927; - } - if (type.indexOf('csv') !== -1) { - ctor = Docs.Create.DataVizDocument; - if (!options._width) options._width = 400; - if (!options._height) options._height = ((options._width as number) * 1200) / 927; - } - // TODO:al+glr - // if (type.indexOf("map") !== -1) { - // ctor = Docs.Create.MapDocument; - // if (!options._width) options._width = 800; - // if (!options._height) options._height = (options._width as number) * 3 / 4; - // } - if (type.indexOf('html') !== -1) { - if (path.includes(window.location.hostname)) { - const s = path.split('/'); - const id = s[s.length - 1]; - return DocServer.GetRefField(id).then(field => { - if (field instanceof Doc) { - const embedding = Doc.MakeEmbedding(field); - embedding.x = (options.x as number) || 0; - embedding.y = (options.y as number) || 0; - embedding._width = (options._width as number) || 300; - embedding._height = (options._height as number) || (options._width as number) || 300; - return embedding; - } - return undefined; - }); - } - ctor = Docs.Create.WebDocument; - // eslint-disable-next-line no-param-reassign - options = { ...options, _width: 400, _height: 512, title: path }; - } - - return ctor ? ctor(path, overwriteDoc ? { ...options, title: StrCast(overwriteDoc.title, path) } : options, overwriteDoc) : undefined; - } - - export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false, pivotField?: string, pivotValue?: string): void { - const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data) - .filter(btnDoc => !btnDoc.hidden) - .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) - .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title) - .map(dragDoc => ({ - description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), - event: undoable(() => { - const newDoc = DocUtils.copyDragFactory(dragDoc); - if (newDoc) { - newDoc.author = ClientUtils.CurrentUserEmail(); - newDoc.x = x; - newDoc.y = y; - EquationBox.SelectOnLoad = newDoc[Id]; - if (newDoc.type === DocumentType.RTF) FormattedTextBox.SetSelectOnLoad(newDoc); - if (pivotField) { - newDoc[pivotField] = pivotValue; - } - docAdder?.(newDoc); - } - }, StrCast(dragDoc.title)), - icon: Doc.toIcon(dragDoc), - })) as ContextMenuProps[]; - ContextMenu.Instance.addItem({ - description: 'Create document', - subitems: documentList, - icon: 'file', - }); - !simpleMenu && - ContextMenu.Instance.addItem({ - description: 'Styled Notes', - subitems: DocListCast((Doc.UserDoc().template_notes as Doc).data).map(note => ({ - description: ':' + StrCast(note.title), - event: undoable(() => { - const textDoc = Docs.Create.TextDocument('', { - _width: 200, - x, - y, - _layout_autoHeight: note._layout_autoHeight !== false, - title: StrCast(note.title) + '#' + (note.embeddingCount = NumCast(note.embeddingCount) + 1), - }); - textDoc.layout_fieldKey = 'layout_' + note.title; - textDoc[textDoc.layout_fieldKey] = note; - if (pivotField) { - textDoc[pivotField] = pivotValue; - } - docTextAdder(textDoc); - }, 'create quick note'), - icon: StrCast(note.icon) as IconProp, - })) as ContextMenuProps[], - icon: 'sticky-note', - }); - const userDocList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[1]?.data) - .filter(btnDoc => !btnDoc.hidden) - .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) - .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title) - .map(dragDoc => ({ - description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), - event: undoable(() => { - const newDoc = DocUtils.delegateDragFactory(dragDoc); - if (newDoc) { - newDoc.author = ClientUtils.CurrentUserEmail(); - newDoc.x = x; - newDoc.y = y; - EquationBox.SelectOnLoad = newDoc[Id]; - if (newDoc.type === DocumentType.RTF) FormattedTextBox.SetSelectOnLoad(newDoc); - if (pivotField) { - newDoc[pivotField] = pivotValue; - } - docAdder?.(newDoc); - } - }, StrCast(dragDoc.title)), - icon: Doc.toIcon(dragDoc), - })) as ContextMenuProps[]; - ContextMenu.Instance.addItem({ - description: 'User Templates', - subitems: userDocList, - icon: 'file', - }); - } - - // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) - - /** - * Applies a template to a Doc and logs the action with the UndoManager - * If the template already exists and has been registered, it can be specified by it's signature name (e.g., 'icon' not 'layout_icon'). - * Alternatively, the signature can be omitted and the template can be provided. - * @param doc the Doc to apply the template to. - * @param creator a function that will create the template if it doesn't exist - * @param templateSignature the signature name for a template that has already been created and registered on the userDoc. (can be "" if template is provide) - * @param template the template to use (optional if templateSignature is provided) - * @returns doc - */ - export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', template?: Doc) { - const batch = UndoManager.StartBatch('makeCustomViewClicked'); - createCustomView(doc, creator, templateSignature || StrCast(template?.title), template); - batch.end(); - return doc; - } - export function findTemplate(templateName: string, type: string) { - let docLayoutTemplate: Opt; - const iconViews = DocListCast(Cast(Doc.UserDoc().template_icons, Doc, null)?.data); - const templBtns = DocListCast(Cast(Doc.UserDoc().template_buttons, Doc, null)?.data); - const noteTypes = DocListCast(Cast(Doc.UserDoc().template_notes, Doc, null)?.data); - const userTypes = DocListCast(Cast(Doc.UserDoc().template_user, Doc, null)?.data); - const clickFuncs = DocListCast(Cast(Doc.UserDoc().template_clickFuncs, Doc, null)?.data); - const allTemplates = iconViews - .concat(templBtns) - .concat(noteTypes) - .concat(userTypes) - .concat(clickFuncs) - .map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc) - .filter(doc => doc.isTemplateDoc); - // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized - // first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on - !docLayoutTemplate && - allTemplates.forEach(tempDoc => { - StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc); - }); - !docLayoutTemplate && - allTemplates.forEach(tempDoc => { - StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc); - }); - return docLayoutTemplate; - } - export function createCustomView(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) { - const templateName = templateSignature.replace(/\(.*\)/, ''); - doc.layout_fieldKey = 'layout_' + (templateSignature || (docLayoutTemplate?.title ?? '')); - // eslint-disable-next-line no-param-reassign - docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.isGroup && doc.transcription ? 'transcription' : doc.type)); - - const customName = 'layout_' + templateSignature; - const _width = NumCast(doc._width); - const _height = NumCast(doc._height); - const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _layout_autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _layout_showSidebar: false }; - - if (docLayoutTemplate) { - if (docLayoutTemplate !== doc[customName]) { - Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); - } - } else { - let fieldTemplate: Opt; - if (doc.data instanceof RichTextField || typeof doc.data === 'string') { - fieldTemplate = Docs.Create.TextDocument('', options); - } else if (doc.data instanceof PdfField) { - fieldTemplate = Docs.Create.PdfDocument('http://www.msn.com', options); - } else if (doc.data instanceof VideoField) { - fieldTemplate = Docs.Create.VideoDocument('http://www.cs.brown.edu', options); - } else if (doc.data instanceof AudioField) { - fieldTemplate = Docs.Create.AudioDocument('http://www.cs.brown.edu', options); - } else if (doc.data instanceof ImageField) { - fieldTemplate = Docs.Create.ImageDocument('http://www.cs.brown.edu', options); - } - const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + '(' + doc.title + ')', isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); - fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); - docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); - } - } - export function makeCustomView(doc: Doc, custom: boolean, layout: string) { - Doc.setNativeView(doc); - if (custom) { - makeCustomViewClicked(doc, Docs.Create.StackingDocument, layout, undefined); - } - } - export function iconify(doc: Doc) { - const layoutFieldKey = Cast(doc.layout_fieldKey, 'string', null); - DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined); - if (layoutFieldKey && layoutFieldKey !== 'layout' && layoutFieldKey !== 'layout_icon') doc.deiconifyLayout = layoutFieldKey.replace('layout_', ''); - } - - export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) { - runInAction(() => { - docList.forEach((doc, i) => { - const d = doc; - DocUtils.iconify(d); - d.x = Math.cos((Math.PI * 2 * i) / docList.length) * size - size; - d.y = Math.sin((Math.PI * 2 * i) / docList.length) * size - size; - d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - }); - }); - if (create) { - const newCollection = Docs.Create.PileDocument(docList, { title: 'pileup', _freeform_noZoom: true, x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, dragWhenActive: true, _layout_fitWidth: false }); - newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size; - newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size; - newCollection._width = newCollection._height = size * 2; - return newCollection; - } - return undefined; - } - export function makeIntoPortal(doc: Doc, layoutDoc: Doc, allLinks: Doc[]) { - const portalLink = allLinks.find(d => d.link_anchor_1 === doc && d.link_relationship === 'portal to:portal from'); - if (!portalLink) { - DocUtils.MakeLink( - doc, - Docs.Create.FreeformDocument([], { - _width: NumCast(layoutDoc._width) + 10, - _height: Math.max(NumCast(layoutDoc._height), NumCast(layoutDoc._width) + 10), - _isLightbox: true, - _layout_fitWidth: true, - title: StrCast(doc.title) + ' [Portal]', - }), - { link_relationship: 'portal to:portal from' } - ); - } - doc.followLinkLocation = OpenWhere.lightbox; - doc.onClick = FollowLinkScript(); - } - - export function LeavePushpin(doc: Doc, annotationField: string) { - if (doc.followLinkToggle) return undefined; - const context = Cast(doc.embedContainer, Doc, null) ?? Cast(doc.annotationOn, Doc, null); - 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: '', - annotationOn: Cast(doc.annotationOn, Doc, null), - followLinkToggle: true, - icon: 'map-pin', - x: Cast(doc.x, 'number', null), - y: Cast(doc.y, 'number', null), - backgroundColor: '#ACCEF7', - layout_hideAllLinks: true, - _width: 15, - _height: 15, - _xPadding: 0, - onClick: FollowLinkScript(), - _timecodeToShow: Cast(doc._timecodeToShow, 'number', null), - }); - Doc.AddDocToList(context, annotationField, pushpin); - DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, ''); - doc._timecodeToShow = undefined; - return pushpin; - } - return undefined; - } - - // /** - // * - // * @param dms Degree Minute Second format exif gps data - // * @param ref ref that determines negativity of decimal coordinates - // * @returns a decimal format of gps latitude / longitude - // */ - // function getDecimalfromDMS(dms?: number[], ref?: string) { - // if (dms && ref) { - // let degrees = dms[0] / dms[1]; - // let minutes = dms[2] / dms[3] / 60.0; - // let seconds = dms[4] / dms[5] / 3600.0; - - // if (['S', 'W'].includes(ref)) { - // degrees = -degrees; minutes = -minutes; seconds = -seconds - // } - // return (degrees + minutes + seconds).toFixed(5); - // } - // } - - function ConvertDMSToDD(degrees: number, minutes: number, seconds: number, direction: string) { - let dd = degrees + minutes / 60 + seconds / (60 * 60); - if (direction === 'S' || direction === 'W') { - dd *= -1; - } // Don't do anything for N or E - return dd; - } - - export function assignImageInfo(result: Upload.FileInformation, protoIn: Doc) { - const proto = protoIn; - if (Upload.isImageInformation(result)) { - const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim); - const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase(); - proto.data_nativeOrientation = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined); - proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; - proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); - if (NumCast(proto.data_nativeOrientation) >= 5) { - proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; - proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); - } - proto.data_exif = JSON.stringify(result.exifData?.data); - proto.data_contentSize = result.contentSize; - // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates - const latitude = result.exifData?.data?.GPSLatitude; - const latitudeDirection = result.exifData?.data?.GPSLatitudeRef; - const longitude = result.exifData?.data?.GPSLongitude; - const longitudeDirection = result.exifData?.data?.GPSLongitudeRef; - if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) { - proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection); - proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection); - } - } - } - - async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions, overwriteDoc?: Doc) { - if (result instanceof Error) { - alert(`Upload failed: ${result.message}`); - return; - } - const full = { ...options, _width: 400, title: name }; - const pathname = result.accessPaths.agnostic.client; - const doc = await DocUtils.DocumentFromType(type, pathname, full, overwriteDoc); - if (doc) { - const proto = Doc.GetProto(doc); - proto.text = result.rawText; - !(result instanceof Error) && DocUtils.assignImageInfo(result, proto); - if (Upload.isVideoInformation(result)) { - proto.data_duration = result.duration; - } - if (overwriteDoc) { - LoadingBox.removeCurrentlyLoading(overwriteDoc); - } - generatedDocuments.push(doc); - } - } - - export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) { - const defaultTextTemplate = DocCast(Doc.UserDoc().defaultTextLayout); - const tbox = Docs.Create.TextDocument('', { - annotationOn, - backgroundColor, - x, - y, - title, - ...(defaultTextTemplate - ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance - : { - _width: width || 200, - _height: 35, - _layout_centered: BoolCast(Doc.UserDoc()._layout_centered), - _layout_fitWidth: true, - _layout_autoHeight: true, - }), - }); - - if (defaultTextTemplate) { - tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title); - Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = defaultTextTemplate; // set the text doc's layout to render with the text template - tbox[DocData].proto = defaultTextTemplate; // and also set the text doc to inherit from the template (this allows the template to specify default field values) - } - return tbox; - } - - export function uploadYoutubeVideoLoading(videoId: string, options: DocumentOptions, overwriteDoc?: Doc) { - const generatedDocuments: Doc[] = []; - Networking.UploadYoutubeToServer(videoId, overwriteDoc?.[Id]).then(upfiles => { - const { - source: { newFilename, mimetype }, - result, - } = upfiles.lastElement(); - if ((result as any).message) { - if (overwriteDoc) { - overwriteDoc.isLoading = false; - overwriteDoc.loadingError = (result as any).message; - LoadingBox.removeCurrentlyLoading(overwriteDoc); - } - } else newFilename && processFileupload(generatedDocuments, newFilename, mimetype ?? '', result, options, overwriteDoc); - }); - } - - /** - * uploadFilesToDocs will take in an array of Files, and creates documents for the - * new files. - * - * @param files an array of files that will be uploaded - * @param options options to use while uploading - * @returns - */ - export async function uploadFilesToDocs(files: File[], options: DocumentOptions) { - const generatedDocuments: Doc[] = []; - - // These files do not have overwriteDocs, so we do not set the guid and let the client generate one. - const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({ file })); - - const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs); - upfiles.forEach(({ source: { newFilename, mimetype }, result }) => { - newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options); - }); - return generatedDocuments; - } - - export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) { - const generatedDocuments: Doc[] = []; - // Since this file has an overwriteDoc, we can set the client tracking guid to the overwriteDoc's guid. - Networking.UploadFilesToServer([{ file, guid: overwriteDoc[Id] }]).then(upfiles => { - const { - source: { newFilename, mimetype }, - result, - } = upfiles.lastElement() ?? { source: { newFilename: '', mimetype: '' }, result: { message: 'upload failed' } }; - if ((result as any).message) { - if (overwriteDoc) { - overwriteDoc.loadingError = (result as any).message; - LoadingBox.removeCurrentlyLoading(overwriteDoc); - } - } else newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options, overwriteDoc); - }); - } - - // copies the specified drag factory document - export function copyDragFactory(dragFactory: Doc) { - if (!dragFactory) return undefined; - const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); - if (ndoc && dragFactory.dragFactory_count !== undefined) { - dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1; - Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(), true); - } - - return ndoc; - } - export function delegateDragFactory(dragFactory: Doc) { - const ndoc = Doc.MakeDelegateWithProto(dragFactory); - if (ndoc && dragFactory.dragFactory_count !== undefined) { - dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1; - Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(); - } - return ndoc; - } - - export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') { - const { clone, map, linkMap } = await Doc.MakeClone(doc); - const proms = new Set(); - function replacer(key: any, value: any) { - if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined; - if (value?.__type === 'image') { - const extension = value.url.replace(/.*\./, ''); - proms.add(value.url.replace('.' + extension, '_o.' + extension)); - return SerializationHelper.Serialize(new ImageField(value.url)); - } - if (value?.__type === 'pdf') { - proms.add(value.url); - return SerializationHelper.Serialize(new PdfField(value.url)); - } - if (value?.__type === 'audio') { - proms.add(value.url); - return SerializationHelper.Serialize(new AudioField(value.url)); - } - if (value?.__type === 'video') { - proms.add(value.url); - return SerializationHelper.Serialize(new VideoField(value.url)); - } - if ( - value instanceof Doc || - value instanceof ScriptField || - value instanceof RichTextField || - value instanceof InkField || - value instanceof CsvField || - value instanceof WebField || - value instanceof DateField || - value instanceof ProxyField || - value instanceof ComputedField - ) { - return SerializationHelper.Serialize(value); - } - if (value instanceof Array && key !== ListFieldName && key !== InkDataFieldName) return { fields: value, __type: 'list' }; - return value; - } - - const docs: { [id: string]: any } = {}; - const links: { [id: string]: any } = {}; - Array.from(map.entries()).forEach(f => { - docs[f[0]] = f[1]; - }); - Array.from(linkMap.entries()).forEach(l => { - links[l[0]] = l[1]; - }); - const jsonDocs = JSON.stringify({ id: clone[Id], docs, links }, decycle(replacer)); - - const zip = new JSZip(); - let count = 0; - const promArr = Array.from(proms) - .filter(url => url?.startsWith('/files')) - .map(url => url.replace('/', '')); // window.location.origin)); - console.log(promArr.length); - if (!promArr.length) { - zip.file('docs.json', jsonDocs); - zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename)); - } else - promArr.forEach((url, i) => { - // loading a file and add it in a zip file - JSZipUtils.getBinaryContent(window.location.origin + '/' + url, (err: any, data: any) => { - if (err) throw err; // or handle the error - // // Generate a directory within the Zip file structure - // const assets = zip.folder("assets"); - // assets.file(filename, data, {binary: true}); - const assetPathOnServer = promArr[i].replace(window.location.origin + '/', '').replace(/\//g, '%%%'); - zip.file(assetPathOnServer, data, { binary: true }); - console.log(' => ' + url); - if (++count === promArr.length) { - zip.file('docs.json', jsonDocs); - zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename)); - // const a = document.createElement("a"); - // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`); - // a.href = url; - // a.download = `DocExport-${this.props.Document[Id]}.zip`; - // a.click(); - } - }); - }); - } -} - -ScriptingGlobals.add('Docs', Docs); -// eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc, asDelegate?: boolean) { - return dragFactory instanceof Doc ? (asDelegate ? DocUtils.delegateDragFactory(dragFactory) : DocUtils.copyDragFactory(dragFactory)) : dragFactory; -}); -// eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function makeDelegate(proto: any) { - const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title }); - return d; -}); -// eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function generateLinkTitle(link: Doc) { - const linkAnchor1title = link.link_anchor_1 && link.link_anchor_1 !== link ? Cast(link.link_anchor_1, Doc, null)?.title : ''; - const linkAnchor2title = link.link_anchor_2 && link.link_anchor_2 !== link ? Cast(link.link_anchor_2, Doc, null)?.title : ''; - const relation = link.link_relationship || 'to'; - return `${linkAnchor1title} (${relation}) ${linkAnchor2title}`; -}); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 00279e7e1..96f5172bc 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -8,28 +8,28 @@ import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; -import { ScriptField } from "../../fields/ScriptField"; +import { FollowLinkScript, ScriptField } from "../../fields/ScriptField"; import { Cast, DateCast, DocCast, StrCast } from "../../fields/Types"; import { WebField, nullAudio } from "../../fields/URLField"; import { SetCachedGroups, SharingPermissions } from "../../fields/util"; import { Gestures } from "../../pen-gestures/GestureTypes"; import { DocServer } from "../DocServer"; +import { DocUtils } from '../documents/DocUtils'; import { CollectionViewType, DocumentType } from "../documents/DocumentTypes"; -import { DocUtils, Docs, DocumentOptions, FInfo, FInfoFieldType } from "../documents/Documents"; +import { Docs, DocumentOptions, FInfo, FInfoFieldType } from "../documents/Documents"; import { DashboardView } from "../views/DashboardView"; import { OverlayView } from "../views/OverlayView"; import { CollectionTreeView, TreeViewType } from "../views/collections/CollectionTreeView"; import { Colors } from "../views/global/globalEnums"; import { mediaState } from "../views/nodes/AudioBox"; -import { OpenWhere } from "../views/nodes/DocumentView"; import { ButtonType, FontIconBox } from "../views/nodes/FontIconBox/FontIconBox"; import { ImageBox } from "../views/nodes/ImageBox"; import { LabelBox } from "../views/nodes/LabelBox"; +import { OpenWhere } from "../views/nodes/OpenWhere"; import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; import { DragManager } from "./DragManager"; import { dropActionType } from "./DropActionTypes"; import { MakeTemplate } from "./DropConverter"; -import { FollowLinkScript } from "./LinkFollower"; import { LinkManager, UPDATE_SERVER_CACHE } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; import { ColorScheme } from "./SettingsManager"; diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 9026b368f..97ee628e2 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -4,6 +4,7 @@ import * as interpreter from 'words-to-numbers'; import type {} from '@types/dom-speech-recognition'; import { ClientUtils } from '../../ClientUtils'; import { Doc, Opt } from '../../fields/Doc'; +import { DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; import { listSpec } from '../../fields/Schema'; @@ -12,10 +13,10 @@ import { AudioField, ImageField } from '../../fields/URLField'; import { DocumentType } from '../documents/DocumentTypes'; import { Docs } from '../documents/Documents'; import { DictationOverlay } from '../views/DictationOverlay'; -import { DocumentView, OpenWhere } from '../views/nodes/DocumentView'; +import { DocumentView } from '../views/nodes/DocumentView'; +import { OpenWhere } from '../views/nodes/OpenWhere'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; -import { DocData } from '../../fields/DocSymbols'; /** * This namespace provides a singleton instance of a manager that diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index a893fe627..8f0c2b0bf 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -10,10 +10,11 @@ import { GetEffectiveAcl } from '../../fields/util'; import { CollectionViewType } from '../documents/DocumentTypes'; import { TabDocView } from '../views/collections/TabDocView'; import { LightboxView } from '../views/LightboxView'; -import { DocumentView, DocumentViewInternal, OpenWhere } from '../views/nodes/DocumentView'; -import { FocusViewOptions } from '../views/nodes/FieldView'; +import { DocumentView, DocumentViewInternal } from '../views/nodes/DocumentView'; +import { FocusViewOptions } from '../views/nodes/FocusViewOptions'; import { KeyValueBox } from '../views/nodes/KeyValueBox'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; +import { OpenWhere } from '../views/nodes/OpenWhere'; import { PresBox } from '../views/nodes/trails'; import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 3890b7845..34f54be2e 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -18,14 +18,13 @@ import { action, observable, runInAction } from 'mobx'; import { ClientUtils } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; import { DateField } from '../../fields/DateField'; -import { Doc, FieldType, Opt, StrListCast } from '../../fields/Doc'; +import { CreateLinkToActiveAudio, Doc, FieldType, Opt, StrListCast } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { ScriptField } from '../../fields/ScriptField'; import { ScriptCast } from '../../fields/Types'; -import { DocUtils, Docs } from '../documents/Documents'; -import { CollectionFreeFormDocumentView } from '../views/nodes/CollectionFreeFormDocumentView'; +import { Docs } from '../documents/Documents'; import { DocumentView } from '../views/nodes/DocumentView'; import { dropActionType } from './DropActionTypes'; import { ScriptingGlobals } from './ScriptingGlobals'; @@ -74,6 +73,7 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () } export namespace DragManager { + export const dragClassName = 'collectionFreeFormDocumentView-container'; let dragDiv: HTMLDivElement; let dragLabel: HTMLDivElement; export let StartWindowDrag: Opt<(e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => boolean>; @@ -139,11 +139,11 @@ export namespace DragManager { constructor(dragDoc: Doc[], dropAction?: dropActionType) { this.draggedDocuments = dragDoc; this.droppedDocuments = []; - this.draggedViews = []; this.offset = [0, 0]; this.dropAction = dropAction; } - draggedViews: DocumentView[]; + dragEnding?: () => void; + dragStarting?: () => void; draggedDocuments: Doc[]; droppedDocuments: Doc[]; treeViewDoc?: Doc; @@ -157,6 +157,7 @@ export namespace DragManager { removeDocument?: RemoveFunction; isDocDecorationMove?: boolean; // Flags that Document decorations are used to drag document which allows suppression of onDragStart scripts } + Doc.SetDocDragDataName(DocumentDragData.name); export class LinkDragData { constructor(dragView: DocumentView, linkSourceGetAnchor: () => Doc) { this.linkDragView = dragView; @@ -224,12 +225,12 @@ export namespace DragManager { export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => any) { const addAudioTag = (dropDoc: any) => { dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField()); - dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc); + dropDoc instanceof Doc && CreateLinkToActiveAudio(() => dropDoc); return dropDoc; }; const finishDrag = async (e: DragCompleteEvent) => { const { docDragData } = e; - setTimeout(() => dragData.draggedViews.forEach(view => view.props.dragEnding?.())); + setTimeout(() => dragData.dragEnding?.()); onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails if (docDragData && !docDragData.droppedDocuments.length) { docDragData.dropAction = dragData.userDropAction || dragData.dropAction; @@ -265,7 +266,7 @@ export namespace DragManager { }; dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded StartDrag(eles, dragData, downX, downY, options, finishDrag); - dragData.draggedViews.forEach(view => view.props.dragStarting?.()); + dragData.dragStarting?.(); return true; } @@ -418,7 +419,7 @@ export namespace DragManager { // if the parent isn't a freeform view, then the element's width and height are presumed to match the acutal doc's dimensions (eg, dragging from import sidebar menu) let rotation: number | undefined; for (let parEle: HTMLElement | null | undefined = ele.parentElement; parEle; parEle = parEle?.parentElement) { - if (parEle.className === CollectionFreeFormDocumentView.CollectionFreeFormDocViewClassName) { + if (parEle.className === DragManager.dragClassName) { rotation = (rotation ?? 0) + Number(parEle.style.transform.replace(/.*rotate\(([-0-9.e]*)deg\).*/, '$1') || 0); } parEle = parEle.parentElement; @@ -630,7 +631,7 @@ export namespace DragManager { dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options, () => cleanupDrag(false)); }; const cleanupDrag = action((undo: boolean) => { - (dragData as DocumentDragData).draggedViews?.forEach(view => view.props.dragEnding?.()); + (dragData as DocumentDragData).dragEnding?.(); hideDragShowOriginalElements(false); document.removeEventListener('pointermove', moveHandler, true); document.removeEventListener('pointerup', upHandler, true); diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 8d84dbad8..e1a503ac9 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -19,8 +19,8 @@ import { ObservableReactComponent } from '../views/ObservableReactComponent'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; import './GroupManager.scss'; import { GroupMemberView } from './GroupMemberView'; -import { SettingsManager } from './SettingsManager'; import { SharingManager, User } from './SharingManager'; +import { SnappingManager } from './SnappingManager'; /** * Interface for options for the react-select component @@ -289,7 +289,7 @@ export class GroupManager extends ObservableReactComponent<{}> { */ private get groupCreationModal() { const contents = ( -
+

New Group @@ -384,7 +384,7 @@ export class GroupManager extends ObservableReactComponent<{}> { const groups = this.groupSort === 'ascending' ? this.allGroups.sort(sortGroups) : this.groupSort === 'descending' ? this.allGroups.sort(sortGroups).reverse() : this.allGroups; return ( -

+
{this.groupCreationModal} {this.currentGroup ? ( { const hasEditAccess = GroupManager.Instance.hasEditAccess(this.group); return !this.group ? null : ( -
+
containerDocContext.length && (dv.ComponentView as PresBox).PlayTrail(containerDocContext))) { - PresBox.OpenPresMinimized(target, [0, 0]); + if (!DocumentManager.Instance.AddViewRenderedCb(target, dv => containerDocContext.length && dv.ComponentView?.playTrail?.(containerDocContext))) { + alert('OPEN Pres minimized'); + // PresBox.OpenPresMinimized(target, [0, 0]); } finished?.(); } else { @@ -138,9 +137,6 @@ ScriptingGlobals.add(function followLink(doc: Doc, altKey: boolean) { return LinkFollower.FollowLink(undefined, doc, altKey) ? undefined : { select: true }; }); -export function FollowLinkScript() { - return ScriptField.MakeScript('return followLink(this,altKey)', { altKey: 'boolean' }); -} export function IsFollowLinkScript(field: FieldResult) { return ScriptCast(field)?.script.originalScript.includes('return followLink('); } diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx index 05fb849fd..248fda7e3 100644 --- a/src/client/util/RTFMarkup.tsx +++ b/src/client/util/RTFMarkup.tsx @@ -2,7 +2,7 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { MainViewModal } from '../views/MainViewModal'; -import { SettingsManager } from './SettingsManager'; +import { SnappingManager } from './SnappingManager'; @observer export class RTFMarkup extends React.Component<{}> { @@ -10,6 +10,10 @@ export class RTFMarkup extends React.Component<{}> { static Instance: RTFMarkup; @observable private isOpen = false; // whether the SharingManager modal is open or not + public setOpen = action((status: boolean) => { + this.isOpen = status; + }); + constructor(props: {}) { super(props); makeObservable(this); @@ -21,7 +25,7 @@ export class RTFMarkup extends React.Component<{}> { */ @computed get cheatSheet() { return ( -
+

(@wiki:phrase) {` display wikipedia page for entered text (terminate with carriage return)`} @@ -122,24 +126,14 @@ export class RTFMarkup extends React.Component<{}> { ); } - @action - public open = () => { - this.isOpen = true; - }; - - @action - public close = () => { - this.isOpen = false; - }; - render() { return ( this.setOpen(false)} /> ); } diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts index 2c8fdf483..dcc6aabab 100644 --- a/src/client/util/ReplayMovements.ts +++ b/src/client/util/ReplayMovements.ts @@ -2,7 +2,7 @@ import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { Doc, IdToDoc } from '../../fields/Doc'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; -import { OpenWhereMod } from '../views/nodes/DocumentView'; +import { OpenWhereMod } from '../views/nodes/OpenWhere'; import { VideoBox } from '../views/nodes/VideoBox'; import { DocumentManager } from './DocumentManager'; import { Movement, Presentation } from './TrackMovements'; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 8347844f7..8cb97fe50 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -14,9 +14,7 @@ import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types'; import { DocServer } from '../DocServer'; import { Networking } from '../Network'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; -import { GestureOverlay } from '../views/GestureOverlay'; import { MainViewModal } from '../views/MainViewModal'; -import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { GroupManager } from './GroupManager'; import './SettingsManager.scss'; import { SnappingManager } from './SnappingManager'; @@ -256,25 +254,14 @@ export class SettingsManager extends React.Component<{}> { size={Size.XSMALL} color={SettingsManager.userColor} /> - { - FontIconBox.ShowIconLabels = !FontIconBox.ShowIconLabels; - }} - toggleStatus={FontIconBox.ShowIconLabels} - size={Size.XSMALL} - color={SettingsManager.userColor} - /> { - GestureOverlay.RecognizeGestures = !GestureOverlay.RecognizeGestures; + Doc.UserDoc().recognizeGestures = !Doc.UserDoc().recognizeGestures; }} - toggleStatus={GestureOverlay.RecognizeGestures} + toggleStatus={BoolCast(Doc.UserDoc().recognizeGestures)} size={Size.XSMALL} color={SettingsManager.userColor} /> diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 19a7948e6..b0140b73c 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -18,7 +18,6 @@ import { Id } from '../../fields/FieldSymbols'; import { StrCast } from '../../fields/Types'; import { GetEffectiveAcl, SharingPermissions, TraceMobx, distributeAcls, normalizeEmail } from '../../fields/util'; import { DocServer } from '../DocServer'; -import { DictationOverlay } from '../views/DictationOverlay'; import { MainViewModal } from '../views/MainViewModal'; import { DocumentView } from '../views/nodes/DocumentView'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; @@ -27,8 +26,8 @@ import { GroupManager, UserOptions } from './GroupManager'; import { GroupMemberView } from './GroupMemberView'; import { SearchUtil } from './SearchUtil'; import { SelectionManager } from './SelectionManager'; -import { SettingsManager } from './SettingsManager'; import './SharingManager.scss'; +import { SnappingManager } from './SnappingManager'; import { undoable } from './UndoManager'; export interface User { @@ -238,14 +237,14 @@ export class SharingManager extends React.Component<{}> { const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-'; return !permissions ? null : ( -

+
{StrCast(group.title)}
  {group instanceof Doc ? ( } size={Size.XSMALL} - color={SettingsManager.userColor} + color={SnappingManager.userColor} onClick={action(() => { GroupManager.Instance.currentGroup = group; })} @@ -279,10 +278,10 @@ export class SharingManager extends React.Component<{}> {
-

+

window.open('https://brown-dash.github.io/Dash-Documentation/features/collaboration/', '_blank')}> window.open('https://brown-dash.github.io/Dash-Documentation/features/collaboration/', '_blank')} />
@@ -290,10 +289,10 @@ export class SharingManager extends React.Component<{}> { {this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}

-
-
{admin ? (
@@ -335,7 +334,7 @@ export class SharingManager extends React.Component<{}> {
-
@@ -514,7 +513,6 @@ export class SharingManager extends React.Component<{}> { setTimeout( action(() => { // this.copied = false; - DictationOverlay.Instance.hasActiveModal = false; this.targetDoc = undefined; }), 500 @@ -528,7 +526,6 @@ export class SharingManager extends React.Component<{}> { runInAction(() => { this.targetDocView = target; this.targetDoc = targetDoc || target?.Document; - DictationOverlay.Instance.hasActiveModal = true; this.isOpen = this.targetDoc !== undefined; this.permissions = SharingPermissions.Augment; this.upgradeNested = true; diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index c4855d2e7..d92a73b63 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -15,9 +15,10 @@ import { PrefetchProxy } from '../../fields/Proxy'; import { listSpec } from '../../fields/Schema'; import { ScriptField } from '../../fields/ScriptField'; import { Cast, ImageCast, StrCast } from '../../fields/Types'; -import { normalizeEmail } from '../../fields/util'; +import { SharingPermissions, inheritParentAcls, normalizeEmail } from '../../fields/util'; import { DocServer } from '../DocServer'; -import { DocUtils, Docs, DocumentOptions } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; +import { Docs, DocumentOptions } from '../documents/Documents'; import { dropActionType } from '../util/DropActionTypes'; import { HistoryUtil } from '../util/History'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; @@ -36,11 +37,51 @@ enum DashboardGroup { SharedDashboards, } +export type DocConfig = { + doc: Doc; + initialWidth?: number; + path?: Doc[]; +}; + // DashboardView is the view with the dashboard previews, rendered when the app first loads @observer export class DashboardView extends ObservableReactComponent<{}> { public static _urlState: HistoryUtil.DocUrl; + public static makeDocumentConfig(document: Doc, panelName?: string, width?: number, keyValue?: boolean) { + return { + type: 'react-component', + component: 'DocumentFrameRenderer', + title: document.title, + width: width, + props: { + documentId: document[Id], + keyValue, + panelName, // name of tab that can be used to close or replace its contents + }, + }; + } + static StandardCollectionDockingDocument(configs: Array, options: DocumentOptions, id?: string, type: string = 'row') { + const layoutConfig = { + content: [ + { + type: type, + content: [...configs.map(config => DashboardView.makeDocumentConfig(config.doc, undefined, config.initialWidth))], + }, + ], + }; + const doc = Docs.Create.DockDocument( + configs.map(c => c.doc), + JSON.stringify(layoutConfig), + ClientUtils.CurrentUserEmail() === 'guest' ? options : { acl_Guest: SharingPermissions.View, ...options }, + id + ); + configs.forEach(c => { + Doc.SetContainer(c.doc, doc); + inheritParentAcls(doc, c.doc, false); + }); + return doc; + } constructor(props: any) { super(props); makeObservable(this); @@ -389,7 +430,7 @@ export class DashboardView extends ObservableReactComponent<{}> { const title = name || `Dashboard ${dashboardCount}`; const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row'); + const dashboardDoc = DashboardView.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row'); Doc.AddDocToList(Doc.MyHeaderBar, 'data', freeformDoc, undefined, undefined, true); dashboardDoc.pane_count = 1; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 391c2f694..a0e3d2ddd 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -12,8 +12,10 @@ import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PinProps } from './PinFuncs'; -import { DocumentView, OpenWhere } from './nodes/DocumentView'; -import { FieldViewProps, FocusViewOptions } from './nodes/FieldView'; +import { DocumentView } from './nodes/DocumentView'; +import { FocusViewOptions } from './nodes/FocusViewOptions'; +import { FieldViewProps } from './nodes/FieldView'; +import { OpenWhere } from './nodes/OpenWhere'; // import { DocUtils } from '../documents/Documents'; /** @@ -41,6 +43,7 @@ export interface ViewBoxInterface { onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) + playTrail?: (docs: Doc[]) => void; playFrom?: (time: number, endTime?: number) => void; Pause?: () => void; // pause a media document (eg, audio/video) IsPlaying?: () => boolean; // is a media document playing diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index c5358dffe..dd744f272 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -14,7 +14,7 @@ import { returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from ' import { emptyFunction } from '../../Utils'; import { Doc } from '../../fields/Doc'; import { Cast, DocCast } from '../../fields/Types'; -import { DocUtils } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; import { CalendarManager } from '../util/CalendarManager'; import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; @@ -22,15 +22,16 @@ import { IsFollowLinkScript } from '../util/LinkFollower'; import { SelectionManager } from '../util/SelectionManager'; import { SharingManager } from '../util/SharingManager'; import { UndoManager, undoBatch } from '../util/UndoManager'; -import { PinProps } from './DocComponent'; import './DocumentButtonBar.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; +import { PinProps } from './PinFuncs'; import { TemplateMenu } from './TemplateMenu'; import { TabDocView } from './collections/TabDocView'; import { Colors } from './global/globalEnums'; import { LinkPopup } from './linking/LinkPopup'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; -import { DocumentView, DocumentViewInternal, OpenWhere } from './nodes/DocumentView'; +import { DocumentView, DocumentViewInternal } from './nodes/DocumentView'; +import { OpenWhere } from './nodes/OpenWhere'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; @observer diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 1e6eb1aeb..ef493fb69 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -33,9 +33,10 @@ import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { Colors } from './global/globalEnums'; import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView'; -import { DocumentView, OpenWhereMod } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; import { ImageBox } from './nodes/ImageBox'; import { KeyValueBox } from './nodes/KeyValueBox'; +import { OpenWhereMod } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; interface DocumentDecorationsProps { @@ -448,7 +449,7 @@ export class DocumentDecorations extends ObservableReactComponent CollectionFreeFormDocumentView.from(docView)?.CollectionFreeFormView?.dragStarting(false, false)); + SelectionManager.Views.forEach(docView => CollectionFreeFormView.from(docView)?.dragStarting(false, false)); }; projectDragToAspect = (e: PointerEvent, docView: DocumentView, fixedAspect: number) => { diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index bf5b51a2d..0d1573ea3 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -4,17 +4,6 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, Opt } from '../../fields/Doc'; -import { InkData, InkTool } from '../../fields/InkField'; -import { BoolCast, NumCast } from '../../fields/Types'; -import MobileInkOverlay from '../../mobile/MobileInkOverlay'; -import { Gestures } from '../../pen-gestures/GestureTypes'; -import { GestureUtils } from '../../pen-gestures/GestureUtils'; -import { MobileInkOverlayContent } from '../../server/Message'; -import { InteractionUtils } from '../util/InteractionUtils'; -import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { Transform } from '../util/Transform'; -import './GestureOverlay.scss'; import { ActiveArrowEnd, ActiveArrowScale, @@ -24,12 +13,24 @@ import { ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, + Doc, + Opt, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, -} from './InkingStroke'; +} from '../../fields/Doc'; +import { InkData, InkTool } from '../../fields/InkField'; +import { NumCast } from '../../fields/Types'; +// import MobileInkOverlay from '../../mobile/MobileInkOverlay'; +import { Gestures } from '../../pen-gestures/GestureTypes'; +import { GestureUtils } from '../../pen-gestures/GestureUtils'; +// import { MobileInkOverlayContent } from '../../server/Message'; +import { InteractionUtils } from '../util/InteractionUtils'; +import { ScriptingGlobals } from '../util/ScriptingGlobals'; +import { Transform } from '../util/Transform'; +import './GestureOverlay.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { DocumentView } from './nodes/DocumentView'; @@ -49,13 +50,6 @@ export class GestureOverlay extends ObservableReactComponent = undefined; @observable public SavedColor?: string = undefined; @observable public SavedWidth?: number = undefined; @@ -80,7 +74,7 @@ export class GestureOverlay extends ObservableReactComponent(); private _d1: Doc | undefined; @@ -158,7 +152,7 @@ export class GestureOverlay extends ObservableReactComponent 2 && GestureUtils.GestureRecognizer.Recognize([points]); let actionPerformed = false; - if (GestureOverlay.RecognizeGestures && result && result.Score > 0.7) { + if (Doc.UserDoc().recognizeGestures && result && result.Score > 0.7) { switch (result.Name) { case Gestures.Line: case Gestures.Triangle: @@ -502,15 +496,10 @@ export class GestureOverlay extends ObservableReactComponent { - this.showMobileInkOverlay = content.enableOverlay; - }; - render() { return (
- {this.showMobileInkOverlay ? : null} + {/* {this.showMobileInkOverlay ? : null} */} {this.elements}
{}); nudge = (x: number, y: number, label: string) => { - const nudgeable = SelectionManager.Views.some(dv => dv.CollectionFreeFormDocumentView?.nudge); - nudgeable && UndoManager.RunInBatch(() => SelectionManager.Views.map(dv => dv.CollectionFreeFormDocumentView?.nudge(x, y)), label); + const nudgeable = SelectionManager.Views.some(dv => CollectionFreeFormDocumentView.from(dv)?.nudge); + nudgeable && UndoManager.RunInBatch(() => SelectionManager.Views.map(dv => CollectionFreeFormDocumentView.from(dv)?.nudge(x, y)), label); return { stopPropagation: nudgeable, preventDefault: nudgeable }; }; @@ -105,7 +106,7 @@ export class KeyManager { case 'g': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { const selected = SelectionManager.Views; - const cv = selected.reduce((col, dv) => (!col || dv.CollectionFreeFormView === col ? dv.CollectionFreeFormView : undefined), undefined as undefined | CollectionFreeFormView); + const cv = selected.reduce((col, dv) => (!col || CollectionFreeFormView.from(dv) === col ? CollectionFreeFormView.from(dv) : undefined), undefined as undefined | CollectionFreeFormView); cv && undoable(() => cv._marqueeViewRef.current?.collection(e, true, SelectionManager.Docs), 'grouping'); } break; @@ -205,7 +206,7 @@ export class KeyManager { switch (keyname) { case 'Æ’': case 'f': - UndoManager.RunInBatch(() => SelectionManager.Views?.[0]?.CollectionFreeFormDocumentView?.float(), 'float'); + UndoManager.RunInBatch(() => CollectionFreeFormDocumentView.from(SelectionManager.Views?.[0])?.float(), 'float'); break; default: } diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 98cf33c41..109a9cad4 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -13,6 +13,7 @@ import { DocumentManager } from '../util/DocumentManager'; import { undoBatch } from '../util/UndoManager'; import { FitOneCurve } from '../util/bezierFit'; import { InkingStroke } from './InkingStroke'; +import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { DocumentView } from './nodes/DocumentView'; export class InkStrokeProperties { @@ -380,7 +381,7 @@ export class InkStrokeProperties { }; snapToAllCurves = (screenDragPt: { X: number; Y: number }, inkView: DocumentView, snapData: { nearestPt: { X: number; Y: number }; distance: number }, ink: InkData, controlIndex: number) => { - const containingCollection = inkView.CollectionFreeFormView; + const containingCollection = CollectionFreeFormView.from(inkView); const containingDocView = containingCollection?.DocumentView?.(); containingCollection?.childDocs .filter(doc => doc.type === DocumentType.INK) diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index cf609d8f9..884512539 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -30,6 +30,7 @@ import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; import { CognitiveServices } from '../cognitive_services/CognitiveServices'; import { Docs } from '../documents/Documents'; +import { DocumentType } from '../documents/DocumentTypes'; import { InteractionUtils } from '../util/InteractionUtils'; import { SnappingManager } from '../util/SnappingManager'; import { UndoManager } from '../util/UndoManager'; @@ -491,67 +492,17 @@ export class InkingStroke extends ViewBoxAnnotatableComponent() ); } } -export function ActiveInkPen(): Doc { - return Doc.UserDoc(); -} -export function ActiveInkColor(): string { - return StrCast(ActiveInkPen()?.activeInkColor, 'black'); -} -export function ActiveFillColor(): string { - return StrCast(ActiveInkPen()?.activeFillColor, ''); -} -export function ActiveIsInkMask(): boolean { - return BoolCast(ActiveInkPen()?.activeIsInkMask, false); -} -export function ActiveInkHideTextLabels(): boolean { - return BoolCast(ActiveInkPen().activeInkHideTextLabels, false); -} -export function ActiveArrowStart(): string { - return StrCast(ActiveInkPen()?.activeArrowStart, ''); -} -export function ActiveArrowEnd(): string { - return StrCast(ActiveInkPen()?.activeArrowEnd, ''); -} -export function ActiveArrowScale(): number { - return NumCast(ActiveInkPen()?.activeArrowScale, 1); -} -export function ActiveDash(): string { - return StrCast(ActiveInkPen()?.activeDash, '0'); -} -export function ActiveInkWidth(): number { - return Number(ActiveInkPen()?.activeInkWidth); -} -export function ActiveInkBezierApprox(): string { - return StrCast(ActiveInkPen()?.activeInkBezier); -} - -export function SetActiveInkWidth(width: string): void { - !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); -} -export function SetActiveBezierApprox(bezier: string): void { - ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier); -} -export function SetActiveInkColor(value: string) { - ActiveInkPen() && (ActiveInkPen().activeInkColor = value); -} -export function SetActiveIsInkMask(value: boolean) { - ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value); -} -export function SetActiveInkHideTextLabels(value: boolean) { - ActiveInkPen() && (ActiveInkPen().activeInkHideTextLabels = value); -} -export function SetActiveFillColor(value: string) { - ActiveInkPen() && (ActiveInkPen().activeFillColor = value); -} -export function SetActiveArrowStart(value: string) { - ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); -} -export function SetActiveArrowEnd(value: string) { - ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); -} -export function SetActiveArrowScale(value: number) { - ActiveInkPen() && (ActiveInkPen().activeArrowScale = value); -} -export function SetActiveDash(dash: string): void { - !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); -} +Docs.Prototypes.TemplateMap.set(DocumentType.INK, { + // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method + layout: { view: InkingStroke, dataField: 'stroke' }, + options: { + acl: '', + systemIcon: 'BsFillPencilFill', // + _layout_nativeDimEditable: true, + _layout_reflowVertical: true, + _layout_reflowHorizontal: true, + layout_hideDecorationTitle: true, // don't show title when selected + _layout_fitWidth: false, + layout_isSvg: true, + }, +}); diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 7bf6bb9e5..020525ef8 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -9,11 +9,10 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, DocListCast, FieldResult, Opt } from '../../fields/Doc'; +import { CreateLinkToActiveAudio, Doc, DocListCast, FieldResult, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { InkTool } from '../../fields/InkField'; import { Cast, NumCast, toList } from '../../fields/Types'; -import { DocUtils } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; @@ -27,7 +26,8 @@ import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline'; import { TabDocView } from './collections/TabDocView'; -import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; +import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; interface LightboxViewProps { PanelWidth: number; @@ -88,7 +88,7 @@ export class LightboxView extends ObservableReactComponent { savedKeys.forEach(key => { this._savedState[key] = Doc.Get(doc, key, true); }); - const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); + const l = CreateLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); this._history.push({ doc, target }); @@ -136,7 +136,7 @@ export class LightboxView extends ObservableReactComponent { const target = (this._docTarget = this._future.pop()); const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (targetDocView && target) { - const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); + const l = CreateLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (this._history.lastElement().target !== target) this._history.push({ doc: lightDoc, target }); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d3dd51ac6..aec553baa 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -11,7 +11,7 @@ import * as React from 'react'; import '../../../node_modules/browndash-components/dist/styles/global.min.css'; import { ClientUtils, lightOrDark, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { Doc, DocListCast, GetDocFromUrl, Opt } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { DocCast, StrCast, toList } from '../../fields/Types'; import { DocServer } from '../DocServer'; @@ -55,28 +55,28 @@ import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionMenu } from './collections/CollectionMenu'; import { TabDocView } from './collections/TabDocView'; import './collections/TreeView.scss'; +import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu'; import { CollectionLinearView } from './collections/collectionLinear'; import { LinkMenu } from './linking/LinkMenu'; import { AudioBox } from './nodes/AudioBox'; import { SchemaCSVPopUp } from './nodes/DataVizBox/SchemaCSVPopUp'; import { DocButtonState } from './nodes/DocumentLinksButton'; -import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from './nodes/DocumentView'; +import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from './nodes/DocumentView'; import { ImageEditorData as ImageEditor } from './nodes/ImageBox'; import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview'; import { DirectionsAnchorMenu } from './nodes/MapBox/DirectionsAnchorMenu'; import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu'; +import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; import { TaskCompletionBox } from './nodes/TaskCompletedBox'; import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView'; -import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from './nodes/formattedText/RichTextMenu'; import GenerativeFill from './nodes/generativeFill/GenerativeFill'; import { PresBox } from './nodes/trails'; import { AnchorMenu } from './pdf/AnchorMenu'; import { GPTPopup } from './pdf/GPTPopup/GPTPopup'; import { TopBar } from './topbar/TopBar'; -import { CollectionFreeFormView } from './collections/collectionFreeForm'; const _global = (window /* browser */ || global) /* node */ as any; const { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @@ -242,7 +242,7 @@ export class MainView extends ObservableReactComponent<{}> { window.addEventListener('paste', KeyManager.Instance.paste as any); document.addEventListener('dash', (e: any) => { // event used by chrome plugin to tell Dash which document to focus on - const id = FormattedTextBox.GetDocFromUrl(e.detail); + const id = GetDocFromUrl(e.detail); DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.showDocument(doc, { willPan: false }) : null)); }); document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener); diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index ea73a53a9..06cae6d04 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -4,16 +4,16 @@ import * as React from 'react'; import { Doc, Opt } from '../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; +import { FollowLinkScript } from '../../fields/ScriptField'; import { NumCast } from '../../fields/Types'; import { GetEffectiveAcl } from '../../fields/util'; import { unimplementedFunction, Utils } from '../../Utils'; -import { Docs, DocUtils } from '../documents/Documents'; +import { Docs } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; import { DragManager } from '../util/DragManager'; -import { FollowLinkScript } from '../util/LinkFollower'; import { undoable, undoBatch, UndoManager } from '../util/UndoManager'; import './MarqueeAnnotator.scss'; import { DocumentView } from './nodes/DocumentView'; -import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ObservableReactComponent } from './ObservableReactComponent'; import { AnchorMenu } from './pdf/AnchorMenu'; @@ -197,7 +197,7 @@ export class MarqueeAnnotator extends ObservableReactComponent { const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); - FormattedTextBox.SetSelectOnLoad(target); + Doc.SetSelectOnLoad(target); return target; }; DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index fddb40624..034ade50b 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { lightOrDark, returnFalse } from '../../ClientUtils'; import { Doc, Opt } from '../../fields/Doc'; -import { DocUtils, Docs, DocumentOptions } from '../documents/Documents'; +import { Docs, DocumentOptions } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; import { ImageUtils } from '../util/Import & Export/ImageUtils'; import { Transform } from '../util/Transform'; import { UndoManager, undoBatch } from '../util/UndoManager'; diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index d83fea2a5..cbbf48c75 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -20,8 +20,8 @@ import { DocData } from '../../fields/DocSymbols'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, ScriptCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; +import { DocUtils } from '../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { DocUtils } from '../documents/Documents'; import { IsFollowLinkScript } from '../util/LinkFollower'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; @@ -30,7 +30,8 @@ import { undoBatch, undoable } from '../util/UndoManager'; import { InkingStroke } from './InkingStroke'; import './PropertiesButtons.scss'; import { Colors } from './global/globalEnums'; -import { DocumentView, OpenWhere } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; +import { OpenWhere } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; @observer diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index e079b5cde..a806995f2 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -12,7 +12,7 @@ import { SettingsManager } from '../util/SettingsManager'; import './PropertiesDocBacklinksSelector.scss'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { LinkMenu } from './linking/LinkMenu'; -import { OpenWhere } from './nodes/DocumentView'; +import { OpenWhere } from './nodes/OpenWhere'; type PropertiesDocBacklinksSelectorProps = { Document: Doc; diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index 2d04f2fe3..722b1f90a 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -11,7 +11,8 @@ import { DocFocusOrOpen } from '../util/DocumentManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import './PropertiesDocContextSelector.scss'; import { CollectionDockingView } from './collections/CollectionDockingView'; -import { DocumentView, OpenWhere } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; +import { OpenWhere } from './nodes/OpenWhere'; type PropertiesDocContextSelectorProps = { DocView?: DocumentView; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index f4ded8367..ff5dcd1b8 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -14,7 +14,7 @@ import { ColorResult, SketchPicker } from 'react-color'; import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs" import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, Field, FieldType, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; +import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; @@ -41,9 +41,10 @@ import { PropertiesDocContextSelector } from './PropertiesDocContextSelector'; import { PropertiesSection } from './PropertiesSection'; import './PropertiesView.scss'; import { DefaultStyleProvider } from './StyleProvider'; -import { DocumentView, OpenWhere } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; import { StyleProviderFuncType } from './nodes/FieldView'; import { KeyValueBox } from './nodes/KeyValueBox'; +import { OpenWhere } from './nodes/OpenWhere'; import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails'; const _global = (window /* browser */ || global) /* node */ as any; diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 6195dcde8..3648e613d 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -12,7 +12,8 @@ import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; import { DocCast, NumCast, StrCast } from '../../fields/Types'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { DocUtils, Docs } from '../documents/Documents'; +import { Docs } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; import { LinkManager } from '../util/LinkManager'; import { SearchUtil } from '../util/SearchUtil'; import { Transform } from '../util/Transform'; @@ -83,7 +84,7 @@ export class SidebarAnnos extends ObservableReactComponent void) => { this._flush = this._flush ?? UndoManager.StartBatch('golden layout drag'); - const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) : { type: 'row', content: dragDocs.map(doc => CollectionDockingView.makeDocumentConfig(doc)) }; + const config = dragDocs.length === 1 ? DashboardView.makeDocumentConfig(dragDocs[0]) : { type: 'row', content: dragDocs.map(doc => DashboardView.makeDocumentConfig(doc)) }; const dragSource = CollectionDockingView.Instance?._goldenLayout.createDragSource(document.createElement('div'), config); this.tabDragStart(dragSource, finishDrag); dragSource._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }); @@ -139,7 +125,7 @@ export class CollectionDockingView extends CollectionSubView() { public static ReplaceTab(document: Doc, mods: OpenWhereMod, stack: any, panelName: string, addToSplit?: boolean, keyValue?: boolean): boolean { const instance = CollectionDockingView.Instance; if (!instance) return false; - const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue); + const newConfig = DashboardView.makeDocumentConfig(document, panelName, undefined, keyValue); if (!panelName && stack) { const activeContentItemIndex = stack.contentItems.findIndex((item: any) => item.config === stack._activeContentItem.config); const newContentItem = stack.layoutManager.createContentItem(newConfig, instance._goldenLayout); @@ -180,7 +166,7 @@ export class CollectionDockingView extends CollectionSubView() { const instance = CollectionDockingView.Instance; const glayRoot = instance._goldenLayout.root; if (!instance) return false; - const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue); + const docContentConfig = DashboardView.makeDocumentConfig(document, panelName, undefined, keyValue); CollectionDockingView.Instance._flush = CollectionDockingView.Instance._flush ?? UndoManager.StartBatch('Add Split'); setTimeout(CollectionDockingView.Instance.endUndoBatch, 100); @@ -461,7 +447,7 @@ export class CollectionDockingView extends CollectionSubView() { if (content) { const _width = DivWidth(content); const _height = DivHeight(content); - return CollectionFreeFormView.UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', iconFile => { + return UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', iconFile => { const proto = this.dataDoc; // Cast(img.proto, Doc, null)!; proto.thumb_nativeWidth = _width; proto.thumb_nativeHeight = _height; diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 8803f6f79..9a6f1e2eb 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -166,7 +166,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent { const key = this._props.pivotField; doc[key] = this.getValue(this._props.heading); - FormattedTextBox.SetSelectOnLoad(doc); + Doc.SetSelectOnLoad(doc); return this._props.addDocument?.(doc); }, this._props.addDocument, @@ -195,7 +197,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent { - const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document)); + const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document)); if (created) { if (this._props.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this._props.Document); diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 6ccbd6208..e02570d3e 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -8,11 +8,11 @@ import { Doc, DocListCast } from '../../../fields/Doc'; import { ScriptField } from '../../../fields/ScriptField'; import { NumCast, StrCast, toList } from '../../../fields/Types'; import { emptyFunction } from '../../../Utils'; -import { DocUtils } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; import { dropActionType } from '../../util/DropActionTypes'; import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; -import { OpenWhere } from '../nodes/DocumentView'; +import { OpenWhere } from '../nodes/OpenWhere'; import { computePassLayout, computeStarburstLayout } from './collectionFreeForm'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import './CollectionPileView.scss'; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 7adf44a5c..50a66aa41 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -13,7 +13,7 @@ import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; -import { ComputedField, ScriptField } from '../../../fields/ScriptField'; +import { ComputedField, FollowLinkScript, ScriptField } from '../../../fields/ScriptField'; import { Cast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { emptyFunction, formatTime } from '../../../Utils'; @@ -21,21 +21,23 @@ import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; -import { FollowLinkScript, IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower'; +import { IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; -import { CollectionSubView } from './CollectionSubView'; import { LightboxView } from '../LightboxView'; import { AudioWaveform } from '../nodes/audio/AudioWaveform'; -import { DocumentView, OpenWhere } from '../nodes/DocumentView'; -import { FocusFuncType, FocusViewOptions, StyleProviderFuncType } from '../nodes/FieldView'; +import { DocumentView } from '../nodes/DocumentView'; +import { FocusFuncType, StyleProviderFuncType } from '../nodes/FieldView'; +import { FocusViewOptions } from '../nodes/FocusViewOptions'; import { LabelBox } from '../nodes/LabelBox'; +import { OpenWhere } from '../nodes/OpenWhere'; import { VideoBox } from '../nodes/VideoBox'; import { ObservableReactComponent } from '../ObservableReactComponent'; import './CollectionStackedTimeline.scss'; +import { CollectionSubView } from './CollectionSubView'; export type CollectionStackedTimelineProps = { Play: () => void; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index ad9960989..8ae0f2832 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -15,8 +15,9 @@ import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { CollectionViewType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { SettingsManager } from '../../util/SettingsManager'; @@ -28,8 +29,8 @@ import { EditableView } from '../EditableView'; import { LightboxView } from '../LightboxView'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; import { DocumentView } from '../nodes/DocumentView'; -import { FieldViewProps, FocusViewOptions } from '../nodes/FieldView'; -import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; +import { FieldViewProps } from '../nodes/FieldView'; +import { FocusViewOptions } from '../nodes/FocusViewOptions'; import { StyleProp } from '../StyleProvider'; import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow'; import './CollectionStackingView.scss'; @@ -296,7 +297,7 @@ export class CollectionStackingView extends CollectionSubView (NumCast(doc.heading) > prevHeading ? NumCast(doc.heading) : prevHeading), 0); const heading = maxHeading === 0 || this._props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3; newDoc.heading = heading; - FormattedTextBox.SetSelectOnLoad(newDoc); + Doc.SetSelectOnLoad(newDoc); FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' '; return this._props.addDocument?.(newDoc) || false; }; @@ -240,7 +242,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< const height = this._ele ? DivHeight(this._ele) : 0; DocUtils.addDocumentCreatorMenuItems( doc => { - FormattedTextBox.SetSelectOnLoad(doc); + Doc.SetSelectOnLoad(doc); return this._props.addDocument?.(doc); }, this._props.addDocument, @@ -255,7 +257,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< docItems.push({ description: ':' + fieldKey, event: () => { - const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document)); + const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document)); if (created) { if (this._props.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this._props.Document); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index f3dedaedf..7c08aedb1 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import * as rp from 'request-promise'; import { ClientUtils, returnFalse } from '../../../ClientUtils'; import CursorField from '../../../fields/CursorField'; -import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; +import { Doc, DocListCast, GetDocFromUrl, GetHrefFromHTML, Opt, RTFIsFragment, StrListCast } from '../../../fields/Doc'; import { AclPrivate } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -15,8 +15,9 @@ import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; import { DocServer } from '../../DocServer'; import { Networking } from '../../Network'; +import { DocUtils } from '../../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; -import { DocUtils, Docs, DocumentOptions } from '../../documents/Documents'; +import { Docs, DocumentOptions } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; @@ -26,7 +27,6 @@ import { UndoManager, undoBatch } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { FieldViewProps } from '../nodes/FieldView'; import { LoadingBox } from '../nodes/LoadingBox'; -import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; export interface CollectionViewProps extends React.PropsWithChildren { isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) @@ -317,10 +317,10 @@ export function CollectionSubView() { const addDocument = (doc: Doc | Doc[]) => this.addDocument(doc); if (html) { - if (FormattedTextBox.IsFragment(html)) { - const href = FormattedTextBox.GetHref(html); + if (RTFIsFragment(html)) { + const href = GetHrefFromHTML(html); if (href) { - const docId = FormattedTextBox.GetDocFromUrl(href); + const docId = GetDocFromUrl(href); if (docId) { // prosemirror text containing link to dash document DocServer.GetRefField(docId).then(f => { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 365b5acfd..d015e73ad 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -12,7 +12,8 @@ import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, Utils } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; @@ -189,7 +190,7 @@ export class CollectionTreeView extends CollectionSubView 0 && prev) { - FormattedTextBox.SetSelectOnLoad(prev); + Doc.SetSelectOnLoad(prev); DocumentManager.Instance.getDocumentView(prev, this.DocumentView?.())?.select(false); } return true; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index d6cbe0dab..7cadd072b 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -7,14 +7,15 @@ import { Doc, DocListCast } from '../../../fields/Doc'; import { ObjectField } from '../../../fields/ObjectField'; import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { CollectionViewType } from '../../documents/DocumentTypes'; -import { DocUtils } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; +import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; -import { OpenWhere } from '../nodes/DocumentView'; import { FieldView } from '../nodes/FieldView'; +import { OpenWhere } from '../nodes/OpenWhere'; import { CollectionCalendarView } from './CollectionCalendarView'; import { CollectionCarousel3DView } from './CollectionCarousel3DView'; import { CollectionCarouselView } from './CollectionCarouselView'; @@ -234,3 +235,19 @@ export class CollectionView extends ViewBoxAnnotatableComponent { const fieldKey = Doc.LayoutFieldKey(newParent); if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) { remove(child); - FormattedTextBox.SetSelectOnLoad(child); + Doc.SetSelectOnLoad(child); TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined; Doc.AddDocToList(newParent, fieldKey, child, addAfter, false); newParent.treeView_Open = true; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 653a01a04..f55d5a23f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -9,7 +9,7 @@ import { computedFn } from 'mobx-utils'; import * as React from 'react'; import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; -import { Doc, DocListCast, Field, FieldType, Opt } from '../../../../fields/Doc'; +import { ActiveInkWidth, Doc, DocListCast, Field, FieldType, Opt, SetActiveInkColor, SetActiveInkWidth } from '../../../../fields/Doc'; import { DocData, Height, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkData, InkField, InkTool, Segment } from '../../../../fields/InkField'; @@ -23,8 +23,9 @@ import { TraceMobx } from '../../../../fields/util'; import { Gestures, PointData } from '../../../../pen-gestures/GestureTypes'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; import { aggregateBounds, emptyFunction, intersectRect, Utils } from '../../../../Utils'; -import { Docs, DocUtils } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; +import { DocUtils } from '../../../documents/DocUtils'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; @@ -39,13 +40,15 @@ import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; import { Timeline } from '../../animationtimeline/Timeline'; import { ContextMenu } from '../../ContextMenu'; import { GestureOverlay } from '../../GestureOverlay'; -import { ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; +import { InkingStroke } from '../../InkingStroke'; import { LightboxView } from '../../LightboxView'; import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView'; import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp'; -import { DocumentView, OpenWhere } from '../../nodes/DocumentView'; -import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView'; +import { DocumentView } from '../../nodes/DocumentView'; +import { FieldViewProps } from '../../nodes/FieldView'; +import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; +import { OpenWhere } from '../../nodes/OpenWhere'; import { PinDocView, PinProps } from '../../PinFuncs'; import { StyleProp } from '../../StyleProvider'; import { CollectionSubView } from '../CollectionSubView'; @@ -285,7 +288,7 @@ export class CollectionFreeFormView extends CollectionSubView this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); isAnyChildContentActive = () => this._props.isAnyChildContentActive(); addLiveTextBox = (newDoc: Doc) => { - FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed + Doc.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed this.addDocument(newDoc); }; selectDocuments = (docs: Doc[]) => { @@ -1052,7 +1055,7 @@ export class CollectionFreeFormView extends CollectionSubView { const text = Docs.Create.TextDocument('', { _width: 150, _height: 50 }); - FormattedTextBox.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed + Doc.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed Doc.AddDocToList(this.Document, this._props.fieldKey, text); this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(clickEv.clientX, clickEv.clientY)))); }) diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index ee79812a1..406a7d626 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -13,7 +13,8 @@ import { List } from '../../../../fields/List'; import { listSpec } from '../../../../fields/Schema'; import { ColumnType } from '../../../../fields/SchemaHeaderField'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { DocUtils, Docs, DocumentOptions, FInfo } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; +import { Docs, DocumentOptions, FInfo } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; @@ -26,7 +27,8 @@ import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DefaultStyleProvider, StyleProp } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../../nodes/DocumentView'; -import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView'; +import { FieldViewProps } from '../../nodes/FieldView'; +import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { KeyValueBox } from '../../nodes/KeyValueBox'; import { CollectionSubView } from '../CollectionSubView'; import './CollectionSchemaView.scss'; diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 61afe08cf..32b48e4d1 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -5,8 +5,8 @@ import { computedFn } from 'mobx-utils'; import * as React from 'react'; import { CgClose, CgLock, CgLockUnlock } from 'react-icons/cg'; import { FaExternalLinkAlt } from 'react-icons/fa'; -import { emptyFunction } from '../../../../Utils'; import { returnFalse, setupMoveUpEvents } from '../../../../ClientUtils'; +import { emptyFunction } from '../../../../Utils'; import { Doc } from '../../../../fields/Doc'; import { BoolCast } from '../../../../fields/Types'; import { DragManager } from '../../../util/DragManager'; @@ -15,8 +15,8 @@ import { Transform } from '../../../util/Transform'; import { undoable } from '../../../util/UndoManager'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; -import { OpenWhere } from '../../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; +import { OpenWhere } from '../../nodes/OpenWhere'; import { CollectionSchemaView } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; import { SchemaTableCell } from './SchemaTableCell'; diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index 231bac541..d17d4ff7c 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -1,7 +1,7 @@ import { Colors } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { aggregateBounds } from '../../../Utils'; -import { Doc, Opt } from '../../../fields/Doc'; +import { ActiveFillColor, ActiveInkColor, ActiveInkHideTextLabels, ActiveInkWidth, ActiveIsInkMask, Doc, Opt, SetActiveFillColor, SetActiveInkColor, SetActiveInkHideTextLabels, SetActiveInkWidth, SetActiveIsInkMask } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { InkTool } from '../../../fields/InkField'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; @@ -13,7 +13,7 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; import { UndoManager, undoable } from '../../util/UndoManager'; import { GestureOverlay } from '../GestureOverlay'; -import { ActiveFillColor, ActiveInkColor, ActiveInkHideTextLabels, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveFillColor, SetActiveInkColor, SetActiveInkHideTextLabels, SetActiveInkWidth, SetActiveIsInkMask } from '../InkingStroke'; +import { InkingStroke } from '../InkingStroke'; import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; import { DocumentView } from '../nodes/DocumentView'; @@ -111,7 +111,7 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { if (NumCast(selected?.Document.z) >= 1) return true; return false; } - selected ? selected.CollectionFreeFormDocumentView?.float() : console.log('[FontIconBox.tsx] toggleOverlay failed'); + selected ? CollectionFreeFormDocumentView.from(selected)?.float() : console.log('[FontIconBox.tsx] toggleOverlay failed'); return undefined; }); diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 278d30d6a..df3accb0d 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -20,8 +20,9 @@ import { SelectionManager } from '../../util/SelectionManager'; import { SettingsManager } from '../../util/SettingsManager'; import { undoBatch } from '../../util/UndoManager'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView'; +import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import { LinkInfo } from '../nodes/LinkDocPreview'; +import { OpenWhere } from '../nodes/OpenWhere'; import './LinkMenuItem.scss'; interface LinkMenuItemProps { diff --git a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx index bce2b296f..72d81e9eb 100644 --- a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx +++ b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx @@ -5,7 +5,7 @@ import { InkTool } from '../../../../fields/InkField'; import { SelectionManager } from '../../../util/SelectionManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; -import { OpenWhereMod } from '../../nodes/DocumentView'; +import { OpenWhereMod } from '../../nodes/OpenWhere'; import { NewLightboxView } from '../NewLightboxView'; import './ButtonMenu.scss'; import { IButtonMenu } from './utils'; diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx index 6616abad1..dc6bf3e9c 100644 --- a/src/client/views/newlightbox/NewLightboxView.tsx +++ b/src/client/views/newlightbox/NewLightboxView.tsx @@ -6,10 +6,9 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; -import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { CreateLinkToActiveAudio, Doc, DocListCast, Opt } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast, toList } from '../../../fields/Types'; -import { DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { LinkManager } from '../../util/LinkManager'; import { SelectionManager } from '../../util/SelectionManager'; @@ -20,7 +19,8 @@ import { LightboxView } from '../LightboxView'; import { DefaultStyleProvider } from '../StyleProvider'; import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline'; import { TabDocView } from '../collections/TabDocView'; -import { DocumentView, OpenWhere } from '../nodes/DocumentView'; +import { DocumentView } from '../nodes/DocumentView'; +import { OpenWhere } from '../nodes/OpenWhere'; import { ExploreView } from './ExploreView'; import { IBounds, emptyBounds } from './ExploreView/utils'; import { NewLightboxHeader } from './Header'; @@ -108,7 +108,7 @@ export class NewLightboxView extends React.Component { Doc.ActiveTool = InkTool.None; SnappingManager.SetExploreMode(false); } else { - const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); + const l = CreateLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); // TabDocView.PinDoc(doc, { hidePresBox: true }); @@ -150,7 +150,7 @@ export class NewLightboxView extends React.Component { const target = (NewLightboxView._docTarget = this._future?.pop()); const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (targetDocView && target) { - const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); + const l = CreateLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (NewLightboxView._history?.lastElement().target !== target) NewLightboxView._history?.push({ doc, target }); diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 1f618135f..4697491e0 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -13,7 +13,9 @@ import { ComputedField } from '../../../fields/ScriptField'; import { Cast, DateCast, NumCast } from '../../../fields/Types'; import { AudioField, nullAudio } from '../../../fields/URLField'; import { formatTime } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { Networking } from '../../Network'; import { DragManager } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; @@ -24,8 +26,8 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; import './AudioBox.scss'; -import { OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; +import { OpenWhere } from './OpenWhere'; /** * AudioBox @@ -761,3 +763,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.AUDIO, { + layout: { view: AudioBox, dataField: 'data' }, + options: { acl: '', _height: 100, _layout_fitWidth: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true, _layout_nativeDimEditable: true, systemIcon: 'BsFillVolumeUpFill' }, +}); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 9c4d748bd..691d07e31 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -11,14 +11,16 @@ import { ComputedField } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { DocumentManager } from '../../util/DocumentManager'; +import { DragManager } from '../../util/DragManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; import { DocComponent } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import './CollectionFreeFormDocumentView.scss'; -import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView'; +import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldViewProps } from './FieldView'; +import { OpenWhere } from './OpenWhere'; /// Ugh, typescript has no run-time way of iterating through the keys of an interface. so we need /// manaully keep this list of keys in synch wih the fields of the freeFormProps interface @@ -48,7 +50,7 @@ export class CollectionFreeFormDocumentView extends DocComponent() ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.COMPARISON, { + data: '', + layout: { view: ComparisonBox, dataField: 'data' }, + options: { + acl: '', + backgroundColor: 'gray', + dropAction: dropActionType.move, + waitForDoubleClickToClick: 'always', + _layout_reflowHorizontal: true, + _layout_reflowVertical: true, + _layout_nativeDimEditable: true, + systemIcon: 'BsLayoutSplit', + }, +}); diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 113a857c3..ecfdcc229 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -13,22 +13,26 @@ import { List } from '../../../../fields/List'; import { Cast, CsvCast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { CsvField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; +import { DocumentType } from '../../../documents/DocumentTypes'; +import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; -import { PinProps, ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; +import { ContextMenu } from '../../ContextMenu'; +import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; import { MarqueeAnnotator } from '../../MarqueeAnnotator'; +import { PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup'; import { DocumentView } from '../DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; import './DataVizBox.scss'; import { Histogram } from './components/Histogram'; import { LineChart } from './components/LineChart'; import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; -import { ContextMenu } from '../../ContextMenu'; export enum DataVizView { TABLE = 'table', @@ -522,3 +526,18 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im ); } } +Docs.Prototypes.TemplateMap.set(DocumentType.DATAVIZ, { + layout: { view: DataVizBox, dataField: 'data' }, + options: { + acl: '', + dataViz_title: '', + dataViz_line: '', + dataViz_pie: '', + dataViz_histogram: '', + dataViz: 'table', + _layout_fitWidth: true, + _layout_reflowHorizontal: true, + _layout_reflowVertical: true, + _layout_nativeDimEditable: true, + }, +}); diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 9b4e36509..977899589 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -10,13 +10,13 @@ import { emptyFunction } from '../../../Utils'; import { Doc } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { StrCast } from '../../../fields/Types'; -import { DocUtils } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; import { DragManager } from '../../util/DragManager'; import { Hypothesis } from '../../util/HypothesisUtils'; import { LinkManager } from '../../util/LinkManager'; import { UndoManager, undoBatch } from '../../util/UndoManager'; -import { PinProps } from '../DocComponent'; import { ObservableReactComponent } from '../ObservableReactComponent'; +import { PinProps } from '../PinFuncs'; import './DocumentLinksButton.scss'; import { DocumentView } from './DocumentView'; import { LinkDescriptionPopup } from './LinkDescriptionPopup'; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a603de10b..e8d1e582e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -16,20 +16,20 @@ import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { PrefetchProxy } from '../../../fields/Proxy'; import { listSpec } from '../../../fields/Schema'; -import { ScriptField } from '../../../fields/ScriptField'; +import { FollowLinkScript, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { AudioField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { DocServer } from '../../DocServer'; import { Networking } from '../../Network'; +import { DocUtils } from '../../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; -import { DocUtils, Docs } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { DictationManager } from '../../util/DictationManager'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { MakeTemplate, makeUserTemplateButton } from '../../util/DropConverter'; -import { FollowLinkScript } from '../../util/LinkFollower'; import { LinkManager, UPDATE_SERVER_CACHE } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SearchUtil } from '../../util/SearchUtil'; @@ -48,38 +48,14 @@ import { AudioAnnoState, StyleProp } from '../StyleProvider'; import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView'; import { DocumentLinksButton } from './DocumentLinksButton'; import './DocumentView.scss'; -import { FieldViewProps, FieldViewSharedProps, FocusViewOptions } from './FieldView'; +import { FieldViewProps, FieldViewSharedProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import { KeyValueBox } from './KeyValueBox'; import { LinkAnchorBox } from './LinkAnchorBox'; +import { OpenWhere } from './OpenWhere'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { PresEffect, PresEffectDirection } from './trails'; -export enum OpenWhereMod { - none = '', - left = 'left', - right = 'right', - top = 'top', - bottom = 'bottom', - keyvalue = 'keyValue', -} -export enum OpenWhere { - lightbox = 'lightbox', - add = 'add', - addLeft = 'add:left', - addRight = 'add:right', - addBottom = 'add:bottom', - close = 'close', - toggle = 'toggle', - toggleRight = 'toggle:right', - replace = 'replace', - replaceRight = 'replace:right', - replaceLeft = 'replace:left', - inParent = 'inParent', - inParentFromScreen = 'inParentFromScreen', - overlay = 'overlay', - addRightKeyvalue = 'add:right:keyValue', -} - export interface DocumentViewProps extends FieldViewSharedProps { hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected hideResizeHandles?: boolean; // whether to suppress resized handles on doc decorations when this document is selected @@ -292,7 +268,8 @@ export class DocumentViewInternal extends DocComponent docView.props.dragEnding?.(); + dragData.dragStarting = () => docView.props.dragStarting?.(); dragData.canEmbed = !!(this.Document.dragAction ?? this._props.dragAction); (this._props.dragConfig ?? this._componentView?.dragConfig)?.(dragData); DragManager.StartDocumentDrag( diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx index 9c216cba4..32d08fbe7 100644 --- a/src/client/views/nodes/EquationBox.tsx +++ b/src/client/views/nodes/EquationBox.tsx @@ -3,10 +3,12 @@ import { action, makeObservable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { DivHeight, DivWidth } from '../../../ClientUtils'; -import { Id } from '../../../fields/FieldSymbols'; +import { Doc } from '../../../fields/Doc'; import { NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { DocUtils, Docs } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { LightboxView } from '../LightboxView'; @@ -19,7 +21,6 @@ export class EquationBox extends ViewBoxBaseComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); } - public static SelectOnLoad: string = ''; _ref: React.RefObject = React.createRef(); constructor(props: FieldViewProps) { @@ -29,12 +30,12 @@ export class EquationBox extends ViewBoxBaseComponent() { componentDidMount() { this._props.setContentViewBox?.(this); - if (EquationBox.SelectOnLoad === this.Document[Id] && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.()))) { + if (Doc.SelectOnLoad === this.Document && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.()))) { this._props.select(false); this._ref.current!.mathField.focus(); this.dataDoc.text === 'x' && this._ref.current!.mathField.select(); - EquationBox.SelectOnLoad = ''; + Doc.SetSelectOnLoad(undefined); } reaction( () => StrCast(this.dataDoc.text), @@ -69,7 +70,7 @@ export class EquationBox extends ViewBoxBaseComponent() { x: NumCast(this.layoutDoc.x), y: NumCast(this.layoutDoc.y) + _height + 10, }); - EquationBox.SelectOnLoad = nextEq[Id]; + Doc.SetSelectOnLoad(nextEq); this._props.addDocument?.(nextEq); e.stopPropagation(); } @@ -130,3 +131,8 @@ export class EquationBox extends ViewBoxBaseComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.EQUATION, { + layout: { view: EquationBox, dataField: 'text' }, + options: { acl: '', fontSize: '14px', _layout_reflowHorizontal: true, _layout_reflowVertical: true, _layout_nativeDimEditable: true, layout_hideDecorationTitle: true, systemIcon: 'BsCalculatorFill' }, // systemIcon: 'BsSuperscript' + BsSubscript +}); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index ab0850790..c6c77d8d2 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -10,29 +10,12 @@ import { ScriptField } from '../../../fields/ScriptField'; import { WebField } from '../../../fields/URLField'; import { dropActionType } from '../../util/DropActionTypes'; import { Transform } from '../../util/Transform'; -import { PinProps, ViewBoxInterface } from '../DocComponent'; -import { DocumentView, OpenWhere } from './DocumentView'; +import { ViewBoxInterface } from '../DocComponent'; +import { PinProps } from '../PinFuncs'; +import { DocumentView } from './DocumentView'; +import { FocusViewOptions } from './FocusViewOptions'; +import { OpenWhere } from './OpenWhere'; -export interface FocusViewOptions { - willPan?: boolean; // determines whether to pan to target document - willZoomCentered?: boolean; // determines whether to zoom in on target document. if zoomScale is 0, this just centers the document - zoomScale?: number; // percent of containing frame to zoom into document - zoomTime?: number; - didMove?: boolean; // whether a document was changed during the showDocument process - docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom. when this happens the docTransform must accumulate all the transforms of each level of the hierarchy - instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom) - preview?: boolean; // whether changes should be previewed by the componentView or written to the document - effect?: Doc; // animation effect for focus // bcz: needs to be changed to something more generic than a Doc - noSelect?: boolean; // whether target should be selected after focusing - playAudio?: boolean; // whether to play audio annotation on focus - playMedia?: boolean; // whether to play start target videos - openLocation?: OpenWhere; // where to open a missing document - zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections - toggleTarget?: boolean; // whether to toggle target on and off - easeFunc?: 'linear' | 'ease'; // transition method for scrolling - pointFocus?: { X: number; Y: number }; // clientX and clientY coordinates to focus on instead of a document target (used by explore mode) - contextPath?: Doc[]; // path of inner documents that will also be focused -} export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt; // eslint-disable-next-line no-use-before-define export type StyleProviderFuncType = (doc: Opt, props: Opt, property: string) => any; diff --git a/src/client/views/nodes/FocusViewOptions.ts b/src/client/views/nodes/FocusViewOptions.ts new file mode 100644 index 000000000..bb0d2b03c --- /dev/null +++ b/src/client/views/nodes/FocusViewOptions.ts @@ -0,0 +1,24 @@ +import { Doc } from '../../../fields/Doc'; +import { Transform } from '../../util/Transform'; +import { OpenWhere } from './OpenWhere'; + +export interface FocusViewOptions { + willPan?: boolean; // determines whether to pan to target document + willZoomCentered?: boolean; // determines whether to zoom in on target document. if zoomScale is 0, this just centers the document + zoomScale?: number; // percent of containing frame to zoom into document + zoomTime?: number; + didMove?: boolean; // whether a document was changed during the showDocument process + docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom. when this happens the docTransform must accumulate all the transforms of each level of the hierarchy + instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom) + preview?: boolean; // whether changes should be previewed by the componentView or written to the document + effect?: Doc; // animation effect for focus // bcz: needs to be changed to something more generic than a Doc + noSelect?: boolean; // whether target should be selected after focusing + playAudio?: boolean; // whether to play audio annotation on focus + playMedia?: boolean; // whether to play start target videos + openLocation?: OpenWhere; // where to open a missing document + zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections + toggleTarget?: boolean; // whether to toggle target on and off + easeFunc?: 'linear' | 'ease'; // transition method for scrolling + pointFocus?: { X: number; Y: number }; // clientX and clientY coordinates to focus on instead of a document target (used by explore mode) + contextPath?: Doc[]; // path of inner documents that will also be focused +} diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 1b2aefbe2..d83690cdd 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -9,6 +9,7 @@ import { ClientUtils, returnTrue, setupMoveUpEvents } from '../../../../ClientUt import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { emptyFunction } from '../../../../Utils'; +import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; @@ -18,8 +19,8 @@ import { ViewBoxBaseComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { SelectedDocView } from '../../selectedDoc'; import { StyleProp } from '../../StyleProvider'; -import { OpenWhere } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; +import { OpenWhere } from '../OpenWhere'; import './FontIconBox.scss'; import TrailsIcon from './TrailsIcon'; @@ -51,15 +52,6 @@ export class FontIconBox extends ViewBoxBaseComponent() { super(props); makeObservable(this); } - // - // This controls whether fontIconButtons will display labels under their icons or not - // - public static get ShowIconLabels() { - return BoolCast(Doc.UserDoc()._showLabel); - } - public static set ShowIconLabels(show: boolean) { - Doc.UserDoc()._showLabel = show; - } @observable noTooltip = false; @@ -397,3 +389,8 @@ export class FontIconBox extends ViewBoxBaseComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.FONTICON, { + layout: { view: FontIconBox, dataField: 'icon' }, + options: { acl: '', defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideContextMenu: true, layout_hideLinkButton: true, _width: 40, _height: 40 }, +}); diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index 31faa7ac3..f32d39aaf 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -7,12 +7,14 @@ import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { Cast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { DocUtils, Docs } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; -import { PinProps, PinDocView } from '../PinFuncs'; +import { PinDocView, PinProps } from '../PinFuncs'; import { FieldView, FieldViewProps } from './FieldView'; @observer @@ -138,3 +140,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.FUNCPLOT, { + layout: { view: FunctionPlotBox, dataField: 'data' }, + options: { acl: '', _layout_reflowHorizontal: true, _layout_reflowVertical: true, _layout_nativeDimEditable: true }, +}); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 8bd5ab03d..d317f46bb 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -15,8 +15,9 @@ import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { Networking } from '../../Network'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; @@ -30,9 +31,10 @@ import { OverlayView } from '../OverlayView'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProvider'; -import { OpenWhere } from './DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; +import { FieldView, FieldViewProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import './ImageBox.scss'; +import { OpenWhere } from './OpenWhere'; export class ImageEditorData { // eslint-disable-next-line no-use-before-define @@ -546,3 +548,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl input.click(); }; } + +Docs.Prototypes.TemplateMap.set(DocumentType.IMG, { + layout: { view: ImageBox, dataField: 'data' }, + options: { acl: '', freeform: '', systemIcon: 'BsFileEarmarkImageFill' }, +}); diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index b8296ce51..46bb16e50 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -3,12 +3,13 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { returnAlways, returnTrue } from '../../../ClientUtils'; -import { Doc, Field, FieldType, FieldResult } from '../../../fields/Doc'; +import { Doc, Field, FieldResult, FieldType } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { DocCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; +import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { SetupDrag } from '../../util/DragManager'; import { CompiledScript } from '../../util/Scripting'; @@ -17,11 +18,11 @@ import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DocumentIconContainer } from './DocumentIcon'; -import { OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { ImageBox } from './ImageBox'; import './KeyValueBox.scss'; import { KeyValuePair } from './KeyValuePair'; +import { OpenWhere } from './OpenWhere'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; export type KVPScript = { @@ -339,3 +340,8 @@ export class KeyValueBox extends ObservableReactComponent { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.KVP, { + layout: { view: KeyValueBox, dataField: 'data' }, + options: { acl: '', _layout_fitWidth: true, _height: 150 }, +}); diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index a9aa017a1..878b0e54c 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -14,10 +14,11 @@ import { ContextMenu } from '../ContextMenu'; import { EditableView } from '../EditableView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DefaultStyleProvider } from '../StyleProvider'; -import { OpenWhere, returnEmptyDocViewList } from './DocumentView'; +import { returnEmptyDocViewList } from './DocumentView'; import { KeyValueBox } from './KeyValueBox'; import './KeyValueBox.scss'; import './KeyValuePair.scss'; +import { OpenWhere } from './OpenWhere'; // Represents one row in a key value plane diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 89270652c..4dec3506c 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -5,13 +5,14 @@ import { Doc, DocListCast, Field, FieldType } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; +import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxBaseComponent } from '../DocComponent'; -import { PinProps, PinDocView } from '../PinFuncs'; +import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProvider'; import { FieldView, FieldViewProps } from './FieldView'; import BigText from './LabelBigText'; @@ -203,3 +204,12 @@ export class LabelBox extends ViewBoxBaseComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.LABEL, { + layout: { view: LabelBox, dataField: 'title' }, + options: { acl: '', _singleLine: true, _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true }, +}); +Docs.Prototypes.TemplateMap.set(DocumentType.BUTTON, { + layout: { view: LabelBox, dataField: 'title' }, + options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true }, +}); diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index f01905ee1..559b1fcae 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -7,9 +7,12 @@ import { DashColor, lightOrDark, returnFalse } from '../../../ClientUtils'; import { FieldResult } from '../../../fields/Doc'; import { DocCss, DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; +import { List } from '../../../fields/List'; import { DocCast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { SnappingManager } from '../../util/SnappingManager'; import { ViewBoxBaseComponent } from '../DocComponent'; @@ -263,3 +266,18 @@ export class LinkBox extends ViewBoxBaseComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.LINK, { + layout: { view: LinkBox, dataField: 'link' }, + options: { + acl: '', + childDontRegisterViews: true, + layout_hideLinkAnchors: true, + _height: 1, + _width: 1, + link: '', + link_description: '', + color: 'lightBlue', // lightblue is default color for linking dot and link documents text comment area + _dropPropertiesToRemove: new List(['onClick']), + }, +}); diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index a9cfe6c0e..0936acc15 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -19,9 +19,10 @@ import { SearchUtil } from '../../util/SearchUtil'; import { SettingsManager } from '../../util/SettingsManager'; import { Transform } from '../../util/Transform'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { DocumentView, OpenWhere } from './DocumentView'; +import { DocumentView } from './DocumentView'; import { StyleProviderFuncType } from './FieldView'; import './LinkDocPreview.scss'; +import { OpenWhere } from './OpenWhere'; interface LinkDocPreviewProps { linkDoc?: Doc; diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 501831bca..aa89398f3 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -10,6 +10,8 @@ import { DocumentManager } from '../../util/DocumentManager'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; import './LoadingBox.scss'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; /** * LoadingBox Class represents a placeholder doc for documents that are currently @@ -89,3 +91,7 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { ); } } +Docs.Prototypes.TemplateMap.set(DocumentType.LOADING, { + layout: { view: LoadingBox, dataField: '' }, + options: { acl: '', _layout_fitWidth: true, _layout_nativeDimEditable: true }, +}); diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 60dad314f..50831f8ea 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -18,20 +18,21 @@ import { ClientUtils, setupMoveUpEvents } from '../../../../ClientUtils'; import { emptyFunction } from '../../../../Utils'; import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc'; import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types'; +import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { LinkManager } from '../../../util/LinkManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; -import { PinProps, PinDocView } from '../../PinFuncs'; +import { PinDocView, PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; -import { FormattedTextBox } from '../formattedText/FormattedTextBox'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons'; import { AnimationSpeed, AnimationStatus, AnimationUtility } from './AnimationUtility'; import { MapAnchorMenu } from './MapAnchorMenu'; @@ -355,7 +356,7 @@ export class MapBox extends ViewBoxAnnotatableComponent() implem const targetCreator = (annotationOn: Doc | undefined) => { const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); - FormattedTextBox.SetSelectOnLoad(target); + Doc.SetSelectOnLoad(target); return target; }; const docView = this.DocumentView?.(); @@ -1360,3 +1361,8 @@ export class MapBox extends ViewBoxAnnotatableComponent() implem ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.MAP, { + layout: { view: MapBox, dataField: 'data' }, + options: { acl: '', map: '', _height: 600, _width: 800, _layout_reflowHorizontal: true, _layout_reflowVertical: true, _layout_nativeDimEditable: true, systemIcon: 'BsFillPinMapFill' }, +}); diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx index 6ccbbbe1c..c69cd8e89 100644 --- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx +++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx @@ -32,7 +32,7 @@ // addNoteClick = (e: React.PointerEvent) => { // setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => { // const newDoc = Docs.Create.TextDocument('Note', { _layout_autoHeight: true }); -// FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed +// Doc.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed // Doc.AddDocToList(this.props.place, 'data', newDoc); // this._stack?.scrollToBottom(); // e.stopPropagation(); diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx index 8ebc90157..f3dc44755 100644 --- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx +++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx @@ -2,6 +2,8 @@ import * as React from 'react'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { FieldView, FieldViewProps } from '../FieldView'; import { MapBoxContainer } from '../MapboxMapBox/MapboxContainer'; +import { Docs } from '../../../documents/Documents'; +import { DocumentType } from '../../../documents/DocumentTypes'; /** * Map Pushpin doc class @@ -28,3 +30,8 @@ export class MapPushpinBox extends ViewBoxBaseComponent() { return
; } } + +Docs.Prototypes.TemplateMap.set(DocumentType.PUSHPIN, { + layout: { view: MapPushpinBox, dataField: 'data' }, + options: { acl: '' }, +}); diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx index d899fcb9a..3b4ffd4bd 100644 --- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx +++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx @@ -10,23 +10,24 @@ import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc'; import { DocCss, Highlight } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types'; +import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { LinkManager } from '../../../util/LinkManager'; import { Transform } from '../../../util/Transform'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; -import { PinProps, PinDocView } from '../../PinFuncs'; +import { PinDocView, PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; import { MapAnchorMenu } from '../MapBox/MapAnchorMenu'; import '../MapBox/MapBox.scss'; -import { FormattedTextBox } from '../formattedText/FormattedTextBox'; /** * MapBox architecture: @@ -237,7 +238,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, annotationOn, 'yellow'); - FormattedTextBox.SetSelectOnLoad(target); + Doc.SetSelectOnLoad(target); return target; }; const docView = this.DocumentView?.(); diff --git a/src/client/views/nodes/OpenWhere.ts b/src/client/views/nodes/OpenWhere.ts new file mode 100644 index 000000000..e2a5f1f2a --- /dev/null +++ b/src/client/views/nodes/OpenWhere.ts @@ -0,0 +1,25 @@ +export enum OpenWhereMod { + none = '', + left = 'left', + right = 'right', + top = 'top', + bottom = 'bottom', + keyvalue = 'keyValue', +} +export enum OpenWhere { + lightbox = 'lightbox', + add = 'add', + addLeft = 'add:left', + addRight = 'add:right', + addBottom = 'add:bottom', + close = 'close', + toggle = 'toggle', + toggleRight = 'toggle:right', + replace = 'replace', + replaceRight = 'replace:right', + replaceLeft = 'replace:left', + inParent = 'inParent', + inParentFromScreen = 'inParentFromScreen', + overlay = 'overlay', + addRightKeyvalue = 'add:right:keyValue', +} diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index fbf5e018c..5d1874aca 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -16,8 +16,9 @@ import { Cast, FieldValue, ImageCast, NumCast, StrCast, toList } from '../../../ import { ImageField, PdfField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { KeyCodes } from '../../util/KeyCodes'; import { SelectionManager } from '../../util/SelectionManager'; @@ -31,9 +32,11 @@ import { Colors } from '../global/globalEnums'; import { PDFViewer } from '../pdf/PDFViewer'; import { PinDocView, PinProps } from '../PinFuncs'; import { SidebarAnnos } from '../SidebarAnnos'; -import { DocumentView, OpenWhere } from './DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; +import { DocumentView } from './DocumentView'; +import { FieldView, FieldViewProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import { ImageBox } from './ImageBox'; +import { OpenWhere } from './OpenWhere'; import './PDFBox.scss'; import { CreateImage } from './WebBoxRenderer'; @@ -665,3 +668,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent() implem return pdfView ?? this.renderTitleBox; } } + +Docs.Prototypes.TemplateMap.set(DocumentType.PDF, { + layout: { view: PDFBox, dataField: 'data' }, + options: { acl: '', _layout_curPage: 1, _layout_fitWidth: true, _layout_nativeDimEditable: true, _layout_reflowVertical: true, systemIcon: 'BsFileEarmarkPdfFill' }, +}); diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx index ae674d604..f88eb3bca 100644 --- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx @@ -1,3 +1,11 @@ +/* eslint-disable camelcase */ +/* eslint-disable jsx-a11y/control-has-associated-label */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable react/no-array-index-key */ +/* eslint-disable react/jsx-props-no-spreading */ +/* eslint-disable no-return-assign */ import ArrowLeftIcon from '@mui/icons-material/ArrowLeft'; import ArrowRightIcon from '@mui/icons-material/ArrowRight'; import PauseIcon from '@mui/icons-material/Pause'; @@ -13,13 +21,15 @@ import { NumListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { BoolCast, NumCast, StrCast } from '../../../../fields/Types'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; -import { FieldView, FieldViewProps } from './../FieldView'; +import { FieldView, FieldViewProps } from '../FieldView'; import './PhysicsSimulationBox.scss'; import InputField from './PhysicsSimulationInputField'; import questions from './PhysicsSimulationQuestions.json'; import tutorials from './PhysicsSimulationTutorial.json'; import Wall from './PhysicsSimulationWall'; import Weight from './PhysicsSimulationWeight'; +import { Docs } from '../../../documents/Documents'; +import { DocumentType } from '../../../documents/DocumentTypes'; interface IWallProps { length: number; @@ -204,7 +214,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent) { super.componentDidUpdate(prevProps); - if (this.xMax !== this._props.PanelWidth() * 0.6 || this.yMax != this._props.PanelHeight()) { + if (this.xMax !== this._props.PanelWidth() * 0.6 || this.yMax !== this._props.PanelHeight()) { this.xMax = this._props.PanelWidth() * 0.6; this.yMax = this._props.PanelHeight(); this.setupSimulation(); @@ -219,16 +229,16 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { - const simulationType = this.simulationType; + const { simulationType } = this; const mode = this.simulationMode; this.dataDoc.simulation_paused = true; - if (simulationType != 'Circular Motion') { + if (simulationType !== 'Circular Motion') { this.dataDoc.mass1_velocityXstart = 0; this.dataDoc.mass1_velocityYstart = 0; this.dataDoc.mass1_velocityX = 0; this.dataDoc.mass1_velocityY = 0; } - if (mode == 'Freeform') { + if (mode === 'Freeform') { this.dataDoc.simulation_showForceMagnitudes = true; // prettier-ignore switch (simulationType) { @@ -247,9 +257,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { let theta = this.wedgeAngle; - let index = this.selectedQuestion.variablesForQuestionSetup.indexOf('theta - max 45'); + const index = this.selectedQuestion.variablesForQuestionSetup.indexOf('theta - max 45'); if (index >= 0) { theta = NumListCast(this.dataDoc.questionVariables)[index]; } @@ -467,26 +480,26 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { let error: boolean = false; - let epsilon: number = 0.01; + const epsilon: number = 0.01; if (this.selectedQuestion) { for (let i = 0; i < this.selectedQuestion.answerParts.length; i++) { - if (this.selectedQuestion.answerParts[i] == 'force of gravity') { + if (this.selectedQuestion.answerParts[i] === 'force of gravity') { if (Math.abs(NumCast(this.dataDoc.review_GravityMagnitude) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'angle of gravity') { + } else if (this.selectedQuestion.answerParts[i] === 'angle of gravity') { if (Math.abs(NumCast(this.dataDoc.review_GravityAngle) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'normal force') { + } else if (this.selectedQuestion.answerParts[i] === 'normal force') { if (Math.abs(NumCast(this.dataDoc.review_NormalMagnitude) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'angle of normal force') { + } else if (this.selectedQuestion.answerParts[i] === 'angle of normal force') { if (Math.abs(NumCast(this.dataDoc.review_NormalAngle) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'force of static friction') { + } else if (this.selectedQuestion.answerParts[i] === 'force of static friction') { if (Math.abs(NumCast(this.dataDoc.review_StaticMagnitude) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'angle of static friction') { + } else if (this.selectedQuestion.answerParts[i] === 'angle of static friction') { if (Math.abs(NumCast(this.dataDoc.review_StaticAngle) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'coefficient of static friction') { + } else if (this.selectedQuestion.answerParts[i] === 'coefficient of static friction') { if (Math.abs(NumCast(this.dataDoc.coefficientOfStaticFriction) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'wedge angle') { + } else if (this.selectedQuestion.answerParts[i] === 'wedge angle') { if (Math.abs(this.wedgeAngle - this.selectedSolutions[i]) > epsilon) { error = true; } @@ -539,7 +552,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent (this.dataDoc.simulation_paused = true), 3000); } - if (this.selectedQuestion.goal == 'noMovement') { + if (this.selectedQuestion.goal === 'noMovement') { this.dataDoc.noMovement = !error; } }; @@ -571,12 +584,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent(this.getAnswersToQuestion(question, vars)); - //this.dataDoc.simulation_reset = (!this.dataDoc.simulation_reset); + // this.dataDoc.simulation_reset = (!this.dataDoc.simulation_reset); }; // Default setup for uniform circular motion simulation @@ -610,8 +623,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { - let xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius; - let yPos = this.yMin + 200; + const xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius; + const yPos = this.yMin + 200; this.dataDoc.mass1_positionYstart = yPos; this.dataDoc.mass1_positionXstart = xPos; this.dataDoc.mass1_positionY = this.getDisplayYPos(yPos); this.dataDoc.mass1_positionX = xPos; - let tensionMag = (this.mass1 * Math.abs(this.gravity)) / (2 * Math.sin(Math.PI / 4)); + const tensionMag = (this.mass1 * Math.abs(this.gravity)) / (2 * Math.sin(Math.PI / 4)); const tensionForce1: IForce = { description: 'Tension', magnitude: tensionMag, @@ -891,7 +904,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent - {this.simulationType == 'Pulley' && ( + {this.simulationType === 'Pulley' && (
- {(this.simulationType == 'One Weight' || this.simulationType == 'Inclined Plane') && + {(this.simulationType === 'One Weight' || this.simulationType === 'Inclined Plane') && this.wallPositions?.map((element, index) => )}
@@ -927,17 +940,17 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
- {this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && ( + {this.dataDoc.simulation_paused && this.simulationMode !== 'Tutorial' && ( (this.dataDoc.simulation_paused = false)}> )} - {!this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && ( + {!this.dataDoc.simulation_paused && this.simulationMode !== 'Tutorial' && ( (this.dataDoc.simulation_paused = true)}> )} - {this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && ( + {this.dataDoc.simulation_paused && this.simulationMode !== 'Tutorial' && ( this._simReset++)}> @@ -974,15 +987,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
- {this.simulationMode == 'Review' && this.simulationType != 'Inclined Plane' && ( + {this.simulationMode === 'Review' && this.simulationType !== 'Inclined Plane' && (
-

- <>{this.simulationType} review problems in progress! -

+

{this.simulationType} review problems in progress!


)} - {this.simulationMode == 'Review' && this.simulationType == 'Inclined Plane' && ( + {this.simulationMode === 'Review' && this.simulationType === 'Inclined Plane' && (
{!this.dataDoc.hintDialogueOpen && ( )} - (this.dataDoc.hintDialogueOpen = false)}> + (this.dataDoc.hintDialogueOpen = false)}> Hints {this.selectedQuestion.hints?.map((hint: any, index: number) => ( @@ -1030,12 +1041,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('angle of gravity') && ( @@ -1045,13 +1056,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('normal force') && ( @@ -1061,12 +1072,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('angle of normal force') && ( @@ -1076,13 +1087,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('force of static friction') && ( @@ -1092,12 +1103,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('angle of static friction') && ( @@ -1107,13 +1118,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('coefficient of static friction') && ( @@ -1127,7 +1138,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { this.changeWedgeBasedOnNewAngle(val); this.updateReviewForcesBasedOnAngle(val); }} - radianEquivalent={true} + radianEquivalent showIcon={BoolCast(this.dataDoc.simulation_showIcon)} correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('wedge angle')]} /> @@ -1158,7 +1169,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
)} - {this.simulationMode == 'Tutorial' && ( + {this.simulationMode === 'Tutorial' && (

Problem

@@ -1180,7 +1191,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent + disabled={this.dataDoc.tutorial_stepNumber === 0}>
@@ -1204,8 +1215,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
- {(this.simulationType == 'One Weight' || this.simulationType == 'Inclined Plane' || this.simulationType == 'Pendulum') &&

Resources

} - {this.simulationType == 'One Weight' && ( + {(this.simulationType === 'One Weight' || this.simulationType === 'Inclined Plane' || this.simulationType === 'Pendulum') &&

Resources

} + {this.simulationType === 'One Weight' && ( )} - {this.simulationType == 'Inclined Plane' && ( + {this.simulationType === 'Inclined Plane' && ( )} - {this.simulationType == 'Pendulum' && ( + {this.simulationType === 'Pendulum' && (
)} - {this.simulationMode == 'Review' && this.simulationType == 'Inclined Plane' && ( + {this.simulationMode === 'Review' && this.simulationType === 'Inclined Plane' && (
)} - {this.simulationMode == 'Freeform' && ( + {this.simulationMode === 'Freeform' && (
- {this.simulationType == 'One Weight' && ( + {this.simulationType === 'One Weight' && ( (this.dataDoc.elasticCollisions = !this.dataDoc.elasticCollisions)} />} label="Make collisions elastic" @@ -1334,7 +1345,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent - {(this.simulationType == 'Inclined Plane' || this.simulationType == 'Pendulum') && ( + {(this.simulationType === 'Inclined Plane' || this.simulationType === 'Pendulum') && ( (this.dataDoc.simulation_showComponentForces = !this.dataDoc.simulation_showComponentForces)} />} label="Show component force vectors" @@ -1351,80 +1362,80 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent - Speed} lowerBound={1} dataDoc={this.dataDoc} prop="simulation_speed" step={1} unit={'x'} upperBound={10} value={NumCast(this.dataDoc.simulation_speed, 2)} labelWidth={'5em'} /> - {this.dataDoc.simulation_paused && this.simulationType != 'Circular Motion' && ( + Speed} lowerBound={1} dataDoc={this.dataDoc} prop="simulation_speed" step={1} unit="x" upperBound={10} value={NumCast(this.dataDoc.simulation_speed, 2)} labelWidth="5em" /> + {this.dataDoc.simulation_paused && this.simulationType !== 'Circular Motion' && ( Gravity} lowerBound={-30} dataDoc={this.dataDoc} prop="gravity" step={0.01} - unit={'m/s2'} + unit="m/s2" upperBound={0} value={NumCast(this.dataDoc.simulation_gravity, -9.81)} effect={(val: number) => this.setupSimulation()} - labelWidth={'5em'} + labelWidth="5em" /> )} - {this.dataDoc.simulation_paused && this.simulationType != 'Pulley' && ( + {this.dataDoc.simulation_paused && this.simulationType !== 'Pulley' && ( Mass} lowerBound={1} dataDoc={this.dataDoc} prop="mass1" step={0.1} - unit={'kg'} + unit="kg" upperBound={5} value={this.mass1 ?? 1} effect={(val: number) => this.setupSimulation()} - labelWidth={'5em'} + labelWidth="5em" /> )} - {this.dataDoc.simulation_paused && this.simulationType == 'Pulley' && ( + {this.dataDoc.simulation_paused && this.simulationType === 'Pulley' && ( Red mass} lowerBound={1} dataDoc={this.dataDoc} prop="mass1" step={0.1} - unit={'kg'} + unit="kg" upperBound={5} value={this.mass1 ?? 1} effect={(val: number) => this.setupSimulation()} - labelWidth={'5em'} + labelWidth="5em" /> )} - {this.dataDoc.simulation_paused && this.simulationType == 'Pulley' && ( + {this.dataDoc.simulation_paused && this.simulationType === 'Pulley' && ( Blue mass} lowerBound={1} dataDoc={this.dataDoc} prop="mass2" step={0.1} - unit={'kg'} + unit="kg" upperBound={5} value={this.mass2 ?? 1} effect={(val: number) => this.setupSimulation()} - labelWidth={'5em'} + labelWidth="5em" /> )} - {this.dataDoc.simulation_paused && this.simulationType == 'Circular Motion' && ( + {this.dataDoc.simulation_paused && this.simulationType === 'Circular Motion' && ( Rod length} lowerBound={100} dataDoc={this.dataDoc} prop="circularMotionRadius" step={5} - unit={'m'} + unit="m" upperBound={250} value={this.circularMotionRadius} effect={(val: number) => this.setupSimulation()} - labelWidth={'5em'} + labelWidth="5em" /> )} - {this.simulationType == 'Spring' && this.dataDoc.simulation_paused && ( + {this.simulationType === 'Spring' && this.dataDoc.simulation_paused && (
Spring stiffness} @@ -1432,13 +1443,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent this._simReset++)} radianEquivalent={false} - mode={'Freeform'} - labelWidth={'7em'} + mode="Freeform" + labelWidth="7em" /> Rest length} @@ -1452,7 +1463,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent this._simReset++)} radianEquivalent={false} mode="Freeform" - labelWidth={'7em'} + labelWidth="7em" /> Starting displacement} @@ -1470,11 +1481,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
)} - {this.simulationType == 'Inclined Plane' && this.dataDoc.simulation_paused && ( + {this.simulationType === 'Inclined Plane' && this.dataDoc.simulation_paused && (
θ} @@ -1482,16 +1493,16 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { this.changeWedgeBasedOnNewAngle(val); this._simReset++; })} - radianEquivalent={true} - mode={'Freeform'} - labelWidth={'2em'} + radianEquivalent + mode="Freeform" + labelWidth="2em" /> { @@ -1513,8 +1524,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent this._simReset++)} - mode={'Freeform'} - labelWidth={'2em'} + mode="Freeform" + labelWidth="2em" />
)} - {this.simulationType == 'Inclined Plane' && !this.dataDoc.simulation_paused && ( + {this.simulationType === 'Inclined Plane' && !this.dataDoc.simulation_paused && ( <> θ: {Math.round(this.wedgeAngle * 100) / 100}° ≈ {Math.round(((this.wedgeAngle * Math.PI) / 180) * 100) / 100} rad @@ -1546,12 +1557,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} - {this.simulationType == 'Pendulum' && !this.dataDoc.simulation_paused && ( + {this.simulationType === 'Pendulum' && !this.dataDoc.simulation_paused && ( θ: {Math.round(this.pendulumAngle * 100) / 100}° ≈ {Math.round(((this.pendulumAngle * Math.PI) / 180) * 100) / 100} rad )} - {this.simulationType == 'Pendulum' && this.dataDoc.simulation_paused && ( + {this.simulationType === 'Pendulum' && this.dataDoc.simulation_paused && (
Angle} @@ -1559,13 +1570,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { this.dataDoc.pendulum_angleStart = value; this.dataDoc.pendulum_lengthStart = this.dataDoc.pendulum_length; - if (this.simulationType == 'Pendulum') { + if (this.simulationType === 'Pendulum') { const mag = this.mass1 * Math.abs(this.gravity) * Math.cos((value * Math.PI) / 180); const forceOfTension: IForce = { @@ -1598,7 +1609,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent @@ -1612,7 +1623,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { - if (this.simulationType == 'Pendulum') { + if (this.simulationType === 'Pendulum') { this.dataDoc.pendulum_angleStart = this.pendulumAngle; this.dataDoc.pendulum_lengthStart = value; this._simReset++; @@ -1627,11 +1638,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )}
- {this.simulationMode == 'Freeform' && ( + {this.simulationMode === 'Freeform' && ( - + @@ -1646,36 +1657,34 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent Position - {(!this.dataDoc.simulation_paused || this.simulationType == 'Inclined Plane' || this.simulationType == 'Circular Motion' || this.simulationType == 'Pulley') && ( - + {(!this.dataDoc.simulation_paused || this.simulationType === 'Inclined Plane' || this.simulationType === 'Circular Motion' || this.simulationType === 'Pulley') && ( + )}{' '} - {this.dataDoc.simulation_paused && this.simulationType != 'Inclined Plane' && this.simulationType != 'Circular Motion' && this.simulationType != 'Pulley' && ( + {this.dataDoc.simulation_paused && this.simulationType !== 'Inclined Plane' && this.simulationType !== 'Circular Motion' && this.simulationType !== 'Pulley' && ( )}{' '} - {(!this.dataDoc.simulation_paused || this.simulationType == 'Inclined Plane' || this.simulationType == 'Circular Motion' || this.simulationType == 'Pulley') && ( + {(!this.dataDoc.simulation_paused || this.simulationType === 'Inclined Plane' || this.simulationType === 'Circular Motion' || this.simulationType === 'Pulley') && ( )}{' '} - {this.dataDoc.simulation_paused && this.simulationType != 'Inclined Plane' && this.simulationType != 'Circular Motion' && this.simulationType != 'Pulley' && ( + {this.dataDoc.simulation_paused && this.simulationType !== 'Inclined Plane' && this.simulationType !== 'Circular Motion' && this.simulationType !== 'Pulley' && ( @@ -1758,10 +1767,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent Velocity - {(!this.dataDoc.simulation_paused || (this.simulationType != 'One Weight' && this.simulationType != 'Circular Motion')) && ( + {(!this.dataDoc.simulation_paused || (this.simulationType !== 'One Weight' && this.simulationType !== 'Circular Motion')) && ( )}{' '} - {this.dataDoc.simulation_paused && (this.simulationType == 'One Weight' || this.simulationType == 'Circular Motion') && ( + {this.dataDoc.simulation_paused && (this.simulationType === 'One Weight' || this.simulationType === 'Circular Motion') && ( )}{' '} - {(!this.dataDoc.simulation_paused || this.simulationType != 'One Weight') && ( - - )}{' '} - {this.dataDoc.simulation_paused && this.simulationType == 'One Weight' && ( + {(!this.dataDoc.simulation_paused || this.simulationType !== 'One Weight') && }{' '} + {this.dataDoc.simulation_paused && this.simulationType === 'One Weight' && ( @@ -1822,14 +1827,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponentAcceleration @@ -1842,7 +1843,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
{this.simulationType == 'Pulley' ? 'Red Weight' : ''}{this.simulationType === 'Pulley' ? 'Red Weight' : ''} X Y
- <>{this.dataDoc.mass1_positionX} m - {this.dataDoc.mass1_positionX + ''} m { this.dataDoc.mass1_xChange = value; - if (this.simulationType == 'Suspension') { - let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; - let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; - let deltaX1 = value + this.radius - x1rod; - let deltaX2 = x2rod - (value + this.radius); - let deltaY = this.getYPosFromDisplay(NumCast(this.dataDoc.mass1_positionY)) + this.radius; + if (this.simulationType === 'Suspension') { + const x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; + const x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; + const deltaX1 = value + this.radius - x1rod; + const deltaX2 = x2rod - (value + this.radius); + const deltaY = this.getYPosFromDisplay(NumCast(this.dataDoc.mass1_positionY)) + this.radius; let dir1T = Math.PI - Math.atan(deltaY / deltaX1); let dir2T = Math.atan(deltaY / deltaX2); - let tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); - let tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); + const tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); + const tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); dir1T = (dir1T * 180) / Math.PI; dir2T = (dir2T * 180) / Math.PI; const tensionForce1: IForce = { @@ -1692,15 +1701,15 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent {`${NumCast(this.dataDoc.mass1_positionY)} m`} { this.dataDoc.mass1_yChange = value; - if (this.simulationType == 'Suspension') { - let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; - let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; - let deltaX1 = NumCast(this.dataDoc.mass1_positionX) + this.radius - x1rod; - let deltaX2 = x2rod - (NumCast(this.dataDoc.mass1_positionX) + this.radius); - let deltaY = this.getYPosFromDisplay(value) + this.radius; + if (this.simulationType === 'Suspension') { + const x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; + const x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; + const deltaX1 = NumCast(this.dataDoc.mass1_positionX) + this.radius - x1rod; + const deltaX2 = x2rod - (NumCast(this.dataDoc.mass1_positionX) + this.radius); + const deltaY = this.getYPosFromDisplay(value) + this.radius; let dir1T = Math.PI - Math.atan(deltaY / deltaX1); let dir2T = Math.atan(deltaY / deltaX2); - let tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); - let tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); + const tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); + const tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); dir1T = (dir1T * 180) / Math.PI; dir2T = (dir2T * 180) / Math.PI; const tensionForce1: IForce = { @@ -1741,7 +1750,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent {`${NumCast(this.dataDoc.mass1_velocityX)} m/s`} { this.dataDoc.mass1_velocityXstart = value; this._simReset++; })} - small={true} + small mode="Freeform" /> - <>{this.dataDoc.mass1_velocityY} m/s - {this.dataDoc.mass1_velocityY + ''} m/s { this.dataDoc.mass1_velocityYstart = -value; }} - small={true} + small mode="Freeform" /> - <> - {this.dataDoc.mass1_accelerationX} m/s2 - + {this.dataDoc.mass1_accelerationX + ''} m/s2 - <> - {this.dataDoc.mass1_accelerationY} m/s2 - + {this.dataDoc.mass1_accelerationY + ''} m/s2
)} - {this.simulationMode == 'Freeform' && this.simulationType == 'Pulley' && ( + {this.simulationMode === 'Freeform' && this.simulationType === 'Pulley' && ( @@ -1869,14 +1870,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponentAcceleration @@ -1890,7 +1887,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} - {this.simulationType != 'Pendulum' && this.simulationType != 'Spring' && ( + {this.simulationType !== 'Pendulum' && this.simulationType !== 'Spring' && (

Kinematic Equations

    @@ -1907,7 +1904,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
)} - {this.simulationType == 'Spring' && ( + {this.simulationType === 'Spring' && (

Harmonic Motion Equations: Spring

    @@ -1936,7 +1933,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
)} - {this.simulationType == 'Pendulum' && ( + {this.simulationType === 'Pendulum' && (

Harmonic Motion Equations: Pendulum

    @@ -1959,11 +1956,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent - + - - + +

    - {this.simulationType == 'Circular Motion' ? 'Z' : 'Y'} + {this.simulationType === 'Circular Motion' ? 'Z' : 'Y'}

    () ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.SCREENSHOT, { + layout: { view: ScreenshotBox, dataField: 'data' }, + options: { acl: '', _layout_nativeDimEditable: true, systemIcon: 'BsCameraFill' }, +}); diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 5ebc50a1b..bc19d7ad1 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -21,6 +21,8 @@ import { OverlayView } from '../OverlayView'; import { FieldView, FieldViewProps } from './FieldView'; import { DocumentIconContainer } from './DocumentIcon'; import './ScriptingBox.scss'; +import { Docs } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; const _global = (window /* browser */ || global) /* node */ as any; const ReactTextareaAutocomplete = require('@webscopeio/react-textarea-autocomplete').default; @@ -845,3 +847,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent() ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.SCRIPTING, { + layout: { view: ScriptingBox, dataField: 'data' }, + options: { acl: '', systemIcon: 'BsFileEarmarkCodeFill' }, +}); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 13ee3250e..16767d11e 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -10,14 +10,15 @@ import { DocData } from '../../../fields/DocSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; +import { FollowLinkScript } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast, toList } from '../../../fields/Types'; import { AudioField, ImageField, VideoField } from '../../../fields/URLField'; import { emptyFunction, formatTime } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { FollowLinkScript } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { ReplayMovements } from '../../util/ReplayMovements'; import { undoBatch } from '../../util/UndoManager'; @@ -31,7 +32,8 @@ import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProvider'; import { DocumentView } from './DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; +import { FieldView, FieldViewProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import { RecordingBox } from './RecordingBox'; import './VideoBox.scss'; @@ -1162,3 +1164,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent() impl ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.VID, { + layout: { view: VideoBox, dataField: 'data' }, + options: { acl: '', _layout_currentTimecode: 0, systemIcon: 'BsFileEarmarkPlayFill' }, +}); +Docs.Prototypes.TemplateMap.set(DocumentType.REC, { + layout: { view: VideoBox, dataField: 'data' }, + options: { acl: '', _height: 100, backgroundColor: 'pink', systemIcon: 'BsFillMicFill' }, +}); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index dfe237a86..54f246b20 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -18,7 +18,9 @@ import { Cast, NumCast, StrCast, toList, WebCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, stringHash } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; @@ -37,9 +39,11 @@ import { GPTPopup } from '../pdf/GPTPopup/GPTPopup'; import { PinDocView, PinProps } from '../PinFuncs'; import { SidebarAnnos } from '../SidebarAnnos'; import { StyleProp } from '../StyleProvider'; -import { DocumentView, OpenWhere } from './DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; +import { DocumentView } from './DocumentView'; +import { FieldView, FieldViewProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import { LinkInfo } from './LinkDocPreview'; +import { OpenWhere } from './OpenWhere'; import './WebBox.scss'; const { CreateImage } = require('./WebBoxRenderer'); @@ -1242,3 +1246,8 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem ScriptingGlobals.add(function urlHash(url: string) { return stringHash(url); }); + +Docs.Prototypes.TemplateMap.set(DocumentType.WEB, { + layout: { view: WebBox, dataField: 'data' }, + options: { acl: '', _height: 300, _layout_fitWidth: true, _layout_nativeDimEditable: true, _layout_reflowVertical: true, waitForDoubleClickToClick: 'always', systemIcon: 'BsGlobe' }, +}); diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx index 8b9e3cc5d..bd66941c3 100644 --- a/src/client/views/nodes/calendarBox/CalendarBox.tsx +++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx @@ -7,6 +7,8 @@ import * as React from 'react'; import { dateRangeStrToDates } from '../../../../ClientUtils'; import { Doc } from '../../../../fields/Doc'; import { StrCast } from '../../../../fields/Types'; +import { DocumentType } from '../../../documents/DocumentTypes'; +import { Docs } from '../../../documents/Documents'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { FieldView, FieldViewProps } from '../FieldView'; @@ -119,3 +121,7 @@ export class CalendarBox extends ViewBoxBaseComponent() { ); } } +Docs.Prototypes.TemplateMap.set(DocumentType.CALENDAR, { + layout: { view: CalendarBox, dataField: 'data' }, + options: { acl: '' }, +}); diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index f311b3cdd..93371685d 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -9,11 +9,12 @@ import { Doc } from '../../../../fields/Doc'; import { Height, Width } from '../../../../fields/DocSymbols'; import { NumCast } from '../../../../fields/Types'; import { DocServer } from '../../../DocServer'; -import { Docs, DocUtils } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; import { Transform } from '../../../util/Transform'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DocumentView } from '../DocumentView'; -import { FocusViewOptions } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; import { FormattedTextBox } from './FormattedTextBox'; const horizPadding = 3; // horizontal padding to container to allow cursor to show up on either side. diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index dc388b22a..1c5ea2dd4 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -23,7 +23,7 @@ import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableCell'; import { FilterPanel } from '../../FilterPanel'; import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { OpenWhere } from '../DocumentView'; +import { OpenWhere } from '../OpenWhere'; import './DashFieldView.scss'; import { FormattedTextBox } from './FormattedTextBox'; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9bcf5027f..ad6629fc9 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -15,7 +15,7 @@ import * as React from 'react'; import { BsMarkdownFill } from 'react-icons/bs'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, StopEvent } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; -import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; +import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; @@ -28,8 +28,9 @@ import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; import { emptyFunction, numberRange, unimplementedFunction, Utils } from '../../../../Utils'; import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT'; import { DocServer } from '../../../DocServer'; -import { Docs, DocUtils } from '../../../documents/Documents'; -import { CollectionViewType } from '../../../documents/DocumentTypes'; +import { Docs } from '../../../documents/Documents'; +import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; +import { DocUtils } from '../../../documents/DocUtils'; import { DictationManager } from '../../../util/DictationManager'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; @@ -54,9 +55,11 @@ import { PinDocView, PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { styleFromLayoutString, StyleProp } from '../../StyleProvider'; import { mediaState } from '../AudioBox'; -import { DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; +import { DocumentView, DocumentViewInternal } from '../DocumentView'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; import { LinkInfo } from '../LinkDocPreview'; +import { OpenWhere } from '../OpenWhere'; import { DashDocCommentView } from './DashDocCommentView'; import { DashDocView } from './DashDocView'; import { DashFieldView } from './DashFieldView'; @@ -182,26 +185,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn); - FormattedTextBox.SetSelectOnLoad(target); + Doc.SetSelectOnLoad(target); return target; }; @@ -992,7 +977,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent }); + helpItems.push({ description: `show markdown options`, event: () => RTFMarkup.Instance.setOpen(true), icon: }); !help && cm.addItem({ description: 'Help...', subitems: helpItems, icon: 'eye' }); }; @@ -1062,7 +1047,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent>(schema: S, props: any): KeyMa return true; }); bind('Cmd-?', () => { - RTFMarkup.Instance.open(); + RTFMarkup.Instance.setOpen(true); return true; }); bind('Cmd-e', (state: EditorState, dispatch: (tx: Transaction) => void) => { diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 72a2b706d..1ff862859 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -8,7 +8,8 @@ import { List } from '../../../../fields/List'; import { NumCast, StrCast } from '../../../../fields/Types'; import { Utils } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; -import { Docs, DocUtils } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; import { CollectionViewType } from '../../../documents/DocumentTypes'; import { CollectionView } from '../../collections/CollectionView'; import { ContextMenu } from '../../ContextMenu'; diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 87b7a4069..d5fad296f 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -14,12 +14,13 @@ import { Doc, DocListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { NumCast } from '../../../../fields/Types'; import { Networking } from '../../../Network'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; +import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; -import { OpenWhereMod } from '../DocumentView'; import { ImageEditorData } from '../ImageBox'; +import { OpenWhereMod } from '../OpenWhere'; import './GenerativeFill.scss'; import Buttons from './GenerativeFillButtons'; import { BrushHandler } from './generativeFillUtils/BrushHandler'; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 4cf9e99fa..485ba7367 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -36,8 +36,10 @@ import { ViewBoxBaseComponent } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { LightboxView } from '../../LightboxView'; import { pinDataTypes as dataTypes } from '../../PinFuncs'; -import { DocumentView, OpenWhere, OpenWhereMod } from '../DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; +import { DocumentView } from '../DocumentView'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; +import { OpenWhere, OpenWhereMod } from '../OpenWhere'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; @@ -423,7 +425,7 @@ export class PresBox extends ViewBoxBaseComponent() { const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem.presentation_targetDoc).embedContainer) : DocCast(activeItem.presentation_targetDoc); const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext; if (context) { - const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.CollectionFreeFormView; + const ffview = CollectionFreeFormView.from(DocumentManager.Instance.getFirstDocumentView(context)); if (ffview?.childDocs) { PresBox.Instance._keyTimer = CollectionFreeFormView.gotoKeyframe(PresBox.Instance._keyTimer, ffview.childDocs, frameTime); ffview.layoutDoc._currentFrame = NumCast(activeFrame); @@ -745,7 +747,7 @@ export class PresBox extends ViewBoxBaseComponent() { }; _exitTrail: Opt<() => void>; - PlayTrail = (docs: Doc[]) => { + playTrail = (docs: Doc[]) => { const savedStates = docs.map(doc => { switch (doc.type) { case DocumentType.COL: @@ -2688,3 +2690,8 @@ export class PresBox extends ViewBoxBaseComponent() { ScriptingGlobals.add(function navigateToDoc(bestTarget: Doc, activeItem: Doc) { PresBox.NavigateToTarget(bestTarget, activeItem); }); + +Docs.Prototypes.TemplateMap.set(DocumentType.PRES, { + layout: { view: PresBox, dataField: 'data' }, + options: { acl: '', defaultDoubleClick: 'ignore', hideClickBehaviors: true, layout_hideLinkAnchors: true }, +}); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index cf78a45b7..af0ab3b53 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -12,7 +12,7 @@ import { List } from '../../../../fields/List'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { emptyFunction } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; -import { CollectionViewType } from '../../../documents/DocumentTypes'; +import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { SettingsManager } from '../../../util/SettingsManager'; @@ -606,3 +606,8 @@ export class PresElementBox extends ViewBoxBaseComponent() { return !(this.slideDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem; } } + +Docs.Prototypes.TemplateMap.set(DocumentType.PRESELEMENT, { + layout: { view: PresElementBox, dataField: 'data' }, + options: { acl: '', title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' }, +}); diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index f1cd1a4f7..8b2b179d3 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -9,8 +9,8 @@ import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { undoable } from '../../util/UndoManager'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { OpenWhere } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; +import { OpenWhere } from '../nodes/OpenWhere'; import { AnchorMenu } from './AnchorMenu'; import './Annotation.scss'; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 56ff2959c..c1bfdf176 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -12,7 +12,8 @@ import { Doc } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; import { Networking } from '../../../Network'; import { GPTCallType, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { AnchorMenu } from '../AnchorMenu'; import './GPTPopup.scss'; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 22355bc57..15ac73f78 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -6,21 +6,22 @@ import * as Pdfjs from 'pdfjs-dist'; import 'pdfjs-dist/web/pdf_viewer.css'; import * as PDFJSViewer from 'pdfjs-dist/web/pdf_viewer.mjs'; import * as React from 'react'; -import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, returnAll, returnFalse, returnNone, returnZero, smoothScroll } from '../../../ClientUtils'; +import { CreateLinkToActiveAudio, Doc, DocListCast, Opt } from '../../../fields/Doc'; import { DocData, Height } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, returnAll, returnFalse, returnNone, returnZero, smoothScroll } from '../../../ClientUtils'; -import { DocUtils } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { MarqueeOptionsMenu } from '../collections/collectionFreeForm'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; -import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView'; +import { FieldViewProps } from '../nodes/FieldView'; +import { FocusViewOptions } from '../nodes/FocusViewOptions'; import { LinkInfo } from '../nodes/LinkDocPreview'; import { PDFBox } from '../nodes/PDFBox'; import { ObservableReactComponent } from '../ObservableReactComponent'; @@ -331,7 +332,7 @@ export class PDFViewer extends ObservableReactComponent { this._ignoreScroll = false; if (this._scrollTimer) clearTimeout(this._scrollTimer); // wait until a scrolling pause, then create an anchor to audio this._scrollTimer = setTimeout(() => { - DocUtils.MakeLinkToActiveAudio(() => this._props.pdfBox.getAnchor(true)!, false); + CreateLinkToActiveAudio(() => this._props.pdfBox.getAnchor(true)!, false); this._scrollTimer = undefined; }, 200); } diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 384e6d654..5df934231 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -4,11 +4,14 @@ import { Tooltip } from '@mui/material'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; +import { ClientUtils } from '../../../ClientUtils'; import { Doc, DocListCastAsync, Field, FieldType } from '../../../fields/Doc'; import { DirectLinks, DocData } from '../../../fields/DocSymbols'; +import { Id } from '../../../fields/FieldSymbols'; import { DocCast, StrCast } from '../../../fields/Types'; +import { DocUtils } from '../../documents/DocUtils'; import { DocumentType } from '../../documents/DocumentTypes'; -import { DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { LinkManager } from '../../util/LinkManager'; import { SearchUtil } from '../../util/SearchUtil'; @@ -21,8 +24,6 @@ import { IRecommendation, Recommendation } from '../newlightbox/components'; import { fetchRecommendations } from '../newlightbox/utils'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import './SearchBox.scss'; -import { Id } from '../../../fields/FieldSymbols'; -import { ClientUtils } from '../../../ClientUtils'; const DAMPENING_FACTOR = 0.9; const MAX_ITERATIONS = 25; @@ -520,3 +521,8 @@ export class SearchBox extends ViewBoxBaseComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.SEARCH, { + layout: { view: SearchBox, dataField: 'data' }, + options: { acl: '', _width: 400 }, +}); diff --git a/src/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx index 7ad7b2927..6ed3b240d 100644 --- a/src/client/views/selectedDoc/SelectedDocView.tsx +++ b/src/client/views/selectedDoc/SelectedDocView.tsx @@ -8,7 +8,7 @@ import { Doc } from '../../../fields/Doc'; import { StrCast } from '../../../fields/Types'; import { DocumentManager } from '../../util/DocumentManager'; import { SettingsManager } from '../../util/SettingsManager'; -import { FocusViewOptions } from '../nodes/FieldView'; +import { FocusViewOptions } from '../nodes/FocusViewOptions'; export interface SelectedDocViewProps { selectedDocs: Doc[]; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 9083d6ca3..5fd053eef 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -174,6 +174,49 @@ export function updateCachedAcls(doc: Doc) { return undefined; } +export function ActiveInkPen(): Doc { return Doc.UserDoc(); } // prettier-ignore +export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, 'black'); } // prettier-ignore +export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ''); } // prettier-ignore +export function ActiveIsInkMask(): boolean { return BoolCast(ActiveInkPen()?.activeIsInkMask, false); } // prettier-ignore +export function ActiveInkHideTextLabels(): boolean { return BoolCast(ActiveInkPen().activeInkHideTextLabels, false); } // prettier-ignore +export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ''); } // prettier-ignore +export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ''); } // prettier-ignore +export function ActiveArrowScale(): number { return NumCast(ActiveInkPen()?.activeArrowScale, 1); } // prettier-ignore +export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, '0'); } // prettier-ignore +export function ActiveInkWidth(): number { return Number(ActiveInkPen()?.activeInkWidth); } // prettier-ignore +export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } // prettier-ignore + +export function SetActiveInkWidth(width: string): void { + !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); +} +export function SetActiveBezierApprox(bezier: string): void { + ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier); +} +export function SetActiveInkColor(value: string) { + ActiveInkPen() && (ActiveInkPen().activeInkColor = value); +} +export function SetActiveIsInkMask(value: boolean) { + ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value); +} +export function SetActiveInkHideTextLabels(value: boolean) { + ActiveInkPen() && (ActiveInkPen().activeInkHideTextLabels = value); +} +export function SetActiveFillColor(value: string) { + ActiveInkPen() && (ActiveInkPen().activeFillColor = value); +} +export function SetActiveArrowStart(value: string) { + ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); +} +export function SetActiveArrowEnd(value: string) { + ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); +} +export function SetActiveArrowScale(value: number) { + ActiveInkPen() && (ActiveInkPen().activeArrowScale = value); +} +export function SetActiveDash(dash: string): void { + !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); +} + @scriptingGlobal @Deserializable('Doc', updateCachedAcls, ['id']) export class Doc extends RefField { @@ -420,6 +463,16 @@ export class Doc extends RefField { // eslint-disable-next-line no-redeclare export namespace Doc { + // eslint-disable-next-line import/no-mutable-exports + export let SelectOnLoad: Doc | undefined; + export function SetSelectOnLoad(doc: Doc | undefined) { + SelectOnLoad = doc; + } + // eslint-disable-next-line import/no-mutable-exports + export let DocDragDataName: string = ''; + export function SetDocDragDataName(name: string) { + DocDragDataName = name; + } export function SetContainer(doc: Doc, container: Doc) { if (container !== Doc.MyRecentlyClosed) { doc.embedContainer = container; @@ -1605,6 +1658,29 @@ export namespace Doc { } } +export function RTFIsFragment(html: string) { + return html.indexOf('data-pm-slice') !== -1; +} +export function GetHrefFromHTML(html: string): string { + const parser = new DOMParser(); + const parsedHtml = parser.parseFromString(html, 'text/html'); + if (parsedHtml.body.childNodes.length === 1 && parsedHtml.body.childNodes[0].childNodes.length === 1 && (parsedHtml.body.childNodes[0].childNodes[0] as any).href) { + return (parsedHtml.body.childNodes[0].childNodes[0] as any).href; + } + return ''; +} +export function GetDocFromUrl(url: string) { + return url.startsWith(document.location.origin) ? new URL(url).pathname.split('doc/').lastElement() : ''; // docId +} + +let activeAudioLinker: (f: () => Doc | undefined, broadcast?: boolean) => (Doc | undefined)[]; +export function SetActiveAudioLinker(func: (f: () => Doc | undefined, broadcast?: boolean) => (Doc | undefined)[]) { + activeAudioLinker = func; +} +export function CreateLinkToActiveAudio(func: () => Doc | undefined, broadcast?: boolean) { + return activeAudioLinker(func, broadcast); +} + export function IdToDoc(id: string) { return DocCast(DocServer.GetCachedRefField(id)); } diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts index a2cc47a43..5eb60a2f8 100644 --- a/src/fields/RichTextUtils.ts +++ b/src/fields/RichTextUtils.ts @@ -12,7 +12,8 @@ import { DocServer } from '../client/DocServer'; import { Networking } from '../client/Network'; import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils'; import { GooglePhotos } from '../client/apis/google_docs/GooglePhotosClientUtils'; -import { DocUtils, Docs } from '../client/documents/Documents'; +import { Docs } from '../client/documents/Documents'; +import { DocUtils } from '../client/documents/DocUtils'; import { FormattedTextBox } from '../client/views/nodes/formattedText/FormattedTextBox'; import { schema } from '../client/views/nodes/formattedText/schema_rts'; import { Doc, Opt } from './Doc'; diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 8a3787768..5e5f5527f 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -277,6 +277,10 @@ export class ComputedField extends ScriptField { } } +export function FollowLinkScript() { + return ScriptField.MakeScript('return followLink(this,altKey)', { altKey: 'boolean' }); +} + ScriptingGlobals.add( // eslint-disable-next-line prefer-arrow-callback function setIndexVal(list: any[], index: number, value: any) { diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index b355e70a7..55a85ed1b 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -127,6 +127,7 @@ import './AudioUpload.scss'; import { Uploader } from './ImageUpload'; import './ImageUpload.scss'; import './MobileInterface.scss'; +import { DashboardView } from '../client/views/DashboardView'; library.add( ...[ @@ -583,7 +584,7 @@ export class MobileInterface extends React.Component { }; const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row'); + const dashboardDoc = DashboardView.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row'); const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`); -- cgit v1.2.3-70-g09d2 From 76838b7b3842c9b184e6459e29796dd14de37e8d Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 1 May 2024 09:47:11 -0400 Subject: more cycle import removal. fixed ndollar bugs introduced during lint changes. --- src/client/documents/DocUtils.ts | 6 ++++- src/client/util/CurrentUserUtils.ts | 6 ++--- src/client/util/LinkFollower.ts | 4 +-- src/client/views/MarqueeAnnotator.tsx | 3 +-- .../collections/CollectionStackedTimeline.tsx | 7 ++--- src/client/views/global/globalEnums.tsx | 5 ++++ src/client/views/global/globalScripts.ts | 4 ++- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 4 +-- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 30 +++++++++++----------- src/client/views/nodes/VideoBox.tsx | 7 +++-- src/fields/ScriptField.ts | 4 --- src/pen-gestures/ndollar.ts | 12 ++++----- 13 files changed, 50 insertions(+), 44 deletions(-) (limited to 'src/fields/ScriptField.ts') diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts index e93bbc41b..0a47e0359 100644 --- a/src/client/documents/DocUtils.ts +++ b/src/client/documents/DocUtils.ts @@ -16,7 +16,7 @@ import { InkDataFieldName, InkField } from '../../fields/InkField'; import { List, ListFieldName } from '../../fields/List'; import { ProxyField } from '../../fields/Proxy'; import { RichTextField } from '../../fields/RichTextField'; -import { ComputedField, FollowLinkScript, ScriptField } from '../../fields/ScriptField'; +import { ComputedField, ScriptField } from '../../fields/ScriptField'; import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types'; import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from '../../fields/URLField'; import { SharingPermissions } from '../../fields/util'; @@ -864,6 +864,10 @@ export namespace DocUtils { } } +export function FollowLinkScript() { + return ScriptField.MakeScript('return followLink(this,altKey)', { altKey: 'boolean' }); +} + ScriptingGlobals.add('Docs', Docs); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc, asDelegate?: boolean) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 96f5172bc..15c8fec5a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -8,13 +8,13 @@ import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; -import { FollowLinkScript, ScriptField } from "../../fields/ScriptField"; +import { ScriptField } from "../../fields/ScriptField"; import { Cast, DateCast, DocCast, StrCast } from "../../fields/Types"; import { WebField, nullAudio } from "../../fields/URLField"; import { SetCachedGroups, SharingPermissions } from "../../fields/util"; import { Gestures } from "../../pen-gestures/GestureTypes"; import { DocServer } from "../DocServer"; -import { DocUtils } from '../documents/DocUtils'; +import { DocUtils, FollowLinkScript } from '../documents/DocUtils'; import { CollectionViewType, DocumentType } from "../documents/DocumentTypes"; import { Docs, DocumentOptions, FInfo, FInfoFieldType } from "../documents/Documents"; import { DashboardView } from "../views/DashboardView"; @@ -730,7 +730,7 @@ pie title Minerals in my tap water CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.Grid, CollectionViewType.NoteTaking]), - title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}}, + title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: '{ return 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: 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_); }'} }, diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index 7ab476591..a6f6122ab 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -4,6 +4,7 @@ import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../fie import { DocumentType } from '../documents/DocumentTypes'; import { FocusViewOptions } from '../views/nodes/FocusViewOptions'; import { OpenWhere } from '../views/nodes/OpenWhere'; +import { PresBox } from '../views/nodes/trails'; import { DocumentManager } from './DocumentManager'; import { LinkManager } from './LinkManager'; import { ScriptingGlobals } from './ScriptingGlobals'; @@ -89,8 +90,7 @@ export class LinkFollower { const containerDocContext = DocumentManager.GetContextPath(sourceDoc, true); // gather all views that affect layout of sourceDoc so we can revert them after playing the rail SelectionManager.DeselectAll(); if (!DocumentManager.Instance.AddViewRenderedCb(target, dv => containerDocContext.length && dv.ComponentView?.playTrail?.(containerDocContext))) { - alert('OPEN Pres minimized'); - // PresBox.OpenPresMinimized(target, [0, 0]); + PresBox.OpenPresMinimized(target, [0, 0]); } finished?.(); } else { diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 06cae6d04..0f13de9e5 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -4,12 +4,11 @@ import * as React from 'react'; import { Doc, Opt } from '../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; -import { FollowLinkScript } from '../../fields/ScriptField'; import { NumCast } from '../../fields/Types'; import { GetEffectiveAcl } from '../../fields/util'; import { unimplementedFunction, Utils } from '../../Utils'; import { Docs } from '../documents/Documents'; -import { DocUtils } from '../documents/DocUtils'; +import { DocUtils, FollowLinkScript } from '../documents/DocUtils'; import { DragManager } from '../util/DragManager'; import { undoable, undoBatch, UndoManager } from '../util/UndoManager'; import './MarqueeAnnotator.scss'; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 50a66aa41..48d24c910 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -13,12 +13,13 @@ import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; -import { ComputedField, FollowLinkScript, ScriptField } from '../../../fields/ScriptField'; +import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { Cast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { emptyFunction, formatTime } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; +import { FollowLinkScript } from '../../documents/DocUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower'; @@ -27,6 +28,7 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; +import { VideoThumbnails } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; import { AudioWaveform } from '../nodes/audio/AudioWaveform'; import { DocumentView } from '../nodes/DocumentView'; @@ -34,7 +36,6 @@ import { FocusFuncType, StyleProviderFuncType } from '../nodes/FieldView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; import { LabelBox } from '../nodes/LabelBox'; import { OpenWhere } from '../nodes/OpenWhere'; -import { VideoBox } from '../nodes/VideoBox'; import { ObservableReactComponent } from '../ObservableReactComponent'; import './CollectionStackedTimeline.scss'; import { CollectionSubView } from './CollectionSubView'; @@ -327,7 +328,7 @@ export class CollectionStackedTimeline extends CollectionSubView 0 ? new ImageField(this.thumbnails[nearest]) : undefined; this._thumbnail = imgField?.url?.href ? imgField.url.href.replace('.png', '_m.png') : undefined; } diff --git a/src/client/views/global/globalEnums.tsx b/src/client/views/global/globalEnums.tsx index 2cf9e4162..46caa3702 100644 --- a/src/client/views/global/globalEnums.tsx +++ b/src/client/views/global/globalEnums.tsx @@ -42,3 +42,8 @@ export enum Borders { export enum Shadows { STANDARD_SHADOW = '0px 3px 4px rgba(0, 0, 0, 0.3)', } + +export enum VideoThumbnails { + DENSE = 20, + SPARSE = 5, +} diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index d17d4ff7c..e75b01ab6 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -31,9 +31,11 @@ ScriptingGlobals.add(function IsNoneSelected() { // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function setView(view: string) { +ScriptingGlobals.add(function setView(view: string, getSelected: boolean) { + if (getSelected) return SelectionManager.Docs; const selected = SelectionManager.Docs.lastElement(); selected ? (selected._type_collection = view) : console.log('[FontIconBox.tsx] changeView failed'); + return undefined; }); // toggle: Set overlay status of selected document diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index cc4b5b67f..e902d1792 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -19,7 +19,6 @@ import { CollectionView } from '../collections/CollectionView'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { CollectionSchemaView } from '../collections/collectionSchema/CollectionSchemaView'; import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox'; -import { PresElementBox } from './trails/PresElementBox'; import { SearchBox } from '../search/SearchBox'; import { AudioBox } from './AudioBox'; import { ComparisonBox } from './ComparisonBox'; @@ -47,6 +46,7 @@ import { WebBox } from './WebBox'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { ImportElementBox } from './importBox/ImportElementBox'; import { PresBox } from './trails/PresBox'; +import { PresElementBox } from './trails/PresElementBox'; type BindingProps = Without; export interface JsxBindings { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e8d1e582e..1e7f1015a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -16,13 +16,13 @@ import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { PrefetchProxy } from '../../../fields/Proxy'; import { listSpec } from '../../../fields/Schema'; -import { FollowLinkScript, ScriptField } from '../../../fields/ScriptField'; +import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { AudioField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { DocServer } from '../../DocServer'; import { Networking } from '../../Network'; -import { DocUtils } from '../../documents/DocUtils'; +import { DocUtils, FollowLinkScript } from '../../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DictationManager } from '../../util/DictationManager'; diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index d83690cdd..8d1617e66 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -11,8 +11,7 @@ import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fie import { emptyFunction } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; -import { SelectionManager } from '../../../util/SelectionManager'; -import { SettingsManager } from '../../../util/SettingsManager'; +import { SnappingManager } from '../../../util/SnappingManager'; import { undoable, UndoManager } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { ViewBoxBaseComponent } from '../../DocComponent'; @@ -125,7 +124,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { return ( () { let text: string | undefined; let getStyle: (val: string) => any = () => {}; let icon: IconProp = 'caret-down'; - const isViewDropdown = script?.script.originalScript.startsWith('setView'); + const isViewDropdown = script?.script.originalScript.startsWith('{ return setView'); if (isViewDropdown) { - const selected = SelectionManager.Docs; + const selected = Array.from(script?.script.run({ _readOnly_: true }).result) as Doc[]; + // const selected = SelectionManager.Docs; if (selected.lastElement()) { if (StrCast(selected.lastElement().type) === DocumentType.COL) { text = StrCast(selected.lastElement()._type_collection); @@ -178,15 +178,15 @@ export class FontIconBox extends ViewBoxBaseComponent() { icon={} text={text} type={Type.TERT} - color={SettingsManager.userColor} - background={SettingsManager.userVariantColor} + color={SnappingManager.userColor} + background={SnappingManager.userVariantColor} popup={} fillWidth /> ); } } else { - return

- <> - {this.dataDoc.mass2_accelerationX} m/s2 - + {this.dataDoc.mass2_accelerationX + ''} m/s2 - <> - {this.dataDoc.mass2_accelerationY} m/s2 - + {this.dataDoc.mass2_accelerationY + ''} m/s2