diff options
Diffstat (limited to 'src')
22 files changed, 122 insertions, 118 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index cf7a61d24..ac865382d 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -26,7 +26,10 @@ import { SerializationHelper } from './util/SerializationHelper'; */ export namespace DocServer { // eslint-disable-next-line import/no-mutable-exports - export let _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {}; + let _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {}; + export function Cache() { + return _cache; + } function errorFunc(): never { throw new Error("Can't use DocServer without calling init first"); @@ -54,15 +57,6 @@ export namespace DocServer { } } - export function FindDocByTitle(title: string) { - const foundDocId = - title && - Array.from(Object.keys(_cache)) - .filter(key => _cache[key] instanceof Doc) - .find(key => (_cache[key] as Doc).title === title); - - return foundDocId ? (_cache[foundDocId] as Doc) : undefined; - } let _socket: Socket; // this client's distinct GUID created at initialization let USER_ID: string; @@ -228,7 +222,7 @@ export namespace DocServer { const deserializeField = getSerializedField.then(async fieldJson => { // deserialize const field = await SerializationHelper.Deserialize(fieldJson); - if (force && field instanceof Doc && cached instanceof Doc) { + if (force && field && cached instanceof Doc) { cached[UpdatingFromServer] = true; Array.from(Object.keys(field)).forEach(key => { const fieldval = field[key]; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 31a99896f..c2a52cae9 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -94,6 +94,7 @@ export class SharingManager extends React.Component<{}> { super(props); makeObservable(this); SharingManager.Instance = this; + DocumentView.ShareOpen = this.open; } /** diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index d92a73b63..fc6a330f7 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -22,7 +22,6 @@ import { Docs, DocumentOptions } from '../documents/Documents'; import { dropActionType } from '../util/DropActionTypes'; import { HistoryUtil } from '../util/History'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { SharingManager } from '../util/SharingManager'; import { SnappingManager } from '../util/SnappingManager'; import { undoBatch, undoable } from '../util/UndoManager'; import { ContextMenu } from './ContextMenu'; @@ -30,6 +29,7 @@ import './DashboardView.scss'; import { MainViewModal } from './MainViewModal'; import { ObservableReactComponent } from './ObservableReactComponent'; import { Colors } from './global/globalEnums'; +import { DocumentView } from './nodes/DocumentView'; import { ButtonType } from './nodes/FontIconBox/FontIconBox'; enum DashboardGroup { @@ -170,7 +170,7 @@ export class DashboardView extends ObservableReactComponent<{}> { ContextMenu.Instance.addItem({ description: `Share Dashboard`, - event: () => SharingManager.Instance.open(undefined, dashboard), + event: () => DocumentView.ShareOpen(undefined, dashboard), icon: 'edit', }); ContextMenu.Instance.addItem({ @@ -535,7 +535,7 @@ ScriptingGlobals.add(function createNewDashboard() { }, 'creates a new dashboard when called'); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { - SharingManager.Instance.open(undefined, dashboard); + DocumentView.ShareOpen(undefined, dashboard); }, 'opens sharing dialog for Dashboard'); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 97ff346e4..70b20aec7 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -23,9 +23,7 @@ import { OpenWhere } from './nodes/OpenWhere'; * Many of these methods only make sense for specific viewBox'es, but they should be written to * be as general as possible */ -export interface ViewBoxInterface { - fieldKey?: string; - annotationKey?: string; +export class ViewBoxInterface<P> extends ObservableReactComponent<React.PropsWithChildren<P>> { promoteCollection?: () => void; // moves contents of collection to parent updateIcon?: () => void; // updates the icon representation of the document getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) @@ -61,7 +59,7 @@ export interface ViewBoxInterface { snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number }; search?: (str: string, bwd?: boolean, clear?: boolean) => boolean; dontRegisterView?: () => boolean; // KeyValueBox's don't want to register their views - isUnstyledView?: () => boolean; // SchemaView and KeyValue are untyled -- not tiles, no opacity + isUnstyledView?: () => boolean; // SchemaView and KeyValue are unstyled -- not titles, no opacity, no animations } /** * DocComponent returns a React base class used by Doc views with accessors for unpacking the Document,layoutDoc, and dataDoc's @@ -113,7 +111,7 @@ export function DocComponent<P extends DocComponentProps>() { * Example views include: InkingStroke, FontIconBox, EquationBox, etc */ export function ViewBoxBaseComponent<P extends FieldViewProps>() { - class Component extends ObservableReactComponent<React.PropsWithChildren<P>> { + class Component extends ViewBoxInterface<P> { constructor(props: P) { super(props); makeObservable(this); @@ -168,7 +166,7 @@ export function ViewBoxBaseComponent<P extends FieldViewProps>() { * Example views include: PDFBox, ImageBox, MapBox, etc */ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() { - class Component extends ObservableReactComponent<React.PropsWithChildren<P>> { + class Component extends ViewBoxInterface<P> { @observable _annotationKeySuffix = () => 'annotations'; @observable _isAnyChildContentActive = false; @@ -219,8 +217,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() { return this.fieldKey + (this._annotationKeySuffix() ? '_' + this._annotationKeySuffix() : ''); } - @action.bound - removeDocument(docIn: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean, dontAddToRemoved?: boolean): boolean { + override removeDocument = (docIn: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean, dontAddToRemoved?: boolean): boolean => { const effectiveAcl = GetEffectiveAcl(this.dataDoc); const docs = toList(docIn).filter(fdoc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(fdoc) === AclAdmin); @@ -249,12 +246,11 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() { } return false; - } + }; // this is called with the document that was dragged and the collection to move it into. // if the target collection is the same as this collection, then the move will be allowed. // otherwise, the document being moved must be able to be removed from its container before // moving it into the target. - @action.bound moveDocument = (docs: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean, annotationKey?: string): boolean => { if (Doc.AreProtosEqual(this._props.Document, targetCollection)) { return true; @@ -265,8 +261,8 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() { } return false; }; - @action.bound - addDocument = (docIn: Doc | Doc[], annotationKey?: string): boolean => { + + override addDocument = (docIn: Doc | Doc[], annotationKey?: string): boolean => { const docs = toList(docIn); if (this._props.filterAddDocument?.(docs) === false || docs.find(fdoc => Doc.AreProtosEqual(fdoc, this.Document) && Doc.LayoutField(fdoc) === Doc.LayoutField(this.Document))) { return false; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 432b02782..4262c2d57 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -33,7 +33,6 @@ import { Colors } from './global/globalEnums'; import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView'; 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'; @@ -137,7 +136,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora if (titleFieldKey === 'title') { dv.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-'); } - KeyValueBox.SetField(dv.Document, titleFieldKey, this._accumulatedTitle); + Doc.SetField(dv.Document, titleFieldKey, this._accumulatedTitle); }), 'edit title' ); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 62fc73c78..f497ca447 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -42,14 +42,14 @@ import './InkStroke.scss'; import { InkStrokeProperties } from './InkStrokeProperties'; import { InkTangentHandles } from './InkTangentHandles'; import { FieldView, FieldViewProps } from './nodes/FieldView'; -import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; +import { FormattedTextBox, FormattedTextBoxProps } from './nodes/formattedText/FormattedTextBox'; import { PinDocView, PinProps } from './PinFuncs'; import { StyleProp } from './StyleProp'; const { INK_MASK_SIZE } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @observer -export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface { +export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() { static readonly MaskDim = INK_MASK_SIZE; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big) public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); @@ -337,9 +337,9 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() ); }; - _subContentView: ViewBoxInterface | undefined; - setSubContentView = (doc: ViewBoxInterface) => { - this._subContentView = doc; + _subContentView: ViewBoxInterface<FormattedTextBoxProps> | undefined; + setSubContentView = (box: ViewBoxInterface<FormattedTextBoxProps>) => { + this._subContentView = box; }; @computed get fillColor(): string { const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask); diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index b6dfed687..259ffbbc5 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -7,9 +7,11 @@ import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how- import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; import { AssignAllExtensions } from '../../extensions/Extensions'; +import { Doc } from '../../fields/Doc'; import { FieldLoader } from '../../fields/FieldLoader'; import { BranchingTrailManager } from '../util/BranchingTrailManager'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; +import { LinkFollower } from '../util/LinkFollower'; import { PingManager } from '../util/PingManager'; import { ReplayMovements } from '../util/ReplayMovements'; import { TrackMovements } from '../util/TrackMovements'; @@ -18,7 +20,7 @@ import { MainView } from './MainView'; import { CollectionView } from './collections/CollectionView'; import { CollectionFreeFormInfoUI } from './collections/collectionFreeForm/CollectionFreeFormInfoUI'; import './global/globalScripts'; -import { LinkFollower } from '../util/LinkFollower'; +import { KeyValueBox } from './nodes/KeyValueBox'; dotenv.config(); @@ -66,6 +68,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' }; // iniitialize plugin apis CollectionFreeFormInfoUI.Init(); LinkFollower.Init(); + KeyValueBox.Init(); root.render(<MainView />); }, 0); })(); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 7c16e0ddb..b03c1a64e 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -41,7 +41,6 @@ import './PropertiesView.scss'; import { DefaultStyleProvider, SetFilterOpener as SetPropertiesFilterOpener } from './StyleProvider'; 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'; @@ -208,7 +207,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps fontSize={10} GetValue={() => editableContents} SetValue={(value: string) => { - value !== '-multiple-' && docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); + value !== '-multiple-' && docs.map(doc => Doc.SetField(doc, key, value, true)); return true; }} /> @@ -252,10 +251,10 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps if (value.indexOf(':') !== -1) { const newVal = value[0].toUpperCase() + value.substring(1, value.length); const splits = newVal.split(':'); - KeyValueBox.SetField(doc, splits[0], splits[1], true); + Doc.SetField(doc, splits[0], splits[1], true); const tags = StrCast(doc.tags, ':'); if (tags.includes(`${splits[0]}:`) && splits[1] === 'undefined') { - KeyValueBox.SetField(doc, 'tags', `"${tags.replace(splits[0] + ':', '')}"`, true); + Doc.SetField(doc, 'tags', `"${tags.replace(splits[0] + ':', '')}"`, true); } return true; } @@ -645,7 +644,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps DocumentView.Selected().map(dv => Doc.SetInPlace(dv.Document, 'title', value, true)); } else if (this.dataDoc) { if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, 'title', value, true); - else KeyValueBox.SetField(this.dataDoc, 'title', value as string, true); + else Doc.SetField(this.dataDoc, 'title', value as string, true); } }; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index fc9e2e39b..0ee3575f3 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -2,7 +2,7 @@ import { action, IReactionDisposer, makeObservable, observable, reaction } from import { observer } from 'mobx-react'; import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivHeight, DivWidth, incrementTitleCopy, UpdateIcon } from '../../../ClientUtils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivHeight, DivWidth, incrementTitleCopy, returnTrue, UpdateIcon } from '../../../ClientUtils'; import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc'; import { AclAdmin, AclEdit, DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; @@ -49,7 +49,8 @@ export class CollectionDockingView extends CollectionSubView() { private _reactionDisposer?: IReactionDisposer; private _lightboxReactionDisposer?: IReactionDisposer; private _containerRef = React.createRef<HTMLDivElement>(); - public _flush: UndoManager.Batch | undefined; + private _flush: UndoManager.Batch | undefined; + private _unmounting = false; private _ignoreStateChange = ''; public tabMap: Set<any> = new Set(); public get HasFullScreen() { @@ -330,6 +331,7 @@ export class CollectionDockingView extends CollectionSubView() { }; componentDidMount: () => void = async () => { + this._props.setContentViewBox?.(this); this._unmounting = false; SetPropSetterCb('title', this.titleChanged); // this overrides any previously assigned callback for the property if (this._containerRef.current) { @@ -366,7 +368,6 @@ export class CollectionDockingView extends CollectionSubView() { } }; - _unmounting = false; componentWillUnmount: () => void = () => { this._unmounting = true; try { @@ -383,6 +384,9 @@ export class CollectionDockingView extends CollectionSubView() { this._lightboxReactionDisposer?.(); }; + // ViewBoxInterface overrides + override isUnstyledView = returnTrue; + @action onResize = () => { const cur = this._containerRef.current; @@ -437,8 +441,8 @@ export class CollectionDockingView extends CollectionSubView() { } else { const tabTarget = (e.target as HTMLElement)?.parentElement?.className.includes('lm_tab') ? (e.target as HTMLElement).parentElement : (e.target as HTMLElement); const map = Array.from(this.tabMap).find(tab => tab.element[0] === tabTarget); - if (map?.DashDoc && DocumentView.getFirstDocumentView(map.DashDoc)) { - DocumentView.SelectView(DocumentView.getFirstDocumentView(map.DashDoc), false); + if (map?.DashDoc && DocumentView.getDocumentView(map.DashDoc, this.DocumentView?.())) { + DocumentView.SelectView(DocumentView.getDocumentView(map.DashDoc, this.DocumentView?.()), false); } } } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 969b98d91..6ea6bbfbd 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -32,7 +32,6 @@ import { ObservableReactComponent } from '../ObservableReactComponent'; import { StyleProp } from '../StyleProp'; import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import { FieldViewProps, StyleProviderFuncType } from '../nodes/FieldView'; -import { KeyValueBox } from '../nodes/KeyValueBox'; import { OpenWhere } from '../nodes/OpenWhere'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; @@ -568,7 +567,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { height={13} fontSize={12} GetValue={() => Field.toKeyValueString(doc, key)} - SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} + SetValue={(value: string) => Doc.SetField(doc, key, value, true)} /> ); } @@ -600,7 +599,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { const key = match[1]; const assign = match[2]; const val = match[3]; - KeyValueBox.SetField(doc, key, assign + val, false); + Doc.SetField(doc, key, assign + val, false); return true; } return false; diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index b30954ffd..77247e675 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -28,7 +28,6 @@ import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; import { FocusViewOptions } from '../../nodes/FocusViewOptions'; -import { KeyValueBox } from '../../nodes/KeyValueBox'; import { CollectionSubView } from '../CollectionSubView'; import './CollectionSchemaView.scss'; import { SchemaColumnHeader } from './SchemaColumnHeader'; @@ -174,7 +173,9 @@ export class CollectionSchemaView extends CollectionSubView() { document.removeEventListener('keydown', this.onKeyDown); } - isUnstyledView = returnTrue; // used by style provide via ViewBoxInterface + // ViewBoxInterface overrides + override isUnstyledView = returnTrue; // used by style provider : turns off opacity, animation effects, scaling + rowIndex = (doc: Doc) => this.sortedDocs.docs.indexOf(doc); @action @@ -606,7 +607,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; setColumnValues = (key: string, value: string) => { - this.childDocs.forEach(doc => KeyValueBox.SetField(doc, key, value)); + this.childDocs.forEach(doc => Doc.SetField(doc, key, value)); return true; }; @@ -804,7 +805,6 @@ export class CollectionSchemaView extends CollectionSubView() { ); } get renderKeysMenu() { - console.log('RNDERMENUT:' + this._columnMenuIndex); return ( <div className="schema-column-menu" style={{ left: 0, minWidth: CollectionSchemaView._minColWidth }}> <input className="schema-key-search-input" type="text" onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} /> diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index ee6987e89..3df7ecdbe 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -29,7 +29,6 @@ import { DefaultStyleProvider } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; import { DocFocusOrOpen, returnEmptyDocViewList } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; -import { KeyValueBox } from '../../nodes/KeyValueBox'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { FInfotoColType } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; @@ -144,7 +143,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro this._props.finishEdit?.(); return true; } - const ret = KeyValueBox.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(fieldProps.Document) ? true : undefined); + const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(fieldProps.Document) ? true : undefined); this._props.finishEdit?.(); return ret; }, 'edit schema cell')} @@ -357,7 +356,7 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp onChange={undoBatch((value: React.ChangeEvent<HTMLInputElement> | undefined) => { if ((value?.nativeEvent as any).shiftKey) { this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? '')); - } else KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? '')); + } else Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? '')); })} /> <EditableView @@ -371,7 +370,7 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp this._props.finishEdit?.(); return true; } - const set = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined); + const set = Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined); this._props.finishEdit?.(); return set; })} @@ -437,7 +436,7 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC placeholder={StrCast(this._props.Document[this._props.fieldKey], 'select...')} options={options} isMulti={false} - onChange={val => KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)} + onChange={val => Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)} /> </div> </div> diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 6ae6bb228..474d54119 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -19,11 +19,10 @@ import { StyleProp } from '../StyleProp'; import './ComparisonBox.scss'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; -import { KeyValueBox } from './KeyValueBox'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; @observer -export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface { +export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); } @@ -188,9 +187,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() // where (this) is replaced by the text in the fieldKey slot abd this.excludeWords is repalced by the conetnts of the excludeWords field // The GPT call will put the "answer" in the second slot of the comparison (eg., text_2) if (whichSlot.endsWith('2') && !layoutTemplateString?.includes(whichSlot)) { - const queryText = altText?.replace('(this)', subjectText); // TODO: this should be done in KeyValueBox.setField but it doesn't know about the fieldKey ... + const queryText = altText?.replace('(this)', subjectText); // TODO: this should be done in Doc.setField but it doesn't know about the fieldKey ... if (queryText?.match(/\(\(.*\)\)/)) { - KeyValueBox.SetField(this.Document, whichSlot, ':=' + queryText, false); // make the second slot be a computed field on the data doc that calls ChatGpt + Doc.SetField(this.Document, whichSlot, ':=' + queryText, false); // make the second slot be a computed field on the data doc that calls ChatGpt } } return layoutTemplateString; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f8ce50c98..4a5c32b4c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -31,7 +31,6 @@ import { MakeTemplate, makeUserTemplateButton } from '../../util/DropConverter'; import { UPDATE_SERVER_CACHE } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SearchUtil } from '../../util/SearchUtil'; -import { SharingManager } from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; import { UndoManager, undoBatch, undoable } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; @@ -47,7 +46,6 @@ import { DocumentLinksButton } from './DocumentLinksButton'; import './DocumentView.scss'; import { FieldViewProps, FieldViewSharedProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; -import { KeyValueBox } from './KeyValueBox'; import { OpenWhere } from './OpenWhere'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { PresEffect, PresEffectDirection } from './trails'; @@ -115,7 +113,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document @observable _mounted = false; // turn off all pointer events if component isn't yet mounted (enables nested Docs in alternate UI textboxes that appear on hover which otherwise would grab focus from the text box, reverting to the original UI ) @observable _isContentActive: boolean | undefined = undefined; @observable _pointerEvents: 'none' | 'all' | 'visiblePainted' | undefined = undefined; - @observable _componentView: Opt<ViewBoxInterface> = undefined; // needs to be accessed from DocumentView wrapper class + @observable _componentView: Opt<ViewBoxInterface<FieldViewProps>> = undefined; // needs to be accessed from DocumentView wrapper class @observable _animateScaleTime: Opt<number> = undefined; // milliseconds for animating between views. defaults to 300 if not uset @observable _animateScalingTo = 0; @@ -627,7 +625,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document const constantItems: ContextMenuProps[] = []; if (!Doc.IsSystem(this.Document) && this.Document._type_collection !== CollectionViewType.Docking) { constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => DocUtils.Zip(this.Document) }); - (this.Document._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this._docView), icon: 'users' }); + constantItems.push({ description: 'Share', event: () => DocumentView.ShareOpen(this._docView), icon: 'users' }); if (this._props.removeDocument && Doc.ActiveDashboard !== this.Document) { // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions) constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' }); @@ -699,7 +697,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document screenToLocalContent = () => this._props.ScreenToLocalTransform().translate(0, -this.headerMargin); onClickFunc = this.disableClickScriptFunc ? undefined : () => this.onClickHandler; setHeight = (height: number) => { !this._props.suppressSetHeight && (this.layoutDoc._height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), height)); } // prettier-ignore - setContentView = action((view: ViewBoxInterface) => { this._componentView = view; }); // prettier-ignore + setContentView = action((view: ViewBoxInterface<FieldViewProps>) => { this._componentView = view; }); // prettier-ignore isContentActive = (): boolean | undefined => this._isContentActive; childFilters = () => [...this._props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)]; @@ -725,7 +723,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document @computed get viewBoxContents() { TraceMobx(); const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString; - const noBackground = this.Document.isGroup && !this._props.LayoutTemplateString?.includes(KeyValueBox.name) && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent'); + const noBackground = this.Document.isGroup && !this._componentView?.isUnstyledView?.() && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent'); return ( <div className="documentView-contentsView" @@ -784,7 +782,9 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.Document; const background = StrCast( this.layoutDoc.layout_headingColor, - StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SnappingManager.userBackgroundColor)) + // StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, + StrCast(Doc.SharingDoc().headingColor, SnappingManager.userBackgroundColor) + // ) ); const dropdownWidth = this._titleRef.current?._editing || this._changingTitleField ? Math.max(10, (this._titleDropDownInnerWidth * this.titleHeight) / 30) : 0; const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', ''); @@ -839,7 +839,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document Doc.UserDoc().layout_showTitle = input?.substring(1) ? input.substring(1) : 'title'; } } else if (showTitle && !showTitle.includes(';') && !showTitle.includes('Date') && showTitle !== 'author') { - KeyValueBox.SetField(targetDoc, showTitle, input); + Doc.SetField(targetDoc, showTitle, input); } return true; })} @@ -940,7 +940,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document borderRadius: this.borderRounding, pointerEvents: this._pointerEvents === 'visiblePainted' ? 'none' : this._pointerEvents, // visible painted means that the underlying doc contents are irregular and will process their own pointer events (otherwise, the contents are expected to fill the entire doc view box so we can handle pointer events here) }}> - {this._componentView instanceof KeyValueBox ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.Document[Animation])} + {this._componentView?.isUnstyledView?.() ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.Document[Animation])} {borderPath?.jsx} </div> ); @@ -980,6 +980,8 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document @observer export class DocumentView extends DocComponent<DocumentViewProps>() { public static ROOT_DIV = 'documentView-effectsWrapper'; + // Sharing Manager + public static ShareOpen: (target?: DocumentView, targetDoc?: Doc) => void; // LinkFollower public static FollowLink: (linkDoc: Opt<Doc>, sourceDoc: Doc, altKey: boolean) => boolean; // selection funcs @@ -1049,7 +1051,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { @observable public static LongPress = false; @computed private get shouldNotScale() { - return (this.layout_fitWidth && !this.nativeWidth) || this._props.LayoutTemplateString?.includes(KeyValueBox.name) || [CollectionViewType.Docking].includes(this.Document._type_collection as any); + return (this.layout_fitWidth && !this.nativeWidth) || this.ComponentView?.isUnstyledView?.(); } @computed private get effectiveNativeWidth() { return this.shouldNotScale ? 0 : this.nativeWidth || NumCast(this.layoutDoc.width); @@ -1144,10 +1146,10 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { } @computed get nativeWidth() { - return this._props.LayoutTemplateString?.includes(KeyValueBox.name) ? 0 : returnVal(this._props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this._props.TemplateDataDocument, !this.layout_fitWidth)); + return returnVal(this._props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this._props.TemplateDataDocument, !this.layout_fitWidth)); } @computed get nativeHeight() { - return this._props.LayoutTemplateString?.includes(KeyValueBox.name) ? 0 : returnVal(this._props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this._props.TemplateDataDocument, !this.layout_fitWidth)); + return returnVal(this._props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this._props.TemplateDataDocument, !this.layout_fitWidth)); } @computed public get centeringX() { return this._props.dontCenter?.includes('x') ? 0 : this.Xshift; } // prettier-ignore @computed public get centeringY() { return this._props.dontCenter?.includes('y') ? 0 : this.Yshift; } // prettier-ignore diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index c6c77d8d2..8a37000f7 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -45,7 +45,7 @@ export interface FieldViewSharedProps { containerViewPath?: () => DocumentView[]; fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document isGroupActive?: () => string | undefined; // is this document part of a group that is active - setContentViewBox?: (view: ViewBoxInterface) => any; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox + setContentViewBox?: (view: ViewBoxInterface<any>) => any; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox PanelWidth: () => number; PanelHeight: () => number; isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index cd591fa42..66e210c03 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -13,10 +13,10 @@ import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { SetupDrag } from '../../util/DragManager'; import { CompiledScript } from '../../util/Scripting'; -import { undoBatch } from '../../util/UndoManager'; +import { undoable } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { ObservableReactComponent } from '../ObservableReactComponent'; +import { ViewBoxBaseComponent } from '../DocComponent'; import { DocumentIconContainer } from './DocumentIcon'; import { FieldView, FieldViewProps } from './FieldView'; import { ImageBox } from './ImageBox'; @@ -31,7 +31,7 @@ export type KVPScript = { onDelegate: boolean; }; @observer -export class KeyValueBox extends ObservableReactComponent<FieldViewProps> { +export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() { public static LayoutString() { return FieldView.LayoutString(KeyValueBox, 'data'); } @@ -48,19 +48,17 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> { componentDidMount() { this._props.setContentViewBox?.(this); } - isKeyValueBox = returnTrue; - able = returnAlways; - layout_fitWidth = returnTrue; - onClickScriptDisable = returnAlways; + // ViewBoxInterface overrides + override isUnstyledView = returnTrue; // used by style provider via ViewBoxInterface - ignore opacity, anim effects, titles + override dontRegisterView = returnTrue; // don't want to follow links to this view + override onClickScriptDisable = returnAlways; @observable private rows: KeyValuePair[] = []; @observable _splitPercentage = 50; get fieldDocToLayout() { - return DocCast(this._props.Document); + return DocCast(this.Document); } - isUnstyledView = returnTrue; // used by style provider via ViewBoxInterface - dontRegisterView = returnTrue; // used by ViewBoxInterface @action onEnterKey = (e: React.KeyboardEvent): void => { @@ -83,7 +81,7 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> { * @param value * @returns */ - public static CompileKVPScript(rawvalueIn: string): KVPScript | undefined { + public static CompileKVPScript = (rawvalueIn: string): KVPScript | undefined => { let rawvalue = rawvalueIn; const onDelegate = rawvalue.startsWith('='); rawvalue = onDelegate ? rawvalue.substring(1) : rawvalue; @@ -97,9 +95,9 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> { script = ScriptField.CompileScript(value, {}, true, undefined, DocumentIconContainer.getTransformer()); } return !script.compiled ? undefined : { script, type, onDelegate }; - } + }; - public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) { + public static ApplyKVPScript = (doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) => { const { script, type, onDelegate } = kvpScript; // const target = onDelegate ? Doc.Layout(doc.layout) : Doc.GetProto(doc); // bcz: TODO need to be able to set fields on layout templates const target = forceOnDelegate || onDelegate || key.startsWith('_') ? doc : DocCast(doc.proto, doc); @@ -127,14 +125,13 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> { return true; } return false; - } + }; - @undoBatch - public static SetField(doc: Doc, key: string, value: string, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) { - const script = this.CompileKVPScript(value); + public static SetField = undoable((doc: Doc, key: string, value: string, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) => { + const script = KeyValueBox.CompileKVPScript(value); if (!script) return false; - return this.ApplyKVPScript(doc, key, script, forceOnDelegate, setResult); - } + return KeyValueBox.ApplyKVPScript(doc, key, script, forceOnDelegate, setResult); + }, 'Set Doc Field'); onPointerDown = (e: React.PointerEvent): void => { if (e.buttons === 1 && this._props.isSelected()) { @@ -249,15 +246,15 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> { getFieldView = () => { const rows = this.rows.filter(row => row.isChecked); if (rows.length > 1) { - const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this._props.Document).title}`, _chromeHidden: true }); + const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.Document).title}`, _chromeHidden: true }); rows.forEach(row => { - const field = this.createFieldView(DocCast(this._props.Document), row); + const field = this.createFieldView(DocCast(this.Document), row); field && Doc.AddDocToList(parent, 'data', field); row.uncheck(); }); return parent; } - return rows.length ? this.createFieldView(DocCast(this._props.Document), rows.lastElement()) : undefined; + return rows.length ? this.createFieldView(DocCast(this.Document), rows.lastElement()) : undefined; }; createFieldView = (templateDoc: Doc, row: KeyValuePair) => { @@ -305,7 +302,7 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> { openItems.push({ description: 'Default Perspective', event: () => { - this._props.addDocTab(this._props.Document, OpenWhere.close); + this._props.addDocTab(this.Document, OpenWhere.close); this._props.addDocTab(this.fieldDocToLayout, OpenWhere.addRight); }, icon: 'image', @@ -341,6 +338,9 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> { </div> ); } + public static Init() { + Doc.SetField = KeyValueBox.SetField; + } } Docs.Prototypes.TemplateMap.set(DocumentType.KVP, { diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 878b0e54c..397cc15ed 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -15,7 +15,6 @@ import { EditableView } from '../EditableView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DefaultStyleProvider } from '../StyleProvider'; import { returnEmptyDocViewList } from './DocumentView'; -import { KeyValueBox } from './KeyValueBox'; import './KeyValueBox.scss'; import './KeyValuePair.scss'; import { OpenWhere } from './OpenWhere'; @@ -136,7 +135,7 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> { pinToPres: returnZero, }} GetValue={() => Field.toKeyValueString(this._props.doc, this._props.keyName)} - SetValue={(value: string) => KeyValueBox.SetField(this._props.doc, this._props.keyName, value)} + SetValue={(value: string) => Doc.SetField(this._props.doc, this._props.keyName, value)} /> </div> </td> diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 9038ed27a..b8b9f63a9 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -25,7 +25,7 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { CollectionStackingView } from '../collections/CollectionStackingView'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; +import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { Colors } from '../global/globalEnums'; import { PDFViewer } from '../pdf/PDFViewer'; import { PinDocView, PinProps } from '../PinFuncs'; @@ -39,7 +39,7 @@ import './PDFBox.scss'; import { CreateImage } from './WebBoxRenderer'; @observer -export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface { +export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); } @@ -283,7 +283,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem !this.Document._layout_fitWidth && (this.Document._height = NumCast(this.Document._width) * (nh / nw)); }; - public search = action((searchString: string, bwd?: boolean, clear: boolean = false) => { + override search = action((searchString: string, bwd?: boolean, clear: boolean = false) => { if (!this._searching && !clear) { this._searching = true; setTimeout(() => { diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 1003bc473..198652310 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -48,7 +48,7 @@ import './WebBox.scss'; const { CreateImage } = require('./WebBoxRenderer'); @observer -export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface { +export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } @@ -112,7 +112,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem } @action - search = (searchString: string, bwd?: boolean, clear: boolean = false) => { + override search = (searchString: string, bwd?: boolean, clear: boolean = false) => { if (!this._searching && !clear) { this._searching = true; setTimeout(() => { @@ -1000,7 +1000,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem }; _innerCollectionView: CollectionFreeFormView | undefined; zoomScaling = () => this._innerCollectionView?.zoomScaling() ?? 1; - setInnerContent = (component: ViewBoxInterface) => { + setInnerContent = (component: ViewBoxInterface<any>) => { this._innerCollectionView = component as CollectionFreeFormView; }; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 8a4a7718a..321fdbb91 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -43,7 +43,7 @@ import { CollectionStackingView } from '../../collections/CollectionStackingView import { CollectionTreeView } from '../../collections/CollectionTreeView'; import { ContextMenu } from '../../ContextMenu'; import { ContextMenuProps } from '../../ContextMenuItem'; -import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; +import { ViewBoxAnnotatableComponent } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { LightboxView } from '../../LightboxView'; import { AnchorMenu } from '../../pdf/AnchorMenu'; @@ -73,12 +73,12 @@ import { schema } from './schema_rts'; import { SummaryView } from './SummaryView'; // import * as applyDevTools from 'prosemirror-dev-tools'; -interface FormattedTextBoxProps extends FieldViewProps { +export interface FormattedTextBoxProps extends FieldViewProps { onBlur?: () => void; // callback when text loses focus autoFocus?: boolean; // whether text should get input focus when created } @observer -export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextBoxProps>() implements ViewBoxInterface { +export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextBoxProps>() { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); } diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 1ff862859..bf11dfe62 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -7,13 +7,11 @@ import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { NumCast, StrCast } from '../../../../fields/Types'; import { Utils } from '../../../../Utils'; -import { DocServer } from '../../../DocServer'; import { Docs } from '../../../documents/Documents'; -import { DocUtils } from '../../../documents/DocUtils'; import { CollectionViewType } from '../../../documents/DocumentTypes'; +import { DocUtils } from '../../../documents/DocUtils'; import { CollectionView } from '../../collections/CollectionView'; import { ContextMenu } from '../../ContextMenu'; -import { KeyValueBox } from '../KeyValueBox'; import { FormattedTextBox } from './FormattedTextBox'; import { wrappingInputRule } from './prosemirrorPatches'; import { RichTextMenu } from './RichTextMenu'; @@ -311,10 +309,10 @@ export class RichTextRules { } }; const getTitledDoc = (title: string) => { - if (!DocServer.FindDocByTitle(title)) { + if (!Doc.FindDocByTitle(title)) { Docs.Create.TextDocument('', { title: title, _width: 400, _layout_fitWidth: true, _layout_autoHeight: true }); } - const titledDoc = DocServer.FindDocByTitle(title); + const titledDoc = Doc.FindDocByTitle(title); return titledDoc ? Doc.BestEmbedding(titledDoc) : titledDoc; }; const target = getTitledDoc(docTitle); @@ -337,14 +335,14 @@ export class RichTextRules { const assign = match[4] === ':' ? (match[4] = '') : match[4]; const value = match[5]; const dataDoc = value === undefined ? !fieldKey.startsWith('_') : !assign?.startsWith('='); - const getTitledDoc = (title: string) => DocServer.FindDocByTitle(title); + const getTitledDoc = (title: string) => Doc.FindDocByTitle(title); // if the value has commas assume its an array (unless it's part of a chat gpt call indicated by '((' ) if (value?.includes(',') && !value.startsWith('((')) { const values = value.split(','); const strs = values.some(v => !v.match(/^[-]?[0-9.]$/)); this.Document[DocData][fieldKey] = strs ? new List<string>(values) : new List<number>(values.map(v => Number(v))); } else if (value) { - KeyValueBox.SetField( + Doc.SetField( this.Document, fieldKey, assign + value, @@ -367,7 +365,7 @@ export class RichTextRules { // pass the contents between '((' and '))' to chatGPT and append the result new InputRule(/(^|[^=])(\(\(.*\)\))$/, (state, match, start, end) => { let count = 0; // ignore first return value which will be the notation that chat is pending a result - KeyValueBox.SetField(this.Document, '', match[2], false, (gptval: FieldResult) => { + Doc.SetField(this.Document, '', match[2], false, (gptval: FieldResult) => { if (count) { const tr = this.TextBox.EditorView?.state.tr.insertText(' ' + (gptval as string)); tr && this.TextBox.EditorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(end + 2), tr.doc.resolve(end + 2 + (gptval as string).length)))); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index bb6995398..725221a66 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -225,7 +225,17 @@ export class Doc extends RefField { @observable public static GuestTarget: Doc | undefined = undefined; @observable public static GuestMobile: Doc | undefined = undefined; @observable.shallow public static CurrentlyLoading: Doc[] = observable([]); - // removes from currently loading display + // DocServer api + public static FindDocByTitle(title: string) { + const foundDocId = + title && + Array.from(Object.keys(DocServer.Cache())) + .filter(key => DocServer.Cache()[key] instanceof Doc) + .find(key => (DocServer.Cache()[key] as Doc).title === title); + + return foundDocId ? (DocServer.Cache()[foundDocId] as Doc) : undefined; + } + // removes from currently loading doc set public static removeCurrentlyLoading(doc: Doc) { if (Doc.CurrentlyLoading) { const index = Doc.CurrentlyLoading.indexOf(doc); @@ -238,12 +248,14 @@ export class Doc extends RefField { runInAction(() => Doc.CurrentlyLoading.push(doc)); } } - + // LinkManager api public static AddLink: (link: Doc, checkExists?: boolean) => void; public static DeleteLink: (link: Doc) => void; public static Links: (link: Doc | undefined) => Doc[]; public static getOppositeAnchor: (linkDoc: Doc, anchor: Doc) => Doc | undefined; - + // KeyValue SetField + public static SetField: (doc: Doc, key: string, value: string, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) => boolean; + // UserDoc "API" public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); } // prettier-ignore public static get MyUserDocView() { return DocCast(Doc.UserDoc().myUserDocView); } // prettier-ignore public static get MyDockedBtns() { return DocCast(Doc.UserDoc().myDockedBtns); } // prettier-ignore |