diff options
Diffstat (limited to 'src')
52 files changed, 492 insertions, 494 deletions
diff --git a/src/Utils.ts b/src/Utils.ts index 7f83ab8f5..9a94694a2 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -3,10 +3,10 @@ import v5 = require('uuid/v5'); import { ColorState } from 'react-color'; import * as rp from 'request-promise'; import { Socket } from 'socket.io'; +import { DocumentType } from './client/documents/DocumentTypes'; import { Colors } from './client/views/global/globalEnums'; import { Message } from './server/Message'; import Color = require('color'); -import { DocumentType } from './client/documents/DocumentTypes'; export namespace Utils { export let CLICK_TIME = 300; diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 53c7b857a..5fdea131b 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,6 +1,5 @@ import { runInAction } from 'mobx'; import * as rp from 'request-promise'; -import * as io from 'socket.io-client'; import { Doc, DocListCast, Opt } from '../fields/Doc'; import { UpdatingFromServer } from '../fields/DocSymbols'; import { FieldLoader } from '../fields/FieldLoader'; @@ -8,13 +7,13 @@ import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols'; import { ObjectField } from '../fields/ObjectField'; import { RefField } from '../fields/RefField'; import { DocCast, StrCast } from '../fields/Types'; -import MobileInkOverlay from '../mobile/MobileInkOverlay'; +//import MobileInkOverlay from '../mobile/MobileInkOverlay'; import { emptyFunction, Utils } from '../Utils'; import { GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from './../server/Message'; import { DocumentType } from './documents/DocumentTypes'; import { LinkManager } from './util/LinkManager'; import { SerializationHelper } from './util/SerializationHelper'; -import { GestureOverlay } from './views/GestureOverlay'; +//import { GestureOverlay } from './views/GestureOverlay'; /** * This class encapsulates the transfer and cross-client synchronization of @@ -189,17 +188,17 @@ export namespace DocServer { // mobile ink overlay socket events to communicate between mobile view and desktop view _socket.addEventListener('receiveGesturePoints', (content: GestureContent) => { - MobileInkOverlay.Instance.drawStroke(content); + // MobileInkOverlay.Instance.drawStroke(content); }); _socket.addEventListener('receiveOverlayTrigger', (content: MobileInkOverlayContent) => { - GestureOverlay.Instance.enableMobileInkOverlay(content); - MobileInkOverlay.Instance.initMobileInkOverlay(content); + //GestureOverlay.Instance.enableMobileInkOverlay(content); + // MobileInkOverlay.Instance.initMobileInkOverlay(content); }); _socket.addEventListener('receiveUpdateOverlayPosition', (content: UpdateMobileInkOverlayPositionContent) => { - MobileInkOverlay.Instance.updatePosition(content); + // MobileInkOverlay.Instance.updatePosition(content); }); _socket.addEventListener('receiveMobileDocumentUpload', (content: MobileDocumentUploadContent) => { - MobileInkOverlay.Instance.uploadDocument(content); + // MobileInkOverlay.Instance.uploadDocument(content); }); } diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 05879a247..2da9927c0 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -6,7 +6,7 @@ import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { Utils } from '../../../Utils'; import { DocServer } from '../../DocServer'; import { Docs } from '../../documents/Documents'; -import { DocumentDecorations } from '../../views/DocumentDecorations'; +import { DocumentView } from '../../views/nodes/DocumentView'; import { FieldView, FieldViewProps } from '../../views/nodes/FieldView'; import '../../views/nodes/WebBox.scss'; import './YoutubeBox.scss'; @@ -355,9 +355,9 @@ export class YoutubeBox extends React.Component<FieldViewProps> { </div> ); - const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; + const frozen = !this.props.isSelected() || DocumentView.Interacting; - const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? '-interactive' : ''); + const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentView.Interacting ? '-interactive' : ''); return ( <> <div className={classname}>{content}</div> diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1186446e1..919958b24 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -19,7 +19,6 @@ import { aggregateBounds, OmitKeys, Utils } from '../../Utils'; import { YoutubeBox } from '../apis/youtube/YoutubeBox'; import { DocServer } from '../DocServer'; import { Networking } from '../Network'; -import { DocumentManager } from '../util/DocumentManager'; import { DragManager, dropActionType } from '../util/DragManager'; import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox'; import { FollowLinkScript } from '../util/LinkFollower'; @@ -34,12 +33,12 @@ import { ContextMenuProps } from '../views/ContextMenuItem'; import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss'; import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke } from '../views/InkingStroke'; import { AudioBox } from '../views/nodes/AudioBox'; -import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { ColorBox } from '../views/nodes/ColorBox'; import { ComparisonBox } from '../views/nodes/ComparisonBox'; import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox'; import { EquationBox } from '../views/nodes/EquationBox'; import { FieldViewProps } from '../views/nodes/FieldView'; +import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; import { FunctionPlotBox } from '../views/nodes/FunctionPlotBox'; import { ImageBox } from '../views/nodes/ImageBox'; @@ -394,7 +393,7 @@ export class DocumentOptions { onPointerUp?: ScriptField; _forceActive?: BOOLt = new BoolInfo('flag to handle pointer events when not selected (or otherwise active)'); _dragOnlyWithinContainer?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere'); - _raiseWhenDragged?: BOOLt = new BoolInfo('whether a document is brought to front when dragged.'); + _keepZWhenDragged?: BOOLt = new BoolInfo('whether a document should keep its z-order when dragged.'); childDragAction?: DROPt = new DAInfo('what should happen to the child documents when they are dragged from the collection'); dropConverter?: ScriptField; // script to run when documents are dropped on this Document. dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else"); @@ -1371,7 +1370,7 @@ export namespace DocUtils { export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { - broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1)); + broadcastEvent && runInAction(() => (Doc.RecordingEvent = Doc.RecordingEvent + 1)); 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' }); @@ -1380,7 +1379,6 @@ export namespace DocUtils { 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'; - const sv = DocumentManager.Instance.getDocumentView(source); if (target.doc === Doc.UserDoc()) return undefined; const makeLink = action((linkDoc: Doc, showPopup?: number[]) => { @@ -1840,8 +1838,6 @@ export namespace DocUtils { } if (overwriteDoc) { Doc.removeCurrentlyLoading(overwriteDoc); - // loading doc icons are just labels. so any icon views of loading docs need to be replaced with the proper icon view. - DocumentManager.Instance.getAllDocumentViews(overwriteDoc).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify())); } generatedDocuments.push(doc); } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index ea995e4af..d52e389d6 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -18,7 +18,6 @@ import { CollectionViewType, DocumentType } from "../documents/DocumentTypes"; import { TreeViewType } from "../views/collections/CollectionTreeView"; import { DashboardView } from "../views/DashboardView"; import { Colors } from "../views/global/globalEnums"; -import { MainView } from "../views/MainView"; import { OpenWhere } from "../views/nodes/DocumentView"; import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox"; import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; @@ -621,9 +620,9 @@ export class CurrentUserUtils { static freeTools(): Button[] { return [ - { title: "Bottom", icon: "arrows-down-to-line",toolTip: "Make doc topmost", btnType: ButtonType.ClickButton, expertMode: false, funcs: {}, scripts: { onClick: 'sendToBack()'}}, // Only when floating document is selected in freeform - { title: "Top", icon: "arrows-up-to-line", toolTip: "Make doc bottommost", btnType: ButtonType.ClickButton, expertMode: false, funcs: {}, scripts: { onClick: 'bringToFront()'}}, // Only when floating document is selected in freeform - { title: "Z order", icon: "z", toolTip: "Bring Forward on Drag (double click to set for all)",waitForDoubleClickToClick:true, btnType: ButtonType.ToggleButton, expertMode: false, funcs: {}, scripts: { onClick: 'toggleRaiseOnDrag(false, _readOnly_)', onDoubleClick:`{ return toggleRaiseOnDrag(true, _readOnly_)`}}, // Only when floating document is selected in freeform + { title: "Bottom", icon: "arrows-down-to-line",toolTip: "Make doc topmost", btnType: ButtonType.ClickButton, expertMode: false, funcs: {}, scripts: { onClick: 'sendToBack()'}}, // Only when floating document is selected in freeform + { title: "Top", icon: "arrows-up-to-line", toolTip: "Make doc bottommost", btnType: ButtonType.ClickButton, expertMode: false, funcs: {}, scripts: { onClick: 'bringToFront()'}}, // Only when floating document is selected in freeform + { title: "Z order", icon: "z", toolTip: "Keep Z order on Drag", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {}, scripts: { onClick: '{ return toggleRaiseOnDrag(_readOnly_);}'}}, // Only when floating document is selected in freeform ] } static viewTools(): Button[] { @@ -841,7 +840,6 @@ export class CurrentUserUtils { doc.isSystem ?? (doc.isSystem = true); doc.title ?? (doc.title = Doc.CurrentUserEmail); Doc.noviceMode ?? (Doc.noviceMode = true); - doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true); doc._showLabel ?? (doc._showLabel = true); doc.textAlign ?? (doc.textAlign = "left"); doc.activeTool = InkTool.None; @@ -996,8 +994,5 @@ export class CurrentUserUtils { ScriptingGlobals.add(function MySharedDocs() { return Doc.MySharedDocs; }, "document containing all shared Docs"); ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode"); ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering"); -ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called"); -ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called"); -ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
\ No newline at end of file diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 717473aa1..0fd7e840c 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -11,7 +11,7 @@ import { Utils } from '../../Utils'; import { Docs } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; import { DictationOverlay } from '../views/DictationOverlay'; -import { DocumentView, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView'; +import { DocumentView, OpenWhere } from '../views/nodes/DocumentView'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 5b627c2f3..c2827dac7 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,4 +1,4 @@ -import { action, computed, observable, ObservableSet } from 'mobx'; +import { action, computed, observable, ObservableSet, observe, reaction } from 'mobx'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { AclAdmin, AclEdit, Animation } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; @@ -23,7 +23,6 @@ export class DocumentManager { //global holds all of the nodes (regardless of which collection they're in) @observable _documentViews = new Set<DocumentView>(); @observable public LinkAnchorBoxViews: DocumentView[] = []; - @observable public RecordingEvent = 0; @observable public LinkedDocumentViews: { a: DocumentView; b: DocumentView; l: Doc }[] = []; @computed public get DocumentViews() { return Array.from(this._documentViews).filter(view => !(view.ComponentView instanceof KeyValueBox) && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(view.docViewPath))); @@ -41,7 +40,22 @@ export class DocumentManager { } //private constructor so no other class can create a nodemanager - private constructor() {} + private constructor() { + if (!Doc.CurrentlyLoading) Doc.CurrentlyLoading = []; + observe(Doc.CurrentlyLoading, change => { + // watch CurrentlyLoading-- when something is loaded, it's removed from the list and we have to update its icon if it were iconified since LoadingBox icons are different than the media they become + switch (change.type as any) { + case 'update': + break; + case 'remove': + // DocumentManager.Instance.getAllDocumentViews(change as any).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify())); + break; + case 'splice': + (change as any).removed.forEach((doc: Doc) => DocumentManager.Instance.getAllDocumentViews(doc).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify()))); + break; + } + }); + } private _viewRenderedCbs: { doc: Doc; func: (dv: DocumentView) => any }[] = []; public AddViewRenderedCb = (doc: Opt<Doc>, func: (dv: DocumentView) => any) => { @@ -310,7 +324,7 @@ export class DocumentManager { if (viewSpec && docView) { if (docView.ComponentView instanceof FormattedTextBox) docView.ComponentView?.focus(viewSpec, options); PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500); - Doc.linkFollowHighlight(viewSpec ? [docView.rootDoc, viewSpec]: docView.rootDoc, undefined, options.effect); + Doc.linkFollowHighlight(viewSpec ? [docView.rootDoc, viewSpec] : docView.rootDoc, undefined, options.effect); if (options.playAudio) DocumentManager.playAudioAnno(docView.rootDoc); if (options.toggleTarget && (!options.didMove || docView.rootDoc.hidden)) docView.rootDoc.hidden = !docView.rootDoc.hidden; if (options.effect) docView.rootDoc[Animation] = options.effect; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 489c9df4a..05da5ebed 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -61,12 +61,6 @@ export namespace DragManager { export let StartWindowDrag: Opt<(e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void>; export let CompleteWindowDrag: Opt<(aborted: boolean) => void>; - export function GetRaiseWhenDragged() { - return BoolCast(Doc.UserDoc()._raiseWhenDragged); - } - export function SetRaiseWhenDragged(val: boolean) { - Doc.UserDoc()._raiseWhenDragged = val; - } export function Root() { const root = document.getElementById('root'); if (!root) { @@ -605,18 +599,9 @@ export namespace DragManager { } } -ScriptingGlobals.add(function toggleRaiseOnDrag(forAllDocs: boolean, readOnly?: boolean) { +ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) { if (readOnly) { - if (SelectionManager.Views().length) - return SelectionManager.Views().some(dv => dv.rootDoc.raiseWhenDragged) - ? Colors.MEDIUM_BLUE - : SelectionManager.Views().some(dv => dv.rootDoc.raiseWhenDragged === false) - ? 'transparent' - : DragManager.GetRaiseWhenDragged() - ? Colors.MEDIUM_BLUE_ALT - : Colors.LIGHT_BLUE; - return DragManager.GetRaiseWhenDragged() ? Colors.MEDIUM_BLUE_ALT : 'transparent'; + return SelectionManager.Views().some(dv => dv.rootDoc.keepZWhenDragged); } - if (!forAllDocs) SelectionManager.Views().map(dv => (dv.rootDoc.raiseWhenDragged ? (dv.rootDoc.raiseWhenDragged = undefined) : dv.rootDoc.raiseWhenDragged === false ? (dv.rootDoc.raiseWhenDragged = true) : (dv.rootDoc.raiseWhenDragged = false))); - else DragManager.SetRaiseWhenDragged(!DragManager.GetRaiseWhenDragged()); + SelectionManager.Views().map(dv => (dv.rootDoc.keepZWhenDragged = !dv.rootDoc.keepZWhenDragged)); }); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index f235be192..dbdf580cd 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -1,15 +1,15 @@ -import { DragManager } from './DragManager'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { DocumentType } from '../documents/DocumentTypes'; import { ObjectField } from '../../fields/ObjectField'; -import { StrCast, Cast } from '../../fields/Types'; -import { Docs } from '../documents/Documents'; -import { ScriptField, ComputedField } from '../../fields/ScriptField'; import { RichTextField } from '../../fields/RichTextField'; -import { ImageField } from '../../fields/URLField'; -import { ScriptingGlobals } from './ScriptingGlobals'; import { listSpec } from '../../fields/Schema'; +import { ScriptField } from '../../fields/ScriptField'; +import { Cast, StrCast } from '../../fields/Types'; +import { ImageField } from '../../fields/URLField'; +import { Docs } from '../documents/Documents'; +import { DocumentType } from '../documents/DocumentTypes'; import { ButtonType } from '../views/nodes/FontIconBox/FontIconBox'; +import { DragManager } from './DragManager'; +import { ScriptingGlobals } from './ScriptingGlobals'; export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = undefined, templateField: string = '') { if (templateField) Doc.GetProto(doc).title = templateField; /// the title determines which field is being templated diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index c79894032..8973306bf 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -282,7 +282,7 @@ export class GroupManager extends React.Component<{}> { */ private get groupCreationModal() { const contents = ( - <div className="group-create" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}> + <div className="group-create" style={{ background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }}> <div className="group-heading" style={{ marginBottom: 0 }}> <p> <b>New Group</b> @@ -367,7 +367,7 @@ export class GroupManager extends React.Component<{}> { const groups = this.groupSort === 'ascending' ? this.allGroups.sort(sortGroups) : this.groupSort === 'descending' ? this.allGroups.sort(sortGroups).reverse() : this.allGroups; return ( - <div className="group-interface" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}> + <div className="group-interface" style={{ background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }}> {this.groupCreationModal} {this.currentGroup ? <GroupMemberView group={this.currentGroup} onCloseButtonClick={action(() => (this.currentGroup = undefined))} /> : null} <div className="group-heading"> diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index 535d8ccc2..7de0f336f 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -29,7 +29,7 @@ export class GroupMemberView extends React.Component<GroupMemberViewProps> { const hasEditAccess = GroupManager.Instance.hasEditAccess(this.props.group); return !this.props.group ? null : ( - <div className="editing-interface" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}> + <div className="editing-interface" style={{ background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }}> <div className="editing-header"> <input className="group-title" diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index d154c48a4..64aa7ba9b 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -1,13 +1,115 @@ import * as rp from 'request-promise'; import { DocServer } from '../DocServer'; -import { Doc } from '../../fields/Doc'; +import { Doc, DocListCast, Field, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { Utils } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; +import { StrCast } from '../../fields/Types'; export namespace SearchUtil { export type HighlightingResult = { [id: string]: { [key: string]: string[] } }; + export function SearchCollection(rootDoc: Opt<Doc>, query: string) { + const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; + const blockedKeys = [ + 'x', + 'y', + 'proto', + 'width', + 'layout_autoHeight', + 'acl-Override', + 'acl-Guest', + 'embedContainer', + 'zIndex', + 'height', + 'text_scrollHeight', + 'text_height', + 'cloneFieldFilter', + 'isDataDoc', + 'text_annotations', + 'dragFactory_count', + 'text_noTemplate', + 'proto_embeddings', + 'isSystem', + 'layout_fieldKey', + 'isBaseProto', + 'xMargin', + 'yMargin', + 'links', + 'layout', + 'layout_keyValue', + 'layout_fitWidth', + 'type_collection', + 'title_custom', + 'freeform_panX', + 'freeform_panY', + 'freeform_scale', + ]; + query = query.toLowerCase(); + + const results = new Map<Doc, string[]>(); + if (rootDoc) { + const docs = DocListCast(rootDoc[Doc.LayoutFieldKey(rootDoc)]); + const docIDs: String[] = []; + SearchUtil.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => { + const dtype = StrCast(doc.type) as DocumentType; + if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth >= 0) { + const hlights = new Set<string>(); + SearchUtil.documentKeys(doc).forEach( + key => + Field.toString(doc[key] as Field) + .toLowerCase() + .includes(query) && hlights.add(key) + ); + blockedKeys.forEach(key => hlights.delete(key)); + + if (Array.from(hlights.keys()).length > 0) { + results.set(doc, Array.from(hlights.keys())); + } + } + docIDs.push(doc[Id]); + }); + } + return results; + } + /** + * @param {Doc} doc - doc for which keys are returned + * + * This method returns a list of a document doc's keys. + */ + export function documentKeys(doc: Doc) { + const keys: { [key: string]: boolean } = {}; + Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false))); + return Array.from(Object.keys(keys)); + } + + /** + * @param {Doc[]} docs - docs to be searched through recursively + * @param {number, Doc => void} func - function to be called on each doc + * + * This method iterates through an array of docs and all docs within those docs, calling + * the function func on each doc. + */ + export function foreachRecursiveDoc(docs: Doc[], func: (depth: number, doc: Doc) => void) { + let newarray: Doc[] = []; + var depth = 0; + const visited: Doc[] = []; + while (docs.length > 0) { + newarray = []; + docs.filter(d => d && !visited.includes(d)).forEach(d => { + visited.push(d); + const fieldKey = Doc.LayoutFieldKey(d); + const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView'); + const data = d[annos ? fieldKey + '_annotations' : fieldKey]; + data && newarray.push(...DocListCast(data)); + const sidebar = d[fieldKey + '_sidebar']; + sidebar && newarray.push(...DocListCast(sidebar)); + func(depth, d); + }); + docs = newarray; + depth++; + } + } export interface IdSearchResult { ids: string[]; lines: string[][]; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index bb370e1a4..720badd40 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -13,7 +13,6 @@ import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager import { DocServer } from '../DocServer'; import { Networking } from '../Network'; import { MainViewModal } from '../views/MainViewModal'; -import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { GroupManager } from './GroupManager'; import './SettingsManager.scss'; import { undoBatch } from './UndoManager'; @@ -67,15 +66,15 @@ export class SettingsManager extends React.Component<{}> { } }; - @computed get userColor() { + @computed public static get userColor() { return StrCast(Doc.UserDoc().userColor); } - @computed get userVariantColor() { + @computed public static get userVariantColor() { return StrCast(Doc.UserDoc().userVariantColor); } - @computed get userBackgroundColor() { + @computed public static get userBackgroundColor() { return StrCast(Doc.UserDoc().userBackgroundColor); } @@ -150,35 +149,35 @@ export class SettingsManager extends React.Component<{}> { val: scheme, }))} dropdownType={DropdownType.SELECT} - color={this.userColor} + color={SettingsManager.userColor} fillWidth /> {userTheme === ColorScheme.Custom && ( <Group formLabel="Custom Theme"> <ColorPicker tooltip={'User Color'} // - color={this.userColor} + color={SettingsManager.userColor} type={Type.SEC} icon={<FaFillDrip />} - selectedColor={this.userColor} + selectedColor={SettingsManager.userColor} setSelectedColor={this.switchUserColor} setFinalColor={this.switchUserColor} /> <ColorPicker tooltip={'User Background Color'} - color={this.userColor} + color={SettingsManager.userColor} type={Type.SEC} icon={<FaPalette />} - selectedColor={this.userBackgroundColor} + selectedColor={SettingsManager.userBackgroundColor} setSelectedColor={this.switchUserBackgroundColor} setFinalColor={this.switchUserBackgroundColor} /> <ColorPicker tooltip={'User Variant Color'} - color={this.userColor} + color={SettingsManager.userColor} type={Type.SEC} icon={<FaPalette />} - selectedColor={this.userVariantColor} + selectedColor={SettingsManager.userVariantColor} setSelectedColor={this.switchUserVariantColor} setFinalColor={this.switchUserVariantColor} /> @@ -198,7 +197,7 @@ export class SettingsManager extends React.Component<{}> { onClick={e => (Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'author_date')} toggleStatus={Doc.UserDoc().layout_showTitle !== undefined} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> <Toggle formLabel={'Show Full Toolbar'} @@ -207,25 +206,25 @@ export class SettingsManager extends React.Component<{}> { onClick={e => (Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])} toggleStatus={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> <Toggle formLabel={'Show Button Labels'} formLabelPlacement={'right'} toggleType={ToggleType.SWITCH} - onClick={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())} - toggleStatus={FontIconBox.GetShowLabels()} + onClick={e => Doc.SetShowIconLabels(!Doc.GetShowIconLabels())} + toggleStatus={Doc.GetShowIconLabels()} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> <Toggle formLabel={'Recognize Ink Gestures'} formLabelPlacement={'right'} toggleType={ToggleType.SWITCH} - onClick={e => FontIconBox.SetRecognizeGestures(!FontIconBox.GetRecognizeGestures())} - toggleStatus={FontIconBox.GetRecognizeGestures()} + onClick={e => Doc.SetRecognizeGestures(!Doc.GetRecognizeGestures())} + toggleStatus={Doc.GetRecognizeGestures()} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> <Toggle formLabel={'Hide Labels In Ink Shapes'} @@ -234,7 +233,7 @@ export class SettingsManager extends React.Component<{}> { onClick={e => (Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels)} toggleStatus={BoolCast(Doc.UserDoc().activeInkHideTextLabels)} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> <Toggle formLabel={'Open Ink Docs in Lightbox'} @@ -243,7 +242,7 @@ export class SettingsManager extends React.Component<{}> { onClick={e => (Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox)} toggleStatus={BoolCast(Doc.UserDoc().openInkInLightbox)} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> <Toggle formLabel={'Show Link Lines'} @@ -252,7 +251,7 @@ export class SettingsManager extends React.Component<{}> { onClick={e => (Doc.UserDoc().showLinkLines = !Doc.UserDoc().showLinkLines)} toggleStatus={BoolCast(Doc.UserDoc().showLinkLines)} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> </div> ); @@ -284,7 +283,7 @@ export class SettingsManager extends React.Component<{}> { <div className="tab-column-content"> {/* <NumberInput/> */} <Group formLabel={'Default Font'}> - <NumberDropdown color={this.userColor} numberDropdownType={'input'} min={0} max={50} step={2} type={Type.TERT} number={0} unit={'px'} setNumber={() => {}} /> + <NumberDropdown color={SettingsManager.userColor} numberDropdownType={'input'} min={0} max={50} step={2} type={Type.TERT} number={0} unit={'px'} setNumber={() => {}} /> <Dropdown items={fontFamilies.map(val => { return { @@ -301,7 +300,7 @@ export class SettingsManager extends React.Component<{}> { setSelectedVal={val => { this.changeFontFamily(val as string); }} - color={this.userColor} + color={SettingsManager.userColor} fillWidth /> </Group> @@ -329,12 +328,12 @@ export class SettingsManager extends React.Component<{}> { @computed get passwordContent() { return ( <div className="password-content"> - <EditableText placeholder="Current password" type={Type.SEC} color={this.userColor} val={''} setVal={val => this.changeVal(val as string, 'curr')} fillWidth password /> - <EditableText placeholder="New password" type={Type.SEC} color={this.userColor} val={''} setVal={val => this.changeVal(val as string, 'new')} fillWidth password /> - <EditableText placeholder="Confirm new password" type={Type.SEC} color={this.userColor} val={''} setVal={val => this.changeVal(val as string, 'conf')} fillWidth password /> + <EditableText placeholder="Current password" type={Type.SEC} color={SettingsManager.userColor} val={''} setVal={val => this.changeVal(val as string, 'curr')} fillWidth password /> + <EditableText placeholder="New password" type={Type.SEC} color={SettingsManager.userColor} val={''} setVal={val => this.changeVal(val as string, 'new')} fillWidth password /> + <EditableText placeholder="Confirm new password" type={Type.SEC} color={SettingsManager.userColor} val={''} setVal={val => this.changeVal(val as string, 'conf')} fillWidth password /> {!this.passwordResultText ? null : <div className={`${this.passwordResultText.startsWith('Error') ? 'error' : 'success'}-text`}>{this.passwordResultText}</div>} - <Button type={Type.SEC} text={'Forgot Password'} color={this.userColor} /> - <Button type={Type.TERT} text={'Submit'} onClick={this.changePassword} color={this.userColor} /> + <Button type={Type.SEC} text={'Forgot Password'} color={SettingsManager.userColor} /> + <Button type={Type.TERT} text={'Submit'} onClick={this.changePassword} color={SettingsManager.userColor} /> </div> ); } @@ -394,10 +393,10 @@ export class SettingsManager extends React.Component<{}> { dropdownType={DropdownType.SELECT} type={Type.TERT} placement="bottom-start" - color={this.userColor} + color={SettingsManager.userColor} fillWidth /> - <Toggle formLabel={'Playground Mode'} toggleType={ToggleType.SWITCH} toggleStatus={this.playgroundMode} onClick={this.playgroundModeToggle} color={this.userColor} /> + <Toggle formLabel={'Playground Mode'} toggleType={ToggleType.SWITCH} toggleStatus={this.playgroundMode} onClick={this.playgroundModeToggle} color={SettingsManager.userColor} /> </div> <div className="tab-column-title" style={{ marginTop: 20, marginBottom: 10 }}> Freeform Navigation @@ -422,15 +421,21 @@ export class SettingsManager extends React.Component<{}> { dropdownType={DropdownType.SELECT} type={Type.TERT} placement="bottom-start" - color={this.userColor} + color={SettingsManager.userColor} /> </div> </div> <div className="tab-column"> <div className="tab-column-title">Permissions</div> <div className="tab-column-content"> - <Button text={'Manage Groups'} type={Type.TERT} onClick={() => GroupManager.Instance?.open()} color={this.userColor} /> - <Toggle toggleType={ToggleType.SWITCH} formLabel={'Default access private'} color={this.userColor} toggleStatus={BoolCast(Doc.defaultAclPrivate)} onClick={action(() => (Doc.defaultAclPrivate = !Doc.defaultAclPrivate))} /> + <Button text={'Manage Groups'} type={Type.TERT} onClick={() => GroupManager.Instance?.open()} color={SettingsManager.userColor} /> + <Toggle + toggleType={ToggleType.SWITCH} + formLabel={'Default access private'} + color={SettingsManager.userColor} + toggleStatus={BoolCast(Doc.defaultAclPrivate)} + onClick={action(() => (Doc.defaultAclPrivate = !Doc.defaultAclPrivate))} + /> </div> </div> </div> @@ -449,7 +454,7 @@ export class SettingsManager extends React.Component<{}> { ]; return ( <div className="settings-interface"> - <div className="settings-panel" style={{ background: this.userColor }}> + <div className="settings-panel" style={{ background: SettingsManager.userColor }}> <div className="settings-tabs"> {tabs.map(tab => { const isActive = this.activeTab === tab.title; @@ -457,8 +462,8 @@ export class SettingsManager extends React.Component<{}> { <div key={tab.title} style={{ - background: isActive ? this.userBackgroundColor : this.userColor, - color: isActive ? this.userColor : this.userBackgroundColor, + background: isActive ? SettingsManager.userBackgroundColor : SettingsManager.userColor, + color: isActive ? SettingsManager.userColor : SettingsManager.userBackgroundColor, }} className={'tab-control ' + (isActive ? 'active' : 'inactive')} onClick={action(() => (this.activeTab = tab.title))}> @@ -469,19 +474,19 @@ export class SettingsManager extends React.Component<{}> { </div> <div className="settings-user"> - <div style={{ color: this.userBackgroundColor }}>{DashVersion}</div> - <div className="settings-username" style={{ color: this.userBackgroundColor }}> + <div style={{ color: SettingsManager.userBackgroundColor }}>{DashVersion}</div> + <div className="settings-username" style={{ color: SettingsManager.userBackgroundColor }}> {Doc.CurrentUserEmail} </div> - <Button text={Doc.GuestDashboard ? 'Exit' : 'Log Out'} type={Type.TERT} color={this.userVariantColor} onClick={() => window.location.assign(Utils.prepend('/logout'))} /> + <Button text={Doc.GuestDashboard ? 'Exit' : 'Log Out'} type={Type.TERT} color={SettingsManager.userVariantColor} onClick={() => window.location.assign(Utils.prepend('/logout'))} /> </div> </div> <div className="close-button"> - <Button icon={<FontAwesomeIcon icon={'times'} size={'lg'} />} onClick={this.close} color={this.userColor} /> + <Button icon={<FontAwesomeIcon icon={'times'} size={'lg'} />} onClick={this.close} color={SettingsManager.userColor} /> </div> - <div className="settings-content" style={{ color: this.userColor, background: this.userBackgroundColor }}> + <div className="settings-content" style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}> {tabs.map(tab => ( <div key={tab.title} className={'tab-section ' + (this.activeTab === tab.title ? 'active' : 'inactive')}> {tab.ele} diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 81ddeb9e3..9a9097bf7 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -18,10 +18,10 @@ import { DictationOverlay } from '../views/DictationOverlay'; import { MainViewModal } from '../views/MainViewModal'; import { DocumentView } from '../views/nodes/DocumentView'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; -import { SearchBox } from '../views/search/SearchBox'; import { DocumentManager } from './DocumentManager'; import { GroupManager, UserOptions } from './GroupManager'; import { GroupMemberView } from './GroupMemberView'; +import { SearchUtil } from './SearchUtil'; import { SelectionManager } from './SelectionManager'; import { SettingsManager } from './SettingsManager'; import './SharingManager.scss'; @@ -446,7 +446,7 @@ export class SharingManager extends React.Component<{}> { if (this.myDocAcls) { const newDocs: Doc[] = []; - SearchBox.foreachRecursiveDoc(docs, (depth, doc) => newDocs.push(doc)); + SearchUtil.foreachRecursiveDoc(docs, (depth, doc) => newDocs.push(doc)); docs = newDocs.filter(doc => GetEffectiveAcl(doc) === AclAdmin); } @@ -527,10 +527,10 @@ export class SharingManager extends React.Component<{}> { const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-'; return !permissions ? null : ( - <div key={groupKey} className={'container'} style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}> + <div key={groupKey} className={'container'} style={{ background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }}> <div className={'padding'}>{StrCast(group.title)}</div> - {group instanceof Doc ? <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={SettingsManager.Instance.userColor} onClick={action(() => (GroupManager.Instance.currentGroup = group))} /> : null} + {group instanceof Doc ? <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={SettingsManager.userColor} onClick={action(() => (GroupManager.Instance.currentGroup = group))} /> : null} <div className={'edit-actions'}> {admin || this.myDocAcls ? ( <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}> @@ -552,7 +552,7 @@ export class SharingManager extends React.Component<{}> { <div className="sharing-contents" style={{ - background: SettingsManager.Instance.userBackgroundColor, + background: SettingsManager.userBackgroundColor, color: StrCast(Doc.UserDoc().userColor), }}> <p className="share-title" style={{ color: StrCast(Doc.UserDoc().userColor) }}> diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx index 3cd2d47a0..6a236face 100644 --- a/src/client/util/reportManager/ReportManager.tsx +++ b/src/client/util/reportManager/ReportManager.tsx @@ -211,11 +211,11 @@ export class ReportManager extends React.Component<{}> { * @returns the component that dispays all issues */ private viewIssuesComponent = () => { - const darkMode = isDarkMode(SettingsManager.Instance.userBackgroundColor); + const darkMode = isDarkMode(SettingsManager.userBackgroundColor); const colors = darkMode ? darkColors : lightColors; return ( - <div className="view-issues" style={{ backgroundColor: SettingsManager.Instance.userBackgroundColor, color: colors.text }}> + <div className="view-issues" style={{ backgroundColor: SettingsManager.userBackgroundColor, color: colors.text }}> <div className="left" style={{ display: this.rightExpanded ? 'none' : 'flex' }}> <div className="report-header"> <h2 style={{ color: colors.text }}>Open Issues</h2> diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index b53379435..c2cbca3e1 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -96,7 +96,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select <div className={`contextMenu-item-background`} style={{ - background: SettingsManager.Instance.userColor, + background: SettingsManager.userColor, }} /> </div> @@ -112,7 +112,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select style={{ marginLeft: window.innerHeight - this._overPosX - 50 > 0 ? '90%' : '20%', marginTop, - background: SettingsManager.Instance.userBackgroundColor, + background: SettingsManager.userBackgroundColor, }}> {this._items.map(prop => ( <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} /> @@ -146,7 +146,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select <div className={`contextMenu-item-background`} style={{ - background: SettingsManager.Instance.userColor, + background: SettingsManager.userColor, }} /> {submenu} diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 90425f264..3018dad79 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -57,7 +57,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @observable private _editingTitle = false; @observable private _hidden = false; @observable public AddToSelection = false; // if Shift is pressed, then this should be set so that clicking on the selection background is ignored so overlapped documents can be added to the selection set. - @observable public Interacting = false; @observable public pushIcon: IconProp = 'arrow-alt-circle-up'; @observable public pullIcon: IconProp = 'arrow-alt-circle-down'; @observable public pullColor: string = 'white'; @@ -330,7 +329,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P */ @action onRadiusDown = (e: React.PointerEvent): void => { - this._isRounding = DocumentDecorations.Instance.Interacting = true; + this._isRounding = DocumentView.Interacting = true; this._resizeUndo = UndoManager.StartBatch('DocDecs set radius'); // Call util move event function setupMoveUpEvents( @@ -353,7 +352,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P return false; }, // moveEvent action(e => { - DocumentDecorations.Instance.Interacting = this._isRounding = false; + DocumentView.Interacting = this._isRounding = false; this._resizeUndo?.end(); }), // upEvent e => {}, // clickEvent, @@ -478,7 +477,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onPointerDown = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); - this.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them + DocumentView.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them this._resizeHdlId = e.currentTarget.className; const bounds = e.currentTarget.getBoundingClientRect(); this._offX = this._resizeHdlId.toLowerCase().includes('left') ? bounds.right - e.clientX : bounds.left - e.clientX; @@ -688,7 +687,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onPointerUp = (e: PointerEvent): void => { this._resizeHdlId = ''; - this.Interacting = false; + DocumentView.Interacting = false; this._resizeUndo?.end(); SnappingManager.clearSnapLines(); @@ -895,7 +894,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P height: bounds.b - bounds.y + this._resizeBorderWidth + 'px', left: bounds.x - this._resizeBorderWidth / 2, top: bounds.y - this._resizeBorderWidth / 2, - pointerEvents: DocumentDecorations.Instance.AddToSelection || this.Interacting ? 'none' : 'all', + pointerEvents: DocumentDecorations.Instance.AddToSelection || DocumentView.Interacting ? 'none' : 'all', display: SelectionManager.Views().length <= 1 || hideDecorations ? 'none' : undefined, }} onPointerDown={this.onBackgroundDown} diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index d60617020..147921596 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -6,7 +6,6 @@ import { ObjectField } from '../../fields/ObjectField'; import './EditableView.scss'; import { DocumentIconContainer } from './nodes/DocumentIcon'; import { OverlayView } from './OverlayView'; -import { EditableText } from 'browndash-components'; export interface EditableProps { /** diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 11c2fc86c..b61ff35c7 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -1,23 +1,20 @@ import React = require('react'); import { action, computed, observable, ObservableMap } from 'mobx'; import { observer } from 'mobx-react'; +import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider'; +import { AiOutlineMinusSquare, AiOutlinePlusSquare } from 'react-icons/ai'; +import { CiCircleRemove } from 'react-icons/ci'; import Select from 'react-select'; -import { Checkbox, Tooltip } from '@material-ui/core'; import { Doc, DocListCast, Field, StrListCast } from '../../fields/Doc'; import { RichTextField } from '../../fields/RichTextField'; -import { StrCast } from '../../fields/Types'; +import { DocumentOptions, FInfo } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; import { UserOptions } from '../util/GroupManager'; +import { SearchUtil } from '../util/SearchUtil'; +import { undoable } from '../util/UndoManager'; import './FilterPanel.scss'; import { FieldView } from './nodes/FieldView'; -import { SearchBox } from './search/SearchBox'; -import { undoable } from '../util/UndoManager'; -import { AiOutlineMinusSquare, AiOutlinePlusSquare } from 'react-icons/ai'; -import { CiCircleRemove } from 'react-icons/ci'; -import { Slider, Rail, Handles, Tracks, Ticks } from 'react-compound-slider'; -import { TooltipRail, Handle, Tick, Track } from './nodes/SliderBox-components'; -import { DocumentOptions, FInfo } from '../documents/Documents'; -import { string32 } from 'pdfjs-dist/types/src/shared/util'; +import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components'; interface filterProps { rootDoc: Doc; @@ -49,7 +46,7 @@ export class FilterPanel extends React.Component<filterProps> { const allDocs = new Set<Doc>(); const targetDoc = this.targetDoc; if (targetDoc) { - SearchBox.foreachRecursiveDoc([this.targetDoc], (depth, doc) => allDocs.add(doc)); + SearchUtil.foreachRecursiveDoc([this.targetDoc], (depth, doc) => allDocs.add(doc)); } console.log('this is all Docs' + Array.from(allDocs)); return Array.from(allDocs); @@ -62,7 +59,7 @@ export class FilterPanel extends React.Component<filterProps> { const noviceFields = [...noviceReqFields, ...noviceLayoutFields]; const keys = new Set<string>(noviceFields); - this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key))); + this.allDocs.forEach(doc => SearchUtil.documentKeys(doc).filter(key => keys.add(key))); const sortedKeys = Array.from(keys.keys()) .filter(key => key[0]) .filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode) @@ -236,7 +233,7 @@ export class FilterPanel extends React.Component<filterProps> { facetValues = (facetHeader: string) => { const allCollectionDocs = new Set<Doc>(); - SearchBox.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc)); + SearchUtil.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc)); const set = new Set<string>([String.fromCharCode(127) + '--undefined--']); if (facetHeader === 'tags') allCollectionDocs.forEach(child => diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index afb76b9ac..8f081b321 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -16,7 +16,6 @@ import { CollectionStackedTimeline } from './collections/CollectionStackedTimeli import { TabDocView } from './collections/TabDocView'; import { GestureOverlay } from './GestureOverlay'; import './LightboxView.scss'; -import { MainView } from './MainView'; import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider'; @@ -61,7 +60,7 @@ export class LightboxView extends React.Component<LightboxViewProps> { this._childFilters && (this._childFilters.length = 0); this._future = this._history = []; Doc.ActiveTool = InkTool.None; - MainView.Instance._exploreMode = false; + DocumentView.ExploreMode = false; } else { const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); @@ -270,7 +269,7 @@ export class LightboxView extends React.Component<LightboxViewProps> { addDocTab={this.addDocTab} pinToPres={TabDocView.PinDoc} bringToFront={emptyFunction} - onBrowseClick={MainView.Instance.exploreMode} + onBrowseClick={DocumentView.exploreMode} focus={emptyFunction} /> </GestureOverlay> @@ -335,12 +334,12 @@ export class LightboxView extends React.Component<LightboxViewProps> { <div className="lightboxView-exploreBtn" title="toggle explore mode to navigate among documents only" - style={{ background: MainView.Instance._exploreMode ? 'white' : undefined }} + style={{ background: DocumentView.ExploreMode ? 'white' : undefined }} onClick={action(e => { e.stopPropagation(); - MainView.Instance._exploreMode = !MainView.Instance._exploreMode; + DocumentView.ExploreMode = !DocumentView.ExploreMode; })}> - <FontAwesomeIcon color={MainView.Instance._exploreMode ? 'black' : 'white'} icon={'globe-americas'} size="2x" /> + <FontAwesomeIcon color={DocumentView.ExploreMode ? 'black' : 'white'} icon={'globe-americas'} size="2x" /> </div> </div> ); diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 6dd1d53ee..a04309d0c 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -2,18 +2,18 @@ // (module as any).hot.accept(); // } +import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; import { AssignAllExtensions } from '../../extensions/General/Extensions'; import { FieldLoader } from '../../fields/FieldLoader'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; +import { PingManager } from '../util/PingManager'; import { ReplayMovements } from '../util/ReplayMovements'; import { TrackMovements } from '../util/TrackMovements'; import { CollectionView } from './collections/CollectionView'; -import { MainView } from './MainView'; -import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import -import { PingManager } from '../util/PingManager'; import './global/globalScripts'; +import { MainView } from './MainView'; dotenv.config(); AssignAllExtensions(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 6a743a5e6..dde04dcc0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -9,7 +9,6 @@ import { observer } from 'mobx-react'; import 'normalize.css'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { ScriptField } from '../../fields/ScriptField'; import { DocCast, StrCast } from '../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; @@ -77,7 +76,6 @@ export class MainView extends React.Component { public static Live: boolean = false; private _docBtnRef = React.createRef<HTMLDivElement>(); - @observable public LastButton: Opt<Doc>; @observable private _windowWidth: number = 0; @observable private _windowHeight: number = 0; @observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row) @@ -529,7 +527,6 @@ export class MainView extends React.Component { window.addEventListener('beforeunload', DocServer.UPDATE_SERVER_CACHE); window.addEventListener('drop', e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page window.addEventListener('dragover', e => e.preventDefault(), false); - // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined)); document.addEventListener('pointerdown', this.globalPointerDown, true); document.addEventListener('pointermove', this.globalPointerMove, true); document.addEventListener('pointerup', this.globalPointerClick, true); @@ -572,11 +569,7 @@ export class MainView extends React.Component { Doc.AddDocToList(Doc.MyFilesystem, 'data', folder); }; - @observable _exploreMode = false; - @computed get exploreMode() { - return () => (this._exploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined); - } - waitForDoubleClick = () => (this._exploreMode ? 'never' : undefined); + waitForDoubleClick = () => (DocumentView.ExploreMode ? 'never' : undefined); headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1); mainScreenToLocalXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.topOfMainDocContent, 1); addHeaderDoc = (doc: Doc | Doc[], annotationKey?: string) => (doc instanceof Doc ? [doc] : doc).reduce((done, doc) => Doc.AddDocToList(this.headerBarDoc, 'data', doc), true); @@ -747,7 +740,7 @@ export class MainView extends React.Component { @computed get leftMenuPanel() { return ( - <div key="menu" className="mainView-leftMenuPanel" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), display: LightboxView.LightboxDoc ? 'none' : undefined }}> + <div key="menu" className="mainView-leftMenuPanel" style={{ background: SettingsManager.userBackgroundColor, display: LightboxView.LightboxDoc ? 'none' : undefined }}> <DocumentView Document={Doc.MyLeftSidebarMenu} DataDoc={undefined} @@ -804,16 +797,16 @@ export class MainView extends React.Component { {this.flyout} <div className="mainView-libraryHandle" - style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? 'none' : undefined }} + style={{ background: SettingsManager.userBackgroundColor, left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? 'none' : undefined }} onPointerDown={this.onFlyoutPointerDown}> - <FontAwesomeIcon icon="chevron-left" color={StrCast(Doc.UserDoc().userColor)} style={{ opacity: '50%' }} size="sm" /> + <FontAwesomeIcon icon="chevron-left" color={SettingsManager.userColor} style={{ opacity: '50%' }} size="sm" /> </div> <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}> {this.dockingContent} {this._hideUI ? null : ( - <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), right: this.propertiesWidth() - 1 }}> - <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={StrCast(Doc.UserDoc().userColor)} size="sm" /> + <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ background: SettingsManager.userBackgroundColor, right: this.propertiesWidth() - 1 }}> + <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={SettingsManager.userColor} size="sm" /> </div> )} <div className="properties-container" style={{ width: this.propertiesWidth() }}> @@ -857,11 +850,11 @@ export class MainView extends React.Component { //setTimeout(action(() => (this._leftMenuFlyoutWidth += 0.5))); this._sidebarContent.proto = DocCast(button.target); - this.LastButton = button; + DocumentView.LastPressedSidebarBtn = button; }); closeFlyout = action(() => { - this.LastButton = undefined; + DocumentView.LastPressedSidebarBtn = undefined; this._panelContent = 'none'; this._sidebarContent.proto = undefined; this._leftMenuFlyoutWidth = 0; @@ -879,7 +872,7 @@ export class MainView extends React.Component { @computed get docButtons() { return !Doc.MyDockedBtns ? null : ( - <div className="mainView-docButtons" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }} ref={this._docBtnRef}> + <div className="mainView-docButtons" style={{ background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }} ref={this._docBtnRef}> <CollectionLinearView Document={Doc.MyDockedBtns} DataDoc={undefined} @@ -998,8 +991,8 @@ export class MainView extends React.Component { <div className={`mainView-container ${this.colorScheme}`} style={{ - color: StrCast(Doc.UserDoc().userColor), - background: StrCast(Doc.UserDoc().userBackgroundColor), + color: SettingsManager.userColor, + background: SettingsManager.userBackgroundColor, }} onScroll={() => (ele => (ele.scrollTop = ele.scrollLeft = 0))(document.getElementById('root')!)} ref={r => { @@ -1068,3 +1061,12 @@ export class MainView extends React.Component { ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); }); +ScriptingGlobals.add(function createNewPresentation() { + return MainView.Instance.createNewPresentation(); +}, 'creates a new presentation when called'); +ScriptingGlobals.add(function openPresentation(pres: Doc) { + return MainView.Instance.openPresentation(pres); +}, 'creates a new presentation when called'); +ScriptingGlobals.add(function createNewFolder() { + return MainView.Instance.createNewFolder(); +}, 'creates a new folder in myFiles when called'); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 4fe6847c3..83bd1b860 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -188,7 +188,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { }); rows.push( - <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px', backgroundColor: SettingsManager.Instance.userBackgroundColor, textAlign: 'center' }}> + <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px', backgroundColor: SettingsManager.userBackgroundColor, textAlign: 'center' }}> <EditableView key="editableView" oneLine contents={'add key:value or #tags'} height={13} fontSize={10} GetValue={() => ''} SetValue={this.setKeyValue} /> </div> ); @@ -1664,7 +1664,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { <div className="propertiesView-title" style={{ width: this.props.width }}> Properties <div className="propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/properties')}> - <IconButton icon={<FontAwesomeIcon icon="info-circle" />} color={SettingsManager.Instance.userColor} /> + <IconButton icon={<FontAwesomeIcon icon="info-circle" />} color={SettingsManager.userColor} /> </div> </div> </div> @@ -1706,8 +1706,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { className="propertiesView-presentationTrails-title" onPointerDown={action(() => (this.openPresTransitions = !this.openPresTransitions))} style={{ - color: SettingsManager.Instance.userColor, - backgroundColor: this.openPresTransitions ? SettingsManager.Instance.userVariantColor : SettingsManager.Instance.userBackgroundColor, + color: SettingsManager.userColor, + backgroundColor: this.openPresTransitions ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor, }}> <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> Transitions <div className="propertiesView-presentationTrails-title-icon"> @@ -1723,8 +1723,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { className="propertiesView-presentationTrails-title" onPointerDown={action(() => (this.openPresVisibilityAndDuration = !this.openPresVisibilityAndDuration))} style={{ - color: SettingsManager.Instance.userColor, - backgroundColor: this.openPresVisibilityAndDuration ? SettingsManager.Instance.userVariantColor : SettingsManager.Instance.userBackgroundColor, + color: SettingsManager.userColor, + backgroundColor: this.openPresVisibilityAndDuration ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor, }}> <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> Visibilty <div className="propertiesView-presentationTrails-title-icon"> @@ -1740,8 +1740,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { className="propertiesView-presentationTrails-title" onPointerDown={action(() => (this.openPresProgressivize = !this.openPresProgressivize))} style={{ - color: SettingsManager.Instance.userColor, - backgroundColor: this.openPresProgressivize ? SettingsManager.Instance.userVariantColor : SettingsManager.Instance.userBackgroundColor, + color: SettingsManager.userColor, + backgroundColor: this.openPresProgressivize ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor, }}> <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> Progressivize <div className="propertiesView-presentationTrails-title-icon"> @@ -1757,8 +1757,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { className="propertiesView-presentationTrails-title" onPointerDown={action(() => (this.openSlideOptions = !this.openSlideOptions))} style={{ - color: SettingsManager.Instance.userColor, - backgroundColor: this.openSlideOptions ? SettingsManager.Instance.userVariantColor : SettingsManager.Instance.userBackgroundColor, + color: SettingsManager.userColor, + backgroundColor: this.openSlideOptions ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor, }}> <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={type === DocumentType.AUDIO ? 'file-audio' : 'file-video'} /> {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'} <div className="propertiesView-presentationTrails-title-icon"> diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index c48a7241a..f3452c780 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -9,11 +9,11 @@ import { emptyFunction, returnAll, returnFalse, returnOne, returnTrue, returnZer import { Docs, DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { LinkManager } from '../util/LinkManager'; +import { SearchUtil } from '../util/SearchUtil'; import { Transform } from '../util/Transform'; import { CollectionStackingView } from './collections/CollectionStackingView'; import { FieldViewProps } from './nodes/FieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; -import { SearchBox } from './search/SearchBox'; import './SidebarAnnos.scss'; import { StyleProp } from './StyleProvider'; import React = require('react'); @@ -43,7 +43,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { @computed get allMetadata() { const keys = new Map<string, FieldResult<Field>>(); DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => - SearchBox.documentKeys(doc) + SearchUtil.documentKeys(doc) .filter(key => key[0] && key[0] !== '_' && key[0] === key[0].toUpperCase()) .map(key => keys.set(key, doc[key])) ); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 24a269927..12935400b 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -4,6 +4,7 @@ import { Tooltip } from '@material-ui/core'; import { IconButton, Shadows, Size } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { extname } from 'path'; +import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs'; import { Doc, Opt, StrListCast } from '../../fields/Doc'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types'; import { DashColor, lightOrDark, Utils } from '../../Utils'; @@ -14,15 +15,13 @@ import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; import { ColorScheme, SettingsManager } from '../util/SettingsManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; -import { TreeSort } from './collections/TreeView'; +import { TreeSort } from './collections/TreeSort'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; -import { MainView } from './MainView'; -import { DocumentViewProps } from './nodes/DocumentView'; +import { DocumentView, DocumentViewProps } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; import { KeyValueBox } from './nodes/KeyValueBox'; import { SliderBox } from './nodes/SliderBox'; -import { BsArrowDown, BsArrowUp, BsArrowDownUp } from 'react-icons/bs'; import './StyleProvider.scss'; import React = require('react'); @@ -159,7 +158,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps '' ); case StyleProp.Color: - if (MainView.Instance.LastButton === doc) return SettingsManager.Instance.userBackgroundColor; + if (DocumentView.LastPressedSidebarBtn === doc) return SettingsManager.userBackgroundColor; if (Doc.IsSystem(doc!)) return StrCast(Doc.UserDoc().userColor) if (doc?.type === DocumentType.FONTICON) return Doc.UserDoc().userColor; const docColor: Opt<string> = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color)); @@ -194,7 +193,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps ? 15 : 0; case StyleProp.BackgroundColor: { - if (MainView.Instance.LastButton === doc) return StrCast(Doc.UserDoc().userColor); // hack to indicate active menu panel item + if (DocumentView.LastPressedSidebarBtn === doc) return StrCast(Doc.UserDoc().userColor); // hack to indicate active menu panel item let docColor: Opt<string> = StrCast(doc?.[fieldKey + '_backgroundColor'], StrCast(doc?._backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : '')); // prettier-ignore switch (doc?.type) { @@ -217,7 +216,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps case DocumentType.COL: if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break; docColor = docColor || (Doc.IsSystem(doc) - ? SettingsManager.Instance.userBackgroundColor + ? SettingsManager.userBackgroundColor : doc.annotationOn ? '#00000010' // faint interior for collections on PDFs, images, etc : doc?._isGroup @@ -264,7 +263,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name) && !props?.LayoutTemplateString; if (StrCast(doc?.pointerEvents) && !props?.LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc if (docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.() !== undefined) return docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.(); - if (MainView.Instance._exploreMode || doc?.layout_unrendered) return isInk ? 'visiblePainted' : 'all'; + if (DocumentView.ExploreMode || doc?.layout_unrendered) return isInk ? 'visiblePainted' : 'all'; if (props?.contentPointerEvents) return StrCast(props.contentPointerEvents); if (props?.pointerEvents?.() === 'none') return 'none'; if (opacity() === 0) return 'none'; diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx index a551e5332..3fd64d1fd 100644 --- a/src/client/views/UndoStack.tsx +++ b/src/client/views/UndoStack.tsx @@ -19,7 +19,7 @@ export class UndoStack extends React.Component<UndoStackProps> { @observable static HideInline: boolean; @observable static Expand: boolean; render() { - const background = UndoManager.batchCounter.get() ? 'yellow' : SettingsManager.Instance.userBackgroundColor; + const background = UndoManager.batchCounter.get() ? 'yellow' : SettingsManager.userBackgroundColor; return this.props.inline && UndoStack.HideInline ? null : ( <div className="undoStack-outerContainer"> <Popup diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index e15d57306..519b7c905 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -3,6 +3,7 @@ import { observer } from 'mobx-react'; import * as ReactDOM from 'react-dom/client'; import * as GoldenLayout from '../../../client/goldenLayout'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { AclAdmin, AclEdit } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; @@ -13,6 +14,7 @@ import { emptyFunction, incrementTitleCopy } from '../../../Utils'; import { DocServer } from '../../DocServer'; import { Docs } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; +import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { InteractionUtils } from '../../util/InteractionUtils'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; @@ -28,8 +30,6 @@ import './CollectionDockingView.scss'; import { CollectionFreeFormView } from './collectionFreeForm'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; import { TabDocView } from './TabDocView'; -import { DocumentManager } from '../../util/DocumentManager'; -import { AclAdmin, AclEdit } from '../../../fields/DocSymbols'; import React = require('react'); const _global = (window /* browser */ || global) /* node */ as any; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index f65e8698f..5c9dd2058 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -181,7 +181,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { <div className="collectionMenu-container" style={{ - background: SettingsManager.Instance.userBackgroundColor, + background: SettingsManager.userBackgroundColor, // borderColor: StrCast(Doc.UserDoc().userColor) }}> {this.contMenuButtons} diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index de58e1fe7..d2be70577 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -2,12 +2,12 @@ import React = require('react'); import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; -import { Doc, Opt, StrListCast } from '../../../fields/Doc'; +import { Doc, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; -import { Cast, NumCast, ScriptCast } from '../../../fields/Types'; +import { Cast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { emptyFunction, formatTime, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils'; import { Docs } from '../../documents/Documents'; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index f379d09ff..f6acafa95 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -1,6 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Popup, Type } from 'browndash-components'; import { clamp } from 'lodash'; import { action, computed, IReactionDisposer, observable, ObservableSet, reaction } from 'mobx'; import { observer } from 'mobx-react'; @@ -10,7 +10,7 @@ import { DocData, Height, Width } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { FieldId } from '../../../fields/RefField'; -import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types'; +import { Cast, DocCast, NumCast, StrCast } from '../../../fields/Types'; import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils'; import { DocServer } from '../../DocServer'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; @@ -21,9 +21,8 @@ import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoable, UndoManager } from '../../util/UndoManager'; import { DashboardView } from '../DashboardView'; -import { Colors, Shadows } from '../global/globalEnums'; +import { Colors } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; -import { MainView } from '../MainView'; import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, OpenWhereMod } from '../nodes/DocumentView'; import { DashFieldView } from '../nodes/formattedText/DashFieldView'; import { KeyValueBox } from '../nodes/KeyValueBox'; @@ -34,7 +33,6 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV import { CollectionView } from './CollectionView'; import './TabDocView.scss'; import React = require('react'); -import { Popup, Toggle, Type } from 'browndash-components'; const _global = (window /* browser */ || global) /* node */ as any; interface TabDocViewProps { @@ -426,6 +424,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { disableMinimap = () => !this._document || this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._type_collection !== CollectionViewType.Freeform; whenChildContentActiveChanges = (isActive: boolean) => (this._isAnyChildContentActive = isActive); isContentActive = () => this._isContentActive; + waitForDoubleClick = () => (DocumentView.ExploreMode ? 'never' : undefined); @computed get docView() { return !this._activated || !this._document ? null : ( <> @@ -441,8 +440,8 @@ export class TabDocView extends React.Component<TabDocViewProps> { hideTitle={this.props.keyValue} Document={this._document} DataDoc={!Doc.AreProtosEqual(this._document[DocData], this._document) ? this._document[DocData] : undefined} - onBrowseClick={MainView.Instance.exploreMode} - waitForDoubleClickToClick={MainView.Instance.waitForDoubleClick} + onBrowseClick={DocumentView.exploreMode} + waitForDoubleClickToClick={this.waitForDoubleClick} isContentActive={this.isContentActive} isDocumentActive={returnFalse} PanelWidth={this.PanelWidth} diff --git a/src/client/views/collections/TreeSort.ts b/src/client/views/collections/TreeSort.ts new file mode 100644 index 000000000..977acd030 --- /dev/null +++ b/src/client/views/collections/TreeSort.ts @@ -0,0 +1,6 @@ +export enum TreeSort { + AlphaUp = 'alphabetical from z', + AlphaDown = 'alphabetical from A', + Zindex = 'by Z index', + WhenAdded = 'when added', +} diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 3402a8c8d..701150769 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -35,6 +35,7 @@ import { CollectionView } from './CollectionView'; import './TreeView.scss'; import React = require('react'); import { IconButton, Size } from 'browndash-components'; +import { TreeSort } from './TreeSort'; export interface TreeViewProps { treeView: CollectionTreeView; @@ -77,12 +78,6 @@ const treeBulletWidth = function () { return Number(TREE_BULLET_WIDTH.replace('px', '')); }; -export enum TreeSort { - AlphaUp = 'alphabetical from z', - AlphaDown = 'alphabetical from A', - Zindex = 'by Z index', - WhenAdded = 'when added', -} /** * Renders a treeView of a collection of documents * @@ -578,7 +573,7 @@ export class TreeView extends React.Component<TreeViewProps> { return ( <div> {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? null : ( - <div className='treeView-sorting'> + <div className="treeView-sorting"> <IconButton color={sortings[sorting]?.color} size={Size.XSMALL} @@ -602,7 +597,7 @@ export class TreeView extends React.Component<TreeViewProps> { style={{ cursor: 'inherit' }} key={expandKey + 'more'} title={`Sorted by : ${this.doc.treeView_SortCriterion}. click to cycle`} - className='' //this.doc.treeView_HideTitle ? 'no-indent' : ''} + className="" //this.doc.treeView_HideTitle ? 'no-indent' : ''} onPointerDown={e => { downX = e.clientX; downY = e.clientY; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f5cc1eb53..16d6f1270 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -371,7 +371,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)]; layoutDoc._width = NumCast(layoutDoc._width, 300); layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? (nd[1] / nd[0]) * NumCast(layoutDoc._width) : 300); - (d._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront + !d._keepZWhenDragged && (d.zIndex = zsorted.length + 1 + i); // bringToFront } if (this.layoutDoc._autoArrange || de.metaKey) { @@ -1267,7 +1267,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; @computed get _pointerEvents() { const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); - const pointerEvents = DocumentDecorations.Instance.Interacting + const pointerEvents = DocumentView.Interacting ? 'none' : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.()); return pointerEvents; diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index 6895c0746..9a9a89732 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -3,15 +3,14 @@ import { observer } from 'mobx-react'; import { EditorView } from 'prosemirror-view'; import { Doc } from '../../../fields/Doc'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils'; -import { DocUtils } from '../../documents/Documents'; import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; +import { OpenWhere } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { SearchBox } from '../search/SearchBox'; import { DefaultStyleProvider } from '../StyleProvider'; import './LinkPopup.scss'; import React = require('react'); -import { OpenWhere } from '../nodes/DocumentView'; interface LinkPopupProps { linkFrom?: () => Doc | undefined; diff --git a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx index ff17e5c12..3a95e5f74 100644 --- a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx +++ b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx @@ -1,14 +1,13 @@ -import './ButtonMenu.scss'; +import { action } from 'mobx'; import * as React from 'react'; -import { IButtonMenu } from './utils'; -import { NewLightboxView } from '../NewLightboxView'; -import { SelectionManager } from '../../../util/SelectionManager'; -import { CollectionDockingView } from '../../collections/CollectionDockingView'; -import { OpenWhereMod } from '../../nodes/DocumentView'; import { Doc } from '../../../../fields/Doc'; import { InkTool } from '../../../../fields/InkField'; -import { MainView } from '../../MainView'; -import { action } from 'mobx'; +import { SelectionManager } from '../../../util/SelectionManager'; +import { CollectionDockingView } from '../../collections/CollectionDockingView'; +import { DocumentView, OpenWhereMod } from '../../nodes/DocumentView'; +import { NewLightboxView } from '../NewLightboxView'; +import './ButtonMenu.scss'; +import { IButtonMenu } from './utils'; export const ButtonMenu = (props: IButtonMenu) => { return ( @@ -40,10 +39,10 @@ export const ButtonMenu = (props: IButtonMenu) => { <div className="newLightboxView-exploreBtn" title="toggle explore mode to navigate among documents only" - style={{ background: MainView.Instance._exploreMode ? 'white' : undefined }} + style={{ background: DocumentView.ExploreMode ? 'white' : undefined }} onClick={action(e => { e.stopPropagation(); - MainView.Instance._exploreMode = !MainView.Instance._exploreMode; + DocumentView.ExploreMode = !DocumentView.ExploreMode; })}></div> </div> ); diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx index 3acbd1a32..ca90f6a0f 100644 --- a/src/client/views/newlightbox/NewLightboxView.tsx +++ b/src/client/views/newlightbox/NewLightboxView.tsx @@ -2,35 +2,32 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../../Utils'; -import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { LinkManager } from '../../util/LinkManager'; import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; -import { GestureOverlay } from '../GestureOverlay'; -import { MainView } from '../MainView'; -import { DefaultStyleProvider } from '../StyleProvider'; import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline'; import { TabDocView } from '../collections/TabDocView'; +import { GestureOverlay } from '../GestureOverlay'; +import { LightboxView } from '../LightboxView'; import { DocumentView, OpenWhere } from '../nodes/DocumentView'; +import { DefaultStyleProvider } from '../StyleProvider'; +import { IRecommendation } from './components'; import { ExploreView } from './ExploreView'; -import { IBounds, emptyBounds } from './ExploreView/utils'; +import { emptyBounds, IBounds } from './ExploreView/utils'; import { NewLightboxHeader } from './Header'; import './NewLightboxView.scss'; import { RecommendationList } from './RecommendationList'; -import { IRecommendation } from './components'; -import { fetchKeywords, fetchRecommendations } from './utils'; -import { List } from '../../../fields/List'; -import { LightboxView } from '../LightboxView'; enum LightboxStatus { - RECOMMENDATIONS = "recommendations", - ANNOTATIONS = "annotations", - NONE = "none" + RECOMMENDATIONS = 'recommendations', + ANNOTATIONS = 'annotations', + NONE = 'none', } interface LightboxViewProps { @@ -63,30 +60,30 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { @observable private static _docView: Opt<DocumentView>; // keywords - @observable private static _keywords: string[] = [] + @observable private static _keywords: string[] = []; @action public static SetKeywords(kw: string[]) { - this._keywords = kw + this._keywords = kw; } @computed public static get Keywords() { - return this._keywords + return this._keywords; } // query - @observable private static _query: string = '' + @observable private static _query: string = ''; @action public static SetQuery(query: string) { - this._query = query + this._query = query; } @computed public static get Query() { - return this._query + return this._query; } // keywords - @observable private static _recs: IRecommendation[] = [] + @observable private static _recs: IRecommendation[] = []; @action public static SetRecs(recs: IRecommendation[]) { - this._recs = recs + this._recs = recs; } @computed public static get Recs() { - return this._recs + return this._recs; } // bounds @@ -108,7 +105,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { } // newLightbox sidebar status - @observable private static _sidebarStatus: Opt<string> = ""; + @observable private static _sidebarStatus: Opt<string> = ''; @action public static SetSidebarStatus(sidebarStatus: Opt<string>) { this._sidebarStatus = sidebarStatus; } @@ -129,7 +126,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { this._docFilters && (this._docFilters.length = 0); this._future = this._history = []; Doc.ActiveTool = InkTool.None; - MainView.Instance._exploreMode = false; + DocumentView.ExploreMode = false; } else { const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); @@ -289,35 +286,38 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { @computed get documentView() { - if (!LightboxView.LightboxDoc) return null - else return (<GestureOverlay isActive={true}> - <DocumentView - ref={action((r: DocumentView | null) => (NewLightboxView._docView = r !== null ? r : undefined))} - Document={LightboxView.LightboxDoc} - DataDoc={undefined} - PanelWidth={this.newLightboxWidth} - PanelHeight={this.newLightboxHeight} - LayoutTemplate={NewLightboxView.LightboxDocTemplate} - isDocumentActive={returnTrue} // without this being true, sidebar annotations need to be activated before text can be selected. - isContentActive={returnTrue} - styleProvider={DefaultStyleProvider} - ScreenToLocalTransform={this.newLightboxScreenToLocal} - renderDepth={0} - rootSelected={returnTrue} - docViewPath={returnEmptyDoclist} - childFilters={this.docFilters} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - addDocument={undefined} - removeDocument={undefined} - whenChildContentsActiveChanged={emptyFunction} - addDocTab={this.addDocTab} - pinToPres={TabDocView.PinDoc} - bringToFront={emptyFunction} - onBrowseClick={MainView.Instance.exploreMode} - focus={emptyFunction} - /> - </GestureOverlay>) + if (!LightboxView.LightboxDoc) return null; + else + return ( + <GestureOverlay isActive={true}> + <DocumentView + ref={action((r: DocumentView | null) => (NewLightboxView._docView = r !== null ? r : undefined))} + Document={LightboxView.LightboxDoc} + DataDoc={undefined} + PanelWidth={this.newLightboxWidth} + PanelHeight={this.newLightboxHeight} + LayoutTemplate={NewLightboxView.LightboxDocTemplate} + isDocumentActive={returnTrue} // without this being true, sidebar annotations need to be activated before text can be selected. + isContentActive={returnTrue} + styleProvider={DefaultStyleProvider} + ScreenToLocalTransform={this.newLightboxScreenToLocal} + renderDepth={0} + rootSelected={returnTrue} + docViewPath={returnEmptyDoclist} + childFilters={this.docFilters} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + addDocument={undefined} + removeDocument={undefined} + whenChildContentsActiveChanged={emptyFunction} + addDocTab={this.addDocTab} + pinToPres={TabDocView.PinDoc} + bringToFront={emptyFunction} + onBrowseClick={DocumentView.exploreMode} + focus={emptyFunction} + /> + </GestureOverlay> + ); } future = () => NewLightboxView._future; @@ -337,29 +337,28 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { NewLightboxView.SetNewLightboxDoc(undefined); } }}> - <div className={`app-document`} style={{gridTemplateColumns: `calc(100% - 400px) 400px`}}> - <div - className="newLightboxView-contents" - style={{ - top: 20, - left: 20, - width: this.newLightboxWidth(), - height: this.newLightboxHeight() - 40, - }}> + <div className={`app-document`} style={{ gridTemplateColumns: `calc(100% - 400px) 400px` }}> + <div + className="newLightboxView-contents" + style={{ + top: 20, + left: 20, + width: this.newLightboxWidth(), + height: this.newLightboxHeight() - 40, + }}> <NewLightboxHeader height={newLightboxHeaderHeight} width={this.newLightboxWidth()} /> - {!NewLightboxView._explore ? - <div className="newLightboxView-doc" style={{height: this.newLightboxHeight()}}> + {!NewLightboxView._explore ? ( + <div className="newLightboxView-doc" style={{ height: this.newLightboxHeight() }}> {this.documentView} </div> - : + ) : ( <div className={`explore`}> - <ExploreView recs={NewLightboxView.Recs} bounds={NewLightboxView.Bounds}/> + <ExploreView recs={NewLightboxView.Recs} bounds={NewLightboxView.Bounds} /> </div> - } - </div> - <RecommendationList keywords={NewLightboxView.Keywords}/> + )} + </div> + <RecommendationList keywords={NewLightboxView.Keywords} /> </div> - </div> ); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index be1eb3901..74eee49e0 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -28,6 +28,7 @@ import { FollowLinkScript } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; +import { SettingsManager } from '../../util/SettingsManager'; import { SharingManager } from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; @@ -51,7 +52,6 @@ import { LinkAnchorBox } from './LinkAnchorBox'; import { PresEffect, PresEffectDirection } from './trails'; import { PinProps, PresBox } from './trails/PresBox'; import React = require('react'); -import { SettingsManager } from '../../util/SettingsManager'; const { Howl } = require('howler'); interface Window { @@ -428,7 +428,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps if (!this.Document.ignoreClick && this.pointerEvents !== 'none' && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) { let stopPropagate = true; let preventDefault = true; - (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc); + !this.rootDoc._keepZWhenDragged && this.props.bringToFront(this.rootDoc); if (this._doubleTap) { const defaultDblclick = this.props.defaultDoubleClick?.() || this.Document.defaultDoubleClick; if (this.onDoubleClickHandler?.script) { @@ -744,8 +744,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: 'arrow-up' }); zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: 'arrow-down' }); zorderItems.push({ - description: this.rootDoc._raiseWhenDragged !== false ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged', - event: undoBatch(action(() => (this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined))), + description: !this.rootDoc._keepZDragged ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged', + event: undoBatch(action(() => (this.rootDoc._keepZWhenDragged = !this.rootDoc._keepZWhenDragged))), icon: 'hand-point-up', }); !zorders && cm.addItem({ description: 'Z Order...', addDivider: true, noexpand: true, subitems: zorderItems, icon: 'layer-group' }); @@ -1114,7 +1114,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps </div> ); const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc; - const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.Instance.userVariantColor)); + const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userVariantColor)); const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', ''); const titleView = !showTitle ? null : ( <div @@ -1276,7 +1276,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps @observer export class DocumentView extends React.Component<DocumentViewProps> { public static ROOT_DIV = 'documentView-effectsWrapper'; + @observable public static Interacting = false; @observable public static LongPress = false; + @observable public static ExploreMode = false; + @observable public static LastPressedSidebarBtn: Opt<Doc>; // bcz: this is a hack to handle highlighting buttons in the leftpanel menu .. need to find a cleaner approach + @computed public static get exploreMode() { + return () => (DocumentView.ExploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined); + } @observable public docView: DocumentViewInternal | undefined | null; @observable public textHtmlOverlay: Opt<string>; @observable private _isHovering = false; diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 94650cc88..fc66d40db 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -67,19 +67,6 @@ export class FontIconBox extends DocComponent<ButtonProps>() { } }; - static GetShowLabels() { - return BoolCast(Doc.UserDoc()._showLabel); - } - static SetShowLabels(show: boolean) { - Doc.UserDoc()._showLabel = show; - } - static GetRecognizeGestures() { - return BoolCast(Doc.UserDoc()._recognizeGestures); - } - static SetRecognizeGestures(show: boolean) { - Doc.UserDoc()._recognizeGestures = show; - } - // Determining UI Specs @computed get label() { return StrCast(this.rootDoc.icon_label, StrCast(this.rootDoc.title)); @@ -139,7 +126,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); // Script for checking the outcome of the toggle const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3))); - const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>; + const label = !Doc.GetShowIconLabels() ? null : <div className="fontIconBox-label">{this.label}</div>; return ( <NumberDropdown @@ -174,7 +161,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { Doc.UnBrushAllDocs(); })}> {this.Icon(color)} - {!this.label || !FontIconBox.GetShowLabels() ? null : ( + {!this.label || !Doc.GetShowIconLabels() ? null : ( <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {' '} {this.label}{' '} diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index d69009415..42c8ea6a4 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -4,7 +4,7 @@ import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import wiki from 'wikijs'; import { Doc, DocCastAsync, Opt } from '../../../fields/Doc'; -import { DirectLinks, Height, Width } from '../../../fields/DocSymbols'; +import { Height, Width } from '../../../fields/DocSymbols'; import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils'; import { DocServer } from '../../DocServer'; @@ -13,9 +13,9 @@ import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; +import { SearchUtil } from '../../util/SearchUtil'; import { SettingsManager } from '../../util/SettingsManager'; import { Transform } from '../../util/Transform'; -import { SearchBox } from '../search/SearchBox'; import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView'; import './LinkDocPreview.scss'; import React = require('react'); @@ -174,7 +174,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { LinkFollower.FollowLink(this._linkDoc, this._linkSrc, false); } else if (this.props.hrefs?.length) { const webDoc = - Array.from(SearchBox.staticSearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ?? + Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ?? Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true }); this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox); } diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index fcbd0128d..0b02626e9 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -1,14 +1,14 @@ -import { action, observable, runInAction } from 'mobx'; +import { observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import ReactLoading from 'react-loading'; import { Doc } from '../../../fields/Doc'; +import { Id } from '../../../fields/FieldSymbols'; import { StrCast } from '../../../fields/Types'; import { Networking } from '../../Network'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; import './LoadingBox.scss'; -import { Id } from '../../../fields/FieldSymbols'; /** * LoadingBox Class represents a placeholder doc for documents that are currently @@ -44,7 +44,7 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this.rootDoc.loadingError = 'Upload interrupted, please try again'; } else { const updateFunc = async () => { - const result = await Networking.QueryYoutubeProgress(StrCast(this.rootDoc[Id])); // We use the guid of the overwriteDoc to track file uploads. + const result = await Networking.QueryYoutubeProgress(StrCast(this.rootDoc[Id])); // We use the guid of the overwriteDoc to track file uploads. runInAction(() => (this.progress = result.progress)); this._timer = setTimeout(updateFunc, 1000); }; diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 83a29f071..fa19caae1 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -314,7 +314,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl </> </CollectionFreeFormView> </div> - <div style={{ background: SettingsManager.Instance.userColor, position: 'relative', height: this.formattedPanelHeight() }}> + <div style={{ background: SettingsManager.userColor, position: 'relative', height: this.formattedPanelHeight() }}> {!(this.dataDoc[this.fieldKey + '_dictation'] instanceof Doc) ? null : ( <FormattedTextBox {...this.props} @@ -336,8 +336,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl </div> </div> {!this.props.isSelected() ? null : ( - <div className="screenshotBox-uiButtons" style={{ background: SettingsManager.Instance.userColor }}> - <div className="screenshotBox-recorder" style={{ color: SettingsManager.Instance.userBackgroundColor, background: SettingsManager.Instance.userVariantColor }} key="snap" onPointerDown={this.toggleRecording}> + <div className="screenshotBox-uiButtons" style={{ background: SettingsManager.userColor }}> + <div className="screenshotBox-recorder" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userVariantColor }} key="snap" onPointerDown={this.toggleRecording}> <FontAwesomeIcon icon="file" size="lg" /> </div> </div> diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 2177adeff..48716b867 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -26,11 +26,10 @@ import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionS import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; -import { DocumentDecorations } from '../DocumentDecorations'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { StyleProp } from '../StyleProvider'; -import { OpenWhere } from './DocumentView'; +import { DocumentView, OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { RecordingBox } from './RecordingBox'; import { PinProps, PresBox } from './trails'; @@ -624,7 +623,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp () => !this._playing && this.Seek(NumCast(this.layoutDoc._layout_currentTimecode)) ); this._disposers.youtubeReactionDisposer = reaction( - () => Doc.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting, + () => Doc.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentView.Interacting, interactive => (iframe.style.pointerEvents = interactive ? 'all' : 'none'), { fireImmediately: true } ); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 200d06a0b..bd969b527 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -11,9 +11,10 @@ import { keymap } from 'prosemirror-keymap'; import { Fragment, Mark, Node, Slice } from 'prosemirror-model'; import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state'; import { EditorView } from 'prosemirror-view'; +import { BsMarkdownFill } from 'react-icons/bs'; import { DateField } from '../../../../fields/DateField'; -import { Doc, DocListCast, StrListCast, Field, Opt } from '../../../../fields/Doc'; -import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, Height, Width, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; +import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; +import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, Height, UpdatingFromServer, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; @@ -25,11 +26,10 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils'; -import { gptAPICall, GPTCallType, gptImageCall } from '../../../apis/gpt/GPT'; +import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT'; import { DocServer } from '../../../DocServer'; import { Docs, DocUtils } from '../../../documents/Documents'; -import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; -import { Networking } from '../../../Network'; +import { CollectionViewType } from '../../../documents/DocumentTypes'; import { DictationManager } from '../../../util/DictationManager'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; @@ -49,6 +49,7 @@ import { DocumentButtonBar } from '../../DocumentButtonBar'; import { Colors } from '../../global/globalEnums'; import { LightboxView } from '../../LightboxView'; import { AnchorMenu } from '../../pdf/AnchorMenu'; +import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup'; import { SidebarAnnos } from '../../SidebarAnnos'; import { StyleProp } from '../../StyleProvider'; import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView'; @@ -70,8 +71,6 @@ import { schema } from './schema_rts'; import { SummaryView } from './SummaryView'; import applyDevTools = require('prosemirror-dev-tools'); import React = require('react'); -import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup'; -import { BsMarkdownFill } from 'react-icons/bs'; const translateGoogleApi = require('translate-google-api'); export const GoogleRef = 'googleDocId'; type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void; @@ -575,7 +574,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps draggedDoc._freeform_fitContentsToBox = true; Doc.SetContainer(draggedDoc, this.rootDoc); const view = this._editorView!; - view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node)); + try { + const pos = view.posAtCoords({ left: de.x, top: de.y })?.pos; + pos && view.dispatch(view.state.tr.insert(pos, node)); + added = pos ? true : false; // pos will be null if you don't drop onto an actual text location + } catch (e) { + console.log('Drop failed', e); + added = false; + } } } } // otherwise, fall through to outer collection to handle drop @@ -1119,7 +1125,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps componentDidMount() { !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. this._cachedLinks = LinkManager.Links(this.Document); - this._disposers.breakupDictation = reaction(() => DocumentManager.Instance.RecordingEvent, this.breakupDictation); + this._disposers.breakupDictation = reaction(() => Doc.RecordingEvent, this.breakupDictation); this._disposers.layout_autoHeight = reaction( () => this.layout_autoHeight, layout_autoHeight => layout_autoHeight && this.tryUpdateScrollHeight() diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 1400d0f6d..1ec6d6e3f 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -1,26 +1,25 @@ -import './GenerativeFill.scss'; -import React = require('react'); -import { useEffect, useRef, useState } from 'react'; -import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; -import { BrushHandler } from './generativeFillUtils/BrushHandler'; -import { IconButton } from 'browndash-components'; import { Checkbox, FormControlLabel, Slider, TextField } from '@mui/material'; -import { CursorData, ImageDimensions, Point } from './generativeFillUtils/generativeFillInterfaces'; -import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants'; -import { PointerHandler } from './generativeFillUtils/PointerHandler'; -import { IoMdUndo, IoMdRedo } from 'react-icons/io'; -import { MainView } from '../../MainView'; +import { IconButton } from 'browndash-components'; +import { useEffect, useRef, useState } from 'react'; +import { CgClose } from 'react-icons/cg'; +import { IoMdRedo, IoMdUndo } from 'react-icons/io'; import { Doc, DocListCast } from '../../../../fields/Doc'; -import { Networking } from '../../../Network'; -import { Utils } from '../../../../Utils'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { List } from '../../../../fields/List'; import { NumCast } from '../../../../fields/Types'; +import { Utils } from '../../../../Utils'; +import { Docs, DocUtils } from '../../../documents/Documents'; +import { Networking } from '../../../Network'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { OpenWhereMod } from '../DocumentView'; -import Buttons from './GenerativeFillButtons'; -import { List } from '../../../../fields/List'; -import { CgClose } from 'react-icons/cg'; import { ImageBox } from '../ImageBox'; +import './GenerativeFill.scss'; +import Buttons from './GenerativeFillButtons'; +import { BrushHandler } from './generativeFillUtils/BrushHandler'; +import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants'; +import { CursorData, ImageDimensions, Point } from './generativeFillUtils/generativeFillInterfaces'; +import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; +import { PointerHandler } from './generativeFillUtils/PointerHandler'; +import React = require('react'); enum BrushStyle { ADD, diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 6ff22e929..f750392f5 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../../fields/Doc'; +import { Doc, DocListCast, FieldResult, Opt, StrListCast } from '../../../../fields/Doc'; import { Animation } from '../../../../fields/DocSymbols'; import { Copy, Id } from '../../../../fields/FieldSymbols'; import { InkField } from '../../../../fields/InkField'; @@ -15,7 +15,7 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Ty import { AudioField } from '../../../../fields/URLField'; import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; -import { Docs, DocUtils } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index b0924888a..6a8b06377 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -1,23 +1,20 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction } from 'mobx'; import { observer } from 'mobx-react'; +import { EditorView } from 'prosemirror-view'; import { ColorState } from 'react-color'; import { Doc, Opt } from '../../../fields/Doc'; +import { StrCast } from '../../../fields/Types'; import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../Utils'; +import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT'; +import { DocumentType } from '../../documents/DocumentTypes'; import { SelectionManager } from '../../util/SelectionManager'; import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu'; import { LinkPopup } from '../linking/LinkPopup'; -import { ButtonDropdown } from '../nodes/formattedText/RichTextMenu'; -import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT'; -import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup'; -import { LightboxView } from '../LightboxView'; -import { EditorView } from 'prosemirror-view'; import './AnchorMenu.scss'; -import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; -import { StrCast } from '../../../fields/Types'; -import { DocumentType } from '../../documents/DocumentTypes'; +import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup'; @observer export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx index 6103b245c..540c1b5e1 100644 --- a/src/client/views/search/IconBar.tsx +++ b/src/client/views/search/IconBar.tsx @@ -1,17 +1,15 @@ import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { DocumentType } from "../../documents/DocumentTypes"; -// import "./SearchBox.scss"; -import "./IconBar.scss"; +import { DocumentType } from '../../documents/DocumentTypes'; +import './IconBar.scss'; import { IconButton } from './IconButton'; -import "./IconButton.scss"; +import './IconButton.scss'; export interface IconBarProps { setIcons: (icons: string[]) => void; } - @observer export class IconBar extends React.Component<IconBarProps> { public _allIcons: string[] = [DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.WEB, DocumentType.MAP]; @@ -32,7 +30,9 @@ export class IconBar extends React.Component<IconBarProps> { } @action.bound - getIcons(): string[] { return this._icons; } + getIcons(): string[] { + return this._icons; + } constructor(props: any) { super(props); @@ -40,29 +40,33 @@ export class IconBar extends React.Component<IconBarProps> { } @action.bound - getList(): string[] { return this.getIcons(); } + getList(): string[] { + return this.getIcons(); + } @action.bound - updateList(newList: string[]) { this.updateIcon(newList); } + updateList(newList: string[]) { + this.updateIcon(newList); + } @action.bound resetSelf = () => { this._resetClicked = true; this.updateList([]); - } + }; @action.bound selectAll = () => { this._selectAllClicked = true; this.updateList(this._allIcons); - } + }; render() { return ( <div className="icon-bar"> - {this._allIcons.map((type: string) => + {this._allIcons.map((type: string) => ( <IconButton key={type.toString()} type={type} /> - )} + ))} </div> ); } diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 1ceea697a..6b6b0f2ff 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -19,6 +19,7 @@ import { fetchRecommendations } from '../newlightbox/utils'; import { IRecommendation, Recommendation } from '../newlightbox/components'; import { Colors } from '../global/globalEnums'; import { SettingsManager } from '../../util/SettingsManager'; +import { SearchUtil } from '../../util/SearchUtil'; const DAMPENING_FACTOR = 0.9; const MAX_ITERATIONS = 25; @@ -132,34 +133,6 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { * @param {Doc[]} docs - docs to be searched through recursively * @param {number, Doc => void} func - function to be called on each doc * - * This method iterates through an array of docs and all docs within those docs, calling - * the function func on each doc. - */ - static foreachRecursiveDoc(docs: Doc[], func: (depth: number, doc: Doc) => void) { - let newarray: Doc[] = []; - var depth = 0; - const visited: Doc[] = []; - while (docs.length > 0) { - newarray = []; - docs.filter(d => d && !visited.includes(d)).forEach(d => { - visited.push(d); - const fieldKey = Doc.LayoutFieldKey(d); - const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView'); - const data = d[annos ? fieldKey + '_annotations' : fieldKey]; - data && newarray.push(...DocListCast(data)); - const sidebar = d[fieldKey + '_sidebar']; - sidebar && newarray.push(...DocListCast(sidebar)); - func(depth, d); - }); - docs = newarray; - depth++; - } - } - - /** - * @param {Doc[]} docs - docs to be searched through recursively - * @param {number, Doc => void} func - function to be called on each doc - * * This method iterates asynchronously through an array of docs and all docs within those * docs, calling the function func on each doc. */ @@ -213,74 +186,10 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { @action searchCollection(query: string) { this._selectedResult = undefined; - this._results = SearchBox.staticSearchCollection(CollectionDockingView.Instance?.rootDoc, query); + this._results = SearchUtil.SearchCollection(CollectionDockingView.Instance?.rootDoc, query); this.computePageRanks(); } - @action - static staticSearchCollection(rootDoc: Opt<Doc>, query: string) { - const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.SEARCH, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; - const blockedKeys = [ - 'x', - 'y', - 'proto', - 'width', - 'layout_autoHeight', - 'acl-Override', - 'acl-Guest', - 'embedContainer', - 'zIndex', - 'height', - 'text_scrollHeight', - 'text_height', - 'cloneFieldFilter', - 'isDataDoc', - 'text_annotations', - 'dragFactory_count', - 'text_noTemplate', - 'proto_embeddings', - 'isSystem', - 'layout_fieldKey', - 'isBaseProto', - 'xMargin', - 'yMargin', - 'links', - 'layout', - 'layout_keyValue', - 'layout_fitWidth', - 'type_collection', - 'title_custom', - 'freeform_panX', - 'freeform_panY', - 'freeform_scale', - ]; - query = query.toLowerCase(); - - const results = new Map<Doc, string[]>(); - if (rootDoc) { - const docs = DocListCast(rootDoc[Doc.LayoutFieldKey(rootDoc)]); - const docIDs: String[] = []; - SearchBox.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => { - const dtype = StrCast(doc.type) as DocumentType; - if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth >= 0) { - const hlights = new Set<string>(); - SearchBox.documentKeys(doc).forEach( - key => - Field.toString(doc[key] as Field) - .toLowerCase() - .includes(query) && hlights.add(key) - ); - blockedKeys.forEach(key => hlights.delete(key)); - - if (Array.from(hlights.keys()).length > 0) { - results.set(doc, Array.from(hlights.keys())); - } - } - docIDs.push(doc[Id]); - }); - } - return results; - } /** * This method initializes the page rank of every document to the reciprocal @@ -374,17 +283,6 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { } /** - * @param {Doc} doc - doc for which keys are returned - * - * This method returns a list of a document doc's keys. - */ - static documentKeys(doc: Doc) { - const keys: { [key: string]: boolean } = {}; - Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false))); - return Array.from(Object.keys(keys)); - } - - /** * This method submits a search with the _searchString as its query and updates * the results array accordingly. */ @@ -518,7 +416,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { className={className}> <div className="searchBox-result-title">{title as string}</div> <div className="searchBox-result-type">{formattedType}</div> - <div className="searchBox-result-keys" style={{ color: SettingsManager.Instance.userVariantColor }}> + <div className="searchBox-result-keys" style={{ color: SettingsManager.userVariantColor }}> {result[1].join(', ')} </div> </div> @@ -530,7 +428,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { const recommendationsJSX: JSX.Element[] = this._recommendations.map(props => <Recommendation {...props} />); return ( - <div className="searchBox-container" style={{ pointerEvents: 'all', color: SettingsManager.Instance.userColor, background: SettingsManager.Instance.userBackgroundColor }}> + <div className="searchBox-container" style={{ pointerEvents: 'all', color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}> <div className="searchBox-bar"> {isLinkSearch ? null : ( <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}> diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index c194ede32..a2f9de9ab 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -1,13 +1,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, IconButton, Size, Type, isDark } from 'browndash-components'; +import { Button, IconButton, isDark, Size, Type } from 'browndash-components'; import { action, computed, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { FaBug, FaCamera, FaStamp } from 'react-icons/fa'; +import { FaBug, FaCamera } from 'react-icons/fa'; import { Doc, DocListCast } from '../../../fields/Doc'; import { AclAdmin, DashVersion } from '../../../fields/DocSymbols'; import { StrCast } from '../../../fields/Types'; import { GetEffectiveAcl } from '../../../fields/util'; +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { PingManager } from '../../util/PingManager'; import { ReportManager } from '../../util/reportManager/ReportManager'; @@ -15,13 +16,12 @@ import { ServerStats } from '../../util/ServerStats'; import { SettingsManager } from '../../util/SettingsManager'; import { SharingManager } from '../../util/SharingManager'; import { UndoManager } from '../../util/UndoManager'; +import { CollectionDockingView } from '../collections/CollectionDockingView'; import { ContextMenu } from '../ContextMenu'; import { DashboardView } from '../DashboardView'; -import { MainView } from '../MainView'; -import { CollectionDockingView } from '../collections/CollectionDockingView'; import { Colors } from '../global/globalEnums'; +import { DocumentView } from '../nodes/DocumentView'; import './TopBar.scss'; -import { CurrentUserUtils } from '../../util/CurrentUserUtils'; /** * ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user @@ -43,7 +43,7 @@ export class TopBar extends React.Component { return StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE); } @computed get backgroundColor() { - return PingManager.Instance.IsBeating ? SettingsManager.Instance.userBackgroundColor : Colors.MEDIUM_GRAY; + return PingManager.Instance.IsBeating ? SettingsManager.userBackgroundColor : Colors.MEDIUM_GRAY; } @observable happyHeart: boolean = PingManager.Instance.IsBeating; @@ -76,9 +76,7 @@ export class TopBar extends React.Component { <span style={{ color: isDark(this.backgroundColor) ? Colors.LIGHT_BLUE : Colors.MEDIUM_BLUE, fontWeight: 500 }}>dash</span> </div> )} - {Doc.ActiveDashboard && ( - <Button text="Explore" tooltip="Browsing mode for directly navigating to documents" size={Size.SMALL} color={this.color} onClick={action(() => (MainView.Instance._exploreMode = !MainView.Instance._exploreMode))} /> - )} + {Doc.ActiveDashboard && <Button text="Explore" tooltip="Browsing mode for directly navigating to documents" size={Size.SMALL} color={this.color} onClick={action(() => (DocumentView.ExploreMode = !DocumentView.ExploreMode))} />} </div> ); } diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx index 02e44a793..524492226 100644 --- a/src/client/views/webcam/DashWebRTCVideo.tsx +++ b/src/client/views/webcam/DashWebRTCVideo.tsx @@ -6,8 +6,8 @@ import { observer } from 'mobx-react'; import { Doc } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; import '../../views/nodes/WebBox.scss'; -import { DocumentDecorations } from '../DocumentDecorations'; import { CollectionFreeFormDocumentViewProps } from '../nodes/CollectionFreeFormDocumentView'; +import { DocumentView } from '../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import './DashWebRTCVideo.scss'; import { hangup, initialize, refreshVideos } from './WebCamLogic'; @@ -71,8 +71,8 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV </div> ); - const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; - const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? '-interactive' : ''); + const frozen = !this.props.isSelected() || DocumentView.Interacting; + const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentView.Interacting ? '-interactive' : ''); return ( <> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 4ad38e7fc..f17e10d9e 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -47,7 +47,7 @@ import { FieldId, RefField } from './RefField'; import { RichTextField } from './RichTextField'; import { listSpec } from './Schema'; import { ComputedField, ScriptField } from './ScriptField'; -import { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types'; +import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types'; import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField'; import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions } from './util'; import JSZip = require('jszip'); @@ -156,7 +156,26 @@ export function updateCachedAcls(doc: Doc) { @scriptingGlobal @Deserializable('Doc', updateCachedAcls, ['id']) export class Doc extends RefField { - @observable public static CurrentlyLoading: Doc[]; + @observable public static RecordingEvent = 0; + + // this isn't really used at the moment, but is intended to indicate whether ink stroke are passed through a gesture recognizer + static GetRecognizeGestures() { + return BoolCast(Doc.UserDoc()._recognizeGestures); + } + static SetRecognizeGestures(show: boolean) { + Doc.UserDoc()._recognizeGestures = show; + } + + // + // This controls whether fontIconButtons will display labels under their icons or not + // + static GetShowIconLabels() { + return BoolCast(Doc.UserDoc()._showLabel); + } + static SetShowIconLabels(show: boolean) { + Doc.UserDoc()._showLabel = show; + } + @observable public static CurrentlyLoading: Doc[] = []; // this assignment doesn't work. the actual assignment happens in DocumentManager's constructor // removes from currently loading display @action public static removeCurrentlyLoading(doc: Doc) { @@ -169,9 +188,6 @@ export class Doc extends RefField { // adds doc to currently loading display @action public static addCurrentlyLoading(doc: Doc) { - if (!Doc.CurrentlyLoading) { - Doc.CurrentlyLoading = []; - } if (Doc.CurrentlyLoading.indexOf(doc) === -1) { Doc.CurrentlyLoading.push(doc); } diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts index 24cd078f2..5ecf25e08 100644 --- a/src/fields/RichTextUtils.ts +++ b/src/fields/RichTextUtils.ts @@ -2,20 +2,20 @@ import { AssertionError } from 'assert'; import { docs_v1 } from 'googleapis'; import { Fragment, Mark, Node } from 'prosemirror-model'; import { sinkListItem } from 'prosemirror-schema-list'; -import { Utils, DashColor } from '../Utils'; -import { Docs, DocUtils } from '../client/documents/Documents'; -import { schema } from '../client/views/nodes/formattedText/schema_rts'; +import { EditorState, TextSelection, Transaction } from 'prosemirror-state'; +import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils'; import { GooglePhotos } from '../client/apis/google_docs/GooglePhotosClientUtils'; import { DocServer } from '../client/DocServer'; +import { Docs, DocUtils } from '../client/documents/Documents'; import { Networking } from '../client/Network'; import { FormattedTextBox } from '../client/views/nodes/formattedText/FormattedTextBox'; +import { schema } from '../client/views/nodes/formattedText/schema_rts'; +import { DashColor, Utils } from '../Utils'; import { Doc, Opt } from './Doc'; import { Id } from './FieldSymbols'; import { RichTextField } from './RichTextField'; import { Cast, StrCast } from './Types'; import Color = require('color'); -import { EditorState, TextSelection, Transaction } from 'prosemirror-state'; -import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils'; export namespace RichTextUtils { const delimiter = '\n'; |