aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/documents/DocumentTypes.ts1
-rw-r--r--src/client/documents/Documents.ts8
-rw-r--r--src/client/util/CurrentUserUtils.ts117
-rw-r--r--src/client/util/SelectionManager.ts3
-rw-r--r--src/client/views/ContextMenuItem.tsx4
-rw-r--r--src/client/views/DocumentButtonBar.tsx8
-rw-r--r--src/client/views/DocumentDecorations.tsx11
-rw-r--r--src/client/views/EditableView.tsx2
-rw-r--r--src/client/views/MainView.scss182
-rw-r--r--src/client/views/MainView.tsx348
-rw-r--r--src/client/views/PropertiesButtons.scss119
-rw-r--r--src/client/views/PropertiesButtons.tsx547
-rw-r--r--src/client/views/collections/CollectionMenu.tsx59
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx5
-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/SchemaTable.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.scss309
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.tsx417
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx3
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx3
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx5
-rw-r--r--src/client/views/nodes/DocumentView.tsx59
-rw-r--r--src/client/views/nodes/MenuIconBox.scss49
-rw-r--r--src/client/views/nodes/MenuIconBox.tsx41
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx2
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx37
29 files changed, 2224 insertions, 143 deletions
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 7578b7df0..e98539498 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 7e094089f..f46b38883 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -31,6 +31,7 @@ import { ColorBox } from "../views/nodes/ColorBox";
import { ComparisonBox } from "../views/nodes/ComparisonBox";
import { DocHolderBox } from "../views/nodes/DocHolderBox";
import { FontIconBox } from "../views/nodes/FontIconBox";
+import { MenuIconBox } from "../views/nodes/MenuIconBox";
import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
import { ImageBox } from "../views/nodes/ImageBox";
import { KeyValueBox } from "../views/nodes/KeyValueBox";
@@ -301,6 +302,9 @@ export namespace Docs {
layout: { view: FontIconBox, dataField: defaultDataKey },
options: { _width: 40, _height: 40, borderRounding: "100%" },
}],
+ [DocumentType.MENUICON, {
+ layout: { view: MenuIconBox, dataField: defaultDataKey },
+ }],
[DocumentType.RECOMMENDATION, {
layout: { view: RecommendationsBox, dataField: defaultDataKey },
options: { _width: 200, _height: 200 },
@@ -791,6 +795,10 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { hideLinkButton: true, ...(options || {}) });
}
+ export function MenuIconDocument(options?: DocumentOptions) {
+ return InstanceFromProto(Prototypes.get(DocumentType.MENUICON), undefined, { hideLinkButton: true, ...(options || {}) });
+ }
+
export function PresElementBoxDocument(options?: DocumentOptions) {
return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) });
}
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 6d752832a..442be98d2 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 panelContent: string = "none";
+
// sets up the default User Templates - slideView, queryView, descriptionView
static setupUserTemplateButtons(doc: Doc) {
if (doc["template-button-query"] === undefined) {
@@ -492,6 +504,53 @@ 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("deleted")' },
+ { title: "Import", icon: "upload", click: 'scriptContext.selectMenu("upload")' },
+ { title: "Sharing", icon: "users", click: 'GroupManager.Instance.open()' },
+ { title: "Tools", icon: "wrench", click: 'scriptContext.selectMenu("tools")' },
+ { title: "Help", icon: "question-circle", click: 'scriptContext.selectMenu("help")' },
+ { title: "Settings", icon: "cog", click: 'SettingsManager.Instance.open()' },
+ ];
+ }
+
+ static setupMenuButtons(doc: Doc) {
+ if (doc.menuStackBtns === undefined) {
+ const buttons = CurrentUserUtils.menuBtnDescriptions();
+ const menuBtns = buttons.map(({ title, icon, click }) => Docs.Create.MenuIconDocument({
+ icon,
+ title,
+ stayInCollection: true,
+ _width: 60,
+ _height: 70,
+ onClick: ScriptField.MakeScript(click),
+ }));
+
+ doc.menuStackBtns = new PrefetchProxy(Docs.Create.MasonryDocument(menuBtns, {
+ _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, lockedPosition: true, _chromeStatus: "disabled",
+ }));
+ }
+ return doc.menuStackBtns as Doc;
+ }
+
+ static setupMenuPanel(doc: Doc) {
+ doc.menuStack = undefined;
+ if (doc.menuStack === undefined) {
+ const menuBtns = CurrentUserUtils.setupMenuButtons(doc);
+ doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument([menuBtns], {
+ _yMargin: 0, _autoHeight: true, _xMargin: 0,
+ _width: 60, lockedPosition: true, _chromeStatus: "disabled",
+ })) as any as Doc;
+ }
+ return doc.menuStack;
+ }
+
+
// 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 +660,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 +678,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 +691,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;
}
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 +756,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 +775,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 +795,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,6 +806,7 @@ 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;
}
@@ -728,17 +824,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 });
}
}
@@ -855,6 +951,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/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/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 190dbc8c3..f7bd6b358 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 } from "mobx";
import { observer } from "mobx-react";
@@ -44,6 +44,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 }> {
@@ -610,9 +611,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>
@@ -659,7 +660,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 ad61d3f91..5fc1d6fcb 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..fe01c27a3 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -102,6 +102,45 @@
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: pointer;
+ }
+}
+
+.mainiView-propertiesView {
+ display: flex;
+ flex-direction: column;
+ width: 200px;
+ 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 +153,119 @@
}
}
+.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-menuPanel-bottomButton {
+// width: 45px;
+// height: 45px;
+// padding: 10px;
+// pointer-events: all;
+// touch-action: none;
+// //border-radius: inherit;
+// background: black;
+// background-color: black;
+// //border-radius: 100%;
+// transform-origin: top left;
+// margin-bottom: 20px;
+// margin-top: 5px;
+
+// margin-right: 25px;
+
+// .mainView-menuPanel-bottomButton-label {
+// background: black;
+// color: white;
+// margin-left: -10px;
+// border-radius: 8px;
+// width: 65px;
+// position: absolute;
+// text-align: center;
+// font-size: 9.5px;
+// margin-top: 2px;
+// letter-spacing: normal;
+// padding: 3px;
+// //margin-bottom: 23px;
+// }
+
+// .mainView-menuPanel-bottomButton-icon {
+// width: 50px;
+// height: 50px;
+// color: white;
+// }
+
+// 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 +314,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;
+ cursor: grab;
+
+ .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 37d99fb16..9bad0867c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -12,10 +12,10 @@ import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
import { listSpec } from '../../fields/Schema';
-import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, FieldValue, StrCast, NumCast } 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, returnFalse, returnOne, returnZero, returnTrue, Utils, returnEmptyFilter, setupMoveUpEvents } from '../../Utils';
import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { Docs, DocumentOptions } from '../documents/Documents';
@@ -44,7 +44,7 @@ import { RadialMenu } from './nodes/RadialMenu';
import { OverlayView } from './OverlayView';
import PDFMenu from './pdf/PDFMenu';
import { PreviewCursor } from './PreviewCursor';
-import { ScriptField } from '../../fields/ScriptField';
+import { ScriptField, ComputedField } from '../../fields/ScriptField';
import { TimelineMenu } from './animationtimeline/TimelineMenu';
import { SnappingManager } from '../util/SnappingManager';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
@@ -57,6 +57,14 @@ import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane";
import HypothesisAuthenticationManager from '../apis/HypothesisAuthenticationManager';
import CollectionMenu from './collections/CollectionMenu';
+import { Tooltip, AccordionActions } from '@material-ui/core';
+import { PropertiesView } from './collections/collectionFreeForm/PropertiesView';
+import { SelectionManager } from '../util/SelectionManager';
+import { PrefetchProxy } from '../../fields/Proxy';
+import { DragManager } from '../util/DragManager';
+import { discovery_v1, dialogflow_v2beta1 } from 'googleapis';
+import { undo } from 'prosemirror-history';
+import { undoBatch } from '../util/UndoManager';
@observer
export class MainView extends React.Component {
@@ -69,8 +77,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 +86,26 @@ 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";
+ }
+ }
componentDidMount() {
DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's
@@ -148,7 +175,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);
+ 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);
this.initEventListeners();
this.initAuthenticationRouters();
}
@@ -210,7 +238,7 @@ export class MainView extends React.Component {
const freeformOptions: DocumentOptions = {
x: 0,
y: 400,
- _width: this._panelWidth * .7,
+ _width: this._panelWidth * .7 - this._propertiesWidth,
_height: this._panelHeight,
title: "Collection " + workspaceCount,
};
@@ -276,10 +304,13 @@ 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;
@@ -307,7 +338,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 +355,6 @@ export class MainView extends React.Component {
NativeWidth={returnZero}
PanelWidth={this.getPWidth}
PanelHeight={this.getPHeight}
- renderDepth={0}
focus={emptyFunction}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
@@ -331,6 +362,7 @@ export class MainView extends React.Component {
docFilters={returnEmptyFilter}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
+ renderDepth={0}
/>;
}
@computed get dockingContent() {
@@ -347,8 +379,12 @@ export class MainView extends React.Component {
}
_canClick = false;
+
+ @action
onPointerDown = (e: React.PointerEvent) => {
if (this._flyoutTranslate) {
+ this.panelContent = "none";
+ CurrentUserUtils.panelContent = "none";
this._canClick = true;
this._flyoutSizeOnDown = e.clientX;
document.removeEventListener("pointermove", this.onPointerMove);
@@ -389,45 +425,24 @@ export class MainView extends React.Component {
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)) {
+ if (!(this.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" }}>
+ 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,50 +465,256 @@ 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>
+ ContainingCollectionDoc={undefined}
+ relative={true}
+ />
+ </div>
+ {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={() => 80}
+ // 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>;
+ // }
+
+ @computed get menuPanel() {
+ return <div className="mainView-menuPanel">
+ <div className="mainView-menuPanel-button" style={{ backgroundColor: this.panelContent === "workspace" ? "lightgrey" : "" }}>
+ <div className="mainView-menuPanel-button-wrap"
+ style={{ backgroundColor: this.panelContent === "workspace" ? "lightgrey" : "" }}
+ onPointerDown={e => this.selectPanel("workspace")}>
+ <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="desktop"
+ color={this.panelContent === "workspace" ? "black" : "white"} size="lg" />
+ <div className="mainView-menuPanel-button-label"
+ style={{ color: this.panelContent === "workspace" ? "black" : "white" }}> Workspace </div>
+ </div>
+ </div>
+
+ <div className="mainView-menuPanel-button" style={{ backgroundColor: this.panelContent === "catalog" ? "lightgrey" : "" }}>
+ <div className="mainView-menuPanel-button-wrap"
+ style={{ backgroundColor: this.panelContent === "catalog" ? "lightgrey" : "" }}
+ onPointerDown={e => this.selectPanel("catalog")}>
+ <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="file"
+ color={this.panelContent === "catalog" ? "black" : "white"} size="lg" />
+ <div className="mainView-menuPanel-button-label"
+ style={{ color: this.panelContent === "catalog" ? "black" : "white" }}> Catalog </div>
+ </div>
+ </div>
+
+ <div className="mainView-menuPanel-button" style={{ backgroundColor: this.panelContent === "deleted" ? "lightgrey" : "" }}>
+ <div className="mainView-menuPanel-button-wrap"
+ style={{ backgroundColor: this.panelContent === "deleted" ? "lightgrey" : "" }}
+ onPointerDown={e => this.selectPanel("deleted")}>
+ <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="archive"
+ color={this.panelContent === "deleted" ? "black" : "white"} size="lg" />
+ <div className="mainView-menuPanel-button-label"
+ style={{ color: this.panelContent === "deleted" ? "black" : "white" }}> Recently Used </div>
+ </div>
+ </div>
+
+ <div className="mainView-menuPanel-button">
+ <div className="mainView-menuPanel-button-wrap"
+ onPointerDown={e => this.selectPanel("upload")}>
+ <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="upload" color="white" size="lg" />
+ <div className="mainView-menuPanel-button-label"> Import </div>
+ </div>
+ </div>
+
+ <div className="mainView-menuPanel-button">
+ <div className="mainView-menuPanel-button-wrap"
+ //onPointerDown={e => this.selectPanel("sharing")}
+ onClick={() => GroupManager.Instance.open()}>
+ <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="users" color="white" size="lg" />
+ <div className="mainView-menuPanel-button-label"> Sharing </div>
+ </div>
+ </div>
+
+ <div className="mainView-menuPanel-button" style={{ marginBottom: "110px", backgroundColor: this.panelContent === "tools" ? "lightgrey" : "", }}>
+ <div className="mainView-menuPanel-button-wrap"
+ onPointerDown={e => this.selectPanel("tools")}
+ style={{
+ backgroundColor: this.panelContent === "tools" ? "lightgrey" : "",
+ }}>
+ <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="wrench"
+ color={this.panelContent === "tools" ? "black" : "white"} size="lg" />
+ <div className="mainView-menuPanel-button-label"
+ style={{ color: this.panelContent === "tools" ? "black" : "white" }}> Tools </div>
+ </div>
+ </div>
+
+ <div className="mainView-menuPanel-button">
+ <div className="mainView-menuPanel-button-wrap"
+ // style={{backgroundColor: this.panelContent= "help" ? "lightgrey" : "black"}}
+ onPointerDown={e => this.selectPanel("help")} >
+ <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="question-circle" color="white" size="lg" />
+ <div className="mainView-menuPanel-button-label"> Help </div>
+ </div>
+ </div>
+
+ <div className="mainView-menuPanel-button">
+ <div className="mainView-menuPanel-button-wrap"
+ // onPointerDown={e => this.selectPanel("settings")}
+ onClick={() => SettingsManager.Instance.open()}>
+ <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="cog" color="white" size="lg" />
+ <div className="mainView-menuPanel-button-label"> Settings </div>
</div>
</div>
- {this.docButtons}
+ </div>;
+ }
+
+ @action @undoBatch
+ selectPanel = (str: string) => {
+ if (this.panelContent === str && this.flyoutWidth !== 0) {
+ this.closeFlyout();
+ } else {
+ this.panelContent = str;
+ MainView.expandFlyout();
+ if (str === "tools") {
+ CurrentUserUtils.toolsBtn;
+ this.sidebarContent.proto = CurrentUserUtils.toolsStack;
+ } else if (str === "workspace") {
+ this.sidebarContent.proto = CurrentUserUtils.workspaceStack;
+ } else if (str === "catalog") {
+ this.sidebarContent.proto = CurrentUserUtils.catalogStack;
+ } else if (str === "deleted") {
+ this.sidebarContent.proto = CurrentUserUtils.closedStack;
+ } else if (str === "search") {
+ this.sidebarContent.proto = CurrentUserUtils.searchStack;
+ }
+ }
+ return true;
+ }
+
+ @action @undoBatch
+ closeFlyout = () => {
+ this.panelContent = "none";
+ this.flyoutWidth = 0;
+ }
+
+ @action @undoBatch
+ selectMenu = (str: string) => {
+ if (CurrentUserUtils.panelContent === str && this.flyoutWidth !== 0) {
+ CurrentUserUtils.panelContent = "none";
+ this.flyoutWidth = 0;
+ } else {
+ CurrentUserUtils.panelContent = str;
+ MainView.expandFlyout();
+ if (str === "tools") {
+ CurrentUserUtils.toolsBtn;
+ this.sidebarContent.proto = CurrentUserUtils.toolsStack;
+ } else if (str === "workspace") {
+ this.sidebarContent.proto = CurrentUserUtils.workspaceStack;
+ } else if (str === "catalog") {
+ this.sidebarContent.proto = CurrentUserUtils.catalogStack;
+ } else if (str === "deleted") {
+ this.sidebarContent.proto = CurrentUserUtils.closedStack;
+ } else if (str === "search") {
+ this.sidebarContent.proto = CurrentUserUtils.searchStack;
+ }
+ }
+ return true;
+ }
+
+ @action @undoBatch
+ onDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
+ this._propertiesWidth = this._panelWidth - Math.max(Transform.Identity().transformPoint(e.clientX, 0)[0], 0);
+ return false;
+ }), returnFalse, action(() => this._propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 200) : 0), false);
+ }
+
+ @computed get propertiesView() {
+ TraceMobx();
+ return <div className="mainView-propertiesView" style={{
+ width: `200px`,
+ overflow: this.propertiesWidth() < 15 ? "hidden" : undefined
+ }}>
+ <PropertiesView
+ width={200}
+ height={this._panelHeight}
+ renderDepth={1}
+ ScreenToLocalTransform={Transform.Identity}
+ onDown={this.onDown}
+ />
</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) : (
+
+ const rightFlyout = this.selectedDocumentView ? this._propertiesWidth - 1 : this.propertiesWidth() > 10 ? 151.5 : 0;
+ return !this.userDoc || !(this.sidebarContent 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%"
}} >
+ {this.menuPanel}
<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) }}>
+ {this.flyoutWidth !== 0 ? <div className="mainView-libraryHandle"
+ onPointerDown={this.onPointerDown}
+ style={{ backgroundColor: this.defaultBackgroundColors(this.sidebarContent) }}>
<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-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.8vw"
+ 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.onDown}
+ style={{ right: rightFlyout, top: "45%" }}>
+ <div className="mainView-propertiesDragger-icon">
+ <FontAwesomeIcon icon={this.propertiesIcon} color="white" size="sm" /> </div>
+ </div>
+ }
+ {this.propertiesWidth() < 10 ? (null) : this.propertiesView}
</div>
</div>);
}
@@ -505,7 +726,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 +820,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 +838,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/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
new file mode 100644
index 000000000..11265b413
--- /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: 25px;
+ width: 25px;
+ 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: 5px;
+
+ &: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: 8px;
+}
+
+.propertiesButtons-linker {
+ height: 25px;
+ width: 25px;
+ 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..cc15a8195
--- /dev/null
+++ b/src/client/views/PropertiesButtons.tsx
@@ -0,0 +1,547 @@
+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 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;
+
+ 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>
+ </div>;
+ }
+}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index d6cb79e9c..e7c0ad6e6 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';
@@ -307,13 +309,28 @@ 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.Invalid || this.props.type === CollectionViewType.Docking ? (null) : this.templateChrome}
+ {this.props.type === CollectionViewType.Invalid ||
+ this.props.type === CollectionViewType.Docking || this.isText ? (null) : this.viewModes}
+ {this.props.type === CollectionViewType.Invalid ||
+ 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" />
@@ -348,6 +365,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 => {
@@ -524,31 +555,34 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
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 ? (null) :
+ {!this.props.isOverlay || 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}
@@ -558,6 +592,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/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 651357e5d..44414fa2b 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); }
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 42d320308..9a7df8693 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 {
@@ -306,7 +307,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" });
}
@@ -365,7 +366,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() {
@@ -487,6 +488,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() {
@@ -556,6 +558,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);
@@ -585,11 +588,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/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index cde795098..75e693f96 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -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..e0981d797 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -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...");
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
new file mode 100644
index 000000000..cb4b7375b
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
@@ -0,0 +1,309 @@
+.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;
+
+ .propertiesView-title-name {
+ font-size: 18px;
+ font-weight: bold;
+ padding-left: 45px;
+ }
+
+ .propertiesView-title-icon {
+ width: 20px;
+ height: 20px;
+ padding-left: 38px;
+ margin-top: -5px;
+
+ &: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;
+ width: 200px;
+ 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;
+ width: 200px;
+ 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;
+ }
+ }
+
+ .notify-button {
+ padding: 2px;
+ width: 12px;
+ height: 12px;
+ background-color: black;
+ border-radius: 10px;
+ padding-left: 2px;
+ padding-right: 2px;
+ margin-top: 2px;
+ margin-right: 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;
+ background-color: #ececec;
+
+ .propertiesView-sharingTable-item {
+
+ display: flex;
+ padding: 3px;
+
+ border-bottom: 0.5px solid grey;
+
+ .propertiesView-sharingTable-item-name {
+ font-weight: bold;
+ width: 80px;
+ overflow-x: hidden;
+ display: inline-block;
+ }
+
+ .propertiesView-sharingTable-item-permission {
+
+ display: flex;
+
+ .permissions-select {
+ z-index: 1;
+ border: none;
+ background-color: inherit;
+ width: 75px;
+
+ &: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;
+ width: 200px;
+ 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-title-checkbox {
+ float: right;
+ height: 20px;
+ margin-top: -13px;
+ margin-left: 115px;
+
+ .propertiesView-fields-title-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;
+ width: 200px;
+ 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;
+ }
+
+ }
+} \ 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..d5317efcb
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -0,0 +1,417 @@
+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 } 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 } from "@material-ui/core";
+import SharingManager from "../../../util/SharingManager";
+
+
+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;
+
+ @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 (contents === "UNDEFINED") {
+ 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 (contents === "UNDEFINED") {
+ 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 + ":'UNDEFINED'";
+ 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}
+ />
+ </div>;
+ } else {
+ return null;
+ }
+ }
+
+ @computed get permissionsSelect() {
+ return <select className="permissions-select" onChange={emptyFunction}>
+ <option key={"Can Edit"} value={"Can Edit"}>
+ Can Edit
+ </option>
+ <option key={"Can Add"} value={"Can Add"}>
+ Can Add
+ </option>
+ <option key={"Can View"} value={"Can View"}>
+ Can View
+ </option>
+ <option key={"Not Shared"} value={"Not Shared"}>
+ Not Shared
+ </option>
+ </select>;
+ }
+
+ @computed get notifyIcon() {
+ return <Tooltip title={<><div className="dash-tooltip">{"Notify group of permissions change"}</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, notify: boolean, editable: boolean, permission?: string) {
+ return <div className="propertiesView-sharingTable-item">
+ <div className="propertiesView-sharingTable-item-name" style={{ width: notify ? "70px" : "80px" }}> {name} </div>
+ {notify ? this.notifyIcon : null}
+ <div className="propertiesView-sharingTable-item-permission">
+ {editable ? this.permissionsSelect : permission}
+ {permission === "Owner" ? this.expansionIcon : null}
+ </div>
+ </div>;
+ }
+
+ @computed get sharingTable() {
+ return <div className="propertiesView-sharingTable">
+ {this.sharingItem("Me", false, false, "Owner")}
+ {this.sharingItem("Public", false, true)}
+ {this.sharingItem("Group 1", true, true)}
+ {this.sharingItem("Group 2", true, true)}
+ </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} />;
+ }
+
+ setTitle = (value: string) => {
+ if (this.dataDoc) {
+ this.selectedDoc && (this.selectedDoc.title = value);
+ KeyValueBox.SetField(this.dataDoc, "title", value, true);
+ return true;
+ }
+ return false;
+ }
+
+ render() {
+
+ if (!this.selectedDoc) {
+ return <div className="propertiesView" >
+ <div className="propertiesView-title">
+ No Document Selected
+ </div> </div>;
+ }
+
+ const novice = Doc.UserDoc().noviceMode;
+
+ return <div className="propertiesView" >
+ <div className="propertiesView-title">
+ <div className="propertiesView-title-name">Properties </div>
+ <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" style={{ backgroundColor: this.openActions ? "black" : "" }}>
+ Document Actions
+ <div className="propertiesView-settings-title-icon"
+ onPointerDown={() => runInAction(() => { this.openActions = !this.openActions; })}>
+ <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" style={{ backgroundColor: this.openSharing ? "black" : "" }}>
+ Sharing {"&"} Permissions
+ <div className="propertiesView-sharing-title-icon"
+ onPointerDown={() => runInAction(() => { this.openSharing = !this.openSharing; })}>
+ <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>
+ <div className="propertiesView-fields">
+ <div className="propertiesView-fields-title" style={{ backgroundColor: this.openFields ? "black" : "" }}>
+ <div className="propertiesView-fields-title-name">
+ Fields {"&"} Tags
+ <div className="propertiesView-fields-title-icon"
+ onPointerDown={() => runInAction(() => { this.openFields = !this.openFields; })}>
+ <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ </div>
+ {!novice && this.openFields ? <div className="propertiesView-fields-title-checkbox">
+ {this.fieldsCheckbox}
+ <div className="propertiesView-fields-title-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" 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/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 47dc0a773..d72133cb9 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -18,6 +18,7 @@ import { DocHolderBox } from "./DocHolderBox";
import { DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
import { FontIconBox } from "./FontIconBox";
+import { MenuIconBox } from "./MenuIconBox";
import { FieldView, FieldViewProps } from "./FieldView";
import { FormattedTextBox } from "./formattedText/FormattedTextBox";
import { ImageBox } from "./ImageBox";
@@ -190,7 +191,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
blacklistedAttrs={[]}
renderInWrapper={false}
components={{
- FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
+ FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, MenuIconBox, LabelBox, SliderBox, FieldView,
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
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 c47edefd6..f5ae9349a 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
@@ -311,6 +313,7 @@ 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);
@@ -576,6 +579,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) {
@@ -653,6 +668,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)) {
@@ -689,21 +712,21 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const options = cm.findByDescription("Options...");
const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
- optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
+ //optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
!options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
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) {
@@ -715,6 +738,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: "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" });
moreItems.push({ description: "Download document", icon: "download", event: async () => Doc.Zip(this.props.Document) });
if (!Doc.UserDoc().noviceMode) {
moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
@@ -727,16 +753,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" });
@@ -778,8 +806,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}
@@ -868,8 +897,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>;
@@ -995,7 +1028,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/MenuIconBox.scss b/src/client/views/nodes/MenuIconBox.scss
new file mode 100644
index 000000000..1b72f5a8f
--- /dev/null
+++ b/src/client/views/nodes/MenuIconBox.scss
@@ -0,0 +1,49 @@
+.menuButton {
+ //padding: 7px;
+ padding-left: 7px;
+ width: 100%;
+ width: 60px;
+ height: 70px;
+
+ .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: 8px;
+ margin-top: 1px;
+ letter-spacing: normal;
+ padding: 3px;
+ background-color: inherit;
+ }
+
+ .menuButton-icon {
+ width: auto;
+ height: 35px;
+ padding: 5px;
+ }
+
+ svg {
+ width: 95% !important;
+ height: 95%;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/MenuIconBox.tsx b/src/client/views/nodes/MenuIconBox.tsx
new file mode 100644
index 000000000..e1656fcba
--- /dev/null
+++ b/src/client/views/nodes/MenuIconBox.tsx
@@ -0,0 +1,41 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { createSchema, makeInterface } from '../../../fields/Schema';
+import { DocComponent } from '../DocComponent';
+import './MenuIconBox.scss';
+import { FieldView, FieldViewProps } from './FieldView';
+import { StrCast, Cast, NumCast } from '../../../fields/Types';
+import { Utils } from "../../../Utils";
+import { runInAction, observable, reaction, IReactionDisposer } from 'mobx';
+import { Doc } from '../../../fields/Doc';
+import { ScriptField } from '../../../fields/ScriptField';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+const MenuIconSchema = createSchema({
+ icon: "string"
+});
+
+type MenuIconDocument = makeInterface<[typeof MenuIconSchema]>;
+const MenuIconDocument = makeInterface(MenuIconSchema);
+@observer
+export class MenuIconBox extends DocComponent<FieldViewProps, MenuIconDocument>(MenuIconDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MenuIconBox, fieldKey); }
+ _ref: React.RefObject<HTMLButtonElement> = React.createRef();
+
+ render() {
+
+ const menuBTN = <div className="menuButton" style={{ backgroundColor: CurrentUserUtils.panelContent === this.dataDoc.title ? "lightgrey" : "" }}>
+ <div className="menuButton-wrap"
+ style={{ backgroundColor: CurrentUserUtils.panelContent === this.dataDoc.title ? "lightgrey" : "" }}
+ //onPointerDown={this.dataDoc.click}
+ >
+ <FontAwesomeIcon className="menuButton-icon" icon={StrCast(this.dataDoc.icon, "user") as any}
+ color={CurrentUserUtils.panelContent === this.dataDoc.title ? "black" : "white"} size="lg" />
+ <div className="menuButton-label"
+ style={{ color: CurrentUserUtils.panelContent === this.dataDoc.title ? "black" : "white" }}> {this.dataDoc.title} </div>
+ </div>
+ </div>;
+
+ return menuBTN;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 627c6e363..3d389cf87 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;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 47a4911b8..9de2fc18b 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 },
@@ -918,16 +919,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(),
@@ -955,16 +962,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 (