aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.DS_Storebin0 -> 8196 bytes
-rw-r--r--src/client/DocServer.ts128
-rw-r--r--src/client/documents/Documents.ts29
-rw-r--r--src/client/util/CurrentUserUtils.ts168
-rw-r--r--src/client/util/DocumentManager.ts1
-rw-r--r--src/client/util/DragManager.ts2
-rw-r--r--src/client/util/DropConverter.ts2
-rw-r--r--src/client/util/HypothesisUtils.ts2
-rw-r--r--src/client/util/InteractionUtils.tsx4
-rw-r--r--src/client/util/KeyCodes.ts2
-rw-r--r--src/client/util/SearchUtil.ts12
-rw-r--r--src/client/util/SelectionManager.ts35
-rw-r--r--src/client/util/SettingsManager.scss13
-rw-r--r--src/client/util/SettingsManager.tsx8
-rw-r--r--src/client/util/SharingManager.scss21
-rw-r--r--src/client/util/SharingManager.tsx74
-rw-r--r--src/client/views/.DS_Storebin10244 -> 10244 bytes
-rw-r--r--src/client/views/AntimodeMenu.tsx6
-rw-r--r--src/client/views/DocComponent.tsx27
-rw-r--r--src/client/views/DocumentButtonBar.tsx2
-rw-r--r--src/client/views/DocumentDecorations.scss34
-rw-r--r--src/client/views/DocumentDecorations.tsx75
-rw-r--r--src/client/views/GestureOverlay.tsx4
-rw-r--r--src/client/views/GlobalKeyHandler.ts14
-rw-r--r--src/client/views/InkingStroke.tsx20
-rw-r--r--src/client/views/MainView.scss6
-rw-r--r--src/client/views/MainView.tsx167
-rw-r--r--src/client/views/MainViewNotifs.scss20
-rw-r--r--src/client/views/MainViewNotifs.tsx38
-rw-r--r--src/client/views/OverlayView.scss2
-rw-r--r--src/client/views/OverlayView.tsx2
-rw-r--r--src/client/views/PreviewCursor.tsx1
-rw-r--r--src/client/views/PropertiesButtons.tsx94
-rw-r--r--src/client/views/ScriptingRepl.tsx2
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx2
-rw-r--r--src/client/views/collections/CollectionDockingView.scss76
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx89
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx2
-rw-r--r--src/client/views/collections/CollectionMapView.tsx19
-rw-r--r--src/client/views/collections/CollectionMenu.scss64
-rw-r--r--src/client/views/collections/CollectionMenu.tsx241
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx457
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx413
-rw-r--r--src/client/views/collections/CollectionSchemaMovableTableHOC.tsx49
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss124
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx138
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx9
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx4
-rw-r--r--src/client/views/collections/CollectionSubView.tsx66
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx28
-rw-r--r--src/client/views/collections/CollectionView.tsx129
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx14
-rw-r--r--src/client/views/collections/SchemaTable.tsx130
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx178
-rw-r--r--src/client/views/collections/collectionFreeForm/FormatShapePane.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx67
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.scss15
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.tsx52
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx4
-rw-r--r--src/client/views/globalCssVariables.scss2
-rw-r--r--src/client/views/globalCssVariables.scss.d.ts1
-rw-r--r--src/client/views/linking/LinkEditor.tsx4
-rw-r--r--src/client/views/nodes/AudioBox.scss16
-rw-r--r--src/client/views/nodes/AudioBox.tsx200
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx52
-rw-r--r--src/client/views/nodes/DocHolderBox.tsx2
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx26
-rw-r--r--src/client/views/nodes/DocumentView.tsx96
-rw-r--r--src/client/views/nodes/FieldView.tsx17
-rw-r--r--src/client/views/nodes/FontIconBox.scss21
-rw-r--r--src/client/views/nodes/FontIconBox.tsx41
-rw-r--r--src/client/views/nodes/ImageBox.tsx4
-rw-r--r--src/client/views/nodes/LinkBox.tsx2
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx11
-rw-r--r--src/client/views/nodes/MenuIconBox.tsx6
-rw-r--r--src/client/views/nodes/PDFBox.tsx10
-rw-r--r--src/client/views/nodes/PresBox.scss226
-rw-r--r--src/client/views/nodes/PresBox.tsx890
-rw-r--r--src/client/views/nodes/WebBox.tsx10
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx4
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx86
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx83
-rw-r--r--src/client/views/pdf/PDFMenu.tsx8
-rw-r--r--src/client/views/pdf/PDFViewer.tsx15
-rw-r--r--src/client/views/presentationview/PresElementBox.scss4
-rw-r--r--src/client/views/presentationview/PresElementBox.tsx145
-rw-r--r--src/client/views/search/SearchBox.tsx909
-rw-r--r--src/fields/Doc.ts25
-rw-r--r--src/fields/RichTextField.ts2
-rw-r--r--src/fields/SchemaHeaderField.ts2
-rw-r--r--src/fields/ScriptField.ts12
-rw-r--r--src/fields/documentSchemas.ts2
-rw-r--r--src/fields/util.ts47
-rw-r--r--src/mobile/ImageUpload.tsx2
-rw-r--r--src/mobile/MobileInterface.tsx16
-rw-r--r--src/mobile/MobileMenu.scss271
-rw-r--r--src/server/ApiManagers/SearchManager.ts5
-rw-r--r--src/server/ApiManagers/UploadManager.ts2
-rw-r--r--src/server/Search.ts6
-rw-r--r--src/server/websocket.ts6
102 files changed, 3648 insertions, 3036 deletions
diff --git a/src/.DS_Store b/src/.DS_Store
new file mode 100644
index 000000000..299b902c6
--- /dev/null
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index a36dfaf69..dde75497c 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -261,7 +261,7 @@ export namespace DocServer {
} else {
// CACHED => great, let's just return the cached field we have
return Promise.resolve(cached).then(field => {
- (field instanceof Doc) && fetchProto(field);
+ //(field instanceof Doc) && fetchProto(field);
return field;
});
}
@@ -330,72 +330,76 @@ export namespace DocServer {
}
}
- // 2) synchronously, we emit a single callback to the server requesting the serialized (i.e. represented by a string)
- // fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of
- // the fields have been returned from the server
- const getSerializedFields: Promise<any> = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
-
- // 3) when the serialized RefFields have been received, go head and begin deserializing them into objects.
- // Here, once deserialized, we also invoke .proto to 'load' the documents' prototypes, which ensures that all
- // future .proto calls on the Doc won't have to go farther than the cache to get their actual value.
- const deserializeFields = getSerializedFields.then(async fields => {
- const fieldMap: { [id: string]: RefField } = {};
- const proms: Promise<void>[] = [];
- runInAction(() => {
- for (const field of fields) {
- if (field !== undefined && field !== null && !_cache[field.id]) {
- // deserialize
- const cached = _cache[field.id];
- if (!cached) {
- const prom = SerializationHelper.Deserialize(field).then(deserialized => {
- fieldMap[field.id] = deserialized;
-
- //overwrite or delete any promises (that we inserted as flags
- // to indicate that the field was in the process of being fetched). Now everything
- // should be an actual value within or entirely absent from the cache.
- if (deserialized !== undefined) {
- _cache[field.id] = deserialized;
- } else {
- delete _cache[field.id];
- }
- return deserialized;
- });
- // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
- // we set the value at the field's id to a promise that will resolve to the field.
- // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
- // The mapping in the .then call ensures that when other callers await these promises, they'll
- // get the resolved field
- _cache[field.id] = prom;
-
- // adds to a list of promises that will be awaited asynchronously
- proms.push(prom);
- } else if (cached instanceof Promise) {
- proms.push(cached as any);
+ if (requestedIds.length) {
+
+ // 2) synchronously, we emit a single callback to the server requesting the serialized (i.e. represented by a string)
+ // fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of
+ // the fields have been returned from the server
+ const getSerializedFields: Promise<any> = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
+
+ // 3) when the serialized RefFields have been received, go head and begin deserializing them into objects.
+ // Here, once deserialized, we also invoke .proto to 'load' the documents' prototypes, which ensures that all
+ // future .proto calls on the Doc won't have to go farther than the cache to get their actual value.
+ const deserializeFields = getSerializedFields.then(async fields => {
+ const fieldMap: { [id: string]: RefField } = {};
+ const proms: Promise<void>[] = [];
+ runInAction(() => {
+ for (const field of fields) {
+ if (field !== undefined && field !== null && !_cache[field.id]) {
+ // deserialize
+ const cached = _cache[field.id];
+ if (!cached) {
+ const prom = SerializationHelper.Deserialize(field).then(deserialized => {
+ fieldMap[field.id] = deserialized;
+
+ //overwrite or delete any promises (that we inserted as flags
+ // to indicate that the field was in the process of being fetched). Now everything
+ // should be an actual value within or entirely absent from the cache.
+ if (deserialized !== undefined) {
+ _cache[field.id] = deserialized;
+ } else {
+ delete _cache[field.id];
+ }
+ return deserialized;
+ });
+ // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
+ // we set the value at the field's id to a promise that will resolve to the field.
+ // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
+ // The mapping in the .then call ensures that when other callers await these promises, they'll
+ // get the resolved field
+ _cache[field.id] = prom;
+
+ // adds to a list of promises that will be awaited asynchronously
+ proms.push(prom);
+ } else if (cached instanceof Promise) {
+ proms.push(cached as any);
+ }
+ } else if (_cache[field.id] instanceof Promise) {
+ proms.push(_cache[field.id] as any);
+ (_cache[field.id] as any).then((f: any) => fieldMap[field.id] = f);
+ } else if (field) {
+ proms.push(_cache[field.id] as any);
+ fieldMap[field.id] = field;
}
- } else if (_cache[field.id] instanceof Promise) {
- proms.push(_cache[field.id] as any);
- (_cache[field.id] as any).then((f: any) => fieldMap[field.id] = f);
- } else if (field) {
- proms.push(_cache[field.id] as any);
- fieldMap[field.id] = field;
}
- }
+ });
+ await Promise.all(proms);
+ return fieldMap;
});
- await Promise.all(proms);
- return fieldMap;
- });
- // 5) at this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose
- // prototype documents, if any, have also been fetched and cached.
- const fields = await deserializeFields;
+ // 5) at this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose
+ // prototype documents, if any, have also been fetched and cached.
+ const fields = await deserializeFields;
- // 6) with this confidence, we can now go through and update the cache at the ids of the fields that
- // we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given
- // id to the soon-to-be-returned field mapping.
- requestedIds.forEach(id => {
- const field = fields[id];
- map[id] = field;
- });
+ // 6) with this confidence, we can now go through and update the cache at the ids of the fields that
+ // we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given
+ // id to the soon-to-be-returned field mapping.
+ requestedIds.forEach(id => {
+ const field = fields[id];
+ map[id] = field;
+ });
+
+ }
// 7) those promises we encountered in the else if of 1), which represent
// other callers having already submitted a request to the server for (a) document(s)
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e54c89d9f..7d114d417 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -20,7 +20,7 @@ import { dropActionType } from "../util/DragManager";
import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox";
import { LinkManager } from "../util/LinkManager";
import { Scripting } from "../util/Scripting";
-import { UndoManager } from "../util/UndoManager";
+import { UndoManager, undoBatch } from "../util/UndoManager";
import { DocumentType } from "./DocumentTypes";
import { SearchBox } from "../views/search/SearchBox";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
@@ -112,6 +112,7 @@ export interface DocumentOptions {
_columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden
isTemplateForField?: string; // the field key for which the containing document is a rendering template
isTemplateDoc?: boolean;
+ watchedDocuments?: Doc; // list of documents to "watch" in an icon doc to display a badge
targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc)
templates?: List<string>;
hero?: ImageField; // primary image that best represents a compound document (e.g., for a buxton device document that has multiple images)
@@ -143,8 +144,6 @@ export interface DocumentOptions {
presTransition?: number; //the time taken for the transition TO a document
presDuration?: number; //the duration of the slide in presentation view
presProgressivize?: boolean;
- // xArray?: number[];
- // yArray?: number[];
borderRounding?: string;
boxShadow?: string;
dontRegisterChildViews?: boolean;
@@ -174,6 +173,7 @@ export interface DocumentOptions {
onPointerUp?: ScriptField;
dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script
+ clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script
onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop
clipboard?: Doc;
UseCors?: boolean;
@@ -183,7 +183,8 @@ export interface DocumentOptions {
targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script
searchFileTypes?: List<string>; // file types allowed in a search query
strokeWidth?: number;
- stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
+ cloneFieldFilter?: List<string>; // fields not to copy when the document is cloned
+ _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
treeViewPreventOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expand/collapse state to be independent of other views of the same document in the tree view
treeViewHideTitle?: boolean; // whether to hide the title of a tree view
treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
@@ -440,7 +441,7 @@ export namespace Docs {
// whatever options pertain to this specific prototype
const options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) };
options.layout = layout.view?.LayoutString(layout.dataField);
- const doc = Doc.assign(new Doc(prototypeId, true), { layoutKey: "layout", ...options });
+ const doc = Doc.assign(new Doc(prototypeId, true), { system: true, layoutKey: "layout", ...options });
doc.layout_keyValue = KeyValueBox.LayoutString("");
return doc;
}
@@ -562,6 +563,7 @@ export namespace Docs {
if (!("creationDate" in protoProps)) {
protoProps.creationDate = new DateField;
+ protoProps[`${fieldKey}-lastModified`] = new DateField;
}
protoProps.isPrototype = true;
@@ -573,7 +575,7 @@ export namespace Docs {
// without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
dataDoc[fieldKey + "-annotations"] = new List<Doc>();
- dataDoc.aliases = new List<Doc>();
+ dataDoc.aliases = new List<Doc>([viewDoc]);
proto.links = ComputedField.MakeFunction("links(self)");
@@ -1038,7 +1040,7 @@ export namespace DocUtils {
description: "Add Note ...",
subitems: DocListCast((Doc.UserDoc()["template-notes"] as Doc).data).map((note, i) => ({
description: ":" + StrCast(note.title),
- event: (args: { x: number, y: number }) => {
+ event: undoBatch((args: { x: number, y: number }) => {
const textDoc = Docs.Create.TextDocument("", {
_width: 200, x, y, _autoHeight: note._autoHeight !== false,
title: StrCast(note.title) + "#" + (note.aliasCount = NumCast(note.aliasCount) + 1)
@@ -1046,7 +1048,7 @@ export namespace DocUtils {
textDoc.layoutKey = "layout_" + note.title;
textDoc[textDoc.layoutKey] = note;
docTextAdder(textDoc);
- },
+ }),
icon: "eye"
})) as ContextMenuProps[],
icon: "eye"
@@ -1055,7 +1057,7 @@ export namespace DocUtils {
description: "Add Template Doc ...",
subitems: DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc).map((dragDoc, i) => ({
description: ":" + StrCast(dragDoc.title),
- event: (args: { x: number, y: number }) => {
+ event: undoBatch((args: { x: number, y: number }) => {
const newDoc = Doc.ApplyTemplate(dragDoc);
if (newDoc) {
newDoc.author = Doc.CurrentUserEmail;
@@ -1063,7 +1065,7 @@ export namespace DocUtils {
newDoc.y = y;
docAdder(newDoc);
}
- },
+ }),
icon: "eye"
})) as ContextMenuProps[],
icon: "eye"
@@ -1162,7 +1164,7 @@ export namespace DocUtils {
export async function addFieldEnumerations(doc: Opt<Doc>, enumeratedFieldKey: string, enumerations: { title: string, _backgroundColor?: string, color?: string }[]) {
let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey);
if (!(optionsCollection instanceof Doc)) {
- optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey);
+ optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set`, system: true }, enumeratedFieldKey);
Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
}
const options = optionsCollection as Doc;
@@ -1177,7 +1179,7 @@ export namespace DocUtils {
found._backgroundColor = enumeration._backgroundColor || found._backgroundColor;
found._color = enumeration.color || found._color;
} else {
- Doc.AddDocToList(options, "data", Docs.Create.TextDocument(enumeration.title, enumeration));
+ Doc.AddDocToList(options, "data", Docs.Create.TextDocument(enumeration.title, { ...enumeration, system: true }));
}
});
return optionsCollection;
@@ -1211,5 +1213,4 @@ export namespace DocUtils {
}
Scripting.addGlobal("Docs", Docs);
-Scripting.addGlobal(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; });
-
+Scripting.addGlobal(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; }); \ No newline at end of file
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 7eed4d2b1..8dd7b033b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -23,6 +23,7 @@ import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
import { LabelBox } from "../views/nodes/LabelBox";
import { LinkManager } from "./LinkManager";
+import { Id } from "../../fields/FieldSymbols";
export class CurrentUserUtils {
private static curr_id: string;
@@ -45,7 +46,7 @@ export class CurrentUserUtils {
if (doc["template-button-query"] === undefined) {
const queryTemplate = Docs.Create.MulticolumnDocument(
[
- Docs.Create.SearchDocument({ _viewType: CollectionViewType.Schema, ignoreClick: true, forceActive: true, lockedPosition: true, title: "query", _height: 200, system: true }),
+ Docs.Create.SearchDocument({ _viewType: CollectionViewType.Schema, ignoreClick: true, forceActive: true, _chromeStatus: "disabled", lockedPosition: true, title: "query", _height: 200, system: true }),
Docs.Create.FreeformDocument([], { title: "data", _height: 100, system: true })
],
{ _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true, system: true }
@@ -95,6 +96,7 @@ export class CurrentUserUtils {
if (doc["template-button-description"] === undefined) {
const descriptionTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _height: 100, system: true }, "header")); // text needs to be a space to allow templateText to be created
+ descriptionTemplate.system = true;
descriptionTemplate[DataSym].layout =
"<div>" +
" <FormattedTextBox {...props} height='{this._headerHeight||75}px' background='{this._headerColor||`orange`}' fieldKey={'header'}/>" +
@@ -105,12 +107,13 @@ export class CurrentUserUtils {
doc["template-button-description"] = CurrentUserUtils.ficon({
onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
dragFactory: new PrefetchProxy(descriptionTemplate) as any as Doc,
- removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize"
+ removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize", system: true
});
}
if (doc["template-button-link"] === undefined) { // set _backgroundColor to transparent to prevent link dot from obscuring document it's attached to.
const linkTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _height: 100, system: true }, "header")); // text needs to be a space to allow templateText to be created
+ linkTemplate.system = true;
Doc.GetProto(linkTemplate).layout =
"<div>" +
" <FormattedTextBox {...props} height='{this._headerHeight||75}px' background='{this._headerColor||`lightGray`}' fieldKey={'header'}/>" +
@@ -151,7 +154,7 @@ export class CurrentUserUtils {
doc["template-button-link"] = CurrentUserUtils.ficon({
onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
dragFactory: new PrefetchProxy(linkTemplate) as any as Doc,
- removeDropProperties: new List<string>(["dropAction"]), title: "link view", icon: "window-maximize"
+ removeDropProperties: new List<string>(["dropAction"]), title: "link view", icon: "window-maximize", system: true
});
}
@@ -215,7 +218,7 @@ export class CurrentUserUtils {
const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 };
const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12pt" };
- const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title" };
+ const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title", system: true };
const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts });
descriptionWrapper._columnHeaders = new List<SchemaHeaderField>([
@@ -387,73 +390,78 @@ export class CurrentUserUtils {
static creatorBtnDescriptors(doc: Doc): {
title: string, toolTip: string, icon: string, drag?: string, ignoreClick?: boolean,
- click?: string, ischecked?: string, activeInkPen?: Doc, backgroundColor?: string, dragFactory?: Doc, noviceMode?: boolean
+ click?: string, ischecked?: string, activeInkPen?: Doc, backgroundColor?: string, dragFactory?: Doc, noviceMode?: boolean, clickFactory?: Doc
}[] {
if (doc.emptyPresentation === undefined) {
doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
- { title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias", _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0", system: true });
+ { title: "Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true });
}
if (doc.emptyCollection === undefined) {
doc.emptyCollection = Docs.Create.FreeformDocument([],
- { _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: "freeform", system: true });
+ { _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: "freeform", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ }
+ if (doc.emptyPane === undefined) {
+ doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, title: "Untitled Collection", system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.emptyComparison === undefined) {
- doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true });
+ 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 });
+ doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script", system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.emptyScreenshot === undefined) {
- doc.emptyScreenshot = Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot", system: true });
+ 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 });
+ doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, title: "ready to record audio", system: true, cloneFieldFilter: new List<string>(["system"]) });
}
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 });
+ doc.emptyButton = Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title: "Button", system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.emptyDocHolder === undefined) {
doc.emptyDocHolder = Docs.Create.DocumentDocument(
ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]") as any,
- { _width: 250, _height: 250, title: "container", system: true });
+ { _width: 250, _height: 250, title: "container", system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.emptyWebpage === undefined) {
- doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 400, UseCors: true, system: true });
+ doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 400, UseCors: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.activeMobileMenu === undefined) {
this.setupActiveMobileMenu(doc);
}
return [
- { toolTip: "Drag a collection", title: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc, noviceMode: true },
- { toolTip: "Drag a web page", title: "Web", icon: "globe-asia", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true },
- { toolTip: "Drag a cat image", title: "Image", icon: "cat", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyImage as Doc },
- { toolTip: "Drag 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: "Drag a screengrabber", 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(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 },
// { title: "Drag a webcam", title: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
- { toolTip: "Drag a 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: "Drag 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(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: "Drag a presentation view", title: "Present", icon: "tv", 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: "Drag a search box", title: "Query", icon: "search", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptySearch as Doc },
- { toolTip: "Drag 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 presentation in a new pane, drag for a presentation", title: "Present", icon: "tv", 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 },
// { 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: "Drag a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc },
+ { 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()' },
// { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this)', backgroundColor: "blue", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
// { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
// { 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: "Drag 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(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', 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)' },
];
}
+
+
// setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
static async setupCreatorButtons(doc: Doc) {
let alreadyCreatedButtons: string[] = [];
@@ -466,13 +474,13 @@ export class CurrentUserUtils {
}
}
const buttons = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title));
- const creatorBtns = buttons.map(({ title, toolTip, icon, ignoreClick, drag, click, ischecked, activeInkPen, backgroundColor, dragFactory, noviceMode }) => Docs.Create.FontIconDocument({
+ const creatorBtns = buttons.map(({ title, toolTip, icon, ignoreClick, drag, click, ischecked, activeInkPen, backgroundColor, dragFactory, noviceMode, clickFactory }) => Docs.Create.FontIconDocument({
_nativeWidth: 50, _nativeHeight: 50, _width: 50, _height: 50,
icon,
title,
toolTip,
ignoreClick,
- dropAction: "copy",
+ dropAction: "alias",
onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined,
onClick: click ? ScriptField.MakeScript(click) : undefined,
ischecked: ischecked ? ComputedField.MakeFunction(ischecked) : undefined,
@@ -480,6 +488,7 @@ export class CurrentUserUtils {
backgroundColor,
removeDropProperties: new List<string>(["dropAction"]),
dragFactory,
+ clickFactory,
userDoc: noviceMode ? undefined as any : doc,
hidden: noviceMode ? undefined as any : ComputedField.MakeFunction("self.userDoc.noviceMode"), system: true
}));
@@ -496,19 +505,20 @@ export class CurrentUserUtils {
return doc.myItemCreators as Doc;
}
- static menuBtnDescriptions(): {
- title: string, icon: string, click: string,
+ static menuBtnDescriptions(doc: 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: 'scriptContext.selectMenu(self, "Sharing")' },
- { title: "Workspace", icon: "desktop", click: 'scriptContext.selectMenu(self, "Workspace")' },
- { title: "Catalog", icon: "file", click: 'scriptContext.selectMenu(self, "Catalog")' },
- { title: "Archive", icon: "archive", click: 'scriptContext.selectMenu(self, "Archive")' },
- { title: "Import", icon: "upload", click: 'scriptContext.selectMenu(self, "Import")' },
- { title: "Tools", icon: "wrench", click: 'scriptContext.selectMenu(self, "Tools")' },
- { title: "Help", icon: "question-circle", click: 'scriptContext.selectMenu(self, "Help")' },
- { title: "Settings", icon: "cog", click: 'scriptContext.selectMenu(self, "Settings")' },
- { title: "User Doc", icon: "address-card", click: 'scriptContext.selectMenu(self, "UserDoc")' },
+ { title: "Sharing", target: Cast(doc["sidebar-sharing"], Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc },
+ { title: "Workspace", target: Cast(doc["sidebar-workspaces"], Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
+ { title: "Catalog", target: undefined as any, icon: "file", click: 'selectMainMenu(self)' },
+ { title: "Archive", target: Cast(doc["sidebar-recentlyClosed"], Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
+ { title: "Import", target: Cast(doc["sidebar-import"], Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
+ { title: "Tools", target: Cast(doc["sidebar-tools"], Doc, null), icon: "wrench", 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)' },
];
}
@@ -522,16 +532,19 @@ export class CurrentUserUtils {
}
static setupMenuPanel(doc: Doc) {
if (doc.menuStack === undefined) {
- const menuBtns = CurrentUserUtils.menuBtnDescriptions().map(({ title, icon, click }) =>
+ const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments }) =>
Docs.Create.FontIconDocument({
icon,
iconShape: "square",
title,
+ target,
_backgroundColor: "black",
- stayInCollection: true,
+ dropAction: "alias",
+ removeDropProperties: new List<string>(["dropAction"]),
childDropAction: "same",
_width: 60,
_height: 60,
+ watchedDocuments,
onClick: ScriptField.MakeScript(click, { scriptContext: "any" }), system: true
}));
const userDoc = menuBtns[menuBtns.length - 1];
@@ -629,7 +642,7 @@ export class CurrentUserUtils {
const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, clipboard?: Doc, activeInkPen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
{ title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
{ title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
- { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
+ { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }), backgroundColor: "orange", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
{ title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
{ title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
];
@@ -658,8 +671,29 @@ export class CurrentUserUtils {
return Cast(userDoc.thumbDoc, Doc);
}
- // 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 setupMobileInkingDoc(userDoc: Doc) {
+ return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", system: true });
+ }
+
+ static setupMobileUploadDoc(userDoc: Doc) {
+ // const addButton = Docs.Create.FontIconDocument({ onDragStart: ScriptField.MakeScript('addWebToMobileUpload()'), title: "Add Web Doc to Upload Collection", icon: "plus", backgroundColor: "black" })
+ const webDoc = Docs.Create.WebDocument("https://www.britannica.com/biography/Miles-Davis", {
+ title: "Upload Images From the Web", _chromeStatus: "enabled", lockedPosition: true, system: true
+ });
+ const uploadDoc = Docs.Create.StackingDocument([], {
+ title: "Mobile Upload Collection", backgroundColor: "white", lockedPosition: true, system: true
+ });
+ return Docs.Create.StackingDocument([webDoc, uploadDoc], {
+ _width: screen.width, lockedPosition: true, _chromeStatus: "disabled", title: "Upload", _autoHeight: true, _yMargin: 80, backgroundColor: "lightgray", system: true
+ });
+ }
+
+ static setupLibrary(userDoc: Doc) {
+ return CurrentUserUtils.setupWorkspaces(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)
static async setupToolsBtnPanel(doc: Doc) {
// setup a masonry view of all he creators
const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc);
@@ -690,9 +724,9 @@ export class CurrentUserUtils {
}
}
- static setupWorkspaces(doc: Doc) {
+ static async setupWorkspaces(doc: Doc) {
// setup workspaces library item
- doc.myWorkspaces === undefined;
+ 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
@@ -711,6 +745,7 @@ export class CurrentUserUtils {
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
})) as any as Doc;
}
+ return doc.myWorkspaces as any as Doc;
}
static setupCatalog(doc: Doc) {
@@ -718,7 +753,7 @@ export class CurrentUserUtils {
if (doc.myCatalog === undefined) {
doc.myCatalog = new PrefetchProxy(Docs.Create.SchemaDocument([], [], {
title: "CATALOG", _height: 1000, _fitWidth: true, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false,
- childDropAction: "alias", targetDropAction: "same", stayInCollection: true, treeViewOpen: true, system: true
+ childDropAction: "alias", targetDropAction: "same", _stayInCollection: true, treeViewOpen: true, system: true
}));
}
@@ -738,7 +773,7 @@ export class CurrentUserUtils {
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
+ title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true
}));
}
// this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
@@ -776,9 +811,8 @@ export class CurrentUserUtils {
if (doc.sidebar === undefined) {
const sidebarContainer = new Doc();
sidebarContainer._chromeStatus = "disabled";
- sidebarContainer.onClick = ScriptField.MakeScript("freezeSidebar()");
+ sidebarContainer.system = true;
doc.sidebar = new PrefetchProxy(sidebarContainer);
- doc.system = true;
}
return doc.sidebar as Doc;
}
@@ -829,18 +863,24 @@ export class CurrentUserUtils {
title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data", system: true
}));
}
- if (doc.activePresentation === undefined) {
- doc.activePresentation = Docs.Create.PresDocument(new List<Doc>(), {
- title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias",
- _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0", system: true
- });
- }
}
- // Right sidebar is where mobile uploads are contained
+ // Sharing sidebar is where shared documents are contained
static setupSharingSidebar(doc: Doc) {
if (doc["sidebar-sharing"] === undefined) {
- doc["sidebar-sharing"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Shared Documents", childDropAction: "alias", system: true }));
+ doc["sidebar-sharing"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Shared Documents", childDropAction: "alias", system: true, _yMargin: 30, _showTitle: "title", ignoreClick: true, lockedPosition: true }));
+ }
+ }
+
+ // Import sidebar is where shared documents are contained
+ static setupImportSidebar(doc: Doc) {
+ if (doc["sidebar-import-documents"] === undefined) {
+ doc["sidebar-import-documents"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Imported Documents", forceActive: true, _showTitle: "title", childDropAction: "alias", _autoHeight: true, _yMargin: 30, lockedPosition: true, _chromeStatus: "disabled", system: true }));
+ }
+ if (doc["sidebar-import"] === undefined) {
+ const uploads = Cast(doc["sidebar-import-documents"], Doc, null);
+ const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _backgroundColor: "black", title: "Import", icon: "upload", system: true });
+ doc["sidebar-import"] = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "Imported Documents", _yMargin: 20, ignoreClick: true, lockedPosition: true, system: true }));
}
}
@@ -894,6 +934,7 @@ export class CurrentUserUtils {
}
static async updateUserDocument(doc: Doc) {
+ doc.system = true;
doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
doc.title = Doc.CurrentUserEmail;
doc.activeInkPen = doc;
@@ -908,25 +949,28 @@ export class CurrentUserUtils {
doc.fontFamily = StrCast(doc.fontFamily, "Arial");
doc.fontColor = StrCast(doc.fontColor, "black");
doc.fontHighlight = StrCast(doc.fontHighlight, "");
- doc.defaultColor = StrCast(doc.defaultColor, "white");
+ doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, true);
+ doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white");
+ doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null);
doc.noviceMode = BoolCast(doc.noviceMode, true);
doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); //
doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); //
Utils.DRAG_THRESHOLD = NumCast(doc["constants-dragThreshold"]);
this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon
this.setupDocTemplates(doc); // sets up the template menu of templates
- this.setupSharingSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing
+ 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
- this.setupDefaultPresentation(doc); // presentation that's initially triggered
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();
+ setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered
+
// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index bd57e7f48..962294933 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -231,6 +231,7 @@ 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/DragManager.ts b/src/client/util/DragManager.ts
index 4b1860b5c..0cca61841 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -210,7 +210,7 @@ export namespace DragManager {
docDragData.droppedDocuments =
dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
docDragData.dropAction === "alias" ? Doc.MakeAlias(d) :
- docDragData.dropAction === "copy" ? Doc.MakeDelegate(d) : d);
+ docDragData.dropAction === "copy" ? Doc.MakeClone(d) : d);
docDragData.dropAction !== "same" && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []);
const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index d0acf14c3..f1848f7e5 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -58,7 +58,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
let dbox = doc;
// bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant
if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes("FontIconBox")) {
- dbox = Doc.MakeAlias(doc);
+ //dbox = Doc.MakeAlias(doc); // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon
} else if (!doc.onDragStart && !doc.isButtonBar) {
const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
if (layoutDoc.type !== DocumentType.FONTICON) {
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 9ede94e4b..04e937878 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -34,7 +34,7 @@ export namespace Hypothesis {
const results: Doc[] = [];
await SearchUtil.Search("web", true).then(action(async (res: SearchUtil.DocSearchResult) => {
- const docs = await Promise.all(res.docs.map(async doc => (await Cast(doc.extendsDoc, Doc)) || doc));
+ const docs = res.docs;
const filteredDocs = docs.filter(doc =>
doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data
);
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index f905ce5a7..bb58423c8 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -140,7 +140,7 @@ export namespace InteractionUtils {
export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number,
color: string, width: number, strokeWidth: number, bezier: string, fill: string, arrowStart: string, arrowEnd: string,
- dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) {
+ dash: string | undefined, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) {
let pts: { X: number; Y: number; }[] = [];
if (shape) { //if any of the shape are true
pts = makePolygon(shape, points);
@@ -182,7 +182,7 @@ export namespace InteractionUtils {
const strpts = pts.reduce((acc: string, pt: { X: number, Y: number }) => acc +
`${(pt.X - left - width / 2) * scalex + width / 2},
${(pt.Y - top - width / 2) * scaley + width / 2} `, "");
- const dashArray = String(Number(width) * Number(dash));
+ const dashArray = dash && Number(dash) ? String(Number(width) * Number(dash)) : undefined;
const defGuid = Utils.GenerateGuid();
const arrowDim = Math.max(0.5, 8 / Math.log(Math.max(2, strokeWidth)));
diff --git a/src/client/util/KeyCodes.ts b/src/client/util/KeyCodes.ts
index cacb72a57..de2457a5a 100644
--- a/src/client/util/KeyCodes.ts
+++ b/src/client/util/KeyCodes.ts
@@ -131,6 +131,6 @@ export class KeyCodes {
public static NUM_7: number = 55;
public static NUM_8: number = 56;
public static NUM_9: number = 57;
- public static SUBSTRACT: number = 189;
+ public static SUBTRACT: number = 189;
public static ADD: number = 187;
} \ No newline at end of file
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 7b2c601fe..afa8ff575 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -28,7 +28,9 @@ export namespace SearchUtil {
start?: number;
rows?: number;
fq?: string;
+ sort?: string;
allowAliases?: boolean;
+ onlyAliases?: boolean;
"facet"?: string;
"facet.field"?: string;
}
@@ -37,7 +39,11 @@ export namespace SearchUtil {
export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) {
query = query || "*"; //If we just have a filter query, search for * as the query
const rpquery = Utils.prepend("/dashsearch");
- const gotten = await rp.get(rpquery, { qs: { ...options, q: query } });
+ let replacedQuery = query.replace(/type_t:([^ )])/g, (substring, arg) => `{!join from=id to=proto_i}type_t:${arg}`);
+ if (options.onlyAliases) {
+ replacedQuery = `{!join from=id to=proto_i}DEFAULT:${replacedQuery}`;
+ }
+ const gotten = await rp.get(rpquery, { qs: { ...options, q: replacedQuery } });
const result: IdSearchResult = gotten.startsWith("<") ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten);
if (!returnDocs) {
return result;
@@ -79,10 +85,12 @@ 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--;
}
}
- return { docs: theDocs, numFound: theDocs.length, highlighting, lines: theLines };
+ return { docs: theDocs, numFound: result.numFound, highlighting, lines: theLines };
}
export async function GetAliasesOfDocument(doc: Doc): Promise<Doc[]>;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 05ba00331..35b82cc30 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,10 +1,9 @@
import { observable, action, runInAction, ObservableMap } from "mobx";
-import { Doc } from "../../fields/Doc";
+import { Doc, Opt } from "../../fields/Doc";
import { DocumentView } from "../views/nodes/DocumentView";
import { computedFn } from "mobx-utils";
import { List } from "../../fields/List";
-import { Scripting } from "./Scripting";
-import { DocumentManager } from "./DocumentManager";
+import { CollectionSchemaView } from "../views/collections/CollectionSchemaView";
export namespace SelectionManager {
@@ -12,8 +11,15 @@ export namespace SelectionManager {
@observable IsDragging: boolean = false;
SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
+ @observable SelectedSchemaDocument: Doc | undefined;
+ @observable SelectedSchemaCollection: CollectionSchemaView | undefined;
@action
+ SelectSchemaDoc(collectionView: Opt<CollectionSchemaView>, doc: Opt<Doc>) {
+ manager.SelectedSchemaDocument = doc;
+ manager.SelectedSchemaCollection = collectionView;
+ }
+ @action
SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
// if doc is not in SelectedDocuments, add it
@@ -26,6 +32,8 @@ export namespace SelectionManager {
docView.props.whenActiveChanged(true);
} else if (!ctrlPressed && Array.from(manager.SelectedDocuments.entries()).length > 1) {
Array.from(manager.SelectedDocuments.keys()).map(dv => dv !== docView && dv.props.whenActiveChanged(false));
+ manager.SelectedSchemaDocument = undefined;
+ manager.SelectedSchemaCollection = undefined;
manager.SelectedDocuments.clear();
manager.SelectedDocuments.set(docView, true);
}
@@ -42,7 +50,8 @@ export namespace SelectionManager {
}
@action
DeselectAll(): void {
-
+ manager.SelectedSchemaCollection = undefined;
+ manager.SelectedSchemaDocument = undefined;
Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false));
manager.SelectedDocuments.clear();
Doc.UserDoc().activeSelection = new List<Doc>([]);
@@ -57,6 +66,9 @@ export namespace SelectionManager {
export function SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
manager.SelectDoc(docView, ctrlPressed);
}
+ export function SelectSchemaDoc(colSchema: Opt<CollectionSchemaView>, document: Opt<Doc>): void {
+ manager.SelectSchemaDoc(colSchema, document);
+ }
// computed functions, such as used in IsSelected generate errors if they're called outside of a
// reaction context. Specifying the context with 'outsideReaction' allows an efficiency feature
@@ -84,11 +96,10 @@ export namespace SelectionManager {
export function SelectedDocuments(): Array<DocumentView> {
return Array.from(manager.SelectedDocuments.keys());
}
-}
-
-
-Scripting.addGlobal(function selectDoc(doc: any) {
- const view = DocumentManager.Instance.getDocumentView(doc);
- view && SelectionManager.SelectDoc(view, false);
- //Doc.UserDoc().activeSelection = new List([doc]);
-}); \ No newline at end of file
+ export function SelectedSchemaDoc(): Doc | undefined {
+ return manager.SelectedSchemaDocument;
+ }
+ export function SelectedSchemaCollection(): CollectionSchemaView | undefined {
+ return manager.SelectedSchemaCollection;
+ }
+} \ No newline at end of file
diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss
index 01dda0aca..ec513e5d5 100644
--- a/src/client/util/SettingsManager.scss
+++ b/src/client/util/SettingsManager.scss
@@ -33,10 +33,10 @@
font-size: 12px;
padding-right: 15px;
color: black;
- margin-top: 4px;
+ margin-top: 10px;
/* right: 135; */
position: absolute;
- left: 235;
+ left: 243;
}
.settings-section {
@@ -97,6 +97,8 @@
.modes-content {
display: flex;
+ margin-left: 10px;
+ font-size: 12;
.modes-select {
// width: 170px;
@@ -112,6 +114,8 @@
.modes-playground,
.default-acl {
display: flex;
+ margin-left: 10px;
+ font-size: 12;
.playground-check,
.acl-check {
@@ -125,10 +129,12 @@
.playground-text {
color: black;
margin-right: 10px;
+ margin-top: 2;
}
.acl-text {
color: black;
+ margin-top: 2;
}
}
@@ -147,6 +153,7 @@
height: 20px;
border: 0.5px solid black;
border-radius: 5px;
+ padding-top: 3px;
}
}
@@ -229,7 +236,7 @@
}
.logout-button {
- right: 35;
+ right: 355;
position: absolute;
}
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index e3b91925a..5642c5a42 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -32,7 +32,7 @@ export default class SettingsManager extends React.Component<{}> {
@observable private new_password = "";
@observable private new_confirm = "";
- @computed get backgroundColor() { return Doc.UserDoc().defaultColor; }
+ @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
constructor(props: {}) {
super(props);
@@ -56,7 +56,7 @@ export default class SettingsManager extends React.Component<{}> {
@undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.UserDoc().noviceMode = (e.currentTarget as any)?.value === "Novice");
@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().defaultColor = String(color.hex));
+ @undoBatch switchColor = action((color: ColorState) => Doc.UserDoc().activeCollectionBackground = String(color.hex));
@undoBatch
playgroundModeToggle = action(() => {
this.playgroundMode = !this.playgroundMode;
@@ -91,10 +91,10 @@ export default class SettingsManager extends React.Component<{}> {
</div>
<div className="preferences-font">
<div className="preferences-font-text">Default Font</div>
- <select className="font-select" onChange={this.changeFontFamily}>
+ <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}>
+ <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>
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 7912db74d..42c300712 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -62,6 +62,7 @@
input {
height: 10px;
+ cursor: pointer;
}
label {
@@ -69,11 +70,29 @@
font-style: italic;
}
}
+
+ .layoutDoc-acls {
+ display: flex;
+ flex-direction: column;
+ float: right;
+ margin-right: 12;
+ margin-top: -15;
+ align-items: center;
+
+ label {
+ font-weight: normal;
+ font-style: italic;
+ }
+
+ input {
+ cursor: pointer;
+ }
+ }
}
.main-container {
display: flex;
- margin-top: -10px;
+ margin-top: -25px;
.individual-container,
.group-container {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index d50a132f8..b9918e900 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,7 +1,7 @@
import { observable, runInAction, action } from "mobx";
import * as React from "react";
import MainViewModal from "../views/MainViewModal";
-import { Doc, Opt, AclAdmin, AclPrivate, DocListCast } from "../../fields/Doc";
+import { Doc, Opt, AclAdmin, AclPrivate, DocListCast, DataSym } from "../../fields/Doc";
import { DocServer } from "../DocServer";
import { Cast, StrCast } from "../../fields/Types";
import * as RequestPromise from "request-promise";
@@ -25,7 +25,6 @@ import { library } from "@fortawesome/fontawesome-svg-core";
library.add(fa.faInfoCircle, fa.faCaretUp, fa.faCaretRight, fa.faCaretDown);
-
export interface User {
email: string;
userDocumentId: string;
@@ -47,6 +46,8 @@ interface GroupedOptions {
const indType = "!indType/";
const groupType = "!groupType/";
+const storage = "data";
+
/**
* A user who also has a notificationDoc.
*/
@@ -55,7 +56,6 @@ interface ValidatedUser {
notificationDoc: Doc;
}
-const storage = "data";
@observer
export default class SharingManager extends React.Component<{}> {
@@ -75,21 +75,22 @@ export default class SharingManager extends React.Component<{}> {
// if both showUserOptions and showGroupOptions are false then both are displayed
@observable private showUserOptions: boolean = false; // whether to show individuals as options when sharing (in the react-select component)
@observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
- private populating: boolean = false;
+ private populating: boolean = false; // whether the list of users is populating or not
+ @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
// private get linkVisible() {
// return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false;
// }
- public open = (target: DocumentView) => {
+ public open = (target?: DocumentView, target_doc?: Doc) => {
runInAction(() => this.users = []);
// SelectionManager.DeselectAll();
this.populateUsers();
runInAction(() => {
this.targetDocView = target;
- this.targetDoc = target.props.Document;
+ this.targetDoc = target_doc || target?.props.Document;
DictationOverlay.Instance.hasActiveModal = true;
- this.isOpen = true;
+ this.isOpen = this.targetDoc !== undefined;
this.permissions = SharingPermissions.Edit;
});
this.targetDoc!.author === Doc.CurrentUserEmail && !this.targetDoc![`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, this.targetDoc!);
@@ -155,16 +156,17 @@ export default class SharingManager extends React.Component<{}> {
const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
const target = targetDoc || this.targetDoc!;
- const ACL = `ACL-${StrCast(group.groupName)}`;
+ const key = StrCast(group.groupName).replace(".", "_");
+ const ACL = `ACL-${key}`;
- target.author === Doc.CurrentUserEmail && distributeAcls(ACL, permission as SharingPermissions, target);
+ GetEffectiveAcl(target) === AclAdmin && distributeAcls(ACL, permission as SharingPermissions, target);
// if documents have been shared, add the target to that list if it doesn't already exist, otherwise create a new list with the target
group.docsShared ? Doc.IndexOf(target, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(target) : group.docsShared = new List<Doc>([target]);
- users.forEach(({ notificationDoc }) => {
+ users.forEach(({ user, notificationDoc }) => {
if (permission !== SharingPermissions.None) Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target); // add the target to the notificationDoc if it hasn't already been added
- else Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); // remove the target from the list if it already exists
+ else GetEffectiveAcl(target, undefined, user.email) === AclPrivate && Doc.IndexOf((target.aliasOf as Doc || target), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (target.aliasOf as Doc || target)); // remove the target from the list if it already exists
});
}
@@ -175,14 +177,19 @@ export default class SharingManager extends React.Component<{}> {
*/
shareWithAddedMember = (group: Doc, emailId: string) => {
const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!;
-
if (group.docsShared) DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc));
}
+ /**
+ * Called from the properties sidebar to change permissions of a user.
+ */
shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, target: Doc) => {
- const user = this.users.find(({ user: { email } }) => email === (shareWith === "Me" ? Doc.CurrentUserEmail : shareWith));
- if (user) this.setInternalSharing(user, permission, target);
- else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, target);
+ if (shareWith !== "Public") {
+ const user = this.users.find(({ user: { email } }) => email === (shareWith === "Me" ? Doc.CurrentUserEmail : shareWith));
+ if (user) this.setInternalSharing(user, permission, target);
+ else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, target);
+ }
+ else if (GetEffectiveAcl(target) === AclAdmin) distributeAcls("ACL-Public", permission, target);
}
/**
@@ -228,15 +235,11 @@ export default class SharingManager extends React.Component<{}> {
const key = user.email.replace('.', '_');
const ACL = `ACL-${key}`;
+ GetEffectiveAcl(target) === AclAdmin && distributeAcls(ACL, permission as SharingPermissions, target);
- target.author === Doc.CurrentUserEmail && distributeAcls(ACL, permission as SharingPermissions, target);
+ if (permission !== SharingPermissions.None) Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target);
+ else GetEffectiveAcl(target, undefined, user.email) === AclPrivate && Doc.IndexOf((target.aliasOf as Doc || target), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (target.aliasOf as Doc || target));
- if (permission !== SharingPermissions.None) {
- Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target);
- }
- else {
- Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target);
- }
}
@@ -370,8 +373,8 @@ export default class SharingManager extends React.Component<{}> {
* @returns the main interface of the SharingManager.
*/
private get sharingInterface() {
- const groupList = GroupManager.Instance?.getAllGroups() || [];
+ const groupList = GroupManager.Instance?.getAllGroups() || [];
const sortedUsers = this.users.slice().sort(this.sortUsers)
.map(({ user: { email } }) => ({ label: email, value: indType + email }));
const sortedGroups = groupList.slice().sort(this.sortGroups)
@@ -404,17 +407,19 @@ export default class SharingManager extends React.Component<{}> {
}
}
- const users = this.individualSort === "ascending" ? this.users.sort(this.sortUsers) : this.individualSort === "descending" ? this.users.sort(this.sortUsers).reverse() : this.users;
- const groups = this.groupSort === "ascending" ? groupList.sort(this.sortGroups) : this.groupSort === "descending" ? groupList.sort(this.sortGroups).reverse() : groupList;
+ const users = this.individualSort === "ascending" ? this.users.slice().sort(this.sortUsers) : this.individualSort === "descending" ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
+ const groups = this.groupSort === "ascending" ? groupList.slice().sort(this.sortGroups) : this.groupSort === "descending" ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
+
+ const targetDoc = this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym];
- const effectiveAcl = this.targetDoc ? GetEffectiveAcl(this.targetDoc) : AclPrivate;
+ const effectiveAcl = targetDoc ? GetEffectiveAcl(targetDoc) : AclPrivate;
// the list of users shared with
const userListContents: (JSX.Element | null)[] = users.map(({ user, notificationDoc }) => {
const userKey = user.email.replace('.', '_');
- const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`]);
+ const permissions = StrCast(targetDoc?.[`ACL-${userKey}`]);
- return !permissions || user.email === this.targetDoc?.author ? null : (
+ return !permissions || user.email === targetDoc?.author ? (null) : (
<div
key={userKey}
className={"container"}
@@ -446,7 +451,7 @@ export default class SharingManager extends React.Component<{}> {
key={"owner"}
className={"container"}
>
- <span className={"padding"}>{this.targetDoc?.author === Doc.CurrentUserEmail ? "Me" : this.targetDoc?.author}</span>
+ <span className={"padding"}>{targetDoc?.author === Doc.CurrentUserEmail ? "Me" : targetDoc?.author}</span>
<div className="edit-actions">
<div className={"permissions-dropdown"}>
Owner
@@ -454,7 +459,7 @@ export default class SharingManager extends React.Component<{}> {
</div>
</div>
),
- this.targetDoc?.author !== Doc.CurrentUserEmail ?
+ targetDoc?.author !== Doc.CurrentUserEmail ?
(
<div
key={"me"}
@@ -463,7 +468,7 @@ export default class SharingManager extends React.Component<{}> {
<span className={"padding"}>Me</span>
<div className="edit-actions">
<div className={"permissions-dropdown"}>
- {this.targetDoc?.[`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]}
+ {targetDoc?.[`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]}
</div>
</div>
</div>
@@ -472,7 +477,7 @@ export default class SharingManager extends React.Component<{}> {
// the list of groups shared with
const groupListContents = groups.map(group => {
- const permissions = StrCast(this.targetDoc?.[`ACL-${StrCast(group.groupName)}`]);
+ const permissions = StrCast(targetDoc?.[`ACL-${StrCast(group.groupName)}`]);
return !permissions ? null : (
<div
@@ -538,7 +543,7 @@ export default class SharingManager extends React.Component<{}> {
</div>
<div className={"hr-substitute"} /> */}
<div className="sharing-contents">
- <p className={"share-title"}><b>Share </b>{this.focusOn(StrCast(this.targetDoc?.title, "this document"))}</p>
+ <p className={"share-title"}><b>Share </b>{this.focusOn(StrCast(targetDoc?.title, "this document"))}</p>
<div className={"close-button"} onClick={this.close}>
<FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
</div>
@@ -569,6 +574,9 @@ export default class SharingManager extends React.Component<{}> {
<input type="checkbox" onChange={action(() => this.showUserOptions = !this.showUserOptions)} /> <label style={{ marginRight: 10 }}>Individuals</label>
<input type="checkbox" onChange={action(() => this.showGroupOptions = !this.showGroupOptions)} /> <label>Groups</label>
</div>
+ <div className="layoutDoc-acls">
+ <input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label>
+ </div>
</div>
}
<div className="main-container">
diff --git a/src/client/views/.DS_Store b/src/client/views/.DS_Store
index c379549d0..c6f3afa14 100644
--- a/src/client/views/.DS_Store
+++ b/src/client/views/.DS_Store
Binary files differ
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index 68ccefcb5..ccc54058d 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -1,12 +1,14 @@
import React = require("react");
import { observable, action } from "mobx";
import "./AntimodeMenu.scss";
+export interface AntimodeMenuProps {
+}
/**
* This is an abstract class that serves as the base for a PDF-style or Marquee-style
* menu. To use this class, look at PDFMenu.tsx or MarqueeOptionsMenu.tsx for an example.
*/
-export default abstract class AntimodeMenu extends React.Component {
+export default abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Component<T, {}> {
protected _offsetY: number = 0;
protected _offsetX: number = 0;
protected _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
@@ -18,7 +20,7 @@ export default abstract class AntimodeMenu extends React.Component {
@observable protected _transitionProperty: string = "opacity";
@observable protected _transitionDuration: string = "0.5s";
@observable protected _transitionDelay: string = "";
- @observable protected _canFade: boolean = true;
+ @observable protected _canFade: boolean = false;
@observable public Pinned: boolean = false;
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index eea133ed9..23ce71c4f 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -122,15 +122,22 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
@action.bound
removeDocument(doc: Doc | Doc[]): boolean {
- const docs = doc instanceof Doc ? [doc] : doc;
- docs.map(doc => doc.isPushpin = doc.annotationOn = undefined);
- const targetDataDoc = this.dataDoc;
- const value = DocListCast(targetDataDoc[this.annotationKey]);
- const toRemove = value.filter(v => docs.includes(v));
- // can't assign new List<Doc>(result) to this because you can't assign new values in addonly
- if (toRemove.length !== 0) {
- toRemove.forEach(doc => Doc.RemoveDocFromList(targetDataDoc, this.annotationKey, doc));
- return true;
+ const effectiveAcl = GetEffectiveAcl(this.dataDoc);
+ if (effectiveAcl === AclAdmin || effectiveAcl === AclEdit) {
+ const docs = doc instanceof Doc ? [doc] : doc;
+ docs.map(doc => doc.isPushpin = doc.annotationOn = undefined);
+ const targetDataDoc = this.dataDoc;
+ const value = DocListCast(targetDataDoc[this.annotationKey]);
+ const toRemove = value.filter(v => docs.includes(v));
+
+ if (toRemove.length !== 0) {
+ const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
+ toRemove.forEach(doc => {
+ Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey + "-annotations", doc);
+ recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
+ });
+ return true;
+ }
}
return false;
@@ -171,6 +178,8 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
added.map(doc => doc.context = this.props.Document);
(targetDataDoc[this.annotationKey] as List<Doc>).push(...added);
targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now()));
+ const lastModified = "lastModified";
+ targetDataDoc[lastModified] = new DateField(new Date(Date.now()));
}
}
}
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 8748b1880..7effc4aa0 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -199,7 +199,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div></>}>
<div className="documentButtonBar-linker"
style={{ backgroundColor: isPinned ? "white" : "", color: isPinned ? "black" : "white", border: isPinned ? "black 1px solid " : "" }}
- onClick={e => DockedFrameRenderer.PinDoc(targetDoc, isPinned)}>
+ onClick={e => this.props.views().map(view => view && DockedFrameRenderer.PinDoc(view.props.Document, isPinned))}>
<FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin"
/>
</div></Tooltip>;
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 5401623e8..1e8cfdff4 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -21,14 +21,6 @@ $linkGap : 3px;
background: none;
}
-
- .documentDecorations-rotation {
- pointer-events: auto;
- cursor: alias;
- width: 10px;
- height: 10px;
- }
-
.documentDecorations-resizer {
pointer-events: auto;
background: $alt-accent;
@@ -76,11 +68,17 @@ $linkGap : 3px;
grid-column-end: 7;
}
+ #documentDecorations-rotation,
#documentDecorations-borderRadius {
- grid-column-start: 5;
- grid-column-end: 7;
+ grid-column: 5;
+ grid-row: 4;
border-radius: 100%;
background: dimgray;
+ height: 8;
+ right: -12;
+ top: 12;
+ position: relative;
+ pointer-events: all;
.borderRadiusTooltip {
width: 10px;
@@ -88,6 +86,11 @@ $linkGap : 3px;
position: absolute;
}
}
+ #documentDecorations-rotation {
+ background: transparent;
+ right: -15;
+ }
+
#documentDecorations-topLeftResizer,
#documentDecorations-bottomRightResizer {
@@ -193,11 +196,11 @@ $linkGap : 3px;
.documentDecorations-iconifyButton {
opacity: 1;
grid-column-start: 4;
- grid-column-end: 5;
+ grid-column-end: 4;
pointer-events: all;
text-align: center;
- left: -25px;
- top: -2px;
+ right: 0;
+ top: 0;
cursor: pointer;
position: absolute;
background: transparent;
@@ -206,14 +209,11 @@ $linkGap : 3px;
.documentDecorations-openInTab {
opacity: 1;
- grid-column-start: 4;
+ grid-column-start: 5;
grid-column-end: 5;
pointer-events: all;
text-align: center;
cursor: pointer;
- width: 15px;
- margin-left: -8px;
- margin-top: auto;
}
.documentDecorations-closeButton {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 552d504c0..0cc492ee9 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,34 +1,30 @@
import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes, faAngleLeft, faAngleRight, faAngleDoubleLeft, faAngleDoubleRight, faPause, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
+import { faAngleDoubleLeft, faAngleDoubleRight, faAngleLeft, faAngleRight, faArrowAltCircleDown, faArrowAltCircleUp, faCaretUp, faCheckCircle, faCloudUploadAlt, faExternalLinkAlt, faFilePdf, faFilm, faImage, faLink, faObjectGroup, faPause, faShare, faStickyNote, faStopCircle, faSyncAlt, faTag, faTextHeight, faTimes } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, reaction, runInAction, get } from "mobx";
+import { Tooltip } from '@material-ui/core';
+import { action, computed, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DataSym, Field, WidthSym, HeightSym, AclEdit, AclAdmin } from "../../fields/Doc";
+import { AclAdmin, AclEdit, DataSym, Doc, Field } from "../../fields/Doc";
import { Document } from '../../fields/documentSchemas';
+import { HtmlField } from '../../fields/HtmlField';
+import { InkField } from "../../fields/InkField";
import { ScriptField } from '../../fields/ScriptField';
-import { Cast, StrCast, NumCast } from "../../fields/Types";
-import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils";
+import { Cast, NumCast } from "../../fields/Types";
+import { GetEffectiveAcl } from '../../fields/util';
+import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from "../../Utils";
import { DocUtils } from "../documents/Documents";
import { DocumentType } from '../documents/DocumentTypes';
import { DragManager } from "../util/DragManager";
import { SelectionManager } from "../util/SelectionManager";
+import { SnappingManager } from '../util/SnappingManager';
import { undoBatch, UndoManager } from "../util/UndoManager";
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import FormatShapePane from './collections/collectionFreeForm/FormatShapePane';
import { DocumentButtonBar } from './DocumentButtonBar';
import './DocumentDecorations.scss';
import { DocumentView } from "./nodes/DocumentView";
import React = require("react");
import e = require('express');
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { SnappingManager } from '../util/SnappingManager';
-import { HtmlField } from '../../fields/HtmlField';
-import { InkField } from "../../fields/InkField";
-import { Tooltip } from '@material-ui/core';
-import { GetEffectiveAcl } from '../../fields/util';
-import { DocumentIcon } from './nodes/DocumentIcon';
-import { render } from 'react-dom';
-import { createLessThan } from 'typescript';
-import FormatShapePane from './collections/collectionFreeForm/FormatShapePane';
-import { PropertiesView } from './collections/collectionFreeForm/PropertiesView';
library.add(faCaretUp);
library.add(faObjectGroup);
@@ -197,17 +193,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@action
onCloseClick = async (e: React.MouseEvent | undefined) => {
if (!e?.button) {
- const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
const selected = SelectionManager.SelectedDocuments().slice();
SelectionManager.DeselectAll();
-
- selected.map(dv => {
- const effectiveAcl = GetEffectiveAcl(dv.props.Document);
- if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { // deletes whatever you have the right to delete
- recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
- dv.props.removeDocument?.(dv.props.Document);
- }
- });
+ selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
}
}
@action
@@ -372,7 +360,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;
this.Interacting = true;
this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
- SelectionManager.SelectedDocuments()[0].props.setupDragLines?.();
+ SelectionManager.SelectedDocuments()[0].props.setupDragLines?.(e.ctrlKey || e.shiftKey);
}
this._snapX = e.pageX;
this._snapY = e.pageY;
@@ -611,13 +599,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
const darkScheme = Cast(Doc.UserDoc().activeWorkspace, 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 < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
+ 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)) {
return (null);
}
const canDelete = SelectionManager.SelectedDocuments().some(docView => {
- const docAcl = GetEffectiveAcl(docView.props.Document);
- const collectionAcl = GetEffectiveAcl(docView.props.ContainingCollectionDoc);
- return [docAcl, collectionAcl].some(acl => [AclAdmin, AclEdit].includes(acl));
+ const collectionAcl = GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]);
+ return collectionAcl === AclAdmin || collectionAcl === AclEdit;
});
const minimal = bounds.r - bounds.x < 100 ? true : false;
const maximizeIcon = minimal ? (
@@ -625,7 +612,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<div className="documentDecorations-contextMenu" onPointerDown={this.onSettingsDown}>
<FontAwesomeIcon size="lg" icon="cog" />
</div></Tooltip>) : canDelete ? (
- <Tooltip title={<><div className="dash-tooltip">Delete</div></>} placement="top">
+ <Tooltip title={<><div className="dash-tooltip">Close</div></>} placement="top">
<div className="documentDecorations-closeButton" onClick={this.onCloseClick}>
{/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/}
<FontAwesomeIcon className="documentdecorations-times" icon={faTimes} size="lg" />
@@ -668,14 +655,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
if (bounds.y > bounds.b) {
bounds.y = bounds.b - (this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight);
}
- var offset = 0;
- var rotButton = <></>;
- //make offset larger for ink to edit points
- if (seldoc.rootDoc.type === DocumentType.INK) {
- offset = 20;
- rotButton = <div id="documentDecorations-rotation" title="rotate" className="documentDecorations-rotation"
- onPointerDown={this.onRotateDown}> ⟲ </div>;
- }
+ const useRotation = seldoc.rootDoc.type === DocumentType.INK;
return (<div className="documentDecorations" style={{ background: darkScheme }} >
<div className="documentDecorations-background" style={{
@@ -689,22 +669,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
</div>
{bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
<div className="documentDecorations-container" key="container" ref={this.setTextBar} style={{
- width: (bounds.r - bounds.x + this._resizeBorderWidth + offset) + "px",
- height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + offset) + "px",
- left: bounds.x - this._resizeBorderWidth / 2 - offset / 2,
- top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight - offset / 2,
+ width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+ height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
+ left: bounds.x - this._resizeBorderWidth / 2,
+ top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
}}>
{maximizeIcon}
{titleArea}
{SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
<Tooltip title={<><div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div></>} placement="top">
<div className="documentDecorations-iconifyButton" onPointerDown={this.onIconifyDown}>
- {"_"}
+ <FontAwesomeIcon icon={seldoc.finalLayoutKey.includes("icon") ? "window-restore" : "window-minimize"} className="documentView-minimizedIcon" />
</div></Tooltip>}
- <Tooltip title={<><div className="dash-tooltip">Open Document In Tab</div></>} placement="top"><div className="documentDecorations-openInTab" onPointerDown={this.onMaximizeDown}>
+ <Tooltip title={<><div className="dash-tooltip">Open In a New Pane</div></>} placement="top"><div className="documentDecorations-openInTab" onPointerDown={this.onMaximizeDown}>
{SelectionManager.SelectedDocuments().length === 1 ? <FontAwesomeIcon icon="external-link-alt" className="documentView-minimizedIcon" /> : "..."}
</div></Tooltip>
- {rotButton}
<div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer"
onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-topResizer" className="documentDecorations-resizer"
@@ -728,8 +707,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
<FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" />
</div></Tooltip>}
- <div id="documentDecorations-borderRadius" className="documentDecorations-radius"
- onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
+ onPointerDown={useRotation ? this.onRotateDown : this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}>{useRotation && "⟲"}</div>
</div >
<div className="link-button-container" key="links" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}>
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 7e26f8ec4..2d41343b3 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -613,10 +613,10 @@ export default class GestureOverlay extends Touchable {
}
}
// if we're not drawing in a toolglass try to recognize as gesture
- else {
+ else { // need to decide when to turn gestures back on
const result = points.length > 2 && GestureUtils.GestureRecognizer.Recognize(new Array(points));
let actionPerformed = false;
- if (result && result.Score > 0.7) {
+ if (Doc.UserDoc().recognizeGestures && result && result.Score > 0.7) {
switch (result.Name) {
case GestureUtils.Gestures.Box: actionPerformed = this.dispatchGesture(GestureUtils.Gestures.Box); break;
case GestureUtils.Gestures.StartBracket: actionPerformed = this.dispatchGesture(GestureUtils.Gestures.StartBracket); break;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 182f6397c..be6aa6be2 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -1,6 +1,6 @@
import { action } from "mobx";
import { DateField } from "../../fields/DateField";
-import { Doc, DocListCast, AclEdit, AclAdmin } from "../../fields/Doc";
+import { Doc, DocListCast } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
import { InkTool } from "../../fields/InkField";
import { List } from "../../fields/List";
@@ -23,7 +23,6 @@ import PDFMenu from "./pdf/PDFMenu";
import { ContextMenu } from "./ContextMenu";
import GroupManager from "../util/GroupManager";
import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
-import { GetEffectiveAcl } from "../../fields/util";
const modifiers = ["control", "meta", "shift", "alt"];
type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -120,16 +119,9 @@ export default class KeyManager {
}
}
- const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
const selected = SelectionManager.SelectedDocuments().slice();
UndoManager.RunInBatch(() => {
- selected.map(dv => {
- const effectiveAcl = GetEffectiveAcl(dv.props.Document);
- if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { // deletes whatever you have the right to delete
- recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
- dv.props.removeDocument?.(dv.props.Document);
- }
- });
+ selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
}, "delete");
SelectionManager.DeselectAll();
break;
@@ -331,6 +323,8 @@ export default class KeyManager {
undoBatch(() => {
targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]);
targetDataDoc[fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+ const lastModified = "lastModified";
+ targetDataDoc[lastModified] = new DateField(new Date(Date.now()));
})();
}
}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index e3390426b..3376fcd97 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -43,7 +43,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
this.props.Document._backgroundColor = "rgba(0,0,0,0.7)";
this.props.Document.mixBlendMode = "hard-light";
this.props.Document.color = "#9b9b9bff";
- this.props.Document.stayInCollection = true;
+ this.props.Document._stayInCollection = true;
this.props.Document.isInkMask = true;
}
@@ -103,11 +103,15 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
const strokeWidth = Number(this.layoutDoc.strokeWidth);
const xs = data.map(p => p.X);
const ys = data.map(p => p.Y);
- const left = Math.min(...xs) - strokeWidth / 2;
- const top = Math.min(...ys) - strokeWidth / 2;
- const right = Math.max(...xs) + strokeWidth / 2;
- const bottom = Math.max(...ys) + strokeWidth / 2;
- const width = Math.max(right - left);
+ const lineTop = Math.min(...ys);
+ const lineBot = Math.max(...ys);
+ const lineLft = Math.min(...xs);
+ const lineRgt = Math.max(...xs);
+ const left = lineLft - strokeWidth / 2;
+ const top = lineTop - strokeWidth / 2;
+ const right = lineRgt + strokeWidth / 2;
+ const bottom = lineBot + strokeWidth / 2;
+ const width = Math.max(1, right - left);
const height = Math.max(1, bottom - top);
const scaleX = width === strokeWidth ? 1 : (this.props.PanelWidth() - strokeWidth) / (width - strokeWidth);
const scaleY = height === strokeWidth ? 1 : (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth);
@@ -116,12 +120,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth,
StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker),
- StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
+ StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBot - lineTop > 1 && lineRgt - lineLft > 1, false);
const hpoints = InteractionUtils.CreatePolyline(data, left, top,
this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15),
StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
- "none", "none", "0", scaleX, scaleY, "", this.props.active() ? "visiblepainted" : "none", false, true);
+ "none", "none", undefined, scaleX, scaleY, "", this.props.active() ? "visiblepainted" : "none", false, true);
//points for adding
const apoints = InteractionUtils.CreatePoints(data, left, top, strokeColor, strokeWidth, strokeWidth,
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index f3fba82bc..a05a2b858 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -4,6 +4,8 @@
.dash-tooltip {
font-size: 11px;
padding: 2px;
+ max-width: 150;
+ line-height: 150%;
}
.mainView-tabButtons {
@@ -157,7 +159,7 @@
width: 60px;
background-color: #121721;
- height: calc(100% - 32px);
+ height: calc(100% - $searchpanel-height);
//overflow-y: scroll;
//overflow-x: hidden;
@@ -215,7 +217,7 @@
.mainView-searchPanel {
width: 100%;
- height: 32px;
+ height: $searchpanel-height;
background-color: black;
color: white;
text-align: center;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4d5dfc99e..6bbe09974 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,12 +1,14 @@
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';
@@ -14,16 +16,20 @@ import { listSpec } from '../../fields/Schema';
import { ScriptField } from '../../fields/ScriptField';
import { BoolCast, Cast, FieldValue, StrCast } 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 } 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';
@@ -41,34 +47,28 @@ import { ContextMenu } from './ContextMenu';
import { DictationOverlay } from './DictationOverlay';
import { DocumentDecorations } from './DocumentDecorations';
import GestureOverlay from './GestureOverlay';
-import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss';
+import { ANTIMODEMENU_HEIGHT, SEARCH_PANEL_HEIGHT } from './globalCssVariables.scss';
import KeyManager from './GlobalKeyHandler';
import { LinkMenu } from './linking/LinkMenu';
import "./MainView.scss";
-import { MainViewNotifs } from './MainViewNotifs';
import { AudioBox } from './nodes/AudioBox';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { DocumentView } from './nodes/DocumentView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
-import RichTextMenu from './nodes/formattedText/RichTextMenu';
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 { undoBatch } from '../util/UndoManager';
-import { WebBox } from './nodes/WebBox';
-import * as ReactDOM from 'react-dom';
import { SearchBox } from './search/SearchBox';
@observer
export class MainView extends React.Component {
public static Instance: MainView;
private _buttonBarHeight = 36;
- private _flyoutSizeOnDown = 0;
private _urlState: HistoryUtil.DocUrl;
private _docBtnRef = React.createRef<HTMLDivElement>();
private _mainViewRef = React.createRef<HTMLDivElement>();
@@ -143,6 +143,7 @@ export class MainView extends React.Component {
constructor(props: Readonly<{}>) {
super(props);
MainView.Instance = this;
+ this.sidebarContent.proto = undefined;
this._urlState = HistoryUtil.parseUrl(window.location) || {} as any;
// causes errors to be generated when modifying an observable outside of an action
@@ -164,7 +165,7 @@ export class MainView extends React.Component {
}
}
- library.add(fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt,
+ library.add(fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
@@ -180,7 +181,8 @@ export class MainView extends React.Component {
fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper,
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.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, faBuffer, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
+ fa.faWindowMinimize, fa.faWindowRestore);
this.initEventListeners();
this.initAuthenticationRouters();
}
@@ -199,7 +201,7 @@ export class MainView extends React.Component {
let check = false;
const icon = "icon";
targets.forEach((thing) => {
- if (thing.className.toString() === "collectionSchemaView-table" || (thing as any)?.dataset[icon] === "filter" || thing.className.toString() === "beta" || thing.className.toString() === "collectionSchemaView-menuOptions-wrapper") {
+ if (thing.className.toString() === "collectionSchemaView-searchContainer" || (thing as any)?.dataset[icon] === "filter" || thing.className.toString() === "collectionSchema-header-menuOptions" || thing.className.toString() === "altcollectionTimeView-treeView") {
check = true;
}
});
@@ -251,6 +253,8 @@ export class MainView extends React.Component {
@action
createNewWorkspace = async (id?: string) => {
+ const myCatalog = Doc.UserDoc().myCatalog as Doc;
+ const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc;
const workspaceCount = DocListCast(workspaces.data).length + 1;
const freeformOptions: DocumentOptions = {
@@ -258,11 +262,13 @@ export class MainView extends React.Component {
y: 400,
_width: this._panelWidth * .7 - this.propertiesWidth() * 0.7,
_height: this._panelHeight,
- title: "Collection " + workspaceCount,
+ title: "Untitled Collection",
};
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 workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Workspace ${workspaceCount}` }, id, "row");
+ Doc.AddDocToList(myCatalog, "data", freeformDoc);
+ Doc.AddDocToList(myCatalog, "data", presentation);
+ Doc.UserDoc().activePresentation = presentation;
const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
const copyWorkspace = ScriptField.MakeScript(`copyWorkspace()`);
@@ -307,11 +313,7 @@ export class MainView extends React.Component {
DocServer.Control.makeEditable();
}
}
- // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
- setTimeout(async () => {
- const col = this.userDoc && await Cast(this.userDoc["sidebar-sharing"], Doc);
- col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col);
- }, 100);
+
return true;
}
@@ -332,7 +334,7 @@ export class MainView extends React.Component {
getPHeight = () => this._panelHeight;
getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
- defaultBackgroundColors = (doc: Opt<Doc>) => {
+ defaultBackgroundColors = (doc: Opt<Doc>, renderDepth: number) => {
if (this.panelContent === doc?.title) return "lightgrey";
if (doc?.type === DocumentType.COL) {
@@ -342,7 +344,7 @@ export class MainView extends React.Component {
|| doc.title === "Advanced Item Prototypes" || doc.title === "all Creators") {
return "lightgrey";
}
- return StrCast(Doc.UserDoc().defaultColor);
+ return StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
}
if (this.darkScheme) {
switch (doc?.type) {
@@ -402,7 +404,7 @@ export class MainView extends React.Component {
TraceMobx();
const mainContainer = this.mainContainer;
const width = this.flyoutWidth + this.propertiesWidth();
- return <div className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)`, height: `calc(100% - 32px)` }}>
+ return <div className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)`, height: `calc(100% - ${SEARCH_PANEL_HEIGHT})` }}>
{!mainContainer ? (null) : this.mainDocView}
</div>;
}
@@ -419,7 +421,7 @@ export class MainView extends React.Component {
onFlyoutPointerDown = (e: React.PointerEvent) => {
if (this._flyoutTranslate) {
setupMoveUpEvents(this, e, action((e: PointerEvent) => {
- this.flyoutWidth = Math.max(e.clientX, 0);
+ this.flyoutWidth = Math.max(e.clientX - 58, 0);
if (this.flyoutWidth < 5) {
this.panelContent = "none";
this._lastButton && (this._lastButton.color = "white");
@@ -439,15 +441,13 @@ export class MainView extends React.Component {
doc.dockingConfig ? this.openWorkspace(doc) :
CollectionDockingView.AddRightSplit(doc, libraryPath);
}
- sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0), 1);
- //sidebarScreenToLocal = () => new Transform(0, (RichTextMenu.Instance.Pinned ? -35 : 0) + (CollectionMenu.Instance.Pinned ? -35 : 0), 1);
- mainContainerXf = () => this.sidebarScreenToLocal().translate(-55, -this._buttonBarHeight);
+ sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1);
+ mainContainerXf = () => this.sidebarScreenToLocal().translate(-58, 0);
- @computed get closePosition() { return 55 + this.flyoutWidth; }
@computed get flyout() {
if (!this.sidebarContent) return null;
return <div className="mainView-libraryFlyout">
- <div className="mainView-contentArea" style={{ position: "relative", height: `calc(100% - 32px)`, width: "100%", overflow: "visible" }}>
+ <div className="mainView-contentArea" style={{ position: "relative", height: `calc(100% - ${SEARCH_PANEL_HEIGHT})`, width: "100%", overflow: "visible" }}>
{/* {this.flyoutWidth > 0 ? <div className="mainView-libraryFlyout-close"
onPointerDown={this.closeFlyout}>
<FontAwesomeIcon icon="times" color="black" size="lg" />
@@ -531,22 +531,24 @@ export class MainView extends React.Component {
_lastButton: Doc | undefined;
@action
- selectMenu = (button: Doc, str: string) => {
+ selectMenu = (button: Doc) => {
+ const title = StrCast(Doc.GetProto(button).title);
this._lastButton && (this._lastButton.color = "white");
this._lastButton && (this._lastButton._backgroundColor = "");
- if (this.panelContent === str && this.flyoutWidth !== 0) {
+ if (this.panelContent === title && this.flyoutWidth !== 0) {
this.panelContent = "none";
this.flyoutWidth = 0;
} else {
let panelDoc: Doc | undefined;
- switch (this.panelContent = str) {
- case "Tools": panelDoc = Doc.UserDoc()["sidebar-tools"] as Doc ?? undefined; break;
- case "Workspace": panelDoc = Doc.UserDoc()["sidebar-workspaces"] as Doc ?? undefined; break;
- case "Catalog": panelDoc = Doc.UserDoc()["sidebar-catalog"] as Doc ?? undefined; break;
- case "Archive": panelDoc = Doc.UserDoc()["sidebar-recentlyClosed"] as Doc ?? undefined; break;
+ switch (this.panelContent = title) {
case "Settings": SettingsManager.Instance.open(); break;
- case "Sharing": panelDoc = Doc.UserDoc()["sidebar-sharing"] as Doc ?? undefined; break;
- case "UserDoc": panelDoc = Doc.UserDoc()["sidebar-userDoc"] as Doc ?? undefined; 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;
+ default:
+ panelDoc = button.target as any; break;
}
this.sidebarContent.proto = panelDoc;
if (panelDoc) {
@@ -610,7 +612,6 @@ export class MainView extends React.Component {
</div>
</div>
{this.dockingContent}
- <MainViewNotifs />
{this.showProperties ? (null) :
<div className="mainView-propertiesDragger" title="Properties View Dragger" onPointerDown={this.onPropertiesPointerDown}
style={{ right: rightFlyout, top: "50%" }}>
@@ -654,7 +655,11 @@ export class MainView extends React.Component {
return !this._flyoutTranslate ? (<div className="mainView-expandFlyoutButton" title="Re-attach sidebar" onPointerDown={MainView.expandFlyout}></div>) : (null);
}
- addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
+ addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => {
+ const ret = flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc);
+ ret && (doc._stayInCollection = undefined);
+ return ret;
+ }, true)
remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
@@ -820,7 +825,6 @@ export class MainView extends React.Component {
{this.search}
<CollectionMenu />
<FormatShapePane />
- <div style={{ display: "none" }}><RichTextMenu key="rich" /></div>
{LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
{DocumentLinksButton.EditLink ? <LinkMenu docView={DocumentLinksButton.EditLink} addDocTab={DocumentLinksButton.EditLink.props.addDocTab} changeFlyout={emptyFunction} /> : (null)}
{LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} backgroundColor={this.defaultBackgroundColors}
@@ -895,8 +899,83 @@ export class MainView extends React.Component {
document.addEventListener("editSuccess", onSuccess);
});
}
+
+ importDocument = () => {
+ const sidebar = Cast(Doc.UserDoc()["sidebar-import-documents"], Doc, null);
+ const sidebarDocView = DocumentManager.Instance.getDocumentView(sidebar);
+ const input = document.createElement("input");
+ input.type = "file";
+ input.multiple = true;
+ input.accept = ".zip, application/pdf, video/*, image/*, audio/*";
+ input.onchange = async _e => {
+ const upload = Utils.prepend("/uploadDoc");
+ const formData = new FormData();
+ const file = input.files && input.files[0];
+ if (file && file.type === 'application/zip') {
+ formData.append('file', file);
+ formData.append('remap', "true");
+ const response = await fetch(upload, { method: "POST", body: formData });
+ const json = await response.json();
+ if (json !== "error") {
+ const doc = await DocServer.GetRefField(json);
+ if (doc instanceof Doc && sidebarDocView) {
+ sidebarDocView.props.addDocument?.(doc);
+ setTimeout(() => {
+ SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => {
+ docs.docs.forEach(d => LinkManager.Instance.addLink(d));
+ });
+ }, 2000); // need to give solr some time to update so that this query will find any link docs we've added.
+
+ }
+ }
+ } else if (input.files && input.files.length !== 0) {
+ const files = input.files || [];
+ Array.from(files).forEach(async file => {
+ const res = await Networking.UploadFilesToServer(file);
+ res.map(async ({ result }) => {
+ const name = file.name;
+ if (result instanceof Error) {
+ return;
+ }
+ const path = Utils.prepend(result.accessPaths.agnostic.client);
+ let doc: Doc;
+ // Case 1: File is a video
+ if (file.type.includes("video")) {
+ doc = Docs.Create.VideoDocument(path, { _height: 100, title: name });
+ // Case 2: File is a PDF document
+ } else if (file.type === "application/pdf") {
+ doc = Docs.Create.PdfDocument(path, { _height: 100, _fitWidth: true, title: name });
+ // Case 3: File is an image
+ } else if (file.type.includes("image")) {
+ doc = Docs.Create.ImageDocument(path, { _height: 100, title: name });
+ // Case 4: File is an audio document
+ } else {
+ doc = Docs.Create.AudioDocument(path, { title: name });
+ }
+ const res = await rp.get(Utils.prepend("/getUserDocumentId"));
+ if (!res) {
+ throw new Error("No user id returned");
+ }
+ const field = await DocServer.GetRefField(res);
+ let pending: Opt<Doc>;
+ if (field instanceof Doc) {
+ pending = sidebar;
+ }
+ if (pending) {
+ const data = await Cast(pending.data, listSpec(Doc));
+ if (data) data.push(doc);
+ else pending.data = new List([doc]);
+ }
+ });
+ });
+ } else {
+ console.log("No file selected");
+ }
+ };
+ input.click();
+ }
}
-Scripting.addGlobal(function freezeSidebar() { MainView.expandFlyout(); });
+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);
@@ -905,3 +984,5 @@ Scripting.addGlobal(function copyWorkspace() {
// 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 importDocument() { return MainView.Instance.importDocument(); },
+ "imports files from device directly into the import sidebar");
diff --git a/src/client/views/MainViewNotifs.scss b/src/client/views/MainViewNotifs.scss
deleted file mode 100644
index 92d7d6ee3..000000000
--- a/src/client/views/MainViewNotifs.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-.mainNotifs-container {
- position:absolute;
- z-index: 1000;
- top: 12px;
-
- .mainNotifs-badge {
- position: absolute;
- top: -10px;
- right: -10px;
- color: white;
- background: #f44b42;
- font-weight: 300;
- border-radius: 100%;
- width: 25px;
- height: 25px;
- text-align: center;
- padding-top: 4px;
- font-size: 12px;
- }
-} \ No newline at end of file
diff --git a/src/client/views/MainViewNotifs.tsx b/src/client/views/MainViewNotifs.tsx
deleted file mode 100644
index 89006ebc8..000000000
--- a/src/client/views/MainViewNotifs.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { observable } from 'mobx';
-import { observer } from 'mobx-react';
-import "normalize.css";
-import * as React from 'react';
-import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { returnFalse, setupMoveUpEvents } from '../../Utils';
-import { DragManager } from '../util/DragManager';
-import "./MainViewNotifs.scss";
-import { MainView } from './MainView';
-import { NumCast } from '../../fields/Types';
-
-
-@observer
-export class MainViewNotifs extends React.Component {
- @observable static NotifsCol: Opt<Doc>;
- _notifsRef = React.createRef<HTMLDivElement>();
-
- onPointerDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e,
- (e: PointerEvent) => {
- const dragData = new DragManager.DocumentDragData([MainViewNotifs.NotifsCol!]);
- DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
- return true;
- },
- returnFalse,
- () => MainViewNotifs.NotifsCol && MainView.Instance.selectMenu(MainViewNotifs.NotifsCol, "Sharing"));
- }
-
- render() {
- const length = MainViewNotifs.NotifsCol ? DocListCast(MainViewNotifs.NotifsCol.data).length : 0;
- return <div className="mainNotifs-container" style={{ width: 15, height: 15, top: 12 + NumCast(MainViewNotifs.NotifsCol?.position) * 60 }} ref={this._notifsRef}>
- <button className="mainNotifs-badge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
- onPointerDown={this.onPointerDown} >
- {length}
- </button>
- </div>;
- }
-}
diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss
index 09a349012..555f4298d 100644
--- a/src/client/views/OverlayView.scss
+++ b/src/client/views/OverlayView.scss
@@ -44,6 +44,6 @@
}
.overlayView-doc {
- z-index: 1;
+ z-index: 9002; //so that it appears above chroma
position: absolute;
} \ No newline at end of file
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 5c3a8185c..49580cde4 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -182,7 +182,7 @@ export class OverlayView extends React.Component {
offsetx = NumCast(d.x) - e.clientX;
offsety = NumCast(d.y) - e.clientY;
};
- return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
+ return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ top: d.type === 'presentation' ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
<DocumentView
Document={d}
LibraryPath={emptyPath}
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index d7034fcfb..0eee46275 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -132,6 +132,7 @@ export class PreviewCursor extends React.Component<{}> {
e.key !== "Insert" && e.key !== "Home" && e.key !== "End" && e.key !== "PageUp" && e.key !== "PageDown" &&
e.key !== "NumLock" && e.key !== " " &&
(e.keyCode < 112 || e.keyCode > 123) && // F1 thru F12 keys
+ (e.keyCode < 173 || e.keyCode > 183 || e.key === "-") && // mute, volume up/down etc, - is there specifically because its keycode is 173 in Firefox so shouldn't be avoided
!e.key.startsWith("Arrow") &&
!e.defaultPrevented) {
if ((!e.metaKey && !e.ctrlKey) || (e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90)) {// /^[a-zA-Z0-9$*^%#@+-=_|}{[]"':;?/><.,}]$/.test(e.key)) {
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 5e25ead87..9e776c652 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -3,7 +3,7 @@ import { faArrowAltCircleDown, faArrowAltCircleRight, faArrowAltCircleUp, faChec
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, AclEdit, AclAdmin } from "../../fields/Doc";
+import { Doc, DataSym, AclEdit, AclAdmin } from "../../fields/Doc";
import { RichTextField } from '../../fields/RichTextField';
import { Cast, NumCast, BoolCast } from "../../fields/Types";
import { emptyFunction, setupMoveUpEvents, Utils } from "../../Utils";
@@ -30,7 +30,7 @@ import { undoBatch, UndoManager } from '../util/UndoManager';
import { DocumentType } from '../documents/DocumentTypes';
import { InkField } from '../../fields/InkField';
import { PresBox } from './nodes/PresBox';
-import { GetEffectiveAcl } from "../../fields/util";
+import { GetEffectiveAcl } from '../../fields/util';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -77,12 +77,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
public static hasPulledHack = false;
+ @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; }
@computed get selectedDocumentView() {
if (SelectionManager.SelectedDocuments().length) {
return SelectionManager.SelectedDocuments()[0];
- } else { return undefined; }
+ } else return undefined;
}
- @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
@computed get dataDoc() { return this.selectedDocumentView?.dataDoc; }
@computed get onClick() { return this.selectedDoc?.onClickBehavior ? this.selectedDoc?.onClickBehavior : "nothing"; }
@@ -296,13 +296,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
}
@undoBatch
- onAliasButtonMoved = () => {
- if (this._dragRef.current) {
- const dragDocView = this.selectedDocumentView!;
- const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
- const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ onAliasButtonMoved = (e: PointerEvent) => {
+ if (this._dragRef.current && this.selectedDoc) {
+ const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
+ const [left, top] = [e.clientX, e.clientY];
dragData.dropAction = "alias";
- DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, {
+ DragManager.StartDocumentDrag([this._dragRef.current], dragData, left, top, {
offsetX: dragData.offset[0],
offsetY: dragData.offset[1],
hideSource: false
@@ -314,7 +313,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@computed
get templateButton() {
- const docView = this.selectedDocumentView;
+ const docView = this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined;
const templates: Map<Template, boolean> = new Map();
const views = [this.selectedDocumentView];
Array.from(Object.values(Templates.TemplateList)).map(template =>
@@ -366,7 +365,8 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@action @undoBatch
onLock = () => {
- this.selectedDocumentView?.toggleLockPosition();
+ const docView = this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined;
+ docView?.toggleLockPosition();
}
@computed
@@ -401,8 +401,8 @@ export class PropertiesButtons extends React.Component<{}, {}> {
<div>
<div className={"propertiesButtons-linkButton-empty"}
onPointerDown={async () => {
- if (this.selectedDocumentView?.props.Document) {
- Doc.Zip(this.selectedDocumentView?.props.Document);
+ if (this.selectedDoc) {
+ Doc.Zip(this.selectedDoc);
}
}}>
{<FontAwesomeIcon className="propertiesButtons-icon"
@@ -417,14 +417,14 @@ export class PropertiesButtons extends React.Component<{}, {}> {
get deleteButton() {
const targetDoc = this.selectedDoc;
return !targetDoc ? (null) : <Tooltip
- title={<><div className="dash-tooltip">{"Delete Document"}</div></>} placement="top">
+ title={<><div className="dash-tooltip">Close Document</div></>} placement="top">
<div>
<div className={"propertiesButtons-linkButton-empty"}
onPointerDown={this.deleteDocument}>
{<FontAwesomeIcon className="propertiesButtons-icon"
- icon="trash-alt" size="lg" />}
+ icon="times" size="lg" />}
</div>
- <div className="propertiesButtons-title"> delete </div>
+ <div className="propertiesButtons-title"> close </div>
</div>
</Tooltip>;
}
@@ -432,31 +432,22 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@undoBatch
@action
deleteDocument = () => {
- const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
- const selected = SelectionManager.SelectedDocuments().slice();
-
- selected.map(dv => {
- const effectiveAcl = GetEffectiveAcl(dv.props.Document);
- if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { // deletes whatever you have the right to delete
- recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
- dv.props.removeDocument?.(dv.props.Document);
- }
- });
- this.selectedDoc && (this.selectedDoc.deleted = true);
- this.selectedDocumentView?.props.ContainingCollectionView?.removeDocument(this.selectedDocumentView?.props.Document);
+ const removeDoc = this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView?.props.removeDocument : SelectionManager.SelectedSchemaCollection()?.props.removeDocument;
+ this.selectedDoc && removeDoc?.(this.selectedDoc);
SelectionManager.DeselectAll();
}
@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">
<div>
<div className={"propertiesButtons-linkButton-empty"}
onPointerDown={() => {
if (this.selectedDocumentView) {
- SharingManager.Instance.open(this.selectedDocumentView);
+ SharingManager.Instance.open(docView, this.selectedDoc);
}
}}>
{<FontAwesomeIcon className="propertiesButtons-icon"
@@ -493,20 +484,21 @@ export class PropertiesButtons extends React.Component<{}, {}> {
handleOptionChange = (e: any) => {
const value = e.target.value;
this.selectedDoc && (this.selectedDoc.onClickBehavior = e.target.value);
+ const docView = this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined;
if (value === "nothing") {
- this.selectedDocumentView?.noOnClick();
+ docView?.noOnClick();
} else if (value === "enterPortal") {
- this.selectedDocumentView?.noOnClick();
- this.selectedDocumentView?.makeIntoPortal();
+ docView?.noOnClick();
+ docView?.makeIntoPortal();
} else if (value === "toggleDetail") {
- this.selectedDocumentView?.noOnClick();
- this.selectedDocumentView?.toggleDetail();
+ docView?.noOnClick();
+ docView?.toggleDetail();
} else if (value === "linkInPlace") {
- this.selectedDocumentView?.noOnClick();
- this.selectedDocumentView?.toggleFollowLink("inPlace", true, false);
+ docView?.noOnClick();
+ docView?.toggleFollowLink("inPlace", true, false);
} else if (value === "linkOnRight") {
- this.selectedDocumentView?.noOnClick();
- this.selectedDocumentView?.toggleFollowLink("onRight", false, false);
+ docView?.noOnClick();
+ docView?.toggleFollowLink("onRight", false, false);
}
}
@@ -573,8 +565,8 @@ export class PropertiesButtons extends React.Component<{}, {}> {
<div>
<div className={"propertiesButtons-linkButton-empty"}
onPointerDown={() => {
- if (this.selectedDocumentView) {
- GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDocumentView.Document }).then(console.log);
+ if (this.selectedDoc) {
+ GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDoc }).then(console.log);
}
}}>
{<FontAwesomeIcon className="documentdecorations-icon"
@@ -648,7 +640,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
this.selectedDoc._backgroundColor = "rgba(0,0,0,0.7)";
this.selectedDoc.mixBlendMode = "hard-light";
this.selectedDoc.color = "#9b9b9bff";
- this.selectedDoc.stayInCollection = true;
+ this.selectedDoc._stayInCollection = true;
this.selectedDoc.isInkMask = true;
}
}
@@ -718,6 +710,8 @@ export class PropertiesButtons extends React.Component<{}, {}> {
const isInk = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof InkField;
const isCollection = this.selectedDoc.type === DocumentType.COL ? true : false;
const isFreeForm = this.selectedDoc._viewType === "freeform" ? true : false;
+ const hasContext = this.selectedDoc.context ? true : false;
+ const collectionAcl = GetEffectiveAcl(this.selectedDocumentView?.props.ContainingCollectionDoc?.[DataSym]);
return <div><div className="propertiesButtons" style={{ paddingBottom: "5.5px" }}>
<div className="propertiesButtons-button">
@@ -732,7 +726,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
<div className="propertiesButtons-button">
{this.pinWithViewButton}
</div>
- <div className="propertiesButtons-button">
+ <div className="propertiesButtons-button" style={{ display: hasContext ? "" : "none" }}>
{this.copyButton}
</div>
<div className="propertiesButtons-button">
@@ -741,15 +735,20 @@ export class PropertiesButtons extends React.Component<{}, {}> {
<div className="propertiesButtons-button">
{this.downloadButton}
</div>
- <div className="propertiesButtons-button">
- {this.deleteButton}
- </div>
+ {collectionAcl === AclAdmin || collectionAcl === AclEdit ?
+ <div className="propertiesButtons-button">
+ {this.deleteButton}
+ </div>
+ : (null)}
<div className="propertiesButtons-button">
{this.onClickButton}
</div>
<div className="propertiesButtons-button">
{this.sharingButton}
</div>
+ <div className="propertiesButtons-button">
+ {this.contextButton}
+ </div>
<div className="propertiesButtons-button" style={{ display: !considerPush ? "none" : "" }}>
{this.considerGoogleDocsPush}
</div>
@@ -774,9 +773,6 @@ export class PropertiesButtons extends React.Component<{}, {}> {
<div className="propertiesButtons-button" style={{ display: !isInk ? "none" : "" }}>
{this.maskButton}
</div>
- <div className="propertiesButtons-button">
- {this.contextButton}
- </div>
</div>
</div>;
}
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index 1eb380e0b..db087fb23 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -104,7 +104,7 @@ export class ScriptingRepl extends React.Component {
if (ts.isParameter(node.parent)) {
// delete knownVars[node.text];
} else if (isntPropAccess && isntPropAssign && !(node.text in knownVars) && !(node.text in globalThis)) {
- const match = node.text.match(/\d([0-9]+)/);
+ const match = node.text.match(/d([0-9]+)/);
if (match) {
const m = parseInt(match[1]);
usedDocuments.push(m);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 27aea4b99..8a27f8102 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -64,7 +64,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
</div>
<div className="collectionCarouselView-caption" key="caption"
style={{
- background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.backgroundColor?.(this.props.Document)),
+ background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)),
color: StrCast(this.layoutDoc._captionColor, StrCast(this.layoutDoc.color)),
borderRadius: StrCast(this.layoutDoc._captionBorderRounding),
}}>
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 4204ef5bb..6ebd5103b 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -20,82 +20,6 @@
}
}
-.miniPres:hover {
- opacity: 1;
-}
-
-.miniPres {
- position: absolute;
- overflow: hidden;
- right: 10;
- top: 10;
- opacity: 0.1;
- transition: all 0.4s;
- /* border: solid 1px; */
- color: white;
- /* box-shadow: black 0.4vw 0.4vw 0.8vw; */
-
- .miniPresOverlay {
- display: grid;
- grid-template-columns: auto auto auto auto auto auto auto auto;
- grid-template-rows: 100%;
- height: 100%;
- justify-items: center;
- align-items: center;
-
- .miniPres-button-text {
- display: flex;
- height: 20;
- font-weight: 400;
- min-width: 100%;
- border-radius: 5px;
- align-items: center;
- justify-content: center;
- transition: all 0.3s;
- }
-
- .miniPres-button-frame {
- justify-self: center;
- align-self: center;
- align-items: center;
- display: grid;
- grid-template-columns: auto auto auto;
- justify-content: space-around;
- font-size: 11;
- margin-left: 7;
- width: 30;
- height: 85%;
- background-color: rgba(91, 157, 221, 0.4);
- border-radius: 5px;
- }
-
- .miniPres-divider {
- width: 0.5px;
- height: 80%;
- border-right: solid 2px #5a5a5a;
- }
-
- .miniPres-button {
- display: flex;
- height: 20;
- min-width: 20;
- border-radius: 100%;
- align-items: center;
- justify-content: center;
- transition: all 0.3s;
- }
-
- .miniPres-button:hover {
- background-color: #5a5a5a;
- }
-
- .miniPres-button-text:hover {
- background-color: #5a5a5a;
- }
- }
-}
-
-
.lm_title {
margin-top: 3px;
border-radius: 5px;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 3691e844f..43da0d3cf 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -191,31 +191,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
return retVal;
}
- @undoBatch
- @action
- public static ReplaceTab(document: Doc, stack: any): Opt<Doc> {
- if (!CollectionDockingView.Instance) return undefined;
- const instance = CollectionDockingView.Instance;
- const replaceTab = (doc: Doc, child: any): Opt<Doc> => {
- for (const contentItem of child.contentItems) {
- const { config, isStack, isRow, isColumn } = contentItem;
- if (isRow || isColumn || isStack) {
- const val = replaceTab(doc, contentItem);
- if (val) return val;
- } else if (config.component === "DocumentFrameRenderer" &&
- config.props.documentId === doc[Id]) {
- const alias = Doc.MakeAlias(doc);
- config.props.documentId = alias[Id];
- config.title = alias.title;
- instance.stateChanged();
- return alias;
- }
- }
- return undefined;
- };
- return replaceTab(document, instance._goldenLayout.root);
- }
-
//
// Creates a vertical split on the right side of the docking view, and then adds the Document to the right of that split
//
@@ -358,6 +333,32 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
return true;
}
+ @undoBatch
+ @action
+ public ReplaceTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
+ Doc.GetProto(document).lastOpened = new DateField;
+ const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
+ if (stack === undefined) {
+ let stack: any = this._goldenLayout.root;
+ while (!stack.isStack) {
+ if (stack.contentItems.length) {
+ stack = stack.contentItems[0];
+ } else {
+ stack.addChild({ type: 'stack', content: [docContentConfig] });
+ stack = undefined;
+ break;
+ }
+ }
+ if (stack) {
+ stack.addChild(docContentConfig);
+ }
+ } else {
+ stack.addChild(docContentConfig, undefined);
+ }
+ this.layoutChanged();
+ return true;
+ }
+
setupGoldenLayout() {
const config = StrCast(this.props.Document.dockingConfig);
if (config) {
@@ -727,9 +728,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
pinDoc.presZoomButton = true;
pinDoc.context = curPres;
Doc.AddDocToList(curPres, "data", pinDoc);
+ if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
if (!DocumentManager.Instance.getDocumentView(curPres)) {
CollectionDockingView.AddRightSplit(curPres);
}
+ DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null));
}
}
}
@@ -758,7 +761,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
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, "white") }),
+ 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 : "");
@@ -821,7 +824,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
if (this._mainCont && this._mainCont.children) {
const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0].firstChild as HTMLElement);
const scale = Utils.GetScreenTransform(this._mainCont).scale;
- return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale);
+ return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale);
}
return Transform.Identity();
}
@@ -837,12 +840,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
} else if (location === "close") {
return CollectionDockingView.CloseRightSplit(doc);
} else if (location === "replace") {
- const alias = CollectionDockingView.ReplaceTab(doc, this._stack);
- if (alias) {
- runInAction(() => this._document = alias);
- return true;
- }
- return false;
+ CollectionDockingView.UseRightSplit(doc);
+ return true;
} else {// if (location === "inPlace") {
return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath);
}
@@ -873,30 +872,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
const currentFrame = Cast(presTargetDoc.currentFrame, "number", null);
return currentFrame;
}
- renderMiniPres() {
- return (
- <div className="miniPres"
- style={{ width: 250, height: 30, background: '#323232' }}
- >
- {<div className="miniPresOverlay">
- <div className="miniPres-button" onClick={PresBox.Instance.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
- <div className="miniPres-button" onClick={() => PresBox.Instance.startAutoPres(PresBox.Instance.itemIndex)}><FontAwesomeIcon icon={PresBox.Instance.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div>
- <div className="miniPres-button" onClick={PresBox.Instance.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
- <div className="miniPres-divider"></div>
- <div className="miniPres-button-text">
- Slide {PresBox.Instance.itemIndex + 1} / {PresBox.Instance.childDocs.length}
- {PresBox.Instance.playButtonFrames}
- </div>
- <div className="miniPres-divider"></div>
- <div className="miniPres-button-text" onClick={PresBox.Instance.updateMinimize}>EXIT</div>
- </div>}
- </div>
- );
- }
+
renderMiniMap() {
return <div className="miniMap" style={{
width: this.returnMiniSize(), height: this.returnMiniSize(), background: StrCast(this._document!._backgroundColor,
- StrCast(this._document!.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document!))),
+ StrCast(this._document!.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document!, 0))),
}}>
<CollectionFreeFormView
Document={this._document!}
@@ -975,7 +955,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
{document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)}
- {document._viewType === CollectionViewType.Freeform && this._document?.miniPres ? this.renderMiniPres() : (null)}
</>;
}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 3cf46dbed..e1b07077e 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -119,7 +119,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
// transform: this.props.Document.linearViewIsExpanded ? "" : "rotate(45deg)"
}}
onPointerDown={e => e.stopPropagation()} >
- <p>+</p>
+ <p>{BoolCast(this.props.Document.linearViewIsExpanded) ? "–" : "+"}</p>
</label>;
return <div className="collectionLinearView-outer">
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index cfec3a6bc..1af1a05aa 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -69,7 +69,7 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap
if (!this._initialLookupPending.get(id)) {
this._initialLookupPending.set(id, true);
setTimeout(() => {
- titleLoc && Doc.SetInPlace(doc, "title", titleLoc, true);
+ titleLoc && Doc.SetInPlace(doc, `${fieldKey}-address`, titleLoc, true);
this.respondToAddressChange(doc, fieldKey, address).then(() => this._initialLookupPending.delete(id));
});
}
@@ -114,12 +114,12 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap
}
}
- private renderMarker = (layout: Doc) => {
- const location = this.getLocation(layout, Doc.LayoutFieldKey(layout));
+ private renderMarker = (layout: Doc, fieldKey?: string) => {
+ const location = this.getLocation(layout, fieldKey || Doc.LayoutFieldKey(layout));
return !location ? (null) :
<Marker
key={layout[Id]}
- label={StrCast(layout.title)}
+ label={StrCast(layout[`${this.props.fieldKey}-address`])}
position={location}
onClick={() => this.markerClick(layout, location)}
icon={this.renderMarkerIcon(layout)}
@@ -250,7 +250,7 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap
}}
>
{this.reactiveContents}
- {mapLoc ? this.renderMarker(this.rootDoc) : undefined}
+ {mapLoc && StrCast(this.rootDoc[`${fieldKey}-mapCenter-address`]) ? this.renderMarker(this.rootDoc, `${fieldKey}-mapCenter`) : undefined}
</GeoMap>
</div>
</div>;
@@ -260,9 +260,10 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap
export default GoogleApiWrapper({
apiKey: process.env.GOOGLE_MAPS!,
- LoadingContainer: () => (
- <div className={"loadingWrapper"}>
+ LoadingContainer: () => {
+ console.log(process.env.GOOGLE_MAPS);
+ return <div className={"loadingWrapper"}>
<img className={"loadingGif"} src={"/assets/loading.gif"} />
- </div>
- )
+ </div>;
+ }
})(CollectionMapView) as any; \ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index b41cbe92d..d0bfd0a41 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -229,14 +229,18 @@
.flexLabel {
margin-bottom: 0;
}
- }
- .collectionGridViewChrome-entryBox {
- width: 50%;
+ .collectionGridViewChrome-entryBox {
+ width: 50%;
+ color: black;
+ }
+
+ .collectionGridViewChrome-columnButton {
+ color: black;
+ }
}
}
-
.collectionStackingViewChrome-sort,
.collectionTreeViewChrome-sort {
display: flex;
@@ -311,10 +315,62 @@
}
}
+.webBox-urlEditor {
+ position: relative;
+ opacity: 0.9;
+ z-index: 9001;
+ transition: top .5s;
+
+ .urlEditor {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ padding-bottom: 10px;
+ overflow: hidden;
+ margin-top: 5px;
+ height: 35px;
+
+ .editorBase {
+ display: flex;
+
+ .editor-collapse {
+ transition: all .5s, opacity 0.3s;
+ position: absolute;
+ width: 40px;
+ transform-origin: top left;
+ }
+
+ .switchToText {
+ color: $main-accent;
+ }
+
+ .switchToText:hover {
+ color: $dark-color;
+ }
+ }
+
+ button:hover {
+ transform: scale(1);
+ }
+ }
+}
+
+.webpage-urlInput {
+ padding: 12px 10px 11px 10px;
+ border: 0px;
+ color: grey;
+ letter-spacing: 2px;
+ outline-color: black;
+ background: rgb(238, 238, 238);
+ width: 100%;
+ margin-right: 10px;
+ height: 100%;
+}
+
.collectionFreeFormMenu-cont {
display: inline-flex;
position: relative;
align-items: center;
+ margin-left: 10px;
.antimodeMenu-button {
text-align: center;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 53d2a136e..388eda2b3 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -11,17 +11,18 @@ import { Id } from "../../../fields/FieldSymbols";
import { InkTool } from "../../../fields/InkField";
import { List } from "../../../fields/List";
import { ObjectField } from "../../../fields/ObjectField";
-import { RichTextField } from "../../../fields/RichTextField";
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
+import { WebField } from "../../../fields/URLField";
import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils";
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { DragManager } from "../../util/DragManager";
+import { Scripting } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
import { undoBatch } from "../../util/UndoManager";
-import AntimodeMenu from "../AntimodeMenu";
+import AntimodeMenu, { AntimodeMenuProps } from "../AntimodeMenu";
import { EditableView } from "../EditableView";
import GestureOverlay from "../GestureOverlay";
import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
@@ -32,13 +33,13 @@ import "./CollectionMenu.scss";
import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
@observer
-export default class CollectionMenu extends AntimodeMenu {
+export default class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: CollectionMenu;
@observable SelectedCollection: DocumentView | undefined;
@observable FieldKey: string;
- constructor(props: Readonly<{}>) {
+ constructor(props: any) {
super(props);
this.FieldKey = "";
CollectionMenu.Instance = this;
@@ -85,7 +86,8 @@ export default class CollectionMenu extends AntimodeMenu {
const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Properties Panel" : "Open Properties Panel";
const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="properties" placement="bottom">
- <button className="antimodeMenu-button" key="properties" onPointerDown={this.toggleProperties}>
+ <button className="antimodeMenu-button" key="properties" style={{ backgroundColor: "#424242" }}
+ onPointerDown={this.toggleProperties}>
<FontAwesomeIcon icon={propIcon} size="lg" />
</button>
</Tooltip>;
@@ -160,9 +162,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
};
_viewCommand = {
params: ["target"], title: "bookmark view",
- script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale'];",
- immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; }),
- initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; },
+ script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale']; gotoFrame(self.target, self['target-currentFrame']);",
+ immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; this.target.currentFrame = 0; }),
+ initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; button['target-currentFrame'] = this.target.currentFrame; },
};
_clusterCommand = {
params: ["target"], title: "fit content",
@@ -428,7 +430,9 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
@computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
@computed get isText() {
if (this.selectedDoc) {
- return this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof RichTextField;
+ const layoutField = Doc.LayoutField(this.selectedDoc);
+ return StrCast(layoutField).includes("FormattedText") ||
+ (layoutField instanceof Doc && StrCast(layoutField.layout).includes("FormattedText"));
}
else return false;
}
@@ -580,9 +584,9 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
onPointerDown={action(() => { this.changeColor(color, "color"); this._colorBtn = false; this.editProperties(color, "color"); })}
style={{ backgroundColor: this._colorBtn ? "121212" : "", zIndex: 1001 }}>
{/* <FontAwesomeIcon icon="pen-nib" size="lg" /> */}
- <div className="color-previewII" style={{ backgroundColor: color }} />
- {color === "" ? <p style={{ fontSize: 45, color: "red", marginTop: -16, marginLeft: -5, position: "fixed" }}>☒</p> : ""}
-
+ <div className="color-previewII" style={{ backgroundColor: color }}>
+ {color === "" ? <p style={{ fontSize: 45, color: "red", marginTop: -16, marginLeft: -5, position: "fixed" }}>☒</p> : ""}
+ </div>
</button>)}
</div>;
}
@@ -604,6 +608,158 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</div>;
}
+ @action
+ onUrlDrop = (e: React.DragEvent) => {
+ const { dataTransfer } = e;
+ const html = dataTransfer.getData("text/html");
+ const uri = dataTransfer.getData("text/uri-list");
+ const url = uri || html || this._url;
+ this._url = url.startsWith(window.location.origin) ?
+ url.replace(window.location.origin, this._url.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url;
+ this.submitURL();
+ e.stopPropagation();
+ }
+ onUrlDragover = (e: React.DragEvent) => {
+ e.preventDefault();
+ }
+
+ @computed get _url() {
+ return this.selectedDoc ? Cast(this.selectedDoc.data, WebField, null)?.url.toString() : "hello";
+ }
+
+ set _url(value) { this.selectedDoc && (this.selectedDoc.data = value); }
+
+ @action
+ submitURL = () => {
+ if (!this._url.startsWith("http")) this._url = "http://" + this._url;
+ try {
+ const URLy = new URL(this._url);
+ const future = this.selectedDoc ? Cast(this.selectedDoc["data-future"], listSpec("string"), null) : null;
+ const history = this.selectedDoc ? Cast(this.selectedDoc["data-history"], listSpec("string"), null) : [];
+ const annos = this.selectedDoc ? DocListCast(this.selectedDoc["data-annotations"]) : undefined;
+ const url = this.selectedDoc ? Cast(this.selectedDoc.data, WebField, null)?.url.toString() : null;
+ if (url) {
+ if (history === undefined) {
+ this.selectedDoc && (this.selectedDoc["data-history"] = new List<string>([url]));
+
+ } else {
+ history.push(url);
+ }
+ future && (future.length = 0);
+ this.selectedDoc && (this.selectedDoc["data-" + this.urlHash(url)] = new List<Doc>(annos));
+ }
+ this.selectedDoc && (this.selectedDoc.data = new WebField(URLy));
+ this.selectedDoc && (this.selectedDoc["data-annotations"] = new List<Doc>([]));
+ } catch (e) {
+ console.log("WebBox URL error:" + this._url);
+ }
+ }
+
+ urlHash(s: string) {
+ return s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0);
+ }
+
+ toggleAnnotationMode = () => {
+ this.props.docView.layoutDoc.isAnnotating = !this.props.docView.layoutDoc.isAnnotating;
+ }
+
+ @action
+ onURLChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this._url = e.target.value;
+ }
+
+ onValueKeyDown = async (e: React.KeyboardEvent) => {
+ if (e.key === "Enter") {
+ this.submitURL();
+ }
+ e.stopPropagation();
+ }
+
+ @action
+ forward = () => {
+ const future = this.selectedDoc && (Cast(this.selectedDoc["data-future"], listSpec("string"), null));
+ const history = this.selectedDoc && Cast(this.selectedDoc["data-history"], listSpec("string"), null);
+ if (future?.length) {
+ history?.push(this._url);
+ this.selectedDoc && (this.selectedDoc["data-annotations-" + this.urlHash(this._url)] = new List<Doc>(DocListCast(this.selectedDoc["data-annotations"])));
+ this.selectedDoc && (this.selectedDoc.data = new WebField(new URL(this._url = future.pop()!)));
+ this.selectedDoc && (this.selectedDoc["data-annotations"] = new List<Doc>(DocListCast(this.selectedDoc["data-annotations" + "-" + this.urlHash(this._url)])));
+ }
+ }
+
+ @action
+ back = () => {
+ const future = this.selectedDoc && (Cast(this.selectedDoc["data-future"], listSpec("string"), null));
+ const history = this.selectedDoc && Cast(this.selectedDoc["data-history"], listSpec("string"), null);
+ if (history?.length) {
+ if (future === undefined) this.selectedDoc && (this.selectedDoc["data-future"] = new List<string>([this._url]));
+ else future.push(this._url);
+ this.selectedDoc && (this.selectedDoc["data-annotations" + "-" + this.urlHash(this._url)] = new List<Doc>(DocListCast(this.selectedDoc["data-annotations"])));
+ this.selectedDoc && (this.selectedDoc.data = new WebField(new URL(this._url = history.pop()!)));
+ this.selectedDoc && (this.selectedDoc["data-annotations"] = new List<Doc>(DocListCast(this.selectedDoc["data-annotations" + "-" + this.urlHash(this._url)])));
+ }
+ }
+
+ private _keyInput = React.createRef<HTMLInputElement>();
+
+ @computed get urlEditor() {
+ return (
+ <div className="webBox-urlEditor"
+ onDrop={this.onUrlDrop}
+ onDragOver={this.onUrlDragover} style={{ top: 0 }}>
+ <div className="urlEditor">
+ <div className="editorBase">
+ <div className="webBox-buttons"
+ onDrop={this.onUrlDrop}
+ onDragOver={this.onUrlDragover} style={{ display: "flex" }}>
+ <div className="webBox-freeze" title={"Annotate"}
+ style={{ background: this.props.docView.layoutDoc.isAnnotating ? "lightBlue" : "gray" }}
+ onClick={this.toggleAnnotationMode} >
+ <FontAwesomeIcon icon="pen" size={"2x"} />
+ </div>
+ <div className="webBox-freeze" title={"Select"}
+ style={{ background: !this.props.docView.layoutDoc.isAnnotating ? "lightBlue" : "gray" }}
+ onClick={this.toggleAnnotationMode} >
+ <FontAwesomeIcon icon={"mouse-pointer"} size={"2x"} />
+ </div>
+ <input className="webpage-urlInput"
+ placeholder="ENTER URL"
+ value={this._url}
+ onDrop={this.onUrlDrop}
+ onDragOver={this.onUrlDragover}
+ onChange={this.onURLChange}
+ onKeyDown={this.onValueKeyDown}
+ onClick={(e) => {
+ this._keyInput.current!.select();
+ e.stopPropagation();
+ }}
+ ref={this._keyInput}
+ />
+ <div style={{
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ maxWidth: "120px",
+ }}>
+ <button className="submitUrl" onClick={this.submitURL}
+ onDragOver={this.onUrlDragover} onDrop={this.onUrlDrop}>
+ GO
+ </button>
+ <button className="submitUrl" onClick={this.back}>
+ <FontAwesomeIcon icon="caret-left" size="lg"></FontAwesomeIcon>
+ </button>
+ <button className="submitUrl" onClick={this.forward}>
+ <FontAwesomeIcon icon="caret-right" size="lg"></FontAwesomeIcon>
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+
@observable viewType = this.selectedDoc?._viewType;
render() {
@@ -622,7 +778,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</div>
</Tooltip> : null}
{!this.isText && !this.props.isDoc ? <Tooltip key="num" title={<div className="dash-tooltip">Toggle View All</div>} placement="bottom">
- <div className="numKeyframe" style={{ backgroundColor: this.document.editing ? "#759c75" : "#c56565" }}
+ <div className="numKeyframe" style={{ color: this.document.editing ? "white" : "black", backgroundColor: this.document.editing ? "#5B9FDD" : "#AEDDF8" }}
onClick={action(() => this.document.editing = !this.document.editing)} >
{NumCast(this.document.currentFrame)}
</div>
@@ -645,6 +801,9 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</button>
</Tooltip>
}
+ {/* {!this.props.isOverlay || this.document.type !== DocumentType.WEB || this.isText || this.props.isDoc ? (null) :
+ this.urlEditor
+ } */}
{!this.isText ?
<>
{this.drawButtons}
@@ -654,7 +813,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</> :
(null)
}
- {this.isText ? <RichTextMenu key="rich" /> : null}
+ {this.isText ? <RichTextMenu /> : null}
</div>;
}
}
@@ -669,7 +828,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu
@computed get pivotField() { return StrCast(this.document._pivotField); }
getKeySuggestions = async (value: string): Promise<string[]> => {
- value = value.toLowerCase();
+ const val = value.toLowerCase();
const docs = DocListCast(this.document[this.props.fieldKey]);
if (Doc.UserDoc().noviceMode) {
@@ -677,24 +836,24 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu
const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
(key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key !== "UseCors" && key[0] !== "_"));
- return keys.filter(key => key.toLowerCase().indexOf(value.toLowerCase()) > -1);
+ return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
} else {
const keys = new Set<string>();
docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 ||
key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 ||
- key.indexOf("lastModified") >= 0 || (key[0].toUpperCase() === key[0] &&
+ key.indexOf("lastModified") >= 0 || (key[0]?.toUpperCase() === key[0] &&
key.substring(0, 3) !== "ACL" && key !== "UseCors" && key[0] !== "_"));
- return noviceKeys.filter(key => key.toLowerCase().indexOf(value.toLowerCase()) > -1);
+ return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
}
}
if (docs instanceof Doc) {
- return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
+ return Object.keys(docs).filter(key => key.toLowerCase().indexOf(val) > -1);
} else {
const keys = new Set<string>();
docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
- return Array.from(keys).filter(key => key.toLowerCase().startsWith(value));
+ return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1);
}
}
@@ -735,8 +894,10 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu
@action resetValue = () => { this._currentKey = this.pivotField; };
render() {
+ const doctype = this.props.docView.Document.type;
+ const isPres: boolean = (doctype === DocumentType.PRES);
return (
- <div className="collectionStackingViewChrome-cont">
+ isPres ? (null) : <div className="collectionStackingViewChrome-cont">
<div className="collectionStackingViewChrome-pivotField-cont">
<div className="collectionStackingViewChrome-pivotField-label">
GROUP BY:
@@ -930,16 +1091,10 @@ export class CollectionGridViewChrome extends React.Component<CollectionMenuProp
get numCols() { return NumCast(this.document.gridNumCols, 10); }
/**
- * Sets the value of `numCols` on the grid's Document to the value entered.
- */
- @undoBatch
- onNumColsEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
- if (e.key === "Enter" || e.key === "Tab") {
- if (e.currentTarget.valueAsNumber > 0) {
- this.document.gridNumCols = e.currentTarget.valueAsNumber;
- }
-
- }
+ * Sets the value of `numCols` on the grid's Document to the value entered.
+ */
+ onNumColsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ if (e.currentTarget.valueAsNumber > 0) undoBatch(() => this.document.gridNumCols = e.currentTarget.valueAsNumber)();
}
/**
@@ -977,9 +1132,10 @@ export class CollectionGridViewChrome extends React.Component<CollectionMenuProp
*/
onDecrementButtonClick = () => {
this.clicked = true;
- if (!this.decrementLimitReached) {
+ if (this.numCols > 1 && !this.decrementLimitReached) {
this.entered && (this.document.gridNumCols as number)++;
undoBatch(() => this.document.gridNumCols = this.numCols - 1)();
+ if (this.numCols === 1) this.decrementLimitReached = true;
}
this.entered = false;
}
@@ -1002,7 +1158,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionMenuProp
decrementValue = () => {
this.entered = true;
if (!this.clicked) {
- if (this.numCols !== 1) {
+ if (this.numCols > 1) {
this.document.gridNumCols = this.numCols - 1;
}
else {
@@ -1035,9 +1191,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionMenuProp
<span className="grid-icon">
<FontAwesomeIcon icon="columns" size="1x" />
</span>
- <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.numCols.toString()} onKeyDown={this.onNumColsEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
- <input className="columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
- <input className="columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
+ <input className="collectionGridViewChrome-entryBox" type="number" value={this.numCols} onChange={this.onNumColsChange} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
+ <input className="collectionGridViewChrome-columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
+ <input className="collectionGridViewChrome-columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
</span>
{/* <span className="grid-control">
<span className="grid-icon">
@@ -1078,4 +1234,15 @@ export class CollectionGridViewChrome extends React.Component<CollectionMenuProp
</div>
);
}
-} \ No newline at end of file
+}
+Scripting.addGlobal(function gotoFrame(doc: any, newFrame: any) {
+ const dataField = doc[Doc.LayoutFieldKey(doc)];
+ const childDocs = DocListCast(dataField);
+ const currentFrame = Cast(doc.currentFrame, "number", null);
+ if (currentFrame === undefined) {
+ doc.currentFrame = 0;
+ CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
+ }
+ CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
+ doc.currentFrame = Math.max(0, newFrame);
+}); \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 8a1dd8472..ea786800c 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -1,5 +1,5 @@
import React = require("react");
-import { action, observable, trace, computed } from "mobx";
+import { action, observable, trace, computed, runInAction } from "mobx";
import { observer } from "mobx-react";
import { CellInfo } from "react-table";
import "react-table/react-table.css";
@@ -33,6 +33,9 @@ import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { DateField } from "../../../fields/DateField";
import { RichTextField } from "../../../fields/RichTextField";
+import { DocumentManager } from "../../util/DocumentManager";
+import { SearchUtil } from "../../util/SearchUtil";
+import { DocumentType } from "../../documents/DocumentTypes";
const path = require('path');
library.add(faExpand);
@@ -67,10 +70,12 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
protected _document = this.props.rowProps.original;
protected _dropDisposer?: DragManager.DragDropDisposer;
- componentDidMount() {
+ async componentDidMount() {
document.addEventListener("keydown", this.onKeyDown);
}
+ @observable contents: string = "";
+
componentWillUnmount() {
document.removeEventListener("keydown", this.onKeyDown);
}
@@ -142,10 +147,8 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
}
protected dropRef = (ele: HTMLElement | null) => {
- this._dropDisposer && this._dropDisposer();
- if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
- }
+ this._dropDisposer?.();
+ ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
}
// expandDoc = (e: React.PointerEvent) => {
@@ -159,6 +162,31 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
// e.stopPropagation();
// }
+ returnHighlights(bing: (() => string), positions?: number[]) {
+ const results = [];
+ const contents = bing();
+
+ if (positions !== undefined) {
+ StrCast(this.props.Document._searchString);
+ const length = StrCast(this.props.Document._searchString).length;
+ const color = contents ? "black" : "grey";
+
+ results.push(<span key="-1" style={{ color }}>{contents?.slice(0, positions[0])}</span>);
+ positions.forEach((num, cur) => {
+ results.push(<span key={"start" + cur} style={{ backgroundColor: "#FFFF00", color }}>{contents?.slice(num, num + length)}</span>);
+ let end = 0;
+ cur === positions.length - 1 ? end = contents.length : end = positions[cur + 1];
+ results.push(<span key={"end" + cur} style={{ color }}>{contents?.slice(num + length, end)}</span>);
+ }
+ );
+ return results;
+ }
+ else {
+ return <span style={{ color: contents ? "black" : "grey" }}>{contents ? contents?.valueOf() : "undefined"}</span>;
+ }
+ }
+ type: string = "";
+
renderCellWithType(type: string | undefined) {
const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -200,12 +228,23 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const doc = FieldValue(Cast(field, Doc));
const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc);
- const onItemDown = (e: React.PointerEvent) => {
- //fieldIsDoc &&
- SetupDrag(this._focusRef,
- () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
- this._document[props.fieldKey] instanceof Doc ? (doc: Doc | Doc[], target: Doc | undefined, addDoc: (newDoc: Doc | Doc[]) => any) => addDoc(doc) : this.props.moveDocument,
- this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
+ const onItemDown = async (e: React.PointerEvent) => {
+ if (this.props.Document._searchDoc !== undefined) {
+ const doc = Doc.GetProto(this.props.rowProps.original);
+ const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc);
+ let targetContext = undefined;
+ if (aliasdoc.length > 0) {
+ targetContext = Cast(aliasdoc[0].context, Doc) as Doc;
+ }
+ DocumentManager.Instance.jumpToDocument(this.props.rowProps.original, false, undefined, targetContext);
+ }
+ else {
+ fieldIsDoc &&
+ SetupDrag(this._focusRef,
+ () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
+ this._document[props.fieldKey] instanceof Doc ? (doc: Doc | Doc[], target: Doc | undefined, addDoc: (newDoc: Doc | Doc[]) => any) => addDoc(doc) : this.props.moveDocument,
+ this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
+ }
};
const onPointerEnter = (e: React.PointerEvent): void => {
if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) {
@@ -217,9 +256,13 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
};
let contents: any = "incorrect type";
- if (type === undefined) contents = <FieldView {...props} fieldKey={fieldKey} />;
+ if (type === undefined) contents = field === undefined ? undefined : Field.toString(field as Field);//StrCast(field) === "" ? "--" : <FieldView {...props} fieldKey={fieldKey} />;
if (type === "number") contents = typeof field === "number" ? NumCast(field) : "--" + typeof field + "--";
- if (type === "string") contents = typeof field === "string" ? (StrCast(field) === "" ? "--" : StrCast(field)) : "--" + typeof field + "--";
+ if (type === "string") {
+ fieldKey === "text" ?
+ contents = Cast(field, RichTextField)?.Text :
+ contents = typeof field === "string" ? (StrCast(field) === "" ? "--" : StrCast(field)) : "--" + typeof field + "--";
+ }
if (type === "boolean") contents = typeof field === "boolean" ? (BoolCast(field) ? "true" : "false") : "--" + typeof field + "--";
if (type === "document") {
const doc = FieldValue(Cast(field, Doc));
@@ -250,20 +293,10 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
// </div>
// );
const positions = [];
+ let cfield = props.Document[props.fieldKey];
+ this.type = props.fieldKey;
if (StrCast(this.props.Document._searchString).toLowerCase() !== "") {
- const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey]));
- let term = "";
- if (cfield !== undefined) {
- if (cfield.Text !== undefined) {
- term = cfield.Text;
- }
- else if (StrCast(cfield)) {
- term = StrCast(cfield);
- }
- else {
- term = String(NumCast(cfield));
- }
- }
+ let term = (cfield instanceof Promise) ? "...promise pending..." : Field.toString(cfield as Field);
term = term.toLowerCase();
const search = StrCast(this.props.Document._searchString).toLowerCase();
let start = term.indexOf(search);
@@ -271,7 +304,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
if (start !== -1) {
positions.push(start);
}
- while (start < contents.length && start !== -1) {
+ while (start < contents?.length && start !== -1) {
term = term.slice(start + search.length + 1);
tally += start + search.length + 1;
start = term.indexOf(search);
@@ -281,89 +314,123 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
positions.pop();
}
}
+ let search = false;
+ if (this.props.Document._searchDoc !== undefined) {
+ search = true;
+ }
+
+ const placeholder = type === "number" ? "0" : contents === "" ? "--" : "undefined";
return (
<div className="collectionSchemaView-cellContainer" style={{ cursor: fieldIsDoc ? "grab" : "auto" }}
ref={dragRef} onPointerDown={this.onPointerDown} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave}>
<div className={className} ref={this._focusRef} onPointerDown={onItemDown} tabIndex={-1}>
- <div className="collectionSchemaView-cellContents" ref={type === undefined || type === "document" ? this.dropRef : null} key={props.Document[Id]}>
- <EditableView
- positions={positions.length > 0 ? positions : undefined}
- search={StrCast(this.props.Document._searchString) ? StrCast(this.props.Document._searchString) : undefined}
- editing={this._isEditing}
- isEditingCallback={this.isEditingCallback}
- display={"inline"}
- contents={contents ? contents : type === "number" ? "0" : "undefined"}
- highlight={positions.length > 0 ? true : undefined}
- //contents={StrCast(contents)}
- height={"auto"}
- maxHeight={Number(MAX_ROW_HEIGHT)}
- placeholder={"enter value"}
- bing={() => {
- const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey]));
- if (cfield !== undefined) {
- console.log(typeof (cfield));
- // if (typeof(cfield)===RichTextField)
- const a = cfield as RichTextField;
- if (a.Text !== undefined) {
- return (a.Text);
- }
- else if (StrCast(cfield)) {
- return StrCast(cfield);
- }
- else {
- return String(NumCast(cfield));
- }
- }
- }}
- GetValue={() => {
- if (type === "number" && (contents === 0 || contents === "0")) {
- return "0";
- } else {
+ <div className="collectionSchemaView-cellContents"
+ ref={type === undefined || type === "document" ? this.dropRef : null}>
+ {!search ?
+ <EditableView
+ positions={positions.length > 0 ? positions : undefined}
+ search={Cast(this.props.Document._searchString, "string", null)}
+ editing={this._isEditing}
+ isEditingCallback={this.isEditingCallback}
+ display={"inline"}
+ contents={contents}
+ highlight={positions.length > 0 ? true : undefined}
+ //contents={StrCast(contents)}
+ height={"auto"}
+ maxHeight={Number(MAX_ROW_HEIGHT)}
+ placeholder={placeholder}
+ bing={() => {
const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey]));
- if (type === "number") {
- return StrCast(cfield);
+ if (cfield !== undefined) {
+ // if (typeof(cfield)===RichTextField)
+ const a = cfield as RichTextField;
+ const b = cfield as DateField;
+ console.log(b);
+ if (a.Text !== undefined) {
+ return (a.Text);
+ }
+ else if (b.toString() !== undefined) {
+ return b.toString();
+ }
+ else if (StrCast(cfield)) {
+ return StrCast(cfield);
+ }
+ else {
+ return String(NumCast(cfield));
+ }
}
- const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined;
- const cfinalScript = cscript?.split("return")[cscript.split("return").length - 1];
- const val = cscript !== undefined ? (cfinalScript?.endsWith(";") ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) :
- Field.IsField(cfield) ? Field.toScriptString(cfield) : "";
- return val;
+ }}
+ GetValue={() => {
+ if (type === "number" && (contents === 0 || contents === "0")) {
+ return "0";
+ } else {
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey]));
+ if (type === "number") {
+ return StrCast(cfield);
+ }
+ const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined;
+ const cfinalScript = cscript?.split("return")[cscript.split("return").length - 1];
+ const val = cscript !== undefined ? (cfinalScript?.endsWith(";") ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) :
+ Field.IsField(cfield) ? Field.toScriptString(cfield) : "";
+ return val;
- }
+ }
- }}
- SetValue={action((value: string) => {
- let retVal = false;
+ }}
+ SetValue={action((value: string) => {
+ let retVal = false;
- if (value.startsWith(":=")) {
- retVal = this.props.setComputed(value.substring(2), props.Document, this.props.rowProps.column.id!, this.props.row, this.props.col);
- } else {
+ if (value.startsWith(":=") || value.startsWith("=:=")) {
+ const script = value.substring(value.startsWith("=:=") ? 3 : 2);
+ retVal = this.props.setComputed(script, value.startsWith(":=") ? Doc.GetProto(props.Document) : props.Document, this.props.rowProps.column.id!, this.props.row, this.props.col);
+ } else {
+ const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
+ if (script.compiled) {
+ retVal = this.applyToDoc(props.Document, this.props.row, this.props.col, script.run);
+ }
+
+ }
+ if (retVal) {
+ this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing'
+ this.props.setIsEditing(false);
+ }
+ return retVal;
+
+ //return true;
+ })}
+ OnFillDown={async (value: string) => {
const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
if (script.compiled) {
- retVal = this.applyToDoc(props.Document, this.props.row, this.props.col, script.run);
+ DocListCast(this.props.Document[this.props.fieldKey]).
+ forEach((doc, i) => value.startsWith(":=") ?
+ this.props.setComputed(value.substring(2), doc, this.props.rowProps.column.id!, i, this.props.col) :
+ this.applyToDoc(doc, i, this.props.col, script.run));
}
-
+ }}
+ />
+ :
+ this.returnHighlights(() => {
+ const dateCheck: Date | undefined = this.props.rowProps.original[this.props.rowProps.column.id as string] instanceof DateField ? DateCast(this.props.rowProps.original[this.props.rowProps.column.id as string]).date : undefined;
+ if (dateCheck !== undefined) {
+ cfield = dateCheck.toLocaleString();
}
- if (retVal) {
- this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing'
- this.props.setIsEditing(false);
+ if (props.fieldKey === "context") {
+ cfield = this.contents;
}
- return retVal;
+ if (props.fieldKey === "*lastModified") {
+ if (FieldValue(props.Document["data-lastModified"]) !== undefined) {
+ const d = ComputedField.WithoutComputed(() => FieldValue(props.Document["data-lastModified"])) as DateField;
+ cfield = d.date.toLocaleString();
+ }
- //return true;
- })}
- OnFillDown={async (value: string) => {
- const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
- if (script.compiled) {
- DocListCast(this.props.Document[this.props.fieldKey]).
- forEach((doc, i) => value.startsWith(":=") ?
- this.props.setComputed(value.substring(2), doc, this.props.rowProps.column.id!, i, this.props.col) :
- this.applyToDoc(doc, i, this.props.col, script.run));
+ else if (FieldValue(props.Document["text-lastModified"]) !== undefined) {
+ const d = ComputedField.WithoutComputed(() => FieldValue(props.Document["text-lastModified"])) as DateField;
+ cfield = d.date.toLocaleString();
+ }
}
- }}
- />
-
-
+ return Field.toString(cfield as Field);
+ }, positions)
+ }
</div >
{/* {fieldIsDoc ? docExpander : null} */}
</div>
@@ -371,30 +438,22 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
);
}
- render() {
- return this.renderCellWithType(undefined);
- }
+ render() { return this.renderCellWithType(undefined); }
}
@observer
export class CollectionSchemaNumberCell extends CollectionSchemaCell {
- render() {
- return this.renderCellWithType("number");
- }
+ render() { return this.renderCellWithType("number"); }
}
@observer
export class CollectionSchemaBooleanCell extends CollectionSchemaCell {
- render() {
- return this.renderCellWithType("boolean");
- }
+ render() { return this.renderCellWithType("boolean"); }
}
@observer
export class CollectionSchemaStringCell extends CollectionSchemaCell {
- render() {
- return this.renderCellWithType("string");
- }
+ render() { return this.renderCellWithType("string"); }
}
@observer
@@ -548,7 +607,7 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
if (typeof this._field === "object" && this._doc && this._docTitle) {
return (
<div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1}
- onPointerDown={(e) => { this.onDown(e); }}
+ onPointerDown={this.onDown}
onPointerEnter={(e) => { this.showPreview(true, e); }}
onPointerLeave={(e) => { this.showPreview(false, e); }}
>
@@ -720,29 +779,11 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
@observable private _selectedNum = 0;
@action
- toggleOpened(open: boolean) {
- this._opened = open;
- }
-
- // @action
- // onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
- // this._text = e.target.value;
-
- // // change if its a document
- // this._optionsList[this._selectedNum] = this._text;
- // }
-
- @action
onSetValue = (value: string) => {
-
-
- this._text = value;
-
// change if its a document
- this._optionsList[this._selectedNum] = this._text;
+ this._optionsList[this._selectedNum] = this._text = value;
(this.prop.Document[this.prop.fieldKey] as List<any>).splice(this._selectedNum, 1, value);
-
}
@action
@@ -756,55 +797,34 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
}
-
render() {
-
- const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
-
let type = "list";
-
let link = false;
- let doc = false;
const reference = React.createRef<HTMLDivElement>();
if (typeof this._field === "object" && this._optionsList[0]) {
-
- const options = this._optionsList.map((element, index) => {
-
- if (element instanceof Doc) {
- doc = true;
- type = "document";
- if (this.prop.fieldKey.toLowerCase() === "links") {
- link = true;
- type = "link";
+ const options = !this._opened ? (null) : <div>
+ {this._optionsList.map((element, index) => {
+ let title = "";
+ if (element instanceof Doc) {
+ type = "document";
+ if (this.prop.fieldKey.toLowerCase() === "links") {
+ link = true;
+ type = "link";
+ }
+ title = StrCast(element.title);
}
- const document = FieldValue(Cast(element, Doc));
- const title = element.title;
- return <div
- className="collectionSchemaView-dropdownOption"
- onPointerDown={(e) => { this.onSelected(StrCast(element.title), index); }}
- style={{ padding: "6px" }}>
+ return <div className="collectionSchemaView-dropdownOption" style={{ padding: "6px" }}
+ onPointerDown={(e) => this.onSelected(StrCast(element), index)} >
+ {element}
{title}
</div>;
-
- } else {
- return <div
- className="collectionSchemaView-dropdownOption"
- onPointerDown={(e) => { this.onSelected(StrCast(element), index); }}
- style={{ padding: "6px" }}>{element}</div>;
- }
- });
+ })}
+ </div>;
const plainText = <div style={{ padding: "5.9px" }}>{this._text}</div>;
- // const textarea = <textarea onChange={this.onChange} value={this._text}
- // onFocus={doc ? this.onFocus : undefined}
- // onBlur={doc ? e => this._overlayDisposer?.() : undefined}
- // style={{ resize: "none" }}
- // placeholder={"select an item"}></textarea>;
-
- const textarea = <div className="collectionSchemaView-cellContents"
- style={{ padding: "5.9px" }}
- ref={type === undefined || type === "document" ? this.dropRef : null} key={this.prop.Document[Id]}>
+ const textarea = <div className="collectionSchemaView-cellContents" key={this.prop.Document[Id]} style={{ padding: "5.9px" }}
+ ref={type === undefined || type === "document" ? this.dropRef : null} >
<EditableView
editing={this._isEditing}
isEditingCallback={this.isEditingCallback}
@@ -812,11 +832,8 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
contents={this._text}
height={"auto"}
maxHeight={Number(MAX_ROW_HEIGHT)}
- GetValue={() => {
- return this._text;
- }}
+ GetValue={() => this._text}
SetValue={action((value: string) => {
-
// add special for params
this.onSetValue(value);
return true;
@@ -825,37 +842,26 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
</div >;
//☰
-
- const dropdown = <div>
- {options}
- </div>;
-
return (
<div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
<div className="collectionSchemaView-cellContents" key={this._document[Id]} ref={reference}>
<div className="collectionSchemaView-dropDownWrapper">
- <button type="button" className="collectionSchemaView-dropdownButton" onClick={(e) => { this.toggleOpened(!this._opened); }}
- style={{ right: "length", position: "relative" }}>
- {this._opened ? <FontAwesomeIcon icon="caret-up" size="lg" ></FontAwesomeIcon>
- : <FontAwesomeIcon icon="caret-down" size="lg" ></FontAwesomeIcon>}
+ <button type="button" className="collectionSchemaView-dropdownButton" style={{ right: "length", position: "relative" }}
+ onClick={action(e => this._opened = !this._opened)} >
+ <FontAwesomeIcon icon={this._opened ? "caret-up" : "caret-down"} size="lg" />
</button>
<div className="collectionSchemaView-dropdownText"> {link ? plainText : textarea} </div>
</div>
-
- {this._opened ? dropdown : null}
+ {options}
</div >
</div>
);
- } else {
- return this.renderCellWithType("list");
}
+ return this.renderCellWithType("list");
}
}
-
-
-
@observer
export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
@observable private _isChecked: boolean = typeof this.props.rowProps.original[this.props.rowProps.column.id as string] === "boolean" ? BoolCast(this.props.rowProps.original[this.props.rowProps.column.id as string]) : false;
@@ -864,15 +870,13 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
toggleChecked = (e: React.ChangeEvent<HTMLInputElement>) => {
this._isChecked = e.target.checked;
const script = CompileScript(e.target.checked.toString(), { requiredType: "boolean", addReturn: true, params: { this: Doc.name } });
- if (script.compiled) {
- this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
- }
+ script.compiled && this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
}
render() {
const reference = React.createRef<HTMLDivElement>();
const onItemDown = (e: React.PointerEvent) => {
- (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined :
+ (!this.props.CollectionView?.props.isSelected() ? undefined :
SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e));
};
return (
@@ -888,65 +892,28 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
@observer
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 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));
// };
- const doc = this.props.rowProps.original;
- let buttons: JSX.Element | undefined = undefined;
- buttons = <div style={{
- paddingTop: 8,
- paddingLeft: 3,
- }}><button onClick={() => {
- doc.searchMatch = false;
- setTimeout(() => doc.searchMatch = true, 0);
- doc.searchIndex = NumCast(doc.searchIndex);
- }} style={{ padding: 2, left: 77 }}>
- <FontAwesomeIcon icon="arrow-up" size="sm" />
- </button>
- <button onClick={() => {
- {
- doc.searchMatchAlt = false;
- setTimeout(() => doc.searchMatchAlt = true, 0);
- doc.searchIndex = NumCast(doc.searchIndex);
- }
- }} style={{ padding: 2 }}>
- <FontAwesomeIcon icon="arrow-down" size="sm" />
- </button></div>;
- const type = StrCast(doc.type);
- if (type === "pdf") {
- buttons = <div><button
- style={{
- position: "relative",
- height: 30,
- width: 28,
- left: 1,
- }}
-
- onClick={() => {
- doc.searchMatch = false;
- setTimeout(() => doc.searchMatch = true, 0);
- doc.searchIndex = NumCast(doc.searchIndex);
- }}>
- <FontAwesomeIcon icon="arrow-down" size="sm" />
- </button></div >;
- }
- else if (type !== "rtf") {
- buttons = undefined;
- }
-
- if (BoolCast(this.props.Document._searchDoc) === true) {
-
- }
- else {
- buttons = undefined;
- }
- return (
- <div> {buttons}</div>
- );
- }
-}
-
+ 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> :
+ <></>;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index e65adcf76..34a8bfa24 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -1,67 +1,23 @@
import React = require("react");
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import "./CollectionSchemaView.scss";
-import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes, faImage, faListUl, faCalendar } from '@fortawesome/free-solid-svg-icons';
-import { library, IconProp } from "@fortawesome/fontawesome-svg-core";
+import { IconProp, library } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { ColumnType } from "./CollectionSchemaView";
-import { faFile } from "@fortawesome/free-regular-svg-icons";
-import { SchemaHeaderField, PastelSchemaPalette } from "../../../fields/SchemaHeaderField";
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, DocListCast, Opt } from "../../../fields/Doc";
+import { listSpec } from "../../../fields/Schema";
+import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+import { ScriptField } from "../../../fields/ScriptField";
+import { Cast, StrCast } from "../../../fields/Types";
import { undoBatch } from "../../util/UndoManager";
-import { Doc } from "../../../fields/Doc";
-import { StrCast } from "../../../fields/Types";
+import { SearchBox } from "../search/SearchBox";
+import { ColumnType } from "./CollectionSchemaView";
+import "./CollectionSchemaView.scss";
+import { CollectionView } from "./CollectionView";
+
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes, faImage, faListUl, faCalendar);
-
-export interface HeaderProps {
- keyValue: SchemaHeaderField;
- possibleKeys: string[];
- existingKeys: string[];
- keyType: ColumnType;
- typeConst: boolean;
- onSelect: (oldKey: string, newKey: string, addnew: boolean) => void;
- setIsEditing: (isEditing: boolean) => void;
- deleteColumn: (column: string) => void;
- setColumnType: (column: SchemaHeaderField, type: ColumnType) => void;
- setColumnSort: (column: SchemaHeaderField, desc: boolean | undefined) => void;
- setColumnColor: (column: SchemaHeaderField, color: string) => void;
-
-}
-
-export class CollectionSchemaHeader extends React.Component<HeaderProps> {
- render() {
- const icon: IconProp = this.props.keyType === ColumnType.Number ? "hashtag" : this.props.keyType === ColumnType.String ? "font" :
- this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "file" :
- this.props.keyType === ColumnType.Image ? "image" : this.props.keyType === ColumnType.List ? "list-ul" : this.props.keyType === ColumnType.Date ? "calendar" :
- "align-justify";
- return (
- <div className="collectionSchemaView-header" style={{ background: this.props.keyValue.color }}>
- <CollectionSchemaColumnMenu
- columnField={this.props.keyValue}
- // keyValue={this.props.keyValue.heading}
- possibleKeys={this.props.possibleKeys}
- existingKeys={this.props.existingKeys}
- // keyType={this.props.keyType}
- typeConst={this.props.typeConst}
- menuButtonContent={<div><FontAwesomeIcon icon={icon} size="sm" />{this.props.keyValue.heading}</div>}
- addNew={false}
- onSelect={this.props.onSelect}
- setIsEditing={this.props.setIsEditing}
- deleteColumn={this.props.deleteColumn}
- onlyShowOptions={false}
- setColumnType={this.props.setColumnType}
- setColumnSort={this.props.setColumnSort}
- setColumnColor={this.props.setColumnColor}
- />
- </div>
- );
- }
-}
-
export interface AddColumnHeaderProps {
createColumn: () => void;
@@ -77,7 +33,6 @@ export class CollectionSchemaAddColumnHeader extends React.Component<AddColumnHe
}
-
export interface ColumnMenuProps {
columnField: SchemaHeaderField;
// keyValue: string;
@@ -101,37 +56,29 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
@observable private _isOpen: boolean = false;
@observable private _node: HTMLDivElement | null = null;
- componentDidMount() {
- document.addEventListener("pointerdown", this.detectClick);
- }
+ componentDidMount() { document.addEventListener("pointerdown", this.detectClick); }
- componentWillUnmount() {
- document.removeEventListener("pointerdown", this.detectClick);
- }
+ componentWillUnmount() { document.removeEventListener("pointerdown", this.detectClick); }
- detectClick = (e: PointerEvent): void => {
- if (this._node && this._node.contains(e.target as Node)) {
- } else {
- this._isOpen = false;
- this.props.setIsEditing(false);
- }
+ @action
+ detectClick = (e: PointerEvent) => {
+ !this._node?.contains(e.target as Node) && this.props.setIsEditing(this._isOpen = false);
}
@action
toggleIsOpen = (): void => {
- this._isOpen = !this._isOpen;
- this.props.setIsEditing(this._isOpen);
+ this.props.setIsEditing(this._isOpen = !this._isOpen);
}
- changeColumnType = (type: ColumnType): void => {
+ changeColumnType = (type: ColumnType) => {
this.props.setColumnType(this.props.columnField, type);
}
- changeColumnSort = (desc: boolean | undefined): void => {
+ changeColumnSort = (desc: boolean | undefined) => {
this.props.setColumnSort(this.props.columnField, desc);
}
- changeColumnColor = (color: string): void => {
+ changeColumnColor = (color: string) => {
this.props.setColumnColor(this.props.columnField, color);
}
@@ -143,7 +90,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
}
renderTypes = () => {
- if (this.props.typeConst) return <></>;
+ if (this.props.typeConst) return (null);
const type = this.props.columnField.type;
return (
@@ -238,18 +185,6 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
renderContent = () => {
return (
<div className="collectionSchema-header-menuOptions">
- <div className="collectionSchema-headerMenu-group">
- <label>Key:</label>
- <KeysDropdown
- keyValue={this.props.columnField.heading}
- possibleKeys={this.props.possibleKeys}
- existingKeys={this.props.existingKeys}
- canAddNew={true}
- addNew={this.props.addNew}
- onSelect={this.props.onSelect}
- setIsEditing={this.props.setIsEditing}
- />
- </div>
{this.props.onlyShowOptions ? <></> :
<>
{this.renderTypes()}
@@ -286,13 +221,22 @@ export interface KeysDropdownProps {
setIsEditing: (isEditing: boolean) => void;
width?: string;
docs?: Doc[];
+ Document: Doc;
+ dataDoc: Doc | undefined;
+ fieldKey: string;
+ ContainingCollectionDoc: Doc | undefined;
+ ContainingCollectionView: Opt<CollectionView>;
+ active?: (outsideReaction?: boolean) => boolean;
+ openHeader: (column: any, screenx: number, screeny: number) => void;
+ col: SchemaHeaderField;
+ icon: IconProp;
}
@observer
export class KeysDropdown extends React.Component<KeysDropdownProps> {
@observable private _key: string = this.props.keyValue;
@observable private _searchTerm: string = this.props.keyValue;
@observable private _isOpen: boolean = false;
- @observable private _canClose: boolean = true;
+ @observable private _node: HTMLDivElement | null = null;
@observable private _inputRef: React.RefObject<HTMLInputElement> = React.createRef();
@action setSearchTerm = (value: string): void => { this._searchTerm = value; };
@@ -301,34 +245,73 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
@action
onSelect = (key: string): void => {
- if (key.slice(0, this._key.length) === this._key && this._key !== key) {
- const filter = key.slice(this._key.length - key.length);
- this.props.onSelect(this._key, this._key, this.props.addNew, filter);
+ this.props.onSelect(this._key, key, this.props.addNew);
+ this.setKey(key);
+ this._isOpen = false;
+ this.props.setIsEditing(false);
+ }
+
+ @action
+ setNode = (node: HTMLDivElement): void => {
+ if (node) {
+ this._node = node;
}
- else {
- this.props.onSelect(this._key, key, this.props.addNew);
- this.setKey(key);
+ }
+
+ componentDidMount() {
+ document.addEventListener("pointerdown", this.detectClick);
+ }
+
+ @action
+ detectClick = (e: PointerEvent): void => {
+ if (this._node && this._node.contains(e.target as Node)) {
+ } else {
this._isOpen = false;
this.props.setIsEditing(false);
}
}
- @action
- onSelect2 = (key: string): void => {
- this._searchTerm = this._searchTerm.slice(0, this._key.length) + key;
- this._isOpen = false;
-
+ componentWillMount() {
+ document.removeEventListener("pointerdown", this.detectClick);
+ const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+ if (filters?.includes(this._key)) {
+ runInAction(() => this.closeResultsVisibility = "contents");
+ }
}
+ private tempfilter: string = "";
@undoBatch
onKeyDown = (e: React.KeyboardEvent): void => {
if (e.key === "Enter") {
- const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- if (keyOptions.length) {
- this.onSelect(keyOptions[0]);
- } else if (this._searchTerm !== "" && this.props.canAddNew) {
- this.setSearchTerm(this._searchTerm || this._key);
- this.onSelect(this._searchTerm);
+ if (this._searchTerm.includes(":")) {
+ 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");
+ this.props.col.setColor("green");
+ this.closeResultsVisibility = "contents";
+ }
+ }
+ else {
+ 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));
+ if (keyOptions.length) {
+ this.onSelect(keyOptions[0]);
+ } else if (this._searchTerm !== "" && this.props.canAddNew) {
+ this.setSearchTerm(this._searchTerm || this._key);
+ this.onSelect(this._searchTerm);
+ }
}
}
}
@@ -344,103 +327,215 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
}
@action
- onBlur = (e: React.FocusEvent): void => {
- if (this._canClose) {
- this._isOpen = false;
- this.props.setIsEditing(false);
- }
- }
-
- @action
- onPointerEnter = (e: React.PointerEvent): void => {
- this._canClose = false;
- }
-
- @action
- onPointerOut = (e: React.PointerEvent): void => {
- this._canClose = true;
- }
-
renderOptions = (): JSX.Element[] | JSX.Element => {
- if (!this._isOpen) return <></>;
-
+ if (!this._isOpen) {
+ this.defaultMenuHeight = 0;
+ return <></>;
+ }
const searchTerm = this._searchTerm.trim() === "New field" ? "" : this._searchTerm;
- const keyOptions = searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+ let keyOptions = searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
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 options = keyOptions.map(key => {
return <div key={key} className="key-option" style={{
border: "1px solid lightgray",
- width: this.props.width, maxWidth: this.props.width, overflowX: "hidden"
+ width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
}}
- onPointerDown={e => e.stopPropagation()} onClick={() => { this.onSelect(key); this.setSearchTerm(""); }}>{key}</div>;
+ onPointerDown={e => {
+ e.stopPropagation();
+ }}
+ onClick={() => {
+ this.onSelect(key);
+ this.setSearchTerm("");
+ }}>{key}</div>;
});
// if search term does not already exist as a group type, give option to create new group type
+
if (this._key !== this._searchTerm.slice(0, this._key.length)) {
+ console.log("little further");
if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) {
options.push(<div key={""} className="key-option" style={{
- border: "1px solid lightgray",
- width: this.props.width, maxWidth: this.props.width, overflowX: "hidden"
+ border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
}}
onClick={() => { this.onSelect(this._searchTerm); this.setSearchTerm(""); }}>
Create "{this._searchTerm}" key</div>);
}
}
+ if (options.length === 0) {
+ this.defaultMenuHeight = 0;
+ }
+ else {
+ if (this.props.docs) {
+ const panesize = this.props.docs.length * 30;
+ options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
+ }
+ else {
+ options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8;
+ }
+ }
return options;
}
+ docSafe: Doc[] = [];
+
+ @action
renderFilterOptions = (): JSX.Element[] | JSX.Element => {
- if (!this._isOpen) return <></>;
+ if (!this._isOpen) {
+ this.defaultMenuHeight = 0;
+ return <></>;
+ }
const keyOptions: string[] = [];
- const temp = this._searchTerm.slice(this._key.length);
- this.props.docs?.forEach((doc) => {
+ const colpos = this._searchTerm.indexOf(":");
+ const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
+ if (this.docSafe.length === 0) {
+ this.docSafe = DocListCast(this.props.dataDoc![this.props.fieldKey]);
+ }
+ const docs = this.docSafe;
+ docs.forEach((doc) => {
const key = StrCast(doc[this._key]);
- if (keyOptions.includes(key) === false && key.includes(temp)) {
+ if (keyOptions.includes(key) === false && key.includes(temp) && key !== "") {
keyOptions.push(key);
}
});
-
+ const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+ if (filters === undefined || filters.length === 0 || filters.includes(this._key) === false) {
+ this.props.col.setColor("rgb(241, 239, 235)");
+ this.closeResultsVisibility = "none";
+ }
+ for (let i = 0; i < (filters?.length ?? 0) - 1; i += 3) {
+ if (filters![i] === this.props.col.heading && keyOptions.includes(filters![i + 1]) === false) {
+ keyOptions.push(filters![i + 1]);
+ }
+ }
const options = keyOptions.map(key => {
+ //Doc.setDocFilter(this.props.Document!, this._key, key, undefined);
+ let bool = false;
+ console.log(filters);
+ if (filters !== undefined) {
+ bool = filters.includes(key) && filters[filters.indexOf(key) + 1] === "check";
+ console.log(filters.includes(key));
+ }
return <div key={key} className="key-option" style={{
- border: "1px solid lightgray",
- width: this.props.width, maxWidth: this.props.width, overflowX: "hidden"
+ border: "1px solid lightgray", paddingLeft: 5, textAlign: "left",
+ width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", backgroundColor: "white",
}}
- onPointerDown={e => e.stopPropagation()} onClick={() => { this.onSelect2(key); }}>{key}</div>;
+ >
+ <input type="checkbox"
+ onPointerDown={e => e.stopPropagation()}
+ onClick={e => e.stopPropagation()}
+ onChange={(e) => {
+ e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, undefined);
+ e.target.checked === true ? this.closeResultsVisibility = "contents" : console.log("");
+ e.target.checked === true ? this.props.col.setColor("green") : this.updateFilter();
+ e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, undefined);
+ }}
+ checked={bool}
+ />
+ <span style={{ paddingLeft: 4 }}>
+ {key}
+ </span>
+
+ </div>;
});
+ if (options.length === 0) {
+ this.defaultMenuHeight = 0;
+ }
+ else {
+ if (this.props.docs) {
+ const panesize = this.props.docs.length * 30;
+ options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
+ }
+ else {
+ options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8;
+ }
+ }
return options;
}
+ @observable defaultMenuHeight = 0;
+
+
+ updateFilter() {
+ const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+ if (filters === undefined || filters.length === 0 || filters.includes(this._key) === false) {
+ console.log("PLEASE");
+ this.props.col.setColor("rgb(241, 239, 235)");
+ this.closeResultsVisibility = "none";
+ }
+ }
+
+
+ get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; }
+
+ @computed get scriptField() {
+ const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
+ const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
+ return script ? () => script : undefined;
+ }
+ filterBackground = () => "rgba(105, 105, 105, 0.432)";
+ @observable filterOpen: boolean | undefined = undefined;
+ closeResultsVisibility: string = "none";
+
+ removeFilters = (e: React.PointerEvent): void => {
+ const keyOptions: string[] = [];
+ if (this.docSafe.length === 0) {
+ this.docSafe = DocListCast(this.props.dataDoc![this.props.fieldKey]);
+ }
+ const docs = this.docSafe;
+ docs.forEach((doc) => {
+ const key = StrCast(doc[this._key]);
+ if (keyOptions.includes(key) === false) {
+ keyOptions.push(key);
+ }
+ });
+ keyOptions.forEach(key => {
+ Doc.setDocFilter(this.props.Document, this._key, key, undefined);
+ }
+ );
+ Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
+ this.props.col.setColor("rgb(241, 239, 235)");
+ this.closeResultsVisibility = "none";
+ }
render() {
return (
- <div className="keys-dropdown" style={{ zIndex: 10, width: this.props.width, maxWidth: this.props.width }}>
- {this._key === this._searchTerm.slice(0, this._key.length) ?
- <div style={{ position: "absolute", marginLeft: "4px", marginTop: "3", color: "grey", pointerEvents: "none", lineHeight: 1.15 }}>
- {this._key}
+ <div style={{ display: "flex" }} ref={this.setNode}>
+ <FontAwesomeIcon onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); e.stopPropagation(); }} icon={this.props.icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} />
+
+ {/* <FontAwesomeIcon icon={fa.faSearchMinus} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} onClick={e => {
+ runInAction(() => { this._isOpen === undefined ? this._isOpen = true : this._isOpen = !this._isOpen })
+ }} /> */}
+
+ <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>
+ <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" }} />
</div>
- : undefined}
- <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} onBlur={this.onBlur}></input>
- <div className="keys-options-wrapper" style={{
- backgroundColor: "white",
- width: this.props.width, maxWidth: this.props.width,
- }}
- onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerOut}>
- {this._key === this._searchTerm.slice(0, this._key.length) ?
- this.renderFilterOptions() : this.renderOptions()}
- </div>
- </div >
+ {!this._isOpen ? (null) : <div className="keys-options-wrapper" style={{
+ width: this.props.width, maxWidth: this.props.width, height: "auto",
+ }}>
+ {this._searchTerm.includes(":") ? this.renderFilterOptions() : this.renderOptions()}
+ </div>}
+ </div >
+ </div>
);
}
}
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index dade4f2f2..4754adc90 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -1,19 +1,19 @@
import React = require("react");
-import { ReactTableDefaults, TableCellRenderer, RowInfo } from "react-table";
-import "./CollectionSchemaView.scss";
-import { Transform } from "../../util/Transform";
-import { Doc } from "../../../fields/Doc";
-import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager";
-import { Cast, FieldValue, StrCast } from "../../../fields/Types";
-import { ContextMenu } from "../ContextMenu";
-import { action } from "mobx";
import { library } from '@fortawesome/fontawesome-svg-core';
import { faGripVertical, faTrash } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { DocumentManager } from "../../util/DocumentManager";
+import { action } from "mobx";
+import { ReactTableDefaults, RowInfo, TableCellRenderer } from "react-table";
+import { Doc } from "../../../fields/Doc";
import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
-import { undoBatch } from "../../util/UndoManager";
+import { Cast, FieldValue, StrCast } from "../../../fields/Types";
+import { DocumentManager } from "../../util/DocumentManager";
+import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
import { SnappingManager } from "../../util/SnappingManager";
+import { Transform } from "../../util/Transform";
+import { undoBatch } from "../../util/UndoManager";
+import { ContextMenu } from "../ContextMenu";
+import "./CollectionSchemaView.scss";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
library.add(faGripVertical, faTrash);
@@ -40,7 +40,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
onPointerLeave = (e: React.PointerEvent): void => {
this._header!.current!.className = "collectionSchema-col-wrapper";
document.removeEventListener("pointermove", this.onDragMove, true);
- document.removeEventListener("pointermove", this.onPointerMove);
+ !e.buttons && document.removeEventListener("pointermove", this.onPointerMove);
}
onDragMove = (e: PointerEvent): void => {
const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
@@ -68,6 +68,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
const before = x[0] < bounds[0];
const colDragData = de.complete.columnDragData;
if (colDragData) {
+ e.stopPropagation();
this.props.reorderColumns(colDragData.colKey, this.props.columnValue, before, this.props.allColumns);
return true;
}
@@ -108,8 +109,10 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => {
this._dragRef = ref;
const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
- this._startDragPosition = { x: dx, y: dy };
- document.addEventListener("pointermove", this.onPointerMove);
+ if (!(e.target as any)?.tagName.includes("INPUT")) {
+ this._startDragPosition = { x: dx, y: dy };
+ document.addEventListener("pointermove", this.onPointerMove);
+ }
}
@@ -164,6 +167,10 @@ export class MovableRow extends React.Component<MovableRowProps> {
if (!before) this._header!.current!.className += " row-below";
e.stopPropagation();
}
+ componentWillUnmount() {
+
+ this._rowDropDisposer?.();
+ }
createRowDropTarget = (ele: HTMLDivElement) => {
this._rowDropDisposer?.();
@@ -219,13 +226,15 @@ export class MovableRow extends React.Component<MovableRowProps> {
render() {
const { children = null, rowInfo } = this.props;
+
if (!rowInfo) {
return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>;
}
const { original } = rowInfo;
const doc = FieldValue(Cast(original, Doc));
- if (!doc) return <></>;
+
+ if (!doc) return (null);
const reference = React.createRef<HTMLDivElement>();
const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType);
@@ -238,11 +247,11 @@ export class MovableRow extends React.Component<MovableRowProps> {
<div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}>
<div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
<ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown} >
- {/* <div className="row-dragger">
- <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
- <div className="row-option" style={{ cursor: "grab" }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
- <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, "onRight")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
- </div> */}
+ <div className="row-dragger">
+ <div className="row-option" style={{ left: 5 }} onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
+ <div className="row-option" style={{ cursor: "grab", left: 25 }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
+ <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "onRight")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
+ </div>
{children}
</ReactTableDefaults.TrComponent>
</div>
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index ba0a259c5..fc5dffaec 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -43,6 +43,49 @@
// }
}
+.collectionSchemaView-searchContainer {
+ border-width: $COLLECTION_BORDER_WIDTH;
+ border-color: $intermediate-color;
+ border-style: solid;
+ border-radius: $border-radius;
+ box-sizing: border-box;
+ position: relative;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ margin-top: 0;
+ transition: top 0.5s;
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: nowrap;
+ touch-action: none;
+
+ div {
+ touch-action: none;
+ }
+
+
+ .collectionSchemaView-tableContainer {
+ width: 100%;
+ height: 100%;
+ }
+
+ .collectionSchemaView-dividerDragger {
+ position: relative;
+ height: 100%;
+ width: 20px;
+ z-index: 20;
+ right: 0;
+ top: 0;
+ background: gray;
+ cursor: col-resize;
+ }
+
+ // .documentView-node:first-child {
+ // background: $light-color;
+ // }
+}
+
.ReactTable {
width: 100%;
background: white;
@@ -165,10 +208,11 @@
.collectionSchema-header-menu {
- height: 100%;
+ height: auto;
z-index: 100;
position: absolute;
- background:white;
+ background: white;
+ padding: 5px;
.collectionSchema-header-toggler {
z-index: 100;
@@ -200,12 +244,13 @@ button.add-column {
.collectionSchema-header-menuOptions {
color: black;
- width: 200px;
+ width: 180px;
text-align: left;
.collectionSchema-headerMenu-group {
padding: 7px 0;
border-bottom: 1px solid lightgray;
+ cursor: pointer;
&:first-child {
padding-top: 0;
@@ -280,7 +325,6 @@ button.add-column {
background-color: white;
.key-option {
- //background-color: $light-color;
background-color: white;
border: 1px solid lightgray;
padding: 2px 3px;
@@ -315,12 +359,53 @@ button.add-column {
}
}
+.altcollectionTimeView-treeView {
+ display: flex;
+ flex-direction: column;
+ width: 175px;
+ height: auto;
+ position: fixed;
+ border-left: solid 1px;
+ z-index: 1;
+
+ .collectionTimeView-addfacet {
+ display: inline-block;
+ width: 200px;
+ height: 30px;
+ background: darkGray;
+ text-align: left;
+
+ .collectionTimeView-button {
+ align-items: center;
+ display: flex;
+ width: 100%;
+ height: 100%;
+
+ .collectionTimeView-span {
+ margin: auto;
+ }
+ }
+
+ >div,
+ >div>div {
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ .altcollectionTimeView-tree {
+ display: inline-block;
+ width: 100%;
+ height: calc(100% - 30px);
+ }
+}
+
.collectionSchema-row {
height: 100%;
background-color: white;
&.row-focused .rt-td {
- background-color: rgb(255, 246, 246); //$light-color-secondary;
+ background-color: #bfffc0; //$light-color-secondary;
}
&.row-wrapped {
@@ -333,10 +418,11 @@ button.add-column {
display: flex;
justify-content: space-around;
flex: 50 0 auto;
- width: 50px;
+ width: 0;
max-width: 50px;
height: 100%;
min-height: 30px;
+ align-items: center;
color: lightgray;
background-color: white;
transition: color 0.1s ease;
@@ -344,6 +430,7 @@ button.add-column {
.row-option {
// padding: 5px;
cursor: pointer;
+ position: absolute;
transition: color 0.1s ease;
display: flex;
flex-direction: column;
@@ -383,6 +470,9 @@ button.add-column {
.collectionSchemaView-cellWrapper {
height: 100%;
padding: 4px;
+ text-align: left;
+ padding-left: 19px;
+
position: relative;
&:focus {
@@ -513,19 +603,26 @@ button.add-column {
height: 100%;
}
+.rt-td.rt-expandable {
+ overflow:visible;
+ position: relative;
+ height:100%;
+ z-index: 1;
+}
.reactTable-sub {
- padding: 10px 30px;
background-color: rgb(252, 252, 252);
width: 100%;
+
.rt-thead {
- display:none;
+ display: none;
}
- .collectionSchemaView-table{
+
+ .collectionSchemaView-table {
border: solid 1px;
overflow: hidden;
}
-
+
.row-dragger {
background-color: rgb(252, 252, 252);
@@ -543,13 +640,16 @@ button.add-column {
.collectionSchemaView-expander {
height: 100%;
min-height: 30px;
- position: relative;
+ position: absolute;
color: gray;
+ width: 20;
+ height: auto;
+ left: 55;
svg {
position: absolute;
top: 50%;
- left: 50%;
+ left: 10;
transform: translate(-50%, -50%);
}
}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index a003de0d3..257099783 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -7,11 +7,11 @@ import { observer } from "mobx-react";
import Measure from "react-measure";
import { Resize } from "react-table";
import "react-table/react-table.css";
-import { Doc } from "../../../fields/Doc";
+import { Doc, Opt } from "../../../fields/Doc";
import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
-import { Cast, NumCast } from "../../../fields/Types";
+import { Cast, NumCast, BoolCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents } from "../../../Utils";
import { SnappingManager } from "../../util/SnappingManager";
@@ -20,10 +20,12 @@ import { undoBatch } from "../../util/UndoManager";
import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss';
import '../DocumentDecorations.scss';
import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
-import { KeysDropdown } from "./CollectionSchemaHeaders";
import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
import { SchemaTable } from "./SchemaTable";
+import { SelectionManager } from "../../util/SelectionManager";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
library.add(faCog, faPlus, faSortUp, faSortDown);
library.add(faTable);
@@ -62,8 +64,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@observable _menuWidth = 0;
@observable _headerOpen = false;
- @observable _isOpen = false;
- @observable _node: HTMLDivElement | null = null;
@observable _headerIsEditing = false;
@observable _col: any = "";
@observable _menuHeight = 0;
@@ -71,12 +71,22 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@observable _pointerY = 0;
@observable _openTypes: boolean = false;
@computed get menuCoordinates() {
- const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX));
- const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY));
+ let searchx = 0;
+ let searchy = 0;
+ if (this.props.Document._searchDoc !== undefined) {
+ const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0];
+ if (el !== undefined) {
+ const rect = el.getBoundingClientRect();
+ searchx = rect.x;
+ searchy = rect.y;
+ }
+ }
+ const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx;
+ const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy;
return this.props.ScreenToLocalTransform().transformPoint(x, y);
}
- @observable scale = this.props.ScreenToLocalTransform().Scale;
+ @computed get scale() { return this.props.ScreenToLocalTransform().Scale; }
@computed get columns() {
return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
@@ -101,31 +111,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
@computed get possibleKeys() { return this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); }
-
- componentDidMount() {
- document.addEventListener("pointerdown", this.detectClick);
- }
-
- componentWillUnmount() {
- document.removeEventListener("pointerdown", this.detectClick);
- }
-
@action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing;
- detectClick = (e: PointerEvent): void => {
- if (this._node && this._node.contains(e.target as Node)) {
- } else {
- this._isOpen = false;
- this.setHeaderIsEditing(false);
- this.closeHeader();
- }
- }
-
- @action
- toggleIsOpen = (): void => {
- this._isOpen = !this._isOpen;
- this.setHeaderIsEditing(this._isOpen);
- }
@action
changeColumnType = (type: ColumnType, col: any): void => {
@@ -178,16 +165,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
this.columns = columns;
}
- @action
- setNode = (node: HTMLDivElement): void => {
- node && (this._node = node);
- }
-
- @action
- typesDropdownChange = (bool: boolean) => {
- this._openTypes = bool;
- }
-
renderTypes = (col: any) => {
if (columnTypes.get(col.heading)) return (null);
@@ -251,10 +228,10 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
type === ColumnType.Date ? dateType : imageType;
return (
- <div className="collectionSchema-headerMenu-group">
- <div onClick={() => this.typesDropdownChange(!this._openTypes)}>
- <label>Column type:</label>
- <FontAwesomeIcon icon={"caret-down"} size="lg" style={{ float: "right" }} />
+ <div className="collectionSchema-headerMenu-group" onClick={action(() => this._openTypes = !this._openTypes)}>
+ <div>
+ <label style={{ cursor: "pointer" }}>Column type:</label>
+ <FontAwesomeIcon icon={"caret-down"} size="lg" style={{ float: "right", transform: `rotate(${this._openTypes ? "180deg" : 0})`, transition: "0.2s all ease" }} />
</div>
{this._openTypes ? allColumnTypes : justColType}
</div >
@@ -336,7 +313,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
else {
this.props.Document._docFilters = undefined;
if (this.props.Document.selectedDoc !== undefined) {
- const doc = Cast(this.props.Document.selectedDoc, Doc) as Doc;
+ const doc = Cast(this.props.Document.selectedDoc, Doc, null);
doc._docFilters = undefined;
}
}
@@ -348,7 +325,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@action
openHeader = (col: any, screenx: number, screeny: number) => {
this._col = col;
- this._headerOpen = !this._headerOpen;
+ this._headerOpen = true;
this._pointerX = screenx;
this._pointerY = screeny;
}
@@ -356,17 +333,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@action
closeHeader = () => { this._headerOpen = false; }
- renderKeysDropDown = (col: any) => {
- return <KeysDropdown
- keyValue={col.heading}
- possibleKeys={this.possibleKeys}
- existingKeys={this.columns.map(c => c.heading)}
- canAddNew={true}
- addNew={false}
- onSelect={this.changeColumns}
- setIsEditing={this.setHeaderIsEditing}
- />;
- }
+
@undoBatch
@action
@@ -390,7 +357,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@action
onHeaderClick = (e: React.PointerEvent) => {
- this.props.active(true);
e.stopPropagation();
}
@@ -405,12 +371,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@computed get renderMenuContent() {
TraceMobx();
return <div className="collectionSchema-header-menuOptions">
- <div className="collectionSchema-headerMenu-group">
- <label>Key:</label>
- {this.renderKeysDropDown(this._col)}
- </div>
{this.renderTypes(this._col)}
- {this.renderSorting(this._col)}
+ {/* {this.renderSorting(this._col)} */}
{this.renderColors(this._col)}
<div className="collectionSchema-headerMenu-group">
<button onClick={() => { this.deleteColumn(this._col.heading); }}
@@ -424,11 +386,14 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
super.CreateDropTarget(ele);
}
- isFocused = (doc: Doc): boolean => this.props.isSelected() && doc === this._focusedTable;
+ isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable;
@action setFocused = (doc: Doc) => this._focusedTable = doc;
- @action setPreviewDoc = (doc: Doc) => this.previewDoc = doc;
+ @action setPreviewDoc = (doc: Opt<Doc>) => {
+ SelectionManager.SelectSchemaDoc(this, doc);
+ this.previewDoc = doc;
+ }
//toggles preview side-panel of schema
@action
@@ -533,6 +498,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
documentKeys={this.documentKeys}
headerIsEditing={this._headerIsEditing}
openHeader={this.openHeader}
+ onClick={e => { e.stopPropagation(); this.closeHeader(); }}
onPointerDown={this.onTablePointerDown}
onResizedChange={this.onResizedChange}
setColumns={this.setColumns}
@@ -554,15 +520,29 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
</div>
</div>;
}
+ onSpecificMenu = (e: React.MouseEvent) => {
+ if ((e.target as any)?.className?.includes?.("collectionSchemaView-cell") || (e.target instanceof HTMLSpanElement)) {
+ const cm = ContextMenu.Instance;
+ const options = cm.findByDescription("Options...");
+ const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+ optionItems.push({ description: "remove", event: () => this.previewDoc && this.props.removeDocument(this.previewDoc), icon: "trash" });
+ !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
+ cm.displayMenu(e.clientX, e.clientY);
+ (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this.
+ e.stopPropagation();
+ }
+ }
@action
onTablePointerDown = (e: React.PointerEvent): void => {
+ if (!(e.target as any)?.className?.includes?.("collectionSchemaView-cell") && !(e.target instanceof HTMLSpanElement)) {
+ this.setPreviewDoc(undefined);
+ }
this.setFocused(this.props.Document);
if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey && this.props.isSelected(true)) {
e.stopPropagation();
}
- this._pointerY = e.screenY;
- this._pointerX = e.screenX;
+ // this.closeHeader();
}
onResizedChange = (newResized: Resize[], event: any) => {
@@ -611,14 +591,19 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
}
render() {
+ let name = "collectionSchemaView-container";
+ if (this.props.Document._searchDoc !== undefined) {
+ name = "collectionSchemaView-searchContainer";
+ }
+ if (!this.props.active()) setTimeout(() => this.closeHeader(), 0);
TraceMobx();
const menuContent = this.renderMenuContent;
- const menu = <div className="collectionSchema-header-menu" ref={this.setNode}
+ const menu = <div className="collectionSchema-header-menu"
onWheel={e => this.onZoomMenu(e)}
onPointerDown={e => this.onHeaderClick(e)}
style={{
- position: "fixed", background: "white",
- transform: `translate(${this.menuCoordinates[0] / this.scale}px, ${this.menuCoordinates[1] / this.scale}px)`
+ position: "fixed", background: "white", border: "black 1px solid",
+ transform: `translate(${(this.menuCoordinates[0])}px, ${(this.menuCoordinates[1])}px)`
}}>
<Measure offset onResize={action((r: any) => {
const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height);
@@ -627,15 +612,16 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
{({ measureRef }) => <div ref={measureRef}> {menuContent} </div>}
</Measure>
</div>;
- return <div className="collectionSchemaView-container"
+ return <div className={name}
style={{
- overflow: this.props.overflow === true ? "auto" : undefined,
+ overflow: this.props.overflow === true ? "scroll" : undefined, backgroundColor: "white",
pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined,
- width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
+ width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
}} >
<div className="collectionSchemaView-tableContainer"
- style={{ backgroundColor: "white", width: `calc(100% - ${this.previewWidth()}px)` }}
+ style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
onKeyPress={this.onKeyPress}
+ onContextMenu={this.onSpecificMenu}
onPointerDown={this.onPointerDown}
onWheel={e => this.props.active(true) && e.stopPropagation()}
onDrop={e => this.onExternalDrop(e, {})}
@@ -644,7 +630,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
</div>
{this.dividerDragger}
{!this.previewWidth() ? (null) : this.previewPanel}
- {this._headerOpen ? menu : null}
+ {this._headerOpen && this.props.active() ? menu : null}
</div>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index b27af66ba..241c64f9a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,7 +4,7 @@ import { CursorProperty } from "csstype";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import Switch from 'rc-switch';
-import { DataSym, Doc, HeightSym, WidthSym, DocListCastAsync } from "../../../fields/Doc";
+import { DataSym, Doc, HeightSym, WidthSym } from "../../../fields/Doc";
import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
@@ -28,7 +28,6 @@ import { CollectionViewType } from "./CollectionView";
import { SnappingManager } from "../../util/SnappingManager";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocUtils } from "../../documents/Documents";
-import { MainViewNotifs } from "../MainViewNotifs";
const _global = (window /* browser */ || global /* node */) as any;
type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
@@ -48,7 +47,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
@computed get pivotField() { return StrCast(this.layoutDoc._pivotField); }
@computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout); }
@computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
- @computed get yMargin() { return Math.max(this.layoutDoc._showTitle && !this.layoutDoc._showTitleHover ? 30 : 0, NumCast(this.layoutDoc._yMargin, 0)); } // 2 * this.gridGap)); }
+ @computed get yMargin() { return Math.max(this.layoutDoc._showTitle && !this.layoutDoc._showTitleHover ? 30 : 0, NumCast(this.layoutDoc._yMargin, 5)); } // 2 * this.gridGap)); }
@computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
@computed get isStackingView() { return BoolCast(this.layoutDoc._columnsStack, true); }
@computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
@@ -299,10 +298,6 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
const srcInd = docs.indexOf(doc);
docs.splice(srcInd, 1);
docs.splice((targInd > srcInd ? targInd - 1 : targInd) + plusOne, 0, doc);
- DocListCastAsync(docs).then(resolvedDocs => {
- const pos = resolvedDocs?.findIndex(shareDoc => shareDoc.icon === "users") || 0; // hopefully find out if the sharing doc has been moved
- if (MainViewNotifs.NotifsCol && pos !== -1) MainViewNotifs.NotifsCol.position = pos;
- });
} else if (i < (newDocs.length / 2)) { //glr: for some reason dragged documents are duplicated
if (targInd === -1) targInd = docs.length;
else targInd = docs.indexOf(newDocs[0]) + 1;
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index f193a9787..ede75fba8 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -289,7 +289,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
const heading = this._heading;
const style = this.props.parent;
const singleColumn = style.isStackingView;
- const columnYMargin = this.props.headingObject ? 0 : NumCast(this.props.parent.props.Document._yMargin);
+ const columnYMargin = this.props.headingObject ? 0 : NumCast(this.props.parent.props.Document._yMargin, 5);
const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
const evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
const headerEditableViewProps = {
@@ -310,7 +310,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
const headingView = this.props.headingObject ?
<div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
style={{
- marginTop: NumCast(this.props.parent.props.Document._yMargin),
+ marginTop: NumCast(this.props.parent.props.Document._yMargin, 5),
width: (style.columnWidth) /
((uniqueHeadings.length +
((this.props.parent.props.Document._chromeStatus !== 'view-mode' && this.props.parent.props.Document._chromeStatus !== 'disabled') ? 1 : 0)) || 1)
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d1aeb1b65..a1dd905c2 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -112,10 +112,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
[...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
}
@computed get childDocs() {
- let rawdocs: (Doc | Promise<Doc>)[] = DocListCast(this.props.Document._searchDocs);
-
- if (rawdocs.length !== 0) {
- } else if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list;
+ let rawdocs: (Doc | Promise<Doc>)[] = [];
+ if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list;
rawdocs = [this.dataField];
} else if (Cast(this.dataField, listSpec(Doc), null)) { // otherwise, if the collection data is a list, then use it.
rawdocs = Cast(this.dataField, listSpec(Doc), null);
@@ -128,63 +126,41 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const docs = rawdocs.filter(d => !(d instanceof Promise)).map(d => d as Doc);
const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
- let childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
+ const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
+
+ let searchDocs = DocListCast(this.props.Document._searchDocs);
- const searchDocs = DocListCast(this.props.Document._searchDocs);
- // if (searchDocs !== undefined && searchDocs.length > 0) {
- // let newdocs: Doc[] = [];
- // childDocs.forEach((el) => {
- // searchDocs.includes(el) ? newdocs.push(el) : undefined;
- // });
- // childDocs = newdocs;
- // }
let docsforFilter: Doc[] = childDocs;
+
if (searchDocs !== undefined && searchDocs.length > 0) {
docsforFilter = [];
- // let newdocs: Doc[] = [];
- // let newarray: Doc[] = [];
- //while (childDocs.length > 0) {
- //newarray = [];
+ 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);
childDocs.forEach((d) => {
if (d.data !== undefined) {
- console.log(d);
let newdocs = DocListCast(d.data);
if (newdocs.length > 0) {
- let vibecheck: boolean | undefined = undefined;
+ let displaycheck = false;
let newarray: Doc[] = [];
while (newdocs.length > 0) {
newarray = [];
newdocs.forEach((t) => {
- if (d.data !== undefined) {
- const newdocs = DocListCast(t.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- if (searchDocs.includes(t)) {
- vibecheck = true;
- }
+ DocListCast(t.data).forEach((newdoc) => newarray.push(newdoc));
+ displaycheck = displaycheck || searchDocs.includes(t);
});
newdocs = newarray;
}
- if (vibecheck === true) {
- docsforFilter.push(d);
- }
+ displaycheck && docsforFilter.push(d);
}
}
- if (searchDocs.includes(d)) {
- docsforFilter.push(d);
- }
+ searchDocs.includes(d) && docsforFilter.push(d);
});
- //childDocs = newarray;
- //}
+ return docsforFilter;
}
- childDocs = docsforFilter;
-
const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
-
- return this.props.Document.dontRegisterView ? docs : DocUtils.FilterDocs(docs, this.docFilters(), docRangeFilters, viewSpecScript);
+ return this.props.Document.dontRegisterView ? childDocs : DocUtils.FilterDocs(childDocs, this.docFilters(), docRangeFilters, viewSpecScript);
}
@action
@@ -251,11 +227,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
} else added = res;
+ e.stopPropagation();
} else {
ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
added = this.addDocument(docDragData.droppedDocuments);
}
- added && e.stopPropagation();
+ !added && alert("You cannot perform this move");
+ e.stopPropagation();
return added;
}
else if (de.complete.annoDragData) {
@@ -343,7 +321,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
let srcWeb: Doc | undefined;
if (SelectionManager.SelectedDocuments().length) {
srcWeb = SelectionManager.SelectedDocuments()[0].props.Document;
- srcUrl = (srcWeb.data as WebField).url.href?.match(/http[s]?:\/\/[^/]*/)?.[0];
+ srcUrl = (srcWeb.data as WebField).url?.href?.match(/http[s]?:\/\/[^/]*/)?.[0];
}
const reg = new RegExp(Utils.prepend(""), "g");
const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
@@ -351,7 +329,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc).text = text;
this.props.addDocument(htmlDoc);
if (srcWeb) {
- const focusNode = (SelectionManager.SelectedDocuments()[0].ContentDiv?.getElementsByTagName("iframe")[0].contentDocument?.getSelection()?.focusNode as any);
+ const focusNode = (SelectionManager.SelectedDocuments()[0].ContentDiv?.getElementsByTagName("iframe")?.[0].contentDocument?.getSelection()?.focusNode as any);
if (focusNode) {
const rect = "getBoundingClientRect" in focusNode ? focusNode.getBoundingClientRect() : focusNode?.parentElement.getBoundingClientRect();
const x = (rect?.x || 0);
@@ -481,7 +459,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
completed?.();
} else {
if (text && !text.includes("https://")) {
- this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
+ UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, 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 3c7471d7c..0f6274663 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -53,7 +53,7 @@ export interface TreeViewProps {
indentDocument?: () => void;
outdentDocument?: () => void;
ScreenToLocalTransform: () => Transform;
- backgroundColor?: (doc: Doc) => string | undefined;
+ backgroundColor?: (doc: Doc, renderDepth: number) => string | undefined;
outerXf: () => { translateX: number, translateY: number };
treeViewDoc: Doc;
parentKey: string;
@@ -88,7 +88,7 @@ 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 ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); }
+ get defaultExpandedView() { return this.childDocs.length ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); }
@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;
@@ -108,6 +108,7 @@ class TreeView extends React.Component<TreeViewProps> {
}
@computed get childDocs() { return this.childDocList(this.fieldKey); }
@computed get childLinks() { return this.childDocList("links"); }
+ @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
@computed get boundsOfCollectionDocument() {
return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined :
Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
@@ -127,7 +128,7 @@ class TreeView extends React.Component<TreeViewProps> {
constructor(props: any) {
super(props);
- const script = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); selectDoc(self);} `);
+ const script = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
this._editTitleScript = script && (() => script);
if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
}
@@ -313,11 +314,11 @@ class TreeView extends React.Component<TreeViewProps> {
@computed get renderContent() {
TraceMobx();
const expandKey = this.treeViewExpandedView;
- if (["links", this.fieldKey].includes(expandKey)) {
+ if (["links", "annotations", this.fieldKey].includes(expandKey)) {
const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey);
const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) =>
(doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true), true);
- const docs = expandKey === "links" ? this.childLinks : this.childDocs;
+ const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
const sortKey = `${this.fieldKey}-sortAscending`;
return <ul key={expandKey + "more"} onClick={(e) => {
this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true));
@@ -397,7 +398,7 @@ class TreeView extends React.Component<TreeViewProps> {
title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
onClick={this.bulletClick}
style={{ color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}>
- {<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down"))} />}
+ {<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs?.length ? "caret-square-right" : "caret-right") : (this.childDocs?.length ? "caret-square-down" : "caret-down"))} />}
</div>;
}
@@ -424,7 +425,8 @@ class TreeView extends React.Component<TreeViewProps> {
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.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
+ (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.treeViewOpen = true;
})}>
@@ -453,6 +455,7 @@ class TreeView extends React.Component<TreeViewProps> {
NativeHeight={returnZero}
NativeWidth={returnZero}
contextMenuItems={this.contextMenuItems}
+ opacity={returnOne}
renderDepth={1}
focus={returnTrue}
parentActive={returnTrue}
@@ -466,7 +469,7 @@ class TreeView extends React.Component<TreeViewProps> {
return <>
<div className="docContainer" ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
style={{
- fontWeight: this.doc.searchMatch ? "bold" : undefined,
+ fontWeight: this.doc.searchMatch !== undefined ? "bold" : undefined,
textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
outline: BoolCast(this.doc.workspaceBrush) ? "dashed 1px #06123232" : undefined,
pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
@@ -475,7 +478,7 @@ class TreeView extends React.Component<TreeViewProps> {
</div >
{headerElements}
<div className="treeViewItem-openRight" onClick={this.openRight}>
- <FontAwesomeIcon title="open in pane on right" icon="external-link-alt" size="sm" />
+ <FontAwesomeIcon title="open in a new pane" icon="external-link-alt" size="sm" />
</div>
</>;
}
@@ -494,7 +497,7 @@ class TreeView extends React.Component<TreeViewProps> {
}
}
} else this._editMaxWidth = "";
- return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active() && SelectionManager.DeselectAll()}>
+ return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
<li className="collection-child">
<div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
if (this.props.active(true)) {
@@ -533,7 +536,7 @@ class TreeView extends React.Component<TreeViewProps> {
dropAction: dropActionType,
addDocTab: (doc: Doc, where: string) => boolean,
pinToPres: (document: Doc) => void,
- backgroundColor: undefined | ((document: Doc) => string | undefined),
+ backgroundColor: undefined | ((document: Doc, renderDepth: number) => string | undefined),
screenToLocalXf: () => Transform,
outerXf: () => { translateX: number, translateY: number },
active: (outsideReaction?: boolean) => boolean,
@@ -813,7 +816,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
<div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
<div className="collectionTreeView-dropTarget" id="body"
style={{
- background: this.props.backgroundColor?.(this.doc),
+ background: this.props.backgroundColor?.(this.doc, this.props.renderDepth),
paddingLeft: `${NumCast(this.doc._xPadding, 10)}px`,
paddingRight: `${NumCast(this.doc._xPadding, 10)}px`,
paddingTop: `${NumCast(this.doc._yPadding, 20)}px`,
@@ -866,6 +869,7 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey
});
const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
const doc = new Doc();
+ doc.system = true;
doc.title = facetValue.toString();
doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", {}, { layoutDoc, facetHeader, facetValue });
return doc;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 0feec3fbd..0aaceb7f4 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -46,6 +46,7 @@ import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from "./CollectionTreeView";
import './CollectionView.scss';
import { ContextMenuProps } from '../ContextMenuItem';
+import { table } from 'console';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -68,7 +69,7 @@ export enum CollectionViewType {
Carousel = "carousel",
Carousel3D = "3D Carousel",
Linear = "linear",
- Staff = "staff",
+ //Staff = "staff",
Map = "map",
Grid = "grid",
Pile = "pileup"
@@ -138,10 +139,13 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
}
const docs = doc instanceof Doc ? [doc] : doc;
+
+
+ if (docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document))) return false;
const targetDataDoc = this.props.Document[DataSym];
const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
const added = docs.filter(d => !docList.includes(d));
- const effectiveAcl = GetEffectiveAcl(this.props.Document);
+ const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
if (added.length) {
if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
@@ -176,12 +180,15 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", "");
doc.displayTimecode = undefined;
}
+ doc._stayInCollection = undefined;
doc.context = this.props.Document;
});
added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add));
// targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
(targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added);
targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+ const lastModified = "lastModified";
+ targetDataDoc[lastModified] = new DateField(new Date(Date.now()));
}
}
}
@@ -190,16 +197,18 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
@action.bound
removeDocument = (doc: any): boolean => {
- const collectionEffectiveAcl = GetEffectiveAcl(this.props.Document);
- const docEffectiveAcl = GetEffectiveAcl(doc);
- // you can remove the document if you either have Admin/Edit access to the collection or to the specific document
- if (collectionEffectiveAcl === AclEdit || collectionEffectiveAcl === AclAdmin || docEffectiveAcl === AclAdmin || docEffectiveAcl === AclEdit) {
+ const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
+ if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) {
const docs = doc instanceof Doc ? [doc] : doc as Doc[];
const targetDataDoc = this.props.Document[DataSym];
const value = DocListCast(targetDataDoc[this.props.fieldKey]);
const toRemove = value.filter(v => docs.includes(v));
if (toRemove.length !== 0) {
- toRemove.forEach(doc => Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc));
+ const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
+ toRemove.forEach(doc => {
+ Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc);
+ recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
+ });
return true;
}
}
@@ -216,7 +225,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
return true;
}
const first = doc instanceof Doc ? doc : doc[0];
- if (!first?.stayInCollection && addDocument !== returnFalse) {
+ if (!first?._stayInCollection && addDocument !== returnFalse) {
if (UndoManager.RunInTempBatch(() => this.removeDocument(doc))) {
const added = addDocument(doc);
if (!added) UndoManager.UndoTempBatch();
@@ -247,7 +256,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
case CollectionViewType.Tree: return (<CollectionTreeView key="collview" {...props} />);
- case CollectionViewType.Staff: return (<CollectionStaffView key="collview" {...props} />);
+ //case CollectionViewType.Staff: return (<CollectionStaffView key="collview" {...props} />);
case CollectionViewType.Multicolumn: return (<CollectionMulticolumnView key="collview" {...props} />);
case CollectionViewType.Multirow: return (<CollectionMultirowView key="rpwview" {...props} />);
case CollectionViewType.Linear: { return (<CollectionLinearView key="collview" {...props} />); }
@@ -280,7 +289,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
subItems.push({ description: "Tree", event: () => func(CollectionViewType.Tree), icon: "tree" });
subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking), icon: "ellipsis-v" });
subItems.push({ description: "Stacking (AutoHeight)", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" });
- subItems.push({ description: "Staff", event: () => func(CollectionViewType.Staff), icon: "music" });
subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" });
subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" });
subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
@@ -474,6 +482,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet);
} else {
newFacet = new Doc();
+ newFacet.sytem = true;
newFacet.title = facetHeader;
newFacet.treeViewOpen = true;
newFacet.type = DocumentType.COL;
@@ -510,56 +519,56 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
</label>)}
</div>
);
- return !this._facetWidth || this.props.dontRegisterView ? (null) :
- <div className="collectionTimeView-treeView" style={{ width: `${this.facetWidth()}px`, overflow: this.facetWidth() < 15 ? "hidden" : undefined }}>
- <div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}>
- <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
- <div className="collectionTimeView-button">
- <FontAwesomeIcon icon={faEdit} size={"lg"} />
- <span className="collectionTimeView-span">Facet Filters</span>
- </div>
- </Flyout>
- </div>
- <div className="collectionTimeView-tree" key="tree">
- <CollectionTreeView
- PanelPosition={""}
- Document={facetCollection}
- DataDoc={facetCollection}
- fieldKey={`${this.props.fieldKey}-filter`}
- CollectionView={this}
- docFilters={returnEmptyFilter}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- ContainingCollectionView={this.props.ContainingCollectionView}
- PanelWidth={this.facetWidth}
- PanelHeight={this.props.PanelHeight}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
- LibraryPath={emptyPath}
- rootSelected={this.props.rootSelected}
- renderDepth={1}
- dropAction={this.props.dropAction}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
- addDocTab={returnFalse}
- pinToPres={returnFalse}
- isSelected={returnFalse}
- select={returnFalse}
- bringToFront={emptyFunction}
- active={this.props.active}
- whenActiveChanged={returnFalse}
- treeViewHideTitle={true}
- ContentScaling={returnOne}
- focus={returnFalse}
- treeViewHideHeaderFields={true}
- onCheckedClick={this.scriptField}
- ignoreFields={this.ignoreFields}
- annotationsKey={""}
- dontRegisterView={true}
- backgroundColor={this.filterBackground}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- addDocument={returnFalse} />
- </div>
- </div>;
+
+ return !this._facetWidth || this.props.dontRegisterView ? (null) : <div className="collectionTimeView-treeView" style={{ width: `${this.facetWidth()}px`, overflow: this.facetWidth() < 15 ? "hidden" : undefined }}>
+ <div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}>
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
+ <div className="collectionTimeView-button">
+ <FontAwesomeIcon icon={faEdit} size={"lg"} />
+ <span className="collectionTimeView-span">Facet Filters</span>
+ </div>
+ </Flyout>
+ </div>
+ <div className="collectionTimeView-tree" key="tree">
+ <CollectionTreeView
+ PanelPosition={""}
+ Document={facetCollection}
+ DataDoc={facetCollection}
+ fieldKey={`${this.props.fieldKey}-filter`}
+ CollectionView={this}
+ docFilters={returnEmptyFilter}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ PanelWidth={this.facetWidth}
+ PanelHeight={this.props.PanelHeight}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
+ LibraryPath={emptyPath}
+ rootSelected={this.props.rootSelected}
+ renderDepth={1}
+ dropAction={this.props.dropAction}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ addDocTab={returnFalse}
+ pinToPres={returnFalse}
+ isSelected={returnFalse}
+ select={returnFalse}
+ bringToFront={emptyFunction}
+ active={this.props.active}
+ whenActiveChanged={returnFalse}
+ treeViewHideTitle={true}
+ ContentScaling={returnOne}
+ focus={returnFalse}
+ treeViewHideHeaderFields={true}
+ onCheckedClick={this.scriptField}
+ ignoreFields={this.ignoreFields}
+ annotationsKey={""}
+ dontRegisterView={true}
+ backgroundColor={this.filterBackground}
+ moveDocument={returnFalse}
+ removeDocument={returnFalse}
+ addDocument={returnFalse} />
+ </div>
+ </div>;
}
childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 4c8cac3ed..149d4927b 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -15,6 +15,7 @@ import { faCog, faChevronCircleUp } from "@fortawesome/free-solid-svg-icons";
import { library } from "@fortawesome/fontawesome-svg-core";
import { DocumentView } from "../nodes/DocumentView";
import { SelectionManager } from "../../util/SelectionManager";
+import { Tooltip } from "@material-ui/core";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -121,16 +122,17 @@ export class DockingViewButtonSelector extends React.Component<{ views: () => Do
}
render() {
- return <span title="Tap for menu, drag tab as document"
- onPointerDown={e => {
+ return <Tooltip title={<><div className="dash-tooltip">Tap for toolbar, drag to create alias in another pane</div></>} placement="bottom">
+ <span onPointerDown={e => {
if (getComputedStyle(this._ref.current!).width !== "100%") {
e.stopPropagation(); e.preventDefault();
}
this.props.views()[0]?.select(false);
}} className="buttonSelector">
- <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyout} stylesheet={this.customStylesheet}>
- <FontAwesomeIcon icon={"arrows-alt"} size={"sm"} />
- </Flyout>
- </span>;
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyout} stylesheet={this.customStylesheet}>
+ <FontAwesomeIcon icon={"arrows-alt"} size={"sm"} />
+ </Flyout>
+ </span>
+ </Tooltip>;
}
}
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index a974c5496..6ec9783e2 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -63,18 +63,19 @@ export interface SchemaTableProps {
addDocument: (document: Doc | Doc[]) => boolean;
moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
- active: (outsideReaction: boolean) => boolean;
+ active: (outsideReaction: boolean | undefined) => boolean;
onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
addDocTab: (document: Doc, where: string) => boolean;
pinToPres: (document: Doc) => void;
isSelected: (outsideReaction?: boolean) => boolean;
- isFocused: (document: Doc) => boolean;
+ isFocused: (document: Doc, outsideReaction: boolean) => boolean;
setFocused: (document: Doc) => void;
- setPreviewDoc: (document: Doc) => void;
+ setPreviewDoc: (document: Opt<Doc>) => void;
columns: SchemaHeaderField[];
documentKeys: any[];
headerIsEditing: boolean;
openHeader: (column: any, screenx: number, screeny: number) => void;
+ onClick: (e: React.MouseEvent) => void;
onPointerDown: (e: React.PointerEvent) => void;
onResizedChange: (newResized: Resize[], event: any) => void;
setColumns: (columns: SchemaHeaderField[]) => void;
@@ -155,30 +156,36 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
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);
+ const tableIsFocused = this.props.isFocused(this.props.Document, false);
const focusedRow = this._focusedCell.row;
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: 30,
- Expander: (rowInfo) => {
- if (rowInfo.original.type === "collection") {
- if (rowInfo.isExpanded) return <div className="collectionSchemaView-expander" onClick={() => this.onCloseCollection(rowInfo.original)}><FontAwesomeIcon icon={"sort-up"} size="sm" /></div>;
- if (!rowInfo.isExpanded) return <div className="collectionSchemaView-expander" onClick={() => this.onExpandCollection(rowInfo.original)}><FontAwesomeIcon icon={"sort-down"} size="sm" /></div>;
- } else {
- return null;
- }
+ //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;
}
}
- );
- }
+ }
+ );
+ // }
+ this.props.active;
const cols = 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" :
+ this.getColumnType(col) === ColumnType.Date ? "calendar" : "align-justify";
const keysDropdown = <KeysDropdown
keyValue={col.heading}
@@ -189,24 +196,20 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
onSelect={this.props.changeColumns}
setIsEditing={this.props.setHeaderIsEditing}
docs={this.props.childDocs}
+ Document={this.props.Document}
+ dataDoc={this.props.dataDoc}
+ fieldKey={this.props.fieldKey}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ active={this.props.active}
+ openHeader={this.props.openHeader}
+ icon={icon}
+ col={col}
// try commenting this out
width={"100%"}
/>;
- 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" :
- this.getColumnType(col) === ColumnType.Date ? "calendar" : "align-justify";
- const headerText = this._showTitleDropdown ? keysDropdown : <div
- onClick={this.changeTitleMode}
- style={{
- background: col.color, padding: "2px",
- letterSpacing: "2px",
- textTransform: "uppercase",
- display: "flex"
- }}>
- {col.heading}</div>;
const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up";
@@ -218,24 +221,17 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
background: col.color, padding: "2px",
display: "flex", cursor: "default", height: "100%",
}}>
- <FontAwesomeIcon icon={icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px" }} />
- {/* <div className="keys-dropdown"
- style={{ display: "inline", zIndex: 1000 }}> */}
+ {/* <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> */}
<div onClick={e => this.changeSorting(col)}
- style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit" }}>
+ style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
<FontAwesomeIcon icon={sortIcon} size="lg" />
</div>
- {/* <div onClick={e => this.props.openHeader(col, e.clientX, e.clientY)}
- style={{ float: "right", paddingRight: "6px", zIndex: 1, background: "inherit" }}>
- <FontAwesomeIcon icon={"compass"} size="sm" />
- </div> */}
</div>;
return {
Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
- accessor: (doc: Doc) => doc ? doc[col.heading] : 0,
+ accessor: (doc: Doc) => doc ? Field.toString(doc[col.heading] as Field) : 0,
id: col.heading,
Cell: (rowProps: CellInfo) => {
const rowIndex = rowProps.index;
@@ -318,27 +314,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
-
- @action
- nextHighlight = (e: React.MouseEvent, doc: Doc) => {
- e.preventDefault();
- e.stopPropagation();
- doc.searchMatch = false;
- console.log(doc.searchMatch);
- setTimeout(() => doc.searchMatch = true, 0);
- console.log(doc.searchMatch);
-
- doc.searchIndex = NumCast(doc.searchIndex);
- }
-
- @action
- nextHighlight2 = (doc: Doc) => {
-
- doc.searchMatchAlt = false;
- setTimeout(() => doc.searchMatchAlt = true, 0);
- doc.searchIndex = NumCast(doc.searchIndex);
- }
-
constructor(props: SchemaTableProps) {
super(props);
// convert old schema columns (list of strings) into new schema columns (list of schema header fields)
@@ -347,7 +322,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
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) {
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
+ 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)]);
}
}
@@ -369,7 +345,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
addDoc: this.tableAddDoc,
removeDoc: this.props.deleteDocument,
rowInfo,
- rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document),
+ rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true),
textWrapRow: this.toggleTextWrapRow,
rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
dropAction: StrCast(this.props.Document.childDropAction),
@@ -383,7 +359,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const row = rowInfo.index;
//@ts-ignore
const col = this.columns.map(c => c.heading).indexOf(column!.id);
- const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document);
+ 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: {
@@ -403,12 +379,16 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
onKeyDown = (e: KeyboardEvent): void => {
- if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected(true)) {
+ if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {// && this.props.isSelected(true)) {
const direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : "";
this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
const pdoc = FieldValue(this.childDocs[this._focusedCell.row]);
pdoc && this.props.setPreviewDoc(pdoc);
+ e.stopPropagation();
+ } else if (e.keyCode === 27) {
+ this.props.setPreviewDoc(undefined);
+ e.stopPropagation(); // stopPropagation for left/right arrows
}
}
@@ -432,9 +412,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
@undoBatch
- createRow = () => {
+ createRow = action(() => {
this.props.addDocument(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 }));
- }
+ this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
+ });
@undoBatch
@action
@@ -586,10 +567,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
setComputed = (script: string, doc: Doc, field: string, row: number, col: number): boolean => {
script =
`const $ = (row:number, col?:number) => {
- if(col === undefined) {
- return (doc as any)[key][row + ${row}];
- }
- return (doc as any)[key][row + ${row}][(doc as any)._schemaHeaders[col + ${col}].heading];
+ const rval = (doc as any)[key][row + ${row}];
+ return col === undefined ? rval : rval[(doc as any)._schemaHeaders[col + ${col}].heading];
}
return ${script}`;
const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) });
@@ -620,10 +599,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
render() {
const preview = "";
- return <div className="collectionSchemaView-table" onPointerDown={this.props.onPointerDown} onWheel={e => this.props.active(true) && e.stopPropagation()}
+ return <div className="collectionSchemaView-table" style={{ overflow: this.props.Document._searchDoc ? undefined : "auto" }}
+ onPointerDown={this.props.onPointerDown} onClick={this.props.onClick} onWheel={e => this.props.active(true) && e.stopPropagation()}
onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
{this.reactTable}
- {StrCast(this.props.Document.type) !== "search" ? <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
+ {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(); }}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2b8e949b1..f6bb375fc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -15,7 +15,7 @@ import { ScriptField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
import { TraceMobx } from "../../../../fields/util";
import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
-import { aggregateBounds, intersectRect, returnFalse, returnOne, returnZero, Utils } from "../../../../Utils";
+import { aggregateBounds, intersectRect, returnFalse, returnOne, returnZero, Utils, setupMoveUpEvents } from "../../../../Utils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
@@ -179,7 +179,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
}
if (this.Document.currentFrame !== undefined && !this.props.isAnnotationOverlay) {
- CollectionFreeFormDocumentView.setupKeyframes(newBoxes, this.Document.currentFrame);
+ CollectionFreeFormDocumentView.setupKeyframes(newBoxes, this.Document.currentFrame, true);
}
}
return retVal;
@@ -214,7 +214,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const layoutDoc = Doc.Layout(d);
if (this.Document.currentFrame !== undefined) {
const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
- CollectionFreeFormDocumentView.setValues(this.Document.currentFrame, d, x + vals.x - dropPos[0], y + vals.y - dropPos[1], vals.h, vals.w, vals.opacity);
+ CollectionFreeFormDocumentView.setValues(this.Document.currentFrame, d, x + vals.x - dropPos[0], y + vals.y - dropPos[1], vals.h, vals.w, vals.scroll, vals.opacity);
} else {
d.x = x + NumCast(d.x) - dropPos[0];
d.y = y + NumCast(d.y) - dropPos[1];
@@ -378,7 +378,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
getClusterColor = (doc: Doc) => {
- let clusterColor = this.props.backgroundColor?.(doc);
+ let clusterColor = this.props.backgroundColor?.(doc, this.props.renderDepth + 1);
const cluster = NumCast(doc.cluster);
if (this.Document.useClusters) {
if (this._clusterSets.length <= cluster) {
@@ -605,6 +605,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => {
// bcz: theres should be a better way of doing these than referencing these static instances directly
MarqueeOptionsMenu.Instance?.fadeOut(true);// I think it makes sense for the marquee menu to go away when panned. -syip2
+ // PDFMenu.Instance.fadeOut(true); (commented out for mobile)
const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, undefined, true);
@@ -849,7 +850,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
} else {
const docs = this.childLayoutPairs.map(pair => pair.layout);
docs.slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
- let zlast = docs.length ? NumCast(docs[docs.length - 1].zIndex) : 1;
+ let zlast = docs.length ? Math.max(docs.length, NumCast(docs[docs.length - 1].zIndex)) : 1;
if (zlast - docs.length > 100) {
for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1;
zlast = docs.length + 1;
@@ -1199,27 +1200,29 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.props.addDocTab(childDocs as any as Doc, "inParent");
this.props.ContainingCollectionView?.removeDocument(this.props.Document);
}));
- layoutDocsInGrid = () => {
- UndoManager.RunInBatch(() => {
- const docs = this.childLayoutPairs;
- const startX = this.Document._panX || 0;
- let x = startX;
- let y = this.Document._panY || 0;
- let i = 0;
- const width = Math.max(...docs.map(doc => NumCast(doc.layout._width)));
- const height = Math.max(...docs.map(doc => NumCast(doc.layout._height)));
- docs.forEach(pair => {
- pair.layout.x = x;
- pair.layout.y = y;
- x += width + 20;
- if (++i === 6) {
- i = 0;
- x = startX;
- y += height + 20;
- }
- });
- }, "arrange contents");
- }
+
+
+ @undoBatch
+ layoutDocsInGrid = action(() => {
+ const docs = this.childLayoutPairs;
+ const startX = this.Document._panX || 0;
+ let x = startX;
+ let y = this.Document._panY || 0;
+ let i = 0;
+ const width = Math.max(...docs.map(doc => NumCast(doc.layout._width)));
+ const height = Math.max(...docs.map(doc => NumCast(doc.layout._height)));
+ docs.forEach(pair => {
+ pair.layout.x = x;
+ pair.layout.y = y;
+ x += width + 20;
+ if (++i === 6) {
+ i = 0;
+ x = startX;
+ y += height + 20;
+ }
+ });
+ });
+
@undoBatch
@action
toggleNativeDimensions = () => {
@@ -1312,7 +1315,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
@action
- setupDragLines = () => {
+ setupDragLines = (snapToDraggedDoc: boolean = false) => {
const activeDocs = this.getActiveDocuments();
if (activeDocs.length > 50) {
DragManager.SetSnapLines([], []);
@@ -1334,7 +1337,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const horizLines: number[] = [];
const vertLines: number[] = [];
- snappableDocs.filter(doc => !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)).forEach(doc => {
+ snappableDocs.filter(doc => snapToDraggedDoc || !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)).forEach(doc => {
const { left, top, width, height } = docDims(doc);
const topLeftInScreen = this.getTransform().inverse().transformPoint(left, top);
const docSize = this.getTransform().inverse().transformDirection(width, height);
@@ -1346,7 +1349,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
onPointerOver = (e: React.PointerEvent) => {
if (SnappingManager.GetIsDragging()) {
- this.setupDragLines();
+ this.setupDragLines(e.ctrlKey || e.shiftKey);
}
e.stopPropagation();
}
@@ -1494,8 +1497,123 @@ interface CollectionFreeFormViewPannableContentsProps {
@observer
class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps>{
+ @observable private _drag: string = '';
+
+ //Adds event listener so knows pointer is down and moving
+ onPointerDown = (e: React.PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+ const corner = e.target as any;
+ console.log(corner.id);
+ if (corner) this._drag = corner.id;
+ const rect = document.getElementById(this._drag);
+ if (rect) {
+ console.log(this._drag);
+ setupMoveUpEvents(e.target, e, this.onPointerMove, (e) => { }, (e) => { });
+ }
+ }
+
+ //Removes all event listeners
+ onPointerUp = (e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+ this._drag = "";
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ }
+
+ //Adjusts the value in NodeStore
+ @action
+ onPointerMove = (e: PointerEvent) => {
+ const doc = document.getElementById('resizable');
+ const rect = doc!.getBoundingClientRect();
+ const toNumber = (original: number, delta: number): number => {
+ return original + (delta * this.props.zoomScaling());
+ };
+ if (doc) {
+ const height = doc.offsetHeight;
+ const width = doc.offsetWidth;
+ const top = doc.offsetTop;
+ const left = doc.offsetLeft;
+ switch (this._drag) {
+ case "": break;
+ case "resizer-br":
+ doc.style.width = toNumber(width, e.movementX) + 'px';
+ doc.style.height = toNumber(height, e.movementY) + 'px';
+ break;
+ case "resizer-bl":
+ doc.style.width = toNumber(width, -e.movementX) + 'px';
+ doc.style.height = toNumber(height, e.movementY) + 'px';
+ doc.style.left = toNumber(left, e.movementX) + 'px';
+ break;
+ case "resizer-tr":
+ doc.style.width = toNumber(width, -e.movementX) + 'px';
+ doc.style.height = toNumber(height, -e.movementY) + 'px';
+ doc.style.top = toNumber(top, e.movementY) + 'px';
+ case "resizer-tl":
+ doc.style.width = toNumber(width, -e.movementX) + 'px';
+ doc.style.height = toNumber(height, -e.movementY) + 'px';
+ doc.style.top = toNumber(top, e.movementY) + 'px';
+ doc.style.left = toNumber(left, e.movementX) + 'px';
+ case "resizable":
+ doc.style.top = toNumber(top, e.movementY) + 'px';
+ doc.style.left = toNumber(left, e.movementX) + 'px';
+ }
+ this.updateAll(height, width, top, left);
+ return false;
+ }
+ return true;
+ }
+
+ @action
+ updateAll = (width: number, height: number, top: number, left: number) => {
+ const activeItem = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex], Doc, null);
+ const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ this.updateList(targetDoc, activeItem["viewfinder-width-indexed"], width);
+ this.updateList(targetDoc, activeItem["viewfinder-height-indexed"], height);
+ this.updateList(targetDoc, activeItem["viewfinder-top-indexed"], top);
+ this.updateList(targetDoc, activeItem["viewfinder-left-indexed"], left);
+ }
+
+ @action
+ updateList = (doc: Doc, list: any, val: number) => {
+ const x: List<number> = list;
+ if (x && x.length >= NumCast(doc.currentFrame) + 1) {
+ x[NumCast(doc.currentFrame)] = val;
+ list = x;
+ } else if (doc && x) {
+ x.length = NumCast(doc.currentFrame) + 1;
+ x[NumCast(doc.currentFrame)] = val;
+ list = x;
+ }
+ }
+
+ // scale: NumCast(targetDoc._viewScale),
+ @computed get zoomProgressivizeContainer() {
+ const activeItem = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex], Doc, null);
+ const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ if (activeItem && activeItem.zoomProgressivize) {
+ const vfLeft: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-left-indexed"]);
+ const vfWidth: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-width-indexed"]);
+ const vfTop: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-top-indexed"]);
+ const vfHeight: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-height-indexed"]);
+ return (
+ <>
+ {!activeItem.editZoomProgressivize ? (null) : <div id="resizable" className="resizable" onPointerDown={this.onPointerDown} style={{ width: vfWidth, height: vfHeight, top: vfTop, left: vfLeft, position: 'absolute' }}>
+ <div className='resizers'>
+ <div id="resizer-tl" className='resizer top-left' onPointerDown={this.onPointerDown}></div>
+ <div id="resizer-tr" className='resizer top-right' onPointerDown={this.onPointerDown}></div>
+ <div id="resizer-bl" className='resizer bottom-left' onPointerDown={this.onPointerDown}></div>
+ <div id="resizer-br" className='resizer bottom-right' onPointerDown={this.onPointerDown}></div>
+ </div>
+ </div>}
+ </>
+ );
+ }
+ }
+
@computed get zoomProgressivize() {
- return PresBox.Instance && this.props.zoomProgressivize ? PresBox.Instance.zoomProgressivizeContainer : (null);
+ return PresBox.Instance && this.props.zoomProgressivize ? this.zoomProgressivizeContainer : (null);
}
@computed get progressivize() {
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index 1ffa2fbed..a7f44bbbf 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -9,13 +9,13 @@ import { InkField } from "../../../../fields/InkField";
import { BoolCast, Cast, NumCast } from "../../../../fields/Types";
import { DocumentType } from "../../../documents/DocumentTypes";
import { SelectionManager } from "../../../util/SelectionManager";
-import AntimodeMenu from "../../AntimodeMenu";
+import AntimodeMenu, { AntimodeMenuProps } from "../../AntimodeMenu";
import "./FormatShapePane.scss";
import { undoBatch } from "../../../util/UndoManager";
import { ColorState, SketchPicker } from 'react-color';
@observer
-export default class FormatShapePane extends AntimodeMenu {
+export default class FormatShapePane extends AntimodeMenu<AntimodeMenuProps> {
static Instance: FormatShapePane;
private _lastFill = "#D0021B";
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index f1df7998b..2cfe0183c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -1,5 +1,5 @@
import React = require("react");
-import AntimodeMenu from "../../AntimodeMenu";
+import AntimodeMenu, { AntimodeMenuProps } from "../../AntimodeMenu";
import { observer } from "mobx-react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { unimplementedFunction } from "../../../../Utils";
@@ -7,7 +7,7 @@ import { undoBatch } from "../../../util/UndoManager";
import { Tooltip } from "@material-ui/core";
@observer
-export default class MarqueeOptionsMenu extends AntimodeMenu {
+export default class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: MarqueeOptionsMenu;
public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3606d5402..0918e8389 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -12,7 +12,7 @@ import { CognitiveServices } from "../../../cognitive_services/CognitiveServices
import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
import { SelectionManager } from "../../../util/SelectionManager";
import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../../util/UndoManager";
import { ContextMenu } from "../../ContextMenu";
import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
import { PreviewCursor } from "../../PreviewCursor";
@@ -21,6 +21,7 @@ import { CollectionView } from "../CollectionView";
import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import "./MarqueeView.scss";
import React = require("react");
+import { ContextMenuItem } from "../../ContextMenuItem";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -70,23 +71,19 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
onKeyPress = (e: KeyboardEvent) => {
//make textbox and add it to this collection
// tslint:disable-next-line:prefer-const
- let [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
+ const cm = ContextMenu.Instance;
+ const [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
if (e.key === "?") {
- ContextMenu.Instance.setDefaultItem("?", (str: string) => {
- const textDoc = Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, {
- _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false,
- title: "bing", UseCors: true
- });
- this.props.addDocTab(textDoc, "onRight");
- });
+ cm.setDefaultItem("?", (str: string) => this.props.addDocTab(
+ Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }), "onRight"));
- ContextMenu.Instance.displayMenu(this._downX, this._downY);
+ cm.displayMenu(this._downX, this._downY);
e.stopPropagation();
} else
if (e.key === ":") {
DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument, x, y);
- ContextMenu.Instance.displayMenu(this._downX, this._downY);
+ cm.displayMenu(this._downX, this._downY);
e.stopPropagation();
} else if (e.key === "a" && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
@@ -108,11 +105,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (br) break;
}
}
+ let ypos = y;
ns.map(line => {
const indent = line.search(/\S|$/);
- const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: y, title: line });
+ const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: ypos, title: line });
this.props.addDocument(newBox);
- y += 40 * this.props.getTransform().Scale;
+ ypos += 40 * this.props.getTransform().Scale;
});
})();
e.stopPropagation();
@@ -140,6 +138,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
tbox.layoutKey = "layout_" + StrCast(template.title);
Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
}
+ FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch");
this.props.addLiveTextDocument(tbox);
e.stopPropagation();
}
@@ -280,7 +279,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
} else {
this._downX = x;
this._downY = y;
- const effectiveAcl = GetEffectiveAcl(this.props.Document);
+ const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
if ([AclAdmin, AclEdit, AclAddonly].includes(effectiveAcl)) PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge);
this.clearSelection();
}
@@ -342,41 +341,33 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@undoBatch
@action
delete = () => {
- const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
const selected = this.marqueeSelect(false);
SelectionManager.DeselectAll();
-
- selected.map(doc => {
- const effectiveAcl = GetEffectiveAcl(doc);
- if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { // deletes whatever you have the right to delete
- recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
- this.props.removeDocument(doc);
- }
- });
+ selected.forEach(doc => this.props.removeDocument(doc));
this.cleanupInteractions(false);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
}
- getCollection = (selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, isBackground?: boolean) => {
- const bounds = this.Bounds;
- // const inkData = this.ink ? this.ink.inkData : undefined;
- const newCollection = (creator || Docs.Create.FreeformDocument)(selected, {
- x: bounds.left,
- y: bounds.top,
- _panX: 0,
- _panY: 0,
- isBackground,
- backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : isBackground ? "cyan" : undefined,
- _width: bounds.width,
- _height: bounds.height,
- title: "a nested collection",
- });
+ getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, isBackground?: boolean) => {
+ const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
+ Doc.GetProto(doc).data = new List<Doc>(selected);
+ Doc.GetProto(doc).title = "nested freeform";
+ doc._panX = doc._panY = 0;
+ return doc;
+ })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+ newCollection.system = undefined;
+ newCollection.isBackground = isBackground;
+ newCollection.backgroundColor = this.props.isAnnotationOverlay ? "#00000015" : isBackground ? "cyan" : undefined;
+ newCollection._width = this.Bounds.width;
+ newCollection._height = this.Bounds.height;
+ newCollection.x = this.Bounds.left;
+ newCollection.y = this.Bounds.top;
selected.forEach(d => d.context = newCollection);
this.hideMarquee();
return newCollection;
- }
+ });
@action
pileup = (e: KeyboardEvent | React.PointerEvent | undefined) => {
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
index aee28366a..535581f2e 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
@@ -121,6 +121,19 @@
padding: 10px;
margin-left: 5px;
+ .propertiesView-acls-checkbox {
+ float: right;
+ height: 20px;
+ margin-top: -20px;
+ margin-right: -15;
+
+ .propertiesView-acls-checkbox-text {
+ font-size: 7px;
+ margin-top: -10px;
+ margin-left: 6px;
+ }
+ }
+
.change-buttons {
display: flex;
@@ -259,6 +272,7 @@
background-color: #ececec;
max-height: 130px;
overflow-y: scroll;
+ width: 92%;
.propertiesView-sharingTable-item {
@@ -267,7 +281,6 @@
padding: 3px;
align-items: center;
border-bottom: 0.5px solid grey;
- cursor: pointer;
&:hover .propertiesView-sharingTable-item-name {
overflow-x: unset;
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
index e0f3eca44..fb138ecc0 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { observer } from "mobx-react";
import "./PropertiesView.scss";
import { observable, action, computed, runInAction } from "mobx";
-import { Doc, Field, WidthSym, HeightSym, AclSym, AclPrivate, AclReadonly, AclAddonly, AclEdit, AclAdmin, Opt, DocCastAsync } from "../../../../fields/Doc";
+import { Doc, Field, WidthSym, HeightSym, AclSym, AclPrivate, AclReadonly, AclAddonly, AclEdit, AclAdmin, Opt, DocCastAsync, DataSym } from "../../../../fields/Doc";
import { ComputedField } from "../../../../fields/ScriptField";
import { EditableView } from "../../EditableView";
import { KeyValueBox } from "../../nodes/KeyValueBox";
@@ -49,6 +49,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed get MAX_EMBED_HEIGHT() { return 200; }
+ @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; }
@computed get selectedDocumentView() {
if (SelectionManager.SelectedDocuments().length) {
return SelectionManager.SelectedDocuments()[0];
@@ -60,7 +61,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
if (this.selectedDoc?.type === DocumentType.PRES) return true;
return false;
}
- @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
@computed get dataDoc() { return this.selectedDocumentView?.dataDoc; }
@observable layoutFields: boolean = false;
@@ -73,6 +73,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@observable openTransform: boolean = true;
// @observable selectedUser: string = "";
// @observable addButtonPressed: boolean = false;
+ @observable layoutDocAcls: boolean = false;
//Pres Trails booleans:
@observable openPresTransitions: boolean = false;
@@ -316,7 +317,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
*/
getPermissionsSelect(user: string, permission: string) {
return <select className="permissions-select"
- defaultValue={permission}
+ value={permission}
onChange={e => this.changePermissions(e, user)}>
{Object.values(SharingPermissions).map(permission => {
return (
@@ -344,8 +345,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed get expansionIcon() {
return <Tooltip title={<div className="dash-tooltip">{"Show more permissions"}</div>}>
<div className="expansion-button" onPointerDown={() => {
- if (this.selectedDocumentView) {
- SharingManager.Instance.open(this.selectedDocumentView);
+ if (this.selectedDocumentView || this.selectedDoc) {
+ SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDocumentView ? this.selectedDocumentView : undefined, this.selectedDoc);
}
}}>
<FontAwesomeIcon className="expansion-button-icon" icon="ellipsis-h" color="black" size="sm" />
@@ -382,15 +383,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
[AclAdmin, SharingPermissions.Admin]
]);
- const effectiveAcl = GetEffectiveAcl(this.selectedDoc!);
+ const target = this.layoutDocAcls ? this.selectedDoc! : this.selectedDoc![DataSym];
+
+ const effectiveAcl = GetEffectiveAcl(target);
const tableEntries = [];
// DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
- if (this.selectedDoc![AclSym]) {
- for (const [key, value] of Object.entries(this.selectedDoc![AclSym])) {
+ if (target[AclSym]) {
+ for (const [key, value] of Object.entries(target[AclSym])) {
const name = key.substring(4).replace("_", ".");
- if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author/* && sidebarUsersDisplayed![name] !== false*/) {
- tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value)!));
+ if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public"/* && sidebarUsersDisplayed![name] !== false*/) {
+ tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value as symbol)!));
}
}
}
@@ -402,9 +405,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
// }
// })
- // shifts the current user and the owner to the top of the doc.
- tableEntries.unshift(this.sharingItem("Me", effectiveAcl, Doc.CurrentUserEmail === this.selectedDoc!.author ? "Owner" : StrCast(this.selectedDoc![`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`])));
- if (Doc.CurrentUserEmail !== this.selectedDoc!.author) tableEntries.unshift(this.sharingItem(StrCast(this.selectedDoc!.author), effectiveAcl, "Owner"));
+ // shifts the current user, owner, public to the top of the doc.
+ tableEntries.unshift(this.sharingItem("Public", effectiveAcl, (AclMap.get(target[AclSym]?.["ACL-Public"]) || SharingPermissions.None)));
+ tableEntries.unshift(this.sharingItem("Me", effectiveAcl, Doc.CurrentUserEmail === target.author ? "Owner" : AclMap.get(effectiveAcl)!));
+ if (Doc.CurrentUserEmail !== target.author) tableEntries.unshift(this.sharingItem(StrCast(target.author), effectiveAcl, "Owner"));
return <div className="propertiesView-sharingTable">
{tableEntries}
@@ -866,6 +870,14 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>
{!this.openSharing ? (null) :
<div className="propertiesView-sharing-content">
+ <div className="propertiesView-acls-checkbox">
+ <Checkbox
+ color="primary"
+ onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)}
+ checked={this.layoutDocAcls}
+ />;
+ <div className="propertiesView-acls-checkbox-text">Layout</div>
+ </div>
{this.sharingTable}
{/* <div className="change-buttons">
<button
@@ -963,22 +975,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
if (this.isPres) {
const selectedItem: boolean = PresBox.Instance?._selectedArray.length > 0;
- return <div className="propertiesView">
- <div className="propertiesView-title">
+ return <div className="propertiesView" style={{ width: this.props.width }}>
+ <div className="propertiesView-title" style={{ width: this.props.width }}>
Presentation
</div>
<div className="propertiesView-name">
{this.editableTitle}
<div className="propertiesView-presSelected">
- {PresBox.Instance._selectedArray.length} selected
+ {PresBox.Instance?._selectedArray.length} selected
<div className="propertiesView-selectedList">
- {PresBox.Instance.listOfSelected}
+ {PresBox.Instance?.listOfSelected}
</div>
</div>
</div>
{!selectedItem ? (null) : <div className="propertiesView-presTrails">
<div className="propertiesView-presTrails-title"
- onPointerDown={() => runInAction(() => { this.openPresTransitions = !this.openPresTransitions; })}
+ onPointerDown={action(() => { this.openPresTransitions = !this.openPresTransitions; })}
style={{ backgroundColor: this.openPresTransitions ? "black" : "" }}>
&nbsp; <FontAwesomeIcon icon={"rocket"} /> &nbsp; Transitions
<div className="propertiesView-presTrails-title-icon">
@@ -1028,7 +1040,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
{PresBox.Instance.newDocumentDropdown}
</div> : null}
</div>
- <div className="propertiesView-sharing">
+ {/* <div className="propertiesView-sharing">
<div className="propertiesView-sharing-title"
onPointerDown={() => runInAction(() => { this.openSharing = !this.openSharing; })}
style={{ backgroundColor: this.openSharing ? "black" : "" }}>
@@ -1040,7 +1052,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
{this.openSharing ? <div className="propertiesView-sharing-content">
{this.sharingTable}
</div> : null}
- </div>
+ </div> */}
</div>;
}
}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index e6ac7021a..70ebca5e7 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -94,8 +94,8 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
*/
unflexedPosition(index: number): Omit<Layout, "i"> {
return {
- x: (index % Math.floor(this.numCols / this.defaultW)) * this.defaultW,
- y: Math.floor(index / Math.floor(this.numCols / this.defaultH)) * this.defaultH,
+ x: (index % (Math.floor(this.numCols / this.defaultW) || 1)) * this.defaultW,
+ y: Math.floor(index / (Math.floor(this.numCols / this.defaultH) || 1)) * this.defaultH,
w: this.defaultW,
h: this.defaultH,
static: true
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index 4c79a7c2f..7eb07ff55 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -29,6 +29,7 @@ $search-thumnail-size: 130;
$contextMenu-zindex: 100000; // context menu shows up over everything
$radialMenu-zindex: 100000; // context menu shows up over everything
+$searchpanel-height: 32px;
$mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc
$docDecorations-zindex: 998; // then doc decorations appear over everything else
$remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
@@ -43,4 +44,5 @@ $MAX_ROW_HEIGHT: 44px;
MAX_ROW_HEIGHT: $MAX_ROW_HEIGHT;
SEARCH_THUMBNAIL_SIZE: $search-thumnail-size;
ANTIMODEMENU_HEIGHT: $antimodemenu-height;
+ SEARCH_PANEL_HEIGHT: $searchpanel-height;
} \ No newline at end of file
diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts
index a7ca4b300..fd1f60f13 100644
--- a/src/client/views/globalCssVariables.scss.d.ts
+++ b/src/client/views/globalCssVariables.scss.d.ts
@@ -6,6 +6,7 @@ interface IGlobalScss {
MAX_ROW_HEIGHT: string;
SEARCH_THUMBNAIL_SIZE: string;
ANTIMODEMENU_HEIGHT: string;
+ SEARCH_PANEL_HEIGHT: string;
}
declare const globalCssVariables: IGlobalScss;
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index c9e39cf7b..ed64bde32 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -382,11 +382,11 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
</div>
<div className="linkEditor-followingDropdown-option"
onPointerDown={() => this.changeFollowBehavior("onRight")}>
- Always open in new pane on right
+ Always open in a new pane
</div>
<div className="linkEditor-followingDropdown-option"
onPointerDown={() => this.changeFollowBehavior("inTab")}>
- Always open in new tab
+ Always open in a new tab
</div>
{this.props.linkDoc.linksToAnnotation ?
<div className="linkEditor-followingDropdown-option"
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index c80e3af24..f16d13a4e 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -17,6 +17,22 @@
display: flex;
width: 100%;
align-items: center;
+ height: 100%;
+
+ .audiobox-dictation {
+ position: relative;
+ width: 30px;
+ height: 100%;
+ align-items: center;
+ display: inherit;
+ background: dimgray;
+ left: 0px;
+ }
+
+ .audiobox-dictation:hover {
+ color: white;
+ cursor: pointer;
+ }
}
.audiobox-handle {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 289388320..5c8cb5e35 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -85,6 +85,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
constructor(props: Readonly<FieldViewProps>) {
super(props);
+ AudioBox.Instance = this;
// onClick play scripts
AudioBox.RangeScript = AudioBox.RangeScript || ScriptField.MakeScript(`scriptContext.playFrom((this.audioStart), (this.audioEnd))`, { scriptContext: "any" })!;
@@ -529,113 +530,112 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
bringToFront={emptyFunction}
scriptContext={this} />;
};
- return <div className="audiobox-container" onContextMenu={this.specificContextMenu} onClick={!this.path ? this.recordClick : undefined}>
- <div className="audiobox-inner" style={{ pointerEvents: !interactive ? "none" : undefined }}>
- {!this.path ?
- <div className="audiobox-buttons">
- <div className="audiobox-dictation" onClick={this.onFile}>
- <FontAwesomeIcon style={{ width: "30px", background: this.layoutDoc.playOnSelect ? "yellow" : "rgba(0,0,0,0)" }} icon="file-alt" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
- </div>
- {this.audioState === "recording" ?
- <div className="recording" onClick={e => e.stopPropagation()}>
- <div className="buttons" onClick={this.recordClick}>
- <FontAwesomeIcon style={{ width: "100%" }} icon={"stop"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
- </div>
- <div className="buttons" onClick={this._paused ? this.recordPlay : this.recordPause}>
- <FontAwesomeIcon style={{ width: "100%" }} icon={this._paused ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
- </div>
- <div className="time">{formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))}</div>
+ return <div className="audiobox-container" onContextMenu={this.specificContextMenu} onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined} style={{ pointerEvents: !interactive ? "none" : undefined }}>
+ {!this.path ?
+ <div className="audiobox-buttons">
+ <div className="audiobox-dictation" onClick={this.onFile}>
+ <FontAwesomeIcon style={{ width: "30px", background: this.layoutDoc.playOnSelect ? "yellow" : "rgba(0,0,0,0)" }} icon="file-alt" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
+ </div>
+ {this.audioState === "recording" || this.audioState === "paused" ?
+ <div className="recording" onClick={e => e.stopPropagation()}>
+ <div className="buttons" onClick={this.recordClick}>
+ <FontAwesomeIcon style={{ width: "100%" }} icon={"stop"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
+ </div>
+ <div className="buttons" onClick={this._paused ? this.recordPlay : this.recordPause}>
+ <FontAwesomeIcon style={{ width: "100%" }} icon={this._paused ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
</div>
- :
- <button className={`audiobox-record${interactive}`} style={{ backgroundColor: "black" }}>
- RECORD
+ <div className="time">{formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))}</div>
+ </div>
+ :
+ <button className={`audiobox-record${interactive}`} style={{ backgroundColor: "black" }}>
+ RECORD
</button>}
- </div> :
- <div className="audiobox-controls" >
- <div className="audiobox-dictation"></div>
- <div className="audiobox-player" >
- <div className="audiobox-playhead" title={this.audioState === "paused" ? "play" : "pause"} onClick={this.onPlay}> <FontAwesomeIcon style={{ width: "100%", position: "absolute", left: "0px", top: "5px", borderWidth: "thin", borderColor: "white" }} icon={this.audioState === "paused" ? "play" : "pause"} size={"1x"} /></div>
- <div className="audiobox-timeline" onClick={e => { e.stopPropagation(); e.preventDefault(); }}
- onPointerDown={e => {
- if (e.button === 0 && !e.ctrlKey) {
- const rect = (e.target as any).getBoundingClientRect();
-
- if (e.target !== this._audioRef.current) {
- const wasPaused = this.audioState === "paused";
- this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration;
- wasPaused && this.pause();
- }
-
- this.onPointerDownTimeline(e);
- }
- }}>
- <div className="waveform">
- {this.waveform}
- </div>
- {DocListCast(this.dataDoc[this.annotationKey]).map((m, i) =>
- (!m.isLabel) ?
- (this.layoutDoc.hideMarkers) ? (null) :
- <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container1`} key={i}
- title={`${formatTime(Math.round(NumCast(m.audioStart)))}` + " - " + `${formatTime(Math.round(NumCast(m.audioEnd)))}`}
- style={{
- left: `${NumCast(m.audioStart) / this.audioDuration * 100}%`,
- top: `${this.isOverlap(m) * 1 / (this.dataDoc.markerAmount + 1) * 100}%`,
- width: `${(NumCast(m.audioEnd) - NumCast(m.audioStart)) / this.audioDuration * 100}%`, height: `${1 / (this.dataDoc.markerAmount + 1) * 100}%`
- }}
- onClick={e => { this.playFrom(NumCast(m.audioStart), NumCast(m.audioEnd)); e.stopPropagation(); }} >
- <div className="left-resizer" onPointerDown={e => this.onPointerDown(e, m, true)}></div>
- {markerDoc(m, this.rangeScript)}
- <div className="resizer" onPointerDown={e => this.onPointerDown(e, m, false)}></div>
- </div>
- :
- (this.layoutDoc.hideLabels) ? (null) :
- <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={i}
- style={{ left: `${NumCast(m.audioStart) / this.audioDuration * 100}%` }}>
- {markerDoc(m, this.labelScript)}
- </div>
- )}
- {DocListCast(this.dataDoc.links).map((l, i) => {
- const { la1, la2, linkTime } = this.getLinkData(l);
- let startTime = linkTime;
- if (la2.audioStart && !la2.audioEnd) {
- startTime = NumCast(la2.audioStart);
+ </div> :
+ <div className="audiobox-controls" >
+ <div className="audiobox-dictation"></div>
+ <div className="audiobox-player" >
+ <div className="audiobox-playhead" title={this.audioState === "paused" ? "play" : "pause"} onClick={this.onPlay}> <FontAwesomeIcon style={{ width: "100%", position: "absolute", left: "0px", top: "5px", borderWidth: "thin", borderColor: "white" }} icon={this.audioState === "paused" ? "play" : "pause"} size={"1x"} /></div>
+ <div className="audiobox-timeline" onClick={e => { e.stopPropagation(); e.preventDefault(); }}
+ onPointerDown={e => {
+ if (e.button === 0 && !e.ctrlKey) {
+ const rect = (e.target as any).getBoundingClientRect();
+
+ if (e.target !== this._audioRef.current) {
+ const wasPaused = this.audioState === "paused";
+ this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration;
+ wasPaused && this.pause();
}
- return !linkTime ? (null) :
- <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={l[Id]} style={{ left: `${startTime / this.audioDuration * 100}%` }} onClick={e => e.stopPropagation()}>
- <DocumentView {...this.props}
- Document={l}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
- rootSelected={returnFalse}
- ContainingCollectionDoc={this.props.Document}
- parentActive={returnTrue}
- bringToFront={emptyFunction}
- backgroundColor={returnTransparent}
- ContentScaling={returnOne}
- forcedBackgroundColor={returnTransparent}
- pointerEvents={false}
- LayoutTemplate={undefined}
- LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(l, la2)}`)}
- />
- <div key={i} className={`audiobox-marker`} onPointerEnter={() => Doc.linkFollowHighlight(la1)}
- onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { this.playFrom(startTime); e.stopPropagation(); e.preventDefault(); } }} />
- </div>;
- })}
- {this._visible ? this.selectionContainer : null}
-
- <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc.currentTimecode) / this.audioDuration * 100}%`, pointerEvents: "none" }} />
- {this.audio}
- </div>
- <div className="current-time">
- {formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))}
- </div>
- <div className="total-time">
- {formatTime(Math.round(this.audioDuration))}
+ this.onPointerDownTimeline(e);
+ }
+ }}>
+ <div className="waveform">
+ {this.waveform}
</div>
+ {DocListCast(this.dataDoc[this.annotationKey]).map((m, i) =>
+ (!m.isLabel) ?
+ (this.layoutDoc.hideMarkers) ? (null) :
+ <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container1`} key={i}
+ title={`${formatTime(Math.round(NumCast(m.audioStart)))}` + " - " + `${formatTime(Math.round(NumCast(m.audioEnd)))}`}
+ style={{
+ left: `${NumCast(m.audioStart) / this.audioDuration * 100}%`,
+ top: `${this.isOverlap(m) * 1 / (this.dataDoc.markerAmount + 1) * 100}%`,
+ width: `${(NumCast(m.audioEnd) - NumCast(m.audioStart)) / this.audioDuration * 100}%`, height: `${1 / (this.dataDoc.markerAmount + 1) * 100}%`
+ }}
+ onClick={e => { this.playFrom(NumCast(m.audioStart), NumCast(m.audioEnd)); e.stopPropagation(); }} >
+ <div className="left-resizer" onPointerDown={e => this.onPointerDown(e, m, true)}></div>
+ {markerDoc(m, this.rangeScript)}
+ <div className="resizer" onPointerDown={e => this.onPointerDown(e, m, false)}></div>
+ </div>
+ :
+ (this.layoutDoc.hideLabels) ? (null) :
+ <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={i}
+ style={{ left: `${NumCast(m.audioStart) / this.audioDuration * 100}%` }}>
+ {markerDoc(m, this.labelScript)}
+ </div>
+ )}
+ {DocListCast(this.dataDoc.links).map((l, i) => {
+ const { la1, la2, linkTime } = this.getLinkData(l);
+ let startTime = linkTime;
+ if (la2.audioStart && !la2.audioEnd) {
+ startTime = NumCast(la2.audioStart);
+ }
+
+ return !linkTime ? (null) :
+ <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={l[Id]} style={{ left: `${startTime / this.audioDuration * 100}%` }} onClick={e => e.stopPropagation()}>
+ <DocumentView {...this.props}
+ Document={l}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
+ rootSelected={returnFalse}
+ ContainingCollectionDoc={this.props.Document}
+ parentActive={returnTrue}
+ bringToFront={emptyFunction}
+ backgroundColor={returnTransparent}
+ ContentScaling={returnOne}
+ forcedBackgroundColor={returnTransparent}
+ pointerEvents={false}
+ LayoutTemplate={undefined}
+ LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(l, la2)}`)}
+ />
+ <div key={i} className={`audiobox-marker`} onPointerEnter={() => Doc.linkFollowHighlight(la1)}
+ onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { this.playFrom(startTime); e.stopPropagation(); e.preventDefault(); } }} />
+ </div>;
+ })}
+ {this._visible ? this.selectionContainer : null}
+
+ <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc.currentTimecode) / this.audioDuration * 100}%`, pointerEvents: "none" }} />
+ {this.audio}
+ </div>
+ <div className="current-time">
+ {formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))}
+ </div>
+ <div className="total-time">
+ {formatTime(Math.round(this.audioDuration))}
</div>
</div>
- }</div>
+ </div>
+ }
</div>;
}
}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 42a42ddf1..e1661039e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -123,7 +123,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
setTimeout(() => doc.dataTransition = "inherit", 1010);
}
- public static setupScroll(doc: Doc, timecode: number, scrollProgressivize: boolean = false) {
+ public static setupScroll(doc: Doc, timecode: number) {
const scrollList = new List<number>();
scrollList[timecode] = NumCast(doc._scrollTop);
doc["scroll-indexed"] = scrollList;
@@ -132,7 +132,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
- public static updateKeyframe(docs: Doc[], time: number) {
+ public static updateKeyframe(docs: Doc[], time: number, targetDoc?: Doc) {
const timecode = Math.round(time);
docs.forEach(doc => {
const xindexed = Cast(doc['x-indexed'], listSpec("number"), null);
@@ -145,11 +145,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
xindexed?.length <= timecode + 1 && xindexed.push(undefined as any as number);
yindexed?.length <= timecode + 1 && yindexed.push(undefined as any as number);
opacityindexed?.length <= timecode + 1 && opacityindexed.push(undefined as any as number);
- if (doc.appearFrame) {
+ if (doc.appearFrame && targetDoc) {
if (doc.appearFrame === timecode + 1) {
- doc["text-color"] = "red";
+ doc["text-color"] = StrCast(targetDoc["pres-text-color"]);
} else if (doc.appearFrame < timecode + 1) {
- doc["text-color"] = "grey";
+ doc["text-color"] = StrCast(targetDoc["pres-text-viewed-color"]);
} else { doc["text-color"] = "black"; }
} else if (doc.appearFrame === 0) {
doc["text-color"] = "black";
@@ -164,46 +164,40 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
setTimeout(() => docs.forEach(doc => doc.dataTransition = "inherit"), 1010);
}
- public static setupZoom(doc: Doc, zoomProgressivize: boolean = false) {
+
+ public static setupZoom(doc: Doc, targDoc: Doc) {
const width = new List<number>();
const height = new List<number>();
const top = new List<number>();
const left = new List<number>();
- width.push(NumCast(doc.width));
- height.push(NumCast(doc.height));
- top.push(NumCast(doc.height) / -2);
- left.push(NumCast(doc.width) / -2);
+ width.push(NumCast(targDoc._width));
+ height.push(NumCast(targDoc._height));
+ top.push(NumCast(targDoc._height) / -2);
+ left.push(NumCast(targDoc._width) / -2);
doc["viewfinder-width-indexed"] = width;
doc["viewfinder-height-indexed"] = height;
doc["viewfinder-top-indexed"] = top;
doc["viewfinder-left-indexed"] = left;
}
- public static setupKeyframes(docs: Doc[], timecode: number, progressivize: boolean = false) {
- docs.forEach((doc, i) => {
- if (doc.appearFrame === undefined) doc.appearFrame = i;
- const curTimecode = progressivize ? i : timecode;
- const xlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const ylist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const wlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const hlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const olist = new List<number>(numberRange(timecode + 1).map(t => progressivize && t < (doc.appearFrame ? doc.appearFrame : i) ? 0 : 1));
- const oarray = olist;
- oarray.fill(0, 0, NumCast(doc.appearFrame) - 1);
- oarray.fill(1, NumCast(doc.appearFrame), timecode);
- // oarray.fill(0, 0, NumCast(doc.appearFrame) - 1);
- // oarray.fill(1, NumCast(doc.appearFrame), timecode);\
+ public static setupKeyframes(docs: Doc[], currTimecode: number, makeAppear: boolean = false) {
+ docs.forEach(doc => {
+ if (doc.appearFrame === undefined) doc.appearFrame = currTimecode;
+ const curTimecode = currTimecode;
+ const xlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const ylist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const wlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const hlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const olist = new List<number>(numberRange(currTimecode + 1).map(t => !doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1));
wlist[curTimecode] = NumCast(doc._width);
hlist[curTimecode] = NumCast(doc._height);
xlist[curTimecode] = NumCast(doc.x);
ylist[curTimecode] = NumCast(doc.y);
- doc.xArray = xlist;
- doc.yArray = ylist;
doc["x-indexed"] = xlist;
doc["y-indexed"] = ylist;
doc["w-indexed"] = wlist;
doc["h-indexed"] = hlist;
- doc["opacity-indexed"] = oarray;
+ doc["opacity-indexed"] = olist;
doc.activeFrame = ComputedField.MakeFunction("self.context?.currentFrame||0");
doc._height = ComputedField.MakeInterpolated("h", "activeFrame");
doc._width = ComputedField.MakeInterpolated("w", "activeFrame");
@@ -267,13 +261,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
NativeHeight = () => this.nativeHeight;
render() {
TraceMobx();
- const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
+ const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document, this.props.renderDepth);
return <div className="collectionFreeFormDocumentView-container"
style={{
boxShadow:
this.Opacity === 0 ? undefined : // if it's not visible, then no shadow
this.layoutDoc.z ? `#9c9396 ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
- this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
this.layoutDoc.isBackground ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
StrCast(this.layoutDoc.boxShadow, ""),
borderRadius: StrCast(Doc.Layout(this.layoutDoc).borderRounding),
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index 0c4242172..88eb48f51 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -184,7 +184,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
onContextMenu={this.specificContextMenu}
onPointerDown={this.onPointerDown} onClick={this.onClick}
style={{
- background: this.props.backgroundColor?.(containedDoc),
+ background: this.props.backgroundColor?.(containedDoc, this.props.renderDepth),
border: `#00000021 solid ${this.xPad}px`,
borderTop: `#0000005e solid ${this.yPad}px`,
borderBottom: `#0000005e solid ${this.yPad}px`,
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 31dd33fc1..429bc27ad 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -129,9 +129,15 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
LinkDescriptionPopup.popupY = e.screenY - 100;
LinkDescriptionPopup.descriptionPopup = true;
- LinkDescriptionPopup.popupX = e.screenX;
- LinkDescriptionPopup.popupY = e.screenY - 100;
- LinkDescriptionPopup.descriptionPopup = true;
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
}
@@ -176,6 +182,17 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
LinkDescriptionPopup.popupY = screenY - 100;
LinkDescriptionPopup.descriptionPopup = true;
}
+
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
+
setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500);
}
}
@@ -241,10 +258,11 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
style={{
width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px",
backgroundColor: DocumentLinksButton.StartLink ? "" : "grey",
+ opacity: DocumentLinksButton.StartLink ? "" : "50%",
border: DocumentLinksButton.StartLink ? "" : "none"
}}
onPointerDown={DocumentLinksButton.StartLink ? this.completeLink : emptyFunction}
- onClick={e => DocumentLinksButton.StartLink ? DocumentLinksButton.finishLinkClick(e.screenX, e.screenY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View) : emptyFunction} /> : (null)
+ onClick={e => DocumentLinksButton.StartLink ? DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View) : emptyFunction} /> : (null)
}
{
DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ? <div className={"documentLinksButton-startLink"}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 47e1b2715..590befd86 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -6,15 +6,13 @@ import { Document } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { listSpec } from "../../../fields/Schema";
-import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
-import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
+import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { emptyFunction, emptyPath, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
-import { ClientRecommender } from '../../ClientRecommender';
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
import { DocumentManager } from "../../util/DocumentManager";
@@ -22,7 +20,6 @@ import { DragManager, dropActionType } from "../../util/DragManager";
import { InteractionUtils } from '../../util/InteractionUtils';
import { LinkManager } from '../../util/LinkManager';
import { Scripting } from '../../util/Scripting';
-import { SearchUtil } from '../../util/SearchUtil';
import { SelectionManager } from "../../util/SelectionManager";
import SharingManager from '../../util/SharingManager';
import { SnappingManager } from '../../util/SnappingManager';
@@ -33,7 +30,6 @@ import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from "../DocComponent";
import { EditableView } from '../EditableView';
-import { KeyphraseQueryView } from '../KeyphraseQueryView';
import { DocumentContentsView } from "./DocumentContentsView";
import { DocumentLinksButton } from './DocumentLinksButton';
import "./DocumentView.scss";
@@ -73,7 +69,7 @@ export interface DocumentViewProps {
removeDocument?: (doc: Doc | Doc[]) => boolean;
moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
- setupDragLines?: () => void;
+ setupDragLines?: (snapToDraggedDoc: boolean) => void;
renderDepth: number;
ContentScaling: () => number;
PanelWidth: () => number;
@@ -86,7 +82,7 @@ export interface DocumentViewProps {
addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
pinToPres: (document: Doc) => void;
backgroundHalo?: () => boolean;
- backgroundColor?: (doc: Doc) => string | undefined;
+ backgroundColor?: (doc: Doc, renderDepth: number) => string | undefined;
forcedBackgroundColor?: (doc: Doc) => string | undefined;
opacity?: () => number | undefined;
ChromeHeight?: () => number;
@@ -179,7 +175,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
RadialMenu.Instance.openMenu(pt.pageX - 15, pt.pageY - 15);
// RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "map-pin", selected: -1 });
- const effectiveAcl = GetEffectiveAcl(this.props.Document);
+ const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
(effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 });
// RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "trash", selected: -1 });
RadialMenu.Instance.addItem({ description: "Pin", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 });
@@ -295,7 +291,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.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({
@@ -324,6 +320,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
self: this.rootDoc,
scriptContext: this.props.scriptContext,
thisContainer: this.props.ContainingCollectionDoc,
+ documentView: this,
shiftKey: e.shiftKey
}, console.log);
if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) && !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc)) {
@@ -570,19 +567,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (Doc.UserDoc().activeWorkspace === this.props.Document) {
alert("Can't delete the active workspace");
} else {
- const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
const selected = SelectionManager.SelectedDocuments().slice();
SelectionManager.DeselectAll();
-
- selected.map(dv => {
- const effectiveAcl = GetEffectiveAcl(dv.props.Document);
- if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { // deletes whatever you have the right to delete
- recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
- dv.props.removeDocument?.(dv.props.Document);
- }
- });
-
- this.props.Document.deleted = true;
+ selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
this.props.removeDocument?.(this.props.Document);
}
}
@@ -630,6 +617,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
LinkDescriptionPopup.popupY = de.y;
LinkDescriptionPopup.descriptionPopup = true;
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
+
setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
});
if (de.complete.annoDragData) {
@@ -697,7 +694,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@action
- onContextMenu = async (e: React.MouseEvent | Touch): Promise<void> => {
+ onContextMenu = (e: React.MouseEvent | Touch) => {
// the touch onContextMenu is button 0, the pointer onContextMenu is button 2
if (!(e instanceof Touch)) {
if (e.button === 0 && !e.ctrlKey) {
@@ -716,7 +713,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
const cm = ContextMenu.Instance;
- if (!cm) return;
+ if (!cm || (e?.nativeEvent as any)?.SchemaHandled) return;
const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
@@ -776,8 +773,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
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" });
}
- const effectiveAcl = GetEffectiveAcl(this.props.Document);
- (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
+ const collectionAcl = GetEffectiveAcl(this.props.ContainingCollectionDoc?.[DataSym]);
+ if (collectionAcl === AclAdmin || collectionAcl === AclEdit) moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
!more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
@@ -829,7 +826,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get contents() {
const pos = this.props.relative ? "relative " : "absolute";
TraceMobx();
- return (<div style={{ width: "100%", height: "100%" }}>
+ return (<div className="documentView-contentsView" style={{ borderRadius: "inherit", width: "100%", height: "100%" }}>
<DocumentContentsView key={1}
docFilters={this.props.docFilters}
ContainingCollectionView={this.props.ContainingCollectionView}
@@ -893,31 +890,34 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
anchorPanelWidth = () => this.props.PanelWidth() || 1;
anchorPanelHeight = () => this.props.PanelHeight() || 1;
- @computed.struct get directLinks() { return LinkManager.Instance.getAllDirectLinks(this.Document); }
+ @computed.struct get directLinks() { return LinkManager.Instance.getAllDirectLinks(this.rootDoc); }
@computed.struct get allLinks() { return DocListCast(this.Document.links); }
@computed.struct get allAnchors() {
TraceMobx();
if (this.props.LayoutTemplateString?.includes("LinkAnchorBox")) return null;
- return (this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots
+ if ((this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots
this.layoutDoc.presBox || // presentationbox nodes
this.rootDoc.type === DocumentType.LINK ||
- this.props.dontRegisterView ? (null) : // view that are not registered
- DocUtils.FilterDocs(this.directLinks, this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
- <div className="documentView-anchorCont" key={i + 1}>
- <DocumentView {...this.props}
- Document={d}
- ContainingCollectionView={this.props.ContainingCollectionView}
- ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection Need a better prop for passing the containing document to the LinkAnchorBox
- PanelWidth={this.anchorPanelWidth}
- PanelHeight={this.anchorPanelHeight}
- ContentScaling={returnOne}
- dontRegisterView={false}
- forcedBackgroundColor={returnTransparent}
- removeDocument={this.hideLinkAnchor}
- pointerEvents={false}
- LayoutTemplate={undefined}
- LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)} />
- </div >);
+ this.props.dontRegisterView) {// view that are not registered
+ return (null);
+ }
+ const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink(d));
+ return filtered.map((d, i) =>
+ <div className="documentView-anchorCont" key={i + 1}>
+ <DocumentView {...this.props}
+ Document={d}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection Need a better prop for passing the containing document to the LinkAnchorBox
+ PanelWidth={this.anchorPanelWidth}
+ PanelHeight={this.anchorPanelHeight}
+ ContentScaling={returnOne}
+ dontRegisterView={false}
+ forcedBackgroundColor={returnTransparent}
+ removeDocument={this.hideLinkAnchor}
+ pointerEvents={false}
+ LayoutTemplate={undefined}
+ LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)} />
+ </div >);
}
@computed get innards() {
TraceMobx();
@@ -935,7 +935,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const showTitle = StrCast(this.layoutDoc._showTitle);
const showTitleHover = StrCast(this.layoutDoc._showTitleHover);
const showCaption = StrCast(this.layoutDoc._showCaption);
- const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("PresBox") !== -1 || StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
+ const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
const captionView = (!showCaption ? (null) :
<div className="documentView-captionWrapper" style={{ backgroundColor: StrCast(this.layoutDoc["caption-backgroundColor"]), color: StrCast(this.layoutDoc["caption-color"]) }}>
<DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
@@ -1002,9 +1002,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
render() {
if (!(this.props.Document instanceof Doc)) return (null);
- if (GetEffectiveAcl(this.props.Document) === AclPrivate) return (null);
+ if (GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate) return (null);
if (this.props.Document.hidden) return (null);
- const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
+ const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document, this.props.renderDepth);
const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor;
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index e631ad5fe..9d61ec6d1 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -5,7 +5,7 @@ import { DateField } from "../../../fields/DateField";
import { Doc, FieldResult, Opt, Field } from "../../../fields/Doc";
import { List } from "../../../fields/List";
import { ScriptField } from "../../../fields/ScriptField";
-import { AudioField, VideoField } from "../../../fields/URLField";
+import { AudioField, VideoField, WebField } from "../../../fields/URLField";
import { Transform } from "../../util/Transform";
import { CollectionView } from "../collections/CollectionView";
import { AudioBox } from "./AudioBox";
@@ -38,7 +38,7 @@ export interface FieldViewProps {
pinToPres: (document: Doc) => void;
removeDocument?: (document: Doc | Doc[]) => boolean;
moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
- backgroundColor?: (document: Doc) => string | undefined;
+ backgroundColor?: (document: Doc, renderDepth: number) => string | undefined;
ScreenToLocalTransform: () => Transform;
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
active: (outsideReaction?: boolean) => boolean;
@@ -102,9 +102,10 @@ export class FieldView extends React.Component<FieldViewProps> {
else if (field instanceof VideoField) {
return <VideoBox {...this.props} />;
}
- else if (field instanceof AudioField) {
- return <AudioBox {...this.props} />;
- } else if (field instanceof DateField) {
+ // else if (field instanceof AudioField) {
+ // return <AudioBox {...this.props} />;
+ //}
+ else if (field instanceof DateField) {
return <p>{field.date.toLocaleString()}</p>;
}
else if (field instanceof Doc) {
@@ -135,9 +136,9 @@ export class FieldView extends React.Component<FieldViewProps> {
return <div> {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 HtmlField) {
- // return <WebBox {...this.props} />
- // }
+ else if (field instanceof WebField) {
+ return <p>{Field.toString(field.url.href)}</p>;
+ }
else if (!(field instanceof Promise)) {
return <p>{Field.toString(field)}</p>;
}
diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss
index 6a540269e..75bc90d7a 100644
--- a/src/client/views/nodes/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox.scss
@@ -13,6 +13,27 @@
width: 100%;
}
+.fontIconBadge-container {
+ position:absolute;
+ z-index: 1000;
+ top: 12px;
+
+ .fontIconBadge {
+ position: absolute;
+ top: -10px;
+ right: -10px;
+ color: white;
+ background: #f44b42;
+ font-weight: 300;
+ border-radius: 100%;
+ width: 25px;
+ height: 25px;
+ text-align: center;
+ padding-top: 4px;
+ font-size: 12px;
+ }
+}
+
.menuButton-round {
border-radius: 100%;
background-color: black;
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index a6b1678b5..fd71876b0 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -5,13 +5,14 @@ import { createSchema, makeInterface } from '../../../fields/Schema';
import { DocComponent } from '../DocComponent';
import './FontIconBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
-import { StrCast, Cast, NumCast } from '../../../fields/Types';
-import { Utils, emptyFunction } from "../../../Utils";
+import { StrCast, Cast, ScriptCast } from '../../../fields/Types';
+import { Utils, setupMoveUpEvents, returnFalse, emptyFunction } from "../../../Utils";
import { runInAction, observable, reaction, IReactionDisposer } from 'mobx';
-import { Doc } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
import { ContextMenu } from '../ContextMenu';
import { ScriptField } from '../../../fields/ScriptField';
import { Tooltip } from '@material-ui/core';
+import { DragManager } from '../../util/DragManager';
const FontIconSchema = createSchema({
icon: "string",
});
@@ -61,8 +62,9 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
render() {
const label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
const color = StrCast(this.layoutDoc.color, this._foregroundColor);
- const backgroundColor = StrCast(this.layoutDoc._backgroundColor, StrCast(this.rootDoc.backgroundColor, this.props.backgroundColor?.(this.rootDoc)));
+ const backgroundColor = StrCast(this.layoutDoc._backgroundColor, StrCast(this.rootDoc.backgroundColor, this.props.backgroundColor?.(this.rootDoc, this.props.renderDepth)));
const shape = StrCast(this.layoutDoc.iconShape, "round");
+
const button = <button className={`menuButton-${shape}`} ref={this._ref} onContextMenu={this.specificContextMenu}
style={{
boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined,
@@ -72,6 +74,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
{<FontAwesomeIcon className={`menuButton-icon-${shape}`} icon={StrCast(this.dataDoc.icon, "user") as any} color={color}
size={this.layoutDoc.iconShape === "square" ? "sm" : "lg"} />}
{!label ? (null) : <div className="fontIconBox-label" style={{ color, backgroundColor }}> {label} </div>}
+ {this.props.Document.watchedDocuments ? <FontIconBadge collection={Cast(this.props.Document.watchedDocuments, Doc, null)} /> : (null)}
</div>
</button>;
return !this.layoutDoc.toolTip ? button :
@@ -79,4 +82,34 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
{button}
</Tooltip>;
}
+}
+
+interface FontIconBadgeProps {
+ collection: Doc;
+}
+
+@observer
+export class FontIconBadge extends React.Component<FontIconBadgeProps> {
+ _notifsRef = React.createRef<HTMLDivElement>();
+
+ onPointerDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e,
+ (e: PointerEvent) => {
+ const dragData = new DragManager.DocumentDragData([this.props.collection]);
+ DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
+ return true;
+ },
+ returnFalse, emptyFunction, false);
+ }
+
+ render() {
+ if (!(this.props.collection instanceof Doc)) return (null);
+ const length = DocListCast(this.props.collection.data).length;
+ return <div className="fontIconBadge-container" style={{ width: 15, height: 15, top: 12 }} ref={this._notifsRef}>
+ <div className="fontIconBadge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
+ onPointerDown={this.onPointerDown} >
+ {length}
+ </div>
+ </div>;
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d668d332b..216c5f39e 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -158,7 +158,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
if (field) {
const funcs: ContextMenuProps[] = [];
funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" });
- funcs.push({ description: "Make Background", event: () => this.layoutDoc.isBackground = true, icon: "expand-arrows-alt" });
+ funcs.push({ description: "Make Background", event: () => { this.layoutDoc.isBackground = true; this.props.bringToFront?.(this.rootDoc); }, icon: "expand-arrows-alt" });
if (!Doc.UserDoc().noviceMode) {
funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
@@ -466,7 +466,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
width: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
height: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
pointerEvents: this.layoutDoc.isBackground ? "none" : undefined,
- borderRadius: `${Number(StrCast(this.layoutDoc.borderRoundisng).replace("px", "")) / this.props.ContentScaling()}px`
+ borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.props.ContentScaling()}px`
}} >
<CollectionFreeFormView {...this.props}
forceScaling={true}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 3f942e87b..532e7dc15 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -17,7 +17,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
render() {
return <div className={`linkBox-container${this.active() ? "-interactive" : ""}`}
- style={{ background: this.props.backgroundColor?.(this.props.Document) }} >
+ style={{ background: this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) }} >
<CollectionTreeView {...this.props}
ChromeHeight={returnZero}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index ebb916307..c4481b213 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -7,21 +7,16 @@ import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, re
import { Docs } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { Transform } from "../../util/Transform";
+import { ContextMenu } from '../ContextMenu';
import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
-import React = require("react");
-import { DocumentView } from './DocumentView';
-import { sortAndDeduplicateDiagnostics } from 'typescript';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { LinkManager } from '../../util/LinkManager';
import { DocumentLinksButton } from './DocumentLinksButton';
-import { ContextMenu } from '../ContextMenu';
-import { undoBatch } from '../../util/UndoManager';
+import React = require("react");
interface Props {
linkDoc?: Doc;
linkSrc?: Doc;
href?: string;
- backgroundColor: (doc: Doc) => string;
+ backgroundColor: (doc: Doc, renderDepth: number) => string;
addDocTab: (document: Doc, where: string) => boolean;
location: number[];
}
diff --git a/src/client/views/nodes/MenuIconBox.tsx b/src/client/views/nodes/MenuIconBox.tsx
index 0aa7b327e..5ed8a9b78 100644
--- a/src/client/views/nodes/MenuIconBox.tsx
+++ b/src/client/views/nodes/MenuIconBox.tsx
@@ -19,10 +19,10 @@ export class MenuIconBox extends DocComponent<FieldViewProps, MenuIconDocument>(
render() {
- const color = this.props.backgroundColor?.(this.props.Document) === "lightgrey" ? "black" : "white";
- const menuBTN = <div className="menuButton" style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document) }}>
+ const color = this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) === "lightgrey" ? "black" : "white";
+ const menuBTN = <div className="menuButton" style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) }}>
<div className="menuButton-wrap"
- style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document) }} >
+ style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) }} >
<FontAwesomeIcon className="menuButton-icon" icon={StrCast(this.dataDoc.icon, "user") as any} color={color} size="lg" />
<div className="menuButton-label" style={{ color: color }}> {this.dataDoc.title} </div>
</div>
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 2fdb87e63..255a1b2d0 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -60,19 +60,19 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
if (href) {
const pathCorrectionTest = /upload\_[a-z0-9]{32}.(.*)/g;
const matches = pathCorrectionTest.exec(href);
- console.log("\nHere's the { url } being fed into the outer regex:");
- console.log(href);
- console.log("And here's the 'properPath' build from the captured filename:\n");
+ // console.log("\nHere's the { url } being fed into the outer regex:");
+ // console.log(href);
+ // console.log("And here's the 'properPath' build from the captured filename:\n");
if (matches !== null && href.startsWith(window.location.origin)) {
const properPath = Utils.prepend(`/files/pdfs/${matches[0]}`);
- console.log(properPath);
+ //console.log(properPath);
if (!properPath.includes(href)) {
console.log(`The two (url and proper path) were not equal`);
const proto = Doc.GetProto(Document);
proto[this.props.fieldKey] = new PdfField(properPath);
proto[backup] = href;
} else {
- console.log(`The two (url and proper path) were equal`);
+ //console.log(`The two (url and proper path) were equal`);
}
} else {
console.log("Outer matches was null!");
diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss
index a87b0e466..c4d8f1a4f 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/PresBox.scss
@@ -3,6 +3,7 @@ $dark-blue: #5B9FDD;
$light-background: #ececec;
.presBox-cont {
+ cursor: auto;
position: absolute;
display: block;
pointer-events: inherit;
@@ -12,14 +13,14 @@ $light-background: #ececec;
width: 100%;
min-width: 20px;
height: 100%;
- min-height: 41px;
+ min-height: 35px;
letter-spacing: 2px;
overflow: hidden;
transition: 0.7s opacity ease;
.presBox-listCont {
position: relative;
- height: calc(100% - 25px);
+ height: calc(100% - 67px);
width: 100%;
margin-top: 3px;
}
@@ -50,6 +51,7 @@ $light-background: #ececec;
background-color: #323232;
.toolbar-button {
+ cursor: pointer;
margin-left: 10px;
margin-right: 10px;
letter-spacing: 0;
@@ -153,7 +155,6 @@ $light-background: #ececec;
margin-left: 5px;
margin-top: 5px;
margin-bottom: 5px;
- margin-right: 5px;
width: max-content;
justify-content: center;
align-items: center;
@@ -161,6 +162,30 @@ $light-background: #ececec;
padding-left: 10px;
}
+ .ribbon-propertyUpDown {
+ height: 20;
+ width: 20;
+ margin-top: 5px;
+ display: grid;
+ grid-template-rows: 10px 10px;
+
+ .ribbon-propertyUpDownItem {
+ cursor: pointer;
+ color: white;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ width: 100%;
+ background: black;
+ }
+
+ .ribbon-propertyUpDownItem:hover {
+ background: darkgrey;
+ transform: scale(1.05);
+ }
+ }
+
.presBox-subheading {
font-size: 11;
font-weight: 400;
@@ -254,6 +279,18 @@ $light-background: #ececec;
width: 100%;
}
+ .presBox-input {
+ width: 30;
+ height: 100%;
+ background: none;
+ border: none;
+ text-align: right;
+ }
+
+ .presBox-input:focus {
+ outline: none;
+ }
+
.ribbon-frameSelector {
border: black solid 1px;
width: 60px;
@@ -281,6 +318,7 @@ $light-background: #ececec;
}
.numKeyframe {
+ cursor: pointer;
font-size: 10;
font-weight: 600;
position: relative;
@@ -311,6 +349,7 @@ $light-background: #ececec;
.ribbon-final-button {
+ cursor: pointer;
position: relative;
font-size: 11;
font-weight: normal;
@@ -329,7 +368,13 @@ $light-background: #ececec;
background-color: #979797;
}
+ .ribbon-final-button:hover {
+ transform: scale(1.05);
+ transition: all 0.4s;
+ }
+
.ribbon-final-button-hidden {
+ cursor: pointer;
position: relative;
font-size: 11;
font-weight: normal;
@@ -347,6 +392,11 @@ $light-background: #ececec;
border-radius: 10px;
background-color: black;
}
+
+ .ribbon-final-button-hidden:hover {
+ transform: scale(1.05);
+ transition: all 0.4s;
+ }
}
.selectedList {
@@ -363,6 +413,7 @@ $light-background: #ececec;
}
.ribbon-button {
+ cursor: pointer;
font-size: 10.5;
font-weight: 200;
height: 20;
@@ -399,11 +450,9 @@ $light-background: #ececec;
grid-template-rows: max-content auto;
justify-self: center;
margin-top: 10px;
- /* padding-left: 10px; */
padding-right: 10px;
letter-spacing: normal;
width: 100%;
- /* max-width: 100%; */
height: max-content;
font-weight: 500;
position: relative;
@@ -412,10 +461,36 @@ $light-background: #ececec;
border-bottom: solid 1px darkgrey;
.presBox-dropdown:hover {
- border: solid 1px #378AD8;
- border-bottom-left-radius: 0px;
+ border: solid 1px $dark-blue;
+
+ .presBox-dropdownIcon {
+ color: $dark-blue;
+ }
+ }
+
+ .presBox-dropdown {
+ cursor: pointer;
+ display: grid;
+ grid-template-columns: auto 20%;
+ position: relative;
+ border: solid 1px black;
+ background-color: $light-background;
+ border-radius: 5px;
+ font-size: 10;
+ height: 25;
+ padding-left: 5px;
+ align-items: center;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ font-weight: 200;
+ width: 100%;
+ min-width: max-content;
+ max-width: 200px;
+ overflow: visible;
+
.presBox-dropdownOption {
+ cursor: pointer;
font-size: 11;
display: block;
padding-left: 10px;
@@ -431,13 +506,13 @@ $light-background: #ececec;
.presBox-dropdownOption.active {
position: relative;
- background-color: #aedef8;
+ background-color: $light-blue;
}
.presBox-dropdownOptions {
position: absolute;
- top: 24px;
- left: -1px;
+ top: 23px;
+ left: -2px;
z-index: 200;
width: 85%;
min-width: max-content;
@@ -449,34 +524,6 @@ $light-background: #ececec;
}
.presBox-dropdownIcon {
- color: #378AD8;
- }
- }
-
- .presBox-dropdown {
- display: grid;
- grid-template-columns: auto 20%;
- position: relative;
- border: solid 1px black;
- background-color: $light-background;
- border-radius: 5px;
- font-size: 10;
- height: 25;
- padding-left: 5px;
- align-items: center;
- margin-top: 5px;
- margin-bottom: 5px;
- font-weight: 200;
- width: 100%;
- min-width: max-content;
- max-width: 200px;
- overflow: visible;
-
- .presBox-dropdownOptions {
- display: none;
- }
-
- .presBox-dropdownIcon {
position: relative;
color: black;
align-self: center;
@@ -509,6 +556,7 @@ $light-background: #ececec;
}
.dropdown-play-button {
+ cursor: pointer;
font-size: 12;
padding-left: 5px;
padding-right: 5px;
@@ -523,6 +571,7 @@ $light-background: #ececec;
}
.presBox-button-left {
+ cursor: pointer;
position: relative;
align-self: flex-start;
justify-self: flex-start;
@@ -539,6 +588,7 @@ $light-background: #ececec;
}
.presBox-button-right {
+ cursor: pointer;
position: relative;
text-align: center;
border-left: solid 1px darkgrey;
@@ -561,6 +611,7 @@ $light-background: #ececec;
}
.dropdown-play {
+ cursor: pointer;
right: 0px;
top: calc(100% + 2px);
display: none;
@@ -581,6 +632,7 @@ $light-background: #ececec;
}
.open-layout {
+ cursor: pointer;
position: relative;
display: flex;
align-items: center;
@@ -612,6 +664,7 @@ $light-background: #ececec;
}
.layout {
+ cursor: pointer;
align-self: center;
justify-self: center;
margin-top: 5;
@@ -682,6 +735,7 @@ $light-background: #ececec;
grid-template-columns: auto auto;
.presBox-viewPicker {
+ cursor: pointer;
height: 25;
position: relative;
display: inline-block;
@@ -708,6 +762,7 @@ $light-background: #ececec;
}
.presBox-button {
+ cursor: pointer;
height: 25px;
border-radius: 5px;
display: none;
@@ -746,7 +801,7 @@ $light-background: #ececec;
}
- .miniPresOverlay {
+ .presPanelOverlay {
background-color: #323232;
color: white;
border-radius: 5px;
@@ -761,7 +816,7 @@ $light-background: #ececec;
right: 10px;
transition: all 0.2s;
- .miniPres-button-text {
+ .presPanel-button-text {
display: flex;
height: 20;
width: max-content;
@@ -778,13 +833,13 @@ $light-background: #ececec;
transition: all 0.3s;
}
- .miniPres-divider {
+ .presPanel-divider {
width: 0.5px;
height: 80%;
border-right: solid 1px #5a5a5a;
}
- .miniPres-button-frame {
+ .presPanel-button-frame {
justify-self: center;
align-self: center;
align-items: center;
@@ -799,7 +854,8 @@ $light-background: #ececec;
border-radius: 5px;
}
- .miniPres-button {
+ .presPanel-button {
+ cursor: pointer;
display: flex;
height: 20;
min-width: 20;
@@ -811,11 +867,11 @@ $light-background: #ececec;
transition: all 0.3s;
}
- .miniPres-button:hover {
+ .presPanel-button:hover {
background-color: #5a5a5a;
}
- .miniPres-button-text:hover {
+ .presPanel-button-text:hover {
background-color: #5a5a5a;
}
}
@@ -894,4 +950,82 @@ $light-background: #ececec;
.select {
font-size: 100%;
}
-} \ No newline at end of file
+}
+
+.miniPres:hover {
+ opacity: 1;
+}
+
+.miniPres {
+ cursor: grab;
+ position: absolute;
+ overflow: hidden;
+ right: 10;
+ top: 10;
+ opacity: 0.1;
+ transition: all 0.4s;
+ /* border: solid 1px; */
+ color: white;
+ /* box-shadow: black 0.4vw 0.4vw 0.8vw; */
+
+ .miniPresOverlay {
+ display: grid;
+ grid-template-columns: auto auto auto auto auto auto auto auto;
+ grid-template-rows: 100%;
+ height: 100%;
+ justify-items: center;
+ align-items: center;
+
+ .miniPres-button-text {
+ cursor: pointer;
+ display: flex;
+ height: 20;
+ font-weight: 400;
+ min-width: 100%;
+ border-radius: 5px;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s;
+ }
+
+ .miniPres-button-frame {
+ justify-self: center;
+ align-self: center;
+ align-items: center;
+ display: grid;
+ grid-template-columns: auto auto auto;
+ justify-content: space-around;
+ font-size: 11;
+ margin-left: 7;
+ width: 30;
+ height: 85%;
+ background-color: rgba(91, 157, 221, 0.4);
+ border-radius: 5px;
+ }
+
+ .miniPres-divider {
+ width: 0.5px;
+ height: 80%;
+ border-right: solid 2px #5a5a5a;
+ }
+
+ .miniPres-button {
+ cursor: pointer;
+ display: flex;
+ height: 20;
+ min-width: 20;
+ border-radius: 100%;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s;
+ }
+
+ .miniPres-button:hover {
+ background-color: #5a5a5a;
+ }
+
+ .miniPres-button-text:hover {
+ background-color: #5a5a5a;
+ }
+ }
+}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 849fc4076..d30ea03b1 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -23,10 +23,12 @@ import { Scripting } from "../../util/Scripting";
import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
import { List } from "../../../fields/List";
import { Tooltip } from "@material-ui/core";
-import { CollectionFreeFormViewChrome } from "../collections/CollectionMenu";
import { actionAsync } from "mobx-utils";
import { SelectionManager } from "../../util/SelectionManager";
import { AudioBox } from "./AudioBox";
+import { DocumentView } from "./DocumentView";
+import { SketchPicker, ColorState } from "react-color";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
type PresBoxSchema = makeInterface<[typeof documentSchema]>;
const PresBoxDocument = makeInterface(documentSchema);
@@ -54,13 +56,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@observable private presentTools: boolean = false;
@observable private pathBoolean: boolean = false;
@observable private expandBoolean: boolean = false;
+ @observable private openMovementDropdown: boolean = false;
+ @observable private openEffectDropdown: boolean = false;
@computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
@computed get itemIndex() { return NumCast(this.rootDoc._itemIndex); }
@computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); }
constructor(props: any) {
super(props);
- PresBox.Instance = this;
+ if (Doc.UserDoc().activePresentation = this.rootDoc) PresBox.Instance = this;
if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations.
Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
title: "pres element template", backgroundColor: "transparent", _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
@@ -92,8 +96,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
}
@computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
+
componentWillUnmount() {
document.removeEventListener("keydown", this.keyEvents, true);
+ this.turnOffEdit();
}
componentDidMount() {
@@ -106,6 +112,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
updateCurrentPresentation = () => {
Doc.UserDoc().activePresentation = this.rootDoc;
+ PresBox.Instance = this;
}
/**
@@ -123,25 +130,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const lastFrame = Cast(presTargetDoc.lastFrame, "number", null);
const curFrame = NumCast(presTargetDoc.currentFrame);
let internalFrames: boolean = false;
- if (presTargetDoc.presProgressivize || presTargetDoc.zoomProgressivize || presTargetDoc.scrollProgressivize) internalFrames = true;
+ if (presTargetDoc.presProgressivize || activeItem.zoomProgressivize || presTargetDoc.scrollProgressivize) internalFrames = true;
// Case 1: There are still other frames and should go through all frames before going to next slide
if (internalFrames && lastFrame !== undefined && curFrame < lastFrame) {
presTargetDoc._viewTransition = "all 1s";
setTimeout(() => presTargetDoc._viewTransition = undefined, 1010);
presTargetDoc.currentFrame = curFrame + 1;
if (presTargetDoc.scrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(presTargetDoc, currentFrame);
- if (presTargetDoc.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
- if (presTargetDoc.zoomProgressivize) this.zoomProgressivizeNext(presTargetDoc);
+ if (presTargetDoc.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, presTargetDoc);
+ else presTargetDoc.editing = true;
+ if (activeItem.zoomProgressivize) this.zoomProgressivizeNext(presTargetDoc);
// Case 2: Audio or video therefore wait to play the audio or video before moving on
- } else if ((presTargetDoc.type === DocumentType.AUDIO) && !this._moveOnFromAudio) {
+ } else if ((presTargetDoc.type === DocumentType.AUDIO) && !this._moveOnFromAudio && this.layoutDoc.presStatus !== 'auto') {
AudioBox.Instance.playFrom(0);
this._moveOnFromAudio = true;
// Case 3: No more frames in current doc and next slide is defined, therefore move to next slide
} else if (this.childDocs[this.itemIndex + 1] !== undefined) {
const nextSelected = this.itemIndex + 1;
+ if (presTargetDoc.type === DocumentType.AUDIO) AudioBox.Instance.pause();
this.gotoDocument(nextSelected, this.itemIndex);
const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null);
if (activeNext && targetNext.type === DocumentType.AUDIO && activeNext.playAuto) {
+ AudioBox.Instance.playFrom(0);
+ this._moveOnFromAudio = true;
} else this._moveOnFromAudio = false;
}
}
@@ -156,10 +167,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
back = () => {
this.updateCurrentPresentation();
const docAtCurrent = this.childDocs[this.itemIndex];
- if (docAtCurrent) {
+ const targetDoc = Cast(docAtCurrent.presentationTargetDoc, Doc, null);
+ const prevItem = Cast(this.childDocs[Math.max(0, this.itemIndex - 1)], Doc, null);
+ const prevTargetDoc = Cast(prevItem.presentationTargetDoc, Doc, null);
+ const lastFrame = Cast(targetDoc.lastFrame, "number", null);
+ const curFrame = NumCast(targetDoc.currentFrame);
+ if (lastFrame !== undefined && curFrame >= 1) {
+ this.prevKeyframe(targetDoc, docAtCurrent);
+ } else if (docAtCurrent) {
let prevSelected = this.itemIndex;
prevSelected = Math.max(0, prevSelected - 1);
this.gotoDocument(prevSelected, this.itemIndex);
+ if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc.currentFrame = NumCast(prevTargetDoc.lastFrame);
}
}
@@ -174,8 +193,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (presTargetDoc?.lastFrame !== undefined) {
presTargetDoc.currentFrame = 0;
}
- this.navigateToElement(this.childDocs[index]); //Handles movement to element
this._selectedArray = [this.childDocs[index]]; //Update selected array
+ //Handles movement to element
+ if (this.layoutDoc._viewType === "stacking") this.navigateToElement(this.childDocs[index]);
this.onHideDocument(); //Handles hide after/before
}
});
@@ -197,8 +217,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.turnOffEdit();
if (this.itemIndex >= 0) {
- if (targetDoc) {
- if (srcContext) this.layoutDoc.presCollection = srcContext;
+ if (srcContext && targetDoc) {
+ this.layoutDoc.presCollection = srcContext;
} else if (targetDoc) this.layoutDoc.presCollection = targetDoc;
}
if (collectionDocView) {
@@ -211,21 +231,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const docToJump = curDoc;
const willZoom = false;
- //docToJump stayed same meaning, it was not in the group or was the last element in the group
- if (targetDoc.zoomProgressivize && this.rootDoc.presStatus !== 'edit') {
- this.zoomProgressivizeNext(targetDoc);
- } else if (docToJump === curDoc) {
- //checking if curDoc has navigation open
- if (curDoc.presNavButton && targetDoc) {
- await DocumentManager.Instance.jumpToDocument(targetDoc, false, undefined, srcContext);
- } else if (curDoc.presZoomButton && targetDoc) {
+ // If openDocument is selected then it should open the document for the user
+ if (activeItem.openDocument) {
+ this.props.addDocTab(activeItem, "replace");
+ } else
+ //docToJump stayed same meaning, it was not in the group or was the last element in the group
+ if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== 'edit') {
+ this.zoomProgressivizeNext(targetDoc);
+ } else if (docToJump === curDoc) {
+ //checking if curDoc has navigation open
+ if (curDoc.presNavButton && targetDoc) {
+ await DocumentManager.Instance.jumpToDocument(targetDoc, false, undefined, srcContext);
+ } else if (curDoc.presZoomButton && targetDoc) {
+ //awaiting jump so that new scale can be found, since jumping is async
+ await DocumentManager.Instance.jumpToDocument(targetDoc, true, undefined, srcContext);
+ }
+ } else {
//awaiting jump so that new scale can be found, since jumping is async
- await DocumentManager.Instance.jumpToDocument(targetDoc, true, undefined, srcContext);
+ targetDoc && await DocumentManager.Instance.jumpToDocument(targetDoc, willZoom, undefined, srcContext);
}
- } else {
- //awaiting jump so that new scale can be found, since jumping is async
- targetDoc && await DocumentManager.Instance.jumpToDocument(targetDoc, willZoom, undefined, srcContext);
- }
// After navigating to the document, if it is added as a presPinView then it will
// adjust the pan and scale to that of the pinView when it was added.
// TODO: Add option to remove presPinView
@@ -234,10 +258,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
targetDoc._panY = activeItem.presPinViewY;
targetDoc._viewScale = activeItem.presPinViewScale;
}
- // If openDocument is selected then it should open the document for the user
- if (collectionDocView && activeItem.openDocument) {
- collectionDocView.props.addDocTab(activeItem, "inPlace");
- }
// If website and has presWebsite data associated then on click it should
// go back to that specific website
// TODO: Add progressivize for navigating web (storing websites for given frames)
@@ -250,22 +270,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
* Uses the viewfinder to progressivize through the different views of a single collection.
* @param presTargetDoc: document for which internal zoom is used
*/
- zoomProgressivizeNext = (presTargetDoc: Doc) => {
- const srcContext = Cast(presTargetDoc.context, Doc, null);
- const docView = DocumentManager.Instance.getDocumentView(presTargetDoc);
- const vfLeft: number = this.checkList(presTargetDoc, presTargetDoc["viewfinder-left-indexed"]);
- const vfWidth: number = this.checkList(presTargetDoc, presTargetDoc["viewfinder-width-indexed"]);
- const vfTop: number = this.checkList(presTargetDoc, presTargetDoc["viewfinder-top-indexed"]);
- const vfHeight: number = this.checkList(presTargetDoc, presTargetDoc["viewfinder-height-indexed"]);
+ zoomProgressivizeNext = (activeItem: Doc) => {
+ const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const srcContext = Cast(targetDoc?.context, Doc, null);
+ const docView = DocumentManager.Instance.getDocumentView(targetDoc);
+ const vfLeft = this.checkList(targetDoc, activeItem["viewfinder-left-indexed"]);
+ const vfWidth = this.checkList(targetDoc, activeItem["viewfinder-width-indexed"]);
+ const vfTop = this.checkList(targetDoc, activeItem["viewfinder-top-indexed"]);
+ const vfHeight = this.checkList(targetDoc, activeItem["viewfinder-height-indexed"]);
// Case 1: document that is not a Golden Layout tab
if (srcContext) {
const srcDocView = DocumentManager.Instance.getDocumentView(srcContext);
if (srcDocView) {
- const layoutdoc = Doc.Layout(presTargetDoc);
+ const layoutdoc = Doc.Layout(targetDoc);
const panelWidth: number = srcDocView.props.PanelWidth();
const panelHeight: number = srcDocView.props.PanelHeight();
- const newPanX = NumCast(presTargetDoc.x) + NumCast(layoutdoc._width) / 2;
- const newPanY = NumCast(presTargetDoc.y) + NumCast(layoutdoc._height) / 2;
+ const newPanX = NumCast(targetDoc.x) + NumCast(layoutdoc._width) / 2;
+ const newPanY = NumCast(targetDoc.y) + NumCast(layoutdoc._height) / 2;
const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight);
srcContext._panX = newPanX + (vfLeft + (vfWidth / 2));
srcContext._panY = newPanY + (vfTop + (vfHeight / 2));
@@ -277,9 +298,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const panelWidth: number = docView.props.PanelWidth();
const panelHeight: number = docView.props.PanelHeight();
const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight);
- presTargetDoc._panX = vfLeft + (vfWidth / 2);
- presTargetDoc._panY = vfTop + (vfWidth / 2);
- presTargetDoc._viewScale = newScale;
+ targetDoc._panX = vfLeft + (vfWidth / 2);
+ targetDoc._panY = vfTop + (vfWidth / 2);
+ targetDoc._viewScale = newScale;
}
const resize = document.getElementById('resizable');
if (resize) {
@@ -299,7 +320,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
onHideDocument = () => {
this.childDocs.forEach((doc, index) => {
const curDoc = Cast(doc, Doc, null);
- const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
+ const tagDoc = Cast(curDoc.presentationTargetDoc!, Doc, null);
if (tagDoc) tagDoc.opacity = 1;
if (curDoc.presHideTillShownButton) {
if (index > this.itemIndex) {
@@ -319,40 +340,58 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
+
//The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc
@undoBatch
@action
startAutoPres = (startSlide: number) => {
this.updateCurrentPresentation();
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ let activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ let targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ let duration = NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition);
+ const timer = (ms: number) => new Promise(res => this._presTimer = setTimeout(res, ms));
+ const load = async () => { // Wrap the loop into an async function for this to work
+ for (var i = startSlide; i < this.childDocs.length; i++) {
+ activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ duration = NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition);
+ if (duration <= 100) { duration = 2500; }
+ if (NumCast(targetDoc.lastFrame) > 0) {
+ for (var f = 0; f < NumCast(targetDoc.lastFrame); f++) {
+ await timer(duration / NumCast(targetDoc.lastFrame));
+ this.next();
+ }
+ }
+ await timer(duration); this.next(); // then the created Promise can be awaited
+ if (i === this.childDocs.length - 1) setTimeout(() => { clearTimeout(this._presTimer); if (this.layoutDoc.presStatus === 'auto') this.layoutDoc.presStatus = "manual"; }, duration);
+ }
+ };
if (this.layoutDoc.presStatus === "auto") {
- if (this._presTimer) clearInterval(this._presTimer);
+ if (this._presTimer) clearTimeout(this._presTimer);
this.layoutDoc.presStatus = "manual";
} else {
this.layoutDoc.presStatus = "auto";
this.startPresentation(startSlide);
this.gotoDocument(startSlide, this.itemIndex);
- this._presTimer = setInterval(() => {
- if (this.itemIndex + 1 < this.childDocs.length) this.next();
- else {
- clearInterval(this._presTimer);
- this.layoutDoc.presStatus = "manual";
- }
- }, targetDoc.presDuration ? NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition) : 2000);
+ load();
}
}
//The function that resets the presentation by removing every action done by it. It also
//stops the presentaton.
- // TODO: Ensure resetPresentation is called when the presentation is closed
resetPresentation = () => {
this.updateCurrentPresentation();
this.rootDoc._itemIndex = 0;
}
@action togglePath = () => this.pathBoolean = !this.pathBoolean;
- @action toggleExpand = () => this.expandBoolean = !this.expandBoolean;
+ @action toggleExpandMode = () => {
+ this.rootDoc.expandBoolean = !this.rootDoc.expandBoolean;
+ this.childDocs.forEach((doc) => {
+ if (this.rootDoc.expandBoolean) doc.presExpandInlineButton = true;
+ else if (!this.rootDoc.expandBoolean) doc.presExpandInlineButton = false;
+ });
+ }
/**
* The function that starts the presentation at the given index, also checking if actions should be applied
@@ -376,17 +415,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
* The method called to open the presentation as a minimized view
* TODO: Look at old updateMinimize and compare...
*/
+ @undoBatch
+ @action
updateMinimize = () => {
- const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
- this.turnOffEdit();
- if (srcContext) {
- if (srcContext.miniPres) {
- srcContext.miniPres = false;
- CollectionDockingView.AddRightSplit(this.rootDoc);
- } else {
- srcContext.miniPres = true;
- this.props.addDocTab?.(this.rootDoc, "close");
- }
+ if (this.layoutDoc.inOverlay) {
+ this.layoutDoc.presStatus = 'edit';
+ Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
+ CollectionDockingView.AddRightSplit(this.rootDoc);
+ this.layoutDoc.inOverlay = false;
+ } else {
+ this.layoutDoc.presStatus = 'manual';
+ const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250);
+ this.rootDoc.y = pt[1] + 10;
+ this.rootDoc._height = 35;
+ this.rootDoc._width = 250;
+ this.props.addDocTab?.(this.rootDoc, "close");
+ Doc.AddDocToList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
}
}
@@ -426,6 +471,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
break;
case 'jump': //Jump Cut
targetDoc.presTransition = 0;
+ activeItem.presZoomButton = true;
activeItem.presSwitchButton = !activeItem.presSwitchButton;
if (activeItem.presSwitchButton) activeItem.presMovement = 'Jump cut';
else activeItem.presMovement = 'None';
@@ -449,6 +495,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
} else {
doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
!this.childDocs.includes(doc) && (doc.presZoomButton = true);
+ if (this.rootDoc.expandBoolean) doc.presExpandInlineButton = true;
}
});
return true;
@@ -488,20 +535,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
return list;
}
+ @action
+ selectPres = () => {
+ const presDocView = DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc)!;
+ SelectionManager.SelectDoc(presDocView, false);
+ }
+
//Regular click
@action
selectElement = (doc: Doc) => {
this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.itemIndex));
+ this.selectPres();
}
//Command click
@action
multiSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
if (!this._selectedArray.includes(doc)) {
- this._selectedArray.push(this.childDocs[this.childDocs.indexOf(doc)]);
+ this._selectedArray.push(doc);
this._eleArray.push(ref);
this._dragArray.push(drag);
}
+ this.selectPres();
}
//Shift click
@@ -516,17 +571,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this._dragArray.push(drag);
}
}
+ this.selectPres();
}
- // Key for when the presentaiton is active (according to Selection Manager)
+ // Key for when the presentaiton is active
@action
keyEvents = (e: KeyboardEvent) => {
let handled = false;
const anchorNode = document.activeElement as HTMLDivElement;
if (anchorNode && anchorNode.className?.includes("lm_title")) return;
if (e.keyCode === 27) { // Escape key
- if (this.layoutDoc.presStatus === "edit") this._selectedArray = [];
+ if (this.layoutDoc.inOverlay) { this.updateMinimize(); }
+ else if (this.layoutDoc.presStatus === "edit") { this._selectedArray = []; this._eleArray = []; this._dragArray = []; }
else this.layoutDoc.presStatus = "edit";
+ if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
} if ((e.metaKey || e.altKey) && e.keyCode === 65) { // Ctrl-A to select all
if (this.layoutDoc.presStatus === "edit") {
@@ -534,14 +592,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
handled = true;
}
} if (e.keyCode === 37 || e.keyCode === 38) { // left(37) / a(65) / up(38) to go back
- this.back();
+ this.back(); if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
} if (e.keyCode === 39 || e.keyCode === 40) { // right (39) / d(68) / down(40) to go to next
- this.next();
+ this.next(); if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
} if (e.keyCode === 32) { // spacebar to 'present' or autoplay
if (this.layoutDoc.presStatus !== "edit") this.startAutoPres(0);
- else this.layoutDoc.presStatus = "manual";
+ else this.layoutDoc.presStatus = "manual"; if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
}
if (e.keyCode === 8) { // delete selected items
@@ -565,23 +623,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
viewPaths = async () => {
const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
- if (this.pathBoolean) {
- if (srcContext) {
- this.togglePath();
- srcContext._fitToBox = false;
- srcContext._viewType = "freeform";
- srcContext.presPathView = false;
- }
- } else {
- if (srcContext) {
- this.togglePath();
- srcContext._fitToBox = true;
- srcContext._viewType = "freeform";
- srcContext.presPathView = true;
- }
+ if (this.pathBoolean && srcContext) {
+ this.togglePath();
+ srcContext.presPathView = false;
+ } else if (srcContext) {
+ this.togglePath();
+ srcContext.presPathView = true;
}
- const viewType = srcContext?._viewType;
- const fit = srcContext?._fitToBox;
}
// Adds the index in the pres path graphically
@@ -589,7 +637,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const order: JSX.Element[] = [];
this.childDocs.forEach((doc, index) => {
const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
- const srcContext = Cast(targetDoc.context, Doc, null);
+ const srcContext = Cast(targetDoc?.context, Doc, null);
// Case A: Document is contained within the colleciton
if (this.rootDoc.presCollection === srcContext) {
order.push(
@@ -619,7 +667,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
let pathPoints = "";
this.childDocs.forEach((doc, index) => {
const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
- const srcContext = Cast(targetDoc.context, Doc, null);
+ const srcContext = Cast(targetDoc?.context, Doc, null);
if (targetDoc && this.rootDoc.presCollection === srcContext) {
const n1x = NumCast(targetDoc.x) + (NumCast(targetDoc._width) / 2);
const n1y = NumCast(targetDoc.y) + (NumCast(targetDoc._height) / 2);
@@ -669,16 +717,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
// Converts seconds to ms and updates presTransition
- setTransitionTime = (number: String) => {
- const timeInMS = Number(number) * 1000;
+ setTransitionTime = (number: String, change?: number) => {
+ let timeInMS = Number(number) * 1000;
+ if (change) timeInMS += change;
+ if (timeInMS < 100) timeInMS = 100;
+ if (timeInMS > 10000) timeInMS = 10000;
const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
if (targetDoc) targetDoc.presTransition = timeInMS;
}
// Converts seconds to ms and updates presDuration
- setDurationTime = (number: String) => {
- const timeInMS = Number(number) * 1000;
+ setDurationTime = (number: String, change?: number) => {
+ let timeInMS = Number(number) * 1000;
+ if (change) timeInMS += change;
+ if (timeInMS < 100) timeInMS = 100;
+ if (timeInMS > 20000) timeInMS = 20000;
const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
if (targetDoc) targetDoc.presDuration = timeInMS;
@@ -689,19 +743,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
if (activeItem && targetDoc) {
- const transitionSpeed = targetDoc.presTransition ? String(Number(targetDoc.presTransition) / 1000) : 0.5;
- let duration = targetDoc.presDuration ? String(Number(targetDoc.presDuration) / 1000) : 2;
+ const transitionSpeed = targetDoc.presTransition ? NumCast(targetDoc.presTransition) / 1000 : 0.5;
+ let duration = targetDoc.presDuration ? NumCast(targetDoc.presDuration) / 1000 : 2;
if (targetDoc.type === DocumentType.AUDIO) duration = NumCast(targetDoc.duration);
const effect = targetDoc.presEffect ? targetDoc.presEffect : 'None';
activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom';
return (
- <div className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+ <div className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = false; this.openEffectDropdown = false; })}>
<div className="ribbon-box">
Movement
- <div className="presBox-dropdown" onPointerDown={e => e.stopPropagation()}>
+ <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? 'solid 2px #5B9FDD' : 'solid 1px black' }}>
{activeItem.presMovement}
- <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2 }} icon={"angle-down"} />
- <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onClick={e => e.stopPropagation()}>
+ <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? '#5B9FDD' : 'black' }} icon={"angle-down"} />
+ <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}>
<div className={`presBox-dropdownOption ${activeItem.presMovement === 'None' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('none')}>None</div>
<div className={`presBox-dropdownOption ${activeItem.presMovement === 'Zoom' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('zoom')}>Pan and Zoom</div>
<div className={`presBox-dropdownOption ${activeItem.presMovement === 'Pan' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('pan')}>Pan</div>
@@ -709,8 +763,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
</div>
<div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "inline-flex" : "none" }}>
- <div className="presBox-subheading" >Transition Speed</div>
- <div className="ribbon-property"> {transitionSpeed} s </div>
+ <div className="presBox-subheading">Transition Speed</div>
+ <div className="ribbon-property">
+ <input className="presBox-input"
+ type="number" value={transitionSpeed}
+ onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ onChange={action((e) => this.setTransitionTime(e.target.value))} /> s
+ </div>
+ <div className="ribbon-propertyUpDown">
+ <div className="ribbon-propertyUpDownItem" onClick={() => this.setTransitionTime(String(transitionSpeed), 1000)}>
+ <FontAwesomeIcon icon={"caret-up"} />
+ </div>
+ <div className="ribbon-propertyUpDownItem" onClick={() => this.setTransitionTime(String(transitionSpeed), -1000)}>
+ <FontAwesomeIcon icon={"caret-down"} />
+ </div>
+ </div>
</div>
<input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} className={`toolbar-slider ${activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "" : "none"}`} id="toolbar-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setTransitionTime(e.target.value); }} />
<div className={`slider-headers ${activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "" : "none"}`}>
@@ -727,9 +794,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
<div className="ribbon-doubleButton" >
<div className="presBox-subheading">Slide Duration</div>
- <div className="ribbon-property"> {duration} s </div>
+ <div className="ribbon-property">
+ <input className="presBox-input"
+ type="number" value={duration}
+ onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ onChange={action((e) => this.setDurationTime(e.target.value))} /> s
+ </div>
+ <div className="ribbon-propertyUpDown">
+ <div className="ribbon-propertyUpDownItem" onClick={() => this.setDurationTime(String(duration), 1000)}>
+ <FontAwesomeIcon icon={"caret-up"} />
+ </div>
+ <div className="ribbon-propertyUpDownItem" onClick={() => this.setDurationTime(String(duration), -1000)}>
+ <FontAwesomeIcon icon={"caret-down"} />
+ </div>
+ </div>
</div>
- <input type="range" step="0.1" min="0.1" max="10" value={duration} style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "block" }} className={"toolbar-slider"} id="duration-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setDurationTime(e.target.value); }} />
+ <input type="range" step="0.1" min="0.1" max="20" value={duration} style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "block" }} className={"toolbar-slider"} id="duration-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setDurationTime(e.target.value); }} />
<div className={"slider-headers"} style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "grid" }}>
<div className="slider-text">Short</div>
<div className="slider-text">Medium</div>
@@ -738,12 +818,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
<div className="ribbon-box">
Effects
- <div className="presBox-dropdown"
- onPointerDown={e => e.stopPropagation()}
- >
+ <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? 'solid 2px #5B9FDD' : 'solid 1px black' }}>
{effect}
- <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2 }} icon={"angle-down"} />
- <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onClick={e => e.stopPropagation()}>
+ <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? '#5B9FDD' : 'black' }} icon={"angle-down"} />
+ <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? "grid" : "none" }} onPointerDown={e => e.stopPropagation()}>
<div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => targetDoc.presEffect = 'None'}>None</div>
<div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => targetDoc.presEffect = 'Fade'}>Fade In</div>
<div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => targetDoc.presEffect = 'Flip'}>Flip</div>
@@ -774,7 +852,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
Apply to all
</div>
</div>
- </div>
+ </div >
);
}
}
@@ -793,6 +871,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
return effect;
}
+ @undoBatch
+ @action
applyTo = (array: Doc[]) => {
const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
@@ -803,12 +883,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
tagDoc.presTransition = targetDoc.presTransition;
tagDoc.presDuration = targetDoc.presDuration;
tagDoc.presEffect = targetDoc.presEffect;
+ tagDoc.presEffectDirection = targetDoc.presEffectDirection;
+ curDoc.presMovement = activeItem.presMovement;
+ curDoc.presHideTillShownButton = activeItem.presHideTillShownButton;
+ curDoc.presHideAfterButton = activeItem.presHideAfterButton;
}
});
}
-
- private inputRef = React.createRef<HTMLInputElement>();
-
@computed get optionsDropdown() {
const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
@@ -838,9 +919,41 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
}}>Presentation pin view</div>
</div>
- <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.WEB ? "inline-flex" : "none" }}>
- <div className="ribbon-button" onClick={this.progressivizeText}>Store original website</div>
+ <div style={{ display: activeItem.presPinView ? "block" : "none" }}>
+ <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ <div className="presBox-subheading">Pan X</div>
+ <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presPinViewX)}
+ onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewX = Number(val); })} />
+ </div>
+ </div>
+ <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ <div className="presBox-subheading">Pan Y</div>
+ <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presPinViewY)}
+ onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewY = Number(val); })} />
+ </div>
+ </div>
+ <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ <div className="presBox-subheading">Scale</div>
+ <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presPinViewScale)}
+ onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} />
+ </div>
+ </div>
</div>
+ {/* <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.WEB ? "inline-flex" : "none" }}>
+ <div className="ribbon-button" onClick={this.progressivizeText}>Store original website</div>
+ </div> */}
</div>
</div>
</div >
@@ -865,11 +978,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="title" style={{ alignSelf: 'center' }}>Title</div>
<div className="content">Text goes here</div>
</div>
- {/* <div className="layout" style={{ border: this.layout === 'twoColumns' ? 'solid 2px #5b9ddd' : '' }} onClick={() => runInAction(() => { this.layout = 'twoColumns'; this.createNewSlide(this.layout); })}>
- <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>Title</div>
- <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>Column one text</div>
- <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>Column two text</div>
- </div> */}
</div>
</div>
</div >
@@ -884,13 +992,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get newDocumentDropdown() {
return (
<div>
- <div className={"presBox-ribbon"} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+ <div className={"presBox-ribbon"} onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div className="ribbon-box">
Slide Title: <br></br>
- <input className="ribbon-textInput" placeholder="..." type="text" name="fname" ref={this.inputRef} onChange={(e) => {
- e.stopPropagation();
- runInAction(() => this.title = e.target.value);
- }}></input>
+ <input className="ribbon-textInput" placeholder="..." type="text" name="fname"
+ onFocus={() => {
+ document.removeEventListener("keydown", this.keyEvents, true);
+ }}
+ onChange={(e) => {
+ e.stopPropagation();
+ e.preventDefault();
+ runInAction(() => this.title = e.target.value);
+ }}></input>
</div>
<div className="ribbon-box">
Choose type:
@@ -939,15 +1052,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (layout) doc = this.createTemplate(layout);
if (freeform && layout) doc = this.createTemplate(layout, title);
if (!freeform && !layout) doc = Docs.Create.TextDocument("", { _nativeWidth: 400, _width: 225, title: title });
- const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
- const data = Cast(presCollection?.data, listSpec(Doc));
- const presData = Cast(this.rootDoc.data, listSpec(Doc));
- if (data && doc && presData) {
- data.push(doc);
- DockedFrameRenderer.PinDoc(doc, false);
- this.gotoDocument(this.childDocs.length, this.itemIndex);
- } else {
- this.props.addDocTab(doc as Doc, "onRight");
+ if (doc) {
+ const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
+ const data = Cast(presCollection?.data, listSpec(Doc));
+ const presData = Cast(this.rootDoc.data, listSpec(Doc));
+ if (data && presData) {
+ data.push(doc);
+ DockedFrameRenderer.PinDoc(doc, false);
+ this.gotoDocument(this.childDocs.length, this.itemIndex);
+ } else {
+ this.props.addDocTab(doc, "onRight");
+ }
}
}
@@ -1007,7 +1122,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// Case in which the document has keyframes to navigate to next key frame
@undoBatch
@action
- nextKeyframe = (tagDoc: Doc): void => {
+ nextKeyframe = (tagDoc: Doc, activeItem: Doc): void => {
const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
const currentFrame = Cast(tagDoc.currentFrame, "number", null);
if (currentFrame === undefined) {
@@ -1016,23 +1131,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
}
CollectionFreeFormDocumentView.updateScrollframe(tagDoc, currentFrame);
- CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
+ CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, tagDoc);
tagDoc.currentFrame = Math.max(0, (currentFrame || 0) + 1);
tagDoc.lastFrame = Math.max(NumCast(tagDoc.currentFrame), NumCast(tagDoc.lastFrame));
- if (tagDoc.zoomProgressivize) {
+ if (activeItem.zoomProgressivize) {
const resize = document.getElementById('resizable');
if (resize) {
- resize.style.width = this.checkList(tagDoc, tagDoc["viewfinder-width-indexed"]) + 'px';
- resize.style.height = this.checkList(tagDoc, tagDoc["viewfinder-height-indexed"]) + 'px';
- resize.style.top = this.checkList(tagDoc, tagDoc["viewfinder-top-indexed"]) + 'px';
- resize.style.left = this.checkList(tagDoc, tagDoc["viewfinder-left-indexed"]) + 'px';
+ resize.style.width = this.checkList(tagDoc, activeItem["viewfinder-width-indexed"]) + 'px';
+ resize.style.height = this.checkList(tagDoc, activeItem["viewfinder-height-indexed"]) + 'px';
+ resize.style.top = this.checkList(tagDoc, activeItem["viewfinder-top-indexed"]) + 'px';
+ resize.style.left = this.checkList(tagDoc, activeItem["viewfinder-left-indexed"]) + 'px';
}
}
}
@undoBatch
@action
- prevKeyframe = (tagDoc: Doc): void => {
+ prevKeyframe = (tagDoc: Doc, activeItem: Doc): void => {
const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
const currentFrame = Cast(tagDoc.currentFrame, "number", null);
if (currentFrame === undefined) {
@@ -1041,13 +1156,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
CollectionFreeFormDocumentView.gotoKeyframe(childDocs.slice());
tagDoc.currentFrame = Math.max(0, (currentFrame || 0) - 1);
- if (tagDoc.zoomProgressivize) {
+ if (activeItem.zoomProgressivize) {
const resize = document.getElementById('resizable');
if (resize) {
- resize.style.width = this.checkList(tagDoc, tagDoc["viewfinder-width-indexed"]) + 'px';
- resize.style.height = this.checkList(tagDoc, tagDoc["viewfinder-height-indexed"]) + 'px';
- resize.style.top = this.checkList(tagDoc, tagDoc["viewfinder-top-indexed"]) + 'px';
- resize.style.left = this.checkList(tagDoc, tagDoc["viewfinder-left-indexed"]) + 'px';
+ resize.style.width = this.checkList(tagDoc, activeItem["viewfinder-width-indexed"]) + 'px';
+ resize.style.height = this.checkList(tagDoc, activeItem["viewfinder-height-indexed"]) + 'px';
+ resize.style.top = this.checkList(tagDoc, activeItem["viewfinder-top-indexed"]) + 'px';
+ resize.style.left = this.checkList(tagDoc, activeItem["viewfinder-left-indexed"]) + 'px';
}
}
}
@@ -1067,58 +1182,72 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
case DocumentType.AUDIO: type = "Audio"; break;
case DocumentType.VID: type = "Video"; break;
case DocumentType.IMG: type = "Image"; break;
+ case DocumentType.WEB: type = "Web page"; break;
default: type = "Other node"; break;
}
}
return type;
}
+ @observable private openActiveColorPicker: boolean = false;
+ @observable private openViewedColorPicker: boolean = false;
+
+
+
@computed get progressivizeDropdown() {
const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
-
+ const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black";
+ const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black";
if (activeItem && targetDoc) {
return (
<div>
<div className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div className="ribbon-box">
{this.stringType} selected
- <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform' ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Child documents</div>
- <div className="ribbon-button" style={{ display: activeItem.presProgressivize ? "flex" : "none", backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Edit</div>
+ <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform' ? "inline-flex" : "none" }}>
+ <div className="ribbon-button" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Contents</div>
+ <div className="ribbon-button" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Edit</div>
</div>
- <div className="ribbon-doubleButton" style={{ display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.zoomProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeZoom}>Internal zoom</div>
- <div className="ribbon-button" style={{ display: activeItem.zoomProgressivize ? "flex" : "none", backgroundColor: targetDoc.editZoomProgressivize ? "#aedef8" : "" }} onClick={this.editZoomProgressivize}>Viewfinder</div>
- {/* <div className="ribbon-button" style={{ display: activeItem.zoomProgressivize ? "flex" : "none", backgroundColor: targetDoc.editSnapZoomProgressivize ? "#aedef8" : "" }} onClick={this.editSnapZoomProgressivize}>Snapshot</div> */}
+ <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
+ <div className="presBox-subheading">Active text color</div>
+ <div className="ribbon-property" style={{ backgroundColor: activeFontColor }} onClick={action(() => { console.log("hi"); this.openActiveColorPicker = !this.openActiveColorPicker; })}>
+ </div>
+ </div>
+ {this.activeColorPicker}
+ <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
+ <div className="presBox-subheading">Viewed font color</div>
+ <div className="ribbon-property" style={{ backgroundColor: viewedFontColor }} onClick={action(() => this.openViewedColorPicker = !this.openViewedColorPicker)}>
+ </div>
</div>
- {/* <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform' ? "inline-flex" : "none" }}>
- <div className="ribbon-button" onClick={this.progressivizeText}>Text progressivize</div>
- <div className="ribbon-button" style={{ display: activeItem.textProgressivize ? "flex" : "none", backgroundColor: targetDoc.editTextProgressivize ? "#aedef8" : "" }} onClick={this.editTextProgressivize}>Edit</div>
+ {this.viewedColorPicker}
+ {/* <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
+ <div className="ribbon-button" style={{ backgroundColor: activeItem.zoomProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeZoom}>Zoom</div>
+ <div className="ribbon-button" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? "#aedef8" : "" }} onClick={this.editZoomProgressivize}>Edit</div>
</div> */}
- <div className="ribbon-doubleButton" style={{ display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.scrollProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeScroll}>Scroll progressivize</div>
- <div className="ribbon-button" style={{ display: activeItem.scrollProgressivize ? "flex" : "none", backgroundColor: targetDoc.editScrollProgressivize ? "#aedef8" : "" }} onClick={this.editScrollProgressivize}>Edit</div>
+ <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
+ <div className="ribbon-button" style={{ backgroundColor: activeItem.scrollProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeScroll}>Scroll</div>
+ <div className="ribbon-button" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? "#aedef8" : "" }} onClick={this.editScrollProgressivize}>Edit</div>
</div>
</div>
<div className="ribbon-final-box" style={{ display: activeItem.zoomProgressivize || activeItem.scrollProgressivize || activeItem.presProgressivize || activeItem.textProgressivize ? "grid" : "none" }}>
Frames
<div className="ribbon-doubleButton">
<div className="ribbon-frameSelector">
- <div key="back" title="back frame" className="backKeyframe" onClick={e => { e.stopPropagation(); this.prevKeyframe(targetDoc); }}>
+ <div key="back" title="back frame" className="backKeyframe" onClick={e => { e.stopPropagation(); this.prevKeyframe(targetDoc, activeItem); }}>
<FontAwesomeIcon icon={"caret-left"} size={"lg"} />
</div>
- <div key="num" title="toggle view all" className="numKeyframe" style={{ backgroundColor: targetDoc.editing ? "#5a9edd" : "#5a9edd" }}
+ <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.editing ? "white" : "black", backgroundColor: targetDoc.editing ? "#5B9FDD" : "#AEDDF8" }}
onClick={action(() => targetDoc.editing = !targetDoc.editing)} >
{NumCast(targetDoc.currentFrame)}
</div>
- <div key="fwd" title="forward frame" className="fwdKeyframe" onClick={e => { e.stopPropagation(); this.nextKeyframe(targetDoc); }}>
+ <div key="fwd" title="forward frame" className="fwdKeyframe" onClick={e => { e.stopPropagation(); this.nextKeyframe(targetDoc, activeItem); }}>
<FontAwesomeIcon icon={"caret-right"} size={"lg"} />
</div>
</div>
<Tooltip title={<><div className="dash-tooltip">{"Last frame"}</div></>}><div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div></Tooltip>
</div>
- <div className="ribbon-button" style={{ height: 20, backgroundColor: "#5a9edd" }} onClick={() => console.log(" TODO: play frames")}>Play</div>
+ <div className="ribbon-button" style={{ height: 20, backgroundColor: "#AEDDF8" }} onClick={() => console.log(" TODO: play frames")}>Play</div>
</div>
</div>
</div>
@@ -1126,43 +1255,70 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
}
+ @undoBatch
+ @action
+ switchActive = (color: ColorState) => {
+ const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const val = String(color.hex);
+ targetDoc["pres-text-color"] = val;
+ return true;
+ }
+ @undoBatch
+ @action
+ switchPresented = (color: ColorState) => {
+ const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const val = String(color.hex);
+ targetDoc["pres-text-viewed-color"] = val;
+ return true;
+ }
+
+ @computed get activeColorPicker() {
+ const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ return !this.openActiveColorPicker ? (null) : <SketchPicker onChange={this.switchActive}
+ presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
+ '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
+ '#FFFFFF', '#f1efeb', 'transparent']}
+ color={StrCast(targetDoc["pres-text-color"])} />;
+ }
+
+ @computed get viewedColorPicker() {
+ const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ return !this.openViewedColorPicker ? (null) : <SketchPicker onChange={this.switchPresented}
+ presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
+ '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
+ '#FFFFFF', '#f1efeb', 'transparent']}
+ color={StrCast(targetDoc["pres-text-viewed-color"])} />;
+ }
+
turnOffEdit = () => {
this.childDocs.forEach((doc) => {
doc.editSnapZoomProgressivize = false;
doc.editZoomProgressivize = false;
doc.editScrollProgressivize = false;
const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
- targetDoc.editSnapZoomProgressivize = false;
- targetDoc.editZoomProgressivize = false;
- targetDoc.editScrollProgressivize = false;
- if (doc.type === DocumentType.WEB) {
- doc.presWebsite = doc.data;
+ if (targetDoc) {
+ targetDoc.editZoomProgressivize = false;
+ targetDoc.editScrollProgressivize = false;
}
});
}
//Toggle whether the user edits or not
@action
- editSnapZoomProgressivize = (e: React.MouseEvent) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- if (!targetDoc.editSnapZoomProgressivize) {
- targetDoc.editSnapZoomProgressivize = true;
- } else {
- targetDoc.editSnapZoomProgressivize = false;
- }
-
- }
-
- //Toggle whether the user edits or not
- @action
editZoomProgressivize = (e: React.MouseEvent) => {
const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
if (!targetDoc.editZoomProgressivize) {
+ if (!activeItem.zoomProgressivize) activeItem.zoomProgressivize = true; targetDoc.zoomProgressivize = true;
targetDoc.editZoomProgressivize = true;
+ activeItem.editZoomProgressivize = true;
} else {
targetDoc.editZoomProgressivize = false;
+ activeItem.editZoomProgressivize = false;
}
}
@@ -1172,6 +1328,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
if (!targetDoc.editScrollProgressivize) {
+ if (!targetDoc.scrollProgressivize) { targetDoc.scrollProgressivize = true; activeItem.scrollProgressivize = true; }
targetDoc.editScrollProgressivize = true;
} else {
targetDoc.editScrollProgressivize = false;
@@ -1185,8 +1342,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
activeItem.scrollProgressivize = !activeItem.scrollProgressivize;
const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- targetDoc.scrollProgressivize = !targetDoc.zoomProgressivize;
- CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc.currentFrame), true);
+ targetDoc.scrollProgressivize = !targetDoc.scrollProgressivize;
+ CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc.currentFrame));
if (targetDoc.editScrollProgressivize) {
targetDoc.editScrollProgressivize = false;
targetDoc.currentFrame = 0;
@@ -1202,52 +1359,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
activeItem.zoomProgressivize = !activeItem.zoomProgressivize;
const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
targetDoc.zoomProgressivize = !targetDoc.zoomProgressivize;
- CollectionFreeFormDocumentView.setupZoom(targetDoc, true);
- if (targetDoc.editZoomProgressivize) {
- targetDoc.editZoomProgressivize = false;
+ CollectionFreeFormDocumentView.setupZoom(activeItem, targetDoc);
+ if (activeItem.editZoomProgressivize) {
+ activeItem.editZoomProgressivize = false;
targetDoc.currentFrame = 0;
targetDoc.lastFrame = 0;
}
}
- //Progressivize Text nodes
- @action
- editTextProgressivize = (e: React.MouseEvent) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- targetDoc.currentFrame = targetDoc.lastFrame;
- if (targetDoc?.editTextProgressivize) {
- targetDoc.editTextProgressivize = false;
- } else {
- targetDoc.editTextProgressivize = true;
- }
- }
-
- @action
- progressivizeText = (e: React.MouseEvent) => {
- e.stopPropagation();
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- activeItem.presProgressivize = !activeItem.presProgressivize;
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
- targetDoc.presProgressivize = !targetDoc.presProgressivize;
- if (activeItem.presProgressivize) {
- targetDoc.currentFrame = 0;
- CollectionFreeFormDocumentView.setupKeyframes(docs, docs.length, true);
- targetDoc.lastFrame = docs.length - 1;
- }
- }
-
//Progressivize Child Docs
@action
editProgressivize = (e: React.MouseEvent) => {
const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
targetDoc.currentFrame = targetDoc.lastFrame;
- if (targetDoc?.editProgressivize) {
- targetDoc.editProgressivize = false;
- } else {
+ if (!targetDoc.editProgressivize) {
+ if (!activeItem.presProgressivize) { activeItem.presProgressivize = true; targetDoc.presProgressivize = true; }
targetDoc.editProgressivize = true;
+ } else {
+ targetDoc.editProgressivize = false;
}
}
@@ -1258,20 +1388,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
if (!activeItem.presProgressivize) {
+ targetDoc.editing = false;
activeItem.presProgressivize = true;
targetDoc.presProgressivize = true;
targetDoc.currentFrame = 0;
- CollectionFreeFormDocumentView.setupKeyframes(docs, docs.length, true);
+ docs.forEach((doc, i) => CollectionFreeFormDocumentView.setupKeyframes([doc], i, true));
targetDoc.lastFrame = docs.length - 1;
} else {
targetDoc.editProgressivize = false;
activeItem.presProgressivize = false;
targetDoc.presProgressivize = false;
- // docs.forEach((doc, index) => {
- // doc.appearFrame = 0;
- // });
targetDoc.currentFrame = 0;
targetDoc.lastFrame = 0;
+ targetDoc.editing = true;
}
}
@@ -1308,236 +1437,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
else doc.displayMovement = true;
}
- private _isDraggingTL = false;
- private _isDraggingTR = false;
- private _isDraggingBR = false;
- private _isDraggingBL = false;
- private _isDragging = false;
- // private _drag = "";
-
- // onPointerDown = (e: React.PointerEvent): void => {
- // e.stopPropagation();
- // e.preventDefault();
- // if (e.button === 0) {
- // this._drag = e.currentTarget.id;
- // console.log(this._drag);
- // }
- // document.removeEventListener("pointermove", this.onPointerMove);
- // document.addEventListener("pointermove", this.onPointerMove);
- // document.removeEventListener("pointerup", this.onPointerUp);
- // document.addEventListener("pointerup", this.onPointerUp);
- // }
-
-
- //Adds event listener so knows pointer is down and moving
- onPointerMid = (e: React.PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- this._isDragging = true;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointerup", this.onPointerUp);
- }
-
- //Adds event listener so knows pointer is down and moving
- onPointerBR = (e: React.PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- this._isDraggingBR = true;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointerup", this.onPointerUp);
- }
-
- //Adds event listener so knows pointer is down and moving
- onPointerBL = (e: React.PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- this._isDraggingBL = true;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointerup", this.onPointerUp);
- }
-
- //Adds event listener so knows pointer is down and moving
- onPointerTR = (e: React.PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- this._isDraggingTR = true;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointerup", this.onPointerUp);
- }
-
- //Adds event listener so knows pointer is down and moving
- onPointerTL = (e: React.PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- this._isDraggingTL = true;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointerup", this.onPointerUp);
- }
-
- //Removes all event listeners
- onPointerUp = (e: PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- this._isDraggingTL = false;
- this._isDraggingTR = false;
- this._isDraggingBL = false;
- this._isDraggingBR = false;
- this._isDragging = false;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- }
-
- //Adjusts the value in NodeStore
- onPointerMove = (e: PointerEvent): void => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
- const tagDocView = DocumentManager.Instance.getDocumentView(targetDoc);
- e.stopPropagation();
- e.preventDefault();
- const doc = document.getElementById('resizable');
- if (doc && tagDocView) {
-
- const scale2 = tagDocView.childScaling();
- const scale3 = tagDocView.props.ScreenToLocalTransform().Scale;
- const scale = NumCast(targetDoc._viewScale);
- console.log("scale: " + NumCast(targetDoc._viewScale));
- let height = doc.offsetHeight;
- let width = doc.offsetWidth;
- let top = doc.offsetTop;
- let left = doc.offsetLeft;
- // const newHeightB = height += (e.movementY * NumCast(targetDoc._viewScale));
- // const newHeightT = height -= (e.movementY * NumCast(targetDoc._viewScale));
- // const newWidthR = width += (e.movementX * NumCast(targetDoc._viewScale));
- // const newWidthL = width -= (e.movementX * NumCast(targetDoc._viewScale));
- // const newLeft = left += (e.movementX * NumCast(targetDoc._viewScale));
- // const newTop = top += (e.movementY * NumCast(targetDoc._viewScale));
- // switch (this._drag) {
- // case "": break;
- // case "resizer-br":
- // doc.style.height = newHeightB + 'px';
- // doc.style.width = newWidthR + 'px';
- // break;
- // case "resizer-bl":
- // doc.style.height = newHeightB + 'px';
- // doc.style.width = newWidthL + 'px';
- // doc.style.left = newLeft + 'px';
- // break;
- // case "resizer-tr":
- // doc.style.width = newWidthR + 'px';
- // doc.style.height = newHeightT + 'px';
- // doc.style.top = newTop + 'px';
- // case "resizer-tl":
- // doc.style.width = newWidthL + 'px';
- // doc.style.height = newHeightT + 'px';
- // doc.style.top = newTop + 'px';
- // doc.style.left = newLeft + 'px';
- // case "resizable":
- // doc.style.top = newTop + 'px';
- // doc.style.left = newLeft + 'px';
- // }
- //Bottom right
- if (this._isDraggingBR) {
- const newHeight = height += (e.movementY * scale);
- doc.style.height = newHeight + 'px';
- const newWidth = width += (e.movementX * scale);
- doc.style.width = newWidth + 'px';
- // Bottom left
- } else if (this._isDraggingBL) {
- const newHeight = height += (e.movementY * scale);
- doc.style.height = newHeight + 'px';
- const newWidth = width -= (e.movementX * scale);
- doc.style.width = newWidth + 'px';
- const newLeft = left += (e.movementX * scale);
- doc.style.left = newLeft + 'px';
- // Top right
- } else if (this._isDraggingTR) {
- const newWidth = width += (e.movementX * scale);
- doc.style.width = newWidth + 'px';
- const newHeight = height -= (e.movementY * scale);
- doc.style.height = newHeight + 'px';
- const newTop = top += (e.movementY * scale);
- doc.style.top = newTop + 'px';
- // Top left
- } else if (this._isDraggingTL) {
- const newWidth = width -= (e.movementX * scale);
- doc.style.width = newWidth + 'px';
- const newHeight = height -= (e.movementY * scale);
- doc.style.height = newHeight + 'px';
- const newTop = top += (e.movementY * scale);
- doc.style.top = newTop + 'px';
- const newLeft = left += (e.movementX * scale);
- doc.style.left = newLeft + 'px';
- } else if (this._isDragging) {
- const newTop = top += (e.movementY * scale);
- doc.style.top = newTop + 'px';
- const newLeft = left += (e.movementX * scale);
- doc.style.left = newLeft + 'px';
- }
- this.updateList(targetDoc, targetDoc["viewfinder-width-indexed"], width);
- this.updateList(targetDoc, targetDoc["viewfinder-height-indexed"], height);
- this.updateList(targetDoc, targetDoc["viewfinder-top-indexed"], top);
- this.updateList(targetDoc, targetDoc["viewfinder-left-indexed"], left);
- }
- }
-
@action
checkList = (doc: Doc, list: any): number => {
const x: List<number> = list;
if (x && x.length >= NumCast(doc.currentFrame) + 1) {
return x[NumCast(doc.currentFrame)];
- } else {
+ } else if (x) {
x.length = NumCast(doc.currentFrame) + 1;
x[NumCast(doc.currentFrame)] = x[NumCast(doc.currentFrame) - 1];
return x[NumCast(doc.currentFrame)];
- }
-
- }
-
- @action
- updateList = (doc: Doc, list: any, val: number) => {
- const x: List<number> = list;
- if (x && x.length >= NumCast(doc.currentFrame) + 1) {
- x[NumCast(doc.currentFrame)] = val;
- list = x;
- } else {
- x.length = NumCast(doc.currentFrame) + 1;
- x[NumCast(doc.currentFrame)] = val;
- list = x;
- }
- }
-
- // scale: NumCast(targetDoc._viewScale),
- @computed get zoomProgressivizeContainer() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
- if (targetDoc) {
- const vfLeft: number = this.checkList(targetDoc, targetDoc["viewfinder-left-indexed"]);
- const vfWidth: number = this.checkList(targetDoc, targetDoc["viewfinder-width-indexed"]);
- const vfTop: number = this.checkList(targetDoc, targetDoc["viewfinder-top-indexed"]);
- const vfHeight: number = this.checkList(targetDoc, targetDoc["viewfinder-height-indexed"]);
- return (
- <>
- {!targetDoc.editZoomProgressivize ? (null) : <div id="resizable" className="resizable" onPointerDown={this.onPointerMid} style={{ width: vfWidth, height: vfHeight, top: vfTop, left: vfLeft, position: 'absolute' }}>
- <div className='resizers'>
- <div id="resizer-tl" className='resizer top-left' onPointerDown={this.onPointerTL}></div>
- <div id="resizer-tr" className='resizer top-right' onPointerDown={this.onPointerTR}></div>
- <div id="resizer-bl" className='resizer bottom-left' onPointerDown={this.onPointerBL}></div>
- <div id="resizer-br" className='resizer bottom-right' onPointerDown={this.onPointerBR}></div>
- </div>
- </div>}
- </>
- );
- }
+ } else return 100;
}
@computed get progressivizeChildDocs() {
@@ -1620,8 +1529,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
return width;
}
+ @action
+ toggleProperties = () => {
+ if (CurrentUserUtils.propertiesWidth > 0) {
+ CurrentUserUtils.propertiesWidth = 0;
+ } else {
+ CurrentUserUtils.propertiesWidth = 250;
+ }
+ }
+
@computed get toolbar() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left";
+ const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Presentation Panel" : "Open Presentation Panel";
return (
<div id="toolbarContainer" className={'presBox-toolbar'} style={{ display: this.layoutDoc.presStatus === "edit" ? "inline-flex" : "none" }}>
<Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}>
@@ -1634,12 +1553,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<FontAwesomeIcon icon={"exchange-alt"} />
</div>
</Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{this.expandBoolean ? "Minimize all" : "Expand all"}</div></>}>
- <div style={{ opacity: this.childDocs.length > 0 ? 1 : 0.3 }} className={`toolbar-button ${this.expandBoolean ? "active" : ""}`} onClick={() => { if (this.childDocs.length > 0) this.toggleExpand(); this.childDocs.forEach((doc, ind) => { if (this.expandBoolean) doc.presExpandInlineButton = true; else doc.presExpandInlineButton = false; }); }}>
+ <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.expandBoolean ? "Minimize all" : "Expand all"}</div></>}>
+ <div className={`toolbar-button ${this.rootDoc.expandBoolean ? "active" : ""}`} onClick={this.toggleExpandMode}>
<FontAwesomeIcon icon={"eye"} />
</div>
</Tooltip>
<div className="toolbar-divider" />
+ <Tooltip title={<><div className="dash-tooltip">{propTitle}</div></>}>
+ <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
+ <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? '#AEDDF8' : 'white' }} />
+ </div>
+ </Tooltip>
</div>
);
}
@@ -1697,18 +1621,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get playButtons() {
// Case 1: There are still other frames and should go through all frames before going to next slide
- return (<div className="miniPresOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}>
- <div className="miniPres-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
- <div className="miniPres-button" onClick={() => this.startAutoPres(this.itemIndex)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div>
- <div className="miniPres-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
- <div className="miniPres-divider"></div>
- <div className="miniPres-button-text" style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}>
+ return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}>
+ <div className="presPanel-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
+ <div className="presPanel-button" onClick={() => this.startAutoPres(this.itemIndex)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div>
+ <div className="presPanel-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
+ <div className="presPanel-divider"></div>
+ <div className="presPanel-button-text" style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}>
Slide {this.itemIndex + 1} / {this.childDocs.length}
{this.playButtonFrames}
</div>
- <div className="miniPres-divider"></div>
- {this.props.PanelWidth() > 250 ? <div className="miniPres-button-text" onClick={() => this.layoutDoc.presStatus = "edit"}>EXIT</div>
- : <div className="miniPres-button" onClick={() => this.layoutDoc.presStatus = "edit"}>
+ <div className="presPanel-divider"></div>
+ {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={() => this.layoutDoc.presStatus = "edit"}>EXIT</div>
+ : <div className="presPanel-button" onClick={() => this.layoutDoc.presStatus = "edit"}>
<FontAwesomeIcon icon={"times"} />
</div>}
</div>);
@@ -1720,28 +1644,44 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// needed to ensure that the childDocs are loaded for looking up fields
this.childDocs.slice();
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
- return <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} >
- {this.topPanel}
- {this.toolbar}
- {this.newDocumentToolbarDropdown}
- <div className="presBox-listCont">
- {mode !== CollectionViewType.Invalid ?
- <CollectionView {...this.props}
- ContainingCollectionDoc={this.props.Document}
- PanelWidth={this.props.PanelWidth}
- PanelHeight={this.panelHeight}
- moveDocument={returnFalse}
- childOpacity={returnOne}
- childLayoutTemplate={this.childLayoutTemplate}
- filterAddDocument={this.addDocumentFilter}
- removeDocument={returnFalse}
- dontRegisterView={true}
- focus={this.selectElement}
- ScreenToLocalTransform={this.getTransform} />
- : (null)
- }
+ return this.layoutDoc.inOverlay ?
+ <div className="miniPres" style={{ width: 250, height: 35, background: '#323232', top: 0, zIndex: 3000000 }}>
+ {<div className="miniPresOverlay">
+ <div className="miniPres-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
+ <div className="miniPres-button" onClick={() => this.startAutoPres(this.itemIndex)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div>
+ <div className="miniPres-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
+ <div className="miniPres-divider"></div>
+ <div className="miniPres-button-text">
+ Slide {this.itemIndex + 1} / {this.childDocs.length}
+ {this.playButtonFrames}
+ </div>
+ <div className="miniPres-divider"></div>
+ <div className="miniPres-button-text" onClick={this.updateMinimize}>EXIT</div>
+ </div>}
</div>
- </div>;
+ :
+ <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} >
+ {this.topPanel}
+ {this.toolbar}
+ {this.newDocumentToolbarDropdown}
+ <div className="presBox-listCont">
+ {mode !== CollectionViewType.Invalid ?
+ <CollectionView {...this.props}
+ ContainingCollectionDoc={this.props.Document}
+ PanelWidth={this.props.PanelWidth}
+ PanelHeight={this.panelHeight}
+ moveDocument={returnFalse}
+ childOpacity={returnOne}
+ childLayoutTemplate={this.childLayoutTemplate}
+ filterAddDocument={this.addDocumentFilter}
+ removeDocument={returnFalse}
+ dontRegisterView={true}
+ focus={this.selectElement}
+ ScreenToLocalTransform={this.getTransform} />
+ : (null)
+ }
+ </div>
+ </div>;
}
}
Scripting.addGlobal(function lookupPresBoxField(container: Doc, field: string, data: Doc) {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 3283f568a..1393e7868 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
import { observer } from "mobx-react";
import { Dictionary } from "typescript-collections";
import * as WebRequest from 'web-request';
-import { Doc, DocListCast, Opt, AclAddonly, AclEdit, AclAdmin } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt, AclAddonly, AclEdit, AclAdmin, DataSym } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { HtmlField } from "../../../fields/HtmlField";
@@ -454,9 +454,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
view = <span className="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
} else if (field instanceof WebField) {
const url = this.layoutDoc.UseCors ? Utils.CorsProxy(field.url.href) : field.url.href;
- view = <iframe className="webBox-iframe" enable-annotation={true} ref={this._iframeRef} src={url} onLoad={this.iframeLoaded} />;
+ view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={this._iframeRef} src={url} onLoad={this.iframeLoaded}
+ // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
+ sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
} else {
- view = <iframe className="webBox-iframe" enable-annotation={true} ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} />;
+ view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} />;
}
return view;
}
@@ -535,7 +537,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
@action
highlight = (color: string) => {
// creates annotation documents for current highlights
- const effectiveAcl = GetEffectiveAcl(this.props.Document);
+ const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) ? this.makeAnnotationDocument(color) : undefined;
annotationDoc && this.addDocument?.(annotationDoc);
return annotationDoc ?? undefined;
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 212da3f3d..145ee8c2e 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -114,7 +114,7 @@ export class DashDocView extends React.Component<IDashDocView> {
}
/*endregion*/
- componentWillMount = () => {
+ componentWillUnmount = () => {
this._reactionDisposer?.();
}
@@ -254,7 +254,7 @@ export class DashDocView extends React.Component<IDashDocView> {
whenActiveChanged={returnFalse}
bringToFront={emptyFunction}
dontRegisterView={false}
- docFilters={this.props.tbox?.props.docFilters||returnEmptyFilter}
+ docFilters={this.props.tbox?.props.docFilters || returnEmptyFilter}
ContainingCollectionView={this._textBox.props.ContainingCollectionView}
ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
ContentScaling={this.contentScaling}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 8ae71c035..924079096 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -183,7 +183,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
}
if (container) {
const alias = Doc.MakeAlias(container.props.Document);
- alias.viewType = CollectionViewType.Time;
+ alias._viewType = CollectionViewType.Time;
let list = Cast(alias._columnHeaders, listSpec(SchemaHeaderField));
if (!list) {
alias._columnHeaders = list = new List<SchemaHeaderField>();
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 7d4bd5dd3..6b4115e53 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -23,7 +23,7 @@ import { PrefetchProxy } from '../../../../fields/Proxy';
import { RichTextField } from "../../../../fields/RichTextField";
import { RichTextUtils } from '../../../../fields/RichTextUtils';
import { createSchema, makeInterface } from "../../../../fields/Schema";
-import { Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../fields/Types";
+import { Cast, DateCast, NumCast, StrCast, ScriptCast, BoolCast } from "../../../../fields/Types";
import { TraceMobx, OVERRIDE_ACL, GetEffectiveAcl } from '../../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
@@ -34,7 +34,7 @@ import { DictationManager } from '../../../util/DictationManager';
import { DragManager } from "../../../util/DragManager";
import { makeTemplate } from '../../../util/DropConverter';
import buildKeymap, { updateBullets } from "./ProsemirrorExampleTransfer";
-import RichTextMenu from './RichTextMenu';
+import RichTextMenu, { RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from "./RichTextRules";
//import { DashDocView } from "./DashDocView";
@@ -304,18 +304,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
// for inserting timestamps
insertTime = () => {
+ let audioState;
if (this._first) {
- this._first = false;
DocListCast(this.dataDoc.links).map((l, i) => {
let la1 = l.anchor1 as Doc;
let la2 = l.anchor2 as Doc;
this._linkTime = NumCast(l.anchor2_timecode);
+ audioState = la2.audioState;
if (Doc.AreProtosEqual(la2, this.dataDoc)) {
la1 = l.anchor2 as Doc;
la2 = l.anchor1 as Doc;
this._linkTime = NumCast(l.anchor1_timecode);
+ audioState = la1.audioState;
}
-
});
}
this._currentTime = Date.now();
@@ -336,7 +337,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
}
- if (time) {
+ if (time && audioState === "recording") {
let value = "";
this._break = false;
value = this.layoutDoc._timeStampOnEnter ? "[" + time + "] " : "\n" + "[" + time + "] ";
@@ -373,9 +374,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._editorView.dispatch(tr.addMark(flattened[lastSel].from, flattened[lastSel].to, link));
}
}
- public highlightSearchTerms = (terms: string[], alt: boolean) => {
+ 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));
@@ -383,30 +385,29 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
let tr = this._editorView.state.tr;
const flattened: TextSelection[] = [];
res.map(r => r.map(h => flattened.push(h)));
-
-
- const lastSel = Math.min(flattened.length - 1, this._searchIndex);
- flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark));
- this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
- this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView());
- if (alt === 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;
- }
-
+ 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;
+ }
+ }
}
- const index = this._searchIndex;
- Doc.GetProto(this.dataDoc).searchIndex = index;
+ const lastSel = Math.min(flattened.length - 1, this._searchIndex);
+ flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark));
+ flattened[lastSel] && this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView());
}
}
@@ -508,10 +509,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (node.isTextblock) {
let index = 0, foundAt;
const ep = this.getNodeEndpoints(pm.state.doc, node);
- while (ep && (foundAt = node.textContent.slice(index).search(RegExp(find, "i"))) > -1) {
- const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
- ret.push(sel);
- index = index + foundAt + find.length;
+ const regexp = find.replace("*", "");
+ if (regexp) {
+ while (ep && (foundAt = node.textContent.slice(index).search(regexp)) > -1) {
+ const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
+ ret.push(sel);
+ index = index + foundAt + find.length;
+ }
}
} else {
node.content.forEach((child, i) => ret = ret.concat(this.findInNode(pm, child, find)));
@@ -646,7 +650,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const options = cm.findByDescription("Options...");
const optionItems = options && "subitems" in options ? options.subitems : [];
- !Doc.UserDoc().noviceMode && optionItems.push({ description: this.Document._singleLine ? "Make Single Line" : "Make Multi Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" });
+ !Doc.UserDoc().noviceMode && optionItems.push({ description: !this.Document._singleLine ? "Make Single Line" : "Make Multi Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" });
optionItems.push({ description: `${this.Document._autoHeight ? "Lock" : "Auto"} Height`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
optionItems.push({ description: `${!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Lock" : "Unlock"} Aspect`, event: this.toggleNativeDimensions, icon: "snowflake" });
!options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
@@ -905,12 +909,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.setupEditor(this.config, this.props.fieldKey);
- this._disposers.searchAlt = reaction(() => this.rootDoc.searchMatchAlt,
- search => search ? this.highlightSearchTerms([Doc.SearchQuery()], false) : this.unhighlightSearchTerms(),
- { fireImmediately: true });
this._disposers.search = reaction(() => this.rootDoc.searchMatch,
- search => search ? this.highlightSearchTerms([Doc.SearchQuery()], true) : this.unhighlightSearchTerms(),
- { fireImmediately: this.rootDoc.searchMatch ? true : false });
+ search => search !== undefined ? this.highlightSearchTerms([Doc.SearchQuery()], BoolCast(search)) : this.unhighlightSearchTerms(),
+ { fireImmediately: this.rootDoc.searchMatch !== undefined ? true : false });
this._disposers.record = reaction(() => this._recording,
() => {
@@ -1304,7 +1305,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
// jump rich text menu to this textbox
const bounds = this._ref.current?.getBoundingClientRect();
- if (bounds && this.layoutDoc._chromeStatus !== "disabled") {
+ if (bounds && this.layoutDoc._chromeStatus !== "disabled" && RichTextMenu.Instance) {
const x = Math.min(Math.max(bounds.left, 0), window.innerWidth - RichTextMenu.Instance.width);
let y = Math.min(Math.max(0, bounds.top - RichTextMenu.Instance.height - 50), window.innerHeight - RichTextMenu.Instance.height);
if (coords && coords.left > x && coords.left < x + RichTextMenu.Instance.width && coords.top > y && coords.top < y + RichTextMenu.Instance.height + 50) {
@@ -1408,11 +1409,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
+ menuPlugin: any;
+
richTextMenuPlugin() {
+ const self = this;
return new Plugin({
view(newView) {
- RichTextMenu.Instance?.changeView(newView);
- return RichTextMenu.Instance;
+ self.props.isSelected(true) && (RichTextMenu.Instance.view = newView);
+ return self.menuPlugin = new RichTextMenuPlugin({ editorProps: this.props });
}
});
}
@@ -1429,6 +1433,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
return wasUndoing;
}
+ public static LiveTextUndo: UndoManager.Batch | undefined;
public static HadSelection: boolean = false;
onBlur = (e: any) => {
FormattedTextBox.HadSelection = window.getSelection()?.toString() !== "";
@@ -1436,6 +1441,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.endUndoTypingBatch();
this.doLinkOnDeselect();
+ FormattedTextBox.LiveTextUndo?.end();
+ FormattedTextBox.LiveTextUndo = undefined;
// move the richtextmenu offscreen
//if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide();
}
@@ -1522,7 +1529,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const scale = this.props.hideOnLeave ? 1 : this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground;
- setTimeout(() => this._editorView && RichTextMenu.Instance.updateFromDash(this._editorView, undefined, this.props), this.props.isSelected() ? 10 : 0); // need to make sure that we update a text box that is selected after updating the one that was deselected
+ this.props.isSelected() && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props), 0); // need to make sure that we update a text box that is selected after updating the one that was deselected
if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) {
setTimeout(() => FormattedTextBoxComment.Hide(), 0);
}
@@ -1578,6 +1585,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
onScroll={this.onscrolled} onDrop={this.ondrop} >
<div className={`formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
style={{
+ overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`,
pointerEvents: !this.props.active() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined
}}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 459632ec8..96628949a 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -16,7 +16,7 @@ import { unimplementedFunction, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { LinkManager } from "../../../util/LinkManager";
import { SelectionManager } from "../../../util/SelectionManager";
-import AntimodeMenu from "../../AntimodeMenu";
+import AntimodeMenu, { AntimodeMenuProps } from "../../AntimodeMenu";
import { FieldViewProps } from "../FieldView";
import { FormattedTextBox, FormattedTextBoxProps } from "./FormattedTextBox";
import { updateBullets } from "./ProsemirrorExampleTransfer";
@@ -31,11 +31,11 @@ library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSup
@observer
-export default class RichTextMenu extends AntimodeMenu {
+export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: RichTextMenu;
public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
- private view?: EditorView;
+ public view?: EditorView;
public editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
public _brushMap: Map<string, Set<Mark>> = new Map();
@@ -156,19 +156,8 @@ export default class RichTextMenu extends AntimodeMenu {
public delayHide = () => this._delayHide = true;
@action
- changeView(view: EditorView) {
- if ((view as any)?.TextView?.props.isSelected(true)) {
- this.view = view;
- }
- }
-
- update(view: EditorView, lastState: EditorState | undefined) {
- this.updateFromDash(view, lastState, this.editorProps);
- }
-
- @action
- public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) {
- if (!view || !(view as any).TextView?.props.isSelected(true)) {
+ public updateMenu(view: EditorView, lastState: EditorState | undefined, props: any) {
+ if (!view || !(view as any).TextView?.props.isSelected(true) || !view.hasFocus()) {
return;
}
this.view = view;
@@ -196,8 +185,7 @@ export default class RichTextMenu extends AntimodeMenu {
this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length === 1 ? String(activeHighlights[0]) : "...";
// update link in current selection
- const targetTitle = await this.getTextLinkTargetTitle();
- this.setCurrentLink(targetTitle);
+ this.getTextLinkTargetTitle().then(targetTitle => this.setCurrentLink(targetTitle));
}
setMark = (mark: Mark, state: EditorState<any>, dispatch: any, dontToggle: boolean = false) => {
@@ -265,7 +253,9 @@ export default class RichTextMenu extends AntimodeMenu {
const pos = this.view.state.selection.$from;
const ref_node = this.reference_node(pos);
if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
- ref_node.marks.forEach(m => {
+ const marks = Array.from(ref_node.marks);
+ marks.push(...(this.view.state.storedMarks as any));
+ marks.forEach(m => {
m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color);
m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "pt");
@@ -440,14 +430,20 @@ export default class RichTextMenu extends AntimodeMenu {
if ((this.view?.state.selection.$from.pos || 0) < 2) {
this.TextView.layoutDoc._fontSize = mark.attrs.fontSize;
}
- this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }), view.state, view.dispatch, true);
+ const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize });
+ this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
+ view.focus();
+ this.updateMenu(view, undefined, this.props);
}
changeFontFamily = (mark: Mark, view: EditorView) => {
if ((this.view?.state.selection.$from.pos || 0) < 2) {
this.TextView.layoutDoc._fontFamily = mark.attrs.family;
}
- this.setMark(view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family }), view.state, view.dispatch, true);
+ const fmark = view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family });
+ this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
+ view.focus();
+ this.updateMenu(view, undefined, this.props);
}
// TODO: remove doesn't work
@@ -483,6 +479,8 @@ export default class RichTextMenu extends AntimodeMenu {
this.view.dispatch(tx3);
}
}
+ this.view.focus();
+ this.updateMenu(this.view, undefined, this.props);
}
insertSummarizer(state: EditorState<any>, dispatch: any) {
@@ -687,16 +685,22 @@ export default class RichTextMenu extends AntimodeMenu {
e.preventDefault();
e.stopPropagation();
self.TextView.endUndoTypingBatch();
- UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
- self.TextView.EditorView!.focus();
+ if (self.view) {
+ UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
+ self.view.focus();
+ self.updateMenu(self.view, undefined, self.props);
+ }
}
function changeColor(e: React.PointerEvent, color: string) {
e.preventDefault();
e.stopPropagation();
self.setActiveColor(color);
self.TextView.endUndoTypingBatch();
- UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
- self.TextView.EditorView!.focus();
+ if (self.view) {
+ UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
+ self.view.focus();
+ self.updateMenu(self.view, undefined, self.props);
+ }
}
// onPointerDown={onColorClick}
@@ -985,7 +989,7 @@ export default class RichTextMenu extends AntimodeMenu {
{[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => this.activeFontSize = val)),
this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => this.activeFontFamily = val)),
<div className="richTextMenu-divider" key="divider 4" />,
- this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", action((val: string) => this.activeListType = val)),
+ this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule),
@@ -1017,6 +1021,7 @@ interface ButtonDropdownProps {
dropdownContent: JSX.Element;
openDropdownOnButton?: boolean;
link?: boolean;
+ pdf?: boolean;
}
@observer
@@ -1056,15 +1061,33 @@ export class ButtonDropdown extends React.Component<ButtonDropdownProps> {
}, 0);
}
+
render() {
return (
<div className="button-dropdown-wrapper" ref={node => this.ref = node}>
- <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.onDropdownClick}>
- {this.props.button}
- <div style={{ marginTop: "-8.5" }}><FontAwesomeIcon icon="caret-down" size="sm" /></div>
- </div>
+ {!this.props.pdf ?
+ <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.onDropdownClick}>
+ {this.props.button}
+ <div style={{ marginTop: "-8.5" }}><FontAwesomeIcon icon="caret-down" size="sm" /></div>
+ </div>
+ :
+ <>
+ {this.props.button}
+ <button className="dropdown-button antimodeMenu-button" key="antimodebutton" onPointerDown={this.onDropdownClick}>
+ <FontAwesomeIcon icon="caret-down" size="sm" />
+ </button>
+ </>}
{this.showDropdown ? this.props.dropdownContent : (null)}
</div>
);
}
+}
+
+
+interface RichTextMenuPluginProps {
+ editorProps: any;
+}
+export class RichTextMenuPlugin extends React.Component<RichTextMenuPluginProps> {
+ render() { return null; }
+ update(view: EditorView, lastState: EditorState | undefined) { RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps); }
} \ No newline at end of file
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 7bea8d01b..0f7b0a688 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -4,14 +4,14 @@ import { observable, action, computed, } from "mobx";
import { observer } from "mobx-react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { unimplementedFunction, returnFalse, Utils } from "../../../Utils";
-import AntimodeMenu from "../AntimodeMenu";
+import AntimodeMenu, { AntimodeMenuProps } from "../AntimodeMenu";
import { Doc, Opt } from "../../../fields/Doc";
import { ColorState } from "react-color";
import { ButtonDropdown } from "../nodes/formattedText/RichTextMenu";
@observer
-export default class PDFMenu extends AntimodeMenu {
+export default class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: PDFMenu;
private _commentCont = React.createRef<HTMLButtonElement>();
@@ -112,7 +112,7 @@ export default class PDFMenu extends AntimodeMenu {
</div>
</div>;
return (
- <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} />
+ <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} />
);
}
@@ -154,7 +154,7 @@ export default class PDFMenu extends AntimodeMenu {
const buttons = this.Status === "pdf" ?
[
this.highlighter,
- <button key="2" className="antimodeMenu-button" title="Drag to Annotate" ref={this._commentCont} onPointerDown={this.pointerDown}>
+ <button key="2" className="antimodeMenu-button annotate" title="Drag to Annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: "grab" }}>
<FontAwesomeIcon icon="comment-alt" size="lg" /></button>,
] : [
<button key="5" className="antimodeMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index cfa9a1844..0916e8b0c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -4,7 +4,7 @@ const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
import { Dictionary } from "typescript-collections";
-import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym, AclAddonly, AclEdit, AclAdmin } from "../../../fields/Doc";
+import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym, AclAddonly, AclEdit, AclAdmin, DataSym } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { InkTool } from "../../../fields/InkField";
@@ -152,7 +152,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
this._mainCont.current && (this._mainCont.current.scrollTop = this.layoutDoc._scrollTop || 0);
this._searchReactionDisposer = reaction(() => this.Document.searchMatch,
m => {
- if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), true);
+ if (m !== undefined) (this._lastSearch = true) && this.search(Doc.SearchQuery(), true);
else !(this._lastSearch = false) && setTimeout(() => !this._lastSearch && this.search("", false, true), 200);
}, { fireImmediately: true });
@@ -313,7 +313,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
annoDocs.push(annoDoc);
anno.remove();
(annoDoc.y !== undefined) && (minY = Math.min(NumCast(annoDoc.y), minY));
- (annoDoc.x !== undefined) && (maxX = Math.max(NumCast(annoDoc.x) + NumCast(annoDoc.width), maxX));
+ (annoDoc.x !== undefined) && (maxX = Math.max(NumCast(annoDoc.x) + NumCast(annoDoc._width), maxX));
}));
mainAnnoDocProto.y = Math.max(minY, 0);
@@ -337,7 +337,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
nextAnnotation = () => {
this.Index = Math.min(this.Index + 1, this.allAnnotations.length - 1);
this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]);
- this.Document.searchIndex = this.Index;
}
@@ -400,10 +399,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@action
search = (searchString: string, fwd: boolean, clear: boolean = false) => {
if (clear) {
- this._pdfViewer.findController.executeCommand('reset', { query: "" });
+ this._pdfViewer?.findController.executeCommand('reset', { query: "" });
} else if (!searchString) {
fwd ? this.nextAnnotation() : this.prevAnnotation();
- } else if (this._pdfViewer.pageViewsReady) {
+ } else if (this._pdfViewer?.pageViewsReady) {
this._pdfViewer.findController.executeCommand('findagain', {
caseSensitive: false,
findPrevious: !fwd,
@@ -411,7 +410,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
phraseSearch: true,
query: searchString
});
- this.Document.searchIndex = this.Index;
}
else if (this._mainCont.current) {
const executeFind = () => {
@@ -425,7 +423,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
};
this._mainCont.current.addEventListener("pagesloaded", executeFind);
this._mainCont.current.addEventListener("pagerendered", executeFind);
- this.Document.searchIndex = this.Index;
}
}
@@ -576,7 +573,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@action
highlight = (color: string) => {
// creates annotation documents for current highlights
- const effectiveAcl = GetEffectiveAcl(this.props.Document);
+ const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color);
annotationDoc && this.addDocument?.(annotationDoc);
return annotationDoc as Doc ?? undefined;
diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/presentationview/PresElementBox.scss
index 1e776384a..6ee190b82 100644
--- a/src/client/views/presentationview/PresElementBox.scss
+++ b/src/client/views/presentationview/PresElementBox.scss
@@ -3,6 +3,7 @@ $dark-blue: #5B9FDD;
$light-background: #ececec;
.presElementBox-item {
+ cursor: grab;
display: grid;
grid-template-columns: max-content max-content max-content max-content;
background-color: #d5dce2;
@@ -161,6 +162,7 @@ $light-background: #ececec;
}
.presElementBox-closeIcon {
+ cursor: pointer;
position: absolute;
border-radius: 100%;
z-index: 300;
@@ -177,6 +179,7 @@ $light-background: #ececec;
}
.presElementBox-expand {
+ cursor: pointer;
position: absolute;
border-radius: 100%;
z-index: 300;
@@ -193,6 +196,7 @@ $light-background: #ececec;
}
.presElementBox-expand-selected {
+ cursor: pointer;
position: absolute;
border-radius: 100%;
right: 3px;
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index a6dbb76ef..a25a8ee33 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -19,6 +19,7 @@ import { PresBox } from "../nodes/PresBox";
import { DocumentType } from "../../documents/DocumentTypes";
import { Tooltip } from "@material-ui/core";
import { DragManager } from "../../util/DragManager";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
export const presSchema = createSchema({
presentationTargetDoc: Doc,
@@ -59,111 +60,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
}
/**
- * The function that is called on click to turn Hiding document till press option on/off.
- * It also sets the beginning and end opacitys.
- */
- @action
- onHideDocumentUntilPressClick = (e: React.MouseEvent) => {
- e.stopPropagation();
- this.rootDoc.presHideTillShownButton = !this.rootDoc.presHideTillShownButton;
- if (!this.rootDoc.presHideTillShownButton) {
- if (this.indexInPres >= this.itemIndex && this.targetDoc) {
- this.targetDoc.opacity = 1;
- }
- } else {
- if (this.presStatus !== "edit" && this.indexInPres > this.itemIndex && this.targetDoc) {
- this.targetDoc.opacity = 0;
- }
- }
- }
-
- /**
- * The function that is called on click to turn Hiding document after presented option on/off.
- * It also makes sure that the option swithches from fade-after to this one, since both
- * can't coexist.
- */
- @action
- onHideDocumentAfterPresentedClick = (e: React.MouseEvent) => {
- e.stopPropagation();
- this.rootDoc.presHideAfterButton = !this.rootDoc.presHideAfterButton;
- if (!this.rootDoc.presHideAfterButton) {
- if (this.indexInPres <= this.itemIndex && this.targetDoc) {
- this.targetDoc.opacity = 1;
- }
- } else {
- if (this.rootDoc.presFadeButton) this.rootDoc.presFadeButton = false;
- if (this.presStatus !== "edit" && this.indexInPres < this.itemIndex && this.targetDoc) {
- this.targetDoc.opacity = 0;
- }
- }
- }
-
- @action
- progressivize = (e: React.MouseEvent) => {
- e.stopPropagation();
- this.rootDoc.presProgressivize = !this.rootDoc.presProgressivize;
- const rootTarget = Cast(this.rootDoc.presentationTargetDoc, Doc, null);
- const docs = rootTarget.type === DocumentType.COL ? DocListCast(rootTarget[Doc.LayoutFieldKey(rootTarget)]) :
- DocListCast(rootTarget[Doc.LayoutFieldKey(rootTarget) + "-annotations"]);
- if (this.rootDoc.presProgressivize) {
- rootTarget.currentFrame = 0;
- CollectionFreeFormDocumentView.setupKeyframes(docs, docs.length, true);
- rootTarget.lastFrame = docs.length - 1;
- }
- }
-
- /**
- * The function that is called on click to turn fading document after presented option on/off.
- * It also makes sure that the option swithches from hide-after to this one, since both
- * can't coexist.
- */
- @action
- onFadeDocumentAfterPresentedClick = (e: React.MouseEvent) => {
- e.stopPropagation();
- this.rootDoc.presFadeButton = !this.rootDoc.presFadeButton;
- if (!this.rootDoc.presFadeButton) {
- if (this.indexInPres <= this.itemIndex && this.targetDoc) {
- this.targetDoc.opacity = 1;
- }
- } else {
- this.rootDoc.presHideAfterButton = false;
- if (this.presStatus !== "edit" && (this.indexInPres < this.itemIndex) && this.targetDoc) {
- this.targetDoc.opacity = 0.5;
- }
- }
- }
-
- /**
- * The function that is called on click to turn navigation option of docs on/off.
- */
- @action
- onNavigateDocumentClick = (e: React.MouseEvent) => {
- e.stopPropagation();
- this.rootDoc.presNavButton = !this.rootDoc.presNavButton;
- if (this.rootDoc.presNavButton) {
- this.rootDoc.presZoomButton = false;
- if (this.itemIndex === this.indexInPres) {
- this.props.focus(this.rootDoc);
- }
- }
- }
-
- /**
- * The function that is called on click to turn zoom option of docs on/off.
- */
- @action
- onZoomDocumentClick = (e: React.MouseEvent) => {
- e.stopPropagation();
-
- this.rootDoc.presZoomButton = !this.rootDoc.presZoomButton;
- if (this.rootDoc.presZoomButton) {
- this.rootDoc.presNavButton = false;
- if (this.itemIndex === this.indexInPres) {
- this.props.focus(this.rootDoc);
- }
- }
- }
- /**
* Returns a local transformed coordinate array for given coordinates.
*/
ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
@@ -233,12 +129,21 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+ @action
headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
- const element = document.elementFromPoint(e.clientX, e.clientY)?.parentElement;
+ const element = e.target as any;
e.stopPropagation();
e.preventDefault();
- if (element) {
- if (PresBox.Instance._eleArray.includes(element)) {
+ if (element && !(e.ctrlKey || e.metaKey)) {
+ if (PresBox.Instance._eleArray.includes(this._itemRef.current!)) {
+ setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
+ } else {
+ PresBox.Instance._selectedArray = [];
+ PresBox.Instance._selectedArray.push(this.rootDoc);
+ PresBox.Instance._eleArray = [];
+ PresBox.Instance._eleArray.push(this._itemRef.current!);
+ PresBox.Instance._dragArray = [];
+ PresBox.Instance._dragArray.push(this._dragRef.current!);
setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
}
}
@@ -293,8 +198,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
}
}
+ @action
+ toggleProperties = () => {
+ if (CurrentUserUtils.propertiesWidth === 0) {
+ CurrentUserUtils.propertiesWidth = 250;
+ }
+ }
+
render() {
- const treecontainer = this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Tree;
const className = "presElementBox-item" + (PresBox.Instance._selectedArray.includes(this.rootDoc) ? " presElementBox-active" : "");
const pbi = "presElementBox-interaction";
return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : (
@@ -319,6 +230,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
PresBox.Instance._dragArray.push(this._dragRef.current!);
}
}}
+ onDoubleClick={e => {
+ this.toggleProperties();
+ this.props.focus(this.rootDoc);
+ PresBox.Instance._eleArray = [];
+ PresBox.Instance._eleArray.push(this._itemRef.current!);
+ PresBox.Instance._dragArray = [];
+ PresBox.Instance._dragArray.push(this._dragRef.current!);
+ }}
onPointerDown={this.headerDown}
onPointerUp={this.headerUp}
>
@@ -347,14 +266,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
<div ref={this._highlightTopRef} onPointerOver={this.onPointerTop} onPointerLeave={this.onPointerLeave} className="presElementBox-highlightTop" style={{ zIndex: 299, backgroundColor: "rgba(0,0,0,0)" }} />
<div ref={this._highlightBottomRef} onPointerOver={this.onPointerBottom} onPointerLeave={this.onPointerLeave} className="presElementBox-highlightBottom" style={{ zIndex: 299, backgroundColor: "rgba(0,0,0,0)" }} />
<div className="presElementBox-highlight" style={{ backgroundColor: PresBox.Instance._selectedArray.includes(this.rootDoc) ? "#AEDDF8" : "rgba(0,0,0,0)" }} />
- <div className="presElementBox-buttons" style={{ display: this.rootDoc.presExpandInlineButton ? "grid" : "none" }}>
- <button title="Zoom" className={pbi + (this.rootDoc.presZoomButton ? "-selected" : "")} onClick={this.onZoomDocumentClick}><FontAwesomeIcon icon={"search"} onPointerDown={e => e.stopPropagation()} /></button>
- <button title="Navigate" className={pbi + (this.rootDoc.presNavButton ? "-selected" : "")} onClick={this.onNavigateDocumentClick}><FontAwesomeIcon icon={"location-arrow"} onPointerDown={e => e.stopPropagation()} /></button>
- <button title="Hide Before" className={pbi + (this.rootDoc.presHideTillShownButton ? "-selected" : "")} onClick={this.onHideDocumentUntilPressClick}><FontAwesomeIcon icon={"file"} onPointerDown={e => e.stopPropagation()} /></button>
- <button title="Hide After" className={pbi + (this.rootDoc.presHideAfterButton ? "-selected" : "")} onClick={this.onHideDocumentAfterPresentedClick}><FontAwesomeIcon icon={"file-download"} onPointerDown={e => e.stopPropagation()} /></button>
- <button title="Progressivize" className={pbi + (this.rootDoc.presProgressivize ? "-selected" : "")} onClick={this.progressivize}><FontAwesomeIcon icon={"tasks"} onPointerDown={e => e.stopPropagation()} /></button>
- <button title="Effect" className={pbi + (this.rootDoc.presEffect ? "-selected" : "")}>E</button>
- </div>
{this.renderEmbeddedInline}
</div>
);
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index f7b817aa1..ed1dc6de6 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,145 +1,108 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from '@material-ui/core';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import * as rp from 'request-promise';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
import { documentSchema } from "../../../fields/documentSchemas";
import { Id } 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 } 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 { ColumnType } from "../collections/CollectionSchemaView";
import { CollectionView, CollectionViewType } from '../collections/CollectionView';
import { ViewBoxBaseComponent } from "../DocComponent";
import { DocumentView } from '../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import "./SearchBox.scss";
-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[] = [];
-
- 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 currentSelectedCollection: DocumentView | undefined = undefined;
+ private docsforfilter: Doc[] = [];
+ private realTotalResults: number = 0;
+ private collectionRef = React.createRef<HTMLSpanElement>();
- @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 = "";
+ @observable headercount: number = 0;
+ @observable headerscale: number = 0;
+ @observable filter = false;
constructor(props: any) {
super(props);
SearchBox.Instance = this;
- this.resultsScrolled = this.resultsScrolled.bind(this);
-
}
- @observable setupButtons = false;
- componentDidMount = () => {
- if (this.setupButtons === false) {
- runInAction(() => this.setupButtons = true);
- }
- if (this.inputRef.current) {
- this.inputRef.current.focus();
- runInAction(() => { this._searchbarOpen = true; });
+ componentDidMount = action(() => {
+ if (this._inputRef.current) {
+ this._inputRef.current.focus();
+ 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();
- });
- }
+ componentWillUnmount() {
+ Object.values(this._disposers).forEach(disposer => disposer?.());
}
-
@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 === "") {
- this._results.forEach(result => {
- Doc.UnBrushDoc(result[0]);
- result[0].searchMatch = undefined;
- });
+ if (this.currentSelectedCollection) {
+ this.doLoop(this.currentSelectedCollection, undefined);
+ }
+ this.closeSearch(false);
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>([]);
if (this.currentSelectedCollection !== undefined) {
this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
this.currentSelectedCollection = undefined;
this.props.Document.selectedDoc = undefined;
-
}
- runInAction(() => { this.open = false; });
- this._openNoResults = false;
+ this.open = false;
this._results = [];
this._resultsSet.clear();
this._visibleElements = [];
@@ -150,124 +113,93 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
}
}
- enter = (e: React.KeyboardEvent) => {
- if (e.key === "Enter") {
+ enter = action((e: React.KeyboardEvent | undefined) => {
+ if (!e || e.key === "Enter") {
this.layoutDoc._searchString = this.newsearchstring;
-
- if (StrCast(this.layoutDoc._searchString) !== "" || !this.searchFullDB) {
- runInAction(() => this.open = true);
- }
- else {
- runInAction(() => this.open = false);
-
- }
+ this._pageStart = 0;
+ 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 private _collectionStatus = false;
-
+ });
getFinalQuery(query: string): string {
//alters the query so it looks in the correct fields
- //if this is true, then not all of the field boxes are checked
+ //if this is true, th`en not all of the field boxes are checked
//TODO: data
- if (this.fieldFiltersApplied) {
- query = this.applyBasicFieldFilters(query);
- query = query.replace(/\s+/g, ' ').trim();
- }
+ const initialfilters = Cast(this.props.Document._docFilters, listSpec("string"), []);
+
+ const filters: string[] = [];
- //alters the query based on if all words or any words are required
- //if this._wordstatus is false, all words are required and a + is added before each
- if (!this._basicWordStatus) {
- query = this.basicRequireWords(query);
- query = query.replace(/\s+/g, ' ').trim();
+ for (let i = 0; i < initialfilters.length; i = i + 3) {
+ if (initialfilters[i + 2] !== undefined) {
+ filters.push(initialfilters[i]);
+ filters.push(initialfilters[i + 1]);
+ filters.push(initialfilters[i + 2]);
+ }
}
- // if should be searched in a specific collection
- if (this._collectionStatus) {
- query = this.addCollectionFilter(query);
- query = query.replace(/\s+/g, ' ').trim();
+ const finalfilters: { [key: string]: string[] } = {};
+ for (let i = 0; i < filters.length; i = i + 3) {
+ if (finalfilters[filters[i]] !== undefined) {
+ finalfilters[filters[i]].push(filters[i + 1]);
+ }
+ else {
+ finalfilters[filters[i]] = [filters[i + 1]];
+ }
}
- return query;
- }
- basicRequireWords(query: string): string {
- return query.split(" ").join(" + ").replace(/ + /, "");
+ for (const key in finalfilters) {
+ const values = finalfilters[key];
+ if (values.length === 1) {
+ 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 + "\"");
+ });
+ query = `(${query}) AND (${newWords.join(" ")})`;
+ }
+ else {
+ for (let i = 0; i < values.length; i++) {
+ 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 + "\"");
+ });
+ 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;
+ }
+ }
+ }
+ }
+
+ return query.replace(/-\s+/g, '');
}
@action
filterDocsByType(docs: Doc[]) {
const finalDocs: Doc[] = [];
- const blockedTypes: string[] = ["preselement", "docholder", "collection", "search", "searchitem", "script", "fonticonbox", "button", "label"];
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)
@@ -293,12 +225,10 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
}
- currentSelectedCollection: DocumentView | undefined = undefined;
- docsforfilter: Doc[] = [];
-
searchCollection(query: string) {
- const selectedCollection: DocumentView = SelectionManager.SelectedDocuments()[0];
+ const selectedCollection = SelectionManager.SelectedDocuments()[0];
query = query.toLowerCase();
+
if (selectedCollection !== undefined) {
this.currentSelectedCollection = selectedCollection;
if (this.filter === true) {
@@ -316,14 +246,8 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
newarray.push(...DocListCast(d.data));
}
const hlights: string[] = [];
- const protos = Doc.GetAllPrototypes(d);
- protos.forEach(proto => {
- Object.keys(proto).forEach(key => {
- if (StrCast(d[key]).toLowerCase().includes(query) && !hlights.includes(key)) {
- hlights.push(key);
- }
- });
- });
+ 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);
@@ -335,30 +259,17 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
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) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>(docsforFilter);
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
+ this.doLoop(selectedCollection, docsforFilter);
}
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.
@@ -367,135 +278,79 @@ 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 = "";
-
- if (this._titleFieldStatus) {
- finalQuery = finalQuery + this.basicFieldFilters(query, Keys.TITLE);
- }
- if (this._authorFieldStatus) {
- finalQuery = finalQuery + this.basicFieldFilters(query, Keys.AUTHOR);
- }
- if (this._deletedDocsStatus) {
- finalQuery = finalQuery + this.basicFieldFilters(query, Keys.DATA);
- }
- 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.DATA: break; // TODO
- 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) {
+ this.doLoop(this.currentSelectedCollection, undefined);
+ }
if (reset) {
this.layoutDoc._searchString = "";
}
- this.props.Document._docFilters = undefined;
- this.noresults = "";
+ //this.props.Document._docFilters = new List();
+ this._noResults = "";
this.dataDoc[this.fieldKey] = new List<Doc>([]);
this.headercount = 0;
this.children = 0;
- this.buckets = [];
- this.new_buckets = {};
- const query = StrCast(this.layoutDoc._searchString);
+ let query = StrCast(this.layoutDoc._searchString);
Doc.SetSearchQuery(query);
- this.getFinalQuery(query);
- this._results.forEach(result => {
- Doc.UnBrushDoc(result[0]);
- result[0].searchMatch = undefined;
- });
+ this._searchFullDB ? query = this.getFinalQuery(query) : console.log("local");
+ this.closeSearch(false);
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 !== "") {
+ 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.getResults(query) : this.searchCollection(query);
runInAction(() => {
- this._resultsOpen = true;
this._searchbarOpen = true;
- this._openNoResults = true;
this.resultsScrolled();
-
});
}
}
- @observable searchFullDB = true;
-
- @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", "collection", "search", "searchitem", "script", "fonticonbox", "button", "label"]; // this.filterTypes;
- const baseExpr = "NOT baseProto_b:true AND NOT system_b:true";
- const includeDeleted = this.getDataStatus() ? "" : " NOT deleted_b:true";
- const includeIcons = this.getDataStatus() ? "" : " NOT type_t:fonticonbox";
- const typeExpr = !types ? "" : ` ${types.map(type => `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`;
+ const baseExpr = "NOT system_b:true";
+ 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, includeDeleted, includeIcons, typeExpr].join(" AND ").replace(/AND $/, "");
+ const query = [baseExpr, authorExpr, includeDeleted, typeExpr].filter(q => q).join(" AND ").replace(/AND $/, "");
return query;
}
- getDataStatus() { return this._deletedDocsStatus; }
+ @computed get primarySort() {
+ const suffixMap = (type: ColumnType) => {
+ switch (type) {
+ case ColumnType.Date: return "_d";
+ case ColumnType.String: return "_t";
+ case ColumnType.Boolean: return "_b";
+ case ColumnType.Number: return "_n";
+ }
+ };
+ 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 = 25;
- private lockPromise?: Promise<void>;
getResults = async (query: string) => {
- console.log("Get");
- if (this.lockPromise) {
- await this.lockPromise;
- }
- this.lockPromise = new Promise(async res => {
+ 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, { fq: this.filterQuery, start: this._maxSearchIndex, 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) {
this._numTotalResults = res.numFound;
}
@@ -503,44 +358,31 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
const highlightList = res.docs.map(doc => highlighting[doc[Id]]);
const lines = new Map<string, string[]>();
res.docs.map((doc, i) => lines.set(doc[Id], res.lines[i]));
- const docs = await Promise.all(res.docs.map(async doc => (await Cast(doc.extendsDoc, Doc)) || doc));
+ const docs = res.docs;
const highlights: typeof res.highlighting = {};
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) : [];
- doc ? console.log(Cast(doc.context, Doc)) : null;
- if (this.findCommonElements(hlights)) {
- }
- else {
- const layoutresult = Cast(doc.type, "string");
- if (layoutresult) {
- if (this.new_buckets[layoutresult] === undefined) {
- this.new_buckets[layoutresult] = 1;
- }
- else {
- this.new_buckets[layoutresult] = this.new_buckets[layoutresult] + 1;
- }
- }
- 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;
}
@@ -548,21 +390,13 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
this.resultsScrolled();
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))) {
@@ -586,26 +420,27 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
y += 300;
}
}
- return Docs.Create.SearchDocument({ _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString), searchQuery: StrCast(this.layoutDoc._searchString) });
+ 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) });
}
@action.bound
openSearch(e: React.SyntheticEvent) {
e.stopPropagation();
- this._openNoResults = false;
- this._resultsOpen = true;
+ this._results.forEach(result => Doc.BrushDoc(result[0]));
this._searchbarOpen = true;
}
@action.bound
- closeSearch = () => {
- //this.closeResults();
- this._searchbarOpen = false;
+ closeSearch = (closesearchbar = true) => {
+ this._results.forEach(result => {
+ Doc.UnBrushDoc(result[0]);
+ result[0].searchMatch = undefined;
+ });
+ closesearchbar && (this._searchbarOpen = false);
}
@action.bound
closeResults() {
- this._resultsOpen = false;
this._results = [];
this._resultsSet.clear();
this._visibleElements = [];
@@ -615,26 +450,10 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
this._curRequest = undefined;
}
- @observable children: number = 0;
@action
resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => {
- if (!this._resultsRef.current) return;
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>([]);
-
- const scrollY = e ? e.currentTarget.scrollTop : this._resultsRef.current ? this._resultsRef.current.scrollTop : 0;
- const itemHght = 53;
- const startIndex = Math.floor(Math.max(0, scrollY / itemHght));
- //const endIndex = Math.ceil(Math.min(this._numTotalResults - 1, startIndex + (this._resultsRef.current.getBoundingClientRect().height / itemHght)));
- const endIndex = 30;
- //this._endIndex = endIndex === -1 ? 12 : endIndex;
this._endIndex = 30;
- const headers = new Set<string>(["title", "author", "*lastModified", "text"]);
- if ((this._numTotalResults === 0 || this._results.length === 0) && this._openNoResults) {
- if (this.noresults === "") {
- this.noresults = "No search results :(";
- }
- return;
- }
+ const headers = new Set<string>(["title", "author", "text", "type", "data", "*lastModified", "context"]);
if (this._numTotalResults <= this._maxSearchIndex) {
this._numTotalResults = this._results.length;
@@ -646,278 +465,182 @@ 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);
-
}
- for (let i = 0; i < this._numTotalResults; i++) {
+ 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 (i < startIndex || i > endIndex) {
- if (this._isSearch[i] !== "placeholder") {
- this._isSearch[i] = "placeholder";
- this._isSorted[i] = "placeholder";
- this._visibleElements[i] = <div className="searchBox-placeholder" key={`searchBox-placeholder-${i}`}>Loading...</div>;
- }
- }
- else {
- if (this._isSearch[i] !== "search") {
- let result: [Doc, string[], string[]] | undefined = undefined;
- if (i >= this._results.length) {
- this.getResults(StrCast(this.layoutDoc._searchString));
- if (i < this._results.length) result = this._results[i];
- if (result) {
- const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
- const lines = new List<string>(result[2]);
- result[0].lines = lines;
- result[0].highlighting = highlights.join(", ");
- highlights.forEach((item) => headers.add(item));
- this._visibleDocuments[i] = result[0];
- this._isSearch[i] = "search";
- Doc.BrushDoc(result[0]);
- result[0].searchMatch = true;
- Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]);
- this.children++;
- }
- }
- else {
- 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));
+ result[0].lines = lines;
+ result[0].highlighting = highlights.join(", ");
+ result[0].searchMatch = true;
+ 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++;
}
}
}
- const schemaheaders: SchemaHeaderField[] = [];
this.headerscale = headers.size;
- headers.forEach((item) => schemaheaders.push(new SchemaHeaderField(item, "#f1efeb")));
- this.headercount = schemaheaders.length;
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>(schemaheaders);
+ 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._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; }
+ getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
+ panelHeight = () => this.props.PanelHeight();
- 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); }
-
- 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));
}
+ returnHeight = () => 31 + 31 * 6;
+ returnLength = () => Math.min(window.innerWidth, 51 + 205 * Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length);
+
+ @action
+ changeSearchScope = (scope: string) => {
+ scope && (this.filter = false);
+ this._searchFullDB = scope;
+ this.dataDoc[this.fieldKey] = new List<Doc>([]);
+ if (this.currentSelectedCollection !== undefined) {
+ this.doLoop(this.currentSelectedCollection, undefined);
+ }
+ 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("")} />
+ 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>
+ </form>
+ </div>;
}
- @observable filter = false;
+ doLoop = (collectionView: DocumentView, filter: Doc[] | undefined) => {
+ let docs = DocListCast(collectionView.dataDoc[Doc.LayoutFieldKey(collectionView.dataDoc)]);
+ let newarray: Doc[] = [];
+ while (docs.length > 0) {
+ newarray = [];
+ docs.forEach(d => {
+ const subDocs = DocListCast(d.data);
+ if (subDocs.length) {
+ d._searchDocs = filter ? new List<Doc>(filter) : undefined;
+ DocListCast(d.data).forEach(newdoc => newarray.push(newdoc));
+ }
+ });
+ docs = newarray;
+ }
+ collectionView.props.Document._searchDocs = filter ? new List<Doc>(filter) : undefined;
+ this.props.Document.selectedDoc = filter ? collectionView.props.Document : undefined;
+ }
- //Make id layour document
render() {
this.props.Document._chromeStatus === "disabled";
this.props.Document._searchDoc = true;
- const cols = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length;
- let length = 0;
- cols > 5 ? length = 1076 : length = cols * 205 + 51;
- let height = 0;
const rows = this.children;
- rows > 8 ? height = 31 + 31 * 8 : height = 31 * rows + 31;
return (
<div style={{ pointerEvents: "all" }} className="searchBox-container">
+ <div style={{ position: "absolute", left: 15, height: 32, alignItems: "center", display: "flex" }}>{Doc.CurrentUserEmail}</div>
<div className="searchBox-bar">
- <div style={{ position: "absolute", left: 15 }}>{Doc.CurrentUserEmail}</div>
- <div style={{ display: "flex", alignItems: "center" }}>
- <FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg"
- style={{ color: "black", padding: 1, left: 35, position: "relative" }} />
-
- <div style={{ cursor: "default", left: 250, position: "relative", }}>
- <Tooltip title={<div className="dash-tooltip" >only display documents matching search</div>} ><div>
- <FontAwesomeIcon icon={"filter"} size="lg"
- style={{ 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(() => {
- const dofilter = (currentSelectedCollection: DocumentView) => {
- let docs = DocListCast(currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- const newarray: Doc[] = [];
- docs.filter(d => d.data !== undefined).forEach((d) => {
- d._searchDocs = new List<Doc>(this.docsforfilter);
- newarray.push(...DocListCast(d.data));
- });
- docs = newarray;
- }
- };
- this.filter = !this.filter && !this.searchFullDB;
- if (this.filter === true && this.currentSelectedCollection !== undefined) {
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>(this.docsforfilter);
-
- dofilter(this.currentSelectedCollection);
-
- this.currentSelectedCollection.props.Document._docFilters = new List<string>(Cast(this.props.Document._docFilters, listSpec("string"), []));
- this.props.Document.selectedDoc = this.currentSelectedCollection.props.Document;
- }
- else if (this.filter === false && this.currentSelectedCollection !== undefined) {
-
- dofilter(this.currentSelectedCollection);
-
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
- this.currentSelectedCollection.props.Document._docFilters = undefined;
- this.props.Document.selectedDoc = undefined;
- }
- }
- )} />
- </div></Tooltip></div>
- <input value={this.newsearchstring} autoComplete="off" onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this.inputRef}
+ <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}
className="searchBox-barChild searchBox-input" onPointerDown={this.openSearch} onKeyPress={this.enter} onFocus={this.openSearch}
- style={{ padding: 1, paddingLeft: 20, paddingRight: 20, color: "black", height: 20, width: 250 }} />
- <div style={{
- height: 25,
- paddingLeft: "4px",
- paddingRight: "4px",
- border: "1px solid gray",
- borderRadius: "0.3em",
- borderBottom: this.open === false ? "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={() => {
- runInAction(() => {
- this.searchFullDB = !this.searchFullDB;
- 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>();
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
- this.currentSelectedCollection.props.Document._docFilters = undefined;
- this.currentSelectedCollection.props.Document._searchDocs = undefined;
- this.currentSelectedCollection = undefined;
- }
- this.submitSearch();
- });
- }} />
- Collection
- </label>
- </div>
- <div className="radio" style={{ margin: 0 }}>
- <label style={{ fontSize: 12, marginTop: 6 }} >
- <input style={{ marginLeft: -16, marginTop: -1 }} type="radio" checked={this.searchFullDB} onChange={() => {
- runInAction(() => {
- this.searchFullDB = !this.searchFullDB;
- this.dataDoc[this.fieldKey] = new List<Doc>([]);
- this.filter = false;
- 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>();
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
- this.currentSelectedCollection.props.Document._docFilters = undefined;
- this.currentSelectedCollection.props.Document._searchDocs = undefined;
- this.currentSelectedCollection = undefined;
- }
- this.submitSearch();
- });
- }} />
- DB
- </label>
+ 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"
+ style={{ cursor: "hand", color: "black", padding: 1, position: "relative" }} /></div>
+ </Tooltip>
+ </div>
+ <div style={{ position: "absolute", left: 200, width: 30, zIndex: 9000, color: "grey", background: "white", }}>
+ {`${this._results.length}` + " of " + `${this.realTotalResults}`}
+ </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, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }}
+ onClick={action(() => {
+ this.filter = !this.filter && !this._searchFullDB;
+ this.currentSelectedCollection && this.doLoop(this.currentSelectedCollection, this.filter ? this.docsforfilter : undefined);
+ })} />
</div>
- </div>
- </form>
+ </Tooltip>
+ </div>
+ {this.scopeButtons}
</div>
- </div>
-
- </div>
- <div style={{ zIndex: 20000, color: "black" }}>
- {this._searchbarOpen === true ?
- <div style={{ display: "flex", justifyContent: "center", }}>
- {this.noresults === "" ? <div style={{ display: this.open === true ? "flex" : "none", overflow: "auto", }}>
- <CollectionView {...this.props}
- Document={this.props.Document}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- PanelHeight={this.open === true ? () => height : () => 0}
- PanelWidth={this.open === true ? () => length : () => 0}
- overflow={cols > 5 || rows > 8 ? 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>
+ {!this._searchbarOpen ? (null) : <div style={{ zIndex: 20000, color: "black" }}>
+ <div style={{ display: "flex", justifyContent: "center", }}>
+ <div style={{ display: this.open ? "flex" : "none", overflow: "auto", }}>
+ <CollectionView {...this.props}
+ 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}
+ focus={this.selectElement}
+ ScreenToLocalTransform={Transform.Identity}
+ />
+ </div>
+ </div>
+ </div>}
</div >
);
}
-}
+} \ No newline at end of file
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index b535fea5a..c4a49962b 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -195,6 +195,8 @@ export class Doc extends RefField {
@observable
private ___fieldKeys: any = {};
+ @observable
+ public [AclSym]: { [key: string]: symbol };
private [UpdatingFromServer]: boolean = false;
@@ -204,19 +206,11 @@ export class Doc extends RefField {
private [Self] = this;
private [SelfProxy]: any;
- public [FieldsSym] = (clear?: boolean) => {
- if (clear) {
- this.___fields = {};
- this.___fieldKeys = {};
- }
- return this.___fields;
- }
- @observable
- public [AclSym]: { [key: string]: symbol };
+ 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]() { return `DOC-"${this[Self][Id]}"-`; }
- public [ToString]() { return `Doc(${GetEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; }
+ public [ToScriptString] = () => `DOC-"${this[Self][Id]}"-`;
+ 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];
@@ -781,6 +775,7 @@ export namespace Doc {
if (doc) {
const delegate = new Doc(id, true);
delegate.proto = doc;
+ delegate.author = Doc.CurrentUserEmail;
title && (delegate.title = title);
return delegate;
}
@@ -790,7 +785,9 @@ export namespace Doc {
let _applyCount: number = 0;
export function ApplyTemplate(templateDoc: Doc) {
if (templateDoc) {
- const target = Doc.MakeDelegate(new Doc());
+ const proto = new Doc();
+ proto.author = Doc.CurrentUserEmail;
+ const target = Doc.MakeDelegate(proto);
const targetKey = StrCast(templateDoc.layoutKey, "layout");
const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")");
target.layoutKey = targetKey;
@@ -806,7 +803,8 @@ export namespace Doc {
target[targetKey] = new PrefetchProxy(templateDoc);
} else {
titleTarget && (Doc.GetProto(target).title = titleTarget);
- Doc.GetProto(target)[targetKey] = new PrefetchProxy(templateDoc);
+ const setDoc = [AclAdmin, AclEdit].includes(GetEffectiveAcl(Doc.GetProto(target))) ? Doc.GetProto(target) : target;
+ setDoc[targetKey] = new PrefetchProxy(templateDoc);
}
}
return target;
@@ -1044,6 +1042,7 @@ export namespace Doc {
if (docFilters[i] === key && (docFilters[i + 1] === value || modifiers === "match")) {
if (docFilters[i + 2] === modifiers && modifiers && docFilters[i + 1] === value) return;
docFilters.splice(i, 3);
+ container._docFilters = new List<string>(docFilters);
break;
}
}
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index 2ca5ac082..ae5f301d0 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -28,7 +28,7 @@ export class RichTextField extends ObjectField {
}
[ToScriptString]() {
- return `new RichTextField("${this.Data}", "${this.Text}")`;
+ return `new RichTextField("${this.Data.replace(/"/g, "\\\"")}", "${this.Text}")`;
}
[ToString]() {
return this.Text;
diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts
index 07c90f5a2..22ae454f8 100644
--- a/src/fields/SchemaHeaderField.ts
+++ b/src/fields/SchemaHeaderField.ts
@@ -114,7 +114,7 @@ export class SchemaHeaderField extends ObjectField {
}
[ToScriptString]() {
- return `invalid`;
+ return `header(${this.heading},${this.type}})`;
}
[ToString]() {
return `SchemaHeaderField`;
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 4cc3a7cc7..52730ed00 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -56,6 +56,9 @@ async function deserializeScript(script: ScriptField) {
if (script.script.originalScript === 'self.userDoc.noviceMode') {
return (script as any).script = (ScriptField.NoviceMode ?? (ScriptField.NoviceMode = ComputedField.MakeFunction('self.userDoc.noviceMode')))?.script;
}
+ if (script.script.originalScript === `selectMainMenu(self)`) {
+ return (script as any).script = (ScriptField.SelectMenu ?? (ScriptField.SelectMenu = ComputedField.MakeFunction('selectMainMenu(self)')))?.script;
+ }
const captures: ProxyField<Doc> = (script as any).captures;
if (captures) {
const doc = (await captures.value())!;
@@ -70,6 +73,13 @@ async function deserializeScript(script: ScriptField) {
throw new Error("Couldn't compile loaded script");
}
(script as any).script = comp;
+ if (script.setterscript) {
+ const compset = CompileScript(script.setterscript?.originalScript, script.setterscript.options);
+ if (!compset.compiled) {
+ throw new Error("Couldn't compile setter script");
+ }
+ (script as any).setterscript = compset;
+ }
}
@scriptingGlobal
@@ -89,11 +99,13 @@ export class ScriptField extends ObjectField {
public static DeiconifyView: Opt<ScriptField>;
public static ConvertToButtons: Opt<ScriptField>;
public static NoviceMode: Opt<ScriptField>;
+ public static SelectMenu: Opt<ScriptField>;
constructor(script: CompiledScript, setterscript?: CompiledScript) {
super();
if (script?.options.capturedVariables) {
const doc = Doc.assign(new Doc, script.options.capturedVariables);
+ doc.system = true;
this.captures = new ProxyField(doc);
}
this.setterscript = setterscript;
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index ada13226e..848a648e1 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -102,7 +102,7 @@ export const documentSchema = createSchema({
_lockedTransform: "boolean",// whether a freeformview can pan/zoom
// drag drop properties
- stayInCollection: "boolean",// whether document can be dropped into a different collection
+ _stayInCollection: "boolean",// whether document can be dropped into a different collection
dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document.
dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 4c71572db..d8523dfa2 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -5,7 +5,7 @@ import { ProxyField, PrefetchProxy } from "./Proxy";
import { RefField } from "./RefField";
import { ObjectField } from "./ObjectField";
import { action, trace } from "mobx";
-import { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate } from "./FieldSymbols";
+import { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate, ToString, ToScriptString } from "./FieldSymbols";
import { DocServer } from "../client/DocServer";
import { ComputedField } from "./ScriptField";
import { ScriptCast, StrCast } from "./Types";
@@ -154,15 +154,16 @@ export enum SharingPermissions {
/**
* Calculates the effective access right to a document for the current user.
*/
-export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol {
+export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number, user?: string): symbol {
if (!target) return AclPrivate;
if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclAdmin;
if (target[AclSym] && Object.keys(target[AclSym]).length) {
+ const userChecked = user || Doc.CurrentUserEmail;
+
// if the current user is the author of the document / the current user is a member of the admin group
- // but not if the doc in question is an alias - the current user will be the author of their alias rather than the original author
- if ((Doc.CurrentUserEmail === (target.__fields?.author || target.author) && !(target.aliasOf || target.__fields?.aliasOf)) || currentUserGroups.includes("admin")) return AclAdmin;
+ if (userChecked === (target.__fields?.author || target.author) || currentUserGroups.includes("admin")) return AclAdmin;
// if the ACL is being overriden or the property being modified is one of the playground fields (which can be freely modified)
if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit;
@@ -179,7 +180,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number)
for (const [key, value] of Object.entries(target[AclSym])) {
// there are issues with storing fields with . in the name, so they are replaced with _ during creation
// as a result we need to restore them again during this comparison.
- if (currentUserGroups.includes(key.substring(4)) || Doc.CurrentUserEmail === key.substring(4).replace("_", ".")) {
+ if (currentUserGroups.includes(key.substring(4).replace("_", ".")) || userChecked === key.substring(4).replace("_", ".")) {
if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) {
effectiveAcl = value as symbol;
if (effectiveAcl === AclAdmin) break;
@@ -209,51 +210,53 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
["Admin", 4]
]);
- let changed = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym])
+ let layoutDocChanged = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym])
+ let dataDocChanged = false;
const dataDoc = target[DataSym];
// if it is inheriting from a collection, it only inherits if A) the key doesn't already exist or B) the right being inherited is more restrictive
if (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!) {
target[key] = acl;
- changed = true;
-
- // maps over the aliases of the document
- if (target.aliases) {
- DocListCast(target.aliases).map(alias => {
- distributeAcls(key, acl, alias, inheritingFromCollection);
- });
- }
-
+ layoutDocChanged = true;
}
if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) {
dataDoc[key] = acl;
- changed = true;
+ dataDocChanged = true;
+
+ // maps over the aliases of the document
+ const aliases = DocListCast(dataDoc.aliases);
+ if (aliases.length) {
+ aliases.map(alias => {
+ alias !== target && distributeAcls(key, acl, alias, inheritingFromCollection);
+ });
+ }
// maps over the children of the document
DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => {
- if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
+ if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
distributeAcls(key, acl, d, inheritingFromCollection);
}
const data = d[DataSym];
- if (data && data.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) {
+ if (data && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) {
distributeAcls(key, acl, data, inheritingFromCollection);
}
});
// maps over the annotations of the document
DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => {
- if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
+ if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
distributeAcls(key, acl, d, inheritingFromCollection);
}
const data = d[DataSym];
- if (data && data.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) {
+ if (data && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) {
distributeAcls(key, acl, data, inheritingFromCollection);
}
});
}
- changed && fetchProto(target); // updates target[AclSym] when changes to acls have been made
+ layoutDocChanged && fetchProto(target); // updates target[AclSym] when changes to acls have been made
+ dataDocChanged && fetchProto(dataDoc);
}
const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
@@ -286,7 +289,7 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
export function getter(target: any, in_prop: string | symbol | number, receiver: any): any {
let prop = in_prop;
- if (in_prop === FieldsSym || in_prop === Id || in_prop === HandleUpdate || in_prop === CachedUpdates) return target.__fields[prop] || target[prop];
+ if (in_prop === ToString || in_prop === ToScriptString || in_prop === FieldsSym || in_prop === Id || in_prop === HandleUpdate || in_prop === CachedUpdates) return target.__fields[prop] || target[prop];
if (in_prop === AclSym) return _overrideAcl ? undefined : target[AclSym];
if (GetEffectiveAcl(target) === AclPrivate && !_overrideAcl) return prop === HeightSym || prop === WidthSym ? returnZero : undefined;
if (prop === LayoutSym) {
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index c6420bab5..d21d326f6 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -177,4 +177,4 @@ export class Uploader extends React.Component<ImageUploadProps> {
);
}
-}
+} \ No newline at end of file
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 40aa65372..c5e395d2f 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -50,7 +50,7 @@ library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngl
@observer
export class MobileInterface extends React.Component {
static Instance: MobileInterface;
- private _library: Doc = CurrentUserUtils.setupLibrary(Doc.UserDoc()); // to access documents in Dash Web
+ private _library: Promise<Doc>;
private _mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(Doc.UserDoc());
@observable private _sidebarActive: boolean = false; //to toggle sidebar display
@observable private _imageUploadActive: boolean = false; //to toggle image upload
@@ -67,6 +67,7 @@ export class MobileInterface extends React.Component {
constructor(props: Readonly<{}>) {
super(props);
+ this._library = CurrentUserUtils.setupLibrary(Doc.UserDoc()); // to access documents in Dash Web
MobileInterface.Instance = this;
}
@@ -123,7 +124,7 @@ export class MobileInterface extends React.Component {
* Method called when 'Library' button is pressed on the home screen
*/
switchToLibrary = async () => {
- this.switchCurrentView(this._library);
+ this._library.then(library => this.switchCurrentView(library));
runInAction(() => this._homeMenu = false);
this.toggleSidebar();
}
@@ -138,7 +139,7 @@ export class MobileInterface extends React.Component {
// Case 1: Parent document is 'workspaces'
if (doc === Cast(this._library, Doc) as Doc) {
this._child = null;
- this.switchCurrentView(this._library);
+ 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;
@@ -177,7 +178,7 @@ export class MobileInterface extends React.Component {
@action
returnMain = () => {
this._parents = [this._homeDoc];
- this.switchCurrentView(this._library);
+ this._library.then(library => this.switchCurrentView(library));
this._homeMenu = false;
this._child = null;
}
@@ -286,8 +287,9 @@ export class MobileInterface extends React.Component {
// Handles when user clicks on a document in the pathbar
@action
- handlePathClick = (doc: Doc, index: number) => {
- if (doc === this._library) {
+ handlePathClick = async (doc: Doc, index: number) => {
+ const library = await this._library;
+ if (doc === library) {
this._child = null;
this.switchCurrentView(doc);
this._parents.length = index;
@@ -681,4 +683,4 @@ Scripting.addGlobal(function openMobileSettings() { return SettingsManager.Insta
// Other global functions for mobile
Scripting.addGlobal(function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); },
- "changes the active document displayed on the Dash Mobile", "(doc: any)");
+ "changes the active document displayed on the Dash Mobile", "(doc: any)"); \ No newline at end of file
diff --git a/src/mobile/MobileMenu.scss b/src/mobile/MobileMenu.scss
index e69de29bb..7f286efc4 100644
--- a/src/mobile/MobileMenu.scss
+++ b/src/mobile/MobileMenu.scss
@@ -0,0 +1,271 @@
+$navbar-height: 120px;
+$pathbar-height: 50px;
+
+* {
+ margin: 0px;
+ padding: 0px;
+ box-sizing: border-box;
+ font-family: "Open Sans";
+}
+
+body {
+ overflow: hidden;
+}
+
+.navbar {
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ width: 100vw;
+ height: $navbar-height;
+ background-color: whitesmoke;
+ border-bottom: 5px solid black;
+}
+
+.navbar .toggle-btn {
+ position: absolute;
+ right: 20px;
+ top: 30px;
+ height: 70px;
+ width: 70px;
+ transition: all 300ms ease-in-out 200ms;
+}
+
+.navbar .header {
+ position: absolute;
+ top: 50%;
+ top: calc(9px + 50%);
+ right: 50%;
+ transform: translate(50%, -50%);
+ font-size: 40;
+ user-select: none;
+ text-transform: uppercase;
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+.navbar .toggle-btn span {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 70%;
+ height: 4px;
+ background: black;
+ transition: all 200ms ease;
+}
+
+.navbar .toggle-btn span:nth-child(1) {
+ transition: top 200ms ease-in-out;
+ top: 30%;
+}
+
+.navbar .toggle-btn span:nth-child(3) {
+ transition: top 200ms ease-in-out;
+ top: 70%;
+}
+
+.navbar .toggle-btn.active {
+ transition: transform 200ms ease-in-out 200ms;
+ transform: rotate(135deg);
+}
+
+.navbar .toggle-btn.active span:nth-child(1) {
+ top: 50%;
+}
+
+.navbar .toggle-btn.active span:nth-child(2) {
+ transform: translate(-50%, -50%) rotate(90deg);
+}
+
+.navbar .toggle-btn.active span:nth-child(3) {
+ top: 50%;
+}
+// .navbar .home {
+// position: relative;
+// right: 5px;
+// transform: translate(50%, -50%);
+// font-size: 40;
+// user-select: none;
+// text-transform: uppercase;
+// font-family: Arial, Helvetica, sans-serif;
+// z-index: 200;
+// }
+
+.sidebar {
+ position: absolute;
+ top: 200px;
+ opacity: 0;
+ right: -100%;
+ width: 100%;
+ height: calc(100% - (200px));
+ z-index: 5;
+ background-color: whitesmoke;
+ transition: all 400ms ease 50ms;
+ padding: 20px;
+ // overflow-y: auto;
+ // -webkit-overflow-scrolling: touch;
+
+ // border-right: 5px solid black;
+}
+
+.sidebar .item {
+ width: 100%;
+ padding: 13px 12px;
+ border-bottom: 1px solid rgba(200, 200, 200, 0.7);
+ font-family: Arial, Helvetica, sans-serif;
+ font-style: normal;
+ font-weight: normal;
+ user-select: none;
+ font-size: 35px;
+ text-transform: uppercase;
+ color: black;
+
+}
+
+.sidebar .ink {
+ width: 100%;
+ padding: 13px 12px;
+ border-bottom: 1px solid rgba(200, 200, 200, 0.7);
+ font-family: Arial, Helvetica, sans-serif;
+ font-style: normal;
+ font-weight: normal;
+ user-select: none;
+ font-size: 35px;
+ text-transform: uppercase;
+ color: black;
+}
+
+.sidebar .ink:focus {
+ outline: 1px solid blue;
+}
+
+.sidebar .home {
+ position: absolute;
+ top: -135px;
+ right: calc(50% + 80px);
+ transform: translate(0%, -50%);
+ font-size: 40;
+ user-select: none;
+ text-transform: uppercase;
+ font-family: Arial, Helvetica, sans-serif;
+ z-index: 200;
+}
+
+.type {
+ display: inline;
+ text-transform: lowercase;
+ margin-left: 20px;
+ font-size: 35px;
+ font-style: italic;
+ color: rgb(28, 28, 28);
+}
+
+.right {
+ margin-left: 20px;
+ z-index: 200;
+}
+
+.left {
+ width: 100%;
+ height: 100%;
+}
+
+.sidebar .logout {
+ width: 100%;
+ padding: 13px 12px;
+ border-bottom: 1px solid rgba(200, 200, 200, 0.7);
+ font-family: Arial, Helvetica, sans-serif;
+ font-style: normal;
+ font-weight: normal;
+ user-select: none;
+ font-size: 30px;
+ text-transform: uppercase;
+ color: black;
+}
+
+.sidebar .settings {
+ width: 100%;
+ padding: 13px 12px;
+ border-bottom: 1px solid rgba(200, 200, 200, 0.7);
+ font-family: Arial, Helvetica, sans-serif;
+ font-style: normal;
+ font-weight: normal;
+ user-select: none;
+ font-size: 30px;
+ text-transform: uppercase;
+ color: black;
+}
+
+
+.sidebar.active {
+ right: 0%;
+ opacity: 1;
+}
+
+.back {
+ position: absolute;
+ top: -140px;
+ left: 50px;
+ transform: translate(0%, -50%);
+ color: black;
+ font-size: 60;
+ user-select: none;
+ text-transform: uppercase;
+ z-index: 100;
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+
+.pathbar {
+ position: absolute;
+ top: 118px;
+ background: #1a1a1a;
+ z-index: 20;
+ border-radius: 0px;
+ width: 100%;
+ height: 80px;
+ transition: all 400ms ease 50ms;
+}
+
+.pathname {
+ position: relative;
+ font-size: 25;
+ top: 50%;
+ width: 90%;
+ left: 3%;
+ color: whitesmoke;
+ transform: translate(0%, -50%);
+ z-index: 20;
+ font-family: sans-serif;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ direction: rtl;
+ text-align: left;
+ text-transform: uppercase;
+}
+
+.homeContainer {
+ position: relative;
+ top: 200px;
+ height: calc(100% - 250px);
+ width: 90%;
+ overflow: scroll;
+ left: 5%;
+ background-color: lightpink;
+}
+
+.pinButton {
+ position: relative;
+ width: 100px;
+ height: 100px;
+ font-size: 90px;
+ text-align: center;
+ left: 50%;
+ transform: translate(-50%, 0);
+ border-style: solid;
+ border-radius: 50px;
+ border-width: medium;
+ background-color: pink;
+ z-index: 100;
+} \ No newline at end of file
diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts
index 99f227b28..a5aaab63e 100644
--- a/src/server/ApiManagers/SearchManager.ts
+++ b/src/server/ApiManagers/SearchManager.ts
@@ -62,7 +62,7 @@ export class SearchManager extends ApiManager {
subscription: "/dashsearch",
secureHandler: async ({ req, res }) => {
const solrQuery: any = {};
- ["q", "fq", "start", "rows", "hl", "hl.fl"].forEach(key => solrQuery[key] = req.query[key]);
+ ["q", "fq", "start", "rows", "sort", "hl", "hl.fl"].forEach(key => solrQuery[key] = req.query[key]);
if (solrQuery.q === undefined) {
res.send([]);
return;
@@ -136,6 +136,9 @@ export namespace SolrManager {
const term = ToSearchTerm(value);
if (term !== undefined) {
const { suffix, value } = term;
+ if (key.endsWith('lastModified')) {
+ update["lastModified" + suffix] = value;
+ }
update[key + suffix] = value;
dynfield = true;
}
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index bd8fe97eb..e088cd2c4 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -279,7 +279,7 @@ function delay(ms: number) {
*
* On failure, returns undefined.
*/
-async function captureYoutubeScreenshot(targetUrl: string){
+async function captureYoutubeScreenshot(targetUrl: string) {
// const browser = await launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
// const page = await browser.newPage();
// // await page.setViewport({ width: 1920, height: 1080 });
diff --git a/src/server/Search.ts b/src/server/Search.ts
index decd1f5b1..3869867cd 100644
--- a/src/server/Search.ts
+++ b/src/server/Search.ts
@@ -7,11 +7,10 @@ export namespace Search {
export async function updateDocument(document: any) {
try {
- const res = await rp.post(pathTo("update"), {
+ return await rp.post(pathTo("update"), {
headers: { 'content-type': 'application/json' },
body: JSON.stringify([document])
});
- return res;
} catch (e) {
// console.warn("Search error: " + e + document);
}
@@ -19,11 +18,10 @@ export namespace Search {
export async function updateDocuments(documents: any[]) {
try {
- const res = await rp.post(pathTo("update"), {
+ return await rp.post(pathTo("update"), {
headers: { 'content-type': 'application/json' },
body: JSON.stringify(documents)
});
- return res;
} catch (e) {
// console.warn("Search error: ", e, documents);
}
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index f9351ba05..b33e76c0b 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -203,7 +203,7 @@ export namespace WebSocket {
Database.Instance.update(newValue.id, newValue, () =>
socket.broadcast.emit(MessageStore.SetField.Message, newValue));
if (newValue.type === Types.Text) { // if the newValue has sring type, then it's suitable for searching -- pass it to SOLR
- Search.updateDocument({ id: newValue.id, data: (newValue as any).data });
+ Search.updateDocument({ id: newValue.id, data: { set: (newValue as any).data } });
}
}
@@ -230,7 +230,6 @@ export namespace WebSocket {
"RichTextField": ["_t", value => value.Text],
"date": ["_d", value => new Date(value.date).toISOString()],
"proxy": ["_i", "fieldId"],
- // "proxy": ["", "fieldId"],
"list": ["_l", list => {
const results = [];
for (const value of list.fields) {
@@ -291,6 +290,9 @@ export namespace WebSocket {
if (term !== undefined) {
const { suffix, value } = term;
update[key + suffix] = { set: value };
+ if (key.endsWith('lastModified')) {
+ update["lastModified" + suffix] = value;
+ }
}
}
if (dynfield) {