aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/DocumentTypes.ts1
-rw-r--r--src/client/documents/Documents.ts5
-rw-r--r--src/client/util/CurrentUserUtils.ts119
-rw-r--r--src/client/util/GroupMemberView.scss2
-rw-r--r--src/client/util/SelectionManager.ts3
-rw-r--r--src/client/util/SharingManager.tsx12
-rw-r--r--src/client/views/ContextMenuItem.tsx4
-rw-r--r--src/client/views/DocumentButtonBar.tsx8
-rw-r--r--src/client/views/DocumentDecorations.tsx19
-rw-r--r--src/client/views/EditableView.tsx2
-rw-r--r--src/client/views/MainView.scss135
-rw-r--r--src/client/views/MainView.tsx403
-rw-r--r--src/client/views/PreviewCursor.tsx6
-rw-r--r--src/client/views/PropertiesButtons.scss119
-rw-r--r--src/client/views/PropertiesButtons.tsx598
-rw-r--r--src/client/views/collections/CollectionDockingView.scss17
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx20
-rw-r--r--src/client/views/collections/CollectionMenu.tsx79
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx1
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx2
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx7
-rw-r--r--src/client/views/collections/CollectionView.scss1
-rw-r--r--src/client/views/collections/CollectionView.tsx11
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx6
-rw-r--r--src/client/views/collections/SchemaTable.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx12
-rw-r--r--src/client/views/collections/collectionFreeForm/FormatShapePane.tsx27
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.scss620
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.tsx852
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx3
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx3
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx5
-rw-r--r--src/client/views/nodes/DocumentView.tsx62
-rw-r--r--src/client/views/nodes/FieldView.tsx1
-rw-r--r--src/client/views/nodes/FontIconBox.scss58
-rw-r--r--src/client/views/nodes/FontIconBox.tsx59
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx4
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx41
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts8
-rw-r--r--src/fields/Doc.ts6
-rw-r--r--src/server/ApiManagers/UtilManager.ts1
-rw-r--r--src/server/RouteManager.ts8
-rw-r--r--src/server/Search.ts2
-rw-r--r--src/server/downsize.ts2
-rw-r--r--src/server/server_Initialization.ts38
-rw-r--r--src/server/websocket.ts26
49 files changed, 3083 insertions, 349 deletions
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 985fcce11..659f78970 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -13,6 +13,7 @@ export enum DocumentType {
INK = "ink", // ink stroke
SCREENSHOT = "screenshot", // view of a desktop application
FONTICON = "fonticonbox", // font icon
+ MENUICON = "menuiconbox",
QUERY = "query", // search query
LABEL = "label", // simple text label
BUTTON = "button", // onClick button
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 40eae0528..300af4e50 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -151,6 +151,7 @@ export interface DocumentOptions {
annotationOn?: Doc;
removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
dbDoc?: Doc;
+ menuIcon?: boolean; // if the font icon box is in the menu
linkRelationship?: string; // type of relatinoship a link represents
ischecked?: ScriptField; // returns whether a font icon box is checked
activeInkPen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts)
@@ -300,6 +301,10 @@ export namespace Docs {
layout: { view: FontIconBox, dataField: defaultDataKey },
options: { _width: 40, _height: 40, borderRounding: "100%" },
}],
+ // [DocumentType.RECOMMENDATION, {
+ // layout: { view: RecommendationsBox, dataField: defaultDataKey },
+ // options: { _width: 200, _height: 200 },
+ // }],
[DocumentType.WEBCAM, {
layout: { view: DashWebRTCVideo, dataField: defaultDataKey }
}],
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 6d752832a..7b6868f73 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -38,6 +38,18 @@ export class CurrentUserUtils {
@observable public static GuestWorkspace: Doc | undefined;
@observable public static GuestMobile: Doc | undefined;
+ @observable public static toolsBtn: any | undefined;
+ @observable public static libraryBtn: any | undefined;
+ @observable public static searchBtn: any | undefined;
+
+ @observable public static toolsStack: any | undefined;
+ @observable public static workspaceStack: any | undefined;
+ @observable public static catalogStack: any | undefined;
+ @observable public static closedStack: any | undefined;
+ @observable public static searchStack: any | undefined;
+
+ @observable public static selectedPanel: string = "none";
+
// sets up the default User Templates - slideView, queryView, descriptionView
static setupUserTemplateButtons(doc: Doc) {
if (doc["template-button-query"] === undefined) {
@@ -492,6 +504,47 @@ export class CurrentUserUtils {
return doc.myItemCreators as Doc;
}
+ static menuBtnDescriptions(): {
+ title: string, icon: string, click: string,
+ }[] {
+ return [
+ { title: "Workspace", icon: "desktop", click: 'scriptContext.selectMenu("Workspace")' },
+ { title: "Catalog", icon: "file", click: 'scriptContext.selectMenu("Catalog")' },
+ { title: "Archive", icon: "archive", click: 'scriptContext.selectMenu("Archive")' },
+ { title: "Import", icon: "upload", click: 'scriptContext.selectMenu("Import")' },
+ { title: "Sharing", icon: "users", click: 'scriptContext.selectMenu("Sharing")' },
+ { title: "Tools", icon: "wrench", click: 'scriptContext.selectMenu("Tools")' },
+ { title: "Help", icon: "question-circle", click: 'scriptContext.selectMenu("Help")' },
+ { title: "Settings", icon: "cog", click: 'scriptContext.selectMenu("Settings")' },
+ ];
+ }
+
+ static setupMenuPanel(doc: Doc) {
+ if (doc.menuStack === undefined) {
+ const buttons = CurrentUserUtils.menuBtnDescriptions();
+ const menuBtns = buttons.map(({ title, icon, click }) => Docs.Create.FontIconDocument({
+ icon,
+ menuIcon: true,
+ title,
+ _backgroundColor: "black",
+ stayInCollection: true,
+ _width: 60,
+ _height: 60,
+ onClick: ScriptField.MakeScript(click, { scriptContext: "any" }),
+ }));
+
+ doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, {
+ title: "menuItemPanel",
+ _backgroundColor: "black",
+ _gridGap: 0,
+ _yMargin: 0,
+ _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, lockedPosition: true, _chromeStatus: "disabled",
+ }));
+ }
+ return doc.menuStack as Doc;
+ }
+
+
// Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu
static setupActiveMobileMenu(doc: Doc) {
if (doc.activeMobileMenu === undefined) {
@@ -601,6 +654,8 @@ export class CurrentUserUtils {
const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc);
const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc);
+ doc["tabs-button-tools"] = undefined;
+
if (doc.myCreators === undefined) {
doc.myCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], {
title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0,
@@ -617,8 +672,11 @@ export class CurrentUserUtils {
if (doc["tabs-button-tools"] === undefined) {
const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], {
- _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true
+ _width: 500, lockedPosition: true, _chromeStatus: "disabled", hideFilterView: true, title: "tools stack", forceActive: true
})) as any as Doc;
+
+ CurrentUserUtils.toolsStack = toolsStack;
+
doc["tabs-button-tools"] = new PrefetchProxy(Docs.Create.ButtonDocument({
_width: 35, _height: 25, title: "Tools", _fontSize: "10pt",
letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
@@ -627,41 +685,63 @@ export class CurrentUserUtils {
dragFactory: toolsStack,
removeDropProperties: new List<string>(["lockedPosition"]),
stayInCollection: true,
+ hideFilterView: true,
targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"),
}));
}
- (doc["tabs-button-tools"] as Doc).sourcePanel; // prefetch sourcePanel
- return doc["tabs-button-tools"] as Doc;
+ (doc["tabs-button-tools"] as any as Doc).sourcePanel; // prefetch sourcePanel
+
+ return doc["tabs-button-tools"] as any as Doc;
}
static setupWorkspaces(doc: Doc) {
// setup workspaces library item
+ doc.myWorkspaces === undefined;
if (doc.myWorkspaces === undefined) {
doc.myWorkspaces = new PrefetchProxy(Docs.Create.TreeDocument([], {
- title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true,
+ title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true,
}));
}
const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`);
(doc.myWorkspaces as Doc).contextMenuScripts = new List<ScriptField>([newWorkspace!]);
(doc.myWorkspaces as Doc).contextMenuLabels = new List<string>(["Create New Workspace"]);
+ const workspaces = doc.myWorkspaces as Doc;
+
+ CurrentUserUtils.workspaceStack = new PrefetchProxy(Docs.Create.TreeDocument([workspaces], {
+ title: " ", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+ treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
+ lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same"
+ })) as any as Doc;
+
return doc.myWorkspaces as Doc;
}
static setupCatalog(doc: Doc) {
+ doc.myCatalog === undefined;
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,
+ childDropAction: "alias", targetDropAction: "same", stayInCollection: true, treeViewOpen: true,
}));
}
+
+ const catalog = doc.myCatalog as Doc;
+
+ CurrentUserUtils.catalogStack = new PrefetchProxy(Docs.Create.TreeDocument([catalog], {
+ title: " ", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+ treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
+ lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same"
+ })) as any as Doc;
+
return doc.myCatalog as Doc;
}
static setupRecentlyClosed(doc: Doc) {
// setup Recently Closed library item
+ doc.myRecentlyClosed === undefined;
if (doc.myRecentlyClosed === undefined) {
doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], {
- title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, stayInCollection: true,
+ title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, stayInCollection: true,
}));
}
// this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
@@ -670,6 +750,14 @@ export class CurrentUserUtils {
(doc.myRecentlyClosed as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
(doc.myRecentlyClosed as Doc).contextMenuLabels = new List<string>(["Clear All"]);
+ const recentlyClosed = doc.myRecentlyClosed as Doc;
+
+ CurrentUserUtils.closedStack = new PrefetchProxy(Docs.Create.TreeDocument([recentlyClosed], {
+ title: " ", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+ treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
+ lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same"
+ })) as any as Doc;
+
return doc.myRecentlyClosed as Doc;
}
// setup the Library button which will display the library panel. This panel includes a collection of workspaces, documents, and recently closed views
@@ -681,7 +769,7 @@ export class CurrentUserUtils {
if (doc["tabs-button-library"] === undefined) {
const libraryStack = new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], {
title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
- treeViewTruncateTitleWidth: 150,
+ treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same"
})) as any as Doc;
doc["tabs-button-library"] = new PrefetchProxy(Docs.Create.ButtonDocument({
@@ -701,6 +789,7 @@ export class CurrentUserUtils {
// setup the Search button which will display the search panel.
static setupSearchBtnPanel(doc: Doc, sidebarContainer: Doc) {
+ doc["tabs-button-search"] = undefined;
if (doc["tabs-button-search"] === undefined) {
doc["tabs-button-search"] = new PrefetchProxy(Docs.Create.ButtonDocument({
_width: 50, _height: 25, title: "Search", _fontSize: "10pt",
@@ -711,8 +800,9 @@ export class CurrentUserUtils {
lockedPosition: true,
onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel")
}));
+ CurrentUserUtils.searchStack = new PrefetchProxy(Docs.Create.QueryDocument({ title: "search stack", })) as any as Doc;
}
- return doc["tabs-button-search"] as Doc;
+ return doc["tabs-button-search"] as any as Doc;
}
static setupSidebarContainer(doc: Doc) {
@@ -728,17 +818,17 @@ export class CurrentUserUtils {
// setup the list of sidebar mode buttons which determine what is displayed in the sidebar
static async setupSidebarButtons(doc: Doc) {
const sidebarContainer = CurrentUserUtils.setupSidebarContainer(doc);
- const toolsBtn = await CurrentUserUtils.setupToolsBtnPanel(doc, sidebarContainer);
- const libraryBtn = CurrentUserUtils.setupLibraryPanel(doc, sidebarContainer);
- const searchBtn = CurrentUserUtils.setupSearchBtnPanel(doc, sidebarContainer);
+ CurrentUserUtils.toolsBtn = await CurrentUserUtils.setupToolsBtnPanel(doc, sidebarContainer);
+ CurrentUserUtils.libraryBtn = CurrentUserUtils.setupLibraryPanel(doc, sidebarContainer);
+ CurrentUserUtils.searchBtn = CurrentUserUtils.setupSearchBtnPanel(doc, sidebarContainer);
// Finally, setup the list of buttons to display in the sidebar
if (doc["tabs-buttons"] === undefined) {
- doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([libraryBtn, searchBtn, toolsBtn], {
+ doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([CurrentUserUtils.libraryBtn, CurrentUserUtils.searchBtn, CurrentUserUtils.toolsBtn], {
_width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", _columnsHideIfEmpty: true, ignoreClick: true, _chromeStatus: "view-mode",
title: "sidebar btn row stack", backgroundColor: "dimGray",
}));
- (toolsBtn.onClick as ScriptField).script.run({ this: toolsBtn });
+ (CurrentUserUtils.toolsBtn.onClick as ScriptField).script.run({ this: CurrentUserUtils.toolsBtn });
}
}
@@ -749,7 +839,7 @@ export class CurrentUserUtils {
})) as any as Doc
static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
- ...opts, dropAction: "alias", removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100
+ ...opts, dropAction: "alias", removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40
})) as any as Doc
/// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
@@ -855,6 +945,7 @@ export class CurrentUserUtils {
this.setupDocTemplates(doc); // sets up the template menu of templates
this.setupRightSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing
this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
+ this.setupMenuPanel(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
diff --git a/src/client/util/GroupMemberView.scss b/src/client/util/GroupMemberView.scss
index 2fc27ed03..2eb164988 100644
--- a/src/client/util/GroupMemberView.scss
+++ b/src/client/util/GroupMemberView.scss
@@ -44,7 +44,7 @@
background: none;
&:hover {
- text-overflow: visible;
+ text-overflow: unset;
overflow-x: auto;
}
}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 20d881961..05ba00331 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -12,6 +12,7 @@ export namespace SelectionManager {
@observable IsDragging: boolean = false;
SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
+
@action
SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
@@ -32,6 +33,7 @@ export namespace SelectionManager {
}
@action
DeselectDoc(docView: DocumentView): void {
+
if (manager.SelectedDocuments.get(docView)) {
manager.SelectedDocuments.delete(docView);
docView.props.whenActiveChanged(false);
@@ -40,6 +42,7 @@ export namespace SelectionManager {
}
@action
DeselectAll(): void {
+
Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false));
manager.SelectedDocuments.clear();
Doc.UserDoc().activeSelection = new List<Doc>([]);
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 5896bdf92..eda12a5e9 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -72,8 +72,6 @@ export default class SharingManager extends React.Component<{}> {
@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 get linkVisible() {
// return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false;
// }
@@ -148,7 +146,7 @@ export default class SharingManager extends React.Component<{}> {
const members: string[] = JSON.parse(StrCast(group.members));
const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
- const target = this.targetDoc!;
+ const target = targetDoc || this.targetDoc!;
const ACL = `ACL-${StrCast(group.groupName)}`;
target.author === Doc.CurrentUserEmail && distributeAcls(ACL, permission as SharingPermissions, target);
@@ -181,6 +179,12 @@ export default class SharingManager extends React.Component<{}> {
}
}
+ 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);
+ }
+
/**
* Removes the documents shared with a user through a group when the user is removed from the group.
* @param group
@@ -222,7 +226,7 @@ export default class SharingManager extends React.Component<{}> {
setInternalSharing = (recipient: ValidatedUser, permission: string) => {
const { user, notificationDoc } = recipient;
- const target = this.targetDoc!;
+ const target = targetDoc || this.targetDoc!;
const key = user.email.replace('.', '_');
const ACL = `ACL-${key}`;
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 81432968d..7e233ec04 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -80,7 +80,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
render() {
if ("event" in this.props) {
return (
- <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onClick={this.handleEvent}>
+ <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onPointerDown={this.handleEvent}>
{this.props.icon ? (
<span className="icon-background">
<FontAwesomeIcon icon={this.props.icon} size="sm" />
@@ -95,7 +95,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
const where = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "flex-start" : this._overPosY > window.innerHeight * 2 / 3 ? "flex-end" : "center";
const marginTop = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "20px" : this._overPosY > window.innerHeight * 2 / 3 ? "-20px" : "";
const submenu = !this.overItem ? (null) :
- <div className="contextMenu-subMenu-cont" style={{ marginLeft: "25%", left: "0px", marginTop }}>
+ <div className="contextMenu-subMenu-cont" style={{ marginLeft: "90%", left: "0px", marginTop }}>
{this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
</div>;
if (!("noexpand" in this.props)) {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 6b85616c2..23a3e1684 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -281,7 +281,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
{DocumentLinksButton.StartLink ? <div className="documentButtonBar-button">
<DocumentLinksButton links={this.view0.allLinks} View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
</div> : null}
- <div className="documentButtonBar-button">
+ {/* <div className="documentButtonBar-button">
{this.templateButton}
</div>
<div className="documentButtonBar-button">
@@ -289,16 +289,16 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
</div>
<div className="documentButtonBar-button">
{this.contextButton}
- </div>
+ </div> */}
<div className="documentButtonBar-button">
{this.pinButton}
</div>
- <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
+ {/* <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
{this.considerGoogleDocsPush}
</div>
<div className="documentButtonBar-button" style={{ display: !considerPull ? "none" : "" }}>
{this.considerGoogleDocsPull}
- </div>
+ </div> */}
</div>;
}
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 7fc4a5c99..dd5303904 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,5 +1,5 @@
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 } from '@fortawesome/free-solid-svg-icons';
+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 { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, reaction, runInAction, get } from "mobx";
import { observer } from "mobx-react";
@@ -47,6 +47,7 @@ library.add(faAngleDoubleRight);
library.add(faAngleLeft);
library.add(faAngleRight);
library.add(faPause);
+library.add(faExternalLinkAlt);
@observer
export class DocumentDecorations extends React.Component<{}, { value: string }> {
@@ -533,12 +534,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
const ink = Cast(doc.data, InkField)?.inkData;
if (ink) {
const newPoints: { X: number, Y: number }[] = [];
- for (var i = 0; i < ink.length; i++) {
+ ink.forEach(i => {
// (new x — oldx) + (oldxpoint * newWidt)/oldWidth
- const newX = (doc.x - this._inkDocs[index].x) + (ink[i].X * doc._width) / this._inkDocs[index].width;
- const newY = (doc.y - this._inkDocs[index].y) + (ink[i].Y * doc._height) / this._inkDocs[index].height;
+ const newX = ((doc.x || 0) - this._inkDocs[index].x) + (i.X * (doc._width || 0)) / this._inkDocs[index].width;
+ const newY = ((doc.y || 0) - this._inkDocs[index].y) + (i.Y * (doc._height || 0)) / this._inkDocs[index].height;
newPoints.push({ X: newX, Y: newY });
- }
+ });
doc.data = new InkField(newPoints);
}
@@ -617,9 +618,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
</div>}
</> :
<>
- {minimal ? (null) : <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top"><div className="documentDecorations-contextMenu" key="menu" onPointerDown={this.onSettingsDown}>
- <FontAwesomeIcon size="lg" icon="cog" />
- </div></Tooltip>}
+ {/* {minimal ? (null) : <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top"><div className="documentDecorations-contextMenu" key="menu" onPointerDown={this.onSettingsDown}>
+ <FontAwesomeIcon size="lg" icon="bars" />
+ </div></Tooltip>} */}
<div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
<span style={{ width: "100%", display: "inline-block", cursor: "move" }}>{`${this.selectionTitle}`}</span>
</div>
@@ -666,7 +667,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
{"_"}
</div></Tooltip>}
<Tooltip title={<><div className="dash-tooltip">Open Document In Tab</div></>} placement="top"><div className="documentDecorations-openInTab" onPointerDown={this.onMaximizeDown}>
- {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."}
+ {SelectionManager.SelectedDocuments().length === 1 ? <FontAwesomeIcon icon="external-link-alt" className="documentView-minimizedIcon" /> : "..."}
</div></Tooltip>
<div id="documentDecorations-rotation" className="documentDecorations-rotation"
onPointerDown={this.onRotateDown}> ⟲ </div>
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index c7b149842..f9d060681 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -191,7 +191,7 @@ export class EditableView extends React.Component<EditableProps> {
return (this.props.contents instanceof ObjectField ? (null) :
<div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
ref={this._ref}
- style={{ display: this.props.display, minHeight: "20px", height: `${this.props.height ? this.props.height : "auto"}`, maxHeight: `${this.props.maxHeight}` }}
+ style={{ display: this.props.display, minHeight: "17px", whiteSpace: "nowrap", height: `${this.props.height ? this.props.height : "auto"}`, maxHeight: `${this.props.maxHeight}` }}
onClick={this.onClick} placeholder={this.props.placeholder}>
<span style={{
fontStyle: this.props.fontStyle, fontSize: this.props.fontSize,
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index e1ddbc533..054a409df 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -102,6 +102,44 @@
user-select: none;
}
+.mainView-propertiesDragger {
+ background-color: rgb(140, 139, 139);
+ height: 55px;
+ width: 15.5px;
+ position: absolute;
+ top: 55%;
+ border: 1px black solid;
+ border-radius: 0;
+ border-top-left-radius: 10px;
+ border-bottom-left-radius: 10px;
+ border-right: unset;
+ z-index: 2;
+
+ .mainView-propertiesDragger-icon {
+ width: 10px;
+ height: 10px;
+ float: left;
+ margin-left: 3px;
+ padding-top: 19px;
+ }
+
+ &:hover {
+ cursor: grab;
+ }
+}
+
+.mainiView-propertiesView {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ position: absolute;
+ right: 0;
+ top: 0;
+ border-left: solid 1px;
+ z-index: 100000;
+ cursor: auto;
+}
+
.mainView-flyoutContainer {
display: flex;
flex-direction: column;
@@ -114,6 +152,75 @@
}
}
+.mainView-menuPanel {
+
+ width: 60px;
+ background-color: black;
+ height: 100%;
+ //overflow-y: scroll;
+ //overflow-x: hidden;
+
+
+ .mainView-menuPanel-button {
+ padding: 7px;
+ padding-left: 7px;
+ width: 100%;
+
+ .mainView-menuPanel-button-wrap {
+ width: 45px;
+ /* padding: 5px; */
+ touch-action: none;
+ background: black;
+ transform-origin: top left;
+ /* margin-bottom: 5px; */
+ margin-top: 5px;
+ margin-right: 25px;
+ border-radius: 8px;
+
+ &:hover {
+ background: rgb(61, 61, 61);
+ cursor: pointer;
+ }
+ }
+ }
+
+ .mainView-menuPanel-button-label {
+ color: white;
+ margin-left: px;
+ margin-right: 4px;
+ border-radius: 8px;
+ width: 42px;
+ position: relative;
+ text-align: center;
+ font-size: 8px;
+ margin-top: 1px;
+ letter-spacing: normal;
+ padding: 3px;
+ background-color: inherit;
+ }
+
+ .mainView-menuPanel-button-icon {
+ width: auto;
+ height: 35px;
+ padding: 5px;
+ }
+
+ svg {
+ width: 95% !important;
+ height: 95%;
+ }
+}
+
+.mainView-searchPanel {
+ width: 100%;
+ height: 33px;
+ background-color: black;
+ color: white;
+ text-align: center;
+ vertical-align: middle;
+ padding-top: 6px;
+}
+
.mainView-mainDiv {
width: 100%;
height: 100%;
@@ -162,26 +269,44 @@
display: flex;
flex-direction: column;
z-index: 2;
+
+ .mainView-libraryFlyout-close {
+ right: 6;
+ top: 5;
+ position: absolute;
+ margin-right: 6px;
+ z-index: 10;
+ margin-bottom: 10;
+ }
}
.mainView-expandFlyoutButton {
position: absolute;
- top: 100px;
- right: 30px;
+ top: 120px;
+ right: 55px;
cursor: pointer;
}
.mainView-libraryHandle {
- width: 20px;
+ width: 28px;
left: calc(100% - 10px);
- height: 40px;
+ height: 55px;
top: 50%;
border: 1px solid black;
- border-radius: 5px;
+ border-radius: 8px;
position: absolute;
z-index: 2;
touch-action: none;
cursor: ew-resize;
+
+ .mainView-libraryHandle-icon {
+ width: 10px;
+ height: 10px;
+ float: right;
+ margin-right: 3px;
+ padding-top: 19px;
+ }
+
}
.mainView-workspace {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index bb73df75c..c83aeee25 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,7 +1,6 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faHireAHelper, faBuffer } from '@fortawesome/free-brands-svg-icons';
import * as fa from '@fortawesome/free-solid-svg-icons';
-import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -12,51 +11,55 @@ import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
import { listSpec } from '../../fields/Schema';
+import { ScriptField } from '../../fields/ScriptField';
import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
import { TraceMobx } from '../../fields/util';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnZero, returnTrue, Utils, returnEmptyFilter } from '../../Utils';
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils';
import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
+import HypothesisAuthenticationManager from '../apis/HypothesisAuthenticationManager';
import { DocServer } from '../DocServer';
import { Docs, DocumentOptions } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import { DocumentManager } from '../util/DocumentManager';
+import GroupManager from '../util/GroupManager';
import { HistoryUtil } from '../util/History';
-import RichTextMenu from './nodes/formattedText/RichTextMenu';
import { Scripting } from '../util/Scripting';
+import { SelectionManager } from '../util/SelectionManager';
import SettingsManager from '../util/SettingsManager';
-import GroupManager from '../util/GroupManager';
import SharingManager from '../util/SharingManager';
+import { SnappingManager } from '../util/SnappingManager';
import { Transform } from '../util/Transform';
+import { TimelineMenu } from './animationtimeline/TimelineMenu';
import { CollectionDockingView } from './collections/CollectionDockingView';
+import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane";
import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu';
+import { PropertiesView } from './collections/collectionFreeForm/PropertiesView';
import { CollectionLinearView } from './collections/CollectionLinearView';
+import CollectionMenu from './collections/CollectionMenu';
import { CollectionView, CollectionViewType } from './collections/CollectionView';
import { ContextMenu } from './ContextMenu';
import { DictationOverlay } from './DictationOverlay';
import { DocumentDecorations } from './DocumentDecorations';
import GestureOverlay from './GestureOverlay';
+import { ANTIMODEMENU_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 { OverlayView } from './OverlayView';
import PDFMenu from './pdf/PDFMenu';
import { PreviewCursor } from './PreviewCursor';
-import { ScriptField } from '../../fields/ScriptField';
-import { TimelineMenu } from './animationtimeline/TimelineMenu';
-import { SnappingManager } from '../util/SnappingManager';
-import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
-import { DocumentManager } from '../util/DocumentManager';
-import { DocumentLinksButton } from './nodes/DocumentLinksButton';
-import { LinkMenu } from './linking/LinkMenu';
-import { LinkDocPreview } from './nodes/LinkDocPreview';
-import { TaskCompletionBox } from './nodes/TaskCompletedBox';
-import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
-import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane";
-import HypothesisAuthenticationManager from '../apis/HypothesisAuthenticationManager';
-import CollectionMenu from './collections/CollectionMenu';
+import { undoBatch } from '../util/UndoManager';
@observer
export class MainView extends React.Component {
@@ -69,8 +72,8 @@ export class MainView extends React.Component {
@observable private _panelWidth: number = 0;
@observable private _panelHeight: number = 0;
- @observable private _flyoutTranslate: boolean = true;
- @observable public flyoutWidth: number = 250;
+ @observable private _flyoutTranslate: boolean = false;
+ @observable public flyoutWidth: number = 0;
private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); }
@computed private get userDoc() { return Doc.UserDoc(); }
@@ -78,7 +81,27 @@ export class MainView extends React.Component {
@computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
@computed public get sidebarButtonsDoc() { return Cast(this.userDoc["tabs-buttons"], Doc) as Doc; }
+ @observable public sidebarContent: any = this.userDoc?.["tabs-panelContainer"];
+ @observable public panelContent: string = "none";
+ @observable public showProperties: boolean = false;
public isPointerDown = false;
+ @computed get selectedDocumentView() {
+ if (SelectionManager.SelectedDocuments().length) {
+ return SelectionManager.SelectedDocuments()[0];
+ } else { return undefined; }
+ }
+
+ @observable _propertiesWidth: number = 0;
+ propertiesWidth = () => Math.max(0, Math.min(this._panelWidth - 50, this._propertiesWidth));
+
+ @computed get propertiesIcon() {
+ if (this.propertiesWidth() < 10) {
+ return "chevron-left";
+ } else {
+ return "chevron-right";
+ }
+ }
+ @observable propertiesDownX: number | undefined;
componentDidMount() {
DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's
@@ -148,7 +171,8 @@ export class MainView extends React.Component {
fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faChevronLeft, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper,
- fa.faBezierCurve, fa.faCircle, fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer);
+ 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);
this.initEventListeners();
this.initAuthenticationRouters();
}
@@ -210,7 +234,7 @@ export class MainView extends React.Component {
const freeformOptions: DocumentOptions = {
x: 0,
y: 400,
- _width: this._panelWidth * .7,
+ _width: this._panelWidth * .7 - this.propertiesWidth() * 0.7,
_height: this._panelHeight,
title: "Collection " + workspaceCount,
};
@@ -276,16 +300,21 @@ export class MainView extends React.Component {
@action
onResize = (r: any) => {
- this._panelWidth = r.offset.width;
+ this._panelWidth = r.offset.width;// - this.propertiesWidth();
this._panelHeight = r.offset.height;
}
- getPWidth = () => this._panelWidth;
+
+ @action
+ getPWidth = () => this._panelWidth - this.propertiesWidth()
+
getPHeight = () => this._panelHeight;
getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
- defaultBackgroundColors = (doc: Doc) => {
+ defaultBackgroundColors = (doc: Opt<Doc>) => {
+ if (this.panelContent === doc?.title) return "lightgrey";
if (this.darkScheme) {
switch (doc?.type) {
+ case DocumentType.MENUICON: return "white";
case DocumentType.RTF || DocumentType.LABEL || DocumentType.BUTTON: return "#2d2d2d";
case DocumentType.LINK:
case DocumentType.COL: {
@@ -295,6 +324,7 @@ export class MainView extends React.Component {
}
} else {
switch (doc?.type) {
+ case DocumentType.MENUICON: return "black";
case DocumentType.RTF: return "#f1efeb";
case DocumentType.BUTTON:
case DocumentType.LABEL: return "lightgray";
@@ -307,7 +337,8 @@ export class MainView extends React.Component {
}
}
@computed get mainDocView() {
- return <DocumentView Document={this.mainContainer!}
+ return <DocumentView
+ Document={this.mainContainer!}
DataDoc={undefined}
LibraryPath={emptyPath}
addDocument={undefined}
@@ -323,7 +354,6 @@ export class MainView extends React.Component {
NativeWidth={returnZero}
PanelWidth={this.getPWidth}
PanelHeight={this.getPHeight}
- renderDepth={0}
focus={emptyFunction}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
@@ -331,103 +361,69 @@ export class MainView extends React.Component {
docFilters={returnEmptyFilter}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
+ renderDepth={-1}
/>;
}
@computed get dockingContent() {
TraceMobx();
const mainContainer = this.mainContainer;
- const width = this.flyoutWidth;
- return <Measure offset onResize={this.onResize}>
- {({ measureRef }) =>
- <div ref={measureRef} className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)` }}>
- {!mainContainer ? (null) : this.mainDocView}
- </div>
- }
- </Measure>;
- }
-
- _canClick = false;
- onPointerDown = (e: React.PointerEvent) => {
- if (this._flyoutTranslate) {
- this._canClick = true;
- this._flyoutSizeOnDown = e.clientX;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointerup", this.onPointerUp);
- e.stopPropagation();
- e.preventDefault();
- }
+ const width = this.flyoutWidth + this.propertiesWidth();
+ return <div className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)` }}>
+ {!mainContainer ? (null) : this.mainDocView}
+ </div>;
}
@action
- pointerLeaveDragger = () => {
- if (!this._flyoutTranslate) {
- this.flyoutWidth = 0;
- this._flyoutTranslate = true;
- }
+ onPropertiesPointerDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
+ this._propertiesWidth = this._panelWidth - e.clientX;
+ return false;
+ }), returnFalse, action(() => this._propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 250) : 0), false);
}
@action
- onPointerMove = (e: PointerEvent) => {
- this.flyoutWidth = Math.max(e.clientX, 0);
- Math.abs(this.flyoutWidth - this._flyoutSizeOnDown) > 6 && (this._canClick = false);
- this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30;
- }
- @action
- onPointerUp = (e: PointerEvent) => {
- if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4 && this._canClick) {
- this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0;
- this.flyoutWidth && (this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30);
+ onFlyoutPointerDown = (e: React.PointerEvent) => {
+ this.panelContent = "none";
+ if (this._flyoutTranslate) {
+ setupMoveUpEvents(this, e, action((e: PointerEvent) => {
+ this.flyoutWidth = Math.max(e.clientX, 0);
+ this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30;
+ if (this.flyoutWidth === 0) {
+ CurrentUserUtils.selectedPanel = "none";
+ }
+ return false;
+ }), emptyFunction, action(() => {
+ this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0;
+ this.flyoutWidth && (this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30);
+ if (this.flyoutWidth === 0) {
+ CurrentUserUtils.selectedPanel = "none";
+ }
+ }));
}
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
}
+
flyoutWidthFunc = () => this.flyoutWidth;
addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
doc.dockingConfig ? this.openWorkspace(doc) :
CollectionDockingView.AddRightSplit(doc, libraryPath);
}
- sidebarScreenToLocal = () => new Transform(0, (RichTextMenu.Instance.Pinned ? -35 : 0) + (CollectionMenu.Instance.Pinned ? -35 : 0), 1);
+ 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(0, -this._buttonBarHeight);
+ @computed get closePosition() { return 55 + this.flyoutWidth; }
@computed get flyout() {
- const sidebarContent = this.userDoc?.["tabs-panelContainer"];
- if (!(sidebarContent instanceof Doc)) {
- return (null);
- }
- return <div className="mainView-flyoutContainer" >
- <div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight - 10/*margin-top*/}px`, backgroundColor: StrCast(this.sidebarButtonsDoc.backgroundColor) }}>
- <DocumentView
- Document={this.sidebarButtonsDoc}
- DataDoc={undefined}
- LibraryPath={emptyPath}
- addDocument={undefined}
- rootSelected={returnTrue}
- addDocTab={this.addDocTabFunc}
- pinToPres={emptyFunction}
- removeDocument={undefined}
- onClick={undefined}
- ScreenToLocalTransform={this.sidebarScreenToLocal}
- ContentScaling={returnOne}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
- PanelWidth={this.flyoutWidthFunc}
- PanelHeight={this.getPHeight}
- renderDepth={0}
- focus={emptyFunction}
- backgroundColor={this.defaultBackgroundColors}
- parentActive={returnTrue}
- whenActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- docFilters={returnEmptyFilter}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined} />
- </div>
- <div className="mainView-contentArea" style={{ position: "relative", height: `calc(100% - ${this._buttonBarHeight}px)`, width: "100%", overflow: "visible" }}>
+ if (!this.sidebarContent) return null;
+ return <div className="mainView-libraryFlyout">
+ <div className="mainView-contentArea" style={{ position: "relative", height: `100%`, width: "100%", overflow: "visible" }}>
+ {this.flyoutWidth > 0 ? <div className="mainView-libraryFlyout-close"
+ onPointerDown={this.closeFlyout}>
+ <FontAwesomeIcon icon="times" color="black" size="sm" />
+ </div> : null}
+
<DocumentView
- Document={sidebarContent}
+ Document={this.sidebarContent}
DataDoc={undefined}
LibraryPath={emptyPath}
addDocument={undefined}
@@ -450,52 +446,166 @@ export class MainView extends React.Component {
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined} />
- <div className="buttonContainer" >
- <button className="mainView-settings" key="settings" onClick={() => SettingsManager.Instance.open()}>
- <FontAwesomeIcon icon="cog" size="lg" />
- </button>
- </div>
+ ContainingCollectionDoc={undefined}
+ relative={true}
+ />
</div>
- {this.docButtons}
+ {this.docButtons}</div>;
+ }
+
+ @computed get menuPanel() {
+
+ return <div className="mainView-menuPanel">
+ <DocumentView
+ Document={Doc.UserDoc().menuStack as Doc}
+ DataDoc={undefined}
+ LibraryPath={emptyPath}
+ addDocument={undefined}
+ addDocTab={this.addDocTabFunc}
+ pinToPres={emptyFunction}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
+ rootSelected={returnTrue}
+ removeDocument={returnFalse}
+ onClick={undefined}
+ ScreenToLocalTransform={this.mainContainerXf}
+ ContentScaling={returnOne}
+ PanelWidth={() => 60}
+ PanelHeight={this.getContentsHeight}
+ renderDepth={0}
+ focus={emptyFunction}
+ backgroundColor={this.defaultBackgroundColors}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={returnEmptyFilter}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ relative={true}
+ scriptContext={this}
+ />
</div>;
}
+
+ @action @undoBatch
+ closeFlyout = () => {
+ CurrentUserUtils.selectedPanel = "none";
+ this.panelContent = "none";
+ this.flyoutWidth = 0;
+ }
+
+ get groupManager() { return GroupManager.Instance; }
+
+ @action @undoBatch
+ selectMenu = (str: string) => {
+ if (this.panelContent === str && this.flyoutWidth !== 0) {
+ CurrentUserUtils.selectedPanel = "none";
+ this.panelContent = "none";
+ this.flyoutWidth = 0;
+ } else {
+ this.panelContent = str;
+ CurrentUserUtils.selectedPanel = str;
+ switch (this.panelContent) {
+ case "Tools": this.sidebarContent.proto = CurrentUserUtils.toolsStack; break;
+ case "Workspace": this.sidebarContent.proto = CurrentUserUtils.workspaceStack; break;
+ case "Catalog": this.sidebarContent.proto = CurrentUserUtils.catalogStack; break;
+ case "Archive": this.sidebarContent.proto = CurrentUserUtils.closedStack; break;
+ case "Settings": this.sidebarContent.proto = SettingsManager.Instance.open(); break;
+ case "Sharing": this.sidebarContent.proto = GroupManager.Instance.open(); break;
+ }
+ if (str === "Settings" || str === "Sharing" || str === "Help" || str === "Import") {
+ CurrentUserUtils.selectedPanel = "none";
+ this.panelContent = "none";
+ this.flyoutWidth = 0;
+ } else {
+ MainView.expandFlyout();
+ }
+ }
+ return true;
+ }
+
+ @action @undoBatch
+ closeProperties = () => {
+ this._propertiesWidth = 0;
+ }
+
+ @computed get propertiesView() {
+ TraceMobx();
+ return <div className="mainView-propertiesView" style={{
+ overflow: this.propertiesWidth() < 15 ? "hidden" : undefined
+ }}>
+ <PropertiesView
+ width={this.propertiesWidth()}
+ height={this._panelHeight}
+ renderDepth={1}
+ ScreenToLocalTransform={Transform.Identity}
+ onDown={this.closeProperties}
+ />
+ </div>;
+ }
+
+ @computed get mainInnerContent() {
+ const rightFlyout = this.propertiesWidth() - 1;
+ return <>
+ {this.menuPanel}
+ <div style={{ display: "contents", flexDirection: "row", position: "relative" }}>
+ <div className="mainView-flyoutContainer" style={{ width: this.flyoutWidth }}>
+ {this.flyoutWidth !== 0 ? <div className="mainView-libraryHandle"
+ onPointerDown={this.onFlyoutPointerDown}
+ style={{ backgroundColor: this.defaultBackgroundColors(undefined) }}>
+ <span title="library View Dragger" style={{
+ width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
+ //height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
+ position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed",
+ top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
+ }} />
+ <div className="mainview-libraryHandle-icon">
+ <FontAwesomeIcon icon="chevron-left" color="black" size="sm" /> </div>
+ </div> : null}
+ <div className="mainView-libraryFlyout" style={{
+ //transformOrigin: this._flyoutTranslate ? "" : "left center",
+ transition: this._flyoutTranslate ? "" : "width .5s",
+ //transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
+ boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.2vw"
+ }}>
+ {this.flyout}
+ {this.expandButton}
+ </div>
+ </div>
+ {this.dockingContent}
+ {this.showProperties ? (null) :
+ <div className="mainView-propertiesDragger" title="Properties View Dragger" onPointerDown={this.onPropertiesPointerDown}
+ style={{ right: rightFlyout, top: "45%" }}>
+ <div className="mainView-propertiesDragger-icon">
+ <FontAwesomeIcon icon={this.propertiesIcon} color="white" size="sm" /> </div>
+ </div>
+ }
+ {this.propertiesWidth() < 10 ? (null) :
+ <div style={{ width: this.propertiesWidth() }}> {this.propertiesView} </div>}
+ </div>
+ </>;
+ }
+
@computed get mainContent() {
- const sidebar = this.userDoc?.["tabs-panelContainer"];
- const n = (RichTextMenu.Instance?.Pinned ? 1 : 0) + (CollectionMenu.Instance?.Pinned ? 1 : 0);
+ //const n = (RichTextMenu.Instance?.Pinned ? 1 : 0) + (CollectionMenu.Instance?.Pinned ? 1 : 0);
+ const n = (CollectionMenu.Instance?.Pinned ? 1 : 0);
const height = `calc(100% - ${n * Number(ANTIMODEMENU_HEIGHT.replace("px", ""))}px)`;
- return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
- <div className="mainView-mainContent" style={{
- color: this.darkScheme ? "rgb(205,205,205)" : "black",
- //change to times 2 for both pinned
- height,
- width: (FormatShapePane.Instance?.Pinned) ? `calc(100% - 200px)` : "100%"
- }} >
- <div style={{ display: "contents", flexDirection: "row", position: "relative" }}>
- <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
- <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown}
- style={{ backgroundColor: this.defaultBackgroundColors(sidebar) }}>
- <span title="library View Dragger" style={{
- width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
- //height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
- position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed",
- top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
- }} />
- </div>
- <div className="mainView-libraryFlyout" style={{
- //transformOrigin: this._flyoutTranslate ? "" : "left center",
- transition: this._flyoutTranslate ? "" : "width .5s",
- //transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
- boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw"
- }}>
- {this.flyout}
- {this.expandButton}
- </div>
+ const pinned = FormatShapePane.Instance?.Pinned;
+ const innerContent = this.mainInnerContent;
+ return !this.userDoc ? (null) : (
+ <Measure offset onResize={this.onResize}>
+ {({ measureRef }) =>
+ <div className="mainView-mainContent" ref={measureRef} style={{
+ color: this.darkScheme ? "rgb(205,205,205)" : "black",
+ //change to times 2 for both pinned
+ height,
+ width: pinned ? `calc(100% - 200px)` : "100%"
+ }} >
+ {innerContent}
</div>
- {this.dockingContent}
- </div>
- </div>);
+ }
+ </Measure>);
}
public static expandFlyout = action(() => {
@@ -505,7 +615,7 @@ export class MainView extends React.Component {
});
@computed get expandButton() {
- return !this._flyoutTranslate ? (<div className="mainView-expandFlyoutButton" title="Re-attach sidebar" onPointerDown={MainView.expandFlyout}><FontAwesomeIcon icon="chevron-right" color="grey" size="lg" /></div>) : (null);
+ 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);
@@ -599,8 +709,16 @@ export class MainView extends React.Component {
</svg>;
}
+ @computed get search() {
+ return <div className="mainView-searchPanel">
+ <div style={{ float: "left", marginLeft: "10px" }}>{Doc.CurrentUserEmail}</div>
+ <div>SEARCH GOES HERE</div>
+ </div>;
+ }
+
render() {
return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} ref={this._mainViewRef}>
+
{this.inkResources}
<DictationOverlay />
<SharingManager />
@@ -609,9 +727,10 @@ export class MainView extends React.Component {
<GoogleAuthenticationManager />
<HypothesisAuthenticationManager />
<DocumentDecorations />
+ {/* {this.search} */}
<CollectionMenu />
<FormatShapePane />
- <RichTextMenu key="rich" />
+ <div style={{ display: "none" }}><RichTextMenu key="rich" /></div>
{LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
{DocumentLinksButton.EditLink ? <LinkMenu location={DocumentLinksButton.EditLinkLoc} docView={DocumentLinksButton.EditLink} addDocTab={DocumentLinksButton.EditLink.props.addDocTab} changeFlyout={emptyFunction} /> : (null)}
{LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} backgroundColor={this.defaultBackgroundColors}
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index b4116e980..d7034fcfb 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -112,10 +112,10 @@ export class PreviewCursor extends React.Component<{}> {
} else if (e.clipboardData.items.length) {
const batch = UndoManager.StartBatch("collection view drop");
const files: File[] = [];
- for (let i = 0; i < e.clipboardData.items.length; i++) {
- const file = e.clipboardData.items[i].getAsFile();
+ Array.from(e.clipboardData.items).forEach(item => {
+ const file = item.getAsFile();
file && files.push(file);
- }
+ });
const generatedDocuments = await DocUtils.uploadFilesToDocs(files, { x: newPoint[0], y: newPoint[1] });
generatedDocuments.forEach(PreviewCursor._addDocument);
batch.end();
diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
new file mode 100644
index 000000000..e099122c4
--- /dev/null
+++ b/src/client/views/PropertiesButtons.scss
@@ -0,0 +1,119 @@
+@import "globalCssVariables";
+
+$linkGap : 3px;
+
+.propertiesButtons-linkFlyout {
+ grid-column: 2/4;
+}
+
+.propertiesButtons-linkButton-empty:hover {
+ background: $main-accent;
+ transform: scale(1.05);
+ cursor: pointer;
+}
+
+.propertiesButtons-linkButton-nonempty:hover {
+ background: $main-accent;
+ transform: scale(1.05);
+ cursor: pointer;
+}
+
+.propertiesButtons-linkButton-empty,
+.propertiesButtons-linkButton-nonempty {
+ height: 30px;
+ width: 30px;
+ border-radius: 5px;
+ opacity: 0.9;
+ pointer-events: auto;
+ background-color: #121721;
+ color: #fcfbf7;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 75%;
+ transition: transform 0.2s;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-right: 10px;
+
+ &:hover {
+ background: $main-accent;
+ transform: scale(1.05);
+ cursor: pointer;
+ }
+}
+
+.propertiesButtons {
+ margin-top: $linkGap;
+ grid-column: 1/4;
+ width: max-content;
+ height: auto;
+ display: flex;
+ flex-direction: row;
+ overflow-x: visible;
+}
+
+.onClickFlyout-editScript {
+ text-align: center;
+ border: 0.5px solid grey;
+ background-color: rgb(230, 230, 230);
+ border-radius: 9px;
+ padding: 4px;
+}
+
+
+.propertiesButtons-button {
+ pointer-events: auto;
+ padding-right: 5px;
+ width: 25px;
+ border-radius: 5px;
+ margin-right: 18px;
+}
+
+.propertiesButtons-linker {
+ height: 30px;
+ width: 30px;
+ text-align: center;
+ border-radius: 5px;
+ pointer-events: auto;
+ color: $dark-color;
+ border: $dark-color 1px solid;
+ transition: 0.2s ease all;
+ margin-right: 5px;
+}
+
+.propertiesButtons-linker:hover {
+ cursor: pointer;
+ transform: scale(1.05);
+}
+
+
+@-moz-keyframes spin {
+ 100% {
+ -moz-transform: rotate(360deg);
+ }
+}
+
+@-webkit-keyframes spin {
+ 100% {
+ -webkit-transform: rotate(360deg);
+ }
+}
+
+@keyframes spin {
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes shadow-pulse {
+ 0% {
+ box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8);
+ }
+
+ 100% {
+ box-shadow: 0 0 0 10px rgba(0, 255, 0, 0);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
new file mode 100644
index 000000000..59e7cc7c8
--- /dev/null
+++ b/src/client/views/PropertiesButtons.tsx
@@ -0,0 +1,598 @@
+import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
+import { faArrowAltCircleDown, faArrowAltCircleRight, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faPhotoVideo, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, DocListCast } from "../../fields/Doc";
+import { RichTextField } from '../../fields/RichTextField';
+import { Cast, NumCast, BoolCast } from "../../fields/Types";
+import { emptyFunction, setupMoveUpEvents, Utils } from "../../Utils";
+import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
+import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
+import { Docs, DocUtils } from '../documents/Documents';
+import { DragManager } from '../util/DragManager';
+import { CollectionDockingView, DockedFrameRenderer } from './collections/CollectionDockingView';
+import { ParentDocSelector } from './collections/ParentDocumentSelector';
+import './collections/ParentDocumentSelector.scss';
+import './PropertiesButtons.scss';
+import { MetadataEntryMenu } from './MetadataEntryMenu';
+import { DocumentView } from './nodes/DocumentView';
+import { GoogleRef } from "./nodes/formattedText/FormattedTextBox";
+import { TemplateMenu } from "./TemplateMenu";
+import { Template, Templates } from "./Templates";
+import React = require("react");
+import { Tooltip } from '@material-ui/core';
+import { SelectionManager } from '../util/SelectionManager';
+import SharingManager from '../util/SharingManager';
+import { GooglePhotos } from '../apis/google_docs/GooglePhotosClientUtils';
+import { ImageField } from '../../fields/URLField';
+import { undoBatch, UndoManager } from '../util/UndoManager';
+import { DocumentType } from '../documents/DocumentTypes';
+import { CollectionFreeFormView } from './collections/collectionFreeForm/CollectionFreeFormView';
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+library.add(faLink);
+library.add(faTag);
+library.add(faTimes);
+library.add(faArrowAltCircleDown);
+library.add(faArrowAltCircleUp);
+library.add(faArrowAltCircleRight);
+library.add(faStopCircle);
+library.add(faCheckCircle);
+library.add(faCloudUploadAlt);
+library.add(faSyncAlt);
+library.add(faShare);
+library.add(faPhotoVideo);
+
+const cloud: IconProp = "cloud-upload-alt";
+const fetch: IconProp = "sync-alt";
+
+enum UtilityButtonState {
+ Default,
+ OpenRight,
+ OpenExternally
+}
+
+@observer
+export class PropertiesButtons extends React.Component<{}, {}> {
+ private _dragRef = React.createRef<HTMLDivElement>();
+ private _pullAnimating = false;
+ private _pushAnimating = false;
+ private _pullColorAnimating = false;
+
+ @observable private pushIcon: IconProp = "arrow-alt-circle-up";
+ @observable private pullIcon: IconProp = "arrow-alt-circle-down";
+ @observable private pullColor: string = "white";
+ @observable public isAnimatingFetch = false;
+ @observable public isAnimatingPulse = false;
+
+ @observable private openHover: UtilityButtonState = UtilityButtonState.Default;
+
+ @observable public static Instance: PropertiesButtons;
+ public static hasPushedHack = false;
+ public static hasPulledHack = false;
+
+
+ @computed get selectedDocumentView() {
+ if (SelectionManager.SelectedDocuments().length) {
+ return SelectionManager.SelectedDocuments()[0];
+ } 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"; }
+
+ public startPullOutcome = action((success: boolean) => {
+ if (!this._pullAnimating) {
+ this._pullAnimating = true;
+ this.pullIcon = success ? "check-circle" : "stop-circle";
+ setTimeout(() => runInAction(() => {
+ this.pullIcon = "arrow-alt-circle-down";
+ this._pullAnimating = false;
+ }), 1000);
+ }
+ });
+
+ public startPushOutcome = action((success: boolean) => {
+ this.isAnimatingPulse = false;
+ if (!this._pushAnimating) {
+ this._pushAnimating = true;
+ this.pushIcon = success ? "check-circle" : "stop-circle";
+ setTimeout(() => runInAction(() => {
+ this.pushIcon = "arrow-alt-circle-up";
+ this._pushAnimating = false;
+ }), 1000);
+ }
+ });
+
+ public setPullState = action((unchanged: boolean) => {
+ this.isAnimatingFetch = false;
+ if (!this._pullColorAnimating) {
+ this._pullColorAnimating = true;
+ this.pullColor = unchanged ? "lawngreen" : "red";
+ setTimeout(this.clearPullColor, 1000);
+ }
+ });
+
+ private clearPullColor = action(() => {
+ this.pullColor = "white";
+ this._pullColorAnimating = false;
+ });
+
+ @computed
+ get considerGoogleDocsPush() {
+ const targetDoc = this.selectedDoc;
+ const published = targetDoc && Doc.GetProto(targetDoc)[GoogleRef] !== undefined;
+ const animation = this.isAnimatingPulse ? "shadow-pulse 1s linear infinite" : "none";
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`${published ? "Push" : "Publish"} to Google Docs`}</div></>}>
+ <div
+ className="propertiesButtons-linker"
+ style={{ animation }}
+ onClick={async () => {
+ await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
+ !published && runInAction(() => this.isAnimatingPulse = true);
+ PropertiesButtons.hasPushedHack = false;
+ targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1;
+ }}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon={published ? (this.pushIcon as any) : cloud} size={published ? "sm" : "xs"} />
+ </div></Tooltip>;
+ }
+
+ @computed
+ get considerGoogleDocsPull() {
+ const targetDoc = this.selectedDoc;
+ const dataDoc = targetDoc && Doc.GetProto(targetDoc);
+ const animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none";
+
+ const title = (() => {
+ switch (this.openHover) {
+ default:
+ case UtilityButtonState.Default: return `${!dataDoc?.unchanged ? "Pull from" : "Fetch"} Google Docs`;
+ case UtilityButtonState.OpenRight: return "Open in Right Split";
+ case UtilityButtonState.OpenExternally: return "Open in new Browser Tab";
+ }
+ })();
+
+ return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? (null) : <Tooltip
+ title={<><div className="dash-tooltip">{title}</div></>}>
+ <div className="propertiesButtons-linker"
+ style={{ backgroundColor: this.pullColor }}
+ onPointerEnter={action(e => {
+ if (e.altKey) {
+ this.openHover = UtilityButtonState.OpenExternally;
+ } else if (e.shiftKey) {
+ this.openHover = UtilityButtonState.OpenRight;
+ }
+ })}
+ onPointerLeave={action(() => this.openHover = UtilityButtonState.Default)}
+ onClick={async e => {
+ const googleDocUrl = `https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`;
+ if (e.shiftKey) {
+ e.preventDefault();
+ let googleDoc = await Cast(dataDoc.googleDoc, Doc);
+ if (!googleDoc) {
+ const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, UseCors: false };
+ googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
+ dataDoc.googleDoc = googleDoc;
+ }
+ CollectionDockingView.AddRightSplit(googleDoc);
+ } else if (e.altKey) {
+ e.preventDefault();
+ window.open(googleDocUrl);
+ } else {
+ this.clearPullColor();
+ PropertiesButtons.hasPulledHack = false;
+ targetDoc[Pulls] = NumCast(targetDoc[Pulls]) + 1;
+ dataDoc.unchanged && runInAction(() => this.isAnimatingFetch = true);
+ }
+ }}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="sm"
+ style={{ WebkitAnimation: animation, MozAnimation: animation }}
+ icon={(() => {
+ switch (this.openHover) {
+ default:
+ case UtilityButtonState.Default: return dataDoc.unchanged === false ? (this.pullIcon as any) : fetch;
+ case UtilityButtonState.OpenRight: return "arrow-alt-circle-right";
+ case UtilityButtonState.OpenExternally: return "share";
+ }
+ })()}
+ />
+ </div></Tooltip>;
+ }
+ @computed
+ get pinButton() {
+ const targetDoc = this.selectedDoc;
+ const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div></>}>
+ <div className="propertiesButtons-linker"
+ style={{ backgroundColor: isPinned ? "white" : "rgb(37, 43, 51)", color: isPinned ? "black" : "white" }}
+ onClick={e => DockedFrameRenderer.PinDoc(targetDoc, isPinned)}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin"
+ />
+ </div></Tooltip>;
+ }
+
+ @computed
+ get metadataButton() {
+ //const view0 = this.view0;
+ if (this.selectedDoc) {
+ return <Tooltip title={<><div className="dash-tooltip">Show metadata panel</div></>}>
+ <div className="propertiesButtons-linkFlyout">
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP}
+ content={<MetadataEntryMenu docs={[this.selectedDoc]} suggestWithFunction /> /* tfs: @bcz This might need to be the data document? */}>
+ <div className={"propertiesButtons-linkButton-" + "empty"} onPointerDown={e => e.stopPropagation()} >
+ {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" size="sm" />}
+ </div>
+ </Flyout>
+ </div></Tooltip>;
+ } else {
+ return null;
+ }
+
+ }
+
+ @observable _aliasDown = false;
+ onAliasButtonDown = (e: React.PointerEvent): void => {
+ setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
+ }
+ 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);
+ dragData.dropAction = "alias";
+ DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, {
+ offsetX: dragData.offset[0],
+ offsetY: dragData.offset[1],
+ hideSource: false
+ });
+ return true;
+ }
+ return false;
+ }
+
+ @computed
+ get templateButton() {
+ const docView = this.selectedDocumentView;
+ const templates: Map<Template, boolean> = new Map();
+ const views = [this.selectedDocumentView];
+ Array.from(Object.values(Templates.TemplateList)).map(template =>
+ templates.set(template, views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean)));
+ return !docView ? (null) :
+ <Tooltip title={<><div className="dash-tooltip">Customize layout</div></>}>
+ <div className="propertiesButtons-linkFlyout">
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP} //onOpen={action(() => this._aliasDown = true)} onClose={action(() => this._aliasDown = false)}
+ content={<TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}>
+ <div className={"propertiesButtons-linkButton-empty"} >
+ {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />}
+ </div>
+ </Flyout>
+ </div></Tooltip>;
+ }
+
+ onCopy = () => {
+ if (this.selectedDoc && this.selectedDocumentView) {
+ const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true);
+ copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
+ copy.y = NumCast(this.selectedDoc.y) + 30;
+ this.selectedDocumentView.props.addDocument?.(copy);
+ }
+ }
+
+ @computed
+ get copyButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Tap or Drag to create an alias"}</div></>}>
+ <div className={"propertiesButtons-linkButton-empty"}
+ ref={this._dragRef}
+ onPointerDown={this.onAliasButtonDown}
+ onClick={this.onCopy}>
+ {<FontAwesomeIcon className="documentdecorations-icon" icon="copy" size="sm" />}
+ </div>
+ </Tooltip>;
+ }
+
+ @action
+ onLock = () => {
+ this.selectedDocumentView?.toggleLockPosition();
+ }
+
+ @computed
+ get lockButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip
+ title={<><div className="dash-tooltip">{this.selectedDoc?.lockedPosition ?
+ "Unlock Position" : "Lock Position"}</div></>}>
+ <div className={"propertiesButtons-linkButton-empty"}
+ onPointerDown={this.onLock} >
+ {<FontAwesomeIcon className="documentdecorations-icon"
+ icon={BoolCast(this.selectedDoc?.lockedPosition) ? "unlock" : "lock"} size="sm" />}
+ </div>
+ </Tooltip>;
+ }
+
+ @computed
+ get downloadButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip
+ title={<><div className="dash-tooltip">{"Download Document"}</div></>}>
+ <div className={"propertiesButtons-linkButton-empty"}
+ onPointerDown={async () => {
+ if (this.selectedDocumentView?.props.Document) {
+ Doc.Zip(this.selectedDocumentView?.props.Document);
+ }
+ }}>
+ {<FontAwesomeIcon className="propertiesButtons-icon"
+ icon="download" size="sm" />}
+ </div>
+ </Tooltip>;
+ }
+
+ @computed
+ get deleteButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip
+ title={<><div className="dash-tooltip">{"Delete Document"}</div></>}>
+ <div className={"propertiesButtons-linkButton-empty"}
+ onPointerDown={() => {
+ this.selectedDocumentView?.props.ContainingCollectionView?.removeDocument(this.selectedDocumentView?.props.Document);
+ }}>
+ {<FontAwesomeIcon className="propertiesButtons-icon"
+ icon="trash-alt" size="sm" />}
+ </div>
+ </Tooltip>;
+ }
+
+ @computed
+ get sharingButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip
+ title={<><div className="dash-tooltip">{"Share Document"}</div></>}>
+ <div className={"propertiesButtons-linkButton-empty"}
+ onPointerDown={() => {
+ if (this.selectedDocumentView) {
+ SharingManager.Instance.open(this.selectedDocumentView);
+ }
+ }}>
+ {<FontAwesomeIcon className="propertiesButtons-icon"
+ icon="users" size="sm" />}
+ </div>
+ </Tooltip>;
+ }
+
+ @computed
+ get onClickButton() {
+ if (this.selectedDoc) {
+ return <Tooltip title={<><div className="dash-tooltip">Choose onClick behavior</div></>}>
+ <div className="propertiesButtons-linkFlyout">
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP}
+ content={this.onClickFlyout}>
+ <div className={"propertiesButtons-linkButton-" + "empty"} onPointerDown={e => e.stopPropagation()} >
+ {<FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="sm" />}
+ </div>
+ </Flyout>
+ </div></Tooltip>;
+ } else {
+ return null;
+ }
+ }
+
+ @action
+ handleOptionChange = (e: any) => {
+ const value = e.target.value;
+ this.selectedDoc && (this.selectedDoc.onClickBehavior = e.target.value);
+ if (value === "nothing") {
+ this.selectedDocumentView?.noOnClick();
+ } else if (value === "enterPortal") {
+ this.selectedDocumentView?.noOnClick();
+ this.selectedDocumentView?.makeIntoPortal();
+ } else if (value === "toggleDetail") {
+ this.selectedDocumentView?.noOnClick();
+ this.selectedDocumentView?.toggleDetail();
+ } else if (value === "linkInPlace") {
+ this.selectedDocumentView?.noOnClick();
+ this.selectedDocumentView?.toggleFollowLink("inPlace", true, false);
+ } else if (value === "linkOnRight") {
+ this.selectedDocumentView?.noOnClick();
+ this.selectedDocumentView?.toggleFollowLink("onRight", false, false);
+ }
+ }
+
+ @undoBatch @action
+ editOnClickScript = () => {
+ if (this.selectedDoc) {
+ DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, "onClick");
+ }
+ }
+
+ @computed
+ get onClickFlyout() {
+ return <div><form>
+ <div className="radio">
+ <label>
+ <input type="radio" value="nothing"
+ checked={this.onClick === 'nothing'}
+ onChange={this.handleOptionChange} />
+ Select Document
+ </label>
+ </div>
+ <div className="radio">
+ <label>
+ <input type="radio" value="enterPortal"
+ checked={this.onClick === 'enterPortal'}
+ onChange={this.handleOptionChange} />
+ Enter Portal
+ </label>
+ </div>
+ <div className="radio">
+ <label>
+ <input type="radio" value="toggleDetail"
+ checked={this.onClick === 'toggleDetail'}
+ onChange={this.handleOptionChange} />
+ Toggle Detail
+ </label>
+ </div>
+ <div className="radio">
+ <label>
+ <input type="radio" value="linkInPlace"
+ checked={this.onClick === 'linkInPlace'}
+ onChange={this.handleOptionChange} />
+ Follow Link
+ </label>
+ </div>
+ <div className="radio">
+ <label>
+ <input type="radio" value="linkOnRight"
+ checked={this.onClick === 'linkOnRight'}
+ onChange={this.handleOptionChange} />
+ Open Link on Right
+ </label>
+ </div>
+ </form>
+ <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>
+ </div>;
+ }
+
+ @computed
+ get googlePhotosButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip
+ title={<><div className="dash-tooltip">{"Export to Google Photos"}</div></>}>
+ <div className={"propertiesButtons-linkButton-empty"}
+ onPointerDown={() => {
+ if (this.selectedDocumentView) {
+ GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDocumentView.Document }).then(console.log);
+ }
+ }}>
+ {<FontAwesomeIcon className="documentdecorations-icon"
+ icon="cloud-upload-alt" size="sm" />}
+ </div>
+ </Tooltip>;
+ }
+
+ @computed
+ get clustersButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip
+ title={<><div className="dash-tooltip">{this.selectedDoc?.useClusters ? "Stop Showing Clusters" : "Show Clusters"}</div></>}>
+ <div className={"propertiesButtons-linkButton-empty"}
+ style={{ backgroundColor: this.selectedDoc?.useClusters ? "#a0a0a0" : "" }}
+ onPointerDown={this.changeClusters}>
+ {<FontAwesomeIcon className="documentdecorations-icon"
+ color={this.selectedDoc?.useClusters ? "black" : "white"}
+ icon="braille" size="sm" />}
+ </div>
+ </Tooltip>;
+ }
+
+ @action @undoBatch
+ changeFitToBox = () => {
+ this.selectedDoc && (this.selectedDoc._fitToBox = !this.selectedDoc._fitToBox);
+ }
+
+ @action @undoBatch
+ changeClusters = () => {
+ this.selectedDoc && (this.selectedDoc.useClusters = !this.selectedDoc.useClusters);
+ }
+
+ @computed
+ get fitContentButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip
+ title={<><div className="dash-tooltip">{this.selectedDoc?._fitToBox ? "Stop Fitting Content" : "Fit Content"}</div></>}>
+ <div className={"propertiesButtons-linkButton-empty"}
+ style={{ backgroundColor: this.selectedDoc?._fitToBox ? "#a0a0a0" : "" }}
+ onPointerDown={this.changeFitToBox}>
+ {<FontAwesomeIcon className="documentdecorations-icon"
+ color={this.selectedDoc?._fitToBox ? "black" : "white"}
+ icon="expand" size="sm" />}
+ </div>
+ </Tooltip>;
+ }
+
+ // @computed
+ // get importButton() {
+ // const targetDoc = this.selectedDoc;
+ // return !targetDoc ? (null) : <Tooltip
+ // title={<><div className="dash-tooltip">{"Import a Document"}</div></>}>
+ // <div className={"propertiesButtons-linkButton-empty"}
+ // onPointerDown={() => {
+ // if (this.selectedDocumentView) {
+ // CollectionFreeFormView.importDocument(100, 100);
+ // }
+ // }}>
+ // {<FontAwesomeIcon className="documentdecorations-icon"
+ // icon="upload" size="sm" />}
+ // </div>
+ // </Tooltip>;
+ // }
+
+
+ render() {
+ if (!this.selectedDoc) return (null);
+
+ const isText = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof RichTextField;
+ const considerPull = isText && this.considerGoogleDocsPull;
+ const considerPush = isText && this.considerGoogleDocsPush;
+ const isImage = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof ImageField;
+ const isCollection = this.selectedDoc.type === DocumentType.COL ? true : false;
+ const isFreeForm = this.selectedDoc._viewType === "freeform" ? true : false;
+
+ return <div><div className="propertiesButtons" style={{ paddingBottom: "5.5px" }}>
+ <div className="propertiesButtons-button">
+ {this.templateButton}
+ </div>
+ {/* <div className="propertiesButtons-button">
+ {this.metadataButton}
+ </div> */}
+ <div className="propertiesButtons-button">
+ {this.pinButton}
+ </div>
+ <div className="propertiesButtons-button">
+ {this.copyButton}
+ </div>
+ <div className="propertiesButtons-button">
+ {this.lockButton}
+ </div>
+ <div className="propertiesButtons-button">
+ {this.downloadButton}
+ </div>
+ </div>
+ <div className="propertiesButtons">
+ <div className="propertiesButtons-button">
+ {this.deleteButton}
+ </div>
+ <div className="propertiesButtons-button">
+ {this.onClickButton}
+ </div>
+ <div className="propertiesButtons-button">
+ {this.sharingButton}
+ </div>
+ <div className="propertiesButtons-button" style={{ display: !considerPush ? "none" : "" }}>
+ {this.considerGoogleDocsPush}
+ </div>
+ <div className="propertiesButtons-button" style={{ display: !considerPull ? "none" : "" }}>
+ {this.considerGoogleDocsPull}
+ </div>
+ <div className="propertiesButtons-button" style={{ display: !isImage ? "none" : "" }}>
+ {this.googlePhotosButton}
+ </div>
+ {/* <div className="propertiesButtons-button" style={{ display: !isCollection ? "none" : "" }}>
+ {this.importButton}
+ </div> */}
+
+ <div className="propertiesButtons-button" style={{ display: !isFreeForm ? "none" : "" }}>
+ {this.clustersButton}
+ </div>
+
+ <div className="propertiesButtons-button" style={{ display: !isFreeForm ? "none" : "" }}>
+ {this.fitContentButton}
+ </div>
+
+ </div>
+ </div>;
+ }
+}
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 1895c06a1..27d66f796 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -12,12 +12,14 @@
width: 100%;
height: 100%;
position: absolute;
+
.miniThumb {
background: #25252525;
position: absolute;
}
}
}
+
.lm_title {
margin-top: 3px;
border-radius: 5px;
@@ -27,6 +29,7 @@
transform: translate(0px, -3px);
cursor: grab;
}
+
.lm_title.focus-visible {
cursor: text;
}
@@ -34,23 +37,25 @@
.lm_title_wrap {
overflow: hidden;
height: 19px;
- margin-top: -3px;
- display:inline-block;
+ margin-top: -2px;
+ display: inline-block;
}
+
.lm_active .lm_title {
border: solid 1px lightgray;
}
+
.lm_header .lm_tab .lm_close_tab {
position: absolute;
text-align: center;
}
.lm_header .lm_tab {
- padding-right : 20px;
+ padding-right: 20px;
}
.lm_popout {
- display:none;
+ display: none;
}
.messageCounter {
@@ -73,6 +78,7 @@
position: absolute;
top: 0;
left: 0;
+
// overflow: hidden; // bcz: menus don't show up when this is on (e.g., the parentSelectorMenu)
.collectionDockingView-gear {
padding-left: 5px;
@@ -80,7 +86,10 @@
width: 18px;
display: inline-block;
margin: auto;
+
+ display: none;
}
+
.collectionDockingView-dragAsDocument {
touch-action: none;
position: absolute;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 4952dedc9..7a0a06069 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,9 +1,8 @@
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, computed, Lambda, observable, reaction, runInAction, trace } from "mobx";
+import { action, computed, Lambda, observable, reaction, runInAction, trace, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
-import Measure from "react-measure";
import * as GoldenLayout from "../../../client/goldenLayout";
import { DateField } from '../../../fields/DateField';
import { Doc, DocListCast, Field, Opt, DataSym } from "../../../fields/Doc";
@@ -505,7 +504,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
if (doc instanceof Doc) {
- //tab.titleElement[0].outerHTML = `<input class='lm_title' style="background:black" value='${doc.title}' />`;
tab.titleElement[0].onclick = (e: any) => tab.titleElement[0].focus();
tab.titleElement[0].onchange = (e: any) => {
tab.titleElement[0].size = e.currentTarget.value.length + 1;
@@ -520,6 +518,10 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
gearSpan.style.paddingLeft = "0px";
gearSpan.style.paddingRight = "12px";
const stack = tab.contentItem.parent;
+ tab.element[0].onpointerdown = (e: any) => {
+ const view = DocumentManager.Instance.getDocumentView(doc);
+ view && SelectionManager.SelectDoc(view, false);
+ };
// shifts the focus to this tab when another tab is dragged over it
tab.element[0].onmouseenter = (e: any) => {
if (!this._isPointerDown || !SnappingManager.GetIsDragging()) return;
@@ -674,10 +676,15 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
@observable private _panelHeight = 0;
@observable private _document: Opt<Doc>;
@observable private _isActive: boolean = false;
+ _tabReaction: IReactionDisposer | undefined;
get _stack(): any {
return (this.props as any).glContainer.parent.parent;
}
+ get _tab(): any {
+ const tab = (this.props as any).glContainer.tab.element[0] as HTMLElement;
+ return tab.getElementsByClassName("lm_title")?.[0];
+ }
constructor(props: any) {
super(props);
DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => this._document = f as Doc));
@@ -738,9 +745,16 @@ 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") }),
+ (data) => {
+ const selected = data.views.some(v => Doc.AreProtosEqual(v.props.Document, this._document));
+ this._tab.style.backgroundColor = selected ? data.color : "";
+ }
+ );
}
componentWillUnmount() {
+ this._tabReaction?.();
this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
this.props.glContainer.off("tab", this.onActiveContentItemChanged);
}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 0ca86172f..fdd1b4e81 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -25,6 +25,8 @@ import { SelectionManager } from "../../util/SelectionManager";
import { DocumentView } from "../nodes/DocumentView";
import { ColorState } from "react-color";
import { ObjectField } from "../../../fields/ObjectField";
+import RichTextMenu from "../nodes/formattedText/RichTextMenu";
+import { RichTextField } from "../../../fields/RichTextField";
import { ScriptField } from "../../../fields/ScriptField";
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { DocUtils } from "../../documents/Documents";
@@ -47,7 +49,7 @@ export default class CollectionMenu extends AntimodeMenu {
componentDidMount() {
reaction(() => SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0],
- (doc) => doc && this.SetSelection(doc))
+ (doc) => doc && this.SetSelection(doc));
}
@action
@@ -160,7 +162,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
initialize: (button: Doc) => { button['target-docFilters'] = this.target._docFilters instanceof ObjectField ? ObjectField.MakeCopy(this.target._docFilters as any as ObjectField) : ""; },
};
- _freeform_commands = [this._viewCommand, this._saveFilterCommand, this._fitContentCommand, this._clusterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand];
+ @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
+
_stacking_commands = [this._contentCommand, this._templateCommand];
_masonry_commands = [this._contentCommand, this._templateCommand];
_schema_commands = [this._templateCommand, this._narrativeCommand];
@@ -308,18 +311,32 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
</div>;
}
+ @computed get selectedDocumentView() {
+ if (SelectionManager.SelectedDocuments().length) {
+ return SelectionManager.SelectedDocuments()[0];
+ } else { return undefined; }
+ }
+ @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
+ @computed get isText() {
+ if (this.selectedDoc) {
+ return this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof RichTextField;
+ }
+ else return false;
+ }
+
render() {
return (
<div className="collectionMenu-cont" >
<div className="collectionMenu">
<div className="collectionViewBaseChrome">
- {this.props.type === CollectionViewType.Invalid || this.props.type === CollectionViewType.Docking ? (null) : this.viewModes}
- {this.props.type === CollectionViewType.Docking ? (null) : this.templateChrome}
- <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: "grid" }}>
+ {this.props.type === CollectionViewType.Invalid ||
+ this.props.type === CollectionViewType.Docking || this.isText ? (null) : this.viewModes}
+ {this.props.type === CollectionViewType.Docking || this.isText ? (null) : this.templateChrome}
+ {/* <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: "grid" }}>
<button className={"antimodeMenu-button"} onClick={this.toggleViewSpecs} >
<FontAwesomeIcon icon="filter" size="lg" />
</button>
- </div>
+ </div> */}
{this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? (null) : <button className={"antimodeMenu-button"} key="float"
style={{ backgroundColor: !this.props.docView.layoutDoc.isAnnotating ? "121212" : undefined, borderRight: "1px solid gray" }}
@@ -356,6 +373,20 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
@computed get childDocs() {
return DocListCast(this.dataField);
}
+
+ @computed get selectedDocumentView() {
+ if (SelectionManager.SelectedDocuments().length) {
+ return SelectionManager.SelectedDocuments()[0];
+ } else { return undefined; }
+ }
+ @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
+ @computed get isText() {
+ if (this.selectedDoc) {
+ return this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof RichTextField;
+ }
+ else return false;
+ }
+
@undoBatch
@action
nextKeyframe = (): void => {
@@ -384,6 +415,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
miniMap = (): void => {
this.document.hideMinimap = !this.document.hideMinimap;
}
+
private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", ""];
private _width = ["1", "5", "10", "100"];
// private _draw = ["⎯", "→", "↔︎", "∿", "↝", "↭", "ロ", "O", "∆"];
@@ -523,40 +555,44 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
}
@computed get formatPane() {
- return <button className="antimodeMenu-button" key="format" title="toggle foramatting pane"
- onPointerDown={action(e => FormatShapePane.Instance.Pinned = !FormatShapePane.Instance.Pinned)}
- style={{ backgroundColor: this._fillBtn ? "121212" : "" }}>
- <FontAwesomeIcon icon="angle-double-right" size="lg" />
- </button>;
+ // return <button className="antimodeMenu-button" key="format" title="toggle foramatting pane"
+ // onPointerDown={action(e => FormatShapePane.Instance.Pinned = !FormatShapePane.Instance.Pinned)}
+ // style={{ backgroundColor: this._fillBtn ? "121212" : "" }}>
+ // <FontAwesomeIcon icon="angle-double-right" size="lg" />
+ // </button>;
+ return null;
}
render() {
return !this.props.docView.layoutDoc ? (null) : <div className="collectionFreeFormMenu-cont">
- {this.props.docView.props.renderDepth !== 0 ? (null) :
+ {this.props.docView.props.renderDepth !== 0 || this.isText ? (null) :
<div key="map" title="mini map" className="backKeyframe" onClick={this.miniMap}>
<FontAwesomeIcon icon={"map"} size={"lg"} />
</div>
}
- <div key="back" title="back frame" className="backKeyframe" onClick={this.prevKeyframe}>
+ {!!!this.isText ? <div key="back" title="back frame" className="backKeyframe" onClick={this.prevKeyframe}>
<FontAwesomeIcon icon={"caret-left"} size={"lg"} />
- </div>
- <div key="num" title="toggle view all" className="numKeyframe" style={{ backgroundColor: this.document.editing ? "#759c75" : "#c56565" }}
+ </div> : null}
+ {!!!this.isText ? <div key="num" title="toggle view all" className="numKeyframe" style={{ backgroundColor: this.document.editing ? "#759c75" : "#c56565" }}
onClick={action(() => this.document.editing = !this.document.editing)} >
{NumCast(this.document.currentFrame)}
- </div>
- <div key="fwd" title="forward frame" className="fwdKeyframe" onClick={this.nextKeyframe}>
+ </div> : null}
+ {!!!this.isText ? <div key="fwd" title="forward frame" className="fwdKeyframe" onClick={this.nextKeyframe}>
<FontAwesomeIcon icon={"caret-right"} size={"lg"} />
- </div>
+ </div> : null}
- {!this.props.isOverlay || this.document.type !== DocumentType.WEB ? (null) :
+ {!this.props.isOverlay || this.document.type !== DocumentType.WEB || this.isText ? (null) :
<button className={"antimodeMenu-button"} key="hypothesis"
- style={{ backgroundColor: !this.props.docView.layoutDoc.isAnnotating ? "121212" : undefined, borderRight: "1px solid gray" }}
+ style={{
+ backgroundColor: !this.props.docView.layoutDoc.isAnnotating ? "121212" : undefined,
+ borderRight: "1px solid gray"
+ }}
title="Use Hypothesis"
onClick={() => this.props.docView.layoutDoc.isAnnotating = !this.props.docView.layoutDoc.isAnnotating}>
<FontAwesomeIcon icon={["fab", "hire-a-helper"]} size={"lg"} />
</button>
}
- {!this.props.isOverlay || this.props.docView.layoutDoc.isAnnotating ?
+ {(!this.props.isOverlay || this.props.docView.layoutDoc.isAnnotating) && !this.isText ?
<>
{this.drawButtons}
{this.widthPicker}
@@ -566,6 +602,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</> :
(null)
}
+ {this.isText ? <RichTextMenu key="rich" /> : null}
</div>;
}
}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 5553bbbb7..f67e049fd 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -253,7 +253,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
<div className="collectionSchema-headerMenu-group">
<div onClick={() => this.typesDropdownChange(!this._openTypes)}>
<label>Column type:</label>
- <FontAwesomeIcon icon={"caret-down"} size="sm" style={{ float: "right" }} />
+ <FontAwesomeIcon icon={"caret-down"} size="lg" style={{ float: "right" }} />
</div>
{this._openTypes ? allColumnTypes : justColType}
</div >
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 312dd92f0..54ce39cea 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -227,6 +227,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
addDocTab={this.addDocTab}
bringToFront={returnFalse}
ContentScaling={returnOne}
+ scriptContext={this.props.scriptContext}
pinToPres={this.props.pinToPres}
/>;
}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index a6983de05..4042a070d 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -351,7 +351,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
{this.props.parent.Document._columnsHideIfEmpty ? (null) : headingView}
{
this.collapsed ? (null) :
- <div style={{ marginTop: 5 }}>
+ <div>
<div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`}
style={{
padding: singleColumn ? `${columnYMargin}px ${0}px ${style.yMargin}px ${0}px` : `${columnYMargin}px ${0}px`,
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 9f78c15eb..c90e85271 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -296,7 +296,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const reg = new RegExp(Utils.prepend(""), "g");
const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: "-web page-", _width: 300, _height: 300 });
- Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc)["text"] = text;
+ 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);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 21292b900..7787a8b38 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -90,7 +90,10 @@ class TreeView extends React.Component<TreeViewProps> {
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"); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
- set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.doc.treeViewOpen = this._overrideTreeViewOpen = c; }
+ set treeViewOpen(c: boolean) {
+ if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
+ else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
+ }
@computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
@computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); }
@computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
@@ -101,7 +104,7 @@ class TreeView extends React.Component<TreeViewProps> {
const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
return ((this.props.dataDoc ? DocListCast(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
(layout ? DocListCast(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
- DocListCast(this.doc[field])) as Doc[]; // otherwise use the document's data field
+ DocListCast(this.doc[field])); // otherwise use the document's data field
}
@computed get childDocs() { return this.childDocList(this.fieldKey); }
@computed get childLinks() { return this.childDocList("links"); }
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index b630f9cf8..a5aef86de 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -24,6 +24,7 @@
border-right: unset;
z-index: 2;
}
+
.collectionTimeView-treeView {
display: flex;
flex-direction: column;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7e7ea6786..62e8dc26a 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -78,6 +78,7 @@ export interface CollectionViewCustomProps {
childLayoutTemplate?: () => Opt<Doc>; // specify a layout Doc template to use for children of the collection
childLayoutString?: string; // specify a layout string to use for children of the collection
childOpacity?: () => number;
+ hideFilter?: true;
}
export interface CollectionRenderProps {
@@ -309,7 +310,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
const options = cm.findByDescription("Options...");
const optionItems = options && "subitems" in options ? options.subitems : [];
- optionItems.splice(0, 0, { description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
+ !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }) : null;
if (this.props.Document.childLayout instanceof Doc) {
optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" });
}
@@ -368,7 +369,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
get _facetWidth() { return NumCast(this.props.Document._facetWidth); }
set _facetWidth(value) { this.props.Document._facetWidth = value; }
- bodyPanelWidth = () => this.props.PanelWidth() - this.facetWidth();
+ bodyPanelWidth = () => this.props.PanelWidth();
facetWidth = () => Math.max(0, Math.min(this.props.PanelWidth() - 25, this._facetWidth));
@computed get dataDoc() {
@@ -490,6 +491,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
return false;
}), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0), false);
}
+
filterBackground = () => "rgba(105, 105, 105, 0.432)";
get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves)
@computed get scriptField() {
@@ -559,6 +561,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
</div>
</div>;
}
+
childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
childLayoutString = this.props.childLayoutString || StrCast(this.props.Document.childLayoutString);
@@ -588,11 +591,11 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
Utils.CorsProxy(Cast(d.data, ImageField)!.url.href) : Cast(d.data, ImageField)!.url.href
:
""))}
- {(!this.props.isSelected() && !this.props.Document.forceActive) || this.props.Document.hideFilterView ? (null) :
+ {/* {(!this.props.isSelected() && !this.props.Document.forceActive) || this.props.Document.hideFilterView ? (null) :
<div className="collectionView-filterDragger" title="library View Dragger" onPointerDown={this.onPointerDown}
style={{ right: this.facetWidth() - 1, top: this.props.Document._viewType === CollectionViewType.Docking ? "25%" : "55%" }} />
}
- {this.facetWidth() < 10 ? (null) : this.filterView}
+ {this.facetWidth() < 10 ? (null) : this.filterView} */}
</div>);
}
}
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 8c0b8de9d..532dd6abc 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -42,14 +42,14 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
async fetchDocuments() {
const aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document));
const containerProtoSets = await Promise.all(aliases.map(async alias =>
- await Promise.all((await SearchUtil.Search("", true, { fq: `data_l:"${alias[Id]}"` })).docs)));
+ ((await SearchUtil.Search("", true, { fq: `data_l:"${alias[Id]}"` })).docs)));
const containerProtos = containerProtoSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>());
const containerSets = await Promise.all(Array.from(containerProtos.keys()).map(async container => {
- return (await SearchUtil.GetAliasesOfDocument(container));
+ return (SearchUtil.GetAliasesOfDocument(container));
}));
const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>());
const doclayoutSets = await Promise.all(Array.from(containers.keys()).map(async (dp) => {
- return (await SearchUtil.GetAliasesOfDocument(dp));
+ return (SearchUtil.GetAliasesOfDocument(dp));
}));
const doclayouts = Array.from(doclayoutSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>()).keys());
runInAction(() => {
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index cde795098..7e2840c2c 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -148,7 +148,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
@action
- changeTitleMode = () => this._showTitleDropdown = !this._showTitleDropdown;
+ changeTitleMode = () => this._showTitleDropdown = !this._showTitleDropdown
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
@computed get tableColumns(): Column<Doc>[] {
@@ -208,7 +208,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}}>
{col.heading}</div>;
- const sortIcon = col.desc === undefined ? "circle" : col.desc === true ? "caret-down" : "caret-up";
+ const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up";
const header =
<div //className="collectionSchemaView-header"
@@ -224,12 +224,12 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
{keysDropdown}
</div>
<div onClick={e => this.changeSorting(col)}
- style={{ paddingRight: "6px", display: "inline" }}>
+ style={{ paddingRight: "6px", marginLeft: "4px", display: "inline" }}>
<FontAwesomeIcon icon={sortIcon} size="sm" />
</div>
<div onClick={e => this.props.openHeader(col, e.clientX, e.clientY)}
style={{ float: "right", paddingRight: "6px" }}>
- <FontAwesomeIcon icon={"compass"} size="sm" />
+ <FontAwesomeIcon icon={"cog"} size="sm" />
</div>
</div>;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 57336131a..badbc48a1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1144,7 +1144,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
componentDidMount() {
super.componentDidMount?.();
- this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation },
+ this._layoutComputeReaction = reaction(() => this.doLayoutComputation,
(elements) => this._layoutElements = elements || [],
{ fireImmediately: true, name: "doLayout" });
@@ -1239,13 +1239,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
- appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
+ !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
!appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
- viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" });
- viewCtrlItems.push({ description: (this.Document.useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
+
+
+ !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
+ !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document.useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }) : null;
!viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
const options = ContextMenu.Instance.findByDescription("Options...");
@@ -1290,7 +1292,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
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.
}
}
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index ddc282e57..6263be261 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -13,7 +13,6 @@ import AntimodeMenu from "../../AntimodeMenu";
import "./FormatShapePane.scss";
import { undoBatch } from "../../../util/UndoManager";
import { ColorState, SketchPicker } from 'react-color';
-import { DocumentView } from "../../../views/nodes/DocumentView"
@observer
export default class FormatShapePane extends AntimodeMenu {
@@ -124,12 +123,12 @@ export default class FormatShapePane extends AntimodeMenu {
console.log(ink);
if (ink) {
const newPoints: { X: number, Y: number }[] = [];
- for (var j = 0; j < ink.length; j++) {
+ ink.forEach(i => {
// (new x — oldx) + (oldxpoint * newWidt)/oldWidth
- const newX = (doc.x - oldX) + (ink[j].X * doc._width) / oldWidth;
- const newY = (doc.y - oldY) + (ink[j].Y * doc._height) / oldHeight;
+ const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth;
+ const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight;
newPoints.push({ X: newX, Y: newY });
- }
+ });
doc.data = new InkField(newPoints);
}
}
@@ -148,12 +147,12 @@ export default class FormatShapePane extends AntimodeMenu {
console.log(ink);
if (ink) {
const newPoints: { X: number, Y: number }[] = [];
- for (var j = 0; j < ink.length; j++) {
+ ink.forEach(i => {
// (new x — oldx) + (oldxpoint * newWidt)/oldWidth
- const newX = (doc.x - oldX) + (ink[j].X * doc._width) / oldWidth;
- const newY = (doc.y - oldY) + (ink[j].Y * doc._height) / oldHeight;
+ const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth;
+ const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight;
newPoints.push({ X: newX, Y: newY });
- }
+ });
doc.data = new InkField(newPoints);
}
}
@@ -191,11 +190,11 @@ export default class FormatShapePane extends AntimodeMenu {
if (ink) {
const newPoints: { X: number, Y: number }[] = [];
- for (var i = 0; i < ink.length; i++) {
- const newX = Math.cos(angle) * (ink[i].X - _centerPoints[index].X) - Math.sin(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].X;
- const newY = Math.sin(angle) * (ink[i].X - _centerPoints[index].X) + Math.cos(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].Y;
+ ink.forEach(i => {
+ const newX = Math.cos(angle) * (i.X - _centerPoints[index].X) - Math.sin(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].X;
+ const newY = Math.sin(angle) * (i.X - _centerPoints[index].X) + Math.cos(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].Y;
newPoints.push({ X: newX, Y: newY });
- }
+ });
doc.data = new InkField(newPoints);
const xs = newPoints.map(p => p.X);
const ys = newPoints.map(p => p.Y);
@@ -395,12 +394,12 @@ export default class FormatShapePane extends AntimodeMenu {
@computed get widInput() { return this.inputBox("wid", this.shapeWid, (val: string) => this.shapeWid = val); }
@computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); }
- @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); }
@computed get YpsInput() { return this.inputBox("Yps", this.shapeYps, (val: string) => this.shapeYps = val); }
@computed get controlPoints() { return this.controlPointsButton(); }
@computed get lockRatio() { return this.lockRatioButton(); }
@computed get rotate90() { return this.rotate90Button(); }
+ @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); }
@computed get propertyGroupItems() {
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
new file mode 100644
index 000000000..74f32275a
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
@@ -0,0 +1,620 @@
+.propertiesView {
+
+ background-color: rgb(205, 205, 205);
+ height: 100%;
+ font-family: "Noto Sans";
+ cursor: auto;
+
+ overflow-x: visible;
+ overflow-y: visible;
+
+ .propertiesView-title {
+ background-color: rgb(159, 159, 159);
+ text-align: center;
+ padding-top: 12px;
+ padding-bottom: 12px;
+ display: flex;
+ font-size: 18px;
+ font-weight: bold;
+ justify-content: center;
+
+ .propertiesView-title-icon {
+ width: 20px;
+ height: 20px;
+ padding-left: 38px;
+ margin-top: -5px;
+ right: 19;
+ position: absolute;
+
+ &:hover {
+ color: grey;
+ cursor: pointer;
+ }
+
+ }
+
+ }
+
+ .propertiesView-name {
+ border-bottom: 1px solid black;
+ padding: 8.5px;
+ font-size: 12.5px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+
+ .propertiesView-settings {
+ border-bottom: 1px solid black;
+ //padding: 8.5px;
+ font-size: 12.5px;
+ font-weight: bold;
+
+ .propertiesView-settings-title {
+ font-weight: bold;
+ font-size: 12.5px;
+ padding: 4px;
+ display: flex;
+ color: white;
+ padding-left: 8px;
+ background-color: rgb(51, 51, 51);
+
+ .propertiesView-settings-title-icon {
+ float: right;
+ right: 0;
+ position: absolute;
+ margin-left: 2px;
+ margin-right: 9px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .propertiesView-settings-content {
+ margin-left: 12px;
+ padding-bottom: 10px;
+ padding-top: 8px;
+ }
+
+ }
+
+ .propertiesView-sharing {
+ border-bottom: 1px solid black;
+ //padding: 8.5px;
+
+ .propertiesView-sharing-title {
+ font-weight: bold;
+ font-size: 12.5px;
+ padding: 4px;
+ display: flex;
+ color: white;
+ padding-left: 8px;
+ background-color: rgb(51, 51, 51);
+
+ .propertiesView-sharing-title-icon {
+ float: right;
+ right: 0;
+ position: absolute;
+ margin-left: 2px;
+ margin-right: 9px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .propertiesView-sharing-content {
+ font-size: 10px;
+ padding: 10px;
+ margin-left: 5px;
+ }
+ }
+
+ .propertiesView-appearance {
+ border-bottom: 1px solid black;
+ //padding: 8.5px;
+
+ .propertiesView-appearance-title {
+ font-weight: bold;
+ font-size: 12.5px;
+ padding: 4px;
+ display: flex;
+ color: white;
+ padding-left: 8px;
+ background-color: rgb(51, 51, 51);
+
+ .propertiesView-appearance-title-icon {
+ float: right;
+ right: 0;
+ position: absolute;
+ margin-left: 2px;
+ margin-right: 9px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .propertiesView-appearance-content {
+ font-size: 10px;
+ padding: 10px;
+ margin-left: 5px;
+ }
+ }
+
+ .propertiesView-transform {
+ border-bottom: 1px solid black;
+ //padding: 8.5px;
+
+ .propertiesView-transform-title {
+ font-weight: bold;
+ font-size: 12.5px;
+ padding: 4px;
+ display: flex;
+ color: white;
+ padding-left: 8px;
+ background-color: rgb(51, 51, 51);
+
+ .propertiesView-transform-title-icon {
+ float: right;
+ right: 0;
+ position: absolute;
+ margin-left: 2px;
+ margin-right: 9px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .propertiesView-transform-content {
+ font-size: 10px;
+ padding: 10px;
+ margin-left: 5px;
+ }
+ }
+
+ .notify-button {
+ padding: 2px;
+ width: 12px;
+ height: 12px;
+ background-color: black;
+ border-radius: 10px;
+ padding-left: 2px;
+ padding-right: 2px;
+ margin-top: 2px;
+ margin-left: 3px;
+
+ .notify-button-icon {
+ width: 6px;
+ height: 6.5px;
+ margin-left: .5px;
+ }
+
+ &:hover {
+ background-color: rgb(158, 158, 158);
+ cursor: pointer;
+ }
+ }
+
+ .expansion-button-icon {
+ width: 11px;
+ height: 11px;
+ color: black;
+ margin-left: 27px;
+
+ &:hover {
+ color: rgb(131, 131, 131);
+ cursor: pointer;
+ }
+ }
+
+ .propertiesView-sharingTable {
+
+ border: 1px solid black;
+ padding: 5px;
+ border-radius: 6px;
+ /* width: 170px; */
+ margin-right: 10px;
+ background-color: #ececec;
+ max-height: 130px;
+ overflow-y: scroll;
+
+ .propertiesView-sharingTable-item {
+
+ display: flex;
+ padding: 3px;
+ align-items: center;
+ border-bottom: 0.5px solid grey;
+
+ &:hover .propertiesView-sharingTable-item-name {
+ overflow-x: unset;
+ white-space: unset;
+ overflow-wrap: break-word;
+ }
+
+ .propertiesView-sharingTable-item-name {
+ font-weight: bold;
+ width: 80px;
+ overflow-x: hidden;
+ display: inline-block;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .propertiesView-sharingTable-item-permission {
+ display: flex;
+ right: 34;
+ float: right;
+ position: absolute;
+
+ .permissions-select {
+ z-index: 1;
+ border: none;
+ background-color: inherit;
+ width: 75px;
+ text-align: justify; // for Edge
+ text-align-last: end;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ &:last-child {
+ border-bottom: none;
+ }
+ }
+ }
+
+ .propertiesView-fields {
+ border-bottom: 1px solid black;
+ //padding: 8.5px;
+
+ .propertiesView-fields-title {
+ font-weight: bold;
+ font-size: 12.5px;
+ padding: 4px;
+ display: flex;
+ color: white;
+ padding-left: 8px;
+ background-color: rgb(51, 51, 51);
+
+ .propertiesView-fields-title-name {
+ font-size: 12.5px;
+ font-weight: bold;
+ white-space: nowrap;
+ width: 35px;
+ display: flex;
+ }
+
+ .propertiesView-fields-title-icon {
+ float: right;
+ right: 0;
+ position: absolute;
+ margin-left: 2px;
+ margin-right: 9px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .propertiesView-fields-checkbox {
+ float: right;
+ height: 20px;
+ margin-top: -9px;
+
+ .propertiesView-fields-checkbox-text {
+ font-size: 7px;
+ margin-top: -10px;
+ margin-left: 6px;
+ }
+ }
+
+ .propertiesView-fields-content {
+ font-size: 10px;
+ margin-left: 2px;
+ padding: 10px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .field {
+ display: flex;
+ font-size: 7px;
+ background-color: #e8e8e8;
+ padding-right: 3px;
+ border: 0.5px solid grey;
+ border-radius: 5px;
+ padding-left: 3px;
+ }
+
+ .uneditable-field {
+ display: flex;
+ overflow-y: visible;
+ margin-bottom: 2px;
+
+ &:hover {
+ cursor: auto;
+ }
+ }
+
+ .propertiesView-layout {
+
+ .propertiesView-layout-title {
+ font-weight: bold;
+ font-size: 12.5px;
+ padding: 4px;
+ display: flex;
+ color: white;
+ padding-left: 8px;
+ background-color: rgb(51, 51, 51);
+
+ .propertiesView-layout-title-icon {
+ float: right;
+ right: 0;
+ position: absolute;
+ margin-left: 2px;
+ margin-right: 9px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .propertiesView-layout-content {
+ overflow: hidden;
+ padding: 10px;
+ }
+
+ }
+}
+
+.inking-button {
+
+ display: flex;
+
+ .inking-button-points {
+ background-color: #333333;
+ padding: 7px;
+ border-radius: 7px;
+ margin-right: 32px;
+ width: 32;
+ height: 32;
+ padding-top: 9px;
+ margin-left: 18px;
+
+ &:hover {
+ background: rgb(131, 131, 131);
+ transform: scale(1.05);
+ cursor: pointer;
+ }
+ }
+
+ .inking-button-lock {
+ background-color: #333333;
+ padding: 7px;
+ border-radius: 7px;
+ margin-right: 32px;
+ width: 32;
+ height: 32;
+ padding-top: 9px;
+ padding-left: 10px;
+
+ &:hover {
+ background: rgb(131, 131, 131);
+ transform: scale(1.05);
+ cursor: pointer;
+ }
+ }
+
+ .inking-button-rotate {
+ background-color: #333333;
+ padding: 7px;
+ border-radius: 7px;
+ width: 32;
+ height: 32;
+ padding-top: 9px;
+ padding-left: 10px;
+
+ &:hover {
+ background: rgb(131, 131, 131);
+ transform: scale(1.05);
+ cursor: pointer;
+ }
+ }
+}
+
+.inputBox-duo {
+ display: flex;
+}
+
+.inputBox {
+
+ margin-top: 10px;
+ display: flex;
+ height: 19px;
+ margin-right: 15px;
+
+ .inputBox-title {
+ font-size: 12px;
+ padding-right: 5px;
+ }
+
+ .inputBox-input {
+ font-size: 10px;
+ width: 50px;
+ margin-right: 1px;
+ border-radius: 3px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+
+ .inputBox-button {
+
+ .inputBox-button-up {
+ background-color: #333333;
+ height: 9px;
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ border-radius: 1.5px;
+
+ &:hover {
+ background: rgb(131, 131, 131);
+ transform: scale(1.05);
+ cursor: pointer;
+ }
+ }
+
+ .inputBox-button-down {
+ background-color: #333333;
+ height: 9px;
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ border-radius: 1.5px;
+
+ &:hover {
+ background: rgb(131, 131, 131);
+ transform: scale(1.05);
+ cursor: pointer;
+ }
+ }
+
+ }
+}
+
+.color-palette {
+ width: 160px;
+ height: 360;
+}
+
+.strokeAndFill {
+ display: flex;
+ margin-top: 10px;
+
+ .fill {
+ margin-right: 40px;
+ display: flex;
+ padding-bottom: 7px;
+ margin-left: 35px;
+
+ .fill-title {
+ font-size: 12px;
+ margin-right: 2px;
+ }
+
+ .fill-button {
+ padding-top: 2px;
+ margin-top: -1px;
+ }
+ }
+
+ .stroke {
+ display: flex;
+
+ .stroke-title {
+ font-size: 12px;
+ }
+
+ .stroke-button {
+ padding-top: 2px;
+ margin-left: 2px;
+ margin-top: -1px;
+ }
+ }
+}
+
+.widthAndDash {
+
+ .width {
+ .width-top {
+ display: flex;
+
+ .width-title {
+ font-size: 12;
+ margin-right: 20px;
+ margin-left: 35px;
+ text-align: center;
+ }
+
+ .width-input {
+ margin-right: 30px;
+ margin-top: -10px;
+ }
+ }
+
+ .width-range {
+ margin-right: 1px;
+ margin-bottom: 6;
+ }
+ }
+
+ .arrows {
+
+ display: flex;
+ margin-bottom: 3px;
+
+ .arrows-head {
+
+ display: flex;
+ margin-right: 35px;
+
+ .arrows-head-title {
+ font-size: 10;
+ }
+
+ .arrows-head-input {
+ margin-left: 6px;
+ margin-top: 2px;
+ }
+ }
+
+ .arrows-tail {
+ display: flex;
+
+ .arrows-tail-title {
+ font-size: 10;
+ }
+
+ .arrows-tail-input {
+ margin-left: 6px;
+ margin-top: 2px;
+ }
+ }
+ }
+
+ .dashed {
+
+ display: flex;
+ margin-left: 74px;
+ margin-bottom: 6px;
+
+ .dashed-title {
+ font-size: 10;
+ }
+
+ .dashed-input {
+ margin-left: 6px;
+ margin-top: 2px;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
new file mode 100644
index 000000000..3eb3ef8ab
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -0,0 +1,852 @@
+import React = require("react");
+import { observer } from "mobx-react";
+import "./PropertiesView.scss";
+import { observable, action, computed, runInAction } from "mobx";
+import { Doc, Field, DocListCast, WidthSym, HeightSym, AclSym, AclPrivate, AclReadonly, AclAddonly, AclEdit, AclAdmin, Opt } from "../../../../fields/Doc";
+import { DocumentView } from "../../nodes/DocumentView";
+import { ComputedField } from "../../../../fields/ScriptField";
+import { EditableView } from "../../EditableView";
+import { KeyValueBox } from "../../nodes/KeyValueBox";
+import { Cast, NumCast, StrCast } from "../../../../fields/Types";
+import { listSpec } from "../../../../fields/Schema";
+import { ContentFittingDocumentView } from "../../nodes/ContentFittingDocumentView";
+import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils } from "../../../../Utils";
+import { Id } from "../../../../fields/FieldSymbols";
+import { Transform } from "../../../util/Transform";
+import { PropertiesButtons } from "../../PropertiesButtons";
+import { SelectionManager } from "../../../util/SelectionManager";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip, Checkbox, Divider } from "@material-ui/core";
+import SharingManager from "../../../util/SharingManager";
+import { DocumentType } from "../../../documents/DocumentTypes";
+import FormatShapePane from "./FormatShapePane";
+import { SharingPermissions, GetEffectiveAcl } from "../../../../fields/util";
+import { InkField } from "../../../../fields/InkField";
+import { undoBatch } from "../../../util/UndoManager";
+import { ColorState, SketchPicker } from "react-color";
+import AntimodeMenu from "../../AntimodeMenu";
+import "./FormatShapePane.scss";
+import { discovery_v1 } from "googleapis";
+
+
+interface PropertiesViewProps {
+ width: number;
+ height: number;
+ renderDepth: number;
+ ScreenToLocalTransform: () => Transform;
+ onDown: (event: any) => void;
+}
+
+@observer
+export class PropertiesView extends React.Component<PropertiesViewProps> {
+
+ @computed get MAX_EMBED_HEIGHT() { return 200; }
+
+ @computed get selectedDocumentView() {
+ if (SelectionManager.SelectedDocuments().length) {
+ return SelectionManager.SelectedDocuments()[0];
+ } else { return undefined; }
+ }
+ @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
+ @computed get dataDoc() { return this.selectedDocumentView?.dataDoc; }
+
+ @observable layoutFields: boolean = false;
+
+ @observable openActions: boolean = true;
+ @observable openSharing: boolean = true;
+ @observable openFields: boolean = true;
+ @observable openLayout: boolean = true;
+ @observable openAppearance: boolean = true;
+ @observable openTransform: boolean = true;
+
+ @computed get isInk() { return this.selectedDoc?.type === DocumentType.INK; }
+
+ @action
+ rtfWidth = () => {
+ if (this.selectedDoc) {
+ return Math.min(this.selectedDoc?.[WidthSym](), this.props.width - 20);
+ } else {
+ return 0;
+ }
+ }
+ @action
+ rtfHeight = () => {
+ if (this.selectedDoc) {
+ return this.rtfWidth() <= this.selectedDoc?.[WidthSym]() ? Math.min(this.selectedDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+ } else {
+ return 0;
+ }
+ }
+
+ @action
+ docWidth = () => {
+ if (this.selectedDoc) {
+ const layoutDoc = this.selectedDoc;
+ const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
+ if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.width - 20));
+ return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.width - 20) : this.props.width - 20;
+ } else {
+ return 0;
+ }
+ }
+
+ @action
+ docHeight = () => {
+ if (this.selectedDoc && this.dataDoc) {
+ const layoutDoc = this.selectedDoc;
+ return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
+ const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
+ if (aspect) return this.docWidth() * aspect;
+ return layoutDoc._fitWidth ? (!this.dataDoc._nativeHeight ? NumCast(this.props.height) :
+ Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth,
+ NumCast(this.props.height)))) :
+ NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50;
+ })()));
+ } else {
+ return 0;
+ }
+ }
+
+ @computed get expandedField() {
+ if (this.dataDoc && this.selectedDoc) {
+ const ids: { [key: string]: string } = {};
+ const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc;
+ doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
+ const rows: JSX.Element[] = [];
+ for (const key of Object.keys(ids).slice().sort()) {
+ const contents = doc[key];
+ if (key[0] === "#") {
+ rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "2px" }} key={key}>
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
+ &nbsp;
+ </div>);
+ } else {
+ let contentElement: (JSX.Element | null)[] | JSX.Element = [];
+ contentElement = <EditableView key="editableView"
+ contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
+ height={13}
+ fontSize={10}
+ GetValue={() => Field.toKeyValueString(doc, key)}
+ SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)}
+ />;
+ rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
+ &nbsp;
+ {contentElement}
+ </div>);
+ }
+ }
+ rows.push(<div className="field" key={"newKeyValue"} style={{ marginTop: "3px" }}>
+ <EditableView
+ key="editableView"
+ contents={"add key:value or #tags"}
+ height={13}
+ fontSize={10}
+ GetValue={() => ""}
+ SetValue={this.setKeyValue} />
+ </div>);
+ return rows;
+ }
+ }
+
+ @computed get noviceFields() {
+ if (this.dataDoc && this.selectedDoc) {
+ const ids: { [key: string]: string } = {};
+ const doc = this.dataDoc;
+ doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
+ const rows: JSX.Element[] = [];
+ for (const key of Object.keys(ids).slice().sort()) {
+ if (key[0] === key[0].toUpperCase() || key[0] === "#" || key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) {
+ const contents = doc[key];
+ if (key[0] === "#") {
+ rows.push(<div className="uneditable-field" key={key}>
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
+ &nbsp;
+ </div>);
+ } else {
+ const value = Field.toString(contents as Field);
+ if (key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) {
+ rows.push(<div className="uneditable-field" key={key}>
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ": "}</span>
+ <div style={{ whiteSpace: "nowrap", overflowX: "hidden" }}>{value}</div>
+ </div>);
+ } else {
+ let contentElement: (JSX.Element | null)[] | JSX.Element = [];
+ contentElement = <EditableView key="editableView"
+ contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
+ height={13}
+ fontSize={10}
+ GetValue={() => Field.toKeyValueString(doc, key)}
+ SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)}
+ />;
+
+ rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
+ &nbsp;
+ {contentElement}
+ </div>);
+ }
+ }
+ }
+ }
+ rows.push(<div className="field" key={"newKeyValue"} style={{ marginTop: "3px" }}>
+ <EditableView
+ key="editableView"
+ contents={"add key:value or #tags"}
+ height={13}
+ fontSize={10}
+ GetValue={() => ""}
+ SetValue={this.setKeyValue} />
+ </div>);
+ return rows;
+ }
+ }
+
+
+ setKeyValue = (value: string) => {
+ if (this.selectedDoc && this.dataDoc) {
+ const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc;
+ if (value.indexOf(":") !== -1) {
+ const newVal = value[0].toUpperCase() + value.substring(1, value.length);
+ KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true);
+ return true;
+ } else if (value[0] === "#") {
+ const newVal = value + `:'${value}'`;
+ KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @computed get layoutPreview() {
+ if (this.selectedDoc) {
+ const layoutDoc = Doc.Layout(this.selectedDoc);
+ const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
+ const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
+ return <div style={{ display: "inline-block", height: panelHeight() }} key={this.selectedDoc[Id]}>
+ <ContentFittingDocumentView
+ Document={layoutDoc}
+ DataDoc={this.dataDoc}
+ LibraryPath={emptyPath}
+ renderDepth={this.props.renderDepth + 1}
+ rootSelected={returnFalse}
+ treeViewDoc={undefined}
+ backgroundColor={() => "lightgrey"}
+ fitToBox={false}
+ FreezeDimensions={true}
+ NativeWidth={layoutDoc.type ===
+ StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : returnZero}
+ NativeHeight={layoutDoc.type ===
+ StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : returnZero}
+ PanelWidth={panelWidth}
+ PanelHeight={panelHeight}
+ focus={returnFalse}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ docFilters={returnEmptyFilter}
+ ContainingCollectionDoc={undefined}
+ ContainingCollectionView={undefined}
+ addDocument={returnFalse}
+ moveDocument={undefined}
+ removeDocument={returnFalse}
+ parentActive={() => false}
+ whenActiveChanged={emptyFunction}
+ addDocTab={returnFalse}
+ pinToPres={emptyFunction}
+ bringToFront={returnFalse}
+ ContentScaling={returnOne}
+ dontRegisterView={true}
+ />
+ </div>;
+ } else {
+ return null;
+ }
+ }
+
+ getPermissionsSelect(user: string) {
+ return <select className="permissions-select"
+ onChange={e => SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, this.selectedDoc!)}>
+ {Object.values(SharingPermissions).map(permission => {
+ return (
+ <option key={permission} value={permission} selected={this.selectedDoc![`ACL-${user.replace(".", "_")}`] === permission}>
+ {permission}
+ </option>);
+ })}
+ </select>;
+ }
+
+ @computed get notifyIcon() {
+ return <Tooltip title={<><div className="dash-tooltip">Notify with message</div></>}>
+ <div className="notify-button">
+ <FontAwesomeIcon className="notify-button-icon" icon="bell" color="white" size="sm" />
+ </div>
+ </Tooltip>;
+ }
+
+ @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);
+ }
+ }}>
+ <FontAwesomeIcon className="expansion-button-icon" icon="ellipsis-h" color="black" size="sm" />
+ </div>
+ </Tooltip>;
+ }
+
+ sharingItem(name: string, effectiveAcl: symbol, permission?: string) {
+ return <div className="propertiesView-sharingTable-item">
+ <div className="propertiesView-sharingTable-item-name" style={{ width: name !== "Me" ? "70px" : "80px" }}> {name} </div>
+ {name !== "Me" ? this.notifyIcon : null}
+ <div className="propertiesView-sharingTable-item-permission">
+ {effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name) : permission}
+ {permission === "Owner" ? this.expansionIcon : null}
+ </div>
+ </div>;
+ }
+
+ @computed get sharingTable() {
+ const AclMap = new Map<symbol, string>([
+ [AclPrivate, SharingPermissions.None],
+ [AclReadonly, SharingPermissions.View],
+ [AclAddonly, SharingPermissions.Add],
+ [AclEdit, SharingPermissions.Edit],
+ [AclAdmin, SharingPermissions.Admin]
+ ]);
+
+ const effectiveAcl = GetEffectiveAcl(this.selectedDoc!);
+ const tableEntries = [];
+
+ if (this.selectedDoc![AclSym]) {
+ for (const [key, value] of Object.entries(this.selectedDoc![AclSym])) {
+ const name = key.substring(4).replace("_", ".");
+ if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author) tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value)!));
+ }
+ }
+
+ 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"));
+
+ return <div className="propertiesView-sharingTable">
+ {tableEntries}
+ </div>;
+ }
+
+ @computed get fieldsCheckbox() {
+ return <Checkbox
+ color="primary"
+ onChange={this.toggleCheckbox}
+ checked={this.layoutFields}
+ />;
+ }
+
+ @action
+ toggleCheckbox = () => {
+ this.layoutFields = !this.layoutFields;
+ }
+
+ @computed get editableTitle() {
+ return <EditableView
+ key="editableView"
+ contents={StrCast(this.selectedDoc?.title)}
+ height={25}
+ fontSize={14}
+ GetValue={() => StrCast(this.selectedDoc?.title)}
+ SetValue={this.setTitle} />;
+ }
+
+ @action
+ setTitle = (value: string) => {
+ if (this.dataDoc) {
+ this.selectedDoc && (this.selectedDoc.title = value);
+ KeyValueBox.SetField(this.dataDoc, "title", value, true);
+ return true;
+ }
+ return false;
+ }
+
+
+
+
+
+
+
+
+
+ @undoBatch
+ @action
+ rotate = (angle: number) => {
+ const _centerPoints: { X: number, Y: number }[] = [];
+ if (this.selectedDoc) {
+ const doc = this.selectedDoc;
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
+ const xs = ink.map(p => p.X);
+ const ys = ink.map(p => p.Y);
+ const left = Math.min(...xs);
+ const top = Math.min(...ys);
+ const right = Math.max(...xs);
+ const bottom = Math.max(...ys);
+ _centerPoints.push({ X: left, Y: top });
+ }
+ }
+
+ var index = 0;
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
+ doc.rotation = Number(doc.rotation) + Number(angle);
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
+
+ const newPoints: { X: number, Y: number }[] = [];
+ for (var i = 0; i < ink.length; i++) {
+ const newX = Math.cos(angle) * (ink[i].X - _centerPoints[index].X) - Math.sin(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].X;
+ const newY = Math.sin(angle) * (ink[i].X - _centerPoints[index].X) + Math.cos(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].Y;
+ newPoints.push({ X: newX, Y: newY });
+ }
+ doc.data = new InkField(newPoints);
+ const xs = newPoints.map(p => p.X);
+ const ys = newPoints.map(p => p.Y);
+ const left = Math.min(...xs);
+ const top = Math.min(...ys);
+ const right = Math.max(...xs);
+ const bottom = Math.max(...ys);
+
+ doc._height = (bottom - top);
+ doc._width = (right - left);
+ }
+ index++;
+ }
+ }
+ }
+
+ @observable _controlBtn: boolean = false;
+ @observable _lock: boolean = false;
+
+ @computed
+ get controlPointsButton() {
+ return <div className="inking-button">
+ <Tooltip title={<><div className="dash-tooltip">{"Edit points"}</div></>}>
+ <div className="inking-button-points" onPointerDown={action(() => this._controlBtn = !this._controlBtn)} style={{ backgroundColor: this._controlBtn ? "black" : "" }}>
+ <FontAwesomeIcon icon="bezier-curve" color="white" size="lg" />
+ </div>
+ </Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{this._lock ? "Unlock points" : "Lock points"}</div></>}>
+ <div className="inking-button-lock" onPointerDown={action(() => this._lock = !this._lock)} >
+ <FontAwesomeIcon icon={this._lock ? "unlock" : "lock"} color="white" size="lg" />
+ </div>
+ </Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Rotate 90˚"}</div></>}>
+ <div className="inking-button-rotate" onPointerDown={action(() => this.rotate(Math.PI / 2))}>
+ <FontAwesomeIcon icon="undo" color="white" size="lg" />
+ </div>
+ </Tooltip>
+ </div>;
+ }
+
+ inputBox = (key: string, value: any, setter: (val: string) => {}, title: string) => {
+ return <div className="inputBox"
+ style={{
+ marginRight: title === "X:" ? "19px" : "",
+ marginLeft: title === "∠:" ? "39px" : ""
+ }}>
+ <div className="inputBox-title"> {title} </div>
+ <input className="inputBox-input"
+ type="text" value={value}
+ onChange={e => setter(e.target.value)} />
+ <div className="inputBox-button">
+ <div className="inputBox-button-up" key="up2"
+ onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))} >
+ <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
+ </div>
+ <div className="inputbox-Button-down" key="down2"
+ onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} >
+ <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+ </div>
+ </div>
+ </div>;
+ }
+
+ inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => {
+ return <div className="inputBox-duo">
+ {this.inputBox(key, value, setter, title1)}
+ {title2 === "" ? (null) : this.inputBox(key2, value2, setter2, title2)}
+ </div>;
+ }
+
+ @action
+ upDownButtons = (dirs: string, field: string) => {
+ switch (field) {
+ case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break;
+ // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break;
+ case "Xps": this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === "up" ? 10 : -10)); break;
+ case "Yps": this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === "up" ? 10 : -10)); break;
+ case "stk": this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;
+ case "wid":
+ const oldWidth = NumCast(this.selectedDoc?._width);
+ const oldHeight = NumCast(this.selectedDoc?._height);
+ const oldX = NumCast(this.selectedDoc?.x);
+ const oldY = NumCast(this.selectedDoc?.y);
+ this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === "up" ? 10 : - 10));
+ this._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height)));
+ const doc = this.selectedDoc;
+ if (doc?.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
+ console.log(doc.x, doc.y, doc._height, doc._width);
+ const ink = Cast(doc.data, InkField)?.inkData;
+ console.log(ink);
+ if (ink) {
+ const newPoints: { X: number, Y: number }[] = [];
+ for (var j = 0; j < ink.length; j++) {
+ // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
+ const newX = (NumCast(doc.x) - oldX) + (ink[j].X * NumCast(doc._width)) / oldWidth;
+ const newY = (NumCast(doc.y) - oldY) + (ink[j].Y * NumCast(doc._height)) / oldHeight;
+ newPoints.push({ X: newX, Y: newY });
+ }
+ doc.data = new InkField(newPoints);
+ }
+ }
+ break;
+ case "hgt":
+ const oWidth = NumCast(this.selectedDoc?._width);
+ const oHeight = NumCast(this.selectedDoc?._height);
+ const oX = NumCast(this.selectedDoc?.x);
+ const oY = NumCast(this.selectedDoc?.y);
+ this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === "up" ? 10 : - 10));
+ this._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width)));
+ const docu = this.selectedDoc;
+ if (docu?.type === DocumentType.INK && docu.x && docu.y && docu._height && docu._width) {
+ console.log(docu.x, docu.y, docu._height, docu._width);
+ const ink = Cast(docu.data, InkField)?.inkData;
+ console.log(ink);
+ if (ink) {
+ const newPoints: { X: number, Y: number }[] = [];
+ for (var j = 0; j < ink.length; j++) {
+ // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
+ const newX = (NumCast(docu.x) - oX) + (ink[j].X * NumCast(docu._width)) / oWidth;
+ const newY = (NumCast(docu.y) - oY) + (ink[j].Y * NumCast(docu._height)) / oHeight;
+ newPoints.push({ X: newX, Y: newY });
+ }
+ docu.data = new InkField(newPoints);
+ }
+ }
+ break;
+ }
+ }
+
+ getField(key: string) {
+ //if (this.selectedDoc) {
+ return Field.toString(this.selectedDoc[key] as Field);
+ // } else {
+ // return undefined as Opt<string>;
+ // }
+ }
+
+ @computed get shapeXps() { return this.getField("x"); }
+ @computed get shapeYps() { return this.getField("y"); }
+ @computed get shapeRot() { return this.getField("rotation"); }
+ @computed get shapeHgt() { return this.getField("_height"); }
+ @computed get shapeWid() { return this.getField("_width"); }
+ set shapeXps(value) { this.selectedDoc && (this.selectedDoc.x = Number(value)); }
+ set shapeYps(value) { this.selectedDoc && (this.selectedDoc.y = Number(value)); }
+ set shapeRot(value) { this.selectedDoc && (this.selectedDoc.rotation = Number(value)); }
+ set shapeWid(value) {
+ const oldWidth = NumCast(this.selectedDoc?._width);
+ this.selectedDoc && (this.selectedDoc._width = Number(value));
+ this._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) * NumCast(this.selectedDoc?._height)) / oldWidth);
+ }
+ set shapeHgt(value) {
+ const oldHeight = NumCast(this.selectedDoc?._height);
+ this.selectedDoc && (this.selectedDoc._height = Number(value));
+ this._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight);
+ }
+
+ @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => this.shapeHgt = val, "H:", "wid", this.shapeWid, (val: string) => this.shapeWid = val, "W:"); }
+ @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); }
+ @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); }
+
+ @observable private _fillBtn = false;
+ @observable private _lineBtn = false;
+
+ private _lastFill = "#D0021B";
+ private _lastLine = "#D0021B";
+ private _lastDash: any = "2";
+
+ @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; }
+ @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; }
+ set colorFil(value) { value && (this._lastFill = value); this.selectedDoc && (this.selectedDoc.fillColor = value ? value : undefined); }
+ set colorStk(value) { value && (this._lastLine = value); this.selectedDoc && (this.selectedDoc.color = value ? value : undefined); }
+
+ colorButton(value: string, setter: () => {}) {
+ return <div className="color-button" key="color" onPointerDown={undoBatch(action(e => setter()))}>
+ <div className="color-button-preview" style={{
+ backgroundColor: value ?? "121212", width: 15, height: 15,
+ display: value === "" || value === "transparent" ? "none" : ""
+ }} />
+ {value === "" || value === "transparent" ? <p style={{ fontSize: 25, color: "red", marginTop: -14, position: "fixed" }}>☒</p> : ""}
+ </div>;
+ }
+
+ @undoBatch
+ @action
+ switchStk = (color: ColorState) => {
+ const val = String(color.hex);
+ this.colorStk = val;
+ return true;
+ }
+ @undoBatch
+ @action
+ switchFil = (color: ColorState) => {
+ const val = String(color.hex);
+ this.colorFil = val;
+ return true;
+ }
+
+ colorPicker(setter: (color: string) => {}, type: string) {
+ return <SketchPicker onChange={type === "stk" ? this.switchStk : this.switchFil}
+ presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
+ '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
+ '#FFFFFF', '#f1efeb', 'transparent']}
+ color={type === "stk" ? this.colorStk : this.colorFil} />;
+ }
+
+ @computed get fillButton() { return this.colorButton(this.colorFil, () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); }
+ @computed get lineButton() { return this.colorButton(this.colorStk, () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); }
+
+ @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); }
+ @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); }
+
+ @computed get strokeAndFill() {
+ return <div>
+ <div key="fill" className="strokeAndFill">
+ <div className="fill">
+ <div className="fill-title">Fill:</div>
+ <div className="fill-button">{this.fillButton}</div>
+ </div>
+ <div className="stroke">
+ <div className="stroke-title"> Stroke: </div>
+ <div className="stroke-button">{this.lineButton}</div>
+ </div>
+ </div>
+ {this._fillBtn ? this.fillPicker : ""}
+ {this._lineBtn ? this.linePicker : ""}
+ </div>;
+ }
+
+ @computed get solidStk() { return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === "0") ? true : false; }
+ @computed get dashdStk() { return this.selectedDoc?.strokeDash || ""; }
+ @computed get unStrokd() { return this.selectedDoc?.color ? true : false; }
+ @computed get widthStk() { return this.getField("strokeWidth") || "1"; }
+ @computed get markHead() { return this.getField("strokeStartMarker") || ""; }
+ @computed get markTail() { return this.getField("strokeEndMarker") || ""; }
+ set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; }
+ set dashdStk(value) {
+ value && (this._lastDash = value) && (this.unStrokd = false);
+ this.selectedDoc && (this.selectedDoc.strokeDash = value ? this._lastDash : undefined);
+ }
+ set widthStk(value) { this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value)); }
+ set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; }
+ set markHead(value) { this.selectedDoc && (this.selectedDoc.strokeStartMarker = value); }
+ set markTail(value) { this.selectedDoc && (this.selectedDoc.strokeEndMarker = value); }
+
+
+ @computed get stkInput() { return this.regInput("stk", this.widthStk, (val: string) => this.widthStk = val); }
+
+
+ regInput = (key: string, value: any, setter: (val: string) => {}) => {
+ return <div className="inputBox">
+ <input className="inputBox-input"
+ type="text" value={value}
+ onChange={e => setter(e.target.value)} />
+ <div className="inputBox-button">
+ <div className="inputBox-button-up" key="up2"
+ onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))} >
+ <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
+ </div>
+ <div className="inputbox-Button-down" key="down2"
+ onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} >
+ <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+ </div>
+ </div>
+ </div>;
+ }
+
+ @computed get widthAndDash() {
+ return <div className="widthAndDash">
+ <div className="width">
+ <div className="width-top">
+ <div className="width-title">Width:</div>
+ <div className="width-input">{this.stkInput}</div>
+ </div>
+ <input className="width-range" type="range"
+ defaultValue={Number(this.widthStk)} min={1} max={100}
+ onChange={undoBatch(action((e) => this.widthStk = e.target.value))} />
+ </div>
+
+ <div className="arrows">
+ <div className="arrows-head">
+ <div className="arrows-head-title" >Arrow Head: </div>
+ <input key="markHead" className="arrows-head-input" type="checkbox"
+ checked={this.markHead !== ""}
+ onChange={undoBatch(action(() => this.markHead = this.markHead ? "" : "arrow"))} />
+ </div>
+ <div className="arrows-tail">
+ <div className="arrows-tail-title" >Arrow End: </div>
+ <input key="markTail" className="arrows-tail-input" type="checkbox"
+ checked={this.markTail !== ""}
+ onChange={undoBatch(action(() => this.markTail = this.markTail ? "" : "arrow"))} />
+ </div>
+ </div>
+ <div className="dashed">
+ <div className="dashed-title">Dash:</div>
+ <input key="markHead" className="dashed-input"
+ type="checkbox" checked={this.dashdStk === "2"}
+ onChange={this.changeDash} />
+ </div>
+ </div>;
+ }
+
+ @undoBatch @action
+ changeDash = () => {
+ const dash = this.dashdStk;
+ if (dash === "2") {
+ this.dashdStk = "0";
+ } else {
+ this.dashdStk = "2";
+ }
+ }
+
+ @computed get appearanceEditor() {
+ return <div className="appearance-editor">
+ {this.widthAndDash}
+ {this.strokeAndFill}
+ </div>;
+ }
+
+ @computed get transformEditor() {
+ return <div className="transform-editor">
+ {this.controlPointsButton}
+ {this.hgtInput}
+ {this.XpsInput}
+ {this.rotInput}
+ </div>;
+ }
+
+ render() {
+
+ if (!this.selectedDoc) {
+ return <div className="propertiesView" style={{ width: this.props.width }}>
+ <div className="propertiesView-title" style={{ width: this.props.width, paddingLeft: 6 }}>
+ No Document Selected
+ </div> </div>;
+ }
+
+ const novice = Doc.UserDoc().noviceMode;
+
+ return <div className="propertiesView" style={{ width: this.props.width }} >
+ <div className="propertiesView-title" style={{ width: this.props.width }}>
+ Properties
+ <div className="propertiesView-title-icon" onPointerDown={this.props.onDown}>
+ <FontAwesomeIcon icon="times" color="black" size="sm" />
+ </div>
+ </div>
+ <div className="propertiesView-name">
+ {this.editableTitle}
+ </div>
+ <div className="propertiesView-settings">
+ <div className="propertiesView-settings-title"
+ onPointerDown={() => runInAction(() => { this.openActions = !this.openActions; })}
+ style={{ backgroundColor: this.openActions ? "black" : "" }}>
+ Actions
+ <div className="propertiesView-settings-title-icon">
+ <FontAwesomeIcon icon={this.openActions ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {this.openActions ? <div className="propertiesView-settings-content">
+ <PropertiesButtons />
+ </div> : null}
+ </div>
+ <div className="propertiesView-sharing">
+ <div className="propertiesView-sharing-title"
+ onPointerDown={() => runInAction(() => { this.openSharing = !this.openSharing; })}
+ style={{ backgroundColor: this.openSharing ? "black" : "" }}>
+ Sharing {"&"} Permissions
+ <div className="propertiesView-sharing-title-icon">
+ <FontAwesomeIcon icon={this.openSharing ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {this.openSharing ? <div className="propertiesView-sharing-content">
+ {this.sharingTable}
+ </div> : null}
+ </div>
+
+
+
+
+ {this.isInk ? <div className="propertiesView-appearance">
+ <div className="propertiesView-appearance-title"
+ onPointerDown={() => runInAction(() => { this.openAppearance = !this.openAppearance; })}
+ style={{ backgroundColor: this.openAppearance ? "black" : "" }}>
+ Appearance
+ <div className="propertiesView-appearance-title-icon">
+ <FontAwesomeIcon icon={this.openAppearance ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {this.openAppearance ? <div className="propertiesView-appearance-content">
+ {this.appearanceEditor}
+ </div> : null}
+ </div> : null}
+
+ {this.isInk ? <div className="propertiesView-transform">
+ <div className="propertiesView-transform-title"
+ onPointerDown={() => runInAction(() => { this.openTransform = !this.openTransform; })}
+ style={{ backgroundColor: this.openTransform ? "black" : "" }}>
+ Transform
+ <div className="propertiesView-transform-title-icon">
+ <FontAwesomeIcon icon={this.openTransform ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {this.openTransform ? <div className="propertiesView-transform-content">
+ {this.transformEditor}
+ </div> : null}
+ </div> : null}
+
+
+
+
+
+ <div className="propertiesView-fields">
+ <div className="propertiesView-fields-title"
+ onPointerDown={() => runInAction(() => { this.openFields = !this.openFields; })}
+ style={{ backgroundColor: this.openFields ? "black" : "" }}>
+ <div className="propertiesView-fields-title-name">
+ Fields {"&"} Tags
+ <div className="propertiesView-fields-title-icon">
+ <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ </div>
+ {!novice && this.openFields ? <div className="propertiesView-fields-checkbox">
+ {this.fieldsCheckbox}
+ <div className="propertiesView-fields-checkbox-text">Layout</div>
+ </div> : null}
+ {this.openFields ?
+ <div className="propertiesView-fields-content">
+ {novice ? this.noviceFields : this.expandedField}
+ </div> : null}
+ </div>
+ <div className="propertiesView-layout">
+ <div className="propertiesView-layout-title"
+ onPointerDown={() => runInAction(() => { this.openLayout = !this.openLayout; })}
+ style={{ backgroundColor: this.openLayout ? "black" : "" }}>
+ Layout
+ <div className="propertiesView-layout-title-icon"
+ onPointerDown={() => runInAction(() => { this.openLayout = !this.openLayout; })}>
+ <FontAwesomeIcon icon={this.openLayout ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {this.openLayout ? <div className="propertiesView-layout-content">{this.layoutPreview}</div> : null}
+ </div>
+ </div>;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index d1c839c3b..0aabf5319 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -182,7 +182,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
switch (this.props.destinationDoc.type) {
case DocumentType.IMG: destinationIcon = "image"; break;
case DocumentType.COMPARISON: destinationIcon = "columns"; break;
- case DocumentType.RTF: destinationIcon = "font"; break;
+ case DocumentType.RTF: destinationIcon = "sticky-note"; break;
case DocumentType.COL: destinationIcon = "folder"; break;
case DocumentType.WEB: destinationIcon = "globe-asia"; break;
case DocumentType.SCREENSHOT: destinationIcon = "photo-video"; break;
@@ -196,6 +196,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
case DocumentType.DOCHOLDER: destinationIcon = "expand"; break;
case DocumentType.VID: destinationIcon = "video"; break;
case DocumentType.INK: destinationIcon = "pen-nib"; break;
+ case DocumentType.PDF: destinationIcon = "file"; break;
default: destinationIcon = "question"; break;
}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 6081def5d..f7e253f42 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -78,6 +78,9 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
render() {
TraceMobx();
+ if (this.props.Document.title === "Archive") {
+ console.log("");
+ }
return (<div className="contentFittingDocumentView" style={{
width: Math.abs(this.centeringYOffset) > 0.001 ? "auto" : this.props.PanelWidth(),
height: Math.abs(this.centeringOffset) > 0.0001 ? "auto" : this.props.PanelHeight(),
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 3b91ac6b5..198a90225 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -198,8 +198,11 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
// }))}
>
+ {/* {this.props.InMenu ? this.props.StartLink ? <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> :
+ <FontAwesomeIcon className="documentdecorations-icon" icon="hand-paper" size="sm" /> : links.length} */}
+
{this.props.InMenu ? this.props.StartLink ? <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> :
- <FontAwesomeIcon className="documentdecorations-icon" icon="hand-paper" size="sm" /> : links.length}
+ link : links.length}
</div>
{DocumentLinksButton.StartLink && this.props.InMenu && !!!this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View ? <div className={"documentLinksButton-endLink"}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 15cf9556b..30382a4ef 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -94,6 +94,8 @@ export interface DocumentViewProps {
layoutKey?: string;
radialMenu?: String[];
display?: string;
+ relative?: boolean;
+ scriptContext?: any;
}
@observer
@@ -287,7 +289,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
onClick = action((e: React.MouseEvent | React.PointerEvent) => {
- if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick &&
+ if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 &&
(Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
let stopPropagate = true;
let preventDefault = true;
@@ -319,10 +321,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const func = () => this.onClickHandler.script.run({
this: this.layoutDoc,
self: this.rootDoc,
+ scriptContext: this.props.scriptContext,
thisContainer: this.props.ContainingCollectionDoc,
shiftKey: e.shiftKey
}, console.log);
- if (this.props.Document !== Doc.UserDoc()["dockedBtn-undo"] && this.props.Document !== Doc.UserDoc()["dockedBtn-redo"]) {
+ if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) && !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc)) {
UndoManager.RunInBatch(func, "on click");
} else func();
} else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself
@@ -584,6 +587,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
+
+ @undoBatch
+ noOnClick = (): void => {
+ this.Document.ignoreClick = false;
+ this.Document.isLinkButton = false;
+ }
+
+ @undoBatch
+ toggleDetail = (): void => {
+ this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`);
+ }
+
@undoBatch @action
drop = async (e: Event, de: DragManager.DropEvent) => {
if (this.props.Document === Doc.UserDoc().activeWorkspace) {
@@ -661,6 +676,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@action
+ onCopy = () => {
+ const copy = Doc.MakeCopy(this.props.Document, true);
+ copy.x = NumCast(this.props.Document.x) + NumCast(this.props.Document._width);
+ copy.y = NumCast(this.props.Document.y) + 30;
+ this.props.addDocument?.(copy);
+ }
+
+ @action
onContextMenu = async (e: React.MouseEvent | Touch): Promise<void> => {
// the touch onContextMenu is button 0, the pointer onContextMenu is button 2
if (!(e instanceof Touch)) {
@@ -704,14 +727,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const existingOnClick = cm.findByDescription("OnClick...");
const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
- onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "window-restore" });
+ onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" });
onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
- onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "concierge-bell" });
- !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "concierge-bell" });
- onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "concierge-bell" });
- onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "snowflake" });
- onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" });
- !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "hand-point-right" });
+ onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
+ !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "link" });
+ onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
+ onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
+ onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
+ !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
const funcs: ContextMenuProps[] = [];
if (this.layoutDoc.onDragStart) {
@@ -724,6 +747,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const more = cm.findByDescription("More...");
const moreItems = more && "subitems" in more ? more.subitems : [];
moreItems.push({ description: "Download document", icon: "download", event: async () => Doc.Zip(this.props.Document) });
+ moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "users" });
+ //moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
+ moreItems.push({ description: "Create an Alias", event: () => this.onCopy(), icon: "copy" });
if (!Doc.UserDoc().noviceMode) {
moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
@@ -735,16 +761,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
}
+ //GetEffectiveAcl(this.props.Document) === AclEdit && moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
+
const effectiveAcl = GetEffectiveAcl(this.props.Document);
(effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
- moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" });
+
!more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
const help = cm.findByDescription("Help...");
const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
+ !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" });
- helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
@@ -786,8 +814,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
@computed.struct get linkOffset() { return [-15, 0]; }
@computed get contents() {
+ const pos = this.props.relative ? "relative " : "absolute";
TraceMobx();
- return (<div style={{ position: "absolute", width: "100%", height: "100%" }}>
+ return (<div style={{ width: "100%", height: "100%" }}>
<DocumentContentsView key={1}
docFilters={this.props.docFilters}
ContainingCollectionView={this.props.ContainingCollectionView}
@@ -823,6 +852,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
ChromeHeight={this.chromeHeight}
isSelected={this.isSelected}
select={this.select}
+ scriptContext={this.props.scriptContext}
onClick={this.onClickFunc}
layoutKey={this.finalLayoutKey} />
{this.layoutDoc.hideAllLinks ? (null) : this.allAnchors}
@@ -877,8 +907,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@computed get innards() {
TraceMobx();
+ const pos = this.props.relative ? "relative" : undefined;
if (this.props.treeViewDoc && !this.props.LayoutTemplateString?.includes("LinkAnchorBox")) { // this happens when the document is a tree view label (but not an anchor dot)
- return <div className="documentView-treeView" style={{ maxWidth: this.props.PanelWidth() || undefined }}>
+ return <div className="documentView-treeView" style={{
+ maxWidth: this.props.PanelWidth() || undefined,
+ position: pos
+ }}>
{StrCast(this.props.Document.title)}
{this.allAnchors}
</div>;
@@ -1004,7 +1038,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
background: finalColor,
opacity: finalOpacity,
fontFamily: StrCast(this.Document._fontFamily, "inherit"),
- fontSize: Cast(this.Document._fontSize, "string", null)
+ fontSize: Cast(this.Document._fontSize, "string", null),
}}>
{this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <>
{this.innards}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 48e1f6ce3..23ae48108 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -60,6 +60,7 @@ export interface FieldViewProps {
color?: string;
xMargin?: number;
yMargin?: number;
+ scriptContext?: any;
}
@observer
diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss
index 5b85d8b0b..13af511a6 100644
--- a/src/client/views/nodes/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox.scss
@@ -10,13 +10,13 @@
.fontIconBox-label {
background: gray;
- color:white;
+ color: white;
border-radius: 8px;
- width:100%;
+ width: 100%;
position: absolute;
text-align: center;
font-size: 8px;
- margin-top:4px;
+ //margin-top:4px;
letter-spacing: normal;
left: 0;
overflow: hidden;
@@ -26,4 +26,56 @@
width: 95% !important;
height: 95%;
}
+}
+
+.menuButton {
+ //padding: 7px;
+ padding-left: 5px;
+ width: 100%;
+ width: 60px;
+ height: 70px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+
+ .menuButton-wrap {
+ width: 45px;
+ /* padding: 5px; */
+ touch-action: none;
+ background: black;
+ transform-origin: top left;
+ /* margin-bottom: 5px; */
+ margin-top: 5px;
+ margin-right: 25px;
+ border-radius: 8px;
+
+ &:hover {
+ background: rgb(61, 61, 61);
+ cursor: pointer;
+ }
+ }
+
+ .menuButton-label {
+ color: white;
+ margin-right: 4px;
+ border-radius: 8px;
+ width: 42px;
+ position: relative;
+ text-align: center;
+ font-size: 7px;
+ margin-top: 1px;
+ letter-spacing: normal;
+ padding: 3px;
+ background-color: inherit;
+ }
+
+ .menuButton-icon {
+ width: auto;
+ height: 32px;
+ padding: 5px;
+ }
+
+ svg {
+ width: 95% !important;
+ height: 95%;
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 2611d2ca7..720d6a15d 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -12,8 +12,10 @@ import { Doc } from '../../../fields/Doc';
import { ContextMenu } from '../ContextMenu';
import { ScriptField } from '../../../fields/ScriptField';
import { Tooltip } from '@material-ui/core';
+import { createUnionOrIntersectionTypeNode } from 'typescript';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
const FontIconSchema = createSchema({
- icon: "string"
+ icon: "string",
});
type FontIconDocument = makeInterface<[typeof FontIconSchema]>;
@@ -59,20 +61,45 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
}
render() {
- const referenceDoc = (this.layoutDoc.dragFactory instanceof Doc ? this.layoutDoc.dragFactory : this.layoutDoc);
- const refLayout = Doc.Layout(referenceDoc);
- const button = <button className="fontIconBox-outerDiv" ref={this._ref} onContextMenu={this.specificContextMenu}
- style={{
- padding: Cast(this.layoutDoc._xPadding, "number", null),
- background: StrCast(refLayout._backgroundColor, StrCast(refLayout.backgroundColor)),
- boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined
- }}>
- <FontAwesomeIcon className="fontIconBox-icon" icon={StrCast(this.dataDoc.icon, "user") as any} color={StrCast(this.layoutDoc.color, this._foregroundColor)} size="sm" />
- {!this.rootDoc.title ? (null) : <div className="fontIconBox-label" style={{ width: this.rootDoc.label ? "max-content" : undefined }}> {StrCast(this.rootDoc.label, StrCast(this.rootDoc.title).substring(0, 6))} </div>}
- </button>;
- return !this.layoutDoc.toolTip ? button :
- <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
- {button}
- </Tooltip>;
+
+ //style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document) }}>
+
+ if (this.layoutDoc.menuIcon) {
+
+ let backgroundColor = "black";
+ if (this.dataDoc.title === "Sharing" || this.dataDoc.title === "Help" || this.dataDoc.title === "Settings" || this.dataDoc.title === "Import") {
+ backgroundColor = "black";
+ } else {
+ backgroundColor = CurrentUserUtils.selectedPanel === this.dataDoc.title ? "lightgrey" : "black";
+ }
+
+ const color = backgroundColor === "lightgrey" ? "black" : "white";
+ const menuBTN = <div className="menuButton" style={{ backgroundColor: backgroundColor }}>
+
+ <div className="menuButton-wrap" style={{ backgroundColor: backgroundColor }}>
+ <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>
+ </div>;
+
+ return menuBTN;
+ } else {
+ const referenceDoc = (this.layoutDoc.dragFactory instanceof Doc ? this.layoutDoc.dragFactory : this.layoutDoc);
+ const refLayout = Doc.Layout(referenceDoc);
+ const button = <button className="fontIconBox-outerDiv" ref={this._ref} onContextMenu={this.specificContextMenu}
+ style={{
+ padding: Cast(this.layoutDoc._xPadding, "number", null),
+ background: StrCast(refLayout._backgroundColor, StrCast(refLayout.backgroundColor)),
+ boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined
+ }}>
+ <FontAwesomeIcon className="fontIconBox-icon" icon={StrCast(this.dataDoc.icon, "user") as any} color={StrCast(this.layoutDoc.color, this._foregroundColor)} size="sm" />
+ {!this.rootDoc.title ? (null) : <div className="fontIconBox-label" style={{ width: this.rootDoc.label ? "max-content" : undefined }}> {StrCast(this.rootDoc.label, StrCast(this.rootDoc.title).substring(0, 6))} </div>}
+ </button>;
+ return !this.layoutDoc.toolTip ? button :
+ <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
+ {button}
+ </Tooltip>;
+ }
+
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 958a37568..8ae71c035 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -190,7 +190,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
}
list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb"));
list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb"));
- alias._pivotField = this._fieldKey;
+ alias._pivotField = this._fieldKey.startsWith("#") ? "#" : this._fieldKey;
this.props.tbox.props.addDocTab(alias, "onRight");
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 627c6e363..fc65f34eb 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1267,7 +1267,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.doLinkOnDeselect();
// move the richtextmenu offscreen
- if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide();
+ //if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide();
}
_lastTimedMark: Mark | undefined = undefined;
@@ -1346,7 +1346,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
@computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); }
render() {
TraceMobx();
- const scale = this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
+ 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
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 7ccbfa051..f76707a79 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -77,7 +77,8 @@ export default class RichTextMenu extends AntimodeMenu {
super(props);
RichTextMenu.Instance = this;
this._canFade = false;
- this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
+ //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
+ this.Pinned = true;
this.fontSizeOptions = [
{ mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
@@ -529,7 +530,7 @@ export default class RichTextMenu extends AntimodeMenu {
indentParagraph(state: EditorState<any>, dispatch: any) {
var tr = state.tr;
- let headin = false;
+ const heading = false;
state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
if (node.type === schema.nodes.paragraph || node.type === schema.nodes.heading) {
const nodeval = node.attrs.indent ? Number(node.attrs.indent) : undefined;
@@ -539,7 +540,7 @@ export default class RichTextMenu extends AntimodeMenu {
}
return true;
});
- !headin && dispatch?.(tr);
+ !heading && dispatch?.(tr);
return true;
}
@@ -920,16 +921,22 @@ export default class RichTextMenu extends AntimodeMenu {
render() {
TraceMobx();
const row1 = <div className="antimodeMenu-row" key="row 1" style={{ display: this.collapsed ? "none" : undefined }}>{[
- !this.collapsed ? this.getDragger() : (null),
- !this.Pinned ? (null) : <div key="frag1"> {[
- this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
- this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
- this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
- this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
- this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
- this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
- <div className="richTextMenu-divider" key="divider" />
- ]}</div>,
+ //!this.collapsed ? this.getDragger() : (null),
+ // !this.Pinned ? (null) : <div key="frag1"> {[
+ // this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
+ // this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
+ // this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
+ // this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
+ // this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
+ // this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
+ // <div className="richTextMenu-divider" key="divider" />
+ // ]}</div>,
+ this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
+ this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
+ this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
+ this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
+ this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
+ this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
this.createColorButton(),
this.createHighlighterButton(),
this.createLinkButton(),
@@ -957,16 +964,16 @@ export default class RichTextMenu extends AntimodeMenu {
this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule),
<div className="richTextMenu-divider" key="divider 5" />,]}
</div>
- <div key="collapser">
- {/* <div key="collapser">
+ {/* <div key="collapser">
+ {<div key="collapser">
<button className="antimodeMenu-button" key="collapse menu" title="Collapse menu" onClick={this.toggleCollapse} style={{ backgroundColor: this.collapsed ? "#121212" : "", width: 25 }}>
<FontAwesomeIcon icon="chevron-left" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.3s", transform: `rotate(${this.collapsed ? 180 : 0}deg)` }} />
</button>
- </div> */}
+ </div> }
<button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: this.Pinned ? "#121212" : "", display: this.collapsed ? "none" : undefined }}>
<FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
</button>
- </div>
+ </div> */}
</div>;
return (
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 0eca6d753..1616500f6 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -79,14 +79,14 @@ export const nodes: { [index: string]: NodeSpec } = {
{ tag: "h5", attrs: { level: 5 } },
{ tag: "h6", attrs: { level: 6 } }],
toDOM(node) {
- var dom = toParagraphDOM(node) as any;
- var level = node.attrs.level || 1;
+ const dom = toParagraphDOM(node) as any;
+ const level = node.attrs.level || 1;
dom[0] = 'h' + level;
return dom;
},
getAttrs(dom: any) {
- var attrs = getParagraphNodeAttrs(dom) as any;
- var level = Number(dom.nodeName.substring(1)) || 1;
+ const attrs = getParagraphNodeAttrs(dom) as any;
+ const level = Number(dom.nodeName.substring(1)) || 1;
attrs.level = level;
return attrs;
}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index a1acc7060..6d01785aa 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -528,10 +528,10 @@ export namespace Doc {
const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
const field = ProxyField.WithoutProxy(() => doc[key]);
const copyObjectField = async (field: ObjectField) => {
- const list = await Cast(doc[key], listSpec(Doc));
+ const list = Cast(doc[key], listSpec(Doc));
const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
if (docs !== undefined && docs.length) {
- const clones = await Promise.all(docs.map(async d => await Doc.makeClone(d as Doc, cloneMap, rtfs, exclusions, dontCreate)));
+ const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, rtfs, exclusions, dontCreate)));
!dontCreate && assignKey(new List<Doc>(clones));
} else if (doc[key] instanceof Doc) {
assignKey(key.includes("layout[") ? undefined : key.startsWith("layout") ? doc[key] as Doc : await Doc.makeClone(doc[key] as Doc, cloneMap, rtfs, exclusions, dontCreate)); // reference documents except copy documents that are expanded teplate fields
@@ -625,7 +625,7 @@ export namespace Doc {
Array.from(map.entries()).forEach(f => docs[f[0]] = f[1]);
const docString = JSON.stringify({ id: doc[Id], docs }, replacer);
- var zip = new JSZip();
+ const zip = new JSZip();
zip.file("doc.json", docString);
diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts
index e2cd88726..e657866ce 100644
--- a/src/server/ApiManagers/UtilManager.ts
+++ b/src/server/ApiManagers/UtilManager.ts
@@ -6,7 +6,6 @@ import { exec } from 'child_process';
// const recommender = new Recommender();
// recommender.testModel();
-import executeImport from "../../scraping/buxton/final/BuxtonImporter";
export default class UtilManager extends ApiManager {
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index 1a2340afc..78b75d6be 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -1,8 +1,8 @@
-import RouteSubscriber from "./RouteSubscriber";
-import { DashUserModel } from "./authentication/DashUserModel";
-import { Request, Response, Express } from 'express';
-import { cyan, red, green } from 'colors';
+import { cyan, green, red } from 'colors';
+import { Express, Request, Response } from 'express';
import { AdminPriviliges } from ".";
+import { DashUserModel } from "./authentication/DashUserModel";
+import RouteSubscriber from "./RouteSubscriber";
export enum Method {
GET,
diff --git a/src/server/Search.ts b/src/server/Search.ts
index 21064e520..decd1f5b1 100644
--- a/src/server/Search.ts
+++ b/src/server/Search.ts
@@ -1,5 +1,5 @@
-import * as rp from 'request-promise';
import { red } from 'colors';
+import * as rp from 'request-promise';
const pathTo = (relative: string) => `http://localhost:8983/solr/dash/${relative}`;
diff --git a/src/server/downsize.ts b/src/server/downsize.ts
index 5cd709fa3..382994e2d 100644
--- a/src/server/downsize.ts
+++ b/src/server/downsize.ts
@@ -1,5 +1,5 @@
-import * as sharp from 'sharp';
import * as fs from 'fs';
+import * as sharp from 'sharp';
const folder = "./src/server/public/files/";
const pngTypes = ["png", "PNG"];
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index 744d4547b..e40f2b8e5 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -1,31 +1,31 @@
+import * as bodyParser from 'body-parser';
+import { blue, yellow } from 'colors';
+import * as cookieParser from 'cookie-parser';
+import * as cors from "cors";
import * as express from 'express';
-import * as expressValidator from 'express-validator';
import * as session from 'express-session';
+import * as expressValidator from 'express-validator';
+import * as fs from 'fs';
+import { Server as HttpServer } from "http";
+import { createServer, Server as HttpsServer } from "https";
import * as passport from 'passport';
-import * as bodyParser from 'body-parser';
-import * as cookieParser from 'cookie-parser';
-import expressFlash = require('express-flash');
-import flash = require('connect-flash');
-import { Database } from './database';
-import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/AuthenticationManager';
-const MongoStore = require('connect-mongo')(session);
-import RouteManager from './RouteManager';
-import { WebSocket } from './websocket';
+import * as request from 'request';
import * as webpack from 'webpack';
-const config = require('../../webpack.config');
-const compiler = webpack(config);
import * as wdm from 'webpack-dev-middleware';
import * as whm from 'webpack-hot-middleware';
-import * as fs from 'fs';
-import * as request from 'request';
-import RouteSubscriber from './RouteSubscriber';
import { publicDirectory } from '.';
import { logPort } from './ActionUtilities';
-import { blue, yellow } from 'colors';
-import * as cors from "cors";
-import { createServer, Server as HttpsServer } from "https";
-import { Server as HttpServer } from "http";
import { SSL } from './apis/google/CredentialsLoader';
+import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/AuthenticationManager';
+import { Database } from './database';
+import RouteManager from './RouteManager';
+import RouteSubscriber from './RouteSubscriber';
+import { WebSocket } from './websocket';
+import expressFlash = require('express-flash');
+import flash = require('connect-flash');
+const MongoStore = require('connect-mongo')(session);
+const config = require('../../webpack.config');
+const compiler = webpack(config);
/* RouteSetter is a wrapper around the server that prevents the server
from being exposed. */
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index a17dd1afd..827888ff7 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -1,20 +1,20 @@
-import { logPort } from './ActionUtilities';
+import * as express from "express";
+import { blue, green } from "colors";
+import { createServer, Server } from "https";
+import { networkInterfaces } from "os";
+import * as sio from 'socket.io';
+import { Socket } from "socket.io";
+import executeImport from "../scraping/buxton/final/BuxtonImporter";
import { Utils } from "../Utils";
-import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent, RoomMessage } from "./Message";
+import { logPort } from './ActionUtilities';
+import { timeMap } from "./ApiManagers/UserManager";
+import { GoogleCredentialsLoader, SSL } from "./apis/google/CredentialsLoader";
+import YoutubeApi from "./apis/youtube/youtubeApiSample";
import { Client } from "./Client";
-import { Socket } from "socket.io";
import { Database } from "./database";
-import { Search } from "./Search";
-import * as sio from 'socket.io';
-import YoutubeApi from "./apis/youtube/youtubeApiSample";
-import { GoogleCredentialsLoader, SSL } from "./apis/google/CredentialsLoader";
-import { timeMap } from "./ApiManagers/UserManager";
-import { green, blue } from "colors";
-import { networkInterfaces } from "os";
-import executeImport from "../scraping/buxton/final/BuxtonImporter";
import { DocumentsCollection } from "./IDatabase";
-import { createServer, Server } from "https";
-import * as express from "express";
+import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, Transferable, Types, UpdateMobileInkOverlayPositionContent, YoutubeQueryInput, YoutubeQueryTypes } from "./Message";
+import { Search } from "./Search";
import { resolvedPorts } from './server_Initialization';
export namespace WebSocket {