aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/CurrentUserUtils.ts20
-rw-r--r--src/client/util/DictationManager.ts2
-rw-r--r--src/client/util/DocumentManager.ts22
-rw-r--r--src/client/util/DragManager.ts24
-rw-r--r--src/client/util/DropConverter.ts14
-rw-r--r--src/client/util/GroupManager.tsx18
-rw-r--r--src/client/util/GroupMemberView.tsx2
-rw-r--r--src/client/util/RTFMarkup.tsx12
-rw-r--r--src/client/util/SearchUtil.ts104
-rw-r--r--src/client/util/SelectionManager.ts31
-rw-r--r--src/client/util/SettingsManager.tsx107
-rw-r--r--src/client/util/SharingManager.tsx11
-rw-r--r--src/client/util/reportManager/ReportManager.tsx7
13 files changed, 247 insertions, 127 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 4dca38b21..1f130ab1e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,7 +1,6 @@
import { observable, reaction, runInAction } from "mobx";
import * as rp from 'request-promise';
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
-import { FieldLoader } from "../../fields/FieldLoader";
import { InkTool } from "../../fields/InkField";
import { List } from "../../fields/List";
import { PrefetchProxy } from "../../fields/Proxy";
@@ -12,16 +11,16 @@ import { Cast, DateCast, DocCast, StrCast } from "../../fields/Types";
import { nullAudio } from "../../fields/URLField";
import { SetCachedGroups, SharingPermissions } from "../../fields/util";
import { GestureUtils } from "../../pen-gestures/GestureUtils";
-import { OmitKeys, Utils, addStyleSheetRule } from "../../Utils";
+import { addStyleSheetRule, OmitKeys, Utils } from "../../Utils";
import { DocServer } from "../DocServer";
import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents";
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 { ButtonType } from "../views/nodes/FontIconBox/FontIconBox";
import { OpenWhere } from "../views/nodes/DocumentView";
+import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox";
+import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox";
import { OverlayView } from "../views/OverlayView";
import { DragManager, dropActionType } from "./DragManager";
import { MakeTemplate } from "./DropConverter";
@@ -31,7 +30,6 @@ import { ScriptingGlobals } from "./ScriptingGlobals";
import { ColorScheme, SettingsManager } from "./SettingsManager";
import { UndoManager } from "./UndoManager";
import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox";
-import { DocumentManager } from "./DocumentManager";
interface Button {
// DocumentOptions fields a button can set
@@ -273,7 +271,7 @@ export class CurrentUserUtils {
{key: "Webpage", creator: opts => Docs.Create.WebDocument("",opts), opts: { _width: 400, _height: 512, _nativeWidth: 850, data_useCors: true, }},
{key: "Comparison", creator: Docs.Create.ComparisonDocument, opts: { _width: 300, _height: 300 }},
{key: "Audio", creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }},
- {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _layout_fitWidth: true, _layout_showSidebar: true, }},
+ {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _layout_fitWidth: true, }},
{key: "Screengrab", creator: Docs.Create.ScreenshotDocument, opts: { _width: 400, _height: 200 }},
{key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, recording:true, isSystem: true, cloneFieldFilter: new List<string>(["isSystem"]) }},
{key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
@@ -624,9 +622,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[] {
@@ -861,7 +859,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;
@@ -1016,8 +1013,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; });
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 7c3b5be05..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(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 40bf57555..5a11f2dca 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -62,12 +62,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) {
@@ -401,6 +395,7 @@ export namespace DragManager {
dragLabel.style.zIndex = '100001';
dragLabel.style.fontSize = '10px';
dragLabel.style.position = 'absolute';
+ dragLabel.style.background = '#ffffff90';
dragLabel.innerText = 'drag titlebar to embed on drop'; // bcz: need to move this to a status bar
dragDiv.appendChild(dragLabel);
DragManager.Root().appendChild(dragDiv);
@@ -516,7 +511,7 @@ export namespace DragManager {
runInAction(() => docsBeingDragged.push(...docsToDrag));
const hideDragShowOriginalElements = (hide: boolean) => {
- dragLabel.style.display = hide ? '' : 'none';
+ dragLabel.style.display = hide && !CanEmbed ? '' : 'none';
!hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
setTimeout(() => eles.forEach(ele => (ele.hidden = hide)));
};
@@ -661,18 +656,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 f35844020..8973306bf 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -1,22 +1,22 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Button, IconButton, Size, Type } from 'browndash-components';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import Select from 'react-select';
import * as RequestPromise from 'request-promise';
-import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc';
-import { StrCast, Cast } from '../../fields/Types';
+import { DateField } from '../../fields/DateField';
+import { Doc, DocListCast, Opt } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { listSpec } from '../../fields/Schema';
+import { Cast, StrCast } from '../../fields/Types';
import { Utils } from '../../Utils';
import { MainViewModal } from '../views/MainViewModal';
import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
import './GroupManager.scss';
import { GroupMemberView } from './GroupMemberView';
-import { SharingManager, User } from './SharingManager';
-import { listSpec } from '../../fields/Schema';
-import { DateField } from '../../fields/DateField';
-import { Id } from '../../fields/FieldSymbols';
-import { Button, IconButton, Size, Type } from 'browndash-components';
import { SettingsManager } from './SettingsManager';
+import { SharingManager, User } from './SharingManager';
/**
* Interface for options for the react-select component
@@ -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/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
index a0fc617ab..78069d323 100644
--- a/src/client/util/RTFMarkup.tsx
+++ b/src/client/util/RTFMarkup.tsx
@@ -3,6 +3,8 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { MainViewModal } from '../views/MainViewModal';
import { SettingsManager } from './SettingsManager';
+import { Doc } from '../../fields/Doc';
+import { StrCast } from '../../fields/Types';
@observer
export class RTFMarkup extends React.Component<{}> {
@@ -133,6 +135,14 @@ export class RTFMarkup extends React.Component<{}> {
}
render() {
- return <MainViewModal contents={this.cheatSheet} isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} />;
+ return (
+ <MainViewModal
+ dialogueBoxStyle={{ backgroundColor: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor), padding: '16px' }}
+ contents={this.cheatSheet}
+ isDisplayed={this.isOpen}
+ interactive={true}
+ closeOnExternalClick={this.close}
+ />
+ );
}
}
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/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 4be9448b3..dbf33fcf5 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,11 +1,14 @@
import { action, observable, ObservableMap } from 'mobx';
import { computedFn } from 'mobx-utils';
import { Doc, Opt } from '../../fields/Doc';
-import { DocCast } from '../../fields/Types';
-import { CollectionViewType } from '../documents/DocumentTypes';
+import { List } from '../../fields/List';
+import { listSpec } from '../../fields/Schema';
+import { Cast, DocCast } from '../../fields/Types';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
import { DocumentView } from '../views/nodes/DocumentView';
import { LinkManager } from './LinkManager';
import { ScriptingGlobals } from './ScriptingGlobals';
+import { UndoManager } from './UndoManager';
export namespace SelectionManager {
class Manager {
@@ -124,3 +127,27 @@ ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, exp
ScriptingGlobals.add(function deselectAll() {
SelectionManager.DeselectAll();
});
+ScriptingGlobals.add(function undo() {
+ SelectionManager.DeselectAll();
+ return UndoManager.Undo();
+});
+
+export function ShowUndoStack() {
+ SelectionManager.DeselectAll();
+ var buffer = '';
+ UndoManager.undoStack.forEach((batch, i) => {
+ buffer += 'Batch => ' + UndoManager.undoStackNames[i] + '\n';
+ ///batch.forEach(undo => (buffer += ' ' + undo.prop + '\n'));
+ });
+ alert(buffer);
+}
+ScriptingGlobals.add(function redo() {
+ SelectionManager.DeselectAll();
+ return UndoManager.Redo();
+});
+ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
+ const docs = SelectionManager.Views()
+ .map(dv => dv.props.Document)
+ .filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
+ return docs.length ? new List(docs) : prevValue;
+});
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 8133e9eff..720badd40 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -13,13 +13,9 @@ 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';
-const higflyout = require('@hig/flyout');
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
export enum ColorScheme {
Dark = 'Dark',
@@ -51,13 +47,6 @@ export class SettingsManager extends React.Component<{}> {
@observable public static propertiesWidth: number = 0;
@observable public static headerBarHeight: number = 0;
- @computed get backgroundColor() {
- return Doc.UserDoc().activeCollectionBackground;
- }
- @computed get userTheme() {
- return Doc.UserDoc().userTheme;
- }
-
constructor(props: {}) {
super(props);
SettingsManager.Instance = this;
@@ -77,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);
}
@@ -97,12 +86,8 @@ export class SettingsManager extends React.Component<{}> {
Doc.UserDoc().userBackgroundColor = color;
addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${color} !important` });
});
- @undoBatch switchUserColor = action((color: string) => {
- Doc.UserDoc().userColor = color;
- });
- @undoBatch switchUserVariantColor = action((color: string) => {
- Doc.UserDoc().userVariantColor = color;
- });
+ @undoBatch switchUserColor = action((color: string) => (Doc.UserDoc().userColor = color));
+ @undoBatch switchUserVariantColor = action((color: string) => (Doc.UserDoc().userVariantColor = color));
@undoBatch playgroundModeToggle = action(() => {
this.playgroundMode = !this.playgroundMode;
if (this.playgroundMode) {
@@ -164,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}
/>
@@ -212,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'}
@@ -221,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'}
@@ -248,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'}
@@ -257,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'}
@@ -266,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>
);
@@ -298,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 {
@@ -315,7 +300,7 @@ export class SettingsManager extends React.Component<{}> {
setSelectedVal={val => {
this.changeFontFamily(val as string);
}}
- color={this.userColor}
+ color={SettingsManager.userColor}
fillWidth
/>
</Group>
@@ -343,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>
);
}
@@ -408,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
@@ -436,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>
@@ -463,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;
@@ -471,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))}>
@@ -483,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 6171c01d7..9a9097bf7 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -18,11 +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 { LinkManager } from './LinkManager';
+import { SearchUtil } from './SearchUtil';
import { SelectionManager } from './SelectionManager';
import { SettingsManager } from './SettingsManager';
import './SharingManager.scss';
@@ -447,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);
}
@@ -528,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>
&nbsp;
- {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)}>
@@ -553,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 7aad0f2b1..6a236face 100644
--- a/src/client/util/reportManager/ReportManager.tsx
+++ b/src/client/util/reportManager/ReportManager.tsx
@@ -20,9 +20,6 @@ import { Filter, FormInput, FormTextArea, IssueCard, IssueView, Tag } from './Re
import { StrCast } from '../../../fields/Types';
import { MdRefresh } from 'react-icons/md';
import { SettingsManager } from '../SettingsManager';
-const higflyout = require('@hig/flyout');
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
/**
* Class for reporting and viewing Github issues within the app.
@@ -214,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>