aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts2
-rw-r--r--src/client/documents/Documents.ts16
-rw-r--r--src/client/util/CurrentUserUtils.ts146
-rw-r--r--src/client/util/DictationManager.ts19
-rw-r--r--src/client/util/DocumentManager.ts1
-rw-r--r--src/client/util/History.ts2
-rw-r--r--src/client/util/HypothesisUtils.ts1
-rw-r--r--src/client/util/InteractionUtils.tsx53
-rw-r--r--src/client/util/SearchUtil.ts2
-rw-r--r--src/client/util/SettingsManager.tsx9
-rw-r--r--src/client/views/DocComponent.tsx2
-rw-r--r--src/client/views/DocumentButtonBar.tsx30
-rw-r--r--src/client/views/DocumentDecorations.tsx8
-rw-r--r--src/client/views/EditableView.tsx5
-rw-r--r--src/client/views/GestureOverlay.tsx69
-rw-r--r--src/client/views/GlobalKeyHandler.ts1
-rw-r--r--src/client/views/MainView.scss4
-rw-r--r--src/client/views/MainView.tsx100
-rw-r--r--src/client/views/OverlayView.tsx3
-rw-r--r--src/client/views/Palette.tsx3
-rw-r--r--src/client/views/PreviewCursor.tsx1
-rw-r--r--src/client/views/PropertiesButtons.scss7
-rw-r--r--src/client/views/PropertiesButtons.tsx208
-rw-r--r--src/client/views/TemplateMenu.tsx9
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx156
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx2
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx2
-rw-r--r--src/client/views/collections/CollectionMenu.tsx14
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx44
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx26
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss3
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx17
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx5
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx4
-rw-r--r--src/client/views/collections/CollectionSubView.tsx46
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx39
-rw-r--r--src/client/views/collections/CollectionView.tsx7
-rw-r--r--src/client/views/collections/SchemaTable.tsx158
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx57
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx11
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.scss5
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.tsx67
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx2
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx1
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx1
-rw-r--r--src/client/views/linking/LinkMenu.tsx1
-rw-r--r--src/client/views/nodes/AudioBox.tsx6
-rw-r--r--src/client/views/nodes/DocHolderBox.tsx2
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx16
-rw-r--r--src/client/views/nodes/DocumentView.scss7
-rw-r--r--src/client/views/nodes/DocumentView.tsx16
-rw-r--r--src/client/views/nodes/FieldView.tsx6
-rw-r--r--src/client/views/nodes/ImageBox.tsx29
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx5
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx3
-rw-r--r--src/client/views/nodes/PDFBox.tsx23
-rw-r--r--src/client/views/nodes/PresBox.tsx2
-rw-r--r--src/client/views/nodes/VideoBox.tsx1
-rw-r--r--src/client/views/nodes/WebBox.tsx1
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx7
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss4
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx143
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx22
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts2
-rw-r--r--src/client/views/nodes/formattedText/RichTextSchema.tsx5
-rw-r--r--src/client/views/pdf/PDFViewer.tsx33
-rw-r--r--src/client/views/presentationview/PresElementBox.tsx1
-rw-r--r--src/client/views/search/SearchBox.tsx873
-rw-r--r--src/fields/Doc.ts59
-rw-r--r--src/fields/List.ts25
-rw-r--r--src/fields/ScriptField.ts8
-rw-r--r--src/fields/documentSchemas.ts2
-rw-r--r--src/mobile/AudioUpload.tsx3
-rw-r--r--src/mobile/MobileInterface.tsx73
75 files changed, 1220 insertions, 1528 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index d9a5353e8..5b5a06839 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -422,6 +422,8 @@ export function returnEmptyString() { return ""; }
export function returnEmptyFilter() { return [] as string[]; }
+export function returnEmptyDoclist() { return [] as any[]; }
+
export let emptyPath = [];
export function emptyFunction() { }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7d114d417..cce50e306 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -72,6 +72,7 @@ export interface DocumentOptions {
_scrollTop?: number; // scroll location for pdfs
_noAutoscroll?: boolean;// whether collections autoscroll when this item is dragged
_chromeStatus?: string;
+ _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view)
_viewType?: string; // sub type of a collection
_gridGap?: number; // gap between items in masonry view
_xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts
@@ -192,6 +193,8 @@ export interface DocumentOptions {
treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked
treeViewTruncateTitleWidth?: number;
+ treeViewLockExpandedView?: boolean; // whether the expanded view can be changed
+ treeViewDefaultExpandedView?: string; // default expanded view
limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents
// [key: string]: Opt<Field>;
pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown
@@ -819,8 +822,10 @@ export namespace Docs {
}
export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
- const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
- Doc.GetProto(inst).data = new List<Doc>(documents);
+ const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+ const tabs = TreeDocument(documents, { title: "On-Screen Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
+ const all = TreeDocument([], { title: "Off-Screen Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
+ Doc.GetProto(inst).data = new List<Doc>([tabs, all]);
return inst;
}
@@ -868,6 +873,7 @@ export namespace DocUtils {
}
const filteredDocs = docFilters.length ? childDocs.filter(d => {
+ if (d.z) return true;
for (const facetKey of Object.keys(filterFacets)) {
const facet = filterFacets[facetKey];
const satisfiesFacet = Object.keys(facet).some(value => {
@@ -982,7 +988,7 @@ export namespace DocUtils {
created = Docs.Create.StackingDocument(DocListCast(field), resolved);
layout = CollectionView.LayoutString;
} else {
- created = Docs.Create.TextDocument("", { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved });
+ created = Docs.Create.TextDocument("", { ...{ _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, _height: 25, _autoHeight: true }, ...resolved });
layout = FormattedTextBox.LayoutString;
}
if (created) {
@@ -1042,7 +1048,7 @@ export namespace DocUtils {
description: ":" + StrCast(note.title),
event: undoBatch((args: { x: number, y: number }) => {
const textDoc = Docs.Create.TextDocument("", {
- _width: 200, x, y, _autoHeight: note._autoHeight !== false,
+ _width: 200, x, y, _autoHeight: note._autoHeight !== false, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined,
title: StrCast(note.title) + "#" + (note.aliasCount = NumCast(note.aliasCount) + 1)
});
textDoc.layoutKey = "layout_" + note.title;
@@ -1169,7 +1175,7 @@ export namespace DocUtils {
}
const options = optionsCollection as Doc;
const targetDoc = doc && Doc.GetProto(Cast(doc.rootDocument, Doc, null) || doc);
- const docFind = `options.data.find(doc => doc.title === (this.rootDocument||this)["${enumeratedFieldKey}"])?`;
+ const docFind = `options.data?.find(doc => doc.title === (this.rootDocument||this)["${enumeratedFieldKey}"])?`;
targetDoc && (targetDoc.backgroundColor = ComputedField.MakeFunction(docFind + `._backgroundColor || "white"`, undefined, { options }));
targetDoc && (targetDoc.color = ComputedField.MakeFunction(docFind + `.color || "black"`, undefined, { options }));
targetDoc && (targetDoc.borderRounding = ComputedField.MakeFunction(docFind + `.borderRounding`, undefined, { options }));
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index dfae3fce1..c65b5b53c 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -36,7 +36,7 @@ export class CurrentUserUtils {
@computed public static get UserDocument() { return Doc.UserDoc(); }
@observable public static GuestTarget: Doc | undefined;
- @observable public static GuestWorkspace: Doc | undefined;
+ @observable public static GuestDashboard: Doc | undefined;
@observable public static GuestMobile: Doc | undefined;
@observable public static propertiesWidth: number = 0;
@@ -53,7 +53,7 @@ export class CurrentUserUtils {
);
queryTemplate.isTemplateDoc = makeTemplate(queryTemplate);
doc["template-button-query"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(queryTemplate) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "query view", icon: "question-circle"
});
@@ -72,7 +72,7 @@ export class CurrentUserUtils {
this.mobileTextContainer({},
[this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]);
doc["template-mobile-button"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(queryTemplate) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "mobile button", icon: "mobile"
});
@@ -88,7 +88,7 @@ export class CurrentUserUtils {
);
slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
doc["template-button-slides"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(slideTemplate) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "presentation slide", icon: "address-card"
});
@@ -105,7 +105,7 @@ export class CurrentUserUtils {
(descriptionTemplate.proto as Doc).isTemplateDoc = makeTemplate(descriptionTemplate.proto as Doc, true, "descriptionView");
doc["template-button-description"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(descriptionTemplate) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize", system: true
});
@@ -152,7 +152,7 @@ export class CurrentUserUtils {
linkTemplate.header = new RichTextField(JSON.stringify(rtf2), "");
doc["template-button-link"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(linkTemplate) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "link view", icon: "window-maximize", system: true
});
@@ -184,7 +184,7 @@ export class CurrentUserUtils {
box.isTemplateDoc = makeTemplate(box, true, "switch");
doc["template-button-switch"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(box) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "data switch", icon: "toggle-on", system: true
});
@@ -234,7 +234,7 @@ export class CurrentUserUtils {
long.title = "Long Description";
doc["template-button-detail"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(detailView) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "detail view", icon: "window-maximize", system: true
});
@@ -395,31 +395,37 @@ export class CurrentUserUtils {
if (doc.emptyPresentation === undefined) {
doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
{ title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true });
+ ((doc.emptyPresentation as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyCollection === undefined) {
doc.emptyCollection = Docs.Create.FreeformDocument([],
{ _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: "freeform", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyCollection as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyPane === undefined) {
- doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, title: "Untitled Collection", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyComparison === undefined) {
doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.emptyScript === undefined) {
doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyScript as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyScreenshot === undefined) {
doc.emptyScreenshot = Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot", system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.emptyAudio === undefined) {
doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, title: "ready to record audio", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyImage === undefined) {
doc.emptyImage = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth: 250, title: "an image of a cat", system: true });
}
if (doc.emptyButton === undefined) {
doc.emptyButton = Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title: "Button", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyButton as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyDocHolder === undefined) {
doc.emptyDocHolder = Docs.Create.DocumentDocument(
@@ -433,17 +439,18 @@ export class CurrentUserUtils {
this.setupActiveMobileMenu(doc);
}
return [
- { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(getCopy(this.clickFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, },
- { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true },
- { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyImage as Doc },
- { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyComparison as Doc, noviceMode: true },
- { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyScreenshot as Doc },
+ { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, },
+ { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true },
+ { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyImage as Doc },
+ { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true },
+ { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc },
// { title: "Drag a webcam", title: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
- { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyAudio as Doc, noviceMode: true },
- { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyButton as Doc, noviceMode: true },
+ { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true },
+ { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc, noviceMode: true },
+
{ toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true },
- { toolTip: "Tap to create a search box in a new pane, drag for a search box", title: "Query", icon: "search", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptySearch as Doc },
- { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyScript as Doc },
+ { toolTip: "Tap to create a search box in a new pane, drag for a search box", title: "Query", icon: "search", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySearch as Doc },
+ { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc },
// { title: "Drag an import folder", title: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
{ toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc },
// { title: "Drag an instance of the device collection", title: "Buxton", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.Buxton()' },
@@ -452,7 +459,7 @@ export class CurrentUserUtils {
// { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
// { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activeInkPen, this)`, backgroundColor: "pink", activeInkPen: doc },
// { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activeInkPen = this;', ischecked: `sameDocs(this.activeInkPen, this)`, backgroundColor: "white", activeInkPen: doc },
- { toolTip: "Tap to create a document previewer in a new pane, drag for a document previewer", title: "Prev", icon: "expand", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyDocHolder as Doc },
+ { toolTip: "Tap to create a document previewer in a new pane, drag for a document previewer", title: "Prev", icon: "expand", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyDocHolder as Doc },
{ toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
{ toolTip: "Connect a Google Account", title: "Google Account", icon: "external-link-alt", click: 'GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)' },
];
@@ -505,38 +512,39 @@ export class CurrentUserUtils {
}
static menuBtnDescriptions(doc: Doc): {
- title: string, icon: string, click: string, watchedDocuments?: Doc
+ title: string, target: Doc, icon: string, click: string, watchedDocuments?: Doc
}[] {
this.setupSharingSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing
return [
- { title: "Sharing", icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc },
- { title: "Workspace", icon: "desktop", click: 'selectMainMenu(self)' },
- { title: "Pres. Trails", icon: "pres-trail", click: 'selectMainMenu(self)' },
- { title: "Catalog", icon: "file", click: 'selectMainMenu(self)' },
- { title: "Archive", icon: "archive", click: 'selectMainMenu(self)' },
- { title: "Import", icon: "upload", click: 'selectMainMenu(self)' },
- { title: "Tools", icon: "wrench", click: 'selectMainMenu(self)' },
- { title: "Help", icon: "question-circle", click: 'selectMainMenu(self)' },
- { title: "Settings", icon: "cog", click: 'selectMainMenu(self)' },
- { title: "User Doc", icon: "address-card", click: 'selectMainMenu(self)' },
+ { title: "Dashboards", target: Cast(doc["sidebar-dashboards"], Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
+ { title: "Catalog", target: undefined as any, icon: "file", click: 'selectMainMenu(self)' },
+ { title: "Inactive", target: Cast(doc["sidebar-inactiveDocs"], Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
+ { title: "Import", target: Cast(doc["sidebar-import"], Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
+ { title: "Sharing", target: Cast(doc["sidebar-sharing"], Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc },
+ { title: "Tools", target: Cast(doc["sidebar-tools"], Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
+ { title: "Pres. Trails", target: Cast(doc["sidebar-presentations"], Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
+ { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
+ { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
+ { title: "User Doc", target: Cast(doc["sidebar-userDoc"], Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
];
}
static setupSearchPanel(doc: Doc) {
if (doc["search-panel"] === undefined) {
doc["search-panel"] = new PrefetchProxy(Docs.Create.SearchDocument({
- _width: 500, _height: 400, backgroundColor: "dimGray", ignoreClick: true,
+ _width: 500, _height: 400, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
childDropAction: "alias", lockedPosition: true, _viewType: CollectionViewType.Schema, _chromeStatus: "disabled", title: "sidebar search stack", system: true
})) as any as Doc;
}
}
static setupMenuPanel(doc: Doc) {
if (doc.menuStack === undefined) {
- const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, icon, click, watchedDocuments }) =>
+ const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments }) =>
Docs.Create.FontIconDocument({
icon,
iconShape: "square",
title,
+ target,
_backgroundColor: "black",
dropAction: "alias",
removeDropProperties: new List<string>(["dropAction"]),
@@ -591,7 +599,7 @@ export class CurrentUserUtils {
// SEts up mobile buttons for inside mobile menu
static setupMobileButtons(doc?: Doc, buttons?: string[]) {
const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [
- { title: "WORKSPACES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Workspaces from your mobile, and navigate through all of your documents. " },
+ { title: "DASHBOARDS", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " },
{ title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." },
{ title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploadCollection()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." },
{ title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." },
@@ -688,11 +696,11 @@ export class CurrentUserUtils {
}
static setupLibrary(userDoc: Doc) {
- return CurrentUserUtils.setupWorkspaces(userDoc);
+ return CurrentUserUtils.setupDashboards(userDoc);
}
- // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker.
- // when clicked, this panel will be displayed in the target container (ie, sidebarContainer)
+ // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker.
+ // when clicked, this panel will be displayed in the target container (ie, sidebarContainer)
static async setupToolsBtnPanel(doc: Doc) {
// setup a masonry view of all he creators
const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc);
@@ -723,28 +731,29 @@ export class CurrentUserUtils {
}
}
- static async setupWorkspaces(doc: Doc) {
- // setup workspaces library item
- await doc.myWorkspaces;
- if (doc.myWorkspaces === undefined) {
- doc.myWorkspaces = new PrefetchProxy(Docs.Create.TreeDocument([], {
- title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true
+ static async setupDashboards(doc: Doc) {
+ // setup dashboards library item
+ await doc.myDashboards;
+ if (doc.myDashboards === undefined) {
+ doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
+ title: "DASHBOARDS", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true,
+ treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data",
}));
}
- if (doc["sidebar-workspaces"] === undefined) {
- const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`);
- (doc.myWorkspaces as Doc).contextMenuScripts = new List<ScriptField>([newWorkspace!]);
- (doc.myWorkspaces as Doc).contextMenuLabels = new List<string>(["Create New Workspace"]);
+ if (doc["sidebar-dashboards"] === undefined) {
+ const newDashboard = ScriptField.MakeScript(`createNewDashboard()`);
+ (doc.myDashboards as Doc).contextMenuScripts = new List<ScriptField>([newDashboard!]);
+ (doc.myDashboards as Doc).contextMenuLabels = new List<string>(["Create New Dashboard"]);
- const workspaces = doc.myWorkspaces as Doc;
+ const dashboards = doc.myDashboards as Doc;
- doc["sidebar-workspaces"] = new PrefetchProxy(Docs.Create.TreeDocument([workspaces], {
+ doc["sidebar-dashboards"] = new PrefetchProxy(Docs.Create.TreeDocument([dashboards], {
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
})) as any as Doc;
}
- return doc.myWorkspaces as any as Doc;
+ return doc.myDashboards as any as Doc;
}
static async addToPresList(doc: Doc, pres: Doc) {
@@ -802,25 +811,26 @@ export class CurrentUserUtils {
})) as any as Doc;
}
}
- static setupRecentlyClosed(doc: Doc) {
+ static setupInactiveDocs(doc: Doc) {
// setup Recently Closed library item
- doc.myRecentlyClosed === undefined;
- if (doc.myRecentlyClosed === undefined) {
- doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], {
- title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true
+ doc.myInactiveDocs === undefined;
+ if (doc.myInactiveDocs === undefined) {
+ doc.myInactiveDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
+ title: "CLOSED DOCS", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true,
+ treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data",
}));
}
- // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
- PromiseValue(Cast(doc.myRecentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
- if (doc["sidebar-recentlyClosed"] === undefined) {
+ // this is equivalent to using PrefetchProxies to make sure the inactiveDocs doc is ready
+ PromiseValue(Cast(doc.myInactiveDocs, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
+ if (doc["sidebar-inactiveDocs"] === undefined) {
const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
- (doc.myRecentlyClosed as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
- (doc.myRecentlyClosed as Doc).contextMenuLabels = new List<string>(["Clear All"]);
+ (doc.myInactiveDocs as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
+ (doc.myInactiveDocs as Doc).contextMenuLabels = new List<string>(["Clear All"]);
- const recentlyClosed = doc.myRecentlyClosed as Doc;
+ const inactiveDocs = doc.myInactiveDocs as Doc;
- doc["sidebar-recentlyClosed"] = new PrefetchProxy(Docs.Create.TreeDocument([recentlyClosed], {
- title: "sidebar-recentlyClosed",
+ doc["sidebar-inactiveDocs"] = new PrefetchProxy(Docs.Create.TreeDocument([inactiveDocs], {
+ title: "sidebar-inactiveDocs",
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
@@ -855,10 +865,10 @@ export class CurrentUserUtils {
static async setupSidebarButtons(doc: Doc) {
CurrentUserUtils.setupSidebarContainer(doc);
await CurrentUserUtils.setupToolsBtnPanel(doc);
- CurrentUserUtils.setupWorkspaces(doc);
+ CurrentUserUtils.setupDashboards(doc);
CurrentUserUtils.setupCatalog(doc);
CurrentUserUtils.setupPresentations(doc);
- CurrentUserUtils.setupRecentlyClosed(doc);
+ CurrentUserUtils.setupInactiveDocs(doc);
CurrentUserUtils.setupUserDoc(doc);
}
@@ -924,7 +934,7 @@ export class CurrentUserUtils {
static setupClickEditorTemplates(doc: Doc) {
if (doc["clickFuncs-child"] === undefined) {
- // to use this function, select it from the context menu of a collection. then edit the onChildClick script. Add two Doc variables: 'target' and 'thisContainer', then assign 'target' to some target collection. After that, clicking on any document in the initial collection will open it in the target
+ // to use this function, select it from the context menu of a collection. then edit the onChildClick script. Add two Doc variables: 'target' and 'thisContainer', then assign 'target' to some target collection. After that, clicking on any document in the initial collection will open it in the target
const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript(
"docCast(thisContainer.target).then((target) => target && (target.proto.data = new List([self]))) ",
{ thisContainer: Doc.name }), {
@@ -997,11 +1007,11 @@ export class CurrentUserUtils {
this.setupDocTemplates(doc); // sets up the template menu of templates
this.setupImportSidebar(doc);
this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
- this.setupMenuPanel(doc);
this.setupSearchPanel(doc);
this.setupOverlays(doc); // documents in overlay layer
this.setupDockedButtons(doc); // the bottom bar of font icons
await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
+ this.setupMenuPanel(doc);
doc.globalLinkDatabase = Docs.Prototypes.MainLinkDocument();
doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument();
doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument();
@@ -1039,10 +1049,10 @@ export class CurrentUserUtils {
}
}
+Scripting.addGlobal(function createNewDashboard() { return MainView.Instance.createNewDashboard(); },
+ "creates a new dashboard when called");
Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
"creates a new presentation when called");
-Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); },
- "creates a new workspace when called");
Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
"returns all the links to the document or its annotations", "(doc: any)");
Scripting.addGlobal(function directLinks(doc: any) { return new List(LinkManager.Instance.getAllDirectLinks(doc)); },
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 540540642..e66c15fcb 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -6,7 +6,6 @@ import { DocumentType } from "../documents/DocumentTypes";
import { Doc, Opt } from "../../fields/Doc";
import { List } from "../../fields/List";
import { Docs } from "../documents/Documents";
-import { CollectionViewType } from "../views/collections/CollectionView";
import { Cast, CastCtor } from "../../fields/Types";
import { listSpec } from "../../fields/Schema";
import { AudioField, ImageField } from "../../fields/URLField";
@@ -88,7 +87,7 @@ export namespace DictationManager {
export const listen = async (options?: Partial<ListeningOptions>) => {
let results: string | undefined;
- const overlay = options !== undefined && options.useOverlay;
+ const overlay = options?.useOverlay;
if (overlay) {
DictationOverlay.Instance.dictationOverlayVisible = true;
DictationOverlay.Instance.isListening = { interim: false };
@@ -100,11 +99,11 @@ export namespace DictationManager {
Utils.CopyText(results);
if (overlay) {
DictationOverlay.Instance.isListening = false;
- const execute = options && options.tryExecute;
+ const execute = options?.tryExecute;
DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results;
DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true;
}
- options && options.tryExecute && await DictationManager.Commands.execute(results);
+ options?.tryExecute && await DictationManager.Commands.execute(results);
}
} catch (e) {
if (overlay) {
@@ -129,12 +128,12 @@ export namespace DictationManager {
}
isListening = true;
- const handler = options ? options.interimHandler : undefined;
- const continuous = options ? options.continuous : undefined;
+ const handler = options?.interimHandler;
+ const continuous = options?.continuous;
const indefinite = continuous && continuous.indefinite;
- const language = options ? options.language : undefined;
- const intra = options && options.delimiters ? options.delimiters.intra : undefined;
- const inter = options && options.delimiters ? options.delimiters.inter : undefined;
+ const language = options?.language;
+ const intra = options?.delimiters?.intra;
+ const inter = options?.delimiters?.inter;
recognizer.onstart = () => console.log("initiating speech recognition session...");
recognizer.interimResults = handler !== undefined;
@@ -154,7 +153,7 @@ export namespace DictationManager {
recognizer.onresult = (e: SpeechRecognitionEvent) => {
current = synthesize(e, intra);
let matchedTerminator: string | undefined;
- if (options && options.terminators && (matchedTerminator = options.terminators.find(end => current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false))) {
+ if (options?.terminators && (matchedTerminator = options.terminators.find(end => current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false))) {
current = matchedTerminator;
recognizer.abort();
return complete();
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 962294933..bd57e7f48 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -231,7 +231,6 @@ export class DocumentManager {
containerDoc.currentTimecode = targetTimecode;
const targetContext = await target?.context as Doc;
const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
- console.log(targetNavContext);
DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "onRight"), finished), targetNavContext, linkDoc, undefined, doc, finished);
} else {
finished?.();
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 7b7d4b835..cab682ac7 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -197,7 +197,7 @@ export namespace HistoryUtil {
await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
}
if (field instanceof Doc) {
- MainView.Instance.openWorkspace(field, true);
+ MainView.Instance.openDashboard(field, true);
}
}
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 04e937878..ddd2b89d1 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -60,6 +60,7 @@ export namespace Hypothesis {
DocumentLinksButton.AnnotationId = annotationId;
DocumentLinksButton.AnnotationUri = annotationUri;
DocumentLinksButton.StartLink = sourceDoc;
+ DocumentLinksButton.StartLinkView = undefined;
});
} else { // if a link has already been started, complete the link to sourceDoc
runInAction(() => {
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 13c4fac25..bb58423c8 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -300,19 +300,52 @@ export namespace InteractionUtils {
return points;
case "circle":
+ // const centerX = (right + left) / 2;
+ // const centerY = (bottom + top) / 2;
+ // const radius = bottom - centerY;
+
+ // for (var y = top; y < bottom; y++) {
+ // const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ // points.push({ X: x, Y: y });
+ // }
+ // for (var y = bottom; y > top; y--) {
+ // const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ // const newX = centerX - (x - centerX);
+ // points.push({ X: newX, Y: y });
+ // }
+ // points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
const centerX = (right + left) / 2;
const centerY = (bottom + top) / 2;
- const radius = bottom - centerY;
- for (var y = top; y < bottom; y++) {
- const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
- points.push({ X: x, Y: y });
- }
- for (var y = bottom; y > top; y--) {
- const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
- const newX = centerX - (x - centerX);
- points.push({ X: newX, Y: y });
+ if ((bottom - centerY) < (right - centerX)) {
+ const radius = bottom - centerY;
+ for (var y = top; y < bottom; y++) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ points.push({ X: x, Y: y });
+ }
+ for (var y = bottom; y > top; y--) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ const newX = centerX - (x - centerX);
+ points.push({ X: newX, Y: y });
+ }
+ points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
+
+ } else {
+ //right = bottom
+ //left = top
+ const radius = right - centerX;
+ for (var x = left; x < right; x++) {
+ const y = Math.sqrt(Math.pow(radius, 2) - (Math.pow((x - centerX), 2))) + centerY;
+ points.push({ X: x, Y: y });
+ }
+ for (var x = right; x > left; x--) {
+ const y = Math.sqrt(Math.pow(radius, 2) - (Math.pow((x - centerX), 2))) + centerY;
+ const newY = centerY - (y - centerY);
+ points.push({ X: x, Y: newY });
+ }
+ points.push({ X: left, Y: Math.sqrt(Math.pow(radius, 2) - (Math.pow((left - centerX), 2))) + centerY });
+
+
}
- points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
return points;
// case "arrow":
// const x1 = left;
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index ce96ab67b..afa8ff575 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -85,6 +85,8 @@ export namespace SearchUtil {
if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
theDocs.push(testDoc);
theLines.push([]);
+ } else {
+ result.numFound--;
}
}
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 5642c5a42..bc5e1e9d3 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -54,6 +54,7 @@ export default class SettingsManager extends React.Component<{}> {
}
@undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.UserDoc().noviceMode = (e.currentTarget as any)?.value === "Novice");
+ @undoBatch changeShowTitle = action((e: React.ChangeEvent) => Doc.UserDoc().showTitle = (e.currentTarget as any).value);
@undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value);
@undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value);
@undoBatch switchColor = action((color: ColorState) => Doc.UserDoc().activeCollectionBackground = String(color.hex));
@@ -90,13 +91,17 @@ export default class SettingsManager extends React.Component<{}> {
{colorFlyout}
</div>
<div className="preferences-font">
- <div className="preferences-font-text">Default Font</div>
+ <div className="preferences-font-text">Font</div>
<select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} >
{fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)}
</select>
<select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7pt")}>
{fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
</select>
+ <div>
+ Show title
+ <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = !Doc.UserDoc().showTitle} checked={BoolCast(Doc.UserDoc().showTitle)} />
+ </div>
</div>
</div>;
}
@@ -158,7 +163,7 @@ export default class SettingsManager extends React.Component<{}> {
<div className="settings-title">Settings</div>
<div className="settings-username">{Doc.CurrentUserEmail}</div>
<button className="logout-button" onClick={() => window.location.assign(Utils.prepend("/logout"))} >
- {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
+ {CurrentUserUtils.GuestDashboard ? "Exit" : "Log Out"}
</button>
<div className="close-button" onClick={this.close}>
<FontAwesomeIcon icon={fa.faTimes} color="black" size={"lg"} />
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 23ce71c4f..d621a792b 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -131,7 +131,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
const toRemove = value.filter(v => docs.includes(v));
if (toRemove.length !== 0) {
- const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
+ const recent = Cast(Doc.UserDoc().myInactiveDocs, Doc) as Doc;
toRemove.forEach(doc => {
Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey + "-annotations", doc);
recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 8a12bf467..e5f419558 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -23,7 +23,8 @@ import { Template, Templates } from "./Templates";
import React = require("react");
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { Tooltip } from '@material-ui/core';
-import { SelectionManager } from '../util/SelectionManager';
+import SharingManager from '../util/SharingManager';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -213,6 +214,27 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
}
@computed
+ get shareButton() {
+ const targetDoc = this.view0?.props.Document;
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Open Sharing Manager"}</div></>}>
+ <div className="documentButtonBar-linker" style={{ color: "white" }} onClick={e => SharingManager.Instance.open(this.view0, targetDoc)}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="users"
+ />
+ </div></Tooltip >;
+ }
+
+ @computed
+ get moreButton() {
+ const targetDoc = this.view0?.props.Document;
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Open Properties Panel"}</div></>}>
+ <div className="documentButtonBar-linker" style={{ color: "white" }} onClick={action(e =>
+ CurrentUserUtils.propertiesWidth = CurrentUserUtils.propertiesWidth > 0 ? 0 : 250)}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="ellipsis-h"
+ />
+ </div></Tooltip >;
+ }
+
+ @computed
get metadataButton() {
const view0 = this.view0;
return !view0 ? (null) : <Tooltip title={<><div className="dash-tooltip">Show metadata panel</div></>}>
@@ -300,6 +322,12 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
<div className="documentButtonBar-button">
{this.pinButton}
</div>
+ {/* <div className="documentButtonBar-button">
+ {this.shareButton}
+ </div> */}
+ <div className="documentButtonBar-button">
+ {this.moreButton}
+ </div>
{/* <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
{this.considerGoogleDocsPush}
</div>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 0cc492ee9..8e44bcf8f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -117,7 +117,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
this._titleControlString = this._accumulatedTitle;
} else if (this._titleControlString.startsWith("#")) {
const selectionTitleFieldKey = this._titleControlString.substring(1);
- selectionTitleFieldKey === "title" && (SelectionManager.SelectedDocuments()[0].props.Document.customTitle = !this._accumulatedTitle.startsWith("-"));
+ selectionTitleFieldKey === "title" && (SelectionManager.SelectedDocuments()[0].dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-"));
UndoManager.RunInBatch(() => selectionTitleFieldKey && SelectionManager.SelectedDocuments().forEach(d => {
const value = typeof d.props.Document[selectionTitleFieldKey] === "number" ? +this._accumulatedTitle : this._accumulatedTitle;
Doc.SetInPlace(d.props.Document, selectionTitleFieldKey, value, true);
@@ -596,7 +596,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
}
render() {
- const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined;
+ const darkScheme = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimgray" : undefined;
const bounds = this.Bounds;
const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
@@ -624,8 +624,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} />
{minimal ? (null) : <div className="publishBox" // title="make document referenceable by its title"
// onPointerDown={action(e => {
- // if (!seldoc.props.Document.customTitle) {
- // seldoc.props.Document.customTitle = true;
+ // if (!seldoc.props.Document["title-custom"]) {
+ // seldoc.props.Document["title-custom"] = true;
// StrCast(Doc.GetProto(seldoc.props.Document).title).startsWith("-") && (Doc.GetProto(seldoc.props.Document).title = StrCast(seldoc.props.Document.title).substring(1));
// this._accumulatedTitle = StrCast(seldoc.props.Document.title);
// }
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index ec3e754fb..b96802f06 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -227,7 +227,10 @@ export class EditableView extends React.Component<EditableProps> {
ref={this._ref}
style={{ display: this.props.display, minHeight: "17px", whiteSpace: "nowrap", height: `${this.props.height ? this.props.height : "auto"}`, maxHeight: `${this.props.maxHeight}` }}
onClick={this.onClick} placeholder={this.props.placeholder}>
- {this.props.highlight === undefined || this.props.positions === undefined || this.props.bing === undefined ? <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}</span>
+ {this.props.highlight === undefined || this.props.positions === undefined || this.props.bing === undefined ?
+ <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize }}>{//}, color: this.props.contents ? "black" : "grey" }}>{
+ this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}
+ </span>
: this.returnHighlights()}
</div>
);
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index c25ebbf11..260cffc90 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -7,7 +7,7 @@ import { Cast, FieldValue, NumCast } from "../../fields/Types";
import MobileInkOverlay from "../../mobile/MobileInkOverlay";
import { GestureUtils } from "../../pen-gestures/GestureUtils";
import { MobileInkOverlayContent } from "../../server/Message";
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter, setupMoveUpEvents } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter, setupMoveUpEvents, returnEmptyDoclist } from "../../Utils";
import { CognitiveServices } from "../cognitive_services/CognitiveServices";
import { DocUtils } from "../documents/Documents";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
@@ -754,20 +754,64 @@ export default class GestureOverlay extends Touchable {
break;
case "circle":
+ // const centerX = (right + left) / 2;
+ // const centerY = (bottom + top) / 2;
+ // const radius = bottom - centerY;
+
+
+ // for (var y = top; y < bottom; y++) {
+ // const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ // this._points.push({ X: x, Y: y });
+ // }
+ // for (var y = bottom; y > top; y--) {
+ // const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ // const newX = centerX - (x - centerX);
+ // this._points.push({ X: newX, Y: y });
+ // }
+ // this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
+ // this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top - 1 });
+
const centerX = (right + left) / 2;
const centerY = (bottom + top) / 2;
- const radius = bottom - centerY;
- for (var y = top; y < bottom; y++) {
- const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
- this._points.push({ X: x, Y: y });
- }
- for (var y = bottom; y > top; y--) {
- const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
- const newX = centerX - (x - centerX);
- this._points.push({ X: newX, Y: y });
+ if ((bottom - centerY) < (right - centerX)) {
+ const radius = bottom - centerY;
+ for (var y = top; y < bottom; y++) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ this._points.push({ X: x, Y: y });
+ }
+ for (var y = bottom; y > top; y--) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ const newX = centerX - (x - centerX);
+ this._points.push({ X: newX, Y: y });
+ }
+ this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
+ this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top - 1 });
+
+ } else {
+ //right = bottom
+ //left = top
+ const radius = right - centerX;
+ for (var x = left; x < right; x++) {
+ const y = Math.sqrt(Math.pow(radius, 2) - (Math.pow((x - centerX), 2))) + centerY;
+ this._points.push({ X: x, Y: y });
+ }
+ for (var x = right; x > left; x--) {
+ const y = Math.sqrt(Math.pow(radius, 2) - (Math.pow((x - centerX), 2))) + centerY;
+ const newY = centerY - (y - centerY);
+ this._points.push({ X: x, Y: newY });
+ }
+ this._points.push({ X: left, Y: Math.sqrt(Math.pow(radius, 2) - (Math.pow((left - centerX), 2))) + centerY });
+ this._points.push({ X: left, Y: (Math.sqrt(Math.pow(radius, 2) - (Math.pow((left - centerX), 2))) + centerY) - 1 });
+
+
}
- this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
- this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top - 1 });
+
+
+
+
+
+
+
break;
case "line":
@@ -891,6 +935,7 @@ export default class GestureOverlay extends Touchable {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
/>;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index be6aa6be2..9e7f525b7 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -89,6 +89,7 @@ export default class KeyManager {
// }
// }
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
const main = MainView.Instance;
Doc.SetSelectedTool(InkTool.None);
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index a05a2b858..aebb7a19a 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -313,7 +313,7 @@
}
-.mainView-workspace {
+.mainView-dashboard {
height: 200px;
position: relative;
display: flex;
@@ -325,7 +325,7 @@
display: flex;
}
-.mainView-recentlyClosed {
+.mainView-inactiveDocs {
height: 25%;
position: relative;
display: flex;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1df8f6eb7..979866eaa 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,29 +1,35 @@
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faHireAHelper, faBuffer } from '@fortawesome/free-brands-svg-icons';
+import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons';
import * as fa from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
+import * as ReactDOM from 'react-dom';
import Measure from 'react-measure';
+import * as rp from 'request-promise';
import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
import { listSpec } from '../../fields/Schema';
import { ScriptField } from '../../fields/ScriptField';
-import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, FieldValue, StrCast, NumCast } from '../../fields/Types';
import { TraceMobx } from '../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils, simulateMouseClick } from '../../Utils';
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils, returnEmptyDoclist } from '../../Utils';
import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { Docs, DocumentOptions } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
+import { Networking } from '../Network';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
import { DocumentManager } from '../util/DocumentManager';
import GroupManager from '../util/GroupManager';
import { HistoryUtil } from '../util/History';
+import { Hypothesis } from '../util/HypothesisUtils';
+import { LinkManager } from '../util/LinkManager';
import { Scripting } from '../util/Scripting';
+import { SearchUtil } from '../util/SearchUtil';
import { SelectionManager } from '../util/SelectionManager';
import SettingsManager from '../util/SettingsManager';
import SharingManager from '../util/SharingManager';
@@ -53,12 +59,10 @@ import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
import { LinkDocPreview } from './nodes/LinkDocPreview';
import { RadialMenu } from './nodes/RadialMenu';
import { TaskCompletionBox } from './nodes/TaskCompletedBox';
+import { WebBox } from './nodes/WebBox';
import { OverlayView } from './OverlayView';
import PDFMenu from './pdf/PDFMenu';
import { PreviewCursor } from './PreviewCursor';
-import { Hypothesis } from '../util/HypothesisUtils';
-import { WebBox } from './nodes/WebBox';
-import * as ReactDOM from 'react-dom';
import { SearchBox } from './search/SearchBox';
import { SearchUtil } from '../util/SearchUtil';
import { Networking } from '../Network';
@@ -79,10 +83,10 @@ export class MainView extends React.Component {
@observable private _panelHeight: number = 0;
@observable private _flyoutTranslate: boolean = false;
@observable public flyoutWidth: number = 0;
- private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); }
+ private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeDashboard, Doc, null)?.darkScheme); }
@computed private get userDoc() { return Doc.UserDoc(); }
- @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
+ @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeDashboard, Doc)) : CurrentUserUtils.GuestDashboard; }
@computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
@computed public get searchDoc() { return Cast(this.userDoc["search-panel"], Doc) as Doc; }
@@ -184,7 +188,7 @@ export class MainView extends React.Component {
fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle,
fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, faBuffer, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
- fa.faWindowMinimize, fa.faWindowRestore);
+ fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faClosedCaptioning);
this.initEventListeners();
this.initAuthenticationRouters();
}
@@ -208,7 +212,7 @@ export class MainView extends React.Component {
}
});
if (check === false) {
- SearchBox.Instance.closeSearch();
+ SearchBox.Instance.resetSearch(true);
}
}
@@ -226,12 +230,12 @@ export class MainView extends React.Component {
}
initAuthenticationRouters = async () => {
- // Load the user's active workspace, or create a new one if initial session after signup
+ // Load the user's active dashboard, or create a new one if initial session after signup
const received = CurrentUserUtils.MainDocId;
if (received && !this.userDoc) {
reaction(
() => CurrentUserUtils.GuestTarget,
- target => target && this.createNewWorkspace(),
+ target => target && this.createNewDashboard(),
{ fireImmediately: true }
);
} else {
@@ -244,11 +248,11 @@ export class MainView extends React.Component {
}),
);
}
- const doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc);
+ const doc = this.userDoc && await Cast(this.userDoc.activeDashboard, Doc);
if (doc) {
- this.openWorkspace(doc);
+ this.openDashboard(doc);
} else {
- this.createNewWorkspace();
+ this.createNewDashboard();
}
}
}
@@ -270,38 +274,41 @@ export class MainView extends React.Component {
}
@action
- createNewWorkspace = async (id?: string) => {
+ createNewDashboard = async (id?: string) => {
const myCatalog = Doc.UserDoc().myCatalog as Doc;
- const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc;
- const workspaceCount = DocListCast(workspaces.data).length + 1;
+ const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
+ const dashboards = Cast(this.userDoc.myDashboards, Doc) as Doc;
+ const dashboardCount = DocListCast(dashboards.data).length + 1;
+ const emptyPane = Cast(this.userDoc.emptyPane, Doc, null);
+ emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
const freeformOptions: DocumentOptions = {
x: 0,
y: 400,
_width: this._panelWidth * .7 - this.propertiesWidth() * 0.7,
_height: this._panelHeight,
- title: "Untitled Collection",
+ title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
};
const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
- const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Workspace ${workspaceCount}` }, id, "row");
+ const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Dashboard ${dashboardCount}` }, id, "row");
Doc.AddDocToList(myCatalog, "data", freeformDoc);
const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
- const copyWorkspace = ScriptField.MakeScript(`copyWorkspace()`);
- workspaceDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyWorkspace!]);
- workspaceDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Workspace"]);
+ const copyDashboard = ScriptField.MakeScript(`copyDashboard()`);
+ dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyDashboard!]);
+ dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard"]);
- Doc.AddDocToList(workspaces, "data", workspaceDoc);
+ Doc.AddDocToList(dashboards, "data", dashboardDoc);
// bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
- setTimeout(() => this.openWorkspace(workspaceDoc), 0);
+ setTimeout(() => this.openDashboard(dashboardDoc), 0);
}
@action
- openWorkspace = (doc: Doc, fromHistory = false) => {
+ openDashboard = (doc: Doc, fromHistory = false) => {
CurrentUserUtils.MainDocId = doc[Id];
- if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest workspace
+ if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
!("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
- this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc);
+ this.userDoc ? (this.userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
}
const state = this._urlState;
if (state.sharing === true && !this.userDoc) {
@@ -354,7 +361,7 @@ export class MainView extends React.Component {
if (doc?.type === DocumentType.COL) {
if (doc.title === "Basic Item Creators" || doc.title === "sidebar-tools"
- || doc.title === "sidebar-recentlyClosed" || doc.title === "sidebar-catalog"
+ || doc.title === "sidebar-inactiveDocs" || doc.title === "sidebar-catalog"
|| doc.title === "Mobile Uploads" || doc.title === "COLLECTION_PROTO"
|| doc.title === "Advanced Item Prototypes" || doc.title === "all Creators") {
return "lightgrey";
@@ -409,6 +416,7 @@ export class MainView extends React.Component {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
renderDepth={-1}
@@ -453,7 +461,7 @@ export class MainView extends React.Component {
flyoutWidthFunc = () => this.flyoutWidth;
addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
- doc.dockingConfig ? this.openWorkspace(doc) :
+ doc.dockingConfig ? this.openDashboard(doc) :
CollectionDockingView.AddRightSplit(doc, libraryPath);
}
sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1);
@@ -491,6 +499,7 @@ export class MainView extends React.Component {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
relative={true}
@@ -525,6 +534,7 @@ export class MainView extends React.Component {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
relative={true}
@@ -556,19 +566,14 @@ export class MainView extends React.Component {
} else {
let panelDoc: Doc | undefined;
switch (this.panelContent = title) {
- case "Tools": panelDoc = Doc.UserDoc()["sidebar-tools"] as Doc ?? undefined; break;
- case "Workspace": panelDoc = Doc.UserDoc()["sidebar-workspaces"] as Doc ?? undefined; break;
- case "Catalog": SearchBox.Instance.searchFullDB = "My Stuff";
+ case "Settings": SettingsManager.Instance.open(); break;
+ case "Catalog": SearchBox.Instance._searchFullDB = "My Stuff";
SearchBox.Instance.newsearchstring = "";
SearchBox.Instance.enter(undefined);
break;
// panelDoc = Doc.UserDoc()["sidebar-catalog"] as Doc ?? undefined; break;
- case "Pres. Trails": panelDoc = Doc.UserDoc()["sidebar-presentations"] as Doc ?? undefined; break;
- case "Archive": panelDoc = Doc.UserDoc()["sidebar-recentlyClosed"] as Doc ?? undefined; break;
- case "Settings": SettingsManager.Instance.open(); break;
- case "Import": panelDoc = Doc.UserDoc()["sidebar-import"] as Doc ?? undefined; break;
- case "Sharing": panelDoc = Doc.UserDoc()["sidebar-sharing"] as Doc ?? undefined; break;
- case "User Doc": panelDoc = Doc.UserDoc()["sidebar-userDoc"] as Doc ?? undefined; break;
+ default:
+ panelDoc = button.target as any; break;
}
this.sidebarContent.proto = panelDoc;
if (panelDoc) {
@@ -723,6 +728,7 @@ export class MainView extends React.Component {
focus={emptyFunction}
whenActiveChanged={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
</div>;
@@ -795,6 +801,7 @@ export class MainView extends React.Component {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
/></div>
@@ -828,6 +835,7 @@ export class MainView extends React.Component {
NativeWidth={() => 800}
ContentScaling={returnOne}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
/>
</div>;
}
@@ -898,6 +906,7 @@ export class MainView extends React.Component {
NativeWidth={() => 800}
ContentScaling={returnOne}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
/>
</div>;
</span>, ele);
@@ -997,12 +1006,13 @@ export class MainView extends React.Component {
}
Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; });
-Scripting.addGlobal(function copyWorkspace() {
- const copiedWorkspace = Doc.MakeCopy(Cast(Doc.UserDoc().activeWorkspace, Doc, null), true);
- const workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc, null);
- Doc.AddDocToList(workspaces, "data", copiedWorkspace);
- // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
- setTimeout(() => MainView.Instance.openWorkspace(copiedWorkspace), 0);
+Scripting.addGlobal(function copyDashboard() {
+ const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null);
+ CollectionDockingView.Copy(activeDashboard).then(copy => {
+ Doc.AddDocToList(Cast(Doc.UserDoc().myDashboards, Doc, null), "data", copy);
+ // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
+ setTimeout(() => MainView.Instance.openDashboard(copy), 0);
+ });
});
Scripting.addGlobal(function importDocument() { return MainView.Instance.importDocument(); },
"imports files from device directly into the import sidebar");
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 49580cde4..001135340 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -4,7 +4,7 @@ import * as React from "react";
import { Doc, DocListCast, Opt } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
import { NumCast, Cast } from "../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents, returnEmptyFilter } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents, returnEmptyFilter, returnEmptyDoclist } from "../../Utils";
import { Transform } from "../util/Transform";
import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
import { DocumentView } from "./nodes/DocumentView";
@@ -205,6 +205,7 @@ export class OverlayView extends React.Component {
addDocTab={returnFalse}
pinToPres={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
</div>;
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 0a4334302..92c3f09b4 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import * as React from "react";
import { Doc } from "../../fields/Doc";
import { NumCast } from "../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue, returnEmptyFilter } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue, returnEmptyFilter, returnEmptyDoclist } from "../../Utils";
import { Transform } from "../util/Transform";
import { DocumentView } from "./nodes/DocumentView";
import "./Palette.scss";
@@ -60,6 +60,7 @@ export default class Palette extends React.Component<PaletteProps> {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
<div className="palette-cover" style={{ transform: `translate(${Math.max(0, this._selectedIndex) * 50.75 + 23}px, 0px)` }}></div>
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 0eee46275..5d8f30354 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -92,6 +92,7 @@ export class PreviewCursor extends React.Component<{}> {
_width: 500,
limitHeight: 400,
_autoHeight: true,
+ _showTitle: Doc.UserDoc().showTitle ? "title" : undefined,
x: newPoint[0],
y: newPoint[1],
title: "-pasted text-"
diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
index 8d9d56c9e..d63eb874c 100644
--- a/src/client/views/PropertiesButtons.scss
+++ b/src/client/views/PropertiesButtons.scss
@@ -43,6 +43,13 @@ $linkGap : 3px;
cursor: pointer;
}
}
+.propertiesButtons-linkButton-empty.toggle-on {
+ background-color: white;
+ color: black;
+}
+.propertiesButtons-linkButton-empty.toggle-off {
+ color: white;
+}
.propertiesButtons {
margin-top: 3px;
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index ae57560d0..9839222ec 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -219,18 +219,11 @@ export class PropertiesButtons extends React.Component<{}, {}> {
const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top">
<div>
- <div className="propertiesButtons-linker"
- style={{ backgroundColor: isPinned ? "white" : "", color: isPinned ? "black" : "white" }}
- onClick={e => DockedFrameRenderer.PinDoc(targetDoc, isPinned)}>
+ <div className={`propertiesButtons-linkButton-empty toggle-${isPinned ? "on" : "off"}`} onClick={e => DockedFrameRenderer.PinDoc(targetDoc, isPinned)}>
<FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-pin" />
</div>
- <div className="propertiesButtons-title"
- // style={{
- // backgroundColor: Doc.isDocPinned(targetDoc) ? "white" : "black",
- // color: Doc.isDocPinned(targetDoc) ? "black" : "white"
- // }}
- >{Doc.isDocPinned(targetDoc) ? "Unpin" : "Pin"}</div>
+ <div className="propertiesButtons-title">{Doc.isDocPinned(targetDoc) ? "Unpin" : "Pin"}</div>
</div>
</Tooltip>;
}
@@ -319,13 +312,13 @@ export class PropertiesButtons extends React.Component<{}, {}> {
Array.from(Object.values(Templates.TemplateList)).map(template =>
templates.set(template, views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean)));
return !docView ? (null) :
- <Tooltip title={<><div className="dash-tooltip">Customize layout</div></>} placement="top">
+ <Tooltip title={<div className="dash-tooltip">Customize layout</div>} placement="top">
<div className="propertiesButtons-linkFlyout">
<Flyout anchorPoint={anchorPoints.LEFT_TOP} //onOpen={action(() => this._aliasDown = true)} onClose={action(() => this._aliasDown = false)}
content={<TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}>
<div>
<div className={"propertiesButtons-linkButton-empty"} >
- {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="lg" />}
+ <FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="lg" />
</div>
<div className="propertiesButtons-title">Layout</div>
</div>
@@ -350,13 +343,13 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@computed
get copyButton() {
const targetDoc = this.selectedDoc;
- return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Tap or Drag to create an alias"}</div></>} placement="top">
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Tap or Drag to create an alias"}</div>} placement="top">
<div>
<div className={"propertiesButtons-linkButton-empty"}
ref={this._dragRef}
onPointerDown={this.onAliasButtonDown}
onClick={this.onCopy}>
- {<FontAwesomeIcon className="documentdecorations-icon" icon="copy" size="lg" />}
+ <FontAwesomeIcon className="documentdecorations-icon" icon="copy" size="lg" />
</div>
<div className="propertiesButtons-title">Alias</div>
</div>
@@ -373,21 +366,14 @@ export class PropertiesButtons extends React.Component<{}, {}> {
get lockButton() {
const targetDoc = this.selectedDoc;
return !targetDoc ? (null) : <Tooltip
- title={<><div className="dash-tooltip">{this.selectedDoc?.lockedPosition ?
- "Unlock Position" : "Lock Position"}</div></>} placement="top">
+ title={<div className="dash-tooltip">{`${this.selectedDoc?.lockedPosition ? "Unlock" : "Lock"} " Position"`}</div>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- style={{ backgroundColor: BoolCast(this.selectedDoc?.lockedPosition) ? "white" : "" }}
- onPointerDown={this.onLock} >
- {<FontAwesomeIcon className="documentdecorations-icon"
- color={BoolCast(this.selectedDoc?.lockedPosition) ? "black" : "white"}
- icon={BoolCast(this.selectedDoc?.lockedPosition) ? "unlock" : "lock"} size="lg" />}
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc.lockedPosition ? "on" : "off"}`} onPointerDown={this.onLock} >
+ <FontAwesomeIcon className="documentdecorations-icon" size="lg"
+ color={this.selectedDoc?.lockedPosition ? "black" : "white"}
+ icon={this.selectedDoc?.lockedPosition ? "unlock" : "lock"} />
</div>
<div className="propertiesButtons-title"
- // style={{
- // backgroundColor: BoolCast(this.selectedDoc?.lockedPosition) ? "white" : "black",
- // color: BoolCast(this.selectedDoc?.lockedPosition) ? "black" : "white"
- // }}
>Position </div>
</div>
</Tooltip>;
@@ -397,16 +383,10 @@ export class PropertiesButtons extends React.Component<{}, {}> {
get downloadButton() {
const targetDoc = this.selectedDoc;
return !targetDoc ? (null) : <Tooltip
- title={<><div className="dash-tooltip">{"Download Document"}</div></>} placement="top">
+ title={<div className="dash-tooltip">{"Download Document"}</div>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- onPointerDown={async () => {
- if (this.selectedDoc) {
- Doc.Zip(this.selectedDoc);
- }
- }}>
- {<FontAwesomeIcon className="propertiesButtons-icon"
- icon="download" size="lg" />}
+ <div className={"propertiesButtons-linkButton-empty"} onPointerDown={() => this.selectedDoc && Doc.Zip(this.selectedDoc)}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="download" size="lg" />
</div>
<div className="propertiesButtons-title"> downld </div>
</div>
@@ -416,13 +396,10 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@computed
get deleteButton() {
const targetDoc = this.selectedDoc;
- return !targetDoc ? (null) : <Tooltip
- title={<><div className="dash-tooltip">Close Document</div></>} placement="top">
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">Close Document</div>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- onPointerDown={this.deleteDocument}>
- {<FontAwesomeIcon className="propertiesButtons-icon"
- icon="times" size="lg" />}
+ <div className={"propertiesButtons-linkButton-empty"} onPointerDown={this.deleteDocument}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="times" size="lg" />
</div>
<div className="propertiesButtons-title"> close </div>
</div>
@@ -437,21 +414,91 @@ export class PropertiesButtons extends React.Component<{}, {}> {
SelectionManager.DeselectAll();
}
+ @undoBatch
+ @action
+ setDictation = () => {
+ this.selectedDoc && (this.selectedDoc._showAudio = !this.selectedDoc._showAudio);
+ }
+
+ @computed
+ get dictationButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Dictation Controls"}</div>} placement="top">
+ <div>
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._showAudio ? "on" : "off"}`} onPointerDown={this.setDictation}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="microphone" size="lg" />
+ </div>
+ <div className="propertiesButtons-title"> Dictate </div>
+ </div>
+ </Tooltip>;
+ }
+
+
+ @undoBatch
+ @action
+ setTitle = () => {
+ this.selectedDoc && (this.selectedDoc._showTitle = this.selectedDoc._showTitle === undefined ? "title" : undefined);
+ }
+
+ @computed
+ get titleButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Title Header"}</div>} placement="top">
+ <div>
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._showTitle ? "on" : "off"}`} onPointerDown={this.setTitle}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="text-width" size="lg" />
+ </div>
+ <div className="propertiesButtons-title"> Title </div>
+ </div>
+ </Tooltip>;
+ }
+
+ @undoBatch
+ @action
+ setCaption = () => {
+ this.selectedDoc && (this.selectedDoc._showCaption = this.selectedDoc._showCaption === undefined ? "caption" : undefined);
+ }
+
+ @computed
+ get captionButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Caption Footer"}</div>} placement="top">
+ <div>
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._showCaption ? "on" : "off"}`} onPointerDown={this.setCaption}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="closed-captioning" size="lg" />
+ </div>
+ <div className="propertiesButtons-title"> Caption </div>
+ </div>
+ </Tooltip>;
+ }
+
+ @undoBatch
+ @action
+ setChrome = () => {
+ this.selectedDoc && (this.selectedDoc._chromeStatus = this.selectedDoc._chromeStatus === "disabled" ? "enabled" : "disabled");
+ }
+
+ @computed
+ get chromeButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Editing UI"}</div>} placement="top">
+ <div>
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._chromeStatus === "enabled" ? "on" : "off"}`} onPointerDown={this.setChrome}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="edit" size="lg" />
+ </div>
+ <div className="propertiesButtons-title"> Controls </div>
+ </div>
+ </Tooltip>;
+ }
+
@computed
get sharingButton() {
const targetDoc = this.selectedDoc;
const docView = this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined;
- return !targetDoc ? (null) : <Tooltip
- title={<><div className="dash-tooltip">{"Share Document"}</div></>} placement="top">
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Share Document"}</div>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- onPointerDown={() => {
- if (this.selectedDocumentView) {
- SharingManager.Instance.open(docView, this.selectedDoc);
- }
- }}>
- {<FontAwesomeIcon className="propertiesButtons-icon"
- icon="users" size="lg" />}
+ <div className={"propertiesButtons-linkButton-empty"} onPointerDown={() => this.selectedDocumentView && SharingManager.Instance.open(docView, this.selectedDoc)}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="users" size="lg" />
</div>
<div className="propertiesButtons-title"> share </div>
</div>
@@ -564,13 +611,8 @@ export class PropertiesButtons extends React.Component<{}, {}> {
title={<><div className="dash-tooltip">{"Export to Google Photos"}</div></>} placement="top">
<div>
<div className={"propertiesButtons-linkButton-empty"}
- onPointerDown={() => {
- if (this.selectedDoc) {
- GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDoc }).then(console.log);
- }
- }}>
- {<FontAwesomeIcon className="documentdecorations-icon"
- icon="cloud-upload-alt" size="lg" />}
+ onPointerDown={() => this.selectedDoc && GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDoc }).then(console.log)}>
+ {<FontAwesomeIcon className="documentdecorations-icon" icon="cloud-upload-alt" size="lg" />}
</div>
<div className="propertiesButtons-title"> google </div>
</div>
@@ -583,19 +625,10 @@ export class PropertiesButtons extends React.Component<{}, {}> {
return !targetDoc ? (null) : <Tooltip
title={<><div className="dash-tooltip">{this.selectedDoc?.useClusters ? "Stop Showing Clusters" : "Show Clusters"}</div></>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- style={{ backgroundColor: this.selectedDoc?.useClusters ? "white" : "" }}
- onPointerDown={this.changeClusters}>
- {<FontAwesomeIcon className="documentdecorations-icon"
- color={this.selectedDoc?.useClusters ? "black" : "white"}
- icon="braille" size="lg" />}
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc.userClusters ? "on" : "off"}`} onPointerDown={this.changeClusters}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="braille" size="lg" />
</div>
- <div className="propertiesButtons-title"
- // style={{
- // backgroundColor: this.selectedDoc?.useClusters ? "white" : "black",
- // color: this.selectedDoc?.useClusters ? "black" : "white"
- // }}
- > clusters </div>
+ <div className="propertiesButtons-title" > clusters </div>
</div>
</Tooltip>;
}
@@ -616,19 +649,10 @@ export class PropertiesButtons extends React.Component<{}, {}> {
return !targetDoc ? (null) : <Tooltip
title={<><div className="dash-tooltip">{this.selectedDoc?._fitToBox ? "Stop Fitting Content" : "Fit Content"}</div></>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- style={{ backgroundColor: this.selectedDoc?._fitToBox ? "white" : "" }}
- onPointerDown={this.changeFitToBox}>
- {<FontAwesomeIcon className="documentdecorations-icon"
- color={this.selectedDoc?._fitToBox ? "black" : "white"}
- icon="expand" size="lg" />}
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._fitToBox ? "on" : "off"}`} onPointerDown={this.changeFitToBox}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="expand" size="lg" />
</div>
- <div className="propertiesButtons-title"
- // style={{
- // backgroundColor: this.selectedDoc?._fitToBox ? "white" : "black",
- // color: this.selectedDoc?._fitToBox ? "black" : "white"
- // }}
- > {this.selectedDoc?._fitToBox ? "unfit" : "fit"} </div>
+ <div className="propertiesButtons-title"> {this.selectedDoc?._fitToBox ? "unfit" : "fit"} </div>
</div>
</Tooltip>;
}
@@ -651,10 +675,8 @@ export class PropertiesButtons extends React.Component<{}, {}> {
return !targetDoc ? (null) : <Tooltip
title={<><div className="dash-tooltip">Make Mask</div></>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- onPointerDown={this.makeMask}>
- {<FontAwesomeIcon className="documentdecorations-icon"
- color="white" icon="paint-brush" size="lg" />}
+ <div className={"propertiesButtons-linkButton-empty"} onPointerDown={this.makeMask}>
+ <FontAwesomeIcon className="documentdecorations-icon" color="white" icon="paint-brush" size="lg" />
</div>
<div className="propertiesButtons-title"> mask </div>
</div>
@@ -714,9 +736,9 @@ export class PropertiesButtons extends React.Component<{}, {}> {
const collectionAcl = GetEffectiveAcl(this.selectedDocumentView?.props.ContainingCollectionDoc?.[DataSym]);
return <div><div className="propertiesButtons" style={{ paddingBottom: "5.5px" }}>
- <div className="propertiesButtons-button">
+ {/* <div className="propertiesButtons-button">
{this.templateButton}
- </div>
+ </div> */}
{/* <div className="propertiesButtons-button">
{this.metadataButton}
</div> */}
@@ -730,8 +752,20 @@ export class PropertiesButtons extends React.Component<{}, {}> {
{this.copyButton}
</div>
<div className="propertiesButtons-button">
+ {this.titleButton}
+ </div>
+ <div className="propertiesButtons-button">
+ {this.captionButton}
+ </div>
+ <div className="propertiesButtons-button" style={{ display: isCollection ? "" : "none" }}>
+ {this.chromeButton}
+ </div>
+ <div className="propertiesButtons-button">
{this.lockButton}
</div>
+ <div className="propertiesButtons-button" style={{ display: isText || isImage ? "" : "none" }}>
+ {this.dictationButton}
+ </div>
<div className="propertiesButtons-button">
{this.downloadButton}
</div>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index eb20fc257..870af03aa 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -10,7 +10,7 @@ import { Doc, DocListCast } from "../../fields/Doc";
import { Docs, DocUtils, } from "../documents/Documents";
import { StrCast, Cast } from "../../fields/Types";
import { CollectionTreeView } from "./collections/CollectionTreeView";
-import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero, returnEmptyFilter } from "../../Utils";
+import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero, returnEmptyFilter, returnEmptyDoclist } from "../../Utils";
import { Transform } from "../util/Transform";
import { ScriptField, ComputedField } from "../../fields/ScriptField";
import { Scripting } from "../util/Scripting";
@@ -116,9 +116,9 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
const addedTypes = DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
const layout = Doc.Layout(firstDoc);
const templateMenu: Array<JSX.Element> = [];
- this.props.templates.forEach((checked, template) =>
- templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />));
- templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
+ //this.props.templates.forEach((checked, template) =>
+ // templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />));
+ //templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={layout._chromeStatus !== "disabled"} toggle={this.toggleChrome} />);
templateMenu.push(<OtherToggle key={"default"} name={"Default"} checked={templateName === "layout"} toggle={this.toggleDefault} />);
addedTypes.concat(noteTypes).map(template => template.treeViewChecked = this.templateIsUsed(firstDoc, template));
@@ -133,6 +133,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
rootSelected={returnFalse}
onCheckedClick={this.scriptField}
onChildClick={this.scriptField}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 761d88989..f1959460c 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,44 +1,43 @@
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, computed, Lambda, observable, reaction, runInAction, trace, IReactionDisposer } from "mobx";
+import { clamp } from 'lodash';
+import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
import * as GoldenLayout from "../../../client/goldenLayout";
import { DateField } from '../../../fields/DateField';
-import { Doc, DocListCast, Field, Opt, DataSym } from "../../../fields/Doc";
+import { DataSym, Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
import { Id } from '../../../fields/FieldSymbols';
+import { InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
import { FieldId } from "../../../fields/RefField";
+import { listSpec } from '../../../fields/Schema';
import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnOne, returnTrue, Utils, returnZero, returnEmptyFilter, setupMoveUpEvents, returnFalse, emptyPath, aggregateBounds } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils, returnEmptyDoclist } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Docs } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType } from "../../util/DragManager";
+import { InteractionUtils } from '../../util/InteractionUtils';
import { Scripting } from '../../util/Scripting';
import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch } from "../../util/UndoManager";
import { MainView } from '../MainView';
import { DocumentView } from "../nodes/DocumentView";
+import { PresBox } from '../nodes/PresBox';
import "./CollectionDockingView.scss";
-import { SubCollectionViewProps } from "./CollectionSubView";
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+import { SubCollectionViewProps, CollectionSubView } from "./CollectionSubView";
+import { CollectionViewType } from './CollectionView';
import { DockingViewButtonSelector } from './ParentDocumentSelector';
import React = require("react");
-import { CollectionViewType } from './CollectionView';
-import { SnappingManager } from '../../util/SnappingManager';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import { listSpec } from '../../../fields/Schema';
-import { clamp } from 'lodash';
-import { PresBox } from '../nodes/PresBox';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { InteractionUtils } from '../../util/InteractionUtils';
-import { InkTool } from '../../../fields/InkField';
-import { Select } from '@material-ui/core';
const _global = (window /* browser */ || global /* node */) as any;
@observer
-export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
+export class CollectionDockingView extends CollectionSubView(doc => doc) {
@observable public static Instances: CollectionDockingView[] = [];
@computed public static get Instance() { return CollectionDockingView.Instances[0]; }
public static makeDocumentConfig(document: Doc, width?: number, libraryPath?: Doc[]) {
@@ -74,22 +73,13 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
DragManager.StartWindowDrag = this.StartOtherDrag;
}
public StartOtherDrag = (e: any, dragDocs: Doc[]) => {
- let config: any;
- if (dragDocs.length === 1) {
- config = CollectionDockingView.makeDocumentConfig(dragDocs[0]);
- } else {
- config = {
+ const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) :
+ {
type: 'row',
- content: dragDocs.map((doc, i) => {
- CollectionDockingView.makeDocumentConfig(doc);
- })
+ content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc))
};
- }
- const div = document.createElement("div");
- const dragSource = this._goldenLayout.createDragSource(div, config);
- dragSource._dragListener.on("dragStop", () => {
- dragSource.destroy();
- });
+ const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config);
+ dragSource._dragListener.on("dragStop", () => dragSource.destroy());
dragSource._dragListener.onMouseDown(e);
}
@@ -97,7 +87,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@action
public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") {
- return MainView.Instance.openWorkspace(docView.props.Document);
+ return MainView.Instance.openDashboard(docView.props.Document);
}
const document = Doc.MakeAlias(docView.props.Document);
const newItemStackConfig = {
@@ -155,7 +145,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
this._goldenLayout.emit('stateChanged');
this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
- if (removed) CollectionDockingView.Instance._removedDocs.push(removed);
this.stateChanged();
}
@undoBatch
@@ -411,8 +400,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
// Because this is in a set timeout, if this component unmounts right after mounting,
// we will leak a GoldenLayout, because we try to destroy it before we ever create it
setTimeout(() => this.setupGoldenLayout(), 1);
- DocListCast((Doc.UserDoc().myWorkspaces as Doc).data).map(d => d.workspaceBrush = false);
- this.props.Document.workspaceBrush = true;
+ DocListCast((Doc.UserDoc().myDashboards as Doc).data).map(d => d.dashboardBrush = false);
+ this.props.Document.dashboardBrush = true;
}
this._ignoreStateChange = "";
}, { fireImmediately: true });
@@ -422,7 +411,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
componentWillUnmount: () => void = () => {
try {
- this.props.Document.workspaceBrush = false;
+ this.props.Document.dashboardBrush = false;
this._goldenLayout.unbind('itemDropped', this.itemDropped);
this._goldenLayout.unbind('tabCreated', this.tabCreated);
this._goldenLayout.unbind('stackCreated', this.stackCreated);
@@ -442,9 +431,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@action
onResize = (event: any) => {
const cur = this._containerRef.current;
-
// bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed
- this._goldenLayout?.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height);
+ cur && this._goldenLayout?.updateSize(cur.getBoundingClientRect().width, cur.getBoundingClientRect().height);
}
@action
@@ -480,11 +468,38 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
- if (docids) {
- const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
- docs.map(doc => Doc.AddDocToList(Doc.GetProto(this.props.Document), this.props.fieldKey, doc));
- // Doc.GetProto(this.props.Document)[this.props.fieldKey] = new List<Doc>(docs);
- }
+ const docs = !docids ? [] : (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
+ const sublists = DocListCast(this.props.Document[this.props.fieldKey]);
+ const tabs = Cast(sublists[0], Doc, null);
+ const other = Cast(sublists[1], Doc, null);
+ const tabdocs = DocListCast(tabs.data);
+ const otherdocs = DocListCast(other.data);
+ Doc.GetProto(tabs).data = new List<Doc>(docs);
+ const otherSet = new Set<Doc>();
+ otherdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+ tabdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+ Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values()));
+ }
+
+ public static async Copy(doc: Doc) {
+ let json = StrCast(doc.dockingConfig);
+ const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+ const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) || [];
+ const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
+ const newtabs = docs.map(doc => {
+ const copy = Doc.MakeAlias(doc);
+ json = json.replace(doc[Id], copy[Id]);
+ return copy;
+ });
+ const copy = Docs.Create.DockDocument(newtabs, json, { title: "Snapshot: " + doc.title });
+ const docsublists = DocListCast(doc.data);
+ const copysublists = DocListCast(copy.data);
+ const docother = Cast(docsublists[1], Doc, null);
+ const copyother = Cast(copysublists[1], Doc, null);
+ const newother = DocListCast(docother.data).map(doc => Doc.MakeAlias(doc));
+ Doc.GetProto(copyother).data = new List<Doc>(newother);
+
+ return copy;
}
@undoBatch
@@ -558,6 +573,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}, returnFalse, emptyFunction);
};
+ tab.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments(),
+ (sel) => {
+ const selected = sel.some(v => v.props.Document === doc);
+ selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && tab.header.parent.setActiveContentItem(tab.contentItem);
+ }
+ );
tab.buttonDisposer = reaction(() => ((view: Opt<DocumentView>) => view ? [view] : [])(DocumentManager.Instance.getDocumentView(doc)),
(views) => {
if (views.length) {
@@ -582,14 +603,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
tab.closeElement.off('click') //unbind the current click handler
.click(async function () {
+ tab.selectionDisposer?.();
tab.reactionDisposer?.();
tab.buttonDisposer?.();
const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId);
if (doc instanceof Doc) {
const theDoc = doc;
- CollectionDockingView.Instance._removedDocs.push(theDoc);
- const recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc);
+ const recent = await Cast(Doc.UserDoc().myInactiveDocs, Doc);
if (recent) {
Doc.AddDocToList(recent, "data", doc, undefined, true, true);
}
@@ -608,13 +629,16 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
}
}
- _removedDocs: Doc[] = [];
stackCreated = (stack: any) => {
//stack.header.controlsContainer.find('.lm_popout').hide();
stack.header.element.on('mousedown', (e: any) => {
if (e.target === stack.header.element[0] && e.button === 1) {
- this.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: "Untitled Collection" }));
+ const emptyPane = Cast(Doc.UserDoc().emptyPane, Doc, null);
+ emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+ this.AddTab(stack, Docs.Create.FreeformDocument([], {
+ _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
+ }));
}
});
@@ -649,11 +673,10 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
const doc = await DocServer.GetRefField(contentItem.config.props.documentId);
if (doc instanceof Doc) {
let recent: Doc | undefined;
- if (recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc)) {
+ if (recent = await Cast(Doc.UserDoc().myInactiveDocs, Doc)) {
Doc.AddDocToList(recent, "data", doc, undefined, true, true);
}
const theDoc = doc;
- CollectionDockingView.Instance._removedDocs.push(theDoc);
}
});
//}
@@ -669,7 +692,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
render() {
if (this.props.renderDepth > 0) {
- return <div style={{ width: "100%", height: "100%" }}>Nested workspaces can't be rendered</div>;
+ return <div style={{ width: "100%", height: "100%" }}>Nested dashboards can't be rendered</div>;
}
return <div className="collectiondockingview-container" id="menuContainer"
onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} />;
@@ -755,35 +778,32 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
componentDidMount() {
+ const color = () => StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white");
+ const selected = () => SelectionManager.SelectedDocuments().some(v => Doc.AreProtosEqual(v.props.Document, this._document));
+ const updateTabColor = () => this._tab && (this._tab.style.backgroundColor = selected() ? color() : "");
const observer = new _global.ResizeObserver(action((entries: any) => {
for (const entry of entries) {
this._panelWidth = entry.contentRect.width;
this._panelHeight = entry.contentRect.height;
}
+ updateTabColor();
}));
observer.observe(this.props.glContainer._element[0]);
this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
- this.props.glContainer.on("tab", this.onActiveContentItemChanged);
- this.onActiveContentItemChanged();
- this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white") }),
- (data) => {
- const selected = data.views.some(v => Doc.AreProtosEqual(v.props.Document, this._document));
- this._tab && (this._tab.style.backgroundColor = selected ? data.color : "");
- }
- );
+ this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged();
+ this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: color() }), () => updateTabColor(), { fireImmediately: true });
}
componentWillUnmount() {
this._tabReaction?.();
this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
- this.props.glContainer.off("tab", this.onActiveContentItemChanged);
}
@action.bound
private onActiveContentItemChanged() {
- if (this.props.glContainer.tab) {
+ if (this.props.glContainer.tab && this._isActive !== this.props.glContainer.tab.isActive) {
this._isActive = this.props.glContainer.tab.isActive;
- setTimeout(() => {
+ this._isActive && setTimeout(() => {
const dv = this._document && DocumentManager.Instance.getFirstDocumentView(this._document);
dv && SelectionManager.SelectDoc(dv, false);
});
@@ -833,12 +853,12 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
return Transform.Identity();
}
get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
- get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}%` : undefined; }
+ get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}% ` : undefined; }
addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
SelectionManager.DeselectAll();
if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
- return MainView.Instance.openWorkspace(doc);
+ return MainView.Instance.openDashboard(doc);
} else if (location === "onRight") {
return CollectionDockingView.AddRightSplit(doc, libraryPath);
} else if (location === "close") {
@@ -914,15 +934,16 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
addDocTab={this.addDocTab}
pinToPres={DockedFrameRenderer.PinDoc}
- docFilters={returnEmptyFilter}
+ docFilters={CollectionDockingView.Instance.docFilters}
+ searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
fitToBox={true}
/>
<div className="miniOverlay" onPointerDown={this.miniDown} >
<div className="miniThumb" style={{
- width: `${this.miniWidth}%`,
- height: `${this.miniHeight}%`,
- left: `${this.miniLeft}%`,
- top: `${this.miniTop}%`,
+ width: `${this.miniWidth}% `,
+ height: `${this.miniHeight}% `,
+ left: `${this.miniLeft}% `,
+ top: `${this.miniTop}% `,
}}
/>
</div>
@@ -955,7 +976,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
addDocTab={this.addDocTab}
pinToPres={DockedFrameRenderer.PinDoc}
- docFilters={returnEmptyFilter}
+ docFilters={CollectionDockingView.Instance.docFilters}
+ searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
{document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index e1b07077e..866d7245a 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -89,6 +89,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
}
}
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
}
@action
@@ -166,6 +167,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
</div>;
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index c772dcfe7..1c96f69bf 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -140,7 +140,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
addDocument = (value: string, shiftDown?: boolean) => {
this._createAliasSelected = false;
const key = StrCast(this.props.parent.props.Document._pivotField);
- const newDoc = Docs.Create.TextDocument(value, { _autoHeight: true, _width: 200, title: value });
+ const newDoc = Docs.Create.TextDocument(value, { _autoHeight: true, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, title: value });
newDoc[key] = this.getValue(this.props.heading);
const docs = this.props.parent.childDocList;
return docs ? (docs.splice(0, 0, newDoc) ? true : false) : this.props.parent.props.addDocument(newDoc);
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 388eda2b3..00415dab1 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -131,7 +131,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
_contentCommand = {
params: ["target", "source"], title: "set content",
script: "getProto(self.target).data = copyField(self.source);",
- immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)), // Doc.aliasDocs(source),
+ immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)),
initialize: emptyFunction,
};
_onClickCommand = {
@@ -180,12 +180,16 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
};
_saveFilterCommand = {
params: ["target"], title: "save filter",
- script: "self.target._docFilters = copyField(self['target-docFilters']);",
- immediate: undoBatch((source: Doc[]) => this.target._docFilters = undefined),
- initialize: (button: Doc) => { button['target-docFilters'] = this.target._docFilters instanceof ObjectField ? ObjectField.MakeCopy(this.target._docFilters as any as ObjectField) : ""; },
+ script: `self.target._docFilters = compareLists(self['target-docFilters'],self.target._docFilters) ? undefined : copyField(self['target-docFilters']);
+ self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
+ immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
+ initialize: (button: Doc) => {
+ button['target-docFilters'] = Cast(Doc.UserDoc()["search-panel"], Doc, null)._docFilters instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc()["search-panel"], Doc, null)._docFilters as any as ObjectField) : undefined;
+ button['target-searchFilterDocs'] = Cast(Doc.UserDoc()["search-panel"], Doc, null)._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc()["search-panel"], Doc, null)._searchFilterDocs as any as ObjectField) : undefined;
+ },
};
- @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
+ @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
@computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
@computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
@computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index ea786800c..e37644f2f 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -3,7 +3,7 @@ import { action, observable, trace, computed, runInAction } from "mobx";
import { observer } from "mobx-react";
import { CellInfo } from "react-table";
import "react-table/react-table.css";
-import { emptyFunction, returnFalse, returnZero, returnOne, returnEmptyFilter, Utils, emptyPath } from "../../../Utils";
+import { emptyFunction, returnFalse, returnZero, returnOne, returnEmptyFilter, Utils, emptyPath, returnEmptyDoclist } from "../../../Utils";
import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { KeyCodes } from "../../util/KeyCodes";
@@ -199,6 +199,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
rootSelected: returnFalse,
fieldKey: this.props.rowProps.column.id as string,
docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
ContainingCollectionView: this.props.CollectionView,
ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
isSelected: returnFalse,
@@ -229,7 +230,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc);
const onItemDown = async (e: React.PointerEvent) => {
- if (this.props.Document._searchDoc !== undefined) {
+ if (this.props.Document._searchDoc) {
const doc = Doc.GetProto(this.props.rowProps.original);
const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc);
let targetContext = undefined;
@@ -315,7 +316,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
}
}
let search = false;
- if (this.props.Document._searchDoc !== undefined) {
+ if (this.props.Document._searchDoc) {
search = true;
}
@@ -511,7 +512,8 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
addDocTab: this.props.addDocTab,
pinToPres: this.props.pinToPres,
ContentScaling: returnOne,
- docFilters: returnEmptyFilter
+ docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
};
@observable private _field = this.prop.Document[this.prop.fieldKey];
@observable private _doc = FieldValue(Cast(this._field, Doc));
@@ -691,7 +693,8 @@ export class CollectionSchemaImageCell extends CollectionSchemaCell {
addDocTab: this.props.addDocTab,
pinToPres: this.props.pinToPres,
ContentScaling: returnOne,
- docFilters: returnEmptyFilter
+ docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
};
let image = true;
@@ -770,7 +773,8 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
addDocTab: this.props.addDocTab,
pinToPres: this.props.pinToPres,
ContentScaling: returnOne,
- docFilters: returnEmptyFilter
+ docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
};
@observable private _field = this.prop.Document[this.prop.fieldKey];
@observable private _optionsList = this._field as List<any>;
@@ -894,26 +898,22 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
export class CollectionSchemaButtons extends CollectionSchemaCell {
render() {
const doc = this.props.rowProps.original;
- const searchMatch = (backward: boolean = true) => { doc.searchMatch = undefined; setTimeout(() => doc.searchMatch = backward, 0); };
+ const searchMatch = (backward: boolean = true) => Doc.SearchMatchNext(doc, backward);
// const reference = React.createRef<HTMLDivElement>();
// const onItemDown = (e: React.PointerEvent) => {
// (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined :
// SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e));
// };
- return !BoolCast(this.props.Document._searchDoc) ? <></>
- : StrCast(doc.type) === DocumentType.PDF ?
- <button style={{ position: "relative", height: 30, width: 28, left: 1, }} onClick={() => searchMatch(false)}>
- <FontAwesomeIcon icon="arrow-down" size="sm" />
- </button>
- : StrCast(doc.type) === DocumentType.RTF ?
- <div style={{ paddingTop: 8, paddingLeft: 3, }} >
- <button style={{ padding: 2, left: 77 }} onClick={() => searchMatch(true)}>
- <FontAwesomeIcon icon="arrow-up" size="sm" />
- </button>
- <button style={{ padding: 2 }} onClick={() => searchMatch(false)} >
- <FontAwesomeIcon icon="arrow-down" size="sm" />
- </button>
- </div> :
- <></>;
+ return !this.props.Document._searchDoc ? <></>
+ : [DocumentType.PDF, DocumentType.RTF].includes(StrCast(doc.type) as DocumentType) ?
+ <div style={{ paddingTop: 8, paddingLeft: 3, }} >
+ <button style={{ padding: 2, left: 77 }} onClick={() => searchMatch(true)}>
+ <FontAwesomeIcon icon="arrow-up" size="sm" />
+ </button>
+ <button style={{ padding: 2 }} onClick={() => searchMatch(false)} >
+ <FontAwesomeIcon icon="arrow-down" size="sm" />
+ </button>
+ </div> :
+ <></>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 34a8bfa24..03a213002 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -287,12 +287,10 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
const colpos = this._searchTerm.indexOf(":");
const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
if (temp === "") {
- console.log("here we are first");
Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
this.updateFilter();
}
else {
- console.log("here we are first");
Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
this.tempfilter = temp;
Doc.setDocFilter(this.props.Document, this._key, temp, "check");
@@ -304,8 +302,8 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
this.updateFilter();
let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- const blockedkeys = ["system", "ACL-Public", "_scrollTop", "customTitle", "limitHeight", "proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"];
- keyOptions = keyOptions.filter(n => !blockedkeys.includes(n));
+ const blockedkeys = ["system", "title-custom", "limitHeight", "proto", "x", "y", "zIndex", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"];
+ keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("ACL"));
if (keyOptions.length) {
this.onSelect(keyOptions[0]);
} else if (this._searchTerm !== "" && this.props.canAddNew) {
@@ -338,8 +336,8 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
- const blockedkeys = ["proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"];
- keyOptions = keyOptions.filter(n => !blockedkeys.includes(n));
+ const blockedkeys = ["proto", "x", "y", "zIndex", "_timeStampOnEnter", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"];
+ keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("ACL"));
const options = keyOptions.map(key => {
return <div key={key} className="key-option" style={{
@@ -498,11 +496,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
}
});
- keyOptions.forEach(key => {
- Doc.setDocFilter(this.props.Document, this._key, key, undefined);
- }
- );
- Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
+ Doc.setDocFilter(this.props.Document, this._key, "", "remove");
this.props.col.setColor("rgb(241, 239, 235)");
this.closeResultsVisibility = "none";
}
@@ -518,13 +512,9 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
<div className="keys-dropdown" style={{ zIndex: 1, width: this.props.width, maxWidth: this.props.width }}>
<input className="keys-search" style={{ width: "100%" }}
ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown}
- onChange={e => {
- this.onChange(e.target.value);
- }}
- onClick={(e) => {
- //this._inputRef.current!.select();
- e.stopPropagation();
- }} onFocus={this.onFocus} ></input>
+ onChange={e => this.onChange(e.target.value)}
+ onClick={(e) => { e.stopPropagation(); this._inputRef.current?.focus(); }}
+ onFocus={this.onFocus} ></input>
<div style={{ display: this.closeResultsVisibility }}>
<FontAwesomeIcon onPointerDown={this.removeFilters} icon={"times-circle"} size="lg"
style={{ cursor: "hand", color: "grey", padding: 2, left: -20, top: -1, height: 15, position: "relative" }} />
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index fc5dffaec..49d9a5c68 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -99,6 +99,9 @@
direction: ltr;
overflow: visible;
}
+ .rt-noData {
+ display: none;
+ }
.rt-thead {
width: 100%;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index ed8496544..a78061da6 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -9,7 +9,7 @@ import { Resize } from "react-table";
import "react-table/react-table.css";
import { Doc, Opt } from "../../../fields/Doc";
import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
+import { listSpec, makeInterface, emptySchema } from "../../../fields/Schema";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { Cast, NumCast, BoolCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
@@ -73,7 +73,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@computed get menuCoordinates() {
let searchx = 0;
let searchy = 0;
- if (this.props.Document._searchDoc !== undefined) {
+ if (this.props.Document._searchDoc) {
const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0];
if (el !== undefined) {
const rect = el.getBoundingClientRect();
@@ -305,17 +305,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
this.columns = columns;
if (filter) {
Doc.setDocFilter(this.props.Document, newKey, filter, "match");
- if (this.props.Document.selectedDoc !== undefined) {
- const doc = Cast(this.props.Document.selectedDoc, Doc) as Doc;
- Doc.setDocFilter(doc, newKey, filter, "match");
- }
}
else {
this.props.Document._docFilters = undefined;
- if (this.props.Document.selectedDoc !== undefined) {
- const doc = Cast(this.props.Document.selectedDoc, Doc) as Doc;
- doc._docFilters = undefined;
- }
}
}
}
@@ -454,6 +446,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
PanelHeight={this.previewHeight}
ScreenToLocalTransform={this.getPreviewTransform}
docFilters={this.docFilters}
+ searchFilterDocs={this.searchFilterDocs}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
moveDocument={this.props.moveDocument}
@@ -592,7 +585,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
render() {
let name = "collectionSchemaView-container";
- if (this.props.Document._searchDoc !== undefined) {
+ if (this.props.Document._searchDoc) {
name = "collectionSchemaView-searchContainer";
}
if (!this.props.active()) setTimeout(() => this.closeHeader(), 0);
@@ -615,7 +608,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
return <div className={name}
style={{
overflow: this.props.overflow === true ? "scroll" : undefined, backgroundColor: "white",
- pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined,
+ pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined,
width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
}} >
<div className="collectionSchemaView-tableContainer"
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 241c64f9a..a50dab54d 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -217,6 +217,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
opacity={opacity}
focus={this.focusDocument}
docFilters={this.docFilters}
+ searchFilterDocs={this.searchFilterDocs}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
addDocument={this.props.addDocument}
@@ -354,7 +355,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
this.observer = new _global.ResizeObserver(action((entries: any) => {
if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
- Doc.Layout(doc)._height = Math.min(1200, Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
+ Doc.Layout(doc)._height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
}
}));
this.observer.observe(ref);
@@ -489,7 +490,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
transformOrigin: "top left",
}}
onScroll={action(e => {
- if (!this.props.isSelected() && this.props.renderDepth) e.currentTarget.scrollTop = this._scroll;
+ if (!this.props.isSelected(true) && this.props.renderDepth) e.currentTarget.scrollTop = this._scroll;
else this._scroll = e.currentTarget.scrollTop;
})}
onDrop={this.onExternalDrop.bind(this)}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index ede75fba8..0a206a6c6 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -136,7 +136,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
addDocument = (value: string, shiftDown?: boolean) => {
if (!value) return false;
const key = StrCast(this.props.parent.props.Document._pivotField);
- const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, title: value, _autoHeight: true });
+ const newDoc = Docs.Create.TextDocument(value, { _height: 18, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, title: value, _autoHeight: true });
newDoc[key] = this.getValue(this.props.heading);
const maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
@@ -269,7 +269,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
ContextMenu.Instance.setDefaultItem("::", (name: string): void => {
Doc.GetProto(this.props.parent.props.Document)[name] = "";
- const created = Docs.Create.TextDocument("", { title: name, _width: 250, _autoHeight: true });
+ const created = Docs.Create.TextDocument("", { title: name, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 250, _autoHeight: true });
if (created) {
if (this.props.parent.Document.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 075be41fd..cb3f486bb 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -111,6 +111,9 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
return this.props.ignoreFields?.includes("_docFilters") ? [] :
[...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
}
+ searchFilterDocs = () => {
+ return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)];
+ }
@computed get childDocs() {
let rawdocs: (Doc | Promise<Doc>)[] = [];
if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list;
@@ -128,47 +131,32 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
- let searchDocs = DocListCast(this.props.Document._searchDocs);
-
+ let searchDocs = this.searchFilterDocs();
let docsforFilter: Doc[] = childDocs;
- if (searchDocs !== undefined && searchDocs.length > 0) {
+ if (searchDocs.length > 0) {
+ searchDocs = [...searchDocs, ...docs.filter(d => d.z)];
docsforFilter = [];
const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
- console.log(searchDocs);
searchDocs = DocUtils.FilterDocs(searchDocs, this.docFilters(), docRangeFilters, viewSpecScript);
- console.log(this.docFilters());
- console.log(searchDocs);
childDocs.forEach((d) => {
+ let notFiltered = searchDocs.includes(d) || d.z;
if (d.data !== undefined) {
- let newdocs = DocListCast(d.data);
- if (newdocs.length > 0) {
- let displaycheck: boolean | undefined = undefined;
+ let subDocs = DocListCast(d.data);
+ if (subDocs.length > 0) {
let newarray: Doc[] = [];
- while (newdocs.length > 0) {
+ while (subDocs.length > 0 && !notFiltered) {
newarray = [];
- newdocs.forEach((t) => {
- if (d.data !== undefined) {
- const newdocs = DocListCast(t.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- if (searchDocs.includes(t)) {
- displaycheck = true;
- }
+ subDocs.forEach((t) => {
+ notFiltered = notFiltered || searchDocs.includes(t);
+ DocListCast(t.data).forEach((newdoc) => newarray.push(newdoc));
});
- newdocs = newarray;
- }
- if (displaycheck === true) {
- docsforFilter.push(d);
+ subDocs = newarray;
}
}
}
- if (searchDocs.includes(d)) {
- docsforFilter.push(d);
- }
+ notFiltered && docsforFilter.push(d);
});
return docsforFilter;
}
@@ -296,7 +284,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
this.addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
}
} else if (text) {
- this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 }));
+ this.addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 100, _height: 25 }));
}
return;
}
@@ -472,7 +460,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
completed?.();
} else {
if (text && !text.includes("https://")) {
- UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop");
+ UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop");
}
}
disposer();
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 0f6274663..9460095e4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,14 +1,14 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, DocListCastOrNull } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { PrefetchProxy } from '../../../fields/Proxy';
import { Document, listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter } from '../../../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from '../../util/DocumentManager';
@@ -88,23 +88,25 @@ class TreeView extends React.Component<TreeViewProps> {
get doc() { return this.props.document; }
get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive
- get defaultExpandedView() { return this.childDocs.length ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); }
+ get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
+ get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode ? "layout" : "fields"); }
+ get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
set treeViewOpen(c: boolean) {
if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
}
@computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
- @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); }
+ @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
@computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
@computed get dataDoc() { return this.doc[DataSym]; }
@computed get layoutDoc() { return Doc.Layout(this.doc); }
@computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
childDocList(field: string) {
const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
- return ((this.props.dataDoc ? DocListCast(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
- (layout ? DocListCast(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
- DocListCast(this.doc[field])); // otherwise use the document's data field
+ return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
+ (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
+ DocListCastOrNull(this.doc[field])); // otherwise use the document's data field
}
@computed get childDocs() { return this.childDocList(this.fieldKey); }
@computed get childLinks() { return this.childDocList("links"); }
@@ -357,6 +359,7 @@ class TreeView extends React.Component<TreeViewProps> {
focus={returnFalse}
ScreenToLocalTransform={this.docTransform}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={this.props.containingCollection}
ContainingCollectionView={undefined}
addDocument={returnFalse}
@@ -422,11 +425,12 @@ class TreeView extends React.Component<TreeViewProps> {
<span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
onPointerDown={action(() => {
if (this.treeViewOpen) {
- this.doc.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
- this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
- this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
- (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
- this.childDocs.length ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
+ this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView :
+ this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
+ this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
+ this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
+ (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
+ this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
}
this.treeViewOpen = true;
})}>
@@ -463,15 +467,16 @@ class TreeView extends React.Component<TreeViewProps> {
bringToFront={emptyFunction}
dontRegisterView={BoolCast(this.props.treeViewDoc.dontRegisterChildViews)}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={this.props.containingCollection}
/>;
return <>
<div className="docContainer" ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
style={{
- fontWeight: this.doc.searchMatch !== undefined ? "bold" : undefined,
+ fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
- outline: BoolCast(this.doc.workspaceBrush) ? "dashed 1px #06123232" : undefined,
+ outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined,
pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
}} >
{view}
@@ -725,9 +730,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
}
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
- if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myWorkspaces) {
- ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
- ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.doc), icon: "minus" });
+ if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myDashboards) {
+ ContextMenu.Instance.addItem({ description: "Create Dashboard", event: () => MainView.Instance.createNewDashboard(), icon: "plus" });
+ ContextMenu.Instance.addItem({ description: "Delete Dashboard", event: () => this.remove(this.doc), icon: "minus" });
e.stopPropagation();
e.preventDefault();
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 99ba1c706..4fdf1411d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -18,7 +18,7 @@ import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx, GetEffectiveAcl, SharingPermissions, distributeAcls } from '../../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, returnEmptyDoclist } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -208,7 +208,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
const value = DocListCast(targetDataDoc[this.props.fieldKey]);
const toRemove = value.filter(v => docs.includes(v));
if (toRemove.length !== 0) {
- const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
+ const recent = Cast(Doc.UserDoc().myInactiveDocs, Doc) as Doc;
toRemove.forEach(doc => {
Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc);
recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
@@ -541,6 +541,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
fieldKey={`${this.props.fieldKey}-filter`}
CollectionView={this}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
ContainingCollectionView={this.props.ContainingCollectionView}
PanelWidth={this.facetWidth}
@@ -592,7 +593,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
ChildLayoutString: this.childLayoutString,
};
const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
- `${Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+ `${Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
style={{ pointerEvents: this.props.Document.isBackground ? "none" : undefined, boxShadow }}>
{this.showIsTagged()}
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index 6ec9783e2..4dafa4ac4 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -12,9 +12,8 @@ import { listSpec } from "../../../fields/Schema";
import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { ComputedField } from "../../../fields/ScriptField";
import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, returnEmptyDoclist } from "../../../Utils";
import { Docs, DocumentOptions } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
import { CompileScript, Transformer, ts } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
@@ -22,11 +21,12 @@ import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss';
import { ContextMenu } from "../ContextMenu";
import '../DocumentDecorations.scss';
import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
-import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
+import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell } from "./CollectionSchemaCells";
import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders";
import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
import "./CollectionSchemaView.scss";
import { CollectionView } from "./CollectionView";
+import { DocumentType } from "../../documents/DocumentTypes";
enum ColumnType {
@@ -91,7 +91,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@observable _cellIsEditing: boolean = false;
@observable _focusedCell: { row: number, col: number } = { row: 0, col: 0 };
- @observable _openCollections: Array<string> = [];
+ @observable _openCollections: Set<number> = new Set;
@observable _showDoc: Doc | undefined;
@observable _showDataDoc: any = "";
@@ -136,16 +136,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
changeSorting = (col: any) => {
- if (col.desc === undefined) {
- // no sorting
- this.props.changeColumnSort(col, true);
- } else if (col.desc === true) {
- // descending sort
- this.props.changeColumnSort(col, false);
- } else if (col.desc === false) {
- // ascending sort
- this.props.changeColumnSort(col, undefined);
- }
+ this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true);
}
@action
@@ -153,7 +144,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
@computed get tableColumns(): Column<Doc>[] {
-
const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
const columns: Column<Doc>[] = [];
const tableIsFocused = this.props.isFocused(this.props.Document, false);
@@ -161,27 +151,16 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const focusedCol = this._focusedCell.col;
const isEditable = !this.props.headerIsEditing;
- //if (this.childDocs.reduce((found, doc) => found || doc.type === DocumentType.COL, false)) {
- columns.push(
- {
- expander: true,
- Header: "",
- width: 60,
- Expander: (rowInfo) => {
- if (rowInfo.original.type === "collection") {
- return rowInfo.isExpanded ?
- <div className="collectionSchemaView-expander" onClick={() => this.onCloseCollection(rowInfo.original)}><FontAwesomeIcon icon={"caret-down"} size="lg" /></div> :
- <div className="collectionSchemaView-expander" onClick={() => this.onExpandCollection(rowInfo.original)}><FontAwesomeIcon icon={"caret-right"} size="lg" /></div>;
- } else {
- return null;
- }
- }
+ columns.push({
+ expander: true, Header: "", width: 60,
+ Expander: (rowInfo) => {
+ return rowInfo.original.type !== DocumentType.COL ? (null) :
+ <div className="collectionSchemaView-expander" onClick={action(() => (this._openCollections[rowInfo.isExpanded ? "delete" : "add"])(rowInfo.viewIndex))}>
+ <FontAwesomeIcon icon={rowInfo.isExpanded ? "caret-down" : "caret-right"} size="lg" />
+ </div>;
}
- );
- // }
- this.props.active;
-
- const cols = this.props.columns.map(col => {
+ });
+ columns.push(...this.props.columns.map(col => {
const icon: IconProp = this.getColumnType(col) === ColumnType.Number ? "hashtag" : this.getColumnType(col) === ColumnType.String ? "font" :
this.getColumnType(col) === ColumnType.Boolean ? "check-square" : this.getColumnType(col) === ColumnType.Doc ? "file" :
this.getColumnType(col) === ColumnType.Image ? "image" : this.getColumnType(col) === ColumnType.List ? "list-ul" :
@@ -209,25 +188,13 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
width={"100%"}
/>;
-
-
const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up";
-
- const header =
- <div //className="collectionSchemaView-header"
- //onClick={e => this.props.openHeader(col, menuContent, e.clientX, e.clientY)}
- className="collectionSchemaView-menuOptions-wrapper"
- style={{
- background: col.color, padding: "2px",
- display: "flex", cursor: "default", height: "100%",
- }}>
- {/* <FontAwesomeIcon onClick={e => this.props.openHeader(col, e.clientX, e.clientY)} icon={icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} /> */}
- {keysDropdown}
- <div onClick={e => this.changeSorting(col)}
- style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
- <FontAwesomeIcon icon={sortIcon} size="lg" />
- </div>
- </div>;
+ const header = <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: "2px", display: "flex", cursor: "default", height: "100%", }}>
+ {keysDropdown}
+ <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
+ <FontAwesomeIcon icon={sortIcon} size="lg" />
+ </div>
+ </div>;
return {
Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
@@ -260,21 +227,20 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
showDoc: this.showDoc,
};
- const colType = this.getColumnType(col);
- if (colType === ColumnType.Number) return <CollectionSchemaNumberCell {...props} />;
- if (colType === ColumnType.String) return <CollectionSchemaStringCell {...props} />;
- if (colType === ColumnType.Boolean) return <CollectionSchemaCheckboxCell {...props} />;
- if (colType === ColumnType.Doc) return <CollectionSchemaDocCell {...props} />;
- if (colType === ColumnType.Image) return <CollectionSchemaImageCell {...props} />;
- if (colType === ColumnType.List) return <CollectionSchemaListCell {...props} />;
- if (colType === ColumnType.Date) return <CollectionSchemaDateCell {...props} />;
- return <CollectionSchemaCell {...props} />;
+ switch (this.getColumnType(col)) {
+ case ColumnType.Number: return <CollectionSchemaNumberCell {...props} />;
+ case ColumnType.String: return <CollectionSchemaStringCell {...props} />;
+ case ColumnType.Boolean: return <CollectionSchemaCheckboxCell {...props} />;
+ case ColumnType.Doc: return <CollectionSchemaDocCell {...props} />;
+ case ColumnType.Image: return <CollectionSchemaImageCell {...props} />;
+ case ColumnType.List: return <CollectionSchemaListCell {...props} />;
+ case ColumnType.Date: return <CollectionSchemaDateCell {...props} />;
+ default: return <CollectionSchemaCell {...props} />;
+ }
},
minWidth: 200,
};
- });
- columns.push(...cols);
-
+ }));
columns.push({
Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />,
accessor: (doc: Doc) => 0,
@@ -283,8 +249,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const rowIndex = rowProps.index;
const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
- const props: CellProps = {
- row: rowIndex,
+ return <CollectionSchemaButtons {...{
+ row: rowProps.index,
col: columnIndex,
rowProps: rowProps,
isFocused: isFocused,
@@ -303,9 +269,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
setComputed: this.setComputed,
getField: this.getField,
showDoc: this.showDoc,
- };
-
- return <CollectionSchemaButtons {...props} />;
+ }} />;
},
width: 28,
resizable: false
@@ -316,12 +280,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
constructor(props: SchemaTableProps) {
super(props);
- // convert old schema columns (list of strings) into new schema columns (list of schema header fields)
- const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []);
- if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") {
- const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders);
- } else if (this.props.Document._schemaHeaders === undefined) {
+ if (this.props.Document._schemaHeaders === undefined) {
this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb"), new SchemaHeaderField("author", "#f1efeb"), new SchemaHeaderField("*lastModified", "#f1efeb", ColumnType.Date),
new SchemaHeaderField("text", "#f1efeb", ColumnType.String), new SchemaHeaderField("type", "#f1efeb"), new SchemaHeaderField("context", "#f1efeb", ColumnType.Doc)]);
}
@@ -362,19 +321,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true);
// TODO: editing border doesn't work :(
return {
- style: {
- border: !this.props.headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb"
- }
+ style: { border: !this.props.headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb" }
};
}
- @action
- onCloseCollection = (collection: Doc): void => {
- const index = this._openCollections.findIndex(col => col === collection[Id]);
- if (index > -1) this._openCollections.splice(index, 1);
- }
-
- @action onExpandCollection = (collection: Doc) => this._openCollections.push(collection[Id]);
@action setCellIsEditing = (isEditing: boolean) => this._cellIsEditing = isEditing;
@action
@@ -413,7 +363,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@undoBatch
createRow = action(() => {
- this.props.addDocument(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 }));
+ this.props.addDocument(Docs.Create.TextDocument("", { title: "", _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 100, _height: 30 }));
this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
});
@@ -431,21 +381,13 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
getColumnType = (column: SchemaHeaderField): ColumnType => {
- // added functionality to convert old column type stuff to new column type stuff -syip
if (column.type && column.type !== 0) {
return column.type;
}
if (columnTypes.get(column.heading)) {
- column.type = columnTypes.get(column.heading)!;
- return columnTypes.get(column.heading)!;
- }
- const typesDoc = FieldValue(Cast(this.props.Document.schemaColumnTypes, Doc));
- if (!typesDoc) {
- column.type = ColumnType.Any;
- return ColumnType.Any;
+ return column.type = columnTypes.get(column.heading)!;
}
- column.type = NumCast(typesDoc[column.heading]);
- return NumCast(typesDoc[column.heading]);
+ return column.type = ColumnType.Any;
}
@undoBatch
@@ -474,11 +416,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed
get reactTable() {
const children = this.childDocs;
- const hasCollectionChild = children.reduce((found, doc) => found || doc.type === "collection", false);
- const expandedRowsList = this._openCollections.map(col => children.findIndex(doc => doc[Id] === col).toString());
- const expanded = {};
- //@ts-ignore
- expandedRowsList.forEach(row => expanded[row] = true);
+ const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false);
+ const expanded: { [name: string]: any } = {};
+ Array.from(this._openCollections.keys()).map(col => expanded[col.toString()] = true);
const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :((((
return <ReactTable
@@ -496,17 +436,14 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
expanded={expanded}
resized={this.resized}
onResizedChange={this.props.onResizedChange}
- SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== "collection") ? (null) :
+ SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== DocumentType.COL) ? (null) :
<div className="reactTable-sub"><SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} /></div>}
/>;
}
onContextMenu = (e: React.MouseEvent): void => {
- if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- // ContextMenu.Instance.addItem({ description: "Make DB", event: this.makeDB, icon: "table" });
- ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" });
- }
+ ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" });
}
getField = (row: number, col?: number) => {
@@ -588,9 +525,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
onOpenClick = () => {
- if (this._showDoc) {
- this.props.addDocTab(this._showDoc, "onRight");
- }
+ this._showDoc && this.props.addDocTab(this._showDoc, "onRight");
}
getPreviewTransform = (): Transform => {
@@ -606,7 +541,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
{StrCast(this.props.Document._chromeStatus) !== "disabled" ? <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
: undefined}
{!this._showDoc ? (null) :
- <div className="collectionSchemaView-documentPreview" //onClick={() => { this.onOpenClick(); }}
+ <div className="collectionSchemaView-documentPreview"
style={{
position: "absolute", width: 150, height: 150,
background: "dimGray", display: "block", top: 0, left: 0,
@@ -627,6 +562,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
PanelHeight={() => 150}
ScreenToLocalTransform={this.getPreviewTransform}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
moveDocument={this.props.moveDocument}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index b00074cc6..646ae94fb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -359,7 +359,7 @@ export function computeTimelineLayout(
groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
}
- const divider = { type: "div", color: Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+ const divider = { type: "div", color: Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 01bfb0453..02ff3e78c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -591,7 +591,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
onClick = (e: React.MouseEvent) => {
if (this.layoutDoc.targetScale && (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) {
if (Date.now() - this._lastTap < 300) {
- runInAction(() => DocumentLinksButton.StartLink = undefined);
+ runInAction(() => {
+ DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
+ });
const docpt = this.getTransform().transformPoint(e.clientX, e.clientY);
this.scaleAtPt(docpt, 1);
e.stopPropagation();
@@ -641,7 +644,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
- // panning a workspace
if (!e.cancelBubble) {
const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
const pt = myTouches[0];
@@ -975,6 +977,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
ContainingCollectionView: this.props.CollectionView,
ContainingCollectionDoc: this.props.Document,
docFilters: this.docFilters,
+ searchFilterDocs: this.searchFilterDocs,
focus: this.focusDocument,
backgroundColor: this.getClusterColor,
backgroundHalo: this.backgroundHalo,
@@ -1045,7 +1048,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
} else if (viewDef.type === "div") {
return [x, y].some(val => val === undefined) ? undefined :
{
- ele: <div className="collectionFreeform-customDiv" title={viewDef.payload?.join(" ")} key={"div" + x + y + z} onClick={e => this.onViewDefDivClick(e, viewDef)}
+ ele: <div className="collectionFreeform-customDiv" title={viewDef.payload?.join(" ")} key={"div" + x + y + z + viewDef.payload} onClick={e => this.onViewDefDivClick(e, viewDef)}
style={{ width, height, backgroundColor: color, transform }} />,
bounds: viewDef
};
@@ -1601,30 +1604,30 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
@computed get presPaths() {
const presPaths = "presPaths" + (this.props.presPaths ? "" : "-hidden");
- return !(PresBox.Instance) ? (null) : (<>
- {!this.props.presPaths ? (null) : <><div>{PresBox.Instance.order}</div>
- <svg className={presPaths}>
- <defs>
- <marker id="arrow" markerWidth="3" overflow="visible" markerHeight="3" refX="5" refY="5" orient="auto" markerUnits="strokeWidth">
- <path d="M0,0 L0,6 L9,3 z" fill="#69a6db" />
- </marker>
- <marker id="square" markerWidth="3" markerHeight="3" overflow="visible"
- refX="5" refY="5" orient="auto" markerUnits="strokeWidth">
- <path d="M 5,1 L 9,5 5,9 1,5 z" fill="#69a6db" />
- </marker>
- <marker id="markerSquare" markerWidth="7" markerHeight="7" refX="4" refY="4"
- orient="auto" overflow="visible">
- <rect x="1" y="1" width="5" height="5" fill="#69a6db" />
- </marker>
-
- <marker id="markerArrow" markerWidth="5" markerHeight="5" refX="2" refY="7"
- orient="auto" overflow="visible">
- <path d="M2,2 L2,13 L8,7 L2,2" fill="#69a6db" />
- </marker>
- </defs>;
- {PresBox.Instance.paths}
- </svg></>}
- </>);
+ return !PresBox.Instance || !this.props.presPaths ? (null) : <>
+ <div key="presorder">{PresBox.Instance.order}</div>
+ <svg key="svg" className={presPaths}>
+ <defs>
+ <marker id="arrow" markerWidth="3" overflow="visible" markerHeight="3" refX="5" refY="5" orient="auto" markerUnits="strokeWidth">
+ <path d="M0,0 L0,6 L9,3 z" fill="#69a6db" />
+ </marker>
+ <marker id="square" markerWidth="3" markerHeight="3" overflow="visible"
+ refX="5" refY="5" orient="auto" markerUnits="strokeWidth">
+ <path d="M 5,1 L 9,5 5,9 1,5 z" fill="#69a6db" />
+ </marker>
+ <marker id="markerSquare" markerWidth="7" markerHeight="7" refX="4" refY="4"
+ orient="auto" overflow="visible">
+ <rect x="1" y="1" width="5" height="5" fill="#69a6db" />
+ </marker>
+
+ <marker id="markerArrow" markerWidth="5" markerHeight="5" refX="2" refY="7"
+ orient="auto" overflow="visible">
+ <path d="M2,2 L2,13 L8,7 L2,2" fill="#69a6db" />
+ </marker>
+ </defs>
+ {PresBox.Instance.paths}
+ </svg>
+ </>;
}
render() {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 15f277adb..8dbc2bd43 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -132,6 +132,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const tbox = Docs.Create.TextDocument("", {
_width: 200, _height: 100, x: x, y: y, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize),
_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+ _showTitle: Doc.UserDoc().showTitle ? "title" : undefined,
title: "-typed text-"
});
const template = FormattedTextBox.DefaultLayout;
@@ -528,7 +529,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
d.page = -1;
return d;
});
- const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
+ const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
const portal = Doc.MakeAlias(summary);
Doc.GetProto(summary)[Doc.LayoutFieldKey(summary) + "-annotations"] = new List<Doc>(selected);
Doc.GetProto(summary).layout_portal = CollectionView.LayoutString(Doc.LayoutFieldKey(summary) + "-annotations");
@@ -755,14 +756,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
</div>;
} else {
- //subtracted for offset
var str: string = "";
for (var i = 0; i < this._pointsX.length; i++) {
- var x = 0;
- x = this._pointsX[i] - 64;
- str += x.toString();
+ const pt = this.props.getContainerTransform().transformPoint(this._pointsX[i], this._pointsY[i]);
+ str += pt[0].toString();
str += ",";
- str += (this._pointsY[i] - 85).toString();
+ str += pt[1].toString();
str += (" ");
}
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
index 535581f2e..e3ced887d 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
@@ -303,7 +303,6 @@
margin-left: auto;
.permissions-select {
- z-index: 1;
border: none;
background-color: inherit;
width: 75px;
@@ -731,7 +730,9 @@
border: none;
padding: 6px;
padding-bottom: 2px;
-
+ background: #eeeeee;
+ border-top: 1px solid;
+ border-left: 1px solid;
&:hover {
border: 0.75px solid rgb(122, 28, 28);
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
index fb138ecc0..e128f6aab 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -8,7 +8,7 @@ import { EditableView } from "../../EditableView";
import { KeyValueBox } from "../../nodes/KeyValueBox";
import { Cast, NumCast, StrCast } from "../../../../fields/Types";
import { ContentFittingDocumentView } from "../../nodes/ContentFittingDocumentView";
-import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils } from "../../../../Utils";
+import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils, returnEmptyDoclist } from "../../../../Utils";
import { Id } from "../../../../fields/FieldSymbols";
import { Transform } from "../../../util/Transform";
import { PropertiesButtons } from "../../PropertiesButtons";
@@ -181,40 +181,38 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const doc = this.dataDoc;
doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
const rows: JSX.Element[] = [];
- for (const key of Object.keys(ids).slice().sort()) {
- if ((key[0] === key[0].toUpperCase() && key.substring(0, 3) !== "ACL" && key !== "UseCors")
- || key[0] === "#" || key === "author" ||
- key === "creationDate" || key.indexOf("lastModified") !== -1) {
-
- const contents = doc[key];
- if (key[0] === "#") {
+ const noviceReqFields = ["author", "creationDate"];
+ const noviceLayoutFields = ["curPage"];
+ const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("ACL") && key !== "UseCors")),
+ ...noviceReqFields, ...noviceLayoutFields];
+ for (const key of noviceKeys.sort()) {
+ const contents = this.selectedDoc[key];
+ if (key[0] === "#") {
+ rows.push(<div className="uneditable-field" key={key}>
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
+ &nbsp;
+ </div>);
+ } else if (contents !== undefined) {
+ const value = Field.toString(contents as Field);
+ if (noviceReqFields.includes(key) || key.indexOf("lastModified") !== -1) {
rows.push(<div className="uneditable-field" key={key}>
- <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
- &nbsp;
- </div>);
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ": "}</span>
+ <div style={{ whiteSpace: "nowrap", overflowX: "hidden" }}>{value}</div>
+ </div>);
} else {
- const value = Field.toString(contents as Field);
- if (key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) {
- rows.push(<div className="uneditable-field" key={key}>
- <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ": "}</span>
- <div style={{ whiteSpace: "nowrap", overflowX: "hidden" }}>{value}</div>
- </div>);
- } else {
- let contentElement: (JSX.Element | null)[] | JSX.Element = [];
- contentElement = <EditableView key="editableView"
- contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
- height={13}
- fontSize={10}
- GetValue={() => Field.toKeyValueString(doc, key)}
- SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)}
- />;
-
- rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
- <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
- &nbsp;
- {contentElement}
- </div>);
- }
+ const contentElement = <EditableView key="editableView"
+ contents={value}
+ height={13}
+ fontSize={10}
+ GetValue={() => Field.toKeyValueString(this.selectedDoc!, key)}
+ SetValue={(value: string) => KeyValueBox.SetField(noviceLayoutFields.includes(key) ? this.selectedDoc! : doc, key, value, true)}
+ />;
+
+ rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
+ &nbsp;
+ {contentElement}
+ </div>);
}
}
}
@@ -284,6 +282,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
focus={returnFalse}
ScreenToLocalTransform={this.getTransform}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
addDocument={returnFalse}
@@ -321,7 +320,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onChange={e => this.changePermissions(e, user)}>
{Object.values(SharingPermissions).map(permission => {
return (
- <option key={permission} value={permission} selected={this.selectedDoc![`ACL-${user.replace(".", "_")}`] === permission}>
+ <option key={permission} value={permission}>
{permission}
</option>);
})}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 70ebca5e7..4e279c659 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -304,7 +304,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
(e: PointerEvent, doubleTap?: boolean) => {
if (doubleTap) {
undoBatch(action(() => {
- const text = Docs.Create.TextDocument("", { _width: 150, _height: 50 });
+ const text = Docs.Create.TextDocument("", { _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 150, _height: 50 });
FormattedTextBox.SelectOnLoad = text[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
Doc.AddDocToList(this.props.Document, this.props.fieldKey, text);
this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY))));
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 21d283547..0afcab5a3 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -234,6 +234,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
ScreenToLocalTransform={dxf}
focus={this.props.focus}
docFilters={this.docFilters}
+ searchFilterDocs={this.searchFilterDocs}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index d02088a6c..53825eece 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -234,6 +234,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
ScreenToLocalTransform={dxf}
focus={this.props.focus}
docFilters={this.docFilters}
+ searchFilterDocs={this.searchFilterDocs}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
addDocument={this.props.addDocument}
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 8ecde959f..519b78add 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -93,7 +93,6 @@ export class LinkMenu extends React.Component<Props> {
}
render() {
- console.log("computed", this.position.x, this.position.b);
const sourceDoc = this.props.docView.props.Document;
const groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
return <div className="linkMenu" ref={this._linkMenuRef} >
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 5c8cb5e35..7b9a32dbe 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -109,12 +109,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
this._linkPlayDisposer?.();
this._scrubbingDisposer?.();
}
+
+ @action
componentDidMount() {
if (!this.dataDoc.markerAmount) {
this.dataDoc.markerAmount = 0;
}
- runInAction(() => this.audioState = this.path ? "paused" : undefined);
+ this.audioState = this.path ? "paused" : undefined;
this._linkPlayDisposer = reaction(() => this.layoutDoc.scrollToLinkID,
scrollLinkId => {
if (scrollLinkId) {
@@ -285,7 +287,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
// creates a text document for dictation
onFile = (e: any) => {
const newDoc = Docs.Create.TextDocument("", {
- title: "", _chromeStatus: "disabled",
+ _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, title: "", _chromeStatus: "disabled",
x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10,
_width: NumCast(this.props.Document._width), _height: 2 * NumCast(this.props.Document._height)
});
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index 88eb48f51..b3b7cc4f3 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -120,6 +120,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
DataDoc={undefined}
LibraryPath={emptyPath}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={this as any} // bcz: hack! need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked. currently, the up selector allows only a containing collection to be selected
ContainingCollectionDoc={undefined}
fitToBox={true}
@@ -149,6 +150,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
DataDoc={undefined}
LibraryPath={emptyPath}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={this as any} // bcz: hack! need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked. currently, the up selector allows only a containing collection to be selected
ContainingCollectionDoc={undefined}
fitToBox={true}
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 429bc27ad..318f7b7e9 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -36,6 +36,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
private _linkButton = React.createRef<HTMLDivElement>();
@observable public static StartLink: Doc | undefined;
+ @observable public static StartLinkView: DocumentView | undefined;
@observable public static AnnotationId: string | undefined;
@observable public static AnnotationUri: string | undefined;
@@ -79,8 +80,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
//action(() => Doc.BrushDoc(this.props.View.Document));
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
} else {
DocumentLinksButton.StartLink = this.props.View.props.Document;
+ DocumentLinksButton.StartLinkView = this.props.View;
}
} else if (!this.props.InMenu) {
DocumentLinksButton.EditLink = this.props.View;
@@ -95,8 +98,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
DocumentLinksButton.AnnotationUri = undefined;
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
} else {
DocumentLinksButton.StartLink = this.props.View.props.Document;
+ DocumentLinksButton.StartLinkView = this.props.View;
}
//action(() => Doc.BrushDoc(this.props.View.Document));
@@ -110,6 +115,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
if (doubleTap && !this.props.StartLink) {
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
DocumentLinksButton.AnnotationId = undefined;
} else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
const sourceDoc = DocumentLinksButton.StartLink;
@@ -150,6 +156,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
public static finishLinkClick = undoBatch(action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView,) => {
if (startLink === endLink) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
DocumentLinksButton.AnnotationId = undefined;
DocumentLinksButton.AnnotationUri = undefined;
//!this.props.StartLink
@@ -157,8 +164,12 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag");
// this notifies any of the subviews that a document is made so that they can make finer-grained hyperlinks (). see note above in onLInkButtonMoved
if (endLinkView) {
- startLink._link = endLinkView._link = linkDoc;
- setTimeout(action(() => startLink._link = endLinkView._link = undefined), 0);
+ endLinkView._link = linkDoc;
+ DocumentLinksButton.StartLinkView && (DocumentLinksButton.StartLinkView._link = linkDoc);
+ setTimeout(action(() => {
+ DocumentLinksButton.StartLinkView && (DocumentLinksButton.StartLinkView._link = undefined);
+ endLinkView._link = undefined;
+ }), 0);
}
LinkManager.currentLink = linkDoc;
@@ -203,6 +214,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
@action clearLinks() {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
}
@computed
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index e6b8928d4..2dd3bba91 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -133,6 +133,8 @@
bottom: 0;
width: 100%;
transform-origin: bottom left;
+ opacity: 0.1;
+ transition: opacity 0.5s;
}
}
@@ -144,4 +146,9 @@
display:inline-block;
}
}
+ > .documentView-styleWrapper {
+ > .documentView-captionWrapper {
+ opacity: 1;
+ }
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 590befd86..db6d30aac 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -45,6 +45,7 @@ export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView>;
ContainingCollectionDoc: Opt<Doc>;
docFilters: () => string[];
+ searchFilterDocs: () => Doc[];
FreezeDimensions?: boolean;
NativeWidth: () => number;
NativeHeight: () => number;
@@ -291,7 +292,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let stopPropagate = true;
let preventDefault = true;
!this.props.Document.isBackground && this.props.bringToFront(this.props.Document);
- if (this._doubleTap && this.props.renderDepth && (this.props.Document.type !== DocumentType.FONTICON || !this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
+ if (this._doubleTap && this.props.renderDepth && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
if (!(e.nativeEvent as any).formattedHandled) {
if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
const func = () => this.onDoubleClickHandler.script.run({
@@ -564,8 +565,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch @action
deleteClicked = (): void => {
- if (Doc.UserDoc().activeWorkspace === this.props.Document) {
- alert("Can't delete the active workspace");
+ if (Doc.UserDoc().activeDashboard === this.props.Document) {
+ alert("Can't delete the active dashboard");
} else {
const selected = SelectionManager.SelectedDocuments().slice();
SelectionManager.DeselectAll();
@@ -601,7 +602,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch @action
drop = async (e: Event, de: DragManager.DropEvent) => {
- if (this.props.Document === Doc.UserDoc().activeWorkspace) {
+ if (this.props.Document === Doc.UserDoc().activeDashboard) {
alert("linking to document tabs not yet supported. Drop link on document content.");
return;
}
@@ -713,7 +714,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
const cm = ContextMenu.Instance;
- if (!cm || (e?.nativeEvent as any)?.SchemaHandled) return;
+ if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
@@ -770,7 +771,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
}
moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
- Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()) && moreItems.push({ description: "Toggle Always Show Link End", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
+ Doc.AreProtosEqual(this.props.Document, Cast(Doc.UserDoc()["sidebar-userDoc"], Doc, null)) && moreItems.push({ description: "Toggle Always Show Link End", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
}
const collectionAcl = GetEffectiveAcl(this.props.ContainingCollectionDoc?.[DataSym]);
@@ -829,6 +830,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
return (<div className="documentView-contentsView" style={{ borderRadius: "inherit", width: "100%", height: "100%" }}>
<DocumentContentsView key={1}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={this.props.ContainingCollectionView}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
NativeWidth={this.NativeWidth}
@@ -1011,7 +1013,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
const borderRounding = this.layoutDoc.borderRounding;
const localScale = fullDegree;
- const highlightColors = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ?
+ const highlightColors = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ?
["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 9d61ec6d1..fceeced36 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -29,6 +29,7 @@ export interface FieldViewProps {
dropAction: dropActionType;
backgroundHalo?: () => boolean;
docFilters: () => string[];
+ searchFilterDocs: () => Doc[];
isSelected: (outsideReaction?: boolean) => boolean;
select: (isCtrlPressed: boolean) => void;
rootSelected: (outsideReaction?: boolean) => boolean;
@@ -58,9 +59,6 @@ export interface FieldViewProps {
ChromeHeight?: () => number;
childLayoutTemplate?: () => Opt<Doc>;
- highlighting?: string[];
- lines?: string[];
- doc?: Doc;
// properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
height?: number;
width?: number;
@@ -133,7 +131,7 @@ export class FieldView extends React.Component<FieldViewProps> {
// );
}
else if (field instanceof List) {
- return <div> {field.map(f => Field.toString(f)).join(", ")} </div>;
+ return <div> {field.length ? field.map(f => Field.toString(f)).join(", ") : "[]"} </div>;
}
// bcz: this belongs here, but it doesn't render well so taking it out for now
else if (field instanceof WebField) {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 216c5f39e..5f31f8c8d 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -111,23 +111,17 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
}).then(function (stream) {
gumStream = stream;
recorder = new MediaRecorder(stream);
- recorder.ondataavailable = async function (e: any) {
- const formData = new FormData();
- formData.append("file", e.data);
- const res = await fetch(Utils.prepend("/uploadFormData"), {
- method: 'POST',
- body: formData
- });
- const files = await res.json();
- const url = Utils.prepend(files[0].path);
- // upload to server with known URL
- const audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", _width: 200, _height: 32 });
- audioDoc.treeViewExpandedView = "layout";
- const audioAnnos = Cast(this.dataDoc[this.fieldKey + "-audioAnnotations"], listSpec(Doc));
- if (audioAnnos === undefined) {
- this.dataDoc[this.fieldKey + "-audioAnnotations"] = new List([audioDoc]);
- } else {
- audioAnnos.push(audioDoc);
+ recorder.ondataavailable = async (e: any) => {
+ const [{ result }] = await Networking.UploadFilesToServer(e.data);
+ if (!(result instanceof Error)) {
+ const audioDoc = Docs.Create.AudioDocument(Utils.prepend(result.accessPaths.agnostic.client), { title: "audio test", _width: 200, _height: 32 });
+ audioDoc.treeViewExpandedView = "layout";
+ const audioAnnos = Cast(self.dataDoc[self.fieldKey + "-audioAnnotations"], listSpec(Doc));
+ if (audioAnnos === undefined) {
+ self.dataDoc[self.fieldKey + "-audioAnnotations"] = new List([audioDoc]);
+ } else {
+ audioAnnos.push(audioDoc);
+ }
}
};
runInAction(() => self._audioState = 2);
@@ -489,6 +483,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
ScreenToLocalTransform={this.screenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{this.contentFunc}
</CollectionFreeFormView>
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 4568a6b16..6dd85b7f5 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,7 +1,7 @@
import { action, observable } from 'mobx';
import { observer } from "mobx-react";
import { Doc, Field, Opt } from '../../../fields/Doc';
-import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter } from '../../../Utils';
+import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
@@ -56,7 +56,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
Document: this.props.doc,
DataDoc: this.props.doc,
LibraryPath: [],
- docFilters:returnEmptyFilter,
+ docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
ContainingCollectionView: undefined,
ContainingCollectionDoc: undefined,
fieldKey: this.props.keyName,
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index c4481b213..f154c5f56 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import wiki from "wikijs";
import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { Cast, FieldValue, NumCast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, returnEmptyDoclist } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { Transform } from "../../util/Transform";
@@ -101,6 +101,7 @@ export class LinkDocPreview extends React.Component<Props> {
pinToPres={returnFalse}
dontRegisterView={true}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
renderDepth={0}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 255a1b2d0..2570daafd 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -21,6 +21,7 @@ import { KeyCodes } from '../../util/KeyCodes';
import "./PDFBox.scss";
import React = require("react");
import { documentSchema } from '../../../fields/documentSchemas';
+import { CollectionViewType } from '../collections/CollectionView';
type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@@ -99,20 +100,28 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
public search = (string: string, fwd: boolean) => { this._pdfViewer?.search(string, fwd); };
public prevAnnotation = () => { this._pdfViewer?.prevAnnotation(); };
public nextAnnotation = () => { this._pdfViewer?.nextAnnotation(); };
- public backPage = () => { this._pdfViewer!.gotoPage((this.Document.curPage || 1) - 1); };
- public forwardPage = () => { this._pdfViewer!.gotoPage((this.Document.curPage || 1) + 1); };
- public gotoPage = (p: number) => { this._pdfViewer!.gotoPage(p); };
+ public backPage = () => { this.Document.curPage = (this.Document.curPage || 1) - 1; return true; };
+ public forwardPage = () => { this.Document.curPage = (this.Document.curPage || 1) + 1; return true; };
+ public gotoPage = (p: number) => { this.Document.curPage = p; };
@undoBatch
onKeyDown = action((e: KeyboardEvent) => {
+ let processed = false;
if (e.key === "f" && e.ctrlKey) {
this._searching = true;
setTimeout(() => this._searchRef.current && this._searchRef.current.focus(), 100);
+ processed = true;
+ }
+ if (e.key === "PageDown") processed = this.forwardPage();
+ if (e.key === "PageUp") processed = this.backPage();
+ if (e.target instanceof HTMLInputElement || this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform) {
+ if (e.key === "ArrowDown" || e.key === "ArrowRight") processed = this.forwardPage();
+ if (e.key === "ArrowUp" || e.key === "ArrowLeft") processed = this.backPage();
+ }
+ if (processed) {
e.stopImmediatePropagation();
e.preventDefault();
}
- if (e.key === "PageDown" || e.key === "ArrowDown" || e.key === "ArrowRight") this.forwardPage();
- if (e.key === "PageUp" || e.key === "ArrowUp" || e.key === "ArrowLeft") this.backPage();
});
@undoBatch
@@ -177,7 +186,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
<div className="pdfBox-pageNums">
<input value={curPage}
- onChange={e => this.gotoPage(Number(e.currentTarget.value))}
+ onChange={e => this.Document.curPage = Number(e.currentTarget.value)}
style={{ width: `${curPage > 99 ? 4 : 3}ch`, pointerEvents: "all" }}
onClick={action(() => this._pageControls = !this._pageControls)} />
{this._pageControls ? pageBtns : (null)}
@@ -246,7 +255,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
<PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
- addDocTab={this.props.addDocTab} focus={this.props.focus} docFilters={this.props.docFilters}
+ addDocTab={this.props.addDocTab} focus={this.props.focus} docFilters={this.props.docFilters} searchFilterDocs={this.props.searchFilterDocs}
pinToPres={this.props.pinToPres} addDocument={this.addDocument}
Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 552cf0888..f6325433e 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1497,7 +1497,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? "block" : "none" }}>{this.checkMovementLists(doc, doc["x-indexed"], doc["y-indexed"])}</div>);
}
tags.push(
- <div className="progressivizeButton" onPointerLeave={() => { if (NumCast(targetDoc.currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc.currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? "#aedff8" : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
+ <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc.currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc.currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? "#aedff8" : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
<div className="progressivizeButton-prev"><FontAwesomeIcon icon={"caret-left"} size={"lg"} onClick={e => { e.stopPropagation(); this.prevAppearFrame(doc, index); }} /></div>
<div className="progressivizeButton-frame">{doc.appearFrame}</div>
<div className="progressivizeButton-next"><FontAwesomeIcon icon={"caret-right"} size={"lg"} onClick={e => { e.stopPropagation(); this.nextAppearFrame(doc, index); }} /></div>
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index ee92e517c..dd76503d5 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -384,6 +384,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
ScreenToLocalTransform={this.props.ScreenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{this.contentFunc}
</CollectionFreeFormView>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 1393e7868..7d7426e31 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -733,6 +733,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
ScreenToLocalTransform={this.scrollXf}
renderDepth={this.props.renderDepth + 1}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
</CollectionFreeFormView>
</div>
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 145ee8c2e..08bab8d12 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -5,7 +5,7 @@ import { Id } from "../../../../fields/FieldSymbols";
import { ObjectField } from "../../../../fields/ObjectField";
import { ComputedField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter, returnEmptyDoclist } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
import { DocumentView } from "../DocumentView";
@@ -175,7 +175,7 @@ export class DashDocView extends React.Component<IDashDocView> {
const outerStyle = {
position: "relative" as "relative",
textIndent: "0",
- border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray")),
+ border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")),
width: this.props.node.props.width,
height: this.props.node.props.height,
display: this.props.node.props.hidden ? "none" : "inline-block",
@@ -202,7 +202,7 @@ export class DashDocView extends React.Component<IDashDocView> {
({ dim, color }) => {
spanStyle.width = outerStyle.width = Math.max(20, dim[0]) + "px";
spanStyle.height = outerStyle.height = Math.max(20, dim[1]) + "px";
- outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
}, { fireImmediately: true });
if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
@@ -255,6 +255,7 @@ export class DashDocView extends React.Component<IDashDocView> {
bringToFront={emptyFunction}
dontRegisterView={false}
docFilters={this.props.tbox?.props.docFilters || returnEmptyFilter}
+ searchFilterDocs={this.props.tbox?.props.searchFilterDocs || returnEmptyDoclist}
ContainingCollectionView={this._textBox.props.ContainingCollectionView}
ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
ContentScaling={this.contentScaling}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index afdd8fea2..160f4ba72 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -32,8 +32,8 @@
.formattedTextBox-dictation {
height: 12px;
width: 10px;
- top: 0px;
- left: 0px;
+ bottom: 5px;
+ right: 8px;
position: absolute;
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 6b4115e53..77483a179 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -100,7 +100,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
private _pause: boolean = false;
@computed get _recording() { return this.dataDoc.audioState === "recording"; }
- set _recording(value) { this.dataDoc.audioState = value ? "recording" : undefined; }
+ set _recording(value) {
+ this.dataDoc.audioState = value ? "recording" : undefined;
+ }
@observable private _entered = false;
@@ -350,7 +352,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
updateTitle = () => {
if ((this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
- StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.rootDoc.customTitle) {
+ StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.dataDoc["title-custom"] &&
+ Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey) {
let node = this._editorView.state.doc;
while (node.firstChild && node.firstChild.type.name !== "text") node = node.firstChild;
const str = node.textContent;
@@ -376,8 +379,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
public highlightSearchTerms = (terms: string[], backward: boolean) => {
if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) {
-
-
const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
@@ -385,24 +386,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
let tr = this._editorView.state.tr;
const flattened: TextSelection[] = [];
res.map(r => r.map(h => flattened.push(h)));
- if (BoolCast(Doc.GetProto(this.dataDoc).resetSearch) === true) {
- this._searchIndex = 0;
- Doc.GetProto(this.dataDoc).resetSearch = undefined;
- }
- else {
- this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
- if (backward === true) {
- if (this._searchIndex > 1) {
- this._searchIndex += -2;
- }
- else if (this._searchIndex === 1) {
- this._searchIndex = length - 1;
- }
- else if (this._searchIndex === 0 && length !== 1) {
- this._searchIndex = length - 2;
- }
-
+ this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
+ if (backward === true) {
+ if (this._searchIndex > 1) {
+ this._searchIndex += -2;
+ }
+ else if (this._searchIndex === 1) {
+ this._searchIndex = length - 1;
}
+ else if (this._searchIndex === 0 && length !== 1) {
+ this._searchIndex = length - 2;
+ }
+
}
const lastSel = Math.min(flattened.length - 1, this._searchIndex);
@@ -665,7 +660,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const newBullets: Doc[] = this.recursiveProgressivize(1, list)[0];
mainBulletList.push.apply(mainBulletList, newBullets);
}
- console.log(mainBulletList.length);
const title = Docs.Create.TextDocument(StrCast(this.rootDoc.title), { title: "Title", _width: 800, _height: 70, x: 20, y: -10, _fontSize: '20pt', backgroundColor: "rgba(0,0,0,0)", appearFrame: 0, _fontWeight: 700 });
mainBulletList.push(title);
const doc = Docs.Create.FreeformDocument(mainBulletList, {
@@ -720,7 +714,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
recordDictation = () => {
DictationManager.Controls.listen({
- interimHandler: this.setCurrentBulletContent,
+ interimHandler: this.setDictationContent,
continuous: { indefinite: false },
}).then(results => {
if (results && [DictationManager.Controls.Infringed].includes(results)) {
@@ -731,22 +725,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
stopDictation = (abort: boolean) => { DictationManager.Controls.stop(!abort); };
- recordBullet = async () => {
- const completedCue = "end session";
- const results = await DictationManager.Controls.listen({
- interimHandler: this.setCurrentBulletContent,
- continuous: { indefinite: false },
- terminators: [completedCue, "bullet", "next"]
- });
- if (results && [DictationManager.Controls.Infringed, completedCue].includes(results)) {
- DictationManager.Controls.stop();
- return;
- }
- this.nextBullet(this._editorView!.state.selection.to);
- setTimeout(this.recordBullet, 2000);
- }
-
- setCurrentBulletContent = (value: string) => {
+ setDictationContent = (value: string) => {
if (this._editorView) {
const state = this._editorView.state;
const now = Date.now();
@@ -761,33 +740,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
}
- const recordingStart = DateCast(this.props.Document.recordingStart).date.getTime();
- this._break = false;
- value = "" + (mark.attrs.modified * 1000 - recordingStart) / 1000 + value;
const from = state.selection.from;
- const inserted = state.tr.insertText(value).addMark(from, from + value.length + 1, mark);
- this._editorView.dispatch(inserted.setSelection(TextSelection.create(inserted.doc, from, from + value.length + 1)));
- }
- }
-
- nextBullet = (pos: number) => {
- if (this._editorView) {
- const frag = Fragment.fromArray(this.newListItems(2));
- if (this._editorView.state.doc.resolve(pos).depth >= 2) {
- const slice = new Slice(frag, 2, 2);
- let state = this._editorView.state;
- this._editorView.dispatch(state.tr.step(new ReplaceStep(pos, pos, slice)));
- pos += 4;
- state = this._editorView.state;
- this._editorView.dispatch(state.tr.setSelection(TextSelection.create(this._editorView.state.doc, pos, pos)));
+ this._break = false;
+ if (this.props.Document.recordingStart) {
+ const recordingStart = DateCast(this.props.Document.recordingStart)?.date.getTime();
+ value = "" + (mark.attrs.modified * 1000 - recordingStart) / 1000 + value;
}
+ const tr = state.tr.insertText(value).addMark(from, from + value.length + 1, mark);
+ this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, from + value.length + 1)));
}
}
- private newListItems = (count: number) => {
- return numberRange(count).map(x => schema.nodes.list_item.create(undefined, schema.nodes.paragraph.create()));
- }
-
_keymap: any = undefined;
_rules: RichTextRules | undefined;
@computed get config() {
@@ -909,33 +872,38 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.setupEditor(this.config, this.props.fieldKey);
- this._disposers.search = reaction(() => this.rootDoc.searchMatch,
- search => search !== undefined ? this.highlightSearchTerms([Doc.SearchQuery()], BoolCast(search)) : this.unhighlightSearchTerms(),
- { fireImmediately: this.rootDoc.searchMatch !== undefined ? true : false });
-
- this._disposers.record = reaction(() => this._recording,
- () => {
- if (this._recording) {
- setTimeout(action(() => {
- this.stopDictation(true);
- setTimeout(() => this.recordDictation(), 500);
- }), 500);
- } else setTimeout(() => this.stopDictation(true), 0);
- }
- );
+ this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc),
+ search => search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms(),
+ { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false });
+
+ this._disposers.selected = reaction(() => this.props.isSelected(), action(() => this._recording = false));
+
+ if (!this.props.dontRegisterView) {
+ this._disposers.record = reaction(() => this._recording,
+ () => {
+ if (this._recording) {
+ setTimeout(action(() => {
+ this.stopDictation(true);
+ setTimeout(() => this.recordDictation(), 500);
+ }), 500);
+ } else setTimeout(() => this.stopDictation(true), 0);
+ }
+ );
+ }
this._disposers.scrollToRegion = reaction(
() => StrCast(this.layoutDoc.scrollToLinkID),
async (scrollToLinkID) => {
const findLinkFrag = (frag: Fragment, editor: EditorView) => {
const nodes: Node[] = [];
+ let offset = 0;
frag.forEach((node, index) => {
const examinedNode = findLinkNode(node, editor);
if (examinedNode?.textContent) {
nodes.push(examinedNode);
- start += index;
+ offset = index;
}
});
- return { frag: Fragment.fromArray(nodes), start: start };
+ return { frag: Fragment.fromArray(nodes), start: start + offset };
};
const findLinkNode = (node: Node, editor: EditorView) => {
if (!node.isText) {
@@ -1034,7 +1002,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}, 0);
dataDoc.title = exportState.title;
- this.rootDoc.customTitle = true;
+ this.dataDoc["title-custom"] = true;
dataDoc.unchanged = true;
} else {
delete dataDoc[GoogleRef];
@@ -1415,7 +1383,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const self = this;
return new Plugin({
view(newView) {
- self.props.isSelected(true) && (RichTextMenu.Instance.view = newView);
+ self.props.isSelected(true) && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView);
return self.menuPlugin = new RichTextMenuPlugin({ editorProps: this.props });
}
});
@@ -1437,7 +1405,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
public static HadSelection: boolean = false;
onBlur = (e: any) => {
FormattedTextBox.HadSelection = window.getSelection()?.toString() !== "";
- //DictationManager.Controls.stop(false);
this.endUndoTypingBatch();
this.doLinkOnDeselect();
@@ -1550,13 +1517,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
width: "100%",
height: this.props.height ? this.props.height : this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : undefined,
background: Doc.UserDoc().renderStyle === "comic" ? "transparent" : this.props.background ? this.props.background : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : ""),
- opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1,
color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"),
pointerEvents: interactive ? undefined : "none",
fontSize: Cast(this.layoutDoc._fontSize, "string", null),
fontWeight: Cast(this.layoutDoc._fontWeight, "number", null),
fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
- transition: "opacity 1s"
}}
onContextMenu={this.specificContextMenu}
onKeyDown={this.onKeyPress}
@@ -1620,14 +1585,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
<div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} />
</div>}
{!this.layoutDoc._showAudio ? (null) :
- <div className="formattedTextBox-dictation"
- onPointerDown={e => {
- runInAction(() => this._recording = !this._recording);
- setTimeout(() => this._editorView!.focus(), 500);
- e.stopPropagation();
- }} >
+ <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
<FontAwesomeIcon className="formattedTextBox-audioFont"
- style={{ color: this._recording ? "red" : "blue", opacity: this._recording ? 1 : 0.5, display: this.props.isSelected() ? "" : "none" }} icon={"microphone"} size="sm" />
+ style={{
+ color: this._recording ? "red" : "blue",
+ transitionDelay: "0.6s",
+ opacity: this._recording ? 1 : 0.25,
+ }}
+ icon={"microphone"} size="sm" />
</div>}
</div>
</div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 6f3984f39..55d225adc 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -4,7 +4,7 @@ import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
import { Doc, DocCastAsync, Opt } from "../../../../fields/Doc";
import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne, returnEmptyFilter } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne, returnEmptyFilter, returnEmptyDoclist } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { DocumentManager } from "../../../util/DocumentManager";
import { schema } from "./schema_rts";
@@ -232,6 +232,16 @@ export class FormattedTextBoxComment {
const mark = child ? findLinkMark(child.marks) : undefined;
const href = (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allLinks.find((item: { href: string }) => item.href)?.href || forceUrl;
if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) {
+ try {
+ ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+ } catch (e) { }
+ FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText);
+ FormattedTextBoxComment.tooltipText = document.createElement("div");
+ FormattedTextBoxComment.tooltipText.style.width = "100%";
+ FormattedTextBoxComment.tooltipText.style.height = "100%";
+ FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
+ FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
+
FormattedTextBoxComment.tooltipText.textContent = "external => " + href;
(FormattedTextBoxComment.tooltipText as any).href = href;
if (href.startsWith("https://en.wikipedia.org/wiki/")) {
@@ -241,12 +251,9 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltipText.style.overflow = "hidden";
}
if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+ const docTarget = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
FormattedTextBoxComment.tooltipText.textContent = "target not found...";
(FormattedTextBoxComment.tooltipText as any).href = "";
- const docTarget = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
- try {
- ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
- } catch (e) { }
docTarget && DocServer.GetRefField(docTarget).then(async linkDoc => {
if (linkDoc instanceof Doc) {
(FormattedTextBoxComment.tooltipText as any).href = href;
@@ -302,6 +309,7 @@ export class FormattedTextBoxComment {
pinToPres={returnFalse}
dontRegisterView={true}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
renderDepth={0}
@@ -323,8 +331,8 @@ export class FormattedTextBoxComment {
ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText);
- FormattedTextBoxComment.tooltip.style.width = NumCast(target._width) ? `${NumCast(target._width)}` : "100%";
- FormattedTextBoxComment.tooltip.style.height = NumCast(target._height) ? `${NumCast(target._height)}` : "100%";
+ FormattedTextBoxComment.tooltip.style.width = "100%";
+ FormattedTextBoxComment.tooltip.style.height = "100%";
}
}
});
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index dc1d8a2c8..a455516a3 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -92,7 +92,7 @@ export class RichTextRules {
const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation
const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9pt", title: "inline comment" });
textDocInline.title = inlineFieldKey; // give the annotation its own title
- textDocInline.customTitle = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
+ textDocInline["title-custom"] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
textDocInline._textContext = ComputedField.MakeFunction(`copyField(self.${inlineFieldKey})`);
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 33a080fe4..b76e520b7 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -43,7 +43,7 @@ export class DashDocView {
this._outer = document.createElement("span");
this._outer.style.position = "relative";
this._outer.style.textIndent = "0";
- this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
this._outer.style.width = node.attrs.width;
this._outer.style.height = node.attrs.height;
this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
@@ -126,7 +126,7 @@ export class DashDocView {
this._reactionDisposer = reaction(() => ({ dim: [finalLayout[WidthSym](), finalLayout[HeightSym]()], color: finalLayout.color }), ({ dim, color }) => {
this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px";
this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px";
- this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
}, { fireImmediately: true });
const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => {
@@ -155,6 +155,7 @@ export class DashDocView {
bringToFront={emptyFunction}
dontRegisterView={false}
docFilters={this._textBox.props.docFilters}
+ searchFilterDocs={this._textBox.props.searchFilterDocs}
ContainingCollectionView={this._textBox.props.ContainingCollectionView}
ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
ContentScaling={this.contentScaling}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0916e8b0c..201333d95 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -56,6 +56,7 @@ interface IViewerProps {
Document: Doc;
DataDoc?: Doc;
docFilters: () => string[];
+ searchFilterDocs: () => Doc[];
ContainingCollectionView: Opt<CollectionView>;
PanelWidth: () => number;
PanelHeight: () => number;
@@ -100,13 +101,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
private _retries = 0; // number of times tried to create the PDF viewer
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
- private _reactionDisposer?: IReactionDisposer;
- private _selectionReactionDisposer?: IReactionDisposer;
- private _annotationReactionDisposer?: IReactionDisposer;
- private _scrollTopReactionDisposer?: IReactionDisposer;
- private _filterReactionDisposer?: IReactionDisposer;
- private _searchReactionDisposer?: IReactionDisposer;
- private _searchReactionDisposer2?: IReactionDisposer;
+ private _disposers: { [name: string]: IReactionDisposer } = {};
private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _selectionText: string = "";
@@ -150,13 +145,13 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
runInAction(() => this._showWaiting = this._showCover = true);
this.props.startupLive && this.setupPdfJsViewer();
this._mainCont.current && (this._mainCont.current.scrollTop = this.layoutDoc._scrollTop || 0);
- this._searchReactionDisposer = reaction(() => this.Document.searchMatch,
+ this._disposers.searchMatch = reaction(() => Doc.IsSearchMatch(this.rootDoc),
m => {
- if (m !== undefined) (this._lastSearch = true) && this.search(Doc.SearchQuery(), true);
+ if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), m.searchMatch > 0);
else !(this._lastSearch = false) && setTimeout(() => !this._lastSearch && this.search("", false, true), 200);
}, { fireImmediately: true });
- this._selectionReactionDisposer = reaction(() => this.props.isSelected(),
+ this._disposers.selected = reaction(() => this.props.isSelected(),
selected => {
if (!selected) {
this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
@@ -166,7 +161,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
(SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer();
},
{ fireImmediately: true });
- this._reactionDisposer = reaction(
+ this._disposers.scrollY = reaction(
() => this.Document._scrollY,
(scrollY) => {
if (scrollY !== undefined) {
@@ -177,15 +172,15 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
},
{ fireImmediately: true }
);
+ this._disposers.curPage = reaction(
+ () => this.Document.curPage,
+ (page) => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
+ { fireImmediately: true }
+ );
}
componentWillUnmount = () => {
- this._reactionDisposer?.();
- this._scrollTopReactionDisposer?.();
- this._annotationReactionDisposer?.();
- this._filterReactionDisposer?.();
- this._selectionReactionDisposer?.();
- this._searchReactionDisposer?.();
+ Object.values(this._disposers).forEach(disposer => disposer?.());
document.removeEventListener("copy", this.copy);
}
@@ -226,11 +221,11 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
this.props.setPdfViewer(this);
await this.initialLoad();
- this._scrollTopReactionDisposer = reaction(() => Cast(this.layoutDoc._scrollTop, "number", null),
+ this._disposers.scrollTop = reaction(() => Cast(this.layoutDoc._scrollTop, "number", null),
(stop) => (stop !== undefined && this.layoutDoc._scrollY === undefined && this._mainCont.current) && (this._mainCont.current.scrollTop = stop),
{ fireImmediately: true });
- this._filterReactionDisposer = reaction(
+ this._disposers.filterScript = reaction(
() => Cast(this.Document.filterScript, ScriptField),
action(scriptField => {
const oldScript = this._script.originalScript;
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 127ad9d01..e965ac656 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -102,6 +102,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
bringToFront={returnFalse}
opacity={returnOne}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
ContentScaling={returnOne}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 7b3086848..bed6c83b4 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,181 +1,99 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from '@material-ui/core';
-import { action, computed, observable, runInAction, reaction, IReactionDisposer } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import * as rp from 'request-promise';
-import { Doc, DocListCast, Opt, Field } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
import { documentSchema } from "../../../fields/documentSchemas";
-import { Id } from '../../../fields/FieldSymbols';
+import { Id, Copy } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { returnFalse, Utils, returnZero } from '../../../Utils';
+import { returnFalse, returnZero } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
import { SetupDrag } from '../../util/DragManager';
import { SearchUtil } from '../../util/SearchUtil';
-import { SelectionManager } from '../../util/SelectionManager';
import { Transform } from '../../util/Transform';
-import { CollectionView, CollectionViewType } from '../collections/CollectionView';
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionSchemaView, ColumnType } from "../collections/CollectionSchemaView";
+import { CollectionViewType } from '../collections/CollectionView';
import { ViewBoxBaseComponent } from "../DocComponent";
-import { DocumentView } from '../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import "./SearchBox.scss";
-import { ColumnType } from "../collections/CollectionSchemaView";
-export const searchSchema = createSchema({
- id: "string",
- Document: Doc,
- searchQuery: "string",
-});
-
-export enum Keys {
- TITLE = "title",
- AUTHOR = "author",
- DATA = "data",
- TEXT = "text"
-}
+export const searchSchema = createSchema({ Document: Doc });
type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
-//React.Component<SearchProps>
@observer
export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
+ public static Instance: SearchBox;
- get _searchString() { return this.layoutDoc.searchQuery; }
- @computed set _searchString(value) { this.layoutDoc.searchQuery = (value); }
- @observable private _resultsOpen: boolean = false;
- @observable _searchbarOpen: boolean = false;
- @observable private _results: [Doc, string[], string[]][] = [];
- @observable private _openNoResults: boolean = false;
- @observable private _visibleElements: JSX.Element[] = [];
- @observable private _visibleDocuments: Doc[] = [];
-
- static NUM_SEARCH_RESULTS_PER_PAGE = 25;
-
- private _resultsSet = new Map<Doc, number>();
- private _resultsRef = React.createRef<HTMLDivElement>();
- public inputRef = React.createRef<HTMLInputElement>();
-
- private _isSearch: ("search" | "placeholder" | undefined)[] = [];
- private _isSorted: ("sorted" | "placeholder" | undefined)[] = [];
-
+ private _allIcons: string[] = [DocumentType.INK, DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.WEB];
+ private _numResultsPerPage = 500;
private _numTotalResults = -1;
private _endIndex = -1;
-
- static Instance: SearchBox;
-
+ private _lockPromise?: Promise<void>;
+ private _resultsSet = new Map<Doc, number>();
+ private _inputRef = React.createRef<HTMLInputElement>();
private _maxSearchIndex: number = 0;
private _curRequest?: Promise<any> = undefined;
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
+ private _disposers: { [name: string]: IReactionDisposer } = {};
+ private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.DOCHOLDER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
- private new_buckets: { [characterName: string]: number } = {};
- //if true, any keywords can be used. if false, all keywords are required.
- //this also serves as an indicator if the word status filter is applied
- @observable private _basicWordStatus: boolean = false;
- @observable private _nodeStatus: boolean = false;
- @observable private _keyStatus: boolean = false;
+ private docsforfilter: Doc[] | undefined = [];
+ private realTotalResults: number = 0;
+ private collectionRef = React.createRef<HTMLDivElement>();
- @observable private newAssign: boolean = true;
+ @observable _icons: string[] = this._allIcons;
+ @observable _results: [Doc, string[], string[]][] = [];
+ @observable _visibleElements: JSX.Element[] = [];
+ @observable _visibleDocuments: Doc[] = [];
+ @observable _deletedDocsStatus: boolean = false;
+ @observable _onlyAliases: boolean = true;
+ @observable _searchbarOpen = false;
+ @observable _searchFullDB = "DB";
+ @observable _noResults = "";
+ @observable _pageStart = 0;
+ @observable open = false;
+ @observable children = 0;
+ @observable newsearchstring = "";
+ @computed get filter() { return this._results?.length && (this.currentSelectedCollection?.props.Document._searchFilterDocs || this.currentSelectedCollection?.props.Document._docFilters); }
constructor(props: any) {
super(props);
SearchBox.Instance = this;
- this.resultsScrolled = this.resultsScrolled.bind(this);
-
}
- @observable setupButtons = false;
- private _disposers: { [name: string]: IReactionDisposer } = {};
-
- componentDidMount = () => {
- this._disposers.filters = reaction(() => Cast(this.props.Document._docFilters, listSpec("string")), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
- newFilters => {
- if (this.searchFullDB) {
- runInAction(() => this._pageStart = 0);
- this.submitSearch();
- // newFilters?.forEach(f => {
- // console.log(f);
- // })
- }
- });
- if (this.setupButtons === false) {
- runInAction(() => this.setupButtons = true);
+ componentDidMount = action(() => {
+ if (this._inputRef.current) {
+ this._inputRef.current.focus();
+ this._searchbarOpen = true;
}
- if (this.inputRef.current) {
- this.inputRef.current.focus();
- runInAction(() => { this._searchbarOpen = true; });
- }
- if (this.rootDoc.searchQuery && this.newAssign) {
- const sq = this.rootDoc.searchQuery;
- runInAction(() => {
-
- // this._deletedDocsStatus=this.props.filterQuery!.deletedDocsStatus;
- // this._authorFieldStatus=this.props.filterQuery!.authorFieldStatus
- // this._titleFieldStatus=this.props.filterQuery!.titleFieldStatus;
- // this._basicWordStatus=this.props.filterQuery!.basicWordStatus;
- // this._icons=this.props.filterQuery!.icons;
- this.newAssign = false;
- });
- runInAction(() => {
- this.layoutDoc._searchString = StrCast(sq);
- this.submitSearch();
- });
- }
- }
+ this._disposers.filters = reaction(() => this.props.Document._docFilters,
+ (filters: any) => this.setSearchFilter(this.currentSelectedCollection, !this.filter ? undefined : this.docsforfilter));
+ });
componentWillUnmount() {
Object.values(this._disposers).forEach(disposer => disposer?.());
}
+ @computed get currentSelectedCollection() { return CollectionDockingView.Instance; }
- @action
- getViews = (doc: Doc) => SearchUtil.GetViewsOfDocument(doc)
-
-
- @observable newsearchstring: string = "";
@action.bound
onChange(e: React.ChangeEvent<HTMLInputElement>) {
this.layoutDoc._searchString = e.target.value;
this.newsearchstring = e.target.value;
if (e.target.value === "") {
- if (this.currentSelectedCollection !== undefined) {
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>();
- d._docFilters = new List();
- DocListCast(d.data).forEach((newdoc) => newarray.push(newdoc));
- }
- });
- docs = newarray;
- }
-
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
- this.currentSelectedCollection.props.Document._docFilters = new List();
- this.props.Document.selectedDoc = undefined;
- }
- this._results.forEach(result => {
- Doc.UnBrushDoc(result[0]);
- result[0].searchMatch = undefined;
- });
-
- if (this.currentSelectedCollection !== undefined) {
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
- this.currentSelectedCollection = undefined;
- this.props.Document.selectedDoc = undefined;
+ this.docsforfilter = undefined;
+ this.setSearchFilter(this.currentSelectedCollection, undefined);
+ this.resetSearch(false);
- }
- runInAction(() => { this.open = false; });
- this._openNoResults = false;
+ this.open = false;
this._results = [];
this._resultsSet.clear();
this._visibleElements = [];
@@ -190,58 +108,17 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
if (!e || e.key === "Enter") {
this.layoutDoc._searchString = this.newsearchstring;
this._pageStart = 0;
- this.open = StrCast(this.layoutDoc._searchString) !== "" || this.searchFullDB !== "DB";
+ this.open = StrCast(this.layoutDoc._searchString) !== "" || this._searchFullDB !== "DB";
this.submitSearch();
}
});
- @observable open: boolean = false;
-
-
- public static async convertDataUri(imageUri: string, returnedFilename: string) {
- try {
- const posting = Utils.prepend("/uploadURI");
- const returnedUri = await rp.post(posting, {
- body: {
- uri: imageUri,
- name: returnedFilename
- },
- json: true,
- });
- return returnedUri;
-
- } catch (e) {
- console.log("SearchBox:" + e);
- }
- }
-
- public _allIcons: string[] = [DocumentType.INK, DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.WEB];
- //if true, any keywords can be used. if false, all keywords are required.
- //this also serves as an indicator if the word status filter is applied
- @observable private _filterOpen: boolean = false;
- //if icons = all icons, then no icon filter is applied
- // get _icons() { return this.props.searchFileTypes; }
- // set _icons(value) {
- // this.props.setSearchFileTypes(value);
- // }
- @observable _icons: string[] = this._allIcons;
- //if all of these are true, no key filter is applied
- @observable private _titleFieldStatus: boolean = true;
- @observable private _authorFieldStatus: boolean = true;
- //this also serves as an indicator if the collection status filter is applied
- @observable public _deletedDocsStatus: boolean = false;
- @observable public _onlyAliases: boolean = true;
- @observable private _collectionStatus = false;
-
-
getFinalQuery(query: string): string {
//alters the query so it looks in the correct fields
//if this is true, th`en not all of the field boxes are checked
//TODO: data
const initialfilters = Cast(this.props.Document._docFilters, listSpec("string"), []);
- const type: string[] = [];
-
const filters: string[] = [];
for (let i = 0; i < initialfilters.length; i = i + 3) {
@@ -269,9 +146,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
const mod = "_t:";
const newWords: string[] = [];
const oldWords = values[0].split(" ");
- oldWords.forEach((word, i) => {
- i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\"");
- });
+ oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\""));
query = `(${query}) AND (${newWords.join(" ")})`;
}
else {
@@ -279,185 +154,67 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
const mod = "_t:";
const newWords: string[] = [];
const oldWords = values[i].split(" ");
- oldWords.forEach((word, i) => {
- i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\"");
- });
+ oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\""));
const v = "(" + newWords.join(" ") + ")";
if (i === 0) {
- query = `(${query}) AND (${v}`;
- if (values.length === 1) {
- query = query + ")";
- }
- }
- else if (i === values.length - 1) {
- query = query + " OR " + v + ")";
- }
- else {
- query = query + " OR " + v;
+ query = `(${query}) AND (${v}` + (values.length === 1 ? ")" : "");
}
+ else query = query + " OR " + v + (i === values.length - 1 ? ")" : "");
}
}
-
}
-
- // let limit = typepos.length
- // typepos.forEach(i => {
- // if (i === 0) {
- // if (i + 1 === limit) {
- // query = query + " && " + filters[i] + "_t:" + filters;
- // }
- // else if (filters[i] === filters[i + 3]) {
- // query = query + " && (" + filters[i] + "_t:" + filters;
- // }
- // else {
- // query = query + " && " + filters[i] + "_t:" + filters;
- // }
-
- // }
- // else if (i + 3 > filters.length) {
-
- // }
- // else {
-
- // }
-
- // });
-
- // query = this.applyBasicFieldFilters(query);
-
-
-
- query = query.replace(/-\s+/g, '');
- // query = query.replace(/-/g, "");
- return query;
- }
-
- basicRequireWords(query: string): string {
- return query.split(" ").join(" + ").replace(/ + /, "");
+ return query.replace(/-\s+/g, '');
}
@action
filterDocsByType(docs: Doc[]) {
const finalDocs: Doc[] = [];
- const blockedTypes: string[] = [DocumentType.PRESELEMENT, DocumentType.DOCHOLDER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
docs.forEach(doc => {
- const layoutresult = Cast(doc.type, "string");
- if (layoutresult && !blockedTypes.includes(layoutresult)) {
- if (layoutresult && this._icons.includes(layoutresult)) {
- finalDocs.push(doc);
- }
+ const layoutresult = StrCast(doc.type, "string") as DocumentType;
+ if (layoutresult && !this._blockedTypes.includes(layoutresult) && this._icons.includes(layoutresult)) {
+ finalDocs.push(doc);
}
});
return finalDocs;
}
- addCollectionFilter(query: string): string {
- const collections: Doc[] = this.getCurCollections();
- const oldWords = query.split(" ");
-
- const collectionString: string[] = [];
- collections.forEach(doc => {
- const proto = doc.proto;
- const protoId = (proto || doc)[Id];
- const colString: string = "{!join from=data_l to=id}id:" + protoId + " ";
- collectionString.push(colString);
- });
-
- let finalColString = collectionString.join(" ");
- finalColString = finalColString.trim();
- return "+(" + finalColString + ")" + query;
- }
-
- get filterTypes() {
- return this._icons.length === this._allIcons.length ? undefined : this._icons;
- }
-
- //TODO: basically all of this
- //gets all of the collections of all the docviews that are selected
- //if a collection is the only thing selected, search only in that collection (not its container)
- getCurCollections(): Doc[] {
- const selectedDocs: DocumentView[] = SelectionManager.SelectedDocuments();
- const collections: Doc[] = [];
- selectedDocs.forEach(async element => {
- const layout: string = StrCast(element.props.Document.layout);
- //checks if selected view (element) is a collection. if it is, adds to list to search through
- if (layout.indexOf("Collection") > -1) {
- //makes sure collections aren't added more than once
- if (!collections.includes(element.props.Document)) {
- collections.push(element.props.Document);
- }
- }
- //makes sure collections aren't added more than once
- if (element.props.ContainingCollectionDoc && !collections.includes(element.props.ContainingCollectionDoc)) {
- collections.push(element.props.ContainingCollectionDoc);
- }
- });
-
- return collections;
- }
-
-
- currentSelectedCollection: DocumentView | undefined = undefined;
- docsforfilter: Doc[] = [];
-
+ @action
searchCollection(query: string) {
- const selectedCollection: DocumentView = SelectionManager.SelectedDocuments()[0];
+ const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
query = query.toLowerCase();
if (selectedCollection !== undefined) {
- this.currentSelectedCollection = selectedCollection;
- if (this.filter === true) {
- this.props.Document.selectedDoc = selectedCollection.props.Document;
- }
+ // this._currentSelectedCollection = selectedCollection;
let docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
const found: [Doc, string[], string[]][] = [];
- const docsforFilter: Doc[] = [];
let newarray: Doc[] = [];
while (docs.length > 0) {
newarray = [];
docs.forEach((d) => {
- if (d.data !== undefined) {
- newarray.push(...DocListCast(d.data));
- }
- const hlights: string[] = [];
+ d.data && newarray.push(...DocListCast(d.data));
+ const hlights = new Set<string>();
this.documentKeys(d).forEach(key =>
- Field.toString(d[key] as Field).toLowerCase().includes(query) && !hlights.includes(key) && hlights.push(key));
- if (hlights.length > 0) {
- found.push([d, hlights, []]);
- docsforFilter.push(d);
+ Field.toString(d[key] as Field).toLowerCase().includes(query) && hlights.add(key));
+ if (Array.from(hlights.keys()).length > 0) {
+ found.push([d, Array.from(hlights.keys()), []]);
}
});
docs = newarray;
}
this._results = found;
- this.docsforfilter = docsforFilter;
- if (this.filter === true) {
- selectedCollection.props.Document._searchDocs = new List<Doc>(docsforFilter);
- docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach(d => {
- const docs = DocListCast(d.data);
- if (docs.length) {
- d._searchDocs = new List<Doc>(docsforFilter);
- docs.forEach((newdoc) => newarray.push(newdoc));
- }
- });
- docs = newarray;
- }
- }
+ this.docsforfilter = this._results.map(r => r[0]);
+ this.setSearchFilter(selectedCollection, this.filter && found.length ? this.docsforfilter : undefined);
this._numTotalResults = found.length;
this.realTotalResults = found.length;
}
else {
- this.noresults = "No collection selected :(";
+ this._noResults = "No collection selected :(";
}
}
-
documentKeys(doc: Doc) {
const keys: { [key: string]: boolean } = {};
// bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields.
@@ -466,140 +223,52 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
// then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu
// is displayed (unlikely) it won't show up until something else changes.
//TODO Types
- Doc.GetAllPrototypes(doc).map
- (proto => Object.keys(proto).forEach(key => keys[key] = false));
+ Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false));
return Array.from(Object.keys(keys));
}
- applyBasicFieldFilters(query: string) {
- let finalQuery = "";
-
- finalQuery = finalQuery + this.basicFieldFilters(query, Keys.TITLE);
- finalQuery = finalQuery + this.basicFieldFilters(query, Keys.AUTHOR);
-
- if (this._deletedDocsStatus) {
- finalQuery = finalQuery + this.basicFieldFilters(query, Keys.TEXT);
- }
- return finalQuery;
- }
-
- basicFieldFilters(query: string, type: string): string {
- let mod = "";
- switch (type) {
- case Keys.AUTHOR: mod = " author_t:"; break;
- case Keys.TITLE: mod = " title_t:"; break;
- case Keys.TEXT: mod = " text_t:"; break;
- }
-
- const newWords: string[] = [];
- const oldWords = query.split(" ");
- oldWords.forEach(word => newWords.push(mod + word));
-
- query = newWords.join(" ");
-
- return query;
- }
-
- get fieldFiltersApplied() { return !(this._authorFieldStatus && this._titleFieldStatus); }
-
@action
- submitSearch = async (reset?: boolean) => {
- if (this.currentSelectedCollection !== undefined) {
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>();
- //d._docFilters = new List();
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
+ submitSearch = async () => {
+ this.resetSearch(false);
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
- this.currentSelectedCollection.props.Document._docFilters = new List();
- this.props.Document.selectedDoc = undefined;
- }
- if (reset) {
- this.layoutDoc._searchString = "";
- }
//this.props.Document._docFilters = new List();
- this.noresults = "";
+ this._noResults = "";
this.dataDoc[this.fieldKey] = new List<Doc>([]);
- this.headercount = 0;
this.children = 0;
- this.buckets = [];
- this.new_buckets = {};
let query = StrCast(this.layoutDoc._searchString);
Doc.SetSearchQuery(query);
- this.searchFullDB ? query = this.getFinalQuery(query) : console.log("local");
- this._results.forEach(result => {
- Doc.UnBrushDoc(result[0]);
- result[0].searchMatch = undefined;
- });
+ this._searchFullDB && (query = this.getFinalQuery(query));
this._results = [];
this._resultsSet.clear();
- this._isSearch = [];
- this._isSorted = [];
this._visibleElements = [];
this._visibleDocuments = [];
- if (StrCast(this.props.Document.searchQuery)) {
- if (this._timeout) { clearTimeout(this._timeout); this._timeout = undefined; }
- this._timeout = setTimeout(() => {
- console.log("Resubmitting search");
- }, 60000);
- }
- if (query !== "" || this.searchFullDB === "My Stuff") {
+ if (query || this._searchFullDB === "My Stuff") {
this._endIndex = 12;
this._maxSearchIndex = 0;
this._numTotalResults = -1;
- this.searchFullDB ? await this.getResults(query) : this.searchCollection(query);
+ this._searchFullDB ? await this.searchDatabase(query) : this.searchCollection(query);
runInAction(() => {
- this._resultsOpen = true;
this._searchbarOpen = true;
- this._openNoResults = true;
this.resultsScrolled();
-
});
}
}
- @observable searchFullDB = "DB";
-
- @observable _timeout: any = undefined;
-
- @observable firststring: string = "";
- @observable secondstring: string = "";
-
- @observable bucketcount: number[] = [];
- @observable buckets: Doc[] | undefined;
-
getAllResults = async (query: string) => {
return SearchUtil.Search(query, true, { fq: this.filterQuery, start: 0, rows: 10000000 });
}
private get filterQuery() {
- const types = ["preselement", "docholder", "search", "searchitem", "fonticonbox"]; // this.filterTypes;
const baseExpr = "NOT system_b:true";
- const authorExpr = this.searchFullDB === "My Stuff" ? ` author_t:${Doc.CurrentUserEmail}` : undefined;
- const includeDeleted = this.getDataStatus() ? "" : " NOT deleted_b:true";
- const typeExpr = this._onlyAliases ? "NOT {!join from=id to=proto_i}type_t:*" : `(type_t:* OR {!join from=id to=proto_i}type_t:*) ${types.map(type => `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`;
+ const authorExpr = this._searchFullDB === "My Stuff" ? ` author_t:${Doc.CurrentUserEmail}` : undefined;
+ const includeDeleted = this._deletedDocsStatus ? "" : " NOT deleted_b:true";
+ const typeExpr = this._onlyAliases ? "NOT {!join from=id to=proto_i}type_t:*" : `(type_t:* OR {!join from=id to=proto_i}type_t:*) ${this._blockedTypes.map(type => `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`;
// fq: type_t:collection OR {!join from=id to=proto_i}type_t:collection q:text_t:hello
- const query = [baseExpr, authorExpr, includeDeleted, typeExpr].filter(q => q).join(" AND ").replace(/AND $/, "");
- return query;
+ return [baseExpr, authorExpr, includeDeleted, typeExpr].filter(q => q).join(" AND ").replace(/AND $/, "");
}
- getDataStatus() { return this._deletedDocsStatus; }
-
@computed get primarySort() {
const suffixMap = (type: ColumnType) => {
switch (type) {
@@ -612,15 +281,12 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
return headers.reduce((p: Opt<string>, header: SchemaHeaderField) => p || (header.desc !== undefined && suffixMap(header.type) ? (header.heading + suffixMap(header.type) + (header.desc ? " desc" : " asc")) : undefined), undefined);
}
- private NumResults = 500;
- private lockPromise?: Promise<void>;
- getResults = async (query: string) => {
- if (this.lockPromise) {
- await this.lockPromise;
- }
- this.lockPromise = new Promise(async res => {
+
+ searchDatabase = async (query: string) => {
+ this._lockPromise && (await this._lockPromise);
+ this._lockPromise = new Promise(async res => {
while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) {
- this._curRequest = SearchUtil.Search(query, true, { onlyAliases: true, allowAliases: true, /*sort: this.primarySort,*/ fq: this.filterQuery, start: 0, rows: this.NumResults, hl: true, "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => {
+ this._curRequest = SearchUtil.Search(query, true, { onlyAliases: true, allowAliases: true, /*sort: this.primarySort,*/ fq: this.filterQuery, start: 0, rows: this._numResultsPerPage, hl: true, "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => {
// happens at the beginning
this.realTotalResults = res.numFound <= 0 ? 0 : res.numFound;
if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) {
@@ -635,50 +301,44 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
docs.forEach((doc, index) => highlights[doc[Id]] = highlightList[index]);
const filteredDocs = this.filterDocsByType(docs);
- runInAction(() => {
- filteredDocs.forEach((doc, i) => {
- const index = this._resultsSet.get(doc);
- const highlight = highlights[doc[Id]];
- const line = lines.get(doc[Id]) || [];
- const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)).filter(k => k) : [];
- // if (this.findCommonElements(hlights)) {
- // }
- if (index === undefined) {
- this._resultsSet.set(doc, this._results.length);
- this._results.push([doc, hlights, line]);
- } else {
- this._results[index][1].push(...hlights);
- this._results[index][2].push(...line);
- }
+ runInAction(() => filteredDocs.forEach((doc, i) => {
+ const index = this._resultsSet.get(doc);
+ const highlight = highlights[doc[Id]];
+ const line = lines.get(doc[Id]) || [];
+ const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)).filter(k => k) : [];
+ // if (this.findCommonElements(hlights)) {
+ // }
+ if (index === undefined) {
+ this._resultsSet.set(doc, this._results.length);
+ this._results.push([doc, hlights, line]);
+ } else {
+ this._results[index][1].push(...hlights);
+ this._results[index][2].push(...line);
+ }
- });
- });
+ }));
this._curRequest = undefined;
}));
- this._maxSearchIndex += this.NumResults;
+ this._maxSearchIndex += this._numResultsPerPage;
await this._curRequest;
}
this.resultsScrolled();
+
+ const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
+ this.docsforfilter = this._results.map(r => r[0]);
+ this.setSearchFilter(selectedCollection, this.filter ? this.docsforfilter : undefined);
res();
});
- return this.lockPromise;
+ return this._lockPromise;
}
- @observable noresults = "";
- collectionRef = React.createRef<HTMLSpanElement>();
+
startDragCollection = async () => {
const res = await this.getAllResults(this.getFinalQuery(StrCast(this.layoutDoc._searchString)));
const filtered = this.filterDocsByType(res.docs);
- const docs = filtered.map(doc => {
- const isProto = Doc.GetT(doc, "isPrototype", "boolean", true);
- if (isProto) {
- return Doc.MakeDelegate(doc);
- } else {
- return Doc.MakeAlias(doc);
- }
- });
+ const docs = filtered.map(doc => Doc.GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeDelegate(doc) : Doc.MakeAlias(doc));
let x = 0;
let y = 0;
for (const doc of docs.map(d => Doc.Layout(d))) {
@@ -702,35 +362,28 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
y += 300;
}
}
- return Docs.Create.SchemaDocument(Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []), DocListCast(this.dataDoc[this.fieldKey]), { _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString) });
+ const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).map(h => { const v = h[Copy](); v.color = "#f1efeb"; return v; });
+ return Docs.Create.SchemaDocument(headers, DocListCast(this.dataDoc[this.fieldKey]), { _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString) });
}
@action.bound
openSearch(e: React.SyntheticEvent) {
- this._results.forEach(result => {
- Doc.BrushDoc(result[0]);
- });
e.stopPropagation();
- this._openNoResults = false;
- this._resultsOpen = true;
+ this._results.forEach(result => Doc.BrushDoc(result[0]));
this._searchbarOpen = true;
}
- realTotalResults: number = 0;
-
@action.bound
- closeSearch = () => {
+ resetSearch = (close: boolean) => {
this._results.forEach(result => {
Doc.UnBrushDoc(result[0]);
- result[0].searchMatch = undefined;
+ Doc.ClearSearchMatches();
});
- //this.closeResults();
- this._searchbarOpen = false;
+ close && (this._searchbarOpen = false);
}
@action.bound
closeResults() {
- this._resultsOpen = false;
this._results = [];
this._resultsSet.clear();
this._visibleElements = [];
@@ -740,21 +393,10 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
this._curRequest = undefined;
}
- @observable _pageStart: number = 0;
- @observable _pageCount: number = SearchBox.NUM_SEARCH_RESULTS_PER_PAGE;
-
- @observable children: number = 0;
@action
resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => {
- if (!this._resultsRef.current) return;
this._endIndex = 30;
const headers = new Set<string>(["title", "author", "text", "type", "data", "*lastModified", "context"]);
- // if ((this._numTotalResults === 0 || this._results.length === 0) && this._openNoResults) {
- // if (this.noresults === "") {
- // this.noresults = "No search results :(";
- // }
- // return;
- // }
if (this._numTotalResults <= this._maxSearchIndex) {
this._numTotalResults = this._results.length;
@@ -766,155 +408,97 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
// undefined until a searchitem is put in there
this._visibleElements = Array<JSX.Element>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
this._visibleDocuments = Array<Doc>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
- // indicates if things are placeholders
- this._isSearch = Array<undefined>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
- this._isSorted = Array<undefined>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
-
}
- let max = this.NumResults;
+ let max = this._numResultsPerPage;
max > this._results.length ? max = this._results.length : console.log("");
for (let i = this._pageStart; i < max; i++) {
//if the index is out of the window then put a placeholder in
//should ones that have already been found get set to placeholders?
- if (this._isSearch[i] !== "search") {
- let result: [Doc, string[], string[]] | undefined = undefined;
-
- result = this._results[i];
- if (result) {
- const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
- const lines = new List<string>(result[2]);
- highlights.forEach((item) => headers.add(item));
- result[0].lines = lines;
- result[0].highlighting = highlights.join(", ");
- result[0].searchMatch = true;
- if (i < this._visibleDocuments.length) {
- this._visibleDocuments[i] = result[0];
- this._isSearch[i] = "search";
- Doc.BrushDoc(result[0]);
- Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]);
- this.children++;
- }
-
+ let result: [Doc, string[], string[]] | undefined = undefined;
+
+ result = this._results[i];
+ if (result) {
+ const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
+ const lines = new List<string>(result[2]);
+ highlights.forEach((item) => headers.add(item));
+ Doc.SetSearchMatch(result[0], { searchMatch: 1 });
+ if (i < this._visibleDocuments.length) {
+ this._visibleDocuments[i] = result[0];
+ Doc.BrushDoc(result[0]);
+ Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]);
+ this.children++;
}
-
}
}
- this.headerscale = headers.size;
- if (Cast(this.props.Document._docFilters, listSpec("string"), []).length === 0) {
- const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []);
- if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") {
- const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
- headers.forEach(header => {
- if (oldSchemaHeaders.includes(header) === false) {
- newSchemaHeaders.push(new SchemaHeaderField(header, "#f1efeb"));
- }
- });
- this.headercount = newSchemaHeaders.length;
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders);
- } else if (this.props.Document._schemaHeaders === undefined) {
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
- }
+ if (this.props.Document._schemaHeaders === undefined) {
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
}
if (this._maxSearchIndex >= this._numTotalResults) {
this._visibleElements.length = this._results.length;
this._visibleDocuments.length = this._results.length;
- this._isSearch.length = this._results.length;
}
}
- @observable headercount: number = 0;
- @observable headerscale: number = 0;
-
- findCommonElements(arr2: string[]) {
- const arr1 = ["layout", "data"];
- return arr1.some(item => arr2.includes(item));
- }
-
- @computed
- get resFull() { return this._numTotalResults <= 8; }
-
- @computed
- get resultHeight() { return this._numTotalResults * 70; }
- addButtonDoc = (doc: Doc) => Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc);
- remButtonDoc = (doc: Doc) => Doc.RemoveDocFromList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc);
- moveButtonDoc = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
-
- @computed get searchItemTemplate() { return Cast(Doc.UserDoc().searchItemTemplate, Doc, null); }
-
- @computed get viewspec() { return Cast(this.props.Document._docFilters, listSpec("string"), []); }
+ getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
+ panelHeight = () => this.props.PanelHeight();
+ selectElement = (doc: Doc) => { /* this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex)); */ };
+ returnHeight = () => 31 + 31 * 6;
+ returnLength = () => Math.min(window.innerWidth, 51 + 205 * Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length);
- getTransform = () => {
- return this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
- }
- panelHeight = () => {
- return this.props.PanelHeight();
- }
- selectElement = (doc: Doc) => {
- //this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex));
+ @action
+ changeSearchScope = (scope: string) => {
+ this.docsforfilter = undefined;
+ this.setSearchFilter(this.currentSelectedCollection, undefined);
+ this._searchFullDB = scope;
+ this.dataDoc[this.fieldKey] = new List<Doc>([]);
+ this.submitSearch();
}
- addDocument = (doc: Doc) => {
- return null;
+ @computed get scopeButtons() {
+ return <div style={{ height: 25, paddingLeft: "4px", paddingRight: "4px", border: "1px solid gray", borderRadius: "0.3em", borderBottom: !this.open ? "1px solid" : "none", }}>
+ <form className="beta" style={{ justifyContent: "space-evenly", display: "flex" }}>
+ <div style={{ display: "contents" }}>
+ <div className="radio" style={{ margin: 0 }}>
+ <label style={{ fontSize: 12, marginTop: 6 }} >
+ <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={!this._searchFullDB} onChange={() => this.changeSearchScope("")} />
+ Dashboard
+ </label>
+ </div>
+ <div className="radio" style={{ margin: 0 }}>
+ <label style={{ fontSize: 12, marginTop: 6 }} >
+ <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={this._searchFullDB?.length ? true : false} onChange={() => this.changeSearchScope("DB")} />
+ DB
+ <span onClick={action(() => this._searchFullDB = this._searchFullDB === "My Stuff" ? "DB" : "My Stuff")}>
+ {this._searchFullDB === "My Stuff" ? "(me)" : "(full)"}
+ </span>
+ </label>
+ </div>
+ </div>
+ </form>
+ </div>;
}
- @observable filter = false;
-
- @action newpage() {
- this._pageStart += SearchBox.NUM_SEARCH_RESULTS_PER_PAGE;
- this.dataDoc[this.fieldKey] = new List<Doc>([]);
- this.resultsScrolled();
- }
- returnHeight = () => 31 + 31 * 6;
- returnLength = () => {
- const cols = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length;
- return cols * 205 + 51;
- }
- @action
- changeSearchScope = (scope: string) => {
- scope && (this.filter = false);
- this.searchFullDB = scope;
- this.dataDoc[this.fieldKey] = new List<Doc>([]);
- if (this.currentSelectedCollection !== undefined) {
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>();
- d._docFilters = new List();
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
- this.currentSelectedCollection.props.Document._docFilters = new List();
- this.currentSelectedCollection.props.Document._searchDocs = undefined;
- this.currentSelectedCollection = undefined;
+ setSearchFilter = (collectionView: { props: { Document: Doc } }, docsForFilter: Doc[] | undefined) => {
+ if (collectionView) {
+ const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), null);
+ collectionView.props.Document._searchFilterDocs = docsForFilter?.length ? new List<Doc>(docsForFilter) : undefined;
+ collectionView.props.Document._docFilters = docsForFilter?.length && docFilters?.length ? new List<string>(docFilters) : undefined;
}
- this.submitSearch();
}
render() {
- this.props.Document._chromeStatus === "disabled";
- this.props.Document._searchDoc = true;
- const rows = this.children;
return (
<div style={{ pointerEvents: "all" }} className="searchBox-container">
- <div style={{ position: "absolute", left: 15, height: 32, alignItems: "center", display: "flex" }}>{Doc.CurrentUserEmail}</div>
+ <div style={{ position: "absolute", left: 15, height: 32, alignItems: "center", display: "flex" }}>{`${Doc.CurrentUserEmail}/${Cast(Doc.UserDoc().activeDashboard, Doc, null)?.title}`}</div>
<div className="searchBox-bar">
<div style={{ position: "relative", display: "flex", width: 450 }}>
- <input value={this.newsearchstring} autoComplete="off" onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this.inputRef}
+ <input value={this.newsearchstring} autoComplete="off" onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this._inputRef}
className="searchBox-barChild searchBox-input" onPointerDown={this.openSearch} onKeyPress={this.enter} onFocus={this.openSearch}
style={{ padding: 1, paddingLeft: 20, paddingRight: 60, color: "black", height: 20, width: 250 }} />
<div style={{ display: "flex", alignItems: "center" }}>
<div style={{ position: "absolute", left: 10 }}>
<Tooltip title={<div className="dash-tooltip" >drag search results as collection</div>}>
- <div><FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg"
+ <div ref={this.collectionRef}><FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg"
style={{ cursor: "hand", color: "black", padding: 1, position: "relative" }} /></div>
</Tooltip>
</div>
@@ -923,116 +507,39 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
</div>
<div style={{ cursor: "default", left: 235, position: "absolute", }}>
<Tooltip title={<div className="dash-tooltip" >only display documents matching search</div>} >
- <div><FontAwesomeIcon icon={"filter"} size="lg"
- style={{ cursor: "hand", padding: 1, backgroundColor: this.filter ? "white" : "lightgray", color: this.filter ? "black" : "white" }}
- onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined); }}
- onClick={action(() => {
- ///DONT Change without emailing andy r first.
- this.filter = !this.filter && !this.searchFullDB;
- if (this.filter === true && this.currentSelectedCollection !== undefined) {
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>(this.docsforfilter);
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>(this.docsforfilter);
- const newdocs = DocListCast(d.data);
- newdocs.forEach(newdoc => newarray.push(newdoc));
- }
- });
- docs = newarray;
- }
-
- this.currentSelectedCollection.props.Document._docFilters = new List<string>(this.viewspec);
- this.props.Document.selectedDoc = this.currentSelectedCollection.props.Document;
- }
- else if (this.filter === false && this.currentSelectedCollection !== undefined) {
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>();
- d._docFilters = new List();
- const newdocs = DocListCast(d.data);
- newdocs.forEach(newdoc => newarray.push(newdoc));
- }
- });
- docs = newarray;
- }
-
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
- this.currentSelectedCollection.props.Document._docFilters = new List();
- this.props.Document.selectedDoc = undefined;
- }
- }
- )} /></div>
- </Tooltip>
- </div>
- <div style={{
- height: 25,
- paddingLeft: "4px",
- paddingRight: "4px",
- border: "1px solid gray",
- borderRadius: "0.3em",
- borderBottom: !this.open ? "1px solid" : "none",
- }}>
- <form className="beta" style={{ justifyContent: "space-evenly", display: "flex" }}>
- <div style={{ display: "contents" }}>
- <div className="radio" style={{ margin: 0 }}>
- <label style={{ fontSize: 12, marginTop: 6 }} >
- <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={!this.searchFullDB} onChange={() => this.changeSearchScope("")} />
- Collection
- </label>
- </div>
- <div className="radio" style={{ margin: 0 }}>
- <label style={{ fontSize: 12, marginTop: 6 }} >
- <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={this.searchFullDB?.length ? true : false} onChange={() => this.changeSearchScope("DB")} />
- DB
- <span onClick={action(() => this.searchFullDB = this.searchFullDB === "My Stuff" ? "DB" : "My Stuff")}>
- {this.searchFullDB === "My Stuff" ? "(me)" : "(full)"}
- </span>
- </label>
- </div>
+ <div>
+ <FontAwesomeIcon icon={"filter"} size="lg"
+ style={{ cursor: "hand", padding: 1, backgroundColor: this.filter ? "white" : "lightgray", color: this.filter ? "black" : "white" }}
+ onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }}
+ onClick={action(() => this.setSearchFilter(this.currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} />
</div>
- </form>
+ </Tooltip>
</div>
+ {this.scopeButtons}
</div>
-
- </div>
- </div>
- <div style={{ zIndex: 20000, color: "black" }}>
- {this._searchbarOpen === true ?
+ </div >
+ </div >
+ {!this._searchbarOpen ? (null) :
+ <div style={{ zIndex: 20000, color: "black" }} ref={(r) => r?.focus()}>
<div style={{ display: "flex", justifyContent: "center", }}>
- {this.noresults === "" ? <div style={{ display: this.open ? "flex" : "none", overflow: "auto", }}>
- <CollectionView {...this.props}
+ <div style={{ display: this.open ? "flex" : "none", overflow: "auto", }}>
+ <CollectionSchemaView {...this.props}
+ CollectionView={undefined}
+ annotationsKey={""}
+ addDocument={returnFalse}
Document={this.props.Document}
moveDocument={returnFalse}
removeDocument={returnFalse}
PanelHeight={this.open ? this.returnHeight : returnZero}
PanelWidth={this.open ? this.returnLength : returnZero}
- overflow={length > window.innerWidth || rows > 6 ? true : false}
+ overflow={length > window.innerWidth || this.children > 6 ? true : false}
focus={this.selectElement}
ScreenToLocalTransform={Transform.Identity}
/>
- </div> :
- <div style={{ display: "flex", justifyContent: "center" }}><div style={{ height: 200, top: 54, minWidth: 400, position: "absolute", backgroundColor: "rgb(241, 239, 235)", display: "flex", justifyContent: "center", alignItems: "center", border: "black 1px solid", }}>
- <div>{this.noresults}</div>
- </div></div>}
- </div> : undefined}
- </div>
-
- <div className="searchBox-results" onScroll={this.resultsScrolled} style={{
- display: this._resultsOpen ? "flex" : "none",
- height: this.resFull ? "auto" : this.resultHeight,
- overflow: "visibile" // this.resFull ? "auto" : "visible"
- }} ref={this._resultsRef}>
- </div>
+ </div>
+ </div>
+ </div>
+ }
</div >
);
}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 3fdeb8e71..c2b491454 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -23,6 +23,7 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u
import { LinkManager } from "../client/util/LinkManager";
import JSZip = require("jszip");
import { saveAs } from "file-saver";
+import { result } from "lodash";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -91,6 +92,9 @@ export async function DocCastAsync(field: FieldResult): Promise<Opt<Doc>> {
export function DocListCast(field: FieldResult): Doc[] {
return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[];
}
+export function DocListCastOrNull(field: FieldResult) {
+ return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined;
+}
export const WidthSym = Symbol("Width");
export const HeightSym = Symbol("Height");
@@ -206,11 +210,11 @@ export class Doc extends RefField {
private [Self] = this;
private [SelfProxy]: any;
- public [FieldsSym] = (clear?: boolean) => clear ? this.___fields = this.___fieldKeys = {} : this.___fields;
+ public [FieldsSym](clear?: boolean) { return clear ? this.___fields = this.___fieldKeys = {} : this.___fields; }
public [WidthSym] = () => NumCast(this[SelfProxy]._width);
public [HeightSym] = () => NumCast(this[SelfProxy]._height);
public [ToScriptString] = () => `DOC-"${this[Self][Id]}"-`;
- public [ToString] = () => `Doc(${GetEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`;
+ public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? "-inaccessible-" : this[SelfProxy].title})`;
public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; }
public get [DataSym]() {
const self = this[SelfProxy];
@@ -878,6 +882,7 @@ export namespace Doc {
export class DocBrush {
BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
+ SearchMatchDoc: ObservableMap<Doc, { searchMatch: number }> = new ObservableMap();
}
const brushManager = new DocBrush();
@@ -903,6 +908,33 @@ export namespace Doc {
export function SetSelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; }
export function GetSelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
export function SetUserDoc(doc: Doc) { manager._user_doc = doc; }
+
+ export function IsSearchMatch(doc: Doc) {
+ return computedFn(function IsSearchMatch(doc: Doc) {
+ return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) :
+ brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
+ })(doc);
+ }
+ export function IsSearchMatchUnmemoized(doc: Doc) {
+ return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) :
+ brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
+ }
+ export function SetSearchMatch(doc: Doc, results: { searchMatch: number }) {
+ if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
+ brushManager.SearchMatchDoc.set(doc, results);
+ return doc;
+ }
+ export function SearchMatchNext(doc: Doc, backward: boolean) {
+ if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
+ const result = brushManager.SearchMatchDoc.get(doc);
+ const num = Math.abs(result?.searchMatch || 0) + 1;
+ runInAction(() => result && brushManager.SearchMatchDoc.set(doc, { searchMatch: backward ? -num : num }));
+ return doc;
+ }
+ export function ClearSearchMatches() {
+ brushManager.SearchMatchDoc.clear();
+ }
+
export function IsBrushed(doc: Doc) {
return computedFn(function IsBrushed(doc: Doc) {
return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc));
@@ -996,10 +1028,10 @@ export namespace Doc {
const fieldVal = doc[key];
if (Cast(fieldVal, listSpec("string"), []).length) {
const vals = Cast(fieldVal, listSpec("string"), []);
- return vals.some(v => v === value);
+ return vals.some(v => v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
const fieldStr = Field.toString(fieldVal as Field);
- return fieldStr === value;
+ return fieldStr.includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
export function deiconifyView(doc: any) {
@@ -1028,18 +1060,14 @@ export namespace Doc {
}
}
- export function aliasDocs(field: any) {
- return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d)));
- }
-
// filters document in a container collection:
// all documents with the specified value for the specified key are included/excluded
// based on the modifiers :"check", "x", undefined
- export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "match" | "check" | "x" | undefined) {
+ export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "remove" | "match" | "check" | "x" | undefined) {
const docFilters = Cast(container._docFilters, listSpec("string"), []);
runInAction(() => {
for (let i = 0; i < docFilters.length; i += 3) {
- if (docFilters[i] === key && (docFilters[i + 1] === value || modifiers === "match")) {
+ if (docFilters[i] === key && (docFilters[i + 1] === value || modifiers === "match" || modifiers === "remove")) {
if (docFilters[i + 2] === modifiers && modifiers && docFilters[i + 1] === value) return;
docFilters.splice(i, 3);
container._docFilters = new List<string>(docFilters);
@@ -1049,7 +1077,7 @@ export namespace Doc {
if (typeof modifiers === "string") {
if (!docFilters.length && modifiers === "match" && value === undefined) {
container._docFilters = undefined;
- } else {
+ } else if (modifiers !== "remove") {
docFilters.push(key);
docFilters.push(value);
docFilters.push(modifiers);
@@ -1245,8 +1273,15 @@ Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
Scripting.addGlobal(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); });
Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); });
+Scripting.addGlobal(function copyDragFactory(dragFactory: Doc) {
+ const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
+ if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
+ dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
+ Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true);
+ }
+ return ndoc;
+});
Scripting.addGlobal(function copyField(field: any) { return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field; });
-Scripting.addGlobal(function aliasDocs(field: any) { return Doc.aliasDocs(field); });
Scripting.addGlobal(function docList(field: any) { return DocListCast(field); });
Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
diff --git a/src/fields/List.ts b/src/fields/List.ts
index a9da75abb..3601f282b 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -1,14 +1,16 @@
-import { Deserializable, autoObject, afterDocDeserialize } from "../client/util/SerializationHelper";
+import { action, observable, runInAction } from "mobx";
+import { alias, list, serializable } from "serializr";
+import { DocServer } from "../client/DocServer";
+import { Scripting } from "../client/util/Scripting";
+import { afterDocDeserialize, autoObject, Deserializable } from "../client/util/SerializationHelper";
import { Field } from "./Doc";
-import { setter, getter, deleteProperty, updateFunction } from "./util";
-import { serializable, alias, list } from "serializr";
-import { observable, action, runInAction } from "mobx";
+import { Copy, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from "./FieldSymbols";
import { ObjectField } from "./ObjectField";
-import { RefField } from "./RefField";
import { ProxyField } from "./Proxy";
-import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy, Id } from "./FieldSymbols";
-import { Scripting } from "../client/util/Scripting";
-import { DocServer } from "../client/DocServer";
+import { RefField } from "./RefField";
+import { listSpec } from "./Schema";
+import { Cast } from "./Types";
+import { deleteProperty, getter, setter, updateFunction } from "./util";
const listHandlers: any = {
/// Mutator methods
@@ -328,4 +330,9 @@ class ListImpl<T extends Field> extends ObjectField {
export type List<T extends Field> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
export const List: { new <T extends Field>(fields?: T[]): List<T> } = ListImpl as any;
-Scripting.addGlobal("List", List); \ No newline at end of file
+Scripting.addGlobal("List", List);
+Scripting.addGlobal(function compareLists(l1: any, l2: any) {
+ const L1 = Cast(l1, listSpec("string"), []);
+ const L2 = Cast(l2, listSpec("string"), []);
+ return !L1 && !L2 ? true : L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true);
+}, "compare two lists"); \ No newline at end of file
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 52730ed00..47efccc99 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -38,14 +38,14 @@ const scriptSchema = createSimpleSchema({
});
async function deserializeScript(script: ScriptField) {
- if (script.script.originalScript === 'getCopy(this.dragFactory, true)') {
- return (script as any).script = (ScriptField.GetCopyOfDragFactory ?? (ScriptField.GetCopyOfDragFactory = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')))?.script;
+ if (script.script.originalScript === 'copyDragFactory(this.dragFactory)') {
+ return (script as any).script = (ScriptField.GetCopyOfDragFactory ?? (ScriptField.GetCopyOfDragFactory = ScriptField.MakeFunction('copyDragFactory(this.dragFactory)')))?.script;
}
if (script.script.originalScript === 'links(self)') {
return (script as any).script = (ScriptField.LinksSelf ?? (ScriptField.LinksSelf = ComputedField.MakeFunction('links(self)')))?.script;
}
- if (script.script.originalScript === 'openOnRight(getCopy(this.dragFactory, true))') {
- return (script as any).script = (ScriptField.OpenOnRight ?? (ScriptField.OpenOnRight = ComputedField.MakeFunction('openOnRight(getCopy(this.dragFactory, true))')))?.script;
+ if (script.script.originalScript === 'openOnRight(copyDragFactory(this.dragFactory))') {
+ return (script as any).script = (ScriptField.OpenOnRight ?? (ScriptField.OpenOnRight = ComputedField.MakeFunction('openOnRight(copyDragFactory(this.dragFactory))')))?.script;
}
if (script.script.originalScript === 'deiconifyView(self)') {
return (script as any).script = (ScriptField.DeiconifyView ?? (ScriptField.DeiconifyView = ComputedField.MakeFunction('deiconifyView(self)')))?.script;
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 848a648e1..4b75b1a32 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -83,6 +83,8 @@ export const documentSchema = createSchema({
textTransform: "string",
treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden
treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
+ treeViewLockExpandedView: "boolean", // whether the expanded view can be changed
+ treeViewDefaultExpandedView: "string", // name of field whose contents are displayed by default
treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
// interaction and linking properties
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 8ae504f1b..738de09c6 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -3,7 +3,7 @@ import "./ImageUpload.scss";
import React = require('react');
import { observer } from 'mobx-react';
import { observable, action, computed } from 'mobx';
-import { Utils, emptyPath, returnFalse, emptyFunction, returnOne, returnZero, returnTrue, returnEmptyFilter } from '../Utils';
+import { Utils, emptyPath, returnFalse, emptyFunction, returnOne, returnZero, returnTrue, returnEmptyFilter, returnEmptyDoclist } from '../Utils';
import { Doc, Opt } from '../fields/Doc';
import { Cast, FieldValue } from '../fields/Types';
import { listSpec } from '../fields/Schema';
@@ -88,6 +88,7 @@ export class AudioUpload extends React.Component {
rootSelected={returnTrue}
removeDocument={undefined}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
onClick={undefined}
ScreenToLocalTransform={Transform.Identity}
ContentScaling={returnOne}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index c5e395d2f..05a695147 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -14,7 +14,7 @@ import { action, computed, observable, reaction, trace, runInAction } from 'mobx
import { observer } from 'mobx-react';
import { Doc, DocListCast } from '../fields/Doc';
import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter } from '../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter, returnEmptyDoclist } from '../Utils';
import { Docs, DocumentOptions } from '../client/documents/Documents';
import { Scripting } from '../client/util/Scripting';
import { DocumentView } from '../client/views/nodes/DocumentView';
@@ -58,7 +58,7 @@ export class MobileInterface extends React.Component {
@observable private _menuListView: boolean = false; //to switch between menu view (list / icon)
@observable private _ink: boolean = false; //toggle whether ink is being dispalyed
@observable private _homeMenu: boolean = true; // to determine whether currently at home menu
- @observable private _child: Doc | null = null; // currently selected document
+ @observable private dashboards: Doc | null = null; // currently selected document
@observable private _activeDoc: Doc = this._mainDoc; // doc updated as the active mobile page is updated (initially home menu)
@observable private _homeDoc: Doc = this._mainDoc; // home menu as a document
@observable private _parents: Array<Doc> = []; // array of parent docs (for pathbar)
@@ -136,19 +136,19 @@ export class MobileInterface extends React.Component {
back = () => {
const header = document.getElementById("header") as HTMLElement;
const doc = Cast(this._parents.pop(), Doc) as Doc; // Parent document
- // Case 1: Parent document is 'workspaces'
+ // Case 1: Parent document is 'dashboards'
if (doc === Cast(this._library, Doc) as Doc) {
- this._child = null;
+ this.dashboards = null;
this._library.then(library => this.switchCurrentView(library));
// Case 2: Parent document is the 'home' menu (root node)
} else if (doc === Cast(this._homeDoc, Doc) as Doc) {
this._homeMenu = true;
this._parents = [];
- this._child = null;
+ this.dashboards = null;
this.switchCurrentView(this._homeDoc);
// Case 3: Parent document is any document
} else if (doc) {
- this._child = doc;
+ this.dashboards = doc;
this.switchCurrentView(doc);
this._homeMenu = false;
header.textContent = String(doc.title);
@@ -164,7 +164,7 @@ export class MobileInterface extends React.Component {
if (!this._homeMenu || this._sidebarActive) {
this._homeMenu = true;
this._parents = [];
- this._child = null;
+ this.dashboards = null;
this.switchCurrentView(this._homeDoc);
}
if (this._sidebarActive) {
@@ -173,14 +173,14 @@ export class MobileInterface extends React.Component {
}
/**
- * Return to primary Workspace in library (Workspaces Doc)
+ * Return to primary Dashboard in library (Dashboards Doc)
*/
@action
returnMain = () => {
this._parents = [this._homeDoc];
this._library.then(library => this.switchCurrentView(library));
this._homeMenu = false;
- this._child = null;
+ this.dashboards = null;
}
/**
@@ -194,7 +194,7 @@ export class MobileInterface extends React.Component {
/**
* DocumentView for graphic display of all documents
*/
- @computed get displayWorkspaces() {
+ @computed get displayDashboards() {
return !this.mainContainer ? (null) :
<div style={{ position: "relative", top: '198px', height: `calc(100% - 350px)`, width: "100%", left: "0%" }}>
<DocumentView
@@ -220,6 +220,7 @@ export class MobileInterface extends React.Component {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
/>
@@ -243,7 +244,7 @@ export class MobileInterface extends React.Component {
this._parents.push(this._activeDoc);
this.switchCurrentView(doc);
this._homeMenu = false;
- this._child = doc;
+ this.dashboards = doc;
}
});
}
@@ -258,7 +259,7 @@ export class MobileInterface extends React.Component {
this._parents.push(this._activeDoc);
this.switchCurrentView(doc);
this._homeMenu = false;
- this._child = doc;
+ this.dashboards = doc;
this.toggleSidebar();
}
@@ -290,13 +291,13 @@ export class MobileInterface extends React.Component {
handlePathClick = async (doc: Doc, index: number) => {
const library = await this._library;
if (doc === library) {
- this._child = null;
+ this.dashboards = null;
this.switchCurrentView(doc);
this._parents.length = index;
} else if (doc === this._homeDoc) {
this.returnHome();
} else {
- this._child = doc;
+ this.dashboards = doc;
this.switchCurrentView(doc);
this._parents.length = index;
}
@@ -321,13 +322,13 @@ export class MobileInterface extends React.Component {
</div>
);
}
- // stores workspace documents as 'workspaces' variable
- let workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc) as Doc;
- if (this._child) {
- workspaces = this._child;
+ // stores dashboards documents as 'dashboards' variable
+ let dashboards = Cast(Doc.UserDoc().myDashboards, Doc) as Doc;
+ if (this.dashboards) {
+ dashboards = this.dashboards;
}
// returns a list of navbar buttons as 'buttons'
- const buttons = DocListCast(workspaces.data).map((doc: Doc, index: any) => {
+ const buttons = DocListCast(dashboards.data).map((doc: Doc, index: any) => {
if (doc.type !== "ink") {
return (
<div
@@ -357,7 +358,7 @@ export class MobileInterface extends React.Component {
{this.renderPathbar()}
<div className={`sidebar ${this._sidebarActive ? "active" : ""}`}>
<div className="sidebarButtons">
- {this._child ?
+ {this.dashboards ?
<>
{buttons}
<div
@@ -365,7 +366,7 @@ export class MobileInterface extends React.Component {
onClick={this.returnMain}
style={{ opacity: 0.7 }}>
<FontAwesomeIcon className="right" icon="angle-double-left" size="lg" />
- <div className="item-type">Return to workspaces</div>
+ <div className="item-type">Return to dashboards</div>
</div>
</> :
<>
@@ -373,9 +374,9 @@ export class MobileInterface extends React.Component {
<div
className="item"
style={{ opacity: 0.7 }}
- onClick={() => this.createNewWorkspace()}>
+ onClick={() => this.createNewDashboard()}>
<FontAwesomeIcon className="right" icon="plus" size="lg" />
- <div className="item-type">Create New Workspace</div>
+ <div className="item-type">Create New Dashboard</div>
</div>
</>
}
@@ -388,27 +389,27 @@ export class MobileInterface extends React.Component {
}
/**
- * Handles the 'Create New Workspace' button in the menu (taken from MainView.tsx)
+ * Handles the 'Create New Dashboard' button in the menu (taken from MainView.tsx)
*/
@action
- createNewWorkspace = async (id?: string) => {
- const workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc) as Doc;
- const workspaceCount = DocListCast(workspaces.data).length + 1;
+ createNewDashboard = async (id?: string) => {
+ const scens = Cast(Doc.UserDoc().myDashboards, Doc) as Doc;
+ const dashboardCount = DocListCast(scens.data).length + 1;
const freeformOptions: DocumentOptions = {
x: 0,
y: 400,
- title: "Collection " + workspaceCount,
+ title: "Collection " + dashboardCount,
};
const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
- const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
+ const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Dashboard ${dashboardCount}` }, id, "row");
const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
- const cloneWorkspace = ScriptField.MakeScript(`cloneWorkspace()`);
- workspaceDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneWorkspace!]);
- workspaceDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Workspace Layout"]);
+ const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`);
+ dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneDashboard!]);
+ dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Dashboard Layout"]);
- Doc.AddDocToList(workspaces, "data", workspaceDoc);
+ Doc.AddDocToList(scens, "data", dashboardDoc);
}
// Button for switching between pen and ink mode
@@ -612,7 +613,7 @@ export class MobileInterface extends React.Component {
}
// Radial menu can only be used if it is a colleciton and it is not a homeDoc
- // (and cannot be used on Workspace to avoid pin to presentation opening on right)
+ // (and cannot be used on Dashboard to avoid pin to presentation opening on right)
@computed get displayRadialMenu() {
return this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
this._activeDoc._viewType !== CollectionViewType.Docking ? <RadialMenu /> : (null);
@@ -657,7 +658,7 @@ export class MobileInterface extends React.Component {
{this.drawInk}
{this.uploadImageButton}
</div>
- {this.displayWorkspaces}
+ {this.displayDashboards}
{this.renderDefaultContent}
</GestureOverlay>
{this.displayRadialMenu}
@@ -669,7 +670,7 @@ export class MobileInterface extends React.Component {
//Global functions for mobile menu
Scripting.addGlobal(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); },
- "opens the library to navigate through workspaces on Dash Mobile");
+ "opens the library to navigate through dashboards on Dash Mobile");
Scripting.addGlobal(function openMobileUploads() { return MobileInterface.Instance.toggleUpload(); },
"opens the upload files menu for Dash Mobile");
Scripting.addGlobal(function switchToMobileUploadCollection() { return MobileInterface.Instance.switchToMobileUploads(); },