aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/util/CurrentUserUtils.ts108
-rw-r--r--src/client/util/SelectionManager.ts3
-rw-r--r--src/client/views/EditableView.tsx2
-rw-r--r--src/client/views/MainView.scss165
-rw-r--r--src/client/views/MainView.tsx306
-rw-r--r--src/client/views/PropertiesButtons.scss105
-rw-r--r--src/client/views/PropertiesButtons.tsx311
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx1
-rw-r--r--src/client/views/collections/CollectionView.scss1
-rw-r--r--src/client/views/collections/CollectionView.tsx10
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.scss80
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.tsx189
12 files changed, 1208 insertions, 73 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b47cfb33a..c89f9b8fe 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -38,6 +38,16 @@ 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;
+
// sets up the default User Templates - slideView, queryView, descriptionView
static setupUserTemplateButtons(doc: Doc) {
if (doc["template-button-query"] === undefined) {
@@ -484,6 +494,52 @@ export class CurrentUserUtils {
return doc.myItemCreators as Doc;
}
+ static menuBtnDescriptions(): {
+ title: string, icon: string, click: string, backgroundColor?: string,
+ }[] {
+ return [
+ { title: "Workspace", icon: "desktop", click: 'this.selectPanel("workspace")' },
+ { title: "Catalog", icon: "file", click: 'this.selectPanel("catalog")' },
+ { title: "Recently Deleted", icon: "trash-alt", click: 'this.selectPanel("deleted")' },
+ { title: "Import", icon: "upload", click: 'this.selectPanel("upload")' },
+ { title: "Sharing", icon: "users", click: 'GroupManager.Instance.open()' },
+ { title: "Tools", icon: "wrench", click: 'this.selectPanel("tools")' },
+ { title: "Search", icon: "search", click: 'this.selectPanel("search")' },
+ { title: "Help", icon: "question-circle", click: 'this.selectPanel("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, backgroundColor }) => Docs.Create.FontIconDocument({
+ _width: 100, _height: 100,
+ icon,
+ title,
+ onClick: click ? ScriptField.MakeScript(click) : undefined,
+ backgroundColor,
+ }));
+
+ doc.menuStackBtns = new PrefetchProxy(Docs.Create.MasonryDocument(menuBtns, {
+ _xMargin: 0, _autoHeight: true, _width: 100, _columnWidth: 60, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+ }));
+ }
+ return doc.menuStackBtns as Doc;
+ }
+
+ static setupMenuPanel(doc: Doc) {
+ if (doc.menuStack === undefined) {
+ const menuBtns = CurrentUserUtils.setupMenuButtons(doc);
+ doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument([menuBtns], {
+ title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0,
+ _width: 100, ignoreClick: true, 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) {
@@ -593,6 +649,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,
@@ -609,8 +667,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)",
@@ -619,16 +680,19 @@ 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,
@@ -638,19 +702,38 @@ export class CurrentUserUtils {
(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,
+ 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,
}));
}
+
+ 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,
+ 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,
@@ -662,6 +745,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,
+ 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
@@ -673,7 +764,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({
@@ -693,6 +784,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",
@@ -703,6 +795,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;
}
@@ -720,17 +813,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 });
}
}
@@ -847,6 +940,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/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..bd074adea 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -102,6 +102,44 @@
user-select: none;
}
+.mainView-propertiesDragger {
+ background-color: rgb(140, 139, 139);
+ height: 55px;
+ width: 15.5px;
+ position: absolute;
+ top: 55%;
+ border: 1px black solid;
+ border-radius: 0;
+ border-top-left-radius: 10px;
+ border-bottom-left-radius: 10px;
+ border-right: unset;
+ z-index: 2;
+
+ .mainView-propertiesDragger-icon {
+ width: 10px;
+ height: 10px;
+ float: left;
+ margin-left: 3px;
+ padding-top: 19px;
+ }
+
+ &:hover {
+ cursor: pointer;
+ }
+}
+
+.mainiView-propertiesView {
+ display: flex;
+ flex-direction: column;
+ width: 200px;
+ height: 100%;
+ position: absolute;
+ right: 0;
+ top: 0;
+ border-left: solid 1px;
+ z-index: 10;
+}
+
.mainView-flyoutContainer {
display: flex;
flex-direction: column;
@@ -114,6 +152,112 @@
}
}
+.mainView-menuPanel {
+
+ max-width: 95px;
+ background-color: #323232;
+ padding: 20px;
+ padding-right: 50px;
+
+ overflow-y: scroll;
+ overflow-x: hidden;
+
+ .mainView-menuPanel-button {
+ width: 45px;
+ height: 45px;
+ padding: 10px;
+ pointer-events: all;
+ touch-action: none;
+ border-radius: inherit;
+ background: black;
+ border-radius: 100%;
+ transform-origin: top left;
+ margin-bottom: 23px;
+ margin-top: 5px;
+
+ margin-right: 25px;
+
+ .mainView-menuPanel-button-label {
+ background: rgb(168, 168, 168);
+ color: black;
+ margin-left: -13px;
+ border-radius: 8px;
+ width: 65px;
+ position: relative;
+ text-align: center;
+ font-size: 9.5px;
+ margin-top: 4px;
+ letter-spacing: normal;
+ padding: 3px;
+ //margin-bottom: 23px;
+ }
+
+ .mainView-menuPanel-button-icon {
+ width: 35px;
+ height: 35px;
+ padding: 5px;
+ }
+
+ svg {
+ width: 95% !important;
+ height: 95%;
+ }
+ }
+
+ .mainView-menuPanel-bottomButton {
+ width: 50px;
+ height: 50px;
+ padding: 10px;
+ pointer-events: all;
+ touch-action: none;
+ border-radius: inherit;
+ background: #323232;
+ background-color: #323232;
+ border-radius: 100%;
+ transform-origin: top left;
+ margin-bottom: 20px;
+ margin-top: 5px;
+
+ margin-right: 25px;
+
+ .mainView-menuPanel-bottomButton-label {
+ background: #323232;
+ 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%;
@@ -166,22 +310,31 @@
.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 225fb2e8e..bc20059e8 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -8,7 +8,7 @@ import {
faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTimesCircle,
faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight,
faHeading, faRulerCombined, faFillDrip, faLink, faUnlink, faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper,
- faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap
+ faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap, faDesktop, faTrashRestore, faUsers, faWrench, faCog
} from '@fortawesome/free-solid-svg-icons';
import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -21,10 +21,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';
@@ -53,7 +53,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';
@@ -66,6 +66,11 @@ 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';
@observer
export class MainView extends React.Component {
@@ -78,7 +83,7 @@ export class MainView extends React.Component {
@observable private _panelWidth: number = 0;
@observable private _panelHeight: number = 0;
- @observable private _flyoutTranslate: boolean = true;
+ @observable private _flyoutTranslate: boolean = false;
@observable public flyoutWidth: number = 250;
private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); }
@@ -87,8 +92,22 @@ 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;
+ @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
@@ -150,7 +169,7 @@ export class MainView extends React.Component {
faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell,
faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight,
faHeading, faRulerCombined, faFillDrip, faLink, faUnlink, faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper,
- faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap);
+ faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faDesktop, faTrashRestore, faUsers, faWrench, faCog, faMap);
this.initEventListeners();
this.initAuthenticationRouters();
}
@@ -212,7 +231,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,
};
@@ -278,10 +297,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;
@@ -309,7 +331,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}
@@ -325,7 +348,6 @@ export class MainView extends React.Component {
NativeWidth={returnZero}
PanelWidth={this.getPWidth}
PanelHeight={this.getPHeight}
- renderDepth={0}
focus={emptyFunction}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
@@ -333,6 +355,7 @@ export class MainView extends React.Component {
docFilters={returnEmptyFilter}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
+ renderDepth={0}
/>;
}
@computed get dockingContent() {
@@ -395,41 +418,13 @@ export class MainView extends React.Component {
mainContainerXf = () => this.sidebarScreenToLocal().translate(0, -this._buttonBarHeight);
@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" }}>
<DocumentView
- Document={sidebarContent}
+ Document={this.sidebarContent}
DataDoc={undefined}
LibraryPath={emptyPath}
addDocument={undefined}
@@ -453,49 +448,239 @@ export class MainView extends React.Component {
docFilters={returnEmptyFilter}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
- <div className="buttonContainer" >
- <button className="mainView-settings" key="settings" onClick={() => SettingsManager.Instance.open()}>
- <FontAwesomeIcon icon="cog" size="lg" />
- </button>
- </div>
</div>
- {this.docButtons}
+ {this.docButtons}</div>;
+ }
+
+ @computed get menuPanel() {
+
+ return <div className="mainView-menuPanel">
+ <DocumentView
+ Document={Doc.UserDoc().menuStack as Doc}
+ DataDoc={undefined}
+ LibraryPath={emptyPath}
+ addDocument={undefined}
+ addDocTab={this.addDocTabFunc}
+ pinToPres={emptyFunction}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
+ rootSelected={returnTrue}
+ removeDocument={returnFalse}
+ onClick={undefined}
+ ScreenToLocalTransform={this.mainContainerXf}
+ ContentScaling={returnOne}
+ PanelWidth={() => 100}
+ PanelHeight={this.getContentsHeight}
+ renderDepth={0}
+ focus={emptyFunction}
+ backgroundColor={this.defaultBackgroundColors}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={returnEmptyFilter}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined} />
</div>;
}
+ // @computed get menuPanel() {
+ // return <div className="mainView-menuPanel">
+ // <button className="mainView-menuPanel-button"
+ // onPointerDown={e => this.selectPanel("workspace")}
+ // style={{
+ // padding: "5px",
+ // background: "black",
+ // boxShadow: "4px 4px 12px black"
+ // }}>
+ // <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="desktop" color="white" size="lg" />
+ // <div className="mainView-menuPanel-button-label"> Workspace </div>
+ // </button>
+
+ // <button className="mainView-menuPanel-button"
+ // onPointerDown={e => this.selectPanel("catalog")}
+ // style={{
+ // padding: "5px",
+ // background: "black",
+ // boxShadow: "4px 4px 12px black"
+ // }}>
+ // <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="file" color="white" size="lg" />
+ // <div className="mainView-menuPanel-button-label"> Catalog </div>
+ // </button>
+
+ // <button className="mainView-menuPanel-button"
+ // onPointerDown={e => this.selectPanel("deleted")}
+ // style={{
+ // padding: "5px",
+ // background: "black",
+ // boxShadow: "4px 4px 12px black",
+ // marginBottom: "30px"
+ // }}>
+ // <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="trash-alt" color="white" size="lg" />
+ // <div className="mainView-menuPanel-button-label"> Recently Closed </div>
+ // </button>
+
+ // <button className="mainView-menuPanel-button"
+ // onPointerDown={e => this.selectPanel("upload")}
+ // style={{
+ // padding: "5px",
+ // background: "black",
+ // boxShadow: "4px 4px 12px black",
+ // }}>
+ // <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="upload" color="white" size="lg" />
+ // <div className="mainView-menuPanel-button-label"> Import </div>
+ // </button>
+
+ // <button className="mainView-menuPanel-button"
+ // //onPointerDown={e => this.selectPanel("sharing")}
+ // onClick={() => GroupManager.Instance.open()}
+ // style={{
+ // padding: "5px",
+ // background: "black",
+ // boxShadow: "4px 4px 12px black"
+ // }}>
+ // <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="users" color="white" size="lg" />
+ // <div className="mainView-menuPanel-button-label"> Sharing </div>
+ // </button>
+
+ // <button className="mainView-menuPanel-button"
+ // onPointerDown={e => this.selectPanel("tools")}
+ // style={{
+ // padding: "5px",
+ // background: "black",
+ // boxShadow: "4px 4px 12px black",
+ // //marginBottom: "45px"
+ // }}>
+ // <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="wrench" color="white" size="lg" />
+ // <div className="mainView-menuPanel-button-label"> Tools </div>
+ // </button>
+
+ // <button className="mainView-menuPanel-button"
+ // onPointerDown={e => this.selectPanel("search")}
+ // style={{
+ // padding: "5px",
+ // background: "black",
+ // boxShadow: "4px 4px 12px black",
+ // //marginBottom: "45px"
+ // }}>
+ // <FontAwesomeIcon className="mainView-menuPanel-button-icon" icon="search" color="white" size="lg" />
+ // <div className="mainView-menuPanel-button-label"> Search </div>
+ // </button>
+
+ // <button className="mainView-menuPanel-bottomButton"
+ // onPointerDown={e => this.selectPanel("help")}
+ // style={{
+ // padding: "5px",
+ // background: "323232",
+ // }}>
+ // <FontAwesomeIcon className="mainView-menuPanel-bottomButton-icon" icon="question-circle" color="white" size="lg" />
+ // <div className="mainView-menuPanel-bottomButton-label"> Help </div>
+ // </button>
+
+ // <button className="mainView-menuPanel-bottomButton"
+ // // onPointerDown={e => this.selectPanel("settings")}
+ // onClick={() => SettingsManager.Instance.open()}
+ // style={{
+ // padding: "5px",
+ // background: "323232",
+ // }}>
+ // <FontAwesomeIcon className="mainView-menuPanel-bottomButton-icon" icon="cog" color="white" size="lg" />
+ // <div className="mainView-menuPanel-bottomButton-label"> Settings </div>
+ // </button>
+ // </div>;
+ // }
+
+ @action
+ selectPanel = (str: string) => {
+ if (this.panelContent === str && this.flyoutWidth !== 0) {
+ this.panelContent = "none";
+ this.flyoutWidth = 0;
+ } 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
+ 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 this._propertiesWidth === 0 ? (null) :
+ <div className="mainView-propertiesView" style={{
+ width: `${this.propertiesWidth()}px`,
+ overflow: this.propertiesWidth() < 15 ? "hidden" : undefined
+ }}>
+ <PropertiesView
+ width={this._propertiesWidth}
+ height={this._panelHeight}
+ renderDepth={1}
+ ScreenToLocalTransform={Transform.Identity}
+ />
+ </div>;
+ }
+
@computed get mainContent() {
- const sidebar = this.userDoc?.["tabs-panelContainer"];
const n = (RichTextMenu.Instance?.Pinned ? 1 : 0) + (CollectionMenu.Instance?.Pinned ? 1 : 0);
const height = `calc(100% - ${n * Number(ANTIMODEMENU_HEIGHT.replace("px", ""))}px)`;
- return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
+ 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: this._propertiesWidth - 1, 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>);
}
@@ -507,7 +692,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}><FontAwesomeIcon icon="chevron-right" color="black" size="lg" /></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);
@@ -601,8 +786,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 />
@@ -611,6 +804,7 @@ export class MainView extends React.Component {
<GoogleAuthenticationManager />
<HypothesisAuthenticationManager />
<DocumentDecorations />
+ {/* {this.search} */}
<CollectionMenu />
<FormatShapePane />
<RichTextMenu key="rich" />
diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
new file mode 100644
index 000000000..8dd5cec03
--- /dev/null
+++ b/src/client/views/PropertiesButtons.scss
@@ -0,0 +1,105 @@
+@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: 20px;
+ width: 20px;
+ border-radius: 50%;
+ opacity: 0.9;
+ pointer-events: auto;
+ background-color: $dark-color;
+ color: $light-color;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 75%;
+ transition: transform 0.2s;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ &: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;
+}
+
+.propertiesButtons-button {
+ pointer-events: auto;
+ padding-right: 5px;
+ width: 25px;
+}
+
+.propertiesButtons-linker {
+ height: 20px;
+ width: 20px;
+ text-align: center;
+ border-radius: 50%;
+ pointer-events: auto;
+ color: $dark-color;
+ border: $dark-color 1px solid;
+ transition: 0.2s ease all;
+}
+
+.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..b55593b11
--- /dev/null
+++ b/src/client/views/PropertiesButtons.tsx
@@ -0,0 +1,311 @@
+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 } from "../../fields/Types";
+import { emptyFunction, setupMoveUpEvents } 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';
+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?.props.Document; }
+ @computed get dataDoc() { return this.selectedDocumentView?.props.DataDoc; }
+
+ 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;
+ });
+
+ //get view0() { return this.props.views()?.[0]; }
+
+ @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 ? "black" : "white", color: isPinned ? "white" : "black" }}
+ 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;
+ }
+
+ }
+
+ // @computed
+ // get contextButton() {
+ // return <ParentDocSelector Document={this.Document} addDocTab={(doc, where) => {
+ // where === "onRight" ? CollectionDockingView.AddRightSplit(doc) :
+ // this.props.doc.props.addDocTab(doc, "onRight");
+ // return true;
+ // }} />;
+ // }
+
+ @observable _aliasDown = false;
+ onAliasButtonDown = (e: React.PointerEvent): void => {
+ //setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
+
+ }
+
+ // onAliasButtonMoved = () => {
+ // if (this._dragRef.current) {
+ // const dragDocView = this.props.doc!;
+ // if (dragDocView.props){
+ // 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 view0 = this.view0;
+ // const templates: Map<Template, boolean> = new Map();
+ // //const views = this.props.views();
+ // 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 !this.props.doc ? (null) :
+ // <Tooltip title={<><div className="dash-tooltip">Tap: Customize layout. Drag: Create alias</div></>}>
+ // <div className="propertiesButtons-linkFlyout" ref={this._dragRef}>
+ // <Flyout anchorPoint={anchorPoints.LEFT_TOP} onOpen={action(() => this._aliasDown = true)} onClose={action(() => this._aliasDown = false)}
+ // content={!this._aliasDown ? (null) : <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}>
+ // <div className={"propertiesButtons-linkButton-empty"} ref={this._dragRef} onPointerDown={this.onAliasButtonDown} >
+ // {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />}
+ // </div>
+ // </Flyout>
+ // </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;
+ return <div className="propertiesButtons">
+ {/* <div className="propertiesButtons-button">
+ {this.templateButton}
+ </div> */}
+ <div className="propertiesButtons-button">
+ {this.metadataButton}
+ </div>
+ {/* <div className="propertiesButtons-button">
+ {this.contextButton}
+ </div> */}
+ <div className="propertiesButtons-button">
+ {this.pinButton}
+ </div>
+ <div className="propertiesButtons-button" style={{ display: !considerPush ? "none" : "" }}>
+ {this.considerGoogleDocsPush}
+ </div>
+ <div className="propertiesButtons-button" style={{ display: !considerPull ? "none" : "" }}>
+ {this.considerGoogleDocsPull}
+ </div>
+ </div>;
+ }
+}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 651357e5d..c36592c1b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -64,6 +64,7 @@ export interface TreeViewProps {
onCheckedClick?: () => ScriptField;
onChildClick?: () => ScriptField;
ignoreFields?: string[];
+ alwaysOpen?: boolean;
}
@observer
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 e2f78d6f9..9c83cca19 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -49,6 +49,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
import './CollectionView.scss';
import CollectionMenu from './CollectionMenu';
import { SharingPermissions } from '../../util/SharingManager';
+import { DocumentView } from '../nodes/DocumentView';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -81,6 +82,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 {
@@ -366,7 +368,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() {
@@ -488,6 +490,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() {
@@ -557,6 +560,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);
@@ -587,11 +591,11 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
Utils.CorsProxy(Cast(d.data, ImageField)!.url.href) : Cast(d.data, ImageField)!.url.href
:
""))}
- {(!this.props.isSelected() || this.props.Document.hideFilterView) && !this.props.Document.forceActive ? (null) :
+ {/* {this.props.hideFilter || this.props.Document.hideFilterView || !this.props.isSelected() && !this.props.Document.forceActive ? (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/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
new file mode 100644
index 000000000..98eae1ac7
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
@@ -0,0 +1,80 @@
+.propertiesView {
+
+ background-color: rgb(205, 205, 205);
+ height: 100%;
+ z-index: 1;
+ font-family: "Noto Sans";
+ cursor: auto;
+
+ //overflow-y: scroll;
+
+ .propertiesView-title {
+ background-color: rgb(159, 159, 159);
+ text-align: center;
+ font-size: 18px;
+ font-weight: bold;
+ padding-top: 12px;
+ padding-bottom: 12px;
+ }
+
+ .propertiesView-name {
+ border-bottom: 1px solid black;
+ padding: 8.5px;
+ font-size: 12.5px;
+ }
+
+ .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-bottom: 7px;
+ }
+
+ .propertiesView-settings-content {
+ margin-left: 5px;
+ padding-bottom: 10px;
+ }
+
+ }
+
+ .propertiesView-fields {
+ border-bottom: 1px solid black;
+ padding: 8.5px;
+
+ .propertiesView-fields-title {
+ font-size: 12.5px;
+ font-weight: bold;
+ padding-bottom: 7px;
+ }
+
+ .propertiesView-fields-content {
+ font-size: 10px;
+ margin-left: 5px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .propertiesView-layout {
+ padding: 8.5px;
+
+ .propertiesView-layout-title {
+ font-weight: bold;
+ font-size: 12.5px;
+ padding-bottom: 7px;
+ }
+
+ .propertiesView-layout-content {
+ margin-left: 5px;
+ overflow: hidden;
+ }
+
+ }
+} \ 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..1954a11bf
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -0,0 +1,189 @@
+import React = require("react");
+import { observer } from "mobx-react";
+import "./PropertiesView.scss";
+import { observable, action, computed } 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, StrCast, NumCast } 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";
+
+
+interface PropertiesViewProps {
+ width: number;
+ height: number;
+ renderDepth: number;
+ ScreenToLocalTransform: () => Transform;
+}
+
+@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; }
+
+ @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) {
+ 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()) {
+ const contents = doc[key];
+ 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>);
+ }
+ return rows;
+ }
+ }
+
+ @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;
+ }
+ }
+
+ render() {
+
+ if (!this.selectedDoc) {
+ return <div className="propertiesView" >
+ <div className="propertiesView-title">
+ No Document Selected
+ </div> </div>;
+ }
+
+ return <div className="propertiesView" >
+ <div className="propertiesView-title">
+ Properties
+ </div>
+ <div className="propertiesView-name">
+ {this.selectedDoc.title}
+ </div>
+ <div className="propertiesView-settings">
+ <div className="propertiesView-settings-title"> Settings</div>
+ <div className="propertiesView-settings-content">
+ <PropertiesButtons />
+ </div>
+ </div>
+ <div className="propertiesView-fields">
+ <div className="propertiesView-fields-title"> Fields</div>
+ <div className="propertiesView-fields-content"> {this.expandedField} </div>
+ </div>
+ <div className="propertiesView-layout">
+ <div className="propertiesView-layout-title" >Layout</div>
+ <div className="propertiesView-layout-content">{this.layoutPreview}</div>
+ </div>
+ </div>;
+ }
+} \ No newline at end of file