aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts2
-rw-r--r--src/client/DocServer.ts62
-rw-r--r--src/client/documents/DocumentTypes.ts4
-rw-r--r--src/client/documents/Documents.ts20
-rw-r--r--src/client/util/CurrentUserUtils.ts99
-rw-r--r--src/client/util/DocumentManager.ts5
-rw-r--r--src/client/util/DragManager.ts4
-rw-r--r--src/client/util/GroupManager.tsx5
-rw-r--r--src/client/util/GroupMemberView.tsx3
-rw-r--r--src/client/util/LinkManager.ts4
-rw-r--r--src/client/util/PingManager.ts6
-rw-r--r--src/client/util/ReplayMovements.ts1
-rw-r--r--src/client/util/ServerStats.tsx26
-rw-r--r--src/client/util/SettingsManager.tsx11
-rw-r--r--src/client/util/SharingManager.tsx11
-rw-r--r--src/client/util/reportManager/ReportManager.tsx5
-rw-r--r--src/client/views/DashboardView.tsx10
-rw-r--r--src/client/views/DocComponent.tsx4
-rw-r--r--src/client/views/DocumentDecorations.tsx6
-rw-r--r--src/client/views/EditableView.tsx3
-rw-r--r--src/client/views/FilterPanel.scss38
-rw-r--r--src/client/views/FilterPanel.tsx100
-rw-r--r--src/client/views/InkingStroke.tsx16
-rw-r--r--src/client/views/LightboxView.tsx6
-rw-r--r--src/client/views/Main.tsx11
-rw-r--r--src/client/views/MainView.tsx12
-rw-r--r--src/client/views/PropertiesButtons.tsx1
-rw-r--r--src/client/views/PropertiesDocContextSelector.tsx1
-rw-r--r--src/client/views/PropertiesSection.tsx81
-rw-r--r--src/client/views/PropertiesView.tsx760
-rw-r--r--src/client/views/StyleProvider.tsx12
-rw-r--r--src/client/views/UndoStack.tsx43
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx7
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx1
-rw-r--r--src/client/views/collections/CollectionMenu.tsx2
-rw-r--r--src/client/views/collections/CollectionPileView.tsx3
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx3
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx1
-rw-r--r--src/client/views/collections/TabDocView.tsx2
-rw-r--r--src/client/views/collections/TreeView.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx1
-rw-r--r--src/client/views/global/globalScripts.ts1
-rw-r--r--src/client/views/newlightbox/ExploreView/ExploreView.tsx3
-rw-r--r--src/client/views/newlightbox/components/Recommendation/Recommendation.tsx1
-rw-r--r--src/client/views/nodes/ColorBox.tsx9
-rw-r--r--src/client/views/nodes/DataVizBox/utils/D3Utils.ts1
-rw-r--r--src/client/views/nodes/DocumentView.tsx6
-rw-r--r--src/client/views/nodes/EquationBox.scss9
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx51
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx2
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx1
-rw-r--r--src/client/views/nodes/ScreenshotBox.scss36
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx12
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx14
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx22
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts26
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx5
-rw-r--r--src/client/views/search/SearchBox.tsx79
-rw-r--r--src/client/views/topbar/TopBar.tsx44
-rw-r--r--src/fields/Doc.ts81
-rw-r--r--src/fields/DocSymbols.ts2
-rw-r--r--src/fields/FieldLoader.tsx5
-rw-r--r--src/fields/Types.ts9
-rw-r--r--src/mobile/MobileMain.tsx2
-rw-r--r--src/server/ApiManagers/UploadManager.ts3
-rw-r--r--src/server/ApiManagers/UserManager.ts14
-rw-r--r--src/server/DashUploadUtils.ts6
-rw-r--r--src/server/server_Initialization.ts4
70 files changed, 932 insertions, 921 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 8b9fe2aab..7f83ab8f5 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -76,7 +76,7 @@ export namespace Utils {
});
return returnedUri;
} catch (e) {
- console.log('VideoBox :' + e);
+ console.log('ConvertDataURI :' + e);
}
}
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index ad5a73598..53c7b857a 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,16 +1,18 @@
import { runInAction } from 'mobx';
import * as rp from 'request-promise';
import * as io from 'socket.io-client';
-import { Doc, Opt } from '../fields/Doc';
+import { Doc, DocListCast, Opt } from '../fields/Doc';
import { UpdatingFromServer } from '../fields/DocSymbols';
import { FieldLoader } from '../fields/FieldLoader';
import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols';
import { ObjectField } from '../fields/ObjectField';
import { RefField } from '../fields/RefField';
-import { StrCast } from '../fields/Types';
+import { DocCast, StrCast } from '../fields/Types';
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';
@@ -30,35 +32,44 @@ import { GestureOverlay } from './views/GestureOverlay';
export namespace DocServer {
let _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {};
- export function QUERY_SERVER_CACHE(title: string) {
+ export function FindDocByTitle(title: string) {
const foundDocId = Array.from(Object.keys(_cache))
.filter(key => _cache[key] instanceof Doc)
.find(key => (_cache[key] as Doc).title === title);
return foundDocId ? (_cache[foundDocId] as Doc) : undefined;
}
- let lastCacheUpdate = 0;
- export function UPDATE_SERVER_CACHE(print: boolean = false) {
- if (print) {
- const strings: string[] = [];
- Array.from(Object.keys(_cache)).forEach(key => {
- const doc = _cache[key];
- if (doc instanceof Doc) strings.push(StrCast(doc.author) + ' ' + StrCast(doc.title) + ' ' + StrCast(Doc.GetT(doc, 'title', 'string', true)));
- });
- strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
- }
- const filtered = Array.from(Object.keys(_cache)).filter(key => {
- const doc = _cache[key] as Doc;
- return true;
- if (!(StrCast(doc.author).includes('.edu') || StrCast(doc.author).includes('.com')) || doc.author === Doc.CurrentUserEmail) return true;
- return false;
+ let cacheDocumentIds = ''; // ; separate string of all documents ids in the user's working set (cached on the server)
+ export let CacheNeedsUpdate = false;
+ export function UPDATE_SERVER_CACHE() {
+ const prototypes = Object.values(DocumentType)
+ .filter(type => type !== DocumentType.NONE)
+ .map(type => _cache[type + 'Proto'])
+ .filter(doc => doc instanceof Doc)
+ .map(doc => doc as Doc);
+ const references = new Set<Doc>(prototypes);
+ Doc.FindReferences(Doc.UserDoc(), references, undefined);
+ DocListCast(DocCast(Doc.UserDoc().myLinkDatabase).data).forEach(link => {
+ if (!references.has(DocCast(link.link_anchor_1)) && !references.has(DocCast(link.link_anchor_2))) {
+ Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myLinkDatabase), 'data', link);
+ }
});
- if (filtered.length === lastCacheUpdate) return;
- lastCacheUpdate = filtered.length;
+ LinkManager.userLinkDBs.forEach(linkDb => Doc.FindReferences(linkDb, references, undefined));
+ const filtered = Array.from(references);
+
+ const newCacheUpdate = filtered.map(doc => doc[Id]).join(';');
+ if (newCacheUpdate === cacheDocumentIds) return;
+ cacheDocumentIds = newCacheUpdate;
+
+ // print out cached docs
+ console.log('Set cached docs = ');
+ const is_filtered = filtered.filter(doc => !Doc.IsSystem(doc));
+ const strings = is_filtered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
+ strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
rp.post(Utils.prepend('/setCacheDocumentIds'), {
body: {
- cacheDocumentIds: filtered.join(';'),
+ cacheDocumentIds,
},
json: true,
});
@@ -157,7 +168,7 @@ export namespace DocServer {
_cache = {};
USER_ID = identifier;
protocol = protocol.startsWith('https') ? 'wss' : 'ws';
- _socket = io.connect(`${protocol}://${hostname}:${port}`);
+ _socket = require('socket.io-client')(`${protocol}://${hostname}:${port}`, { transports: ['websocket'], rejectUnauthorized: false });
// io.connect(`https://7f079dda.ngrok.io`);// if using ngrok, create a special address for the websocket
_GetCachedRefField = _GetCachedRefFieldImpl;
@@ -353,7 +364,7 @@ export namespace DocServer {
// i) which documents need to be fetched
// ii) which are already in the process of being fetched
// iii) which already exist in the cache
- for (const id of ids) {
+ for (const id of ids.filter(id => id)) {
const cached = _cache[id];
if (cached === undefined) {
defaultPromises.push({
@@ -382,7 +393,7 @@ export namespace DocServer {
// fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of
// the fields have been returned from the server
console.log('Requesting ' + requestedIds.length);
- FieldLoader.active && runInAction(() => (FieldLoader.ServerLoadStatus.requested = requestedIds.length));
+ setTimeout(() => runInAction(() => (FieldLoader.ServerLoadStatus.requested = requestedIds.length)));
const serializedFields = await Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
// 3) when the serialized RefFields have been received, go head and begin deserializing them into objects.
@@ -392,7 +403,7 @@ export namespace DocServer {
console.log('deserializing ' + serializedFields.length + ' fields');
for (const field of serializedFields) {
processed++;
- if (FieldLoader.active && processed % 150 === 0) {
+ if (processed % 150 === 0) {
runInAction(() => (FieldLoader.ServerLoadStatus.retrieved = processed));
await new Promise(res => setTimeout(res)); // force loading to yield to splash screen rendering to update progress
}
@@ -478,6 +489,7 @@ export namespace DocServer {
* @param field the [RefField] to be serialized and sent to the server to be stored in the database
*/
export function CreateField(field: RefField) {
+ CacheNeedsUpdate = true;
_CreateField(field);
}
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index b629d9b8c..1d0ddce40 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -14,13 +14,11 @@ export enum DocumentType {
INK = 'ink',
SCREENSHOT = 'screenshot',
FONTICON = 'fonticonbox',
- FILTER = 'filter',
SEARCH = 'search', // search query
LABEL = 'label', // simple text label
BUTTON = 'button', // onClick button
WEBCAM = 'webcam', // webcam
CONFIG = 'config', // configuration document intended to specify a view layout configuration, but not be directly rendered (e.g., for saving the page# of a PDF, or view transform of a collection)
- DATE = 'date', // calendar view of a date
SCRIPTING = 'script', // script editor
EQUATION = 'equation', // equation editor
FUNCPLOT = 'funcplot', // function plotter
@@ -31,14 +29,12 @@ export enum DocumentType {
// special purpose wrappers that either take no data or are compositions of lower level types
LINK = 'link',
- LINKANCHOR = 'linkanchor',
IMPORT = 'import',
SLIDER = 'slider',
PRES = 'presentation',
PRESELEMENT = 'preselement',
COLOR = 'color',
YOUTUBE = 'youtube',
- SEARCHITEM = 'searchitem',
COMPARISON = 'comparison',
GROUP = 'group',
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 1988c6328..1bfd913a0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,5 +1,5 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { action, runInAction } from 'mobx';
+import { action, reaction, runInAction } from 'mobx';
import { basename } from 'path';
import { DateField } from '../../fields/DateField';
import { Doc, DocListCast, Field, Opt, updateCachedAcls } from '../../fields/Doc';
@@ -765,6 +765,10 @@ export namespace Docs {
// an entry dedicated to the given DocumentType)
target && PrototypeMap.set(type, target);
});
+ reaction(
+ () => (proto => StrCast(proto?.BROADCAST_MESSAGE))(DocServer.GetCachedRefField('rtfProto') as Doc),
+ msg => msg && alert(msg)
+ );
}
/**
@@ -908,8 +912,6 @@ export namespace Docs {
DocUtils.MakeLinkToActiveAudio(() => viewDoc);
}
- Doc.AddFileOrphan(dataDoc);
-
updateCachedAcls(dataDoc);
updateCachedAcls(viewDoc);
@@ -1095,7 +1097,12 @@ export namespace Docs {
}
export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { dropAction: 'move', _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile }, id);
+ return InstanceFromProto(
+ Prototypes.get(DocumentType.COL),
+ new List(documents),
+ { backgroundColor: 'transparent', dropAction: 'move', _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile },
+ id
+ );
}
export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -1171,9 +1178,6 @@ export namespace Docs {
export function FontIconDocument(options?: DocumentOptions) {
return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { ...(options || {}) });
}
- export function FilterDocument(options?: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.FILTER), undefined, { ...(options || {}) });
- }
export function PresElementBoxDocument() {
return Prototypes.get(DocumentType.PRESELEMENT);
@@ -1701,7 +1705,6 @@ export namespace DocUtils {
newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size;
newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size;
newCollection._width = newCollection._height = size * 2;
- newCollection._jitterRotation = 10;
return newCollection;
}
}
@@ -1893,7 +1896,6 @@ export namespace DocUtils {
export function copyDragFactory(dragFactory: Doc) {
if (!dragFactory) return undefined;
const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
- ndoc && Doc.AddFileOrphan(Doc.GetProto(ndoc));
if (ndoc && dragFactory['dragFactory_count'] !== undefined) {
dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1;
Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(), true);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 21bcec6d1..c9a5175bb 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,4 +1,4 @@
-import { reaction } from "mobx";
+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";
@@ -152,7 +152,7 @@ export class CurrentUserUtils {
{ noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" },
{ noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
const reqdNoteList = reqdTempOpts.map(opts => {
- const reqdOpts = {...opts, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true};
+ const reqdOpts = {...opts, isSystem:true, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true};
const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
});
@@ -295,7 +295,7 @@ export class CurrentUserUtils {
{ toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, clickFactory: DocCast(doc.emptyNote)},
{ toolTip: "Tap or drag to create a flashcard", title: "Flashcard", icon: "id-card", dragFactory: doc.emptyFlashcard as Doc, clickFactory: DocCast(doc.emptyFlashcard)},
{ toolTip: "Tap or drag to create an equation", title: "Math", icon: "calculator", dragFactory: doc.emptyEquation as Doc, clickFactory: DocCast(doc.emptyEquation)},
- { toolTip: "Tap or drag to create a physics simulation", title: "Simulation", icon: "atom", dragFactory: doc.emptySimulation as Doc, },
+ { toolTip: "Tap or drag to create a physics simulation", title: "Simulation", icon: "atom", dragFactory: doc.emptySimulation as Doc, funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a note board", title: "Notes", icon: "folder", dragFactory: doc.emptyNoteboard as Doc, clickFactory: DocCast(doc.emptyNoteboard)},
{ toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc, clickFactory: DocCast(doc.emptyTab)},
{ toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, clickFactory: DocCast(doc.emptyWebpage)},
@@ -303,18 +303,19 @@ export class CurrentUserUtils {
{ toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, clickFactory: DocCast(doc.emptyAudio), openFactoryLocation: OpenWhere.overlay},
{ toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, clickFactory: DocCast(doc.emptyMap)},
{ toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreengrab as Doc, clickFactory: DocCast(doc.emptyScreengrab), openFactoryLocation: OpenWhere.overlay},
- { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, clickFactory: DocCast(doc.emptyWebCam), openFactoryLocation: OpenWhere.overlay},
+ { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, clickFactory: DocCast(doc.emptyWebCam), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", dragFactory: doc.emptyButton as Doc, clickFactory: DocCast(doc.emptyButton)},
- { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, clickFactory: DocCast(doc.emptyScript)},
+ { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, clickFactory: DocCast(doc.emptyScript), funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "file", dragFactory: doc.emptyDataViz as Doc, clickFactory: DocCast(doc.emptyDataViz)},
- { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "file", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay},
- { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize",dragFactory: doc.emptyHeader as Doc,clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true },
- { toolTip: "Toggle a Calculator REPL", title: "replviewer", icon: "calculator", clickFactory: '<ScriptingRepl />' as any, openFactoryLocation: OpenWhere.overlay}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
- { toolTip: "Toggle an UndoStack", title: "undostacker", icon: "calculator", clickFactory: "<UndoStack>" as any, openFactoryLocation: OpenWhere.overlay},
+ { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "file", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
+ { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize",dragFactory: doc.emptyHeader as Doc,clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true , funcs: { hidden: "IsNoviceMode()"}},
+ { toolTip: "Toggle a Calculator REPL", title: "replviewer", icon: "calculator", clickFactory: '<ScriptingRepl />' as any, openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
+ { toolTip: "Toggle an UndoStack", title: "undostacker", icon: "calculator", clickFactory: "<UndoStack>" as any, openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
].map(tuple => (
{ openFactoryLocation: OpenWhere.addRight,
scripts: { onClick: 'openDoc(copyDragFactory(this.clickFactory,this.openFactoryAsDelegate), this.openFactoryLocation)',
onDragStart: '{ return copyDragFactory(this.dragFactory,this.openFactoryAsDelegate); }'},
+ funcs: tuple.funcs,
...tuple, }))
}
@@ -323,7 +324,7 @@ export class CurrentUserUtils {
const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit,
- _width: 35, _height: 35, _layout_hideContextMenu: true, _dragOnlyWithinContainer: true,
+ _width: 60, _height: 60, _layout_hideContextMenu: true, _dragOnlyWithinContainer: true,
btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true,
};
return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
@@ -339,16 +340,16 @@ export class CurrentUserUtils {
}
/// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
- static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, toolTip: string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}}[] {
+ static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, toolTip: string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}, hidden?: boolean}[] {
const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())";
const getActiveDashTrails = "Doc.ActiveDashboard?.myTrails";
return [
{ title: "Dashboards", toolTip: "Dashboards", target: this.setupDashboards(doc, "myDashboards"), ignoreClick: true, icon: "desktop", funcs: {hidden: "IsNoviceMode()"} },
{ title: "Search", toolTip: "Search ⌘F", target: this.setupSearcher(doc, "mySearcher"), ignoreClick: true, icon: "search", },
{ title: "Files", toolTip: "Files", target: this.setupFilesystem(doc, "myFilesystem"), ignoreClick: true, icon: "folder-open", },
- { title: "Tools", toolTip: "Tools", target: this.setupToolsBtnPanel(doc, "myTools"), ignoreClick: true, icon: "wrench", funcs: {hidden: "IsNoviceMode()"} },
+ { title: "Tools", toolTip: "Tools", target: this.setupToolsBtnPanel(doc, "myTools"), ignoreClick: true, icon: "wrench", },
{ title: "Imports", toolTip: "Imports ⌘I", target: this.setupImportSidebar(doc, "myImports"), ignoreClick:false, icon: "upload", },
- { title: "Closed", toolTip: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), ignoreClick: true, icon: "archive", },
+ { title: "Closed", toolTip: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), ignoreClick: true, icon: "archive", hidden: true }, // this doc is hidden from the Sidebar, but it's still being used in MyFilesystem which ignores the hidden field
{ title: "Shared", toolTip: "Shared Docs", target: Doc.MySharedDocs, ignoreClick: true, icon: "users", funcs: {badgeValue: badgeValue}},
{ title: "Trails", toolTip: "Trails ⌘R", target: Doc.UserDoc(), ignoreClick: true, icon: "pres-trail", funcs: {target: getActiveDashTrails}},
{ title: "User Doc", toolTip: "User Doc", target: this.setupUserDocView(doc, "myUserDocView"), ignoreClick: true, icon: "address-card",funcs: {hidden: "IsNoviceMode()"} },
@@ -357,17 +358,17 @@ export class CurrentUserUtils {
/// the empty panel that is filled with whichever left menu button's panel has been selected
static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
- DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as any), {isSystem:true, undoIgnoreFields: new List<string>(['proto'])});
+ DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as any), {title:"leftSidebarPanel", isSystem:true, undoIgnoreFields: new List<string>(['proto'])});
}
/// Initializes the left sidebar menu buttons and the panels they open up
static setupLeftSidebarMenu(doc: Doc, field="myLeftSidebarMenu") {
this.setupLeftSidebarPanel(doc);
const myLeftSidebarMenu = DocCast(doc[field]);
- const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, toolTip, scripts, funcs }) => {
+ const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, toolTip, hidden, scripts, funcs }) => {
const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
const reqdBtnOpts:DocumentOptions = {
- title, icon, target, toolTip, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List<string>(['height', 'data_columnHeaders']), dontRegisterView: true,
+ title, icon, target, toolTip, hidden, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List<string>(['height', 'data_columnHeaders']), dontRegisterView: true,
_width: 60, _height: 60, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true,
};
return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
@@ -495,7 +496,7 @@ export class CurrentUserUtils {
const reqdOpts:DocumentOptions = {
title: "My Dashboards", childHideLinkButton: true, treeViewFreezeChildren: "remove|add", treeViewHideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
dropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true, treeViewTruncateTitleWidth: 350, ignoreClick: true,
- layout_headerButton: newDashboardButton, childDragAction: "embed",
+ layout_headerButton: newDashboardButton, childDragAction: "none",
_layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
contextMenuLabels:new List<string>(contextMenuLabels),
contextMenuIcons:new List<string>(contextMenuIcons),
@@ -519,7 +520,6 @@ export class CurrentUserUtils {
/// initializes the left sidebar File system pane
static setupFilesystem(doc: Doc, field:string) {
var myFilesystem = DocCast(doc[field]);
- const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", undoIgnoreFields:new List<string>(['treeViewSortCriterion']), _dragOnlyWithinContainer: true, isSystem: true, isFolder: true });
const newFolder = `TreeView_addNewFolder()`;
const newFolderOpts: DocumentOptions = {
@@ -530,14 +530,15 @@ export class CurrentUserUtils {
const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true,
- title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: "proto", isSystem: true,
+ title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: 'add', isSystem: true,
isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
treeViewTruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed",
childContextMenuLabels: new List<string>(["Create new folder"]),
childContextMenuIcons: new List<string>(["plus"]),
layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
};
- myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]);
+ const fileFolders = new Set(DocListCast(DocCast(doc[field])?.data));
+ myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, Array.from(fileFolders));
const childContextMenuScripts = [newFolder];
if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
myFilesystem.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
@@ -547,8 +548,8 @@ export class CurrentUserUtils {
/// initializes the panel displaying docs that have been recently closed
static setupRecentlyClosed(doc: Doc, field:string) {
- const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true,
- title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "embed", isSystem: true,
+ const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, isFolder: true,
+ title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "move", isSystem: true,
treeViewTruncateTitleWidth: 350, ignoreClick: true, layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same",
contextMenuLabels: new List<string>(["Empty recently closed"]),
contextMenuIcons:new List<string>(["trash"]),
@@ -562,8 +563,6 @@ export class CurrentUserUtils {
toolTip: "Empty recently closed",};
DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
- //if (recentlyClosed.layout_headerButton !== clearDocsButton) Doc.GetProto(recentlyClosed).layout_headerButton = clearDocsButton;
-
if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) {
recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("self"))!])
}
@@ -777,6 +776,7 @@ export class CurrentUserUtils {
const linkDocs = new Doc(linkDatabaseId, true);
linkDocs.title = "LINK DATABASE: " + Doc.CurrentUserEmail;
linkDocs.author = Doc.CurrentUserEmail;
+ linkDocs.isSystem = true;
linkDocs.data = new List<Doc>([]);
linkDocs["acl-Guest"] = SharingPermissions.Augment;
doc.myLinkDatabase = new PrefetchProxy(linkDocs);
@@ -877,11 +877,13 @@ export class CurrentUserUtils {
this.setupDockedButtons(doc); // the bottom bar of font icons
this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
this.setupDocTemplates(doc); // sets up the template menu of templates
- this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
+ //this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {});
- DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
+ DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
+ Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards)
Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
+ Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed)
if (doc.activeDashboard instanceof Doc) {
// undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
@@ -889,7 +891,7 @@ export class CurrentUserUtils {
}
new LinkManager();
- setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
+ DocServer.CacheNeedsUpdate && setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
setInterval(DocServer.UPDATE_SERVER_CACHE, 120000);
return doc;
}
@@ -912,10 +914,12 @@ export class CurrentUserUtils {
});
}
+ @observable public static ServerVersion: string = ';'
public static async loadCurrentUser() {
return rp.get(Utils.prepend("/getCurrentUser")).then(async response => {
if (response) {
- const result: { id: string, email: string, cacheDocumentIds: string, resolvedPorts: string } = JSON.parse(response);
+ const result: { version: string, userDocumentId: string, sharingDocumentId: string, linkDatabaseId: string, email: string, cacheDocumentIds: string, resolvedPorts: string } = JSON.parse(response);
+ runInAction(() => CurrentUserUtils.ServerVersion = result.version);
Doc.CurrentUserEmail = result.email;
resolvedPorts = result.resolvedPorts as any;
DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, result.email);
@@ -923,11 +927,9 @@ export class CurrentUserUtils {
{
const ids = result.cacheDocumentIds.split(";");
const batch = 30000;
- FieldLoader.active = true;
for (let i = 0; i < ids.length; i = Math.min(ids.length, i+batch)) {
await DocServer.GetRefFields(ids.slice(i, i+batch));
}
- FieldLoader.active = false;
}
return result;
} else {
@@ -936,27 +938,24 @@ export class CurrentUserUtils {
});
}
- public static async loadUserDocument(id: string) {
- await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => {
- const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids);
- if (userDocumentId) {
- return DocServer.GetRefField(userDocumentId).then(async field => {
- Docs.newAccount = !(field instanceof Doc);
- await Docs.Prototypes.initialize();
- const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
- this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
- if (Docs.newAccount) {
- if (Doc.CurrentUserEmail === "guest") {
- DashboardView.createNewDashboard(undefined, "guest dashboard");
- } else {
- userDoc.activePage = "home";
- }
- }
- return userDoc;
- });
- } else {
- throw new Error("There should be a user id! Why does Dash think there isn't one?");
+ public static async loadUserDocument(info:{
+ userDocumentId: string;
+ sharingDocumentId: string;
+ linkDatabaseId: string;
+ }) {
+ return DocServer.GetRefField(info.userDocumentId).then(async field => {
+ Docs.newAccount = !(field instanceof Doc);
+ await Docs.Prototypes.initialize();
+ const userDoc = Docs.newAccount ? new Doc(info.userDocumentId, true) : field as Doc;
+ this.updateUserDocument(Doc.SetUserDoc(userDoc), info.sharingDocumentId, info.linkDatabaseId);
+ if (Docs.newAccount) {
+ if (Doc.CurrentUserEmail === "guest") {
+ DashboardView.createNewDashboard(undefined, "guest dashboard");
+ } else {
+ userDoc.activePage = "home";
+ }
}
+ return userDoc;
});
}
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 8e4e0d8f3..42132c2d7 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -75,7 +75,7 @@ export class DocumentManager {
@action
public AddView = (view: DocumentView) => {
- //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
+ if (view.props.LayoutTemplateString?.includes(KeyValueBox.name)) return;
if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
const viewAnchorIndex = view.props.LayoutTemplateString.includes('link_anchor_2') ? 'link_anchor_2' : 'link_anchor_1';
const link = view.rootDoc;
@@ -251,6 +251,7 @@ export class DocumentManager {
options: DocFocusOptions, // options for how to navigate to target
finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done.
) => {
+ Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc);
const docContextPath = DocumentManager.GetContextPath(targetDoc, true);
if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false;
let rootContextView =
@@ -335,7 +336,7 @@ export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCe
if (dv && (!containingDoc || dv.props.docViewPath().lastElement()?.Document === containingDoc)) {
DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc));
} else {
- const container = DocCast(containingDoc ?? doc.embedContainer ?? doc);
+ const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc));
const showDoc = !Doc.IsSystem(container) ? container : doc;
options.toggleTarget = undefined;
DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 85101fcab..f4ff38515 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -15,7 +15,7 @@ import { SelectionManager } from './SelectionManager';
import { SnappingManager } from './SnappingManager';
import { UndoManager } from './UndoManager';
-export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
+export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
/**
* Initialize drag
@@ -213,6 +213,8 @@ export namespace DragManager {
? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
: docDragData.dropAction === 'embed'
? Doc.BestEmbedding(d)
+ : docDragData.dropAction === 'add'
+ ? d
: docDragData.dropAction === 'proto'
? Doc.GetProto(d)
: docDragData.dropAction === 'copy'
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index e406d89e7..f35844020 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -16,6 +16,7 @@ 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';
/**
* Interface for options for the react-select component
@@ -281,7 +282,7 @@ export class GroupManager extends React.Component<{}> {
*/
private get groupCreationModal() {
const contents = (
- <div className="group-create" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
+ <div className="group-create" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
<div className="group-heading" style={{ marginBottom: 0 }}>
<p>
<b>New Group</b>
@@ -366,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: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
+ <div className="group-interface" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.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 057f94f64..535d8ccc2 100644
--- a/src/client/util/GroupMemberView.tsx
+++ b/src/client/util/GroupMemberView.tsx
@@ -9,6 +9,7 @@ import { MainViewModal } from '../views/MainViewModal';
import { GroupManager, UserOptions } from './GroupManager';
import './GroupMemberView.scss';
import { Button, IconButton, Size, Type } from 'browndash-components';
+import { SettingsManager } from './SettingsManager';
interface GroupMemberViewProps {
group: Doc;
@@ -28,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: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
+ <div className="editing-interface" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
<div className="editing-header">
<input
className="group-title"
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index c7f092565..ef4b21b05 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,8 @@
-import { action, observable, observe } from 'mobx';
+import { action, observable, observe, runInAction } from 'mobx';
import { computedFn } from 'mobx-utils';
import { Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
import { DirectLinks } from '../../fields/DocSymbols';
+import { FieldLoader } from '../../fields/FieldLoader';
import { List } from '../../fields/List';
import { ProxyField } from '../../fields/Proxy';
import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types';
@@ -132,6 +133,7 @@ export class LinkManager {
},
true
);
+ runInAction(() => (FieldLoader.ServerLoadStatus.message = 'links'));
LinkManager.addLinkDB(Doc.LinkDBDoc());
}
diff --git a/src/client/util/PingManager.ts b/src/client/util/PingManager.ts
index 0c41a1ea7..4dd2fcd35 100644
--- a/src/client/util/PingManager.ts
+++ b/src/client/util/PingManager.ts
@@ -1,5 +1,6 @@
-import { action, observable } from 'mobx';
+import { action, observable, runInAction } from 'mobx';
import { Networking } from '../Network';
+import { CurrentUserUtils } from './CurrentUserUtils';
export class PingManager {
// create static instance and getter for global use
@observable static _instance: PingManager;
@@ -18,7 +19,8 @@ export class PingManager {
};
sendPing = async (): Promise<void> => {
try {
- await Networking.PostToServer('/ping', { date: new Date() });
+ const res = await Networking.PostToServer('/ping', { date: new Date() });
+ runInAction(() => (CurrentUserUtils.ServerVersion = res.message));
!this.IsBeating && this.setIsBeating(true);
} catch {
if (this.IsBeating) {
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index cbc465d6a..d99630f82 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -187,7 +187,6 @@ export class ReplayMovements {
} else {
// tab wasn't open - open it and play the movement
const openedColFFView = this.openTab(movement.doc);
- console.log('openedColFFView', openedColFFView);
openedColFFView && this.zoomAndPan(movement, openedColFFView);
}
diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx
index 6a6ec158e..3c7c35a7e 100644
--- a/src/client/util/ServerStats.tsx
+++ b/src/client/util/ServerStats.tsx
@@ -6,6 +6,7 @@ import './SharingManager.scss';
import { PingManager } from './PingManager';
import { StrCast } from '../../fields/Types';
import { Doc } from '../../fields/Doc';
+import { SettingsManager } from './SettingsManager';
@observer
export class ServerStats extends React.Component<{}> {
@@ -42,21 +43,22 @@ export class ServerStats extends React.Component<{}> {
*/
@computed get sharingInterface() {
return (
- <div style={{
- display: "flex",
- height: 300,
- width: 400,
- background: StrCast(Doc.UserDoc().userBackgroundColor),
- opacity: 0.6}}>
- <div style={{width: 300,height: 100,margin: "auto",display: "flex",flexDirection: "column"}}>
- {PingManager.Instance.IsBeating ? 'The server connection is active' :
- 'The server connection has been interrupted.NOTE: Any changes made will appear to persist but will be lost after a browser refreshes.'}
-
- <br/>
+ <div
+ style={{
+ display: 'flex',
+ height: 300,
+ width: 400,
+ background: SettingsManager.Instance?.userBackgroundColor,
+ opacity: 0.6,
+ }}>
+ <div style={{ width: 300, height: 100, margin: 'auto', display: 'flex', flexDirection: 'column' }}>
+ {PingManager.Instance.IsBeating ? 'The server connection is active' : 'The server connection has been interrupted.NOTE: Any changes made will appear to persist but will be lost after a browser refreshes.'}
+
+ <br />
<span>Active users:{this._stats?.socketMap.length}</span>
{this._stats?.socketMap.map((user: any) => (
<p>{user.username}</p>
- ))}
+ ))}
</div>
</div>
);
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index b8e327968..b2b5be070 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,10 +1,12 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { ColorState, SketchPicker } from 'react-color';
+import { BsGoogle } from 'react-icons/bs';
+import { FaFillDrip, FaPalette } from 'react-icons/fa';
import { Doc } from '../../fields/Doc';
-import { Id } from '../../fields/FieldSymbols';
+import { DashVersion } from '../../fields/DocSymbols';
import { BoolCast, Cast, StrCast } from '../../fields/Types';
import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
@@ -12,13 +14,9 @@ import { DocServer } from '../DocServer';
import { Networking } from '../Network';
import { MainViewModal } from '../views/MainViewModal';
import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
-import { DragManager } from './DragManager';
import { GroupManager } from './GroupManager';
import './SettingsManager.scss';
import { undoBatch } from './UndoManager';
-import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { BsGoogle } from 'react-icons/bs';
-import { FaFillDrip, FaPalette } from 'react-icons/fa';
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -452,6 +450,7 @@ export class SettingsManager extends React.Component<{}> {
</div>
<div className="settings-user">
+ <div style={{ color: 'black' }}>{DashVersion}</div>
<div className="settings-username" style={{ color: this.userBackgroundColor }}>
{Doc.CurrentUserEmail}
</div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index cadcb1f8a..6171c01d7 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -8,6 +8,7 @@ import Select from 'react-select';
import * as RequestPromise from 'request-promise';
import { Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
import { AclAdmin, AclPrivate, DocAcl, DocData } from '../../fields/DocSymbols';
+import { FieldLoader } from '../../fields/FieldLoader';
import { Id } from '../../fields/FieldSymbols';
import { StrCast } from '../../fields/Types';
import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
@@ -23,6 +24,7 @@ import { GroupManager, UserOptions } from './GroupManager';
import { GroupMemberView } from './GroupMemberView';
import { LinkManager } from './LinkManager';
import { SelectionManager } from './SelectionManager';
+import { SettingsManager } from './SettingsManager';
import './SharingManager.scss';
import { undoable } from './UndoManager';
@@ -136,6 +138,7 @@ export class SharingManager extends React.Component<{}> {
this.populating = true;
const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
const raw = (JSON.parse(userList) as User[]).filter(user => user.email !== 'guest' && user.email !== Doc.CurrentUserEmail);
+ runInAction(() => (FieldLoader.ServerLoadStatus.message = 'users'));
const docs = await DocServer.GetRefFields(raw.reduce((list, user) => [...list, user.sharingDocumentId, user.linkDatabaseId], [] as string[]));
raw.map(
action((newUser: User) => {
@@ -144,7 +147,7 @@ export class SharingManager extends React.Component<{}> {
if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
if (!this.users.find(user => user.user.email === newUser.email)) {
this.users.push({ user: newUser, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.userColor) });
- LinkManager.addLinkDB(linkDatabase);
+ //LinkManager.addLinkDB(linkDatabase);
}
}
})
@@ -525,10 +528,10 @@ export class SharingManager extends React.Component<{}> {
const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
return !permissions ? null : (
- <div key={groupKey} className={'container'} style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
+ <div key={groupKey} className={'container'} style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
<div className={'padding'}>{StrCast(group.title)}</div>
&nbsp;
- {group instanceof Doc ? <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={action(() => (GroupManager.Instance.currentGroup = group))} /> : null}
+ {group instanceof Doc ? <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={SettingsManager.Instance.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)}>
@@ -550,7 +553,7 @@ export class SharingManager extends React.Component<{}> {
<div
className="sharing-contents"
style={{
- background: StrCast(Doc.UserDoc().userBackgroundColor),
+ background: SettingsManager.Instance.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 e684bd637..7aad0f2b1 100644
--- a/src/client/util/reportManager/ReportManager.tsx
+++ b/src/client/util/reportManager/ReportManager.tsx
@@ -19,6 +19,7 @@ import { BugType, FileData, Priority, ReportForm, ViewState, bugDropdownItems, d
import { Filter, FormInput, FormTextArea, IssueCard, IssueView, Tag } from './ReportManagerComponents';
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;
@@ -213,11 +214,11 @@ export class ReportManager extends React.Component<{}> {
* @returns the component that dispays all issues
*/
private viewIssuesComponent = () => {
- const darkMode = isDarkMode(StrCast(Doc.UserDoc().userBackgroundColor));
+ const darkMode = isDarkMode(SettingsManager.Instance.userBackgroundColor);
const colors = darkMode ? darkColors : lightColors;
return (
- <div className="view-issues" style={{ backgroundColor: StrCast(Doc.UserDoc().userBackgroundColor), color: colors.text }}>
+ <div className="view-issues" style={{ backgroundColor: SettingsManager.Instance.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/DashboardView.tsx b/src/client/views/DashboardView.tsx
index d6c7b43d5..acf5b654d 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -108,15 +108,7 @@ export class DashboardView extends React.Component {
}}>
<div className="header">Create New Dashboard</div>
<EditableText formLabel="Title" placeholder={placeholder} type={Type.SEC} color={StrCast(Doc.UserDoc().userColor)} setVal={val => this.setNewDashboardName(val as string)} fillWidth />
- <ColorPicker
- formLabel="Background"
- colorPickerType="github"
- type={Type.TERT}
- selectedColor={this.newDashboardColor}
- setSelectedColor={color => {
- this.setNewDashboardColor(color);
- }}
- />
+ <ColorPicker formLabel="Background" colorPickerType="github" type={Type.TERT} selectedColor={this.newDashboardColor} setSelectedColor={this.setNewDashboardColor} />
<div className="button-bar">
<Button text="Cancel" color={StrCast(Doc.UserDoc().userColor)} onClick={this.abortCreateNewDashboard} />
<Button type={Type.TERT} text="Create" color={StrCast(Doc.UserDoc().userVariantColor)} onClick={() => this.createNewDashboard(this.newDashboardName!, this.newDashboardColor)} />
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 8fe5c2e01..e076e69ca 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -3,7 +3,7 @@ import { DateField } from '../../fields/DateField';
import { Doc, DocListCast, HierarchyMapping, Opt, ReverseHierarchyMap } from '../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocAcl, DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
-import { Cast, StrCast } from '../../fields/Types';
+import { Cast, DocCast, StrCast } from '../../fields/Types';
import { distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
import { returnFalse } from '../../Utils';
import { DocUtils } from '../documents/Documents';
@@ -149,9 +149,9 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
toRemove.forEach(doc => {
leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
+ Doc.RemoveDocFromList(Doc.GetProto(doc), 'proto_embeddings', doc);
doc.embedContainer = undefined;
if (recent) {
- Doc.RemoveDocFromList(recent, 'data', doc);
doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true);
}
});
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 956dac555..f3daf3ffa 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -617,8 +617,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}
let actualdW = Math.max(docwidth + dW * scale, 20);
let actualdH = Math.max(docheight + dH * scale, 20);
- let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth));
- let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight));
+ let dX = !dWin ? 0 : (scale * refCent[0] * -dWin) / refWidth;
+ let dY = !dHin ? 0 : (scale * refCent[1] * -dHin) / refHeight;
const preserveNativeDim = !doc._nativeHeightUnfrozen && !doc._nativeDimModifiable;
const fixedAspect = nwidth && nheight && (!doc._layout_fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
if (fixedAspect) {
@@ -630,6 +630,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
} else {
if (!doc._layout_fitWidth || preserveNativeDim) {
actualdH = (nheight / nwidth) * actualdW;
+ dYin && (dY = -dW * scale * (nheight / nwidth));
doc._height = actualdH;
} else if (!modifyNativeDim || dragBotRight) {
doc._height = actualdH;
@@ -646,6 +647,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
} else {
if (!doc._layout_fitWidth || preserveNativeDim) {
actualdW = (nwidth / nheight) * actualdH;
+ dXin && (dX = -dH * scale * (nwidth / nheight));
doc._width = actualdW;
} else if (!modifyNativeDim || dragBotRight) {
doc._width = actualdW;
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 4a986cb54..d60617020 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -232,7 +232,7 @@ export class EditableView extends React.Component<EditableProps> {
onChange: this.props.autosuggestProps.onChange,
}}
/>
- ) :
+ ) : (
<input
className="editableView-input"
ref={r => (this._inputref = r)}
@@ -248,6 +248,7 @@ export class EditableView extends React.Component<EditableProps> {
onClick={this.stopPropagation}
onPointerUp={this.stopPropagation}
/>
+ );
// ) : (
// <textarea
// className="editableView-input"
diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss
index b18b01325..421bce6d6 100644
--- a/src/client/views/FilterPanel.scss
+++ b/src/client/views/FilterPanel.scss
@@ -156,7 +156,7 @@
right: 0;
top: 0;
z-index: 1;
- // background-color: #9f9f9f;
+ // background-color: #9f9f9f;
.filterBox-tree {
z-index: 0;
@@ -188,50 +188,47 @@
margin-bottom: 10px;
margin-left: 5px;
overflow: auto;
-
-
}
}
+.filterBox-facetHeader {
+ display: flex;
+ align-items: center;
+ // float:right;
-
-.filterBox-facetHeader{
- display: flex;
- align-items: center;
- // float:right;
-
- .filterBox-facetHeader-collapse{
+ .filterBox-facetHeader-collapse {
// float: right;
// justify-items: right;
// align-items: flex-end;
margin-left: auto;
- // margin-right: 9px;
+ // margin-right: 9px;
float: right;
font-size: 16;
}
- .filterBox-facetHeader-remove{
+ .filterBox-facetHeader-remove {
// margin-left: auto;
float: right;
font-size: 16;
- font-weight:bold;
+ font-weight: bold;
}
-
-
-
}
-.filterbox-collpasedAndActive{
+.filterbox-collpasedAndActive {
// left:100px;
text-indent: 18px;
// background-color: pink;
font-size: 12px;
font-weight: bold;
-
}
// .sliderBox-outerDiv {
+// display: flex;
+// align-items: center;
+// }
+
+// .sliderBox-outerDiv {
// width: 30%;// width: calc(100% - 14px); // 14px accounts for handles that are at the max value of the slider that would extend outside the box
// height: 40; // height: 100%;
// border-radius: inherit;
@@ -251,8 +248,3 @@
// position: relative;
// }
// }
-
-
-
-
-
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 93f4cf818..f1eeb6fa7 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -2,6 +2,7 @@ import React = require('react');
import { action, computed, observable, ObservableMap } from 'mobx';
import { observer } from 'mobx-react';
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';
@@ -18,11 +19,6 @@ import { TooltipRail, Handle, Tick, Track } from './nodes/SliderBox-components';
import { DocumentOptions, FInfo } from '../documents/Documents';
import { string32 } from 'pdfjs-dist/types/src/shared/util';
-//slight bug when you don't click on background canvas before creating filter and the you click on the canvas
-
-//use to -- & determine amount of sigfinict digits -- make all sections blue evn when collapsed
-// transform switch to x and y not x coordinate and y coordinate
-
interface filterProps {
rootDoc: Doc;
}
@@ -224,8 +220,8 @@ export class FilterPanel extends React.Component<filterProps> {
this._collapseReturnKeys.splice(0);
Array.from(this.activeRenderedFacetInfos.keys()).map(renderInfo => {
- if (renderInfo.renderType === 'range' && renderInfo.facetHeader === facetHeader) {
- this._collapseReturnKeys.push(renderInfo.range);
+ if (renderInfo.renderType === 'range' && renderInfo.facetHeader === facetHeader && renderInfo.range) {
+ this._collapseReturnKeys.push(renderInfo.range.map(number => number.toFixed(2)));
}
});
@@ -395,49 +391,53 @@ export class FilterPanel extends React.Component<filterProps> {
console.log('this is info domain ' + domain[0] + ', ' + domain[1]);
return (
- <div className="sliderBox-outerDiv" style={{ width: '95%', height: 45 }}>
- <Slider
- mode={2}
- step={Math.min(1, 0.1 * (domain[1] - domain[0]))}
- domain={[domain[0], domain[1]]} // -1000, 1000
- rootStyle={{ position: 'relative', width: '100%' }}
- onChange={values => Doc.setDocRangeFilter(this.targetDoc, facetHeader, values)}
- values={renderInfoRange!}>
- <Rail>{railProps => <TooltipRail {...railProps} />}</Rail>
- <Handles>
- {({ handles, activeHandleID, getHandleProps }) => (
- <div className="slider-handles">
- {handles.map((handle, i) => {
- // const value = i === 0 ? defaultValues[0] : defaultValues[1];
- return (
- <div>
- <Handle key={handle.id} handle={handle} domain={domain} isActive={handle.id === activeHandleID} getHandleProps={getHandleProps} />
- </div>
- );
- })}
- </div>
- )}
- </Handles>
- <Tracks left={false} right={false}>
- {({ tracks, getTrackProps }) => (
- <div className="slider-tracks">
- {tracks.map(({ id, source, target }) => (
- <Track key={id} source={source} target={target} disabled={false} getTrackProps={getTrackProps} />
- ))}
- </div>
- )}
- </Tracks>
- <Ticks count={5}>
- {({ ticks }) => (
- <div className="slider-ticks">
- {ticks.map(tick => (
- <Tick key={tick.id} tick={tick} count={ticks.length} format={(val: number) => val.toString()} />
- ))}
- </div>
- )}
- </Ticks>
- </Slider>
- </div>
+ <>
+ <div className="sliderBox-outerDiv" style={{ width: '95%', height: 45 }}>
+ {/* <Checkbox color="primary" onChange={action(() => console.log('on change'))} /> */}
+
+ <Slider
+ mode={2}
+ step={Math.min(1, 0.1 * (domain[1] - domain[0]))}
+ domain={[domain[0], domain[1]]} // -1000, 1000
+ rootStyle={{ position: 'relative', width: '100%' }}
+ onChange={values => Doc.setDocRangeFilter(this.targetDoc, facetHeader, values)}
+ values={renderInfoRange!}>
+ <Rail>{railProps => <TooltipRail {...railProps} />}</Rail>
+ <Handles>
+ {({ handles, activeHandleID, getHandleProps }) => (
+ <div className="slider-handles">
+ {handles.map((handle, i) => {
+ // const value = i === 0 ? defaultValues[0] : defaultValues[1];
+ return (
+ <div>
+ <Handle key={handle.id} handle={handle} domain={domain} isActive={handle.id === activeHandleID} getHandleProps={getHandleProps} />
+ </div>
+ );
+ })}
+ </div>
+ )}
+ </Handles>
+ <Tracks left={false} right={false}>
+ {({ tracks, getTrackProps }) => (
+ <div className="slider-tracks">
+ {tracks.map(({ id, source, target }) => (
+ <Track key={id} source={source} target={target} disabled={false} getTrackProps={getTrackProps} />
+ ))}
+ </div>
+ )}
+ </Tracks>
+ <Ticks count={5}>
+ {({ ticks }) => (
+ <div className="slider-ticks">
+ {ticks.map(tick => (
+ <Tick key={tick.id} tick={tick} count={ticks.length} format={(val: number) => val.toString()} />
+ ))}
+ </div>
+ )}
+ </Ticks>
+ </Slider>
+ </div>
+ </>
);
}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index d0210d63b..93163c1a3 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -362,6 +362,15 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
_subContentView: DocComponentView | undefined;
setSubContentView = (doc: DocComponentView) => (this._subContentView = doc);
+ @computed get fillColor() {
+ const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask);
+ return isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FillColor) ?? 'transparent';
+ }
+ @computed get strokeColor() {
+ const { inkData } = this.inkScaledData();
+ const fillColor = this.fillColor;
+ return !InkingStroke.IsClosed(inkData) && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color);
+ }
render() {
TraceMobx();
const { inkData, inkStrokeWidth, inkLeft, inkTop, inkScaleX, inkScaleY, inkWidth, inkHeight } = this.inkScaledData();
@@ -371,8 +380,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
const markerScale = NumCast(this.layoutDoc.stroke_markerScale, 1);
const closed = InkingStroke.IsClosed(inkData);
const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask);
- const fillColor = isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FillColor) ?? 'transparent';
- const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color);
+ const fillColor = this.fillColor;
// bcz: Hack!! Not really sure why, but having fractional values for width/height of mask ink strokes causes the dragging clone (see DragManager) to be offset from where it should be.
if (isInkMask && (this.layoutDoc[Width]() !== Math.round(this.layoutDoc[Width]()) || this.layoutDoc[Height]() !== Math.round(this.layoutDoc[Height]()))) {
@@ -387,7 +395,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
inkData,
inkLeft,
inkTop,
- strokeColor,
+ this.strokeColor,
inkStrokeWidth,
inkStrokeWidth,
StrCast(this.layoutDoc.stroke_lineJoin),
@@ -417,7 +425,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
inkData,
inkLeft,
inkTop,
- mask && highlightColor === 'transparent' ? strokeColor : highlightColor,
+ mask && highlightColor === 'transparent' ? this.strokeColor : highlightColor,
inkStrokeWidth,
inkStrokeWidth + (fillColor ? (closed ? 2 : (highlightIndex ?? 0) + 2) : 2),
StrCast(this.layoutDoc.stroke_lineJoin),
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 286d39943..afb76b9ac 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -55,7 +55,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
if (this._savedState.panY !== undefined) this.LightboxDoc._freeform_panY = this._savedState.panY;
if (this._savedState.scrollTop !== undefined) this.LightboxDoc._layout_scrollTop = this._savedState.scrollTop;
if (this._savedState.scale !== undefined) this.LightboxDoc._freeform_scale = this._savedState.scale;
- this.LightboxDoc.layout_fieldKey = this._savedState.layout_fieldKey;
+ this.LightboxDoc.layout_fieldKey = this._savedState.layout_fieldKey ? this._savedState.layout_fieldKey : undefined;
}
if (!doc) {
this._childFilters && (this._childFilters.length = 0);
@@ -313,8 +313,10 @@ export class LightboxView extends React.Component<LightboxViewProps> {
className="lightboxView-tabBtn"
title="open in tab"
onClick={e => {
+ const lightdoc = LightboxView._docTarget || LightboxView._doc!;
e.stopPropagation();
- CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, OpenWhereMod.none);
+ Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', lightdoc);
+ CollectionDockingView.AddSplit(lightdoc, OpenWhereMod.none);
SelectionManager.DeselectAll();
LightboxView.SetLightboxDoc(undefined);
}}>
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 2fa42d091..6dd1d53ee 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -6,10 +6,7 @@ import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { AssignAllExtensions } from '../../extensions/General/Extensions';
import { FieldLoader } from '../../fields/FieldLoader';
-import { DocServer } from '../DocServer';
-import { Docs } from '../documents/Documents';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { LinkManager } from '../util/LinkManager'; // this must come before importing Docs and CurrentUserUtils
import { ReplayMovements } from '../util/ReplayMovements';
import { TrackMovements } from '../util/TrackMovements';
import { CollectionView } from './collections/CollectionView';
@@ -20,7 +17,7 @@ import './global/globalScripts';
dotenv.config();
AssignAllExtensions();
-FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure why this is needed to get the code loaded properly...
+FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' }; // bcz: not sure why this is needed to get the code loaded properly...
(async () => {
MainView.Live = window.location.search.includes('live');
@@ -29,7 +26,11 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure
window.location.search.includes('safe') && CollectionView.SetSafeMode(true);
const info = await CurrentUserUtils.loadCurrentUser();
// if (info.email === 'guest') DocServer.Control.makeReadOnly();
- await CurrentUserUtils.loadUserDocument(info.id);
+ if (!info.userDocumentId) {
+ alert('Fatal Error: user not found in database');
+ return;
+ }
+ await CurrentUserUtils.loadUserDocument(info);
setTimeout(() => {
document.getElementById('root')!.addEventListener(
'wheel',
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index d9136dbd4..10ea08be9 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -4,7 +4,7 @@ import * as far from '@fortawesome/free-regular-svg-icons';
import * as fa from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import 'browndash-components/dist/styles/global.min.css';
-import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
+import { action, computed, configure, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import 'normalize.css';
import * as React from 'react';
@@ -182,14 +182,6 @@ export class MainView extends React.Component {
'currentFrame',
]); // can play with these fields on someone else's
}
- DocServer.GetRefField('rtfProto').then(
- proto =>
- proto instanceof Doc &&
- reaction(
- () => StrCast(proto.BROADCAST_MESSAGE),
- msg => msg && alert(msg)
- )
- );
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
@@ -527,7 +519,7 @@ export class MainView extends React.Component {
});
initEventListeners = () => {
- window.addEventListener('beforeunload', () => DocServer.UPDATE_SERVER_CACHE());
+ 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));
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index b74eabcc3..8cae34d7d 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -375,7 +375,6 @@ export class PropertiesButtons extends React.Component<{}, {}> {
val: value[1],
};
});
- console.log('click val: ', this.onClickVal);
return !this.selectedDoc ? null : (
<Dropdown
tooltip={'Choose onClick behavior'}
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 395aa2b61..d157e7b1c 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -38,7 +38,6 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
}, new Set<Doc>())
.keys()
);
- console.log('embeddings ' + embeddings.length);
return doclayouts
.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.props.Document))
diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx
index ec8043ffe..b72e048df 100644
--- a/src/client/views/PropertiesSection.tsx
+++ b/src/client/views/PropertiesSection.tsx
@@ -1,19 +1,19 @@
import React = require('react');
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
-import { action, computed, observable } from "mobx"
-import { observer } from "mobx-react"
-import './PropertiesSection.scss'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import './PropertiesSection.scss';
import { Doc } from '../../fields/Doc';
import { StrCast } from '../../fields/Types';
export interface PropertiesSectionProps {
- title: string,
- content?: JSX.Element | string | null,
- isOpen: boolean,
- setIsOpen: (bool: boolean) => any
- inSection?: boolean,
- setInSection?: (bool: boolean) => any
- onDoubleClick?: () => void
+ title: string;
+ content?: JSX.Element | string | null;
+ isOpen: boolean;
+ setIsOpen: (bool: boolean) => any;
+ inSection?: boolean;
+ setInSection?: (bool: boolean) => any;
+ onDoubleClick?: () => void;
}
@observer
@@ -33,34 +33,33 @@ export class PropertiesSection extends React.Component<PropertiesSectionProps> {
@observable isDouble: boolean = false;
render() {
- console.log(this.props.title, this.props.content)
- if (this.props.content === undefined || this.props.content === null) return null
- else return <div className="propertiesView-section" onPointerEnter={action(() => (this.props.setInSection && this.props.setInSection(true)))} onPointerLeave={action(() => (this.props.setInSection && this.props.setInSection(false)))}>
- <div className="propertiesView-sectionTitle"
- onDoubleClick={action((e) => {
- this.isDouble = true;
- this.props.onDoubleClick && this.props.onDoubleClick()
- console.log("open options")
- this.props.setIsOpen(true)
- setTimeout(() => this.isDouble = false, 300)
- })}
- onClick={action((e) => {
- this.props.setIsOpen(!this.props.isOpen)
- })}
- style={{
- background: this.props.isOpen ? this.variantColor : this.backgroundColor,
- color: this.color
- }}>
- {this.props.title}
- <div className="propertiesView-sectionTitle-icon">
- <FontAwesomeIcon icon={this.props.isOpen ? 'caret-down' : 'caret-right'} size="lg" />
- </div>
- </div>
- {!this.props.isOpen ? null :
- <div className="propertiesView-content">
- {this.props.content}
- </div>
- }
- </div>
+ if (this.props.content === undefined || this.props.content === null) return null;
+ else
+ return (
+ <div className="propertiesView-section" onPointerEnter={action(() => this.props.setInSection && this.props.setInSection(true))} onPointerLeave={action(() => this.props.setInSection && this.props.setInSection(false))}>
+ <div
+ className="propertiesView-sectionTitle"
+ onDoubleClick={action(e => {
+ this.isDouble = true;
+ this.props.onDoubleClick && this.props.onDoubleClick();
+ this.props.setIsOpen(true);
+ setTimeout(() => (this.isDouble = false), 300);
+ })}
+ onClick={action(e => {
+ this.props.setIsOpen(!this.props.isOpen);
+ })}
+ style={{
+ background: this.variantColor,
+ // this.props.isOpen ? this.variantColor : this.backgroundColor,
+ color: this.color,
+ }}>
+ {this.props.title}
+ <div className="propertiesView-sectionTitle-icon">
+ <FontAwesomeIcon icon={this.props.isOpen ? 'caret-down' : 'caret-right'} size="lg" />
+ </div>
+ </div>
+ {!this.props.isOpen ? null : <div className="propertiesView-content">{this.props.content}</div>}
+ </div>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 35ca5dea4..a54c3771f 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -8,7 +8,7 @@ import { concat } from 'lodash';
import { Lambda, action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { ColorState, SketchPicker } from 'react-color';
-import * as Icons from "react-icons/bs"; //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
+import * as Icons from 'react-icons/bs'; //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
import { GrCircleInformation } from 'react-icons/gr';
import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
import { Doc, DocListCast, Field, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
@@ -51,7 +51,6 @@ interface PropertiesViewProps {
addDocTab: (doc: Doc, where: OpenWhere) => boolean;
}
-
@observer
export class PropertiesView extends React.Component<PropertiesViewProps> {
private _widthUndo?: UndoManager.Batch;
@@ -191,7 +190,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
});
rows.push(
- <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px', backgroundColor: "white", textAlign: "center" }}>
+ <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px', backgroundColor: 'white', textAlign: 'center' }}>
<EditableView key="editableView" oneLine contents={'add key:value or #tags'} height={13} fontSize={10} GetValue={() => ''} SetValue={this.setKeyValue} />
</div>
);
@@ -249,14 +248,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return !this.selectedDoc ? null : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle={true} addDocTab={this.props.addDocTab} />;
}
- @computed get contextCount(){
- console.log("in context count");
- if (this.selectedDocumentView){
- const target = (this.selectedDocumentView.props.Document)
- const embeddings = DocListCast(target.proto_embeddings)
- console.log(embeddings.length -1 );
- return (embeddings.length - 1)
- } else{
+ @computed get contextCount() {
+ if (this.selectedDocumentView) {
+ const target = this.selectedDocumentView.props.Document;
+ const embeddings = DocListCast(target.proto_embeddings);
+ return embeddings.length - 1;
+ } else {
return 0;
}
}
@@ -266,13 +263,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return !selAnchor ? null : <PropertiesDocBacklinksSelector Document={selAnchor} hideTitle={true} addDocTab={this.props.addDocTab} />;
}
- @computed get linkCount(){
- const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
+ @computed get linkCount() {
+ const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
var counter = 0;
- LinkManager.Links(selAnchor).forEach((l, i) =>
- counter ++
- );
+ LinkManager.Links(selAnchor).forEach((l, i) => counter++);
return counter;
}
@@ -356,7 +351,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return (
<Tooltip title={<div className="dash-tooltip">Notify with message</div>}>
<div className="notify-button">
- <FontAwesomeIcon className="notify-button-icon" icon="bell" size="sm" />
+ <FontAwesomeIcon className="notify-button-icon" icon="bell" size="sm" />
</div>
</Tooltip>
);
@@ -367,16 +362,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
*/
@computed get expansionIcon() {
return (
- <div className="expansion-button" >
- <IconButton
- icon={<FontAwesomeIcon icon={'ellipsis-h'} />}
- size={Size.XSMALL}
- color={StrCast(Doc.UserDoc().userColor)}
+ <div className="expansion-button">
+ <IconButton
+ icon={<FontAwesomeIcon icon={'ellipsis-h'} />}
+ size={Size.XSMALL}
+ color={StrCast(Doc.UserDoc().userColor)}
onClick={action(() => {
if (this.selectedDocumentView || this.selectedDoc) {
SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc);
}
- })}
+ })}
/>
</div>
);
@@ -418,7 +413,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div>
<div className={'propertiesView-shareDropDown'}>
<div className={`propertiesView-shareDropDown${permission}`}>
- <div >{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div>
+ <div>{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div>
</div>
</div>
</div>
@@ -450,7 +445,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const target = docs[0];
const showAdmin = GetEffectiveAcl(target) == AclAdmin;
- console.log(GetEffectiveAcl(target), Doc.GetProto(target)[`acl-${normalizeEmail(Doc.CurrentUserEmail)}`])
const individualTableEntries = [];
const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
@@ -522,7 +516,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div>
<br></br> Individuals with Access to this Document
</div>
- <div className="propertiesView-sharingTable" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+ <div className="propertiesView-sharingTable" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
{<div> {individualTableEntries}</div>}
</div>
{groupTableEntries.length > 0 ? (
@@ -530,7 +524,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div>
<br></br> Groups with Access to this Document
</div>
- <div className="propertiesView-sharingTable" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+ <div className="propertiesView-sharingTable" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
{<div> {groupTableEntries}</div>}
</div>
</div>
@@ -562,69 +556,46 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
@computed get editableTitle() {
-
const titles = new Set<string>();
const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title);
SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
- return (
- <EditableText
- val={title}
- setVal={this.setTitle}
- color={this.color}
- type={Type.SEC}
- formLabel={"Title"}
- fillWidth
- />
- );
+ return <EditableText val={title} setVal={this.setTitle} color={this.color} type={Type.SEC} formLabel={'Title'} fillWidth />;
}
@computed get currentType() {
// console.log("current type " + this.selectedDoc?.type)
-
- const documentType = StrCast(this.selectedDoc?.type)
- var currentType: string = documentType;
- var capitalizedDocType = Utils.cleanDocumentType(currentType as DocumentType);
-
- return (
- <div>
- Type
- {/* <div className = "propertiesView-wordType">Type</div> */}
- <div className= "currentType">
- <div className='currentType-icon'>
- {this.currentComponent}
- </div>
-
- {capitalizedDocType}
-
- </div>
-
- </div>
-
- )
- }
- @computed get currentComponent() {
+ const documentType = StrCast(this.selectedDoc?.type);
+ var currentType: string = documentType;
+ var capitalizedDocType = Utils.cleanDocumentType(currentType as DocumentType);
- var iconName = StrCast(this.selectedDoc?.systemIcon)
+ return (
+ <div>
+ Type
+ {/* <div className = "propertiesView-wordType">Type</div> */}
+ <div className="currentType">
+ <div className="currentType-icon">{this.currentComponent}</div>
- // if (this.selectedDoc?.type === DocumentType.COL){
- // console.log("i did it!")
- // }
-
+ {capitalizedDocType}
+ </div>
+ </div>
+ );
+ }
- if (iconName){
+ @computed get currentComponent() {
+ var iconName = StrCast(this.selectedDoc?.systemIcon);
+
+ if (iconName) {
const Icon = Icons[iconName as keyof typeof Icons];
return <Icon />;
- } else{
- return <Icons.BsFillCollectionFill/>
-
+ } else {
+ return <Icons.BsFillCollectionFill />;
}
}
@undoBatch
@action
setTitle = (value: string | number) => {
- console.log(value)
if (SelectionManager.Views().length > 1) {
SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, 'title', value, true));
} else if (this.dataDoc) {
@@ -688,7 +659,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
className="inking-button-points"
style={{ backgroundColor: InkStrokeProperties.Instance._controlButton ? 'black' : '' }}
onPointerDown={action(() => (InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton))}>
- <FontAwesomeIcon icon="bezier-curve" size="lg" />
+ <FontAwesomeIcon icon="bezier-curve" size="lg" />
</div>
</Tooltip>
</div>
@@ -707,10 +678,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} onKeyPress={e => e.stopPropagation()} />
<div className="inputBox-button">
<div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
- <FontAwesomeIcon icon="caret-up" size="sm" />
+ <FontAwesomeIcon icon="caret-up" size="sm" />
</div>
<div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
- <FontAwesomeIcon icon="caret-down" size="sm" />
+ <FontAwesomeIcon icon="caret-down" size="sm" />
</div>
</div>
</div>
@@ -964,10 +935,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} />
<div className="inputBox-button">
<div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
- <FontAwesomeIcon icon="caret-up" size="sm" />
+ <FontAwesomeIcon icon="caret-up" size="sm" />
</div>
<div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
- <FontAwesomeIcon icon="caret-down" size="sm" />
+ <FontAwesomeIcon icon="caret-down" size="sm" />
</div>
</div>
</div>
@@ -984,7 +955,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
this.openSharing = false;
this.openLayout = false;
this.openFilters = false;
- }
+ };
@computed get widthAndDash() {
return (
@@ -1066,64 +1037,45 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: any) => {
- return <div>
- <NumberInput
- formLabel={label}
- formLabelPlacement={'left'}
- type={Type.SEC}
- unit={unit}
- fillWidth
- color={this.color}
- number={number}
- setNumber={setNumber}
- min={min}
- max={max}
- />
- <Slider
- multithumb={false}
- color={this.color}
- size={Size.XSMALL}
- min={min}
- max={max}
- unit={unit}
- number={number}
- setNumber={setNumber}
- fillWidth
- />
- </div>
- }
+ return (
+ <div>
+ <NumberInput formLabel={label} formLabelPlacement={'left'} type={Type.SEC} unit={unit} fillWidth color={this.color} number={number} setNumber={setNumber} min={min} max={max} />
+ <Slider multithumb={false} color={this.color} size={Size.XSMALL} min={min} max={max} unit={unit} number={number} setNumber={setNumber} fillWidth />
+ </div>
+ );
+ };
@computed get transformEditor() {
return (
<div className="transform-editor">
{this.isInk ? this.controlPointsButton : null}
{this.getNumber(
- "Height",
- " px",
+ 'Height',
+ ' px',
0,
1000,
Number(this.shapeHgt),
undoable((val: string) => !isNaN(Number(val)) && (this.shapeHgt = val), 'set height')
)}
{this.getNumber(
- "Width",
- " px",
+ 'Width',
+ ' px',
0,
1000,
Number(this.shapeWid),
undoable((val: string) => !isNaN(Number(val)) && (this.shapeWid = val), 'set width')
)}
{this.getNumber(
- "X Coordinate",
- " px",
+ 'X', //'X Coordinate',
+ ' px',
-2000,
2000,
Number(this.shapeXps),
undoable((val: string) => !isNaN(Number(val)) && (this.shapeXps = val), 'set x coord')
)}
{this.getNumber(
- "Y Coordinate",
- " px",
+ 'Y', //'Y Coordinate',
+ ' px',
-2000,
2000,
Number(this.shapeYps),
@@ -1134,38 +1086,44 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
@computed get optionsSubMenu() {
- return <PropertiesSection
- title="Options"
- content={<PropertiesButtons />}
- inSection={this.inOptions}
- isOpen={this.openOptions}
- setInSection={(bool) => this.inOptions = bool}
- setIsOpen={(bool) => this.openOptions = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return (
+ <PropertiesSection
+ title="Options"
+ content={<PropertiesButtons />}
+ inSection={this.inOptions}
+ isOpen={this.openOptions}
+ setInSection={bool => (this.inOptions = bool)}
+ setIsOpen={bool => (this.openOptions = bool)}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ );
}
@computed get sharingSubMenu() {
- return <PropertiesSection
- title="Sharing & Permissions"
- content={<>
- {/* <div className="propertiesView-buttonContainer"> */}
- <div className="propertiesView-acls-checkbox">
- Layout Permissions
- <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
- </div>
- {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
+ return (
+ <PropertiesSection
+ title="Sharing & Permissions"
+ content={
+ <>
+ {/* <div className="propertiesView-buttonContainer"> */}
+ <div className="propertiesView-acls-checkbox">
+ Layout Permissions
+ <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
+ </div>
+ {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
<button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
<FontAwesomeIcon icon="redo-alt" size="1x" />
</button>
</Tooltip> */}
- {/* </div> */}
- {this.sharingTable}
- </>}
- isOpen={this.openSharing}
- setIsOpen={(bool) => this.openSharing = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ {/* </div> */}
+ {this.sharingTable}
+ </>
+ }
+ isOpen={this.openSharing}
+ setIsOpen={bool => (this.openSharing = bool)}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ );
}
/**
@@ -1193,15 +1151,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
};
@computed get filtersSubMenu() {
- return <PropertiesSection
- title="Filters"
- content={<div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}>
- <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
- </div>}
- isOpen={this.openFilters}
- setIsOpen={(bool) => this.openFilters = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return (
+ <PropertiesSection
+ title="Filters"
+ content={
+ <div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}>
+ <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
+ </div>
+ }
+ isOpen={this.openFilters}
+ setIsOpen={bool => (this.openFilters = bool)}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ );
}
@computed get inkSubMenu() {
@@ -1209,68 +1171,42 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return (
<>
- <PropertiesSection
- title="Appearance"
- content={this.isInk ? this.appearanceEditor : null}
- isOpen={this.openAppearance}
- setIsOpen={(bool) => this.openAppearance = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
- <PropertiesSection
- title="Transform"
- content={this.transformEditor}
- isOpen={this.openTransform}
- setIsOpen={(bool) => this.openTransform = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ <PropertiesSection title="Appearance" content={this.isInk ? this.appearanceEditor : null} isOpen={this.openAppearance} setIsOpen={bool => (this.openAppearance = bool)} onDoubleClick={() => this.onDoubleClick()} />
+ <PropertiesSection title="Transform" content={this.transformEditor} isOpen={this.openTransform} setIsOpen={bool => (this.openTransform = bool)} onDoubleClick={() => this.onDoubleClick()} />
</>
);
}
@computed get fieldsSubMenu() {
- return <PropertiesSection
- title="Fields & Tags"
- content={<div className="propertiesView-content fields">{
- Doc.noviceMode ? this.noviceFields : this.expandedField}
- </div>}
- isOpen={this.openFields}
- setIsOpen={(bool) => this.openFields = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return (
+ <PropertiesSection
+ title="Fields & Tags"
+ content={<div className="propertiesView-content fields">{Doc.noviceMode ? this.noviceFields : this.expandedField}</div>}
+ isOpen={this.openFields}
+ setIsOpen={bool => (this.openFields = bool)}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ );
}
@computed get contextsSubMenu() {
- return <PropertiesSection
- title="Other Contexts"
- content={this.contextCount > 0 ? this.contexts : "There are no other contexts."}
- isOpen={this.openContexts}
- setIsOpen={(bool) => this.openContexts = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return (
+ <PropertiesSection
+ title="Other Contexts"
+ content={this.contextCount > 0 ? this.contexts : 'There are no other contexts.'}
+ isOpen={this.openContexts}
+ setIsOpen={bool => (this.openContexts = bool)}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ );
}
-
-
-
-
@computed get linksSubMenu() {
- return <PropertiesSection
- title="Linked To"
- content={this.linkCount > 0 ? this.links : "There are no current links." }
- isOpen={this.openLinks}
- setIsOpen={(bool) => this.openLinks = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return <PropertiesSection title="Linked To" content={this.linkCount > 0 ? this.links : 'There are no current links.'} isOpen={this.openLinks} setIsOpen={bool => (this.openLinks = bool)} onDoubleClick={() => this.onDoubleClick()} />;
}
@computed get layoutSubMenu() {
- return <PropertiesSection
- title="Layout"
- content={this.layoutPreview}
- isOpen={this.openLayout}
- setIsOpen={(bool) => this.openLayout = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return <PropertiesSection title="Layout" content={this.layoutPreview} isOpen={this.openLayout} setIsOpen={bool => (this.openLayout = bool)} onDoubleClick={() => this.onDoubleClick()} />;
}
@computed get description() {
@@ -1471,224 +1407,226 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
if (scale > 1) scale = 1;
this.sourceAnchor && (this.sourceAnchor.followLinkZoomScale = scale);
};
-
+
@computed get linkProperties() {
const zoom = Number((NumCast(this.sourceAnchor?.followLinkZoomScale, 1) * 100).toPrecision(3));
const targZoom = this.sourceAnchor?.followLinkZoom;
const indent = 30;
const hasSelectedAnchor = LinkManager.Links(this.sourceAnchor).includes(LinkManager.currentLink!);
- return <>
- <div className="propertiesView-section" style={{ background: 'darkgray' }}>
- <div className="propertiesView-input first" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
- <p>Relationship</p>
- {this.editRelationship}
- </div>
- <div className="propertiesView-input" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
- <p>Description</p>
- {this.editDescription}
- </div>
- <div className="propertiesView-input inline">
- <p>Show link</p>
- <button
- style={{ background: !LinkManager.currentLink?.link_displayLine ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleLinkProp(e, 'link_displayLine')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
- <p>Auto-move anchors</p>
- <button
- style={{ background: !LinkManager.currentLink?.link_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleLinkProp(e, 'link_autoMoveAnchors')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
- <p>Display arrow</p>
- <button
- style={{ background: !LinkManager.currentLink?.link_displayArrow ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleLinkProp(e, 'link_displayArrow')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
- </button>
- </div>
- </div>
- {!hasSelectedAnchor ? null : (
- <div className="propertiesView-section">
- <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
- <p>Follow by</p>
- <select onChange={e => this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)} value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}>
- <option value={undefined}>Default</option>
- <option value={OpenWhere.addLeft}>Opening in new left pane</option>
- <option value={OpenWhere.addRight}>Opening in new right pane</option>
- <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
- <option value={OpenWhere.replaceRight}>Replacing right tab</option>
- <option value={OpenWhere.lightbox}>Opening in lightbox</option>
- <option value={OpenWhere.add}>Opening in new tab</option>
- <option value={OpenWhere.replace}>Replacing current tab</option>
- <option value={OpenWhere.inParent}>Opening in same collection</option>
- {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
- </select>
- </div>
- <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
- <p>Animation</p>
- <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
- <option value="default">Default</option>
- {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
- <option key={effect.toString()} value={effect.toString()}>
- {effect.toString()}
- </option>
- ))}
- </select>
- <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
- {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
- {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
- {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
- {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
- {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+ return (
+ <>
+ <div className="propertiesView-section" style={{ background: 'darkgray' }}>
+ <div className="propertiesView-input first" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
+ <p>Relationship</p>
+ {this.editRelationship}
+ </div>
+ <div className="propertiesView-input" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
+ <p>Description</p>
+ {this.editDescription}
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Show link</p>
+ <button
+ style={{ background: !LinkManager.currentLink?.link_displayLine ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleLinkProp(e, 'link_displayLine')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
+ <p>Auto-move anchors</p>
+ <button
+ style={{ background: !LinkManager.currentLink?.link_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleLinkProp(e, 'link_autoMoveAnchors')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
+ <p>Display arrow</p>
+ <button
+ style={{ background: !LinkManager.currentLink?.link_displayArrow ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleLinkProp(e, 'link_displayArrow')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+ </button>
</div>
</div>
- {PresBox.inputter(
- '0.1',
- '0.1',
- '10',
- NumCast(this.sourceAnchor?.followLinkTransitionTime) / 1000,
- true,
- (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.followLinkTransitionTime = timeInMS)),
- indent
- )}{' '}
- <div
- className={'slider-headers'}
- style={{
- display: 'grid',
- justifyContent: 'space-between',
- width: `calc(100% - ${indent * 2}px)`,
- marginLeft: indent,
- marginRight: indent,
- gridTemplateColumns: 'auto auto',
- borderTop: 'solid',
- }}>
- <div className="slider-text">Fast</div>
- <div className="slider-text">Slow</div>
- </div>{' '}
- <div className="propertiesView-input inline">
- <p>Play Target Audio</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Zoom Text Selections</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Toggle Follow to Outer Context</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Toggle Target (Show/Hide)</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToggle', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Ease Transitions</p>
- <button
- style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkEase', this.sourceAnchor, 'ease', 'linear')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Capture Offset to Target</p>
- <button
- style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => {
- this.toggleAnchorProp(e, 'followLinkXoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.x) - NumCast(this.sourceAnchor?.x), undefined);
- this.toggleAnchorProp(e, 'followLinkYoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.y) - NumCast(this.sourceAnchor?.y), undefined);
- }}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Center Target (no zoom)</p>
- <button
- style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
- <p>Zoom %</p>
- <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
- <input className="presBox-input" style={{ width: '100%' }} readOnly={true} type="number" value={zoom} />
- <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
- <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
- <FontAwesomeIcon icon={'caret-up'} />
+ {!hasSelectedAnchor ? null : (
+ <div className="propertiesView-section">
+ <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
+ <p>Follow by</p>
+ <select onChange={e => this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)} value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}>
+ <option value={undefined}>Default</option>
+ <option value={OpenWhere.addLeft}>Opening in new left pane</option>
+ <option value={OpenWhere.addRight}>Opening in new right pane</option>
+ <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
+ <option value={OpenWhere.replaceRight}>Replacing right tab</option>
+ <option value={OpenWhere.lightbox}>Opening in lightbox</option>
+ <option value={OpenWhere.add}>Opening in new tab</option>
+ <option value={OpenWhere.replace}>Replacing current tab</option>
+ <option value={OpenWhere.inParent}>Opening in same collection</option>
+ {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
+ </select>
+ </div>
+ <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
+ <p>Animation</p>
+ <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
+ <option value="default">Default</option>
+ {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
+ <option key={effect.toString()} value={effect.toString()}>
+ {effect.toString()}
+ </option>
+ ))}
+ </select>
+ <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
+ {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
+ {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
+ {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
+ {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
+ {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
</div>
- <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
- <FontAwesomeIcon icon={'caret-down'} />
+ </div>
+ {PresBox.inputter(
+ '0.1',
+ '0.1',
+ '10',
+ NumCast(this.sourceAnchor?.followLinkTransitionTime) / 1000,
+ true,
+ (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.followLinkTransitionTime = timeInMS)),
+ indent
+ )}{' '}
+ <div
+ className={'slider-headers'}
+ style={{
+ display: 'grid',
+ justifyContent: 'space-between',
+ width: `calc(100% - ${indent * 2}px)`,
+ marginLeft: indent,
+ marginRight: indent,
+ gridTemplateColumns: 'auto auto',
+ borderTop: 'solid',
+ }}>
+ <div className="slider-text">Fast</div>
+ <div className="slider-text">Slow</div>
+ </div>{' '}
+ <div className="propertiesView-input inline">
+ <p>Play Target Audio</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Zoom Text Selections</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Toggle Follow to Outer Context</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Toggle Target (Show/Hide)</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToggle', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Ease Transitions</p>
+ <button
+ style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkEase', this.sourceAnchor, 'ease', 'linear')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Capture Offset to Target</p>
+ <button
+ style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => {
+ this.toggleAnchorProp(e, 'followLinkXoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.x) - NumCast(this.sourceAnchor?.x), undefined);
+ this.toggleAnchorProp(e, 'followLinkYoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.y) - NumCast(this.sourceAnchor?.y), undefined);
+ }}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Center Target (no zoom)</p>
+ <button
+ style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
+ <p>Zoom %</p>
+ <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
+ <input className="presBox-input" style={{ width: '100%' }} readOnly={true} type="number" value={zoom} />
+ <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
+ <FontAwesomeIcon icon={'caret-up'} />
+ </div>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
+ <FontAwesomeIcon icon={'caret-down'} />
+ </div>
+ </div>
</div>
+ <button
+ style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+ </button>
</div>
+ {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
+ <div
+ className={'slider-headers'}
+ style={{
+ display: !targZoom ? 'none' : 'grid',
+ justifyContent: 'space-between',
+ width: `calc(100% - ${indent * 2}px)`,
+ marginLeft: indent,
+ marginRight: indent,
+ gridTemplateColumns: 'auto auto',
+ borderTop: 'solid',
+ }}>
+ <div className="slider-text">0%</div>
+ <div className="slider-text">100%</div>
+ </div>{' '}
</div>
- <button
- style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
- </button>
- </div>
- {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
- <div
- className={'slider-headers'}
- style={{
- display: !targZoom ? 'none' : 'grid',
- justifyContent: 'space-between',
- width: `calc(100% - ${indent * 2}px)`,
- marginLeft: indent,
- marginRight: indent,
- gridTemplateColumns: 'auto auto',
- borderTop: 'solid',
- }}>
- <div className="slider-text">0%</div>
- <div className="slider-text">100%</div>
- </div>{' '}
- </div>
- )}
- </>
+ )}
+ </>
+ );
}
/**
@@ -1724,23 +1662,20 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
width: this.props.width,
minWidth: this.props.width,
}}>
- <div className = "propertiesView-propAndInfoGrouping">
+ <div className="propertiesView-propAndInfoGrouping">
<div className="propertiesView-title" style={{ width: this.props.width }}>
Properties
</div>
- <div className = "propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation//properties/')}>
- <GrCircleInformation/> </div>
-
+ <div className="propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation//properties/')}>
+ <GrCircleInformation />{' '}
+ </div>
</div>
-
<div className="propertiesView-name">{this.editableTitle}</div>
- <div className = "propertiesView-type"> {this.currentType} </div>
+ <div className="propertiesView-type"> {this.currentType} </div>
{this.contextsSubMenu}
{this.linksSubMenu}
- {!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : (
- this.linkProperties
- )}
+ {!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : this.linkProperties}
{this.inkSubMenu}
{this.optionsSubMenu}
{this.fieldsSubMenu}
@@ -1761,7 +1696,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
Presentation
</div>
<div className="propertiesView-name" style={{ borderBottom: 0 }}>
-
{this.editableTitle}
<div className="propertiesView-presSelected">
<div className="propertiesView-selectedCount">{PresBox.Instance.selectedArray.size} selected</div>
@@ -1773,7 +1707,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresTransitions = !this.openPresTransitions))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Transitions
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" />
+ <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openPresTransitions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
@@ -1787,7 +1721,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Visibilty
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" />
+ <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openPresVisibilityAndDuration ? <div className="propertiesView-presTrails-content">{PresBox.Instance.visibiltyDurationDropdown}</div> : null}
@@ -1798,7 +1732,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresProgressivize = !this.openPresProgressivize))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Progressivize
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg" />
+ <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openPresProgressivize ? <div className="propertiesView-presTrails-content">{PresBox.Instance.progressivizeDropdown}</div> : null}
@@ -1809,7 +1743,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openSlideOptions = !this.openSlideOptions))} style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={type === DocumentType.AUDIO ? 'file-audio' : 'file-video'} /> &nbsp; {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" />
+ <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openSlideOptions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index bbbad3690..63ff348e3 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -22,7 +22,7 @@ import { 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 { BsArrowDown, BsArrowUp, BsArrowDownUp } from 'react-icons/bs';
import './StyleProvider.scss';
import React = require('react');
@@ -44,7 +44,6 @@ export enum StyleProp {
ShowCaption = 'layout_showCaption',
TitleHeight = 'titleHeight', // Height of Title area
ShowTitle = 'layout_showTitle', // whether to display a title on a Document (optional :hover suffix)
- JitterRotation = 'jitterRotation', // whether documents should be randomly rotated
BorderPath = 'customBorder', // border path for document view
FontSize = 'fontSize', // size of text font
FontFamily = 'fontFamily', // font family of text
@@ -98,7 +97,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
const layout_showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
- const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
// prettier-ignore
switch (property.split(':')[0]) {
case StyleProp.TreeViewIcon:
@@ -161,7 +159,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
''
);
case StyleProp.Color:
- if (MainView.Instance.LastButton === doc) return Doc.UserDoc().userBackgroundColor;
+ if (MainView.Instance.LastButton === doc) return SettingsManager.Instance.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));
@@ -188,8 +186,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
</div>
),
};
- case StyleProp.JitterRotation:
- return Doc.IsComicStyle(doc) ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
case StyleProp.HeaderMargin:
return ([CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._type_collection as any) ||
(doc?.type === DocumentType.RTF && !layout_showTitle()?.includes('noMargin')) ||
@@ -208,12 +204,10 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
case DocumentType.PRES: docColor = docColor || (darkScheme() ? 'transparent' : 'transparent'); break;
case DocumentType.FONTICON: docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break;
case DocumentType.RTF: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
- case DocumentType.FILTER: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : 'rgba(105, 105, 105, 0.432)'); break;
case DocumentType.INK: docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined; break;
case DocumentType.EQUATION: docColor = docColor || 'transparent'; break;
case DocumentType.LABEL: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
- case DocumentType.LINKANCHOR: docColor = isAnchor ? Colors.LIGHT_BLUE : 'transparent'; break;
case DocumentType.LINK: docColor = (isAnchor ? docColor : '') || 'transparent'; break;
case DocumentType.IMG:
case DocumentType.WEB:
@@ -224,7 +218,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)
- ? StrCast(Doc.UserDoc().userBackgroundColor)
+ ? SettingsManager.Instance.userBackgroundColor
: doc.annotationOn
? '#00000010' // faint interior for collections on PDFs, images, etc
: doc?._isGroup
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index caf04cc1b..a551e5332 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -7,6 +7,7 @@ import { StrCast } from '../../fields/Types';
import { Doc } from '../../fields/Doc';
import { Popup, Type, isDark } from 'browndash-components';
import { Colors } from './global/globalEnums';
+import { SettingsManager } from '../util/SettingsManager';
interface UndoStackProps {
width?: number;
@@ -18,35 +19,37 @@ export class UndoStack extends React.Component<UndoStackProps> {
@observable static HideInline: boolean;
@observable static Expand: boolean;
render() {
- const background = UndoManager.batchCounter.get() ? 'yellow' : StrCast(Doc.UserDoc().userBackgroundColor)
+ const background = UndoManager.batchCounter.get() ? 'yellow' : SettingsManager.Instance.userBackgroundColor;
return this.props.inline && UndoStack.HideInline ? null : (
<div className="undoStack-outerContainer">
- <Popup
+ <Popup
text={'Undo/Redo Stack'}
color={UndoManager.batchCounter.get() ? 'yellow' : StrCast(Doc.UserDoc().userVariantColor)}
placement={`top-start`}
type={Type.TERT}
popup={
- <div className="undoStack-commandsContainer" ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })}
- style={{
- background: background,
- color: isDark(background) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY
- }}>
- {UndoManager.undoStackNames.map((name, i) => (
- <div className="undoStack-resultContainer" key={i}>
- <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
- </div>
- ))}
- {Array.from(UndoManager.redoStackNames)
- .reverse()
- .map((name, i) => (
+ <div
+ className="undoStack-commandsContainer"
+ ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })}
+ style={{
+ background: background,
+ color: isDark(background) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY,
+ }}>
+ {UndoManager.undoStackNames.map((name, i) => (
<div className="undoStack-resultContainer" key={i}>
- <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
- {name.replace(/[^\.]*\./, '')}
- </div>
+ <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
</div>
- ))}
- </div>
+ ))}
+ {Array.from(UndoManager.redoStackNames)
+ .reverse()
+ .map((name, i) => (
+ <div className="undoStack-resultContainer" key={i}>
+ <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
+ {name.replace(/[^\.]*\./, '')}
+ </div>
+ </div>
+ ))}
+ </div>
}
/>
</div>
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 16982595d..0052c4196 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -164,7 +164,7 @@ export class CollectionDockingView extends CollectionSubView() {
public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) {
if (document?._type_collection === CollectionViewType.Docking && !keyValue) return DashboardView.openDashboard(document);
if (!CollectionDockingView.Instance) return false;
- const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document && !keyValue);
+ const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document && !tab.contentItem.config.props.keyValue && !keyValue);
if (tab) {
tab.header.parent.setActiveContentItem(tab.contentItem);
return true;
@@ -466,7 +466,10 @@ export class CollectionDockingView extends CollectionSubView() {
this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
if (tab.DashDoc && ![DocumentType.PRES].includes(tab.DashDoc?.type) && !tab.contentItem.config.props.keyValue) {
Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
- Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
+ // if you close a tab that is not embedded somewhere else (an embedded Doc can be opened simultaneously in a tab), then add the tab to recently closed
+ if (tab.DashDoc.embedContainer === this.rootDoc) tab.DashDoc.embedContainer = undefined;
+ if (!tab.DashDoc.embedContainer) Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
+ Doc.RemoveDocFromList(Doc.GetProto(tab.DashDoc), 'proto_embeddings', tab.DashDoc);
}
if (CollectionDockingView.Instance) {
const dview = CollectionDockingView.Instance.props.Document;
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 22beb19de..06522b85e 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -7,7 +7,6 @@ import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
-import { NumCast, StrCast } from '../../../fields/Types';
import { emptyFunction, numberRange, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 5135cfb57..f65e8698f 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: StrCast(Doc.UserDoc().userBackgroundColor),
+ background: SettingsManager.Instance.userBackgroundColor,
// borderColor: StrCast(Doc.UserDoc().userColor)
}}>
{this.contMenuButtons}
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 91be31289..bbd528e13 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -93,13 +93,14 @@ export class CollectionPileView extends CollectionSubView() {
this.layoutDoc._freeform_panY = -10;
this.props.Document._freeform_pileEngine = computePassLayout.name;
} else {
- const defaultSize = NumCast(this.rootDoc._starburstDiameter, 500);
+ const defaultSize = NumCast(this.rootDoc._starburstDiameter, 400);
this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - defaultSize / 2;
this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - defaultSize / 2;
this.layoutDoc._freeform_pileWidth = this.layoutDoc[Width]();
this.layoutDoc._freeform_pileHeight = this.layoutDoc[Height]();
this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0;
this.layoutDoc._width = this.layoutDoc._height = defaultSize;
+ this.layoutDoc.background;
this.props.Document._freeform_pileEngine = computeStarburstLayout.name;
}
});
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a5c276125..e4a0d6dad 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -598,7 +598,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
action((entries: any) => {
if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0);
- this.props.setHeight?.(this.headerMargin + height);
+ this.props.setHeight?.(2 * this.headerMargin + height); // bcz: added 2x for header to fix problem with scrollbars appearing in Tools panel
}
})
);
@@ -669,7 +669,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
if (menuDoc) {
const width: number = NumCast(menuDoc._width, 30);
const height: number = NumCast(menuDoc._height, 30);
- console.log(menuDoc.title, width, height);
return (
<div className="buttonMenu-docBtn" style={{ width: width, height: height }}>
<DocumentView
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 00137736d..ea3b5065f 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -150,6 +150,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
console.log('WHAAAT');
}
dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree ? 'same' : dragData.dropAction;
+ e.stopPropagation();
}
};
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 67b7b39dd..d787f5262 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -396,7 +396,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return NumCast(Cast(PresBox.Instance.activeItem.presentationTargetDoc, Doc, null)._currentFrame);
};
static Activate = (tabDoc: Doc) => {
- const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc);
+ const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc && !tab.contentItem.config.props.keyValue);
tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
return tab !== undefined;
};
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index fb23fc7f1..25a547066 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -127,7 +127,7 @@ export class TreeView extends React.Component<TreeViewProps> {
: this.props.treeView.fileSysMode
? this.doc.isFolder
? this.fieldKey
- : 'embeddings' // for displaying
+ : 'data' // file system folders display their contents (data). used to be they displayed their embeddings but now its a tree structure and not a flat list
: this.props.treeView.outlineMode || this.childDocs
? this.fieldKey
: Doc.noviceMode
@@ -903,6 +903,7 @@ export class TreeView extends React.Component<TreeViewProps> {
height={12}
sizeToContent={true}
fontSize={12}
+ isEditingCallback={action(e => (this._editTitle = e))}
GetValue={() => StrCast(this.doc.title)}
OnTab={undoBatch((shift?: boolean) => {
if (!shift) this.props.indentDocument?.(true);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b5e9994dd..e1455525e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1318,7 +1318,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
layout_showTitle={this.props.childlayout_showTitle}
dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
pointerEvents={this.pointerEvents}
- //rotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
//fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeViewFreezeChildDimensions)} // bcz: check this
/>
);
@@ -1358,6 +1357,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _lightboxDoc: Opt<Doc>;
getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData {
+ const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
const childDoc = params.pair.layout;
const childDocLayout = Doc.Layout(childDoc);
const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values
@@ -1368,11 +1368,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
layoutFrameNumber === undefined
? { _width: Cast(childDocLayout._width, 'number'), _height: Cast(childDocLayout._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this.props.childOpacity?.() }
: CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber);
+ // prettier-ignore
+ const rotation = Cast(_rotation,'number',
+ !this.layoutDoc._rotation_jitter ? null
+ : NumCast(this.layoutDoc._rotation_jitter) * random(-1, 1, NumCast(x), NumCast(y)) );
return {
x: Number.isNaN(NumCast(x)) ? 0 : NumCast(x),
y: Number.isNaN(NumCast(y)) ? 0 : NumCast(y),
z: Cast(z, 'number'),
- rotation: Cast(_rotation, 'number'),
+ rotation: rotation,
color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.getClusterColor(childDoc, this.props, StyleProp.BackgroundColor),
opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 090cf356c..7c53bfdbe 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -377,7 +377,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
: ((doc: Doc) => {
Doc.GetProto(doc).data = new List<Doc>(selected);
Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
- !this.props.isAnnotationOverlay && Doc.AddFileOrphan(Doc.GetProto(doc));
doc._freeform_panX = doc._freeform_panY = 0;
return doc;
})(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 79842047b..256377758 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -197,7 +197,6 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
});
export function checkInksToGroup() {
- // console.log("getting here to inks group");
if (Doc.ActiveTool === InkTool.Write) {
CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
// TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
diff --git a/src/client/views/newlightbox/ExploreView/ExploreView.tsx b/src/client/views/newlightbox/ExploreView/ExploreView.tsx
index 44a0bcd5f..a1d6375c4 100644
--- a/src/client/views/newlightbox/ExploreView/ExploreView.tsx
+++ b/src/client/views/newlightbox/ExploreView/ExploreView.tsx
@@ -12,14 +12,11 @@ export const ExploreView = (props: IExploreView) => {
<div className={`exploreView-container`}>
{recs &&
recs.map(rec => {
- console.log(rec.embedding, bounds);
const x_bound: number = Math.max(Math.abs(bounds.max_x), Math.abs(bounds.min_x));
const y_bound: number = Math.max(Math.abs(bounds.max_y), Math.abs(bounds.min_y));
- console.log(x_bound, y_bound);
if (rec.embedding) {
const x = (rec.embedding.x / x_bound) * 50;
const y = (rec.embedding.y / y_bound) * 50;
- console.log(x, y);
return (
<div className={`exploreView-doc`} onClick={() => {}} style={{ top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)` }}>
{rec.title}
diff --git a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
index b9d05c531..2c2f04b9f 100644
--- a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
+++ b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
@@ -22,7 +22,6 @@ export const Recommendation = (props: IRecommendation) => {
doc = docView.rootDoc;
}
} else if (data) {
- console.log(data, type);
switch (type) {
case 'YouTube':
console.log('create ', type, 'document');
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index aae759702..1b6fe5748 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -14,6 +14,8 @@ import { ActiveInkColor, ActiveInkWidth, SetActiveInkColor, SetActiveInkWidth }
import './ColorBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
import { RichTextMenu } from './formattedText/RichTextMenu';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { DashColor } from '../../../Utils';
@observer
export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -81,3 +83,10 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
}
}
+
+
+ScriptingGlobals.add(
+ function interpColors(c1:string, c2:string, weight=0.5) {
+ return DashColor(c1).mix(DashColor(c2),weight)
+ }
+) \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
index e1ff6f8eb..10bfb0c64 100644
--- a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
+++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
@@ -34,7 +34,6 @@ export const createLineGenerator = (xScale: d3.ScaleLinear<number, number, never
};
export const xAxisCreator = (g: d3.Selection<SVGGElement, unknown, null, undefined>, height: number, xScale: d3.ScaleLinear<number, number, never>) => {
- console.log('x axis creator being called');
g.attr('class', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickSize(15));
};
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 70d2f95ea..bb9f45bdd 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,5 +1,5 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
@@ -47,11 +47,11 @@ import { DocumentLinksButton } from './DocumentLinksButton';
import './DocumentView.scss';
import { FieldViewProps } from './FieldView';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
-import { KeyValueBox } from './KeyValueBox';
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 {
@@ -1110,7 +1110,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
const background = StrCast(
SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor,
- Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().headingColor) : 'rgba(0,0,0,0.4)'
+ Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().headingColor) : SettingsManager.Instance.userVariantColor
);
const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
const titleView = !showTitle ? null : (
diff --git a/src/client/views/nodes/EquationBox.scss b/src/client/views/nodes/EquationBox.scss
index 9714e1bd0..f5871db22 100644
--- a/src/client/views/nodes/EquationBox.scss
+++ b/src/client/views/nodes/EquationBox.scss
@@ -1,8 +1,9 @@
-@import "../global/globalCssVariables.scss";
+@import '../global/globalCssVariables.scss';
.equationBox-cont {
- transform-origin: top left;
+ transform-origin: center;
+ background-color: #e7e7e7;
> span {
- width: 100%;
+ width: 100%;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index ad3532502..1b2209224 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -267,20 +267,25 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
return ScriptCast(this.rootDoc.script);
}
+ colorBatch:UndoManager.Batch|undefined;
/**
* Color button
*/
@computed get colorButton() {
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
- const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
const curColor = this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent';
const tooltip: string = StrCast(this.rootDoc.toolTip);
return (
<ColorPicker
setSelectedColor={value => {
- const s = this.colorScript;
- s && undoable(() => s.script.run({ this: this.layoutDoc, self: this.rootDoc, value: value, _readOnly_: false }).result, `Set ${tooltip} to ${value}`)();
+ if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
+ this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: value, _readOnly_: false });
+ }}
+ setFinalColor={value => {
+ this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: value, _readOnly_: false });
+ this.colorBatch?.end();
+ this.colorBatch= undefined;
}}
selectedColor={curColor}
type={Type.PRIM}
@@ -378,38 +383,26 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
}
render() {
- // determine dash button metadata
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
- const tooltip: string = StrCast(this.rootDoc.toolTip);
- const onClickScript = ScriptCast(this.rootDoc.onClick);
- // TODO:glr Add label of button type
- let button: JSX.Element = this.defaultButton;
+ const tooltip = StrCast(this.rootDoc.toolTip);
+ const scriptFunc = () => ScriptCast(this.rootDoc.onClick)?.script.run({ this: this.layoutDoc, self: this.rootDoc, _readOnly_: false });
+ const btnProps = { tooltip, icon: this.Icon(color)!, label: this.label };
// prettier-ignore
switch (this.type) {
- case ButtonType.EditableText:
- button = this.editableText; break;
- case ButtonType.DropdownList:
- button = this.dropdownListButton; break;
- case ButtonType.ColorButton:
- button = this.colorButton; break;
case ButtonType.NumberDropdownButton:
case ButtonType.NumberInlineButton:
- case ButtonType.NumberSliderButton:
- button = this.numberDropdown; break;
- case ButtonType.DropdownButton:
- button = this.dropdownButton; break;
- case ButtonType.MultiToggleButton:
- button = this.multiToggleButton; break;
- case ButtonType.ToggleButton: button = this.toggleButton; break;
+ case ButtonType.NumberSliderButton: return this.numberDropdown;
+ case ButtonType.EditableText: return this.editableText;
+ case ButtonType.DropdownList: return this.dropdownListButton;
+ case ButtonType.ColorButton: return this.colorButton;
+ case ButtonType.DropdownButton: return this.dropdownButton;
+ case ButtonType.MultiToggleButton: return this.multiToggleButton;
+ case ButtonType.ToggleButton: return this.toggleButton;
case ButtonType.ClickButton:
- case ButtonType.ToolButton:
- button = <IconButton tooltip={tooltip} color={color} icon={this.Icon(color)!} label={this.label}/>; break;
- case ButtonType.TextButton:
- button = <Button tooltip={tooltip} icon={this.Icon(color)!} text={StrCast(this.rootDoc.buttonText)} label={this.label}/>; break;
- case ButtonType.MenuButton:
- button = <IconButton tooltip={tooltip} onPointerDown={() => onClickScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, _readOnly_: false })} tooltipPlacement='right' size={Size.LARGE} color={color} icon={this.Icon(color)!} label={this.label}/>; break;
+ case ButtonType.ToolButton: return <IconButton {...btnProps} size={Size.LARGE} color={color} />;
+ case ButtonType.TextButton: return <Button {...btnProps} text={StrCast(this.rootDoc.buttonText)}/>;
+ case ButtonType.MenuButton: return <IconButton {...btnProps} color={color} size={Size.LARGE} tooltipPlacement='right' onPointerDown={scriptFunc} />;
}
-
- return button;
+ return this.defaultButton;
}
}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 86191de63..d69009415 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 { Height, Width } from '../../../fields/DocSymbols';
+import { DirectLinks, 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';
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index de0b57fd7..4919ee94c 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -61,7 +61,6 @@ const script = document.createElement('script');
script.defer = true;
script.async = true;
script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
-console.log(script.src);
document.head.appendChild(script);
/**
diff --git a/src/client/views/nodes/ScreenshotBox.scss b/src/client/views/nodes/ScreenshotBox.scss
index 6fb5ea7b3..1e9b64a0b 100644
--- a/src/client/views/nodes/ScreenshotBox.scss
+++ b/src/client/views/nodes/ScreenshotBox.scss
@@ -1,6 +1,5 @@
.screenshotBox {
transform-origin: top left;
- background: white;
color: black;
// .screenshotBox-viewer {
// opacity: 0.99; // hack! overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger
@@ -12,46 +11,39 @@
#CANCAN {
canvas {
- width:100% !important;
+ width: 100% !important;
height: 100% !important;
}
}
-.screenshotBox-content, .screenshotBox-content-interactive, .screenshotBox-cont-fullScreen {
+.screenshotBox-content,
+.screenshotBox-content-interactive,
+.screenshotBox-cont-fullScreen {
width: 100%;
z-index: -1; // 0; // logically this should be 0 (or unset) which would give us transparent brush strokes over videos. However, this makes Chrome crawl to a halt
position: absolute;
}
-.screenshotBox-content, .screenshotBox-content-interactive, .screenshotBox-content-fullScreen {
- height: Auto;
+.screenshotBox-content,
+.screenshotBox-content-interactive,
+.screenshotBox-content-fullScreen {
+ height: Auto;
}
.screenshotBox-uiButtons {
- background:dimgray;
- border: orange solid 1px;
position: absolute;
right: 25;
top: 0;
- width:25;
+ width: 22;
height: 25;
- .screenshotBox-snapshot{
- color : white;
- top :0px;
- right : 5px;
- position: absolute;
- background-color:rgba(50, 50, 50, 0.2);
- transform-origin: left top;
- pointer-events:all;
- }
- .screenshotBox-recorder{
- color : white;
- top :0px;
+ .screenshotBox-recorder {
+ color: white;
+ top: 4px;
left: 5px;
position: absolute;
- background-color:rgba(50, 50, 50, 0.2);
+ background-color: rgba(50, 50, 50, 0.2);
transform-origin: left top;
- pointer-events:all;
+ pointer-events: all;
}
}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 312b3c619..271ff3cf8 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -25,6 +25,7 @@ import { FieldView, FieldViewProps } from './FieldView';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import './ScreenshotBox.scss';
import { VideoBox } from './VideoBox';
+import { SettingsManager } from '../../util/SettingsManager';
declare class MediaRecorder {
constructor(e: any, options?: any); // whatever MediaRecorder has
@@ -224,7 +225,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
const aud_chunks: any = [];
this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
this._audioRec.onstop = async (e: any) => {
- const [{ result }] = await Networking.UploadFilesToServer(aud_chunks.map((file: any) => ({file})));
+ const [{ result }] = await Networking.UploadFilesToServer(aud_chunks.map((file: any) => ({ file })));
if (!(result instanceof Error)) {
this.dataDoc[this.props.fieldKey + '-audio'] = new AudioField(result.accessPaths.agnostic.client);
}
@@ -235,9 +236,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
this._videoRec.onstart = () => (this.dataDoc[this.props.fieldKey + '-recordingStart'] = new DateField(new Date()));
this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
this._videoRec.onstop = async (e: any) => {
- console.log('screenshotbox: upload');
const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
- const [{ result }] = await Networking.UploadFilesToServer({file});
+ const [{ result }] = await Networking.UploadFilesToServer({ file });
this.dataDoc[this.fieldKey + '_duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
if (!(result instanceof Error)) {
// convert this screenshotBox into normal videoBox
@@ -313,7 +313,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
</>
</CollectionFreeFormView>
</div>
- <div style={{ background: 'white', position: 'relative', height: this.formattedPanelHeight() }}>
+ <div style={{ background: SettingsManager.Instance.userColor, position: 'relative', height: this.formattedPanelHeight() }}>
{!(this.dataDoc[this.fieldKey + '-dictation'] instanceof Doc) ? null : (
<FormattedTextBox
{...this.props}
@@ -335,8 +335,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
</div>
</div>
{!this.props.isSelected() ? null : (
- <div className="screenshotBox-uiButtons">
- <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording}>
+ <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}>
<FontAwesomeIcon icon="file" size="lg" />
</div>
</div>
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 3ad3c911d..7c8a1849e 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -610,7 +610,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
' ': {
dataProvider: (token: any) => this.handleToken(token),
component: (blob: any) => {
- console.log('Blob', blob);
return this.renderFuncListElement(blob.entity);
},
output: (item: any, trigger: any) => {
@@ -621,7 +620,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
'.': {
dataProvider: (token: any) => this.handleToken(token),
component: (blob: any) => {
- console.log('Blob', blob);
return this.renderFuncListElement(blob.entity);
},
output: (item: any, trigger: any) => {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 34a1229ba..f5df42161 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -125,11 +125,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._searchRef.current?.setRangeText(searchString);
});
}
- if (clear) {
- this._iframe?.contentWindow?.getSelection()?.empty();
- }
- if (searchString) {
- (this._iframe?.contentWindow as any)?.find(searchString, false, bwd, true);
+ try {
+ if (clear) {
+ this._iframe?.contentWindow?.getSelection()?.empty();
+ }
+ if (searchString) {
+ (this._iframe?.contentWindow as any)?.find(searchString, false, bwd, true);
+ }
+ } catch (e) {
+ console.log("WebBox search error", e)
}
return true;
};
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 24d2f0e13..1dcc445e8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -96,6 +96,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
private _editorView: Opt<EditorView>;
public _applyingChange: string = '';
+ private _finishingLink = false;
private _searchIndex = 0;
private _lastTimedMark: Mark | undefined = undefined;
private _cachedLinks: Doc[] = [];
@@ -245,7 +246,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (!pinProps && this._editorView?.state.selection.empty) return this.rootDoc;
const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.rootDoc.title), annotationOn: this.rootDoc });
this.addDocument(anchor);
+ this._finishingLink = true;
this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
+ this._finishingLink = false;
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true } }, this.rootDoc);
return anchor;
};
@@ -326,7 +329,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
if ((!prevData && !protoData) || newText || (!newText && !templateData)) {
// if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
- if (this.props.isContentActive() && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
+ if ((this._finishingLink || this.props.isContentActive()) && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
const numstring = NumCast(dataDoc[this.fieldKey], null);
dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : new RichTextField(newJson, newText);
dataDoc[this.fieldKey + '_noTemplate'] = true; // mark the data field as being split from the template if it has been edited
@@ -457,8 +460,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
alink =
alink ??
- (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)) ||
- DocUtils.MakeLink(this.props.Document, target, { link_relationship: LinkManager.AutoKeywords })!);
+ (LinkManager.Links(this.rootDoc).find(
+ link =>
+ Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && //
+ Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)
+ ) ||
+ DocUtils.MakeLink(this.rootDoc, target, { link_relationship: LinkManager.AutoKeywords })!);
newAutoLinks.add(alink);
const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
@@ -840,7 +847,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const appearance = cm.findByDescription('Appearance...');
const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
- appearanceItems.push({ description: 'Change Perspective...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
+ appearanceItems.push({ description: 'Change Style...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
// this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
!Doc.noviceMode &&
@@ -924,7 +931,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
let image_url = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
if (image_url) {
const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_url] });
- const source = Utils.prepend(result.accessPaths.agnostic.client);
+ const source = result.accessPaths.agnostic.client;
const newDoc = Docs.Create.ImageDocument(source, {
x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10,
y: NumCast(this.rootDoc.y),
@@ -1996,12 +2003,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
cycleAlternateText = () => {
if (this.layoutDoc._layout_enableAltContentUI) {
- const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
+ const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`];
this.rootDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
}
};
@computed get overlayAlternateIcon() {
- const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
+ const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`];
return (
<Tooltip
title={
@@ -2080,7 +2087,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
? {}
: {
transform: `scale(${scale})`,
- transformOrigin: 'top left',
width: `${100 / scale}%`,
height: `${100 / scale}%`,
}),
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index ac1e7ce5d..8bafc2cef 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -243,13 +243,13 @@ export class RichTextRules {
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
// [[<fieldKey> : <Doc>]]
- // [[:Doc]] => hyperlink
+ // [[:docTitle]] => hyperlink
// [[fieldKey]] => show field
// [[fieldKey=value]] => show field and also set its value
- // [[fieldKey:Doc]] => show field of doc
+ // [[fieldKey:docTitle]] => show field of doc
new InputRule(new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => {
const fieldKey = match[1];
- const docId = match[3]?.replace(':', '');
+ const docTitle = match[3]?.replace(':', '');
const value = match[2]?.substring(1);
const linkToDoc = (target: Doc) => {
const rstate = this.TextBox.EditorView?.state;
@@ -266,12 +266,12 @@ export class RichTextRules {
}
};
if (!fieldKey) {
- if (docId) {
- const target = DocServer.QUERY_SERVER_CACHE(docId);
- if (target) setTimeout(() => linkToDoc(target));
- else DocServer.GetRefField(docId).then(docx => linkToDoc((docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docId + '(auto)', _width: 500, _height: 500 }, docId)));
-
- return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
+ if (docTitle) {
+ const target = DocServer.FindDocByTitle(docTitle);
+ if (target) {
+ setTimeout(() => linkToDoc(target));
+ return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
+ }
}
return state.tr;
}
@@ -279,8 +279,12 @@ export class RichTextRules {
const num = value.match(/^[0-9.]$/);
this.Document[DocData][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
}
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId, hideKey: false });
- return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
+ const target = DocServer.FindDocByTitle(docTitle);
+ if (target) {
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target[Id], hideKey: false });
+ return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
+ }
+ return state.tr;
}),
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index b877cc36a..e35e011e2 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -17,6 +17,7 @@ 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';
@observer
export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -262,7 +263,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
colorPicker={this.highlightColor}
color={StrCast(Doc.UserDoc().userColor)}
/>
- <ColorPicker colorPickerType={'github'} selectedColor={this.highlightColor} setSelectedColor={color => this.changeHighlightColor(color)} size={Size.XSMALL} />
+ <ColorPicker selectedColor={this.highlightColor} setSelectedColor={this.changeHighlightColor} size={Size.XSMALL} />
</Group>
);
}
@@ -287,7 +288,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
canSummarize = (): boolean => {
const docs = SelectionManager.Docs();
if (docs.length > 0) {
- return docs.some(doc => doc.type === 'pdf' || doc.type === 'web');
+ return docs.some(doc => doc.type === DocumentType.PDF || doc.type === DocumentType.WEB);
}
return false;
};
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index e911bd283..1ceea697a 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -18,6 +18,7 @@ import './SearchBox.scss';
import { fetchRecommendations } from '../newlightbox/utils';
import { IRecommendation, Recommendation } from '../newlightbox/components';
import { Colors } from '../global/globalEnums';
+import { SettingsManager } from '../../util/SettingsManager';
const DAMPENING_FACTOR = 0.9;
const MAX_ITERATIONS = 25;
@@ -218,7 +219,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
}
@action
static staticSearchCollection(rootDoc: Opt<Doc>, query: string) {
- const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
+ const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.SEARCH, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
const blockedKeys = [
'x',
'y',
@@ -398,22 +399,21 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
if (query) {
this.searchCollection(query);
- const response = await fetchRecommendations('', query, [], true)
- const recs = response.recommendations
- const recommendations:IRecommendation[] = []
+ const response = await fetchRecommendations('', query, [], true);
+ const recs = response.recommendations;
+ const recommendations: IRecommendation[] = [];
for (const key in recs) {
const title = recs[key].title;
- console.log(title);
- const url = recs[key].url
- const type = recs[key].type
- const text = recs[key].text
- const transcript = recs[key].transcript
- const previewUrl = recs[key].previewUrl
- const embedding = recs[key].embedding
- const distance = recs[key].distance
- const source = recs[key].source
- const related_concepts = recs[key].related_concepts
- const docId = recs[key].doc_id
+ const url = recs[key].url;
+ const type = recs[key].type;
+ const text = recs[key].text;
+ const transcript = recs[key].transcript;
+ const previewUrl = recs[key].previewUrl;
+ const embedding = recs[key].embedding;
+ const distance = recs[key].distance;
+ const source = recs[key].source;
+ const related_concepts = recs[key].related_concepts;
+ const docId = recs[key].doc_id;
recommendations.push({
title: title,
data: url,
@@ -425,11 +425,11 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
distance: Math.round(distance * 100) / 100,
source: source,
related_concepts: related_concepts,
- docId: docId
- })
+ docId: docId,
+ });
}
- const setRecommendations = action(() => this._recommendations = recommendations)
- setRecommendations()
+ const setRecommendations = action(() => (this._recommendations = recommendations));
+ setRecommendations();
}
};
@@ -439,6 +439,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
*/
resetSearch = action(() => {
this._results.forEach((_, doc) => {
+ DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true);
Doc.UnBrushDoc(doc);
Doc.UnHighlightDoc(doc);
Doc.ClearSearchMatches();
@@ -461,7 +462,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
*/
@computed
public get selectOptions() {
- const selectValues = ['all', 'rtf', 'image', 'pdf', 'web', 'video', 'audio', 'collection'];
+ const selectValues = ['all', DocumentType.RTF, DocumentType.IMG, DocumentType.PDF, DocumentType.WEB, DocumentType.VID, DocumentType.AUDIO, DocumentType.COL];
return selectValues.map(value => (
<option key={value} value={value}>
@@ -517,19 +518,19 @@ 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">{result[1].join(', ')}</div>
+ <div className="searchBox-result-keys" style={{ color: SettingsManager.Instance.userVariantColor }}>
+ {result[1].join(', ')}
+ </div>
</div>
</Tooltip>
);
}
});
- const recommendationsJSX: JSX.Element[] = this._recommendations.map((props) => (
- <Recommendation {...props}/>
- ))
+ const recommendationsJSX: JSX.Element[] = this._recommendations.map(props => <Recommendation {...props} />);
return (
- <div className="searchBox-container" style={{pointerEvents: 'all', background: StrCast(Doc.UserDoc().userBackgroundColor)}}>
+ <div className="searchBox-container" style={{ pointerEvents: 'all', color: SettingsManager.Instance.userColor, background: SettingsManager.Instance.userBackgroundColor }}>
<div className="searchBox-bar">
{isLinkSearch ? null : (
<select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
@@ -552,20 +553,24 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
ref={this._inputRef}
/>
</div>
- {resultsJSX.length > 0 && <div className="searchBox-results-container">
- <div className="section-header" style={{background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)}}>
- <div className="section-title">Results</div>
- <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+ {resultsJSX.length > 0 && (
+ <div className="searchBox-results-container">
+ <div className="section-header" style={{ background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE) }}>
+ <div className="section-title">Results</div>
+ <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+ </div>
+ <div className="searchBox-results-view">{resultsJSX}</div>
</div>
- <div className="searchBox-results-view">{resultsJSX}</div>
- </div>}
- {recommendationsJSX.length > 0 && <div className="searchBox-recommendations-container">
- <div className="section-header" style={{background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)}}>
- <div className="section-title">Recommendations</div>
- <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+ )}
+ {recommendationsJSX.length > 0 && (
+ <div className="searchBox-recommendations-container">
+ <div className="section-header" style={{ background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE) }}>
+ <div className="section-title">Recommendations</div>
+ <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+ </div>
+ <div className="searchBox-recommendations-view">{recommendationsJSX}</div>
</div>
- <div className="searchBox-recommendations-view">{recommendationsJSX}</div>
- </div>}
+ )}
</div>
);
}
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index cb8eda9de..c194ede32 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -5,7 +5,7 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { FaBug, FaCamera, FaStamp } from 'react-icons/fa';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { AclAdmin } from '../../../fields/DocSymbols';
+import { AclAdmin, DashVersion } from '../../../fields/DocSymbols';
import { StrCast } from '../../../fields/Types';
import { GetEffectiveAcl } from '../../../fields/util';
import { DocumentManager } from '../../util/DocumentManager';
@@ -21,6 +21,7 @@ import { MainView } from '../MainView';
import { CollectionDockingView } from '../collections/CollectionDockingView';
import { Colors } from '../global/globalEnums';
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
@@ -35,10 +36,14 @@ export class TopBar extends React.Component {
});
};
- @computed get color() { return StrCast(Doc.UserDoc().userColor, Colors.LIGHT_GRAY); }
- @computed get variantColor() { return StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE); }
+ @computed get color() {
+ return StrCast(Doc.UserDoc().userColor, Colors.LIGHT_GRAY);
+ }
+ @computed get variantColor() {
+ return StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE);
+ }
@computed get backgroundColor() {
- return PingManager.Instance.IsBeating ? StrCast(Doc.UserDoc().userBackgroundColor, Colors.DARK_GRAY) : Colors.MEDIUM_GRAY;
+ return PingManager.Instance.IsBeating ? SettingsManager.Instance.userBackgroundColor : Colors.MEDIUM_GRAY;
}
@observable happyHeart: boolean = PingManager.Instance.IsBeating;
@@ -102,7 +107,7 @@ export class TopBar extends React.Component {
tooltip="Open Dashboards"
size={Size.SMALL}
color={this.color}
- style={{fontWeight: 700, fontSize: '1rem'}}
+ style={{ fontWeight: 700, fontSize: '1rem' }}
onClick={(e: React.MouseEvent) => {
const dashView = Doc.ActiveDashboard && DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard);
ContextMenu.Instance.addItem({ description: 'Open Dashboard View', event: this.navigateToHome, icon: 'edit' });
@@ -132,9 +137,10 @@ export class TopBar extends React.Component {
* and allows the user to access their account settings etc.
*/
@computed get topbarRight() {
+ const upToDate = DashVersion === CurrentUserUtils.ServerVersion;
return (
<div className="topbar-right">
- {Doc.ActiveDashboard ?
+ {Doc.ActiveDashboard ? (
<Button
text={GetEffectiveAcl(Doc.ActiveDashboard) === AclAdmin ? 'Share' : 'View Original'}
type={Type.TERT}
@@ -143,16 +149,16 @@ export class TopBar extends React.Component {
SharingManager.Instance.open(undefined, Doc.ActiveDashboard);
}}
/>
- : null }
- <IconButton tooltip={"Issue Reporter ⌘I"} size={Size.SMALL} color={this.color} onClick={ReportManager.Instance.open} icon={<FaBug />} />
- <IconButton tooltip={"Documentation ⌘D"} size={Size.SMALL} color={this.color} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} />
- <IconButton tooltip={"Settings ⌘⇧S"} size={Size.SMALL} color={this.color} onClick={SettingsManager.Instance.open} icon={<FontAwesomeIcon icon="cog" />} />
+ ) : null}
+ <IconButton tooltip={'Issue Reporter ⌘I'} size={Size.SMALL} color={this.color} onClick={ReportManager.Instance.open} icon={<FaBug />} />
+ <IconButton tooltip={'Documentation ⌘D'} size={Size.SMALL} color={this.color} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} />
+ <IconButton tooltip={'Settings ⌘⇧S'} size={Size.SMALL} color={this.color} onClick={SettingsManager.Instance.open} icon={<FontAwesomeIcon icon="cog" />} />
<IconButton
size={Size.SMALL}
onClick={ServerStats.Instance.open}
type={Type.TERT}
- tooltip={'Server is ' + (PingManager.Instance.IsBeating ? '' : 'NOT ') + 'running'}
- color={this.happyHeart ? Colors.LIGHT_BLUE : Colors.ERROR_RED}
+ tooltip={'Server is ' + (PingManager.Instance.IsBeating ? '' : 'NOT ') + 'running ' + (upToDate ? DashVersion : 'out of date version:' + DashVersion)}
+ color={this.happyHeart ? (upToDate ? Colors.LIGHT_BLUE : Colors.YELLOW) : Colors.ERROR_RED}
icon={<FontAwesomeIcon icon={this.happyHeart ? 'heart' : 'heart-broken'} />}
/>
{/* <Button text={'Logout'} borderRadius={5} hoverStyle={'gray'} backgroundColor={Colors.DARK_GRAY} color={this.color} fontSize={FontSize.SECONDARY} onClick={() => window.location.assign(Utils.prepend('/logout'))} /> */}
@@ -163,12 +169,14 @@ export class TopBar extends React.Component {
render() {
return (
//TODO:glr Add support for light / dark mode
- <div style={{
- pointerEvents: 'all',
- color: this.color,
- background: this.backgroundColor,
- // borderColor: this.color
- }} className="topbar-container">
+ <div
+ style={{
+ pointerEvents: 'all',
+ color: this.color,
+ background: this.backgroundColor,
+ // borderColor: this.color
+ }}
+ className="topbar-container">
<div className="topbar-inner-container">
{this.topbarLeft}
{this.topbarCenter}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 0bd838ed6..2d18e9506 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -238,22 +238,6 @@ export class Doc extends RefField {
public static get MyFilesystem() {
return DocCast(Doc.UserDoc().myFilesystem);
}
- public static get MyFileOrphans() {
- return DocCast(Doc.UserDoc().myFileOrphans);
- }
- public static AddFileOrphan(doc: Doc) {
- if (
- doc &&
- Doc.MyFileOrphans instanceof Doc &&
- Doc.IsDataProto(doc) &&
- !Doc.IsSystem(doc) &&
- ![DocumentType.CONFIG, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) &&
- !doc.isFolder &&
- !doc.annotationOn
- ) {
- Doc.AddDocToList(Doc.MyFileOrphans, undefined, doc);
- }
- }
public static get MyTools() {
return DocCast(Doc.UserDoc().myTools);
}
@@ -388,7 +372,7 @@ export class Doc extends RefField {
} else {
return Cast(layoutField, Doc, null);
}
- return Cast(self[renderFieldKey + '-layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
+ return Cast(self[renderFieldKey + '_layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
}
return undefined;
}
@@ -701,7 +685,8 @@ export namespace Doc {
}
export function BestEmbedding(doc: Doc) {
- const bestEmbedding = Doc.GetProto(doc) ? DocListCast(doc.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+ const bestEmbedding = Doc.GetProto(doc) ? [doc, ...DocListCast(doc.proto_embeddings)].find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+ bestEmbedding && Doc.AddDocToList(Doc.GetProto(doc), 'protoEmbeddings', doc);
return bestEmbedding ?? Doc.MakeEmbedding(doc);
}
@@ -782,7 +767,6 @@ export namespace Doc {
copy.cloneOf = doc;
cloneMap.set(doc[Id], copy);
- Doc.AddFileOrphan(copy);
return copy;
}
export function repairClone(clone: Doc, cloneMap: Map<string, Doc>, visited: Set<Doc>) {
@@ -917,7 +901,7 @@ export namespace Doc {
// If it doesn't find the expanded layout, then it makes a delegate of the template layout and
// saves it on the data doc indexed by the template layout's id.
//
- const expandedLayoutFieldKey = templateField + '-layout[' + templateLayoutDoc[Id] + ']';
+ const expandedLayoutFieldKey = templateField + '_layout[' + templateLayoutDoc[Id] + ']';
let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
@@ -985,6 +969,59 @@ export namespace Doc {
return overwrite;
}
+ export function FindReferences(infield: Doc | List<any>, references: Set<Doc>, system: boolean | undefined) {
+ if (infield instanceof List<any>) {
+ infield.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
+ return;
+ }
+ const doc = infield as Doc;
+ if (references.has(doc)) {
+ references.add(doc);
+ return;
+ }
+ const excludeLists = doc.title === 'My Recently Closed' || doc.title === 'My Header Bar' || doc.title === 'My Dashboards';
+ if (system !== undefined && ((system && !Doc.IsSystem(doc)) || (!system && Doc.IsSystem(doc)))) return;
+ references.add(doc);
+ Object.keys(doc).forEach(key => {
+ if (key === 'proto') {
+ if (doc.proto instanceof Doc) {
+ Doc.FindReferences(doc.proto, references, system);
+ }
+ } else {
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
+ if (field instanceof RefField) {
+ if (field instanceof Doc) {
+ if (key === 'myLinkDatabase') {
+ field instanceof Doc && references.add(field);
+ // skip docs that have been closed and are scheduled for garbage collection
+ } else {
+ Doc.FindReferences(field, references, system);
+ }
+ }
+ } else if (cfield instanceof ComputedField) {
+ } else if (field instanceof ObjectField) {
+ if (field instanceof Doc) {
+ Doc.FindReferences(field, references, system);
+ } else if (field instanceof List) {
+ !excludeLists && Doc.FindReferences(field, references, system);
+ } else if (field instanceof ProxyField) {
+ if (key === 'myLinkDatabase') {
+ field instanceof Doc && references.add(field);
+ // skip docs that have been closed and are scheduled for garbage collection
+ } else {
+ Doc.FindReferences(field.value, references, system);
+ }
+ } else if (field instanceof PrefetchProxy) {
+ Doc.FindReferences(field.value, references, system);
+ }
+ } else if (field instanceof Promise) {
+ debugger; //This shouldn't happend...
+ }
+ }
+ });
+ }
+
export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
const copy = new Doc(copyProtoId, true);
updateCachedAcls(copy);
@@ -1025,7 +1062,6 @@ export namespace Doc {
if (retitle) {
copy.title = incrementTitleCopy(StrCast(copy.title));
}
- Doc.AddFileOrphan(copy);
return copy;
}
@@ -1044,7 +1080,6 @@ export namespace Doc {
if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DocData], 'proto_embeddings', delegate);
title && (delegate.title = title);
delegate[Initializing] = false;
- Doc.AddFileOrphan(delegate);
return delegate;
}
return undefined;
@@ -1177,7 +1212,7 @@ export namespace Doc {
// the document containing the view layout information - will be the Document itself unless the Document has
// a layout field or 'layout' is given.
export function Layout(doc: Doc, layout?: Doc): Doc {
- const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}-layout[` + layout[Id] + ']'], Doc, null);
+ const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}_layout[` + layout[Id] + ']'], Doc, null);
return overrideLayout || doc[DocLayout] || doc;
}
export function SetLayout(doc: Doc, layout: Doc | string) {
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
index 66d1ab094..dc9d1084b 100644
--- a/src/fields/DocSymbols.ts
+++ b/src/fields/DocSymbols.ts
@@ -23,3 +23,5 @@ export const UpdatingFromServer = Symbol('DocUpdatingFromServer');
export const Initializing = Symbol('DocInitializing');
export const ForceServerWrite = Symbol('DocForceServerWrite');
export const CachedUpdates = Symbol('DocCachedUpdates');
+
+export const DashVersion = 'v0.5.4';
diff --git a/src/fields/FieldLoader.tsx b/src/fields/FieldLoader.tsx
index 2a7b936f7..a5a71833c 100644
--- a/src/fields/FieldLoader.tsx
+++ b/src/fields/FieldLoader.tsx
@@ -6,10 +6,9 @@ import './FieldLoader.scss';
@observer
export class FieldLoader extends React.Component {
- @observable public static ServerLoadStatus = { requested: 0, retrieved: 0 };
- public static active = false;
+ @observable public static ServerLoadStatus = { requested: 0, retrieved: 0, message: '' };
render() {
- return <div className="fieldLoader">{`Requested: ${FieldLoader.ServerLoadStatus.requested} ... ${FieldLoader.ServerLoadStatus.retrieved} `}</div>;
+ return <div className="fieldLoader">{`${FieldLoader.ServerLoadStatus.message} request: ${FieldLoader.ServerLoadStatus.requested} ... ${FieldLoader.ServerLoadStatus.retrieved} `}</div>;
}
}
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 251b1149d..69dbe9756 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -1,11 +1,10 @@
-import { Field, Opt, FieldResult, Doc } from './Doc';
+import { DateField } from './DateField';
+import { Doc, Field, FieldResult, Opt } from './Doc';
import { List } from './List';
import { RefField } from './RefField';
-import { DateField } from './DateField';
-import { ScriptField } from './ScriptField';
-import { URLField, WebField, ImageField, CsvField } from './URLField';
-import { TextField } from '@material-ui/core';
import { RichTextField } from './RichTextField';
+import { ScriptField } from './ScriptField';
+import { CsvField, ImageField, WebField } from './URLField';
export type ToType<T extends InterfaceValue> = T extends 'string'
? string
diff --git a/src/mobile/MobileMain.tsx b/src/mobile/MobileMain.tsx
index 6cbf86f77..dc3a73def 100644
--- a/src/mobile/MobileMain.tsx
+++ b/src/mobile/MobileMain.tsx
@@ -12,7 +12,7 @@ AssignAllExtensions();
const info = await CurrentUserUtils.loadCurrentUser();
DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + ' (mobile)');
await Docs.Prototypes.initialize();
- await CurrentUserUtils.loadUserDocument(info.id);
+ await CurrentUserUtils.loadUserDocument(info);
document.getElementById('root')!.addEventListener(
'wheel',
event => {
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 820e815d8..ebc9deab7 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -12,6 +12,7 @@ import { AcceptableMedia, Upload } from '../SharedMediaTypes';
import ApiManager, { Registration } from './ApiManager';
import { SolrManager } from './SearchManager';
import v4 = require('uuid/v4');
+import { DashVersion } from '../../fields/DocSymbols';
const AdmZip = require('adm-zip');
const imageDataUri = require('image-data-uri');
const fs = require('fs');
@@ -45,7 +46,7 @@ export default class UploadManager extends ApiManager {
method: Method.POST,
subscription: '/ping',
secureHandler: async ({ req, res }) => {
- _success(res, { message: 'pong', date: new Date() });
+ _success(res, { message: DashVersion, date: new Date() });
},
});
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index a00a7ca5d..8b7994eac 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -6,6 +6,7 @@ import * as bcrypt from 'bcrypt-nodejs';
import { Opt } from '../../fields/Doc';
import { WebSocket } from '../websocket';
import { resolvedPorts } from '../server_Initialization';
+import { DashVersion } from '../../fields/DocSymbols';
export const timeMap: { [id: string]: number } = {};
interface ActivityUnit {
@@ -68,7 +69,18 @@ export default class UserManager extends ApiManager {
register({
method: Method.GET,
subscription: '/getCurrentUser',
- secureHandler: ({ res, user: { _id, email, cacheDocumentIds } }) => res.send(JSON.stringify({ id: _id, email, cacheDocumentIds, resolvedPorts })),
+ secureHandler: ({ res, user }) =>
+ res.send(
+ JSON.stringify({
+ version: DashVersion,
+ userDocumentId: user.userDocumentId,
+ linkDatabaseId: user.linkDatabaseId,
+ sharingDocumentId: user.sharingDocumentId,
+ email: user.email,
+ cacheDocumentIds: user.cacheDocumentIds,
+ resolvedPorts,
+ })
+ ),
publicHandler: ({ res }) => res.send(JSON.stringify({ id: '__guest__', email: 'guest' })),
});
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 5f21d7113..337bb812f 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -143,7 +143,6 @@ export namespace DashUploadUtils {
export function uploadYoutube(videoId: string): Promise<Upload.FileResponse> {
return new Promise<Upload.FileResponse<Upload.FileInformation>>((res, rej) => {
- console.log('Uploading YouTube video: ' + videoId);
const name = videoId;
const path = name.replace(/^-/, '__') + '.mp4';
const finalPath = serverPathToFile(Directory.videos, path);
@@ -506,8 +505,8 @@ export namespace DashUploadUtils {
accessPaths: {
agnostic: usingAzure()
? {
- client: BLOBSTORE_URL + `/${filename}`,
- server: BLOBSTORE_URL + `/${filename}`,
+ client: BLOBSTORE_URL + `/${resolved}`,
+ server: BLOBSTORE_URL + `/${resolved}`,
}
: getAccessPaths(images, resolved),
},
@@ -523,6 +522,7 @@ export namespace DashUploadUtils {
try {
const response = await axios.post(RESIZE_FUNCTION_URL, {
url: requestable,
+ filename: resolved,
});
writtenFiles = response.data.writtenFiles;
} catch (err) {
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index c1934451c..354f809e0 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -100,7 +100,7 @@ function buildWithMiddleware(server: express.Express) {
passport.session(),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
res.locals.user = req.user;
- if (req.originalUrl.endsWith('.png') /*|| req.originalUrl.endsWith(".js")*/ && req.method === 'GET' && (res as any)._contentLength) {
+ if ((req.originalUrl.endsWith('.png') || req.originalUrl.endsWith('.jpg') || (process.env.RELEASE === 'true' && req.originalUrl.endsWith('.js'))) && req.method === 'GET') {
const period = 30000;
res.set('Cache-control', `public, max-age=${period}`);
} else {
@@ -176,6 +176,8 @@ function proxyServe(req: any, requrl: string, response: any) {
const htmlText = htmlInputText
.toString('utf8')
.replace('<head>', '<head> <style>[id ^= "google"] { display: none; } </style>')
+ .replace('<script', '<noscript')
+ .replace('</script', '</noscript')
// .replace(/href="https?([^"]*)"/g, httpsToCors)
.replace(/data-srcset="[^"]*"/g, '')
.replace(/srcset="[^"]*"/g, '')