aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/DocumentDecorations.scss7
-rw-r--r--src/client/views/DocumentDecorations.tsx34
-rw-r--r--src/client/views/Main.scss1
-rw-r--r--src/client/views/Main.tsx16
-rw-r--r--src/client/views/MainOverlayTextBox.tsx1
-rw-r--r--src/client/views/PresentationView.tsx66
-rw-r--r--src/client/views/PreviewCursor.tsx2
-rw-r--r--src/client/views/TemplateMenu.tsx24
-rw-r--r--src/client/views/Templates.tsx40
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx14
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx129
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx11
-rw-r--r--src/client/views/collections/CollectionTreeView.scss54
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx88
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx94
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx29
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx105
-rw-r--r--src/client/views/globalCssVariables.scss2
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx60
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx29
-rw-r--r--src/client/views/nodes/DocumentView.tsx38
-rw-r--r--src/client/views/nodes/FieldView.tsx3
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss3
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx28
-rw-r--r--src/client/views/nodes/IconBox.tsx8
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx9
-rw-r--r--src/client/views/nodes/LinkEditor.tsx5
30 files changed, 574 insertions, 342 deletions
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 158b02b5a..6a2e33836 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -210,14 +210,15 @@ $linkGap : 3px;
position: absolute;
top: 0;
left: 30px;
- width: 150px;
- line-height: 25px;
- max-height: 175px;
+ width: max-content;
font-family: $sans-serif;
font-size: 12px;
background-color: $light-color-secondary;
padding: 2px 12px;
list-style: none;
+ .templateToggle {
+ text-align: left;
+ }
input {
margin-right: 10px;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 16e5b6b48..e3eb034fa 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -29,6 +29,7 @@ import { CollectionFreeFormView } from "./collections/collectionFreeForm/Collect
import { CollectionView } from "./collections/CollectionView";
import { createCipher } from "crypto";
import { FieldView } from "./nodes/FieldView";
+import { DocumentManager } from "../util/DocumentManager";
library.add(faLink);
@@ -78,11 +79,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
if (SelectionManager.SelectedDocuments().length > 0) {
let field = SelectionManager.SelectedDocuments()[0].props.Document[this._fieldKey];
if (typeof field === "number") {
- SelectionManager.SelectedDocuments().forEach(d =>
- d.props.Document[this._fieldKey] = +this._title);
+ SelectionManager.SelectedDocuments().forEach(d => {
+ let doc = d.props.Document.proto ? d.props.Document.proto : d.props.Document;
+ doc[this._fieldKey] = +this._title;
+ });
} else {
- SelectionManager.SelectedDocuments().forEach(d =>
- d.props.Document[this._fieldKey] = this._title);
+ SelectionManager.SelectedDocuments().forEach(d => {
+ let doc = d.props.Document.proto ? d.props.Document.proto : d.props.Document;
+ doc[this._fieldKey] = this._title;
+ });
}
}
}
@@ -273,13 +278,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
public getIconDoc = async (docView: DocumentView): Promise<Doc | undefined> => {
let doc = docView.props.Document;
let iconDoc: Doc | undefined = await Cast(doc.minimizedDoc, Doc);
- if (!iconDoc) {
+
+ if (!iconDoc || !DocumentManager.Instance.getDocumentView(iconDoc)) {
const layout = StrCast(doc.backgroundLayout, StrCast(doc.layout, FieldView.LayoutString(DocumentView)));
iconDoc = this.createIcon([docView], layout);
}
- if (SelectionManager.SelectedDocuments()[0].props.addDocument !== undefined) {
- SelectionManager.SelectedDocuments()[0].props.addDocument!(iconDoc);
- }
return iconDoc;
}
moveIconDoc(iconDoc: Doc) {
@@ -503,9 +506,18 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
let templates: Map<Template, boolean> = new Map();
- let doc = SelectionManager.SelectedDocuments()[0];
Array.from(Object.values(Templates.TemplateList)).map(template => {
- let docTemps = doc.templates;
+ let docTemps = SelectionManager.SelectedDocuments().reduce((res: string[], doc: DocumentView, i) => {
+ let temps = doc.props.Document.templates;
+ if (temps instanceof List) {
+ temps.map(temp => {
+ if (temp !== Templates.Bullet.Layout || i === 0) {
+ res.push(temp);
+ }
+ })
+ }
+ return res
+ }, [] as string[]);
let checked = false;
docTemps.forEach(temp => {
if (template.Layout === temp) {
@@ -556,7 +568,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<FontAwesomeIcon className="fa-icon-link" icon="link" size="sm" />
</div>
</div>
- <TemplateMenu doc={doc} templates={templates} />
+ <TemplateMenu docs={SelectionManager.SelectedDocuments()} templates={templates} />
</div>
</div >
</div>
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index cbf920793..5c5c252e9 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -182,6 +182,7 @@ button:hover {
top: 0;
left: 0;
overflow: scroll;
+ z-index: 1;
}
#mainContent-div {
width: 100%;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index c3b48d20f..617580332 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -51,9 +51,6 @@ export class Main extends React.Component {
}
private set mainContainer(doc: Opt<Doc>) {
if (doc) {
- if (!("presentationView" in doc)) {
- doc.presentationView = new Doc();
- }
CurrentUserUtils.UserDocument.activeWorkspace = doc;
}
}
@@ -177,23 +174,12 @@ export class Main extends React.Component {
}
}, 100);
}
-
- @computed
- get presentationView() {
- if (this.mainContainer) {
- let presentation = FieldValue(Cast(this.mainContainer.presentationView, Doc));
- return presentation ? <PresentationView Document={presentation} key="presentation" /> : (null);
- }
- return (null);
- }
-
@computed
get mainContent() {
let pwidthFunc = () => this.pwidth;
let pheightFunc = () => this.pheight;
let noScaling = () => 1;
let mainCont = this.mainContainer;
- let pcontent = this.presentationView;
return <Measure onResize={action((r: any) => { this.pwidth = r.entry.width; this.pheight = r.entry.height; })}>
{({ measureRef }) =>
<div ref={measureRef} id="mainContent-div">
@@ -213,7 +199,7 @@ export class Main extends React.Component {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
ContainingCollectionView={undefined} />}
- {pcontent}
+ <PresentationView key="presentation" />
</div>
}
</Measure>;
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index d32e3f21b..3b75c248a 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -43,6 +43,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
this._textXf = tx ? tx : () => Transform.Identity();
this._textTargetDiv = div;
if (div) {
+ if (div.parentElement && div.parentElement instanceof HTMLDivElement && div.parentElement.id === "screenSpace") this._textXf = () => Transform.Identity();
this._textColor = div.style.color;
div.style.color = "transparent";
this.TextScroll = div.scrollTop;
diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx
index d8bdbacca..4853eb151 100644
--- a/src/client/views/PresentationView.tsx
+++ b/src/client/views/PresentationView.tsx
@@ -1,18 +1,19 @@
import { observer } from "mobx-react";
-import React = require("react");
-import { observable, action } from "mobx";
-import "./PresentationView.scss";
+import React = require("react")
+import { observable, action, runInAction, reaction } from "mobx";
+import "./PresentationView.scss"
import "./Main.tsx";
import { DocumentManager } from "../util/DocumentManager";
import { Utils } from "../../Utils";
import { Doc } from "../../new_fields/Doc";
import { listSpec } from "../../new_fields/Schema";
-import { Cast, NumCast, FieldValue } from "../../new_fields/Types";
+import { Cast, NumCast, FieldValue, PromiseValue } from "../../new_fields/Types";
import { Id } from "../../new_fields/RefField";
import { List } from "../../new_fields/List";
+import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
export interface PresViewProps {
- Document: Doc;
+ //Document: Doc;
}
@@ -22,6 +23,11 @@ export interface PresViewProps {
*/
class PresentationViewItem extends React.Component<PresViewProps> {
+ @observable Document: Doc;
+ constructor(props: PresViewProps) {
+ super(props);
+ this.Document = FieldValue(Cast(FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc))!.presentationView, Doc))!;
+ }
//look at CollectionFreeformView.focusDocument(d)
@action
openDoc = (doc: Doc) => {
@@ -36,7 +42,7 @@ class PresentationViewItem extends React.Component<PresViewProps> {
**/
@action
public RemoveDoc(doc: Doc) {
- const value = Cast(this.props.Document.data, listSpec(Doc), []);
+ const value = Cast(this.Document.data, listSpec(Doc), []);
let index = -1;
for (let i = 0; i < value.length; i++) {
if (value[i][Id] === doc[Id]) {
@@ -57,10 +63,10 @@ class PresentationViewItem extends React.Component<PresViewProps> {
let title = document.title;
//to get currently selected presentation doc
- let selected = NumCast(this.props.Document.selectedDoc, 0);
+ let selected = NumCast(this.Document.selectedDoc, 0);
// finally, if it's a normal document, then render it as such.
- const children = Cast(this.props.Document.data, listSpec(Doc));
+ const children = Cast(this.Document.data, listSpec(Doc));
const styles: any = {};
if (children && children[selected] === document) {
//this doc is selected
@@ -76,7 +82,7 @@ class PresentationViewItem extends React.Component<PresViewProps> {
}
render() {
- const children = Cast(this.props.Document.data, listSpec(Doc), []);
+ const children = Cast(this.Document.data, listSpec(Doc), []);
return (
<div>
@@ -94,13 +100,13 @@ export class PresentationView extends React.Component<PresViewProps> {
//observable means render is re-called every time variable is changed
@observable
collapsed: boolean = false;
- closePresentation = action(() => this.props.Document.width = 0);
+ closePresentation = action(() => this.Document!.width = 0);
next = () => {
- const current = NumCast(this.props.Document.selectedDoc);
- const allDocs = FieldValue(Cast(this.props.Document.data, listSpec(Doc)));
+ const current = NumCast(this.Document!.selectedDoc);
+ const allDocs = FieldValue(Cast(this.Document!.data, listSpec(Doc)));
if (allDocs && current < allDocs.length + 1) {
//can move forwards
- this.props.Document.selectedDoc = current + 1;
+ this.Document!.selectedDoc = current + 1;
const doc = allDocs[current + 1];
let docView = DocumentManager.Instance.getDocumentView(doc);
if (docView) {
@@ -110,11 +116,11 @@ export class PresentationView extends React.Component<PresViewProps> {
}
back = () => {
- const current = NumCast(this.props.Document.selectedDoc);
- const allDocs = FieldValue(Cast(this.props.Document.data, listSpec(Doc)));
+ const current = NumCast(this.Document!.selectedDoc);
+ const allDocs = FieldValue(Cast(this.Document!.data, listSpec(Doc)));
if (allDocs && current - 1 >= 0) {
//can move forwards
- this.props.Document.selectedDoc = current - 1;
+ this.Document!.selectedDoc = current - 1;
const doc = allDocs[current - 1];
let docView = DocumentManager.Instance.getDocumentView(doc);
if (docView) {
@@ -125,9 +131,21 @@ export class PresentationView extends React.Component<PresViewProps> {
private ref = React.createRef<HTMLDivElement>();
+ @observable Document?: Doc;
//initilize class variables
constructor(props: PresViewProps) {
super(props);
+ let self = this;
+ reaction(() =>
+ CurrentUserUtils.UserDocument.activeWorkspace,
+ (activeW) => {
+ if (activeW && activeW instanceof Doc) {
+ PromiseValue(Cast(activeW.presentationView, Doc)).
+ then(pv => runInAction(() =>
+ self.Document = pv ? pv : (activeW.presentationView = new Doc())))
+ }
+ },
+ { fireImmediately: true });
PresentationView.Instance = this;
}
@@ -137,19 +155,21 @@ export class PresentationView extends React.Component<PresViewProps> {
@action
public PinDoc(doc: Doc) {
//add this new doc to props.Document
- const data = Cast(this.props.Document.data, listSpec(Doc));
+ const data = Cast(this.Document!.data, listSpec(Doc));
if (data) {
data.push(doc);
} else {
- this.props.Document.data = new List([doc]);
+ this.Document!.data = new List([doc]);
}
- this.props.Document.width = 300;
+ this.Document!.width = 300;
}
render() {
- let titleStr = this.props.Document.Title;
- let width = NumCast(this.props.Document.width);
+ if (!this.Document)
+ return (null);
+ let titleStr = this.Document.Title;
+ let width = NumCast(this.Document.width);
//TODO: next and back should be icons
return (
@@ -163,9 +183,7 @@ export class PresentationView extends React.Component<PresViewProps> {
</div>
<ul>
- <PresentationViewItem
- Document={this.props.Document}
- />
+ <PresentationViewItem />
</ul>
</div>
);
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 9520f489c..78024a58c 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -28,7 +28,7 @@ export class PreviewCursor extends React.Component<{}> {
//if not these keys, make a textbox if preview cursor is active!
if (e.key.startsWith("F") && !e.key.endsWith("F")) {
} else if (e.key !== "Escape" && e.key !== "Alt" && e.key !== "Shift" && e.key !== "Meta" && e.key !== "Control" && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
- if ((!e.ctrlKey && !e.metaKey) || e.key === "v") {
+ if ((!e.ctrlKey && !e.metaKey) || e.key === "v" || e.key === "q") {
PreviewCursor.Visible && PreviewCursor._onKeyPress && PreviewCursor._onKeyPress(e);
PreviewCursor.Visible = false;
}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index f29d9c8a1..d74982ef8 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -4,6 +4,8 @@ import { observer } from "mobx-react";
import './DocumentDecorations.scss';
import { Template } from "./Templates";
import { DocumentView } from "./nodes/DocumentView";
+import { List } from "../../new_fields/List";
+import { Doc } from "../../new_fields/Doc";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -13,7 +15,7 @@ class TemplateToggle extends React.Component<{ template: Template, checked: bool
render() {
if (this.props.template) {
return (
- <li>
+ <li className="templateToggle">
<input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event, this.props.template)} />
{this.props.template.Name}
</li>
@@ -25,26 +27,32 @@ class TemplateToggle extends React.Component<{ template: Template, checked: bool
}
export interface TemplateMenuProps {
- doc: DocumentView;
+ docs: DocumentView[];
templates: Map<Template, boolean>;
}
@observer
export class TemplateMenu extends React.Component<TemplateMenuProps> {
-
@observable private _hidden: boolean = true;
-
@action
toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
if (event.target.checked) {
- this.props.doc.addTemplate(template);
+ if (template.Name == "Bullet") {
+ this.props.docs[0].addTemplate(template);
+ this.props.docs[0].props.Document.maximizedDocs = new List<Doc>(this.props.docs.filter((v, i) => i !== 0).map(v => v.props.Document));
+ } else {
+ this.props.docs.map(d => d.addTemplate(template));
+ }
this.props.templates.set(template, true);
- this.props.templates.forEach((checked, template) => console.log("Set Checked + " + checked + " " + this.props.templates.get(template)));
} else {
- this.props.doc.removeTemplate(template);
+ if (template.Name == "Bullet") {
+ this.props.docs[0].removeTemplate(template);
+ this.props.docs[0].props.Document.maximizedDocs = undefined;
+ } else {
+ this.props.docs.map(d => d.removeTemplate(template));
+ }
this.props.templates.set(template, false);
- this.props.templates.forEach((checked, template) => console.log("Unset Checked + " + checked + " " + this.props.templates.get(template)));
}
}
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 5858ee014..02f9aa510 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -39,7 +39,7 @@ export namespace Templates {
// export const BasicLayout = new Template("Basic layout", "{layout}");
export const OuterCaption = new Template("Outer caption", TemplatePosition.OutterBottom,
- `<div><div style="margin:auto; height:calc(100%); width:100%;">{layout}</div><div style="height:(100% + 50px); width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={"caption"} /></div></div>`
+ `<div id="screenSpace" style="margin-top: 100%; font-size:14px; background:yellow; width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={"caption"} /></div>`
);
export const InnerCaption = new Template("Inner caption", TemplatePosition.InnerBottom,
@@ -50,27 +50,29 @@ export namespace Templates {
`<div><div style="margin:auto; height:100%; width:100%;">{layout}</div><div style="height:100%; width:300px; position:absolute; top: 0; right: -300px;"><FormattedTextBox {...props} fieldKey={"caption"}/></div> </div>`
);
+ export const TitleOverlay = new Template("TitleOverlay", TemplatePosition.InnerTop,
+ `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div>
+ <div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; ">
+ <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">{props.Document.title}</span>
+ </div></div>`
+ );
export const Title = new Template("Title", TemplatePosition.InnerTop,
- `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.title}</div></div>`
+ `<div><div style="height:calc(100% - 25px); margin-top: 25px; width:100%;position:absolute;">{layout}</div>
+ <div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; ">
+ <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">{props.Document.title}</span>
+ </div></div>`
);
- export const Summary = new Template("Title", TemplatePosition.InnerTop,
- `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.doc1.title}</div></div>`
+
+ export const Bullet = new Template("Bullet", TemplatePosition.InnerTop,
+ `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div>
+ <div id="isBullet" style="height:15px; width:15px; margin-left:-16px; pointer-events:all; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white;">
+ <img id="isBullet" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAZlBMVEX///8AAABmZmb7+/tYWFhgYGBFRUVSUlL4+Pg/Pz9jY2N5eXmcnJyioqKBgYFzc3NtbW1LS0s3NzfW1taWlpaOjo6IiIgvLy9WVlampqZcXFw5OTlvb28mJiYxMTHe3t7l5eUjIyMY8kIZAAAD2UlEQVR4nO2d61biMBRGW1FBEVHxfp15/5ecOVa5lHxtArmck/Xtn1BotjtNoXQtm4YQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEFIrX6UHEA1gsmrneceRjHm7cj28attKFOf/TRyKIliH4vzbZE+xE2zbZYkxRWX5Y9JT/BW0X3G+NtlR3Ahar7jcMtlS3Ba0XXG+Y7JW3BW0XHHZM/lR7AvaVewL/ijuC1pV3Bf8VnQJ2lR0CYriq/Nxg4puwfa1aZ7dz9yUHnEgN26NZ3luWkPFd7fEtHsWVDwpO+YgTgYKCuYn6tAU7TBecaygcGpZEQie7m5luKJPQQFUvCwx5iAuvQoK4KShvSIoOHVtCz7dnOUecxBn7kG/urc2eCz6T9EOcxXDCgpAUetyAwoOCBqrGF5QMKR4mCA8L+pTBIJwkRl95eifJjPHTDYTFQ8vePyrs3BsBfXLzfFHkvKKMY4j1ctNnCmmuGKslfCQT0RZiPdFVmnFmOcy36sDWYn7DU9hxdifRkKuEGQh/pWW0K/QiUlxtUxVxTTXyhQtN6kuI6mpmO5qpxJFIBjl1yMVimmvV4PfrnIq3iYsKICTRj7F9L84gIq38fYwCCj4HnMfRY/FPL8ZFayYo6BQbLlJeZrYpVDFXAUFcMtKWkUgmOhmnwKKOQsK4NaxdIp5CwqZj8X8gv27jNecJ9nZuXtnie/SzjhRQcHkt6Fnq1imoAAUY1csVVDIUrFcQSGDIhC8jriLQZIrli0oXKdVLF1QSFqxfEEBVLyI8NYXCgoKySaqhinakajimxrBRBX1FBQSVNRyDP4SXVGbYHRFfYJN8xhTESwyj5HHHEjEihoLCqDiXfAb3aksKESqCAoqEIxUUW9BAS03E+93mOhcZDYcXVF3QeHBPcI3v4qo4EPiUQcBKr75vHaiv6AAKt6NV0SCqgoKqOKYovpFZgOo+DmsOHkyUlA4ZKKamaIdQPEJK5oqKKCKM7D9zFZBIayiuYICWm5cFWef7o3vs486CP8VdQIEVRcU7sFE7VecgSmqvKDgVxEJqi8ogIof2xVnH2YLCuMT1fAU7RirOPtrXHCsovmCwlDFCgoKWNH4IrMBTdQ/NUzRjiu3CeCq9HAPAVSspaDgX9FkQcG3ollB34qGBf0UTQv6KBoXHFc0LzimWIFg0ywGBBelBxcHXLGKggKqWElBwV2xIkF3xaoEXYqVCe4rVifYV3wpPZwULOouKLzUXVBY1F1QeKm7oLCoXVAqVi7YNM7/F0YIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCG+/ANh4i1CHdc63QAAAABJRU5ErkJggg=="
+ width="15px" height="15px"/>
+ </div>
+ </div>`
);
- // export const Summary = new Template("Title", TemplatePosition.InnerTop,
- // `<div style="height:100%; width:100%;position:absolute; margin:0; padding: 0">
- // <div style="height:60%; width:100%;position:absolute;background:yellow; padding:0; margin:0">
- // {layout}
- // </div>
- // <div style="bottom:0px; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
- // <FieldView {...props} fieldKey={"doc1"} />
- // </div>
- // <div style="bottom:0; left: 50%; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
- // <FieldView {...props} fieldKey={"doc2"} />
- // </div>
- // </div>`
- // );
-
- export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
+
+ export const TemplateList: Template[] = [Title, TitleOverlay, OuterCaption, InnerCaption, SideCaption, Bullet];
export function sortTemplates(a: Template, b: Template) {
if (a.Position < b.Position) { return -1; }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 76adfcdcd..14b92af48 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -95,17 +95,20 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
if (!this.createsCycle(doc, props.Document)) {
//TODO This won't create the field if it doesn't already exist
const value = Cast(props.Document[props.fieldKey], listSpec(Doc));
+ let alreadyAdded = true;
if (value !== undefined) {
- if (allowDuplicates || !value.some(v => v[Id] === doc[Id])) {
+ if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) {
+ alreadyAdded = false;
value.push(doc);
}
} else {
- this.props.Document[this.props.fieldKey] = new List([doc]);
+ alreadyAdded = false;
+ Doc.SetOnPrototype(this.props.Document, this.props.fieldKey, new List([doc]));
}
// set the ZoomBasis only if hasn't already been set -- bcz: maybe set/resetting the ZoomBasis should be a parameter to addDocument?
- if (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid) {
+ if (!alreadyAdded && (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid)) {
let zoom = NumCast(this.props.Document.scale, 1);
- doc.zoomBasis = zoom;
+ Doc.SetOnPrototype(doc, "zoomBasis", zoom);
}
}
return true;
@@ -118,7 +121,8 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
const value = Cast(props.Document[props.fieldKey], listSpec(Doc), []);
let index = -1;
for (let i = 0; i < value.length; i++) {
- if (value[i][Id] === doc[Id]) {
+ let v = value[i];
+ if (v instanceof Doc && v[Id] === doc[Id]) {
index = i;
break;
}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 725f0ab51..159815ea5 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,25 +1,23 @@
-import * as GoldenLayout from "golden-layout";
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, observable, reaction, trace, runInAction } from "mobx";
+import { action, observable, reaction } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
import Measure from "react-measure";
-import { Utils, returnTrue, emptyFunction, returnOne, returnZero } from "../../../Utils";
+import * as GoldenLayout from "../../../client/goldenLayout";
+import { Doc, Field, Opt } from "../../../new_fields/Doc";
+import { FieldId, Id } from "../../../new_fields/RefField";
+import { listSpec } from "../../../new_fields/Schema";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { emptyFunction, returnTrue, Utils } from "../../../Utils";
+import { DocServer } from "../../DocServer";
+import { DragLinksAsDocuments, DragManager } from "../../util/DragManager";
+import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from "../../util/UndoManager";
import { DocumentView } from "../nodes/DocumentView";
import "./CollectionDockingView.scss";
-import React = require("react");
import { SubCollectionViewProps } from "./CollectionSubView";
-import { DragManager, DragLinksAsDocuments } from "../../util/DragManager";
-import { Transform } from '../../util/Transform';
-import { Doc, Opt, Field } from "../../../new_fields/Doc";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { List } from "../../../new_fields/List";
-import { DocServer } from "../../DocServer";
-import { listSpec } from "../../../new_fields/Schema";
-import { Id, FieldId } from "../../../new_fields/RefField";
-import { faSignInAlt } from "@fortawesome/free-solid-svg-icons";
+import React = require("react");
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
@@ -72,6 +70,39 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this.stateChanged();
}
+ @undoBatch
+ @action
+ public CloseRightSplit(document: Doc) {
+ if (this._goldenLayout.root.contentItems[0].isRow) {
+ this._goldenLayout.root.contentItems[0].contentItems.map((child: any, i: number) => {
+ if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
+ child.contentItems[0].config.props.documentId == document[Id]) {
+ child.contentItems[0].remove();
+ this.layoutChanged(document);
+ this.stateChanged();
+ } else
+ child.contentItems.map((tab: any, j: number) => {
+ if (tab.config.component === "DocumentFrameRenderer" && tab.config.props.documentId === document[Id]) {
+ child.contentItems[j].remove();
+ child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0);
+ let docs = Cast(this.props.Document.data, listSpec(Doc));
+ docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1);
+ this.stateChanged();
+ }
+ });
+ })
+ }
+ }
+
+ @action
+ layoutChanged(removed?: Doc) {
+ this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
+ this._goldenLayout.emit('sbcreteChanged');
+ this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
+ if (removed) CollectionDockingView.Instance._removedDocs.push(removed);
+ this.stateChanged();
+ }
+
//
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
@@ -103,14 +134,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
newContentItem.config.width = 50;
}
if (minimize) {
- newContentItem.config.width = 10;
- newContentItem.config.height = 10;
+ // bcz: this makes the drag image show up better, but it also messes with fixed layout sizes
+ // newContentItem.config.width = 10;
+ // newContentItem.config.height = 10;
}
newContentItem.callDownwards('_$init');
- this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
- this._goldenLayout.emit('stateChanged');
- this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
- this.stateChanged();
+ this.layoutChanged();
return newContentItem;
}
@@ -231,6 +260,11 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@undoBatch
stateChanged = () => {
+ let docs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc));
+ CollectionDockingView.Instance._removedDocs.map(theDoc =>
+ docs && docs.indexOf(theDoc) !== -1 &&
+ docs.splice(docs.indexOf(theDoc), 1));
+ CollectionDockingView.Instance._removedDocs.length = 0;
var json = JSON.stringify(this._goldenLayout.toConfig());
this.props.Document.dockingConfig = json;
if (this.undohack && !this.hack) {
@@ -251,43 +285,42 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
return template.content.firstChild;
}
- tabCreated = (tab: any) => {
+ tabCreated = async (tab: any) => {
if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
- DocServer.GetRefField(tab.contentItem.config.props.documentId).then(async f => {
- if (f instanceof Doc) {
- const title = Cast(f.title, "string");
- if (title !== undefined) {
- tab.titleElement[0].textContent = title;
- }
- const lf = await Cast(f.linkedFromDocs, listSpec(Doc));
- const lt = await Cast(f.linkedToDocs, listSpec(Doc));
- let count = (lf ? lf.length : 0) + (lt ? lt.length : 0);
- let counter: any = this.htmlToElement(`<div class="messageCounter">${count}</div>`);
+ if (tab.contentItem.config.fixed) {
+ tab.contentItem.parent.config.fixed = true;
+ }
+ DocServer.GetRefField(tab.contentItem.config.props.documentId).then(async doc => {
+ if (doc instanceof Doc) {
+ let counter: any = this.htmlToElement(`<div class="messageCounter">0</div>`);
tab.element.append(counter);
counter.DashDocId = tab.contentItem.config.props.documentId;
- tab.reactionDisposer = reaction((): [List<Field> | null | undefined, List<Field> | null | undefined] => [lf, lt],
- ([linkedFrom, linkedTo]) => {
- let count = (linkedFrom ? linkedFrom.length : 0) + (linkedTo ? linkedTo.length : 0);
+ tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title],
+ () => {
+ const lf = Cast(doc.linkedFromDocs, listSpec(Doc), []);
+ const lt = Cast(doc.linkedToDocs, listSpec(Doc), []);
+ let count = (lf ? lf.length : 0) + (lt ? lt.length : 0);
counter.innerHTML = count;
- });
+ tab.titleElement[0].textContent = doc.title;
+ }, { fireImmediately: true });
tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
}
});
}
tab.closeElement.off('click') //unbind the current click handler
- .click(function () {
+ .click(async function () {
if (tab.reactionDisposer) {
tab.reactionDisposer();
}
- DocServer.GetRefField(tab.contentItem.config.props.documentId).then(async f => runInAction(() => {
- if (f instanceof Doc) {
- let docs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc));
- docs && docs.indexOf(f) !== -1 && docs.splice(docs.indexOf(f), 1);
- }
- }));
+ let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId);
+ if (doc instanceof Doc) {
+ let theDoc = doc;
+ CollectionDockingView.Instance._removedDocs.push(theDoc);
+ }
tab.contentItem.remove();
});
}
+ _removedDocs: Doc[] = [];
stackCreated = (stack: any) => {
//stack.header.controlsContainer.find('.lm_popout').hide();
@@ -296,13 +329,21 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
.click(action(function () {
//if (confirm('really close this?')) {
stack.remove();
+ stack.contentItems.map(async (contentItem: any) => {
+ let doc = await DocServer.GetRefField(contentItem.config.props.documentId);
+ if (doc instanceof Doc) {
+ let theDoc = doc;
+ CollectionDockingView.Instance._removedDocs.push(theDoc);
+ }
+ });
//}
}));
stack.header.controlsContainer.find('.lm_popout') //get the close icon
.off('click') //unbind the current click handler
.click(action(function () {
- var url = DocServer.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
- let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400");
+ stack.config.fixed = !stack.config.fixed;
+ // var url = DocServer.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
+ // let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400");
}));
}
@@ -312,6 +353,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} />
);
}
+
}
interface DockedFrameProps {
@@ -370,6 +412,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
focus={emptyFunction}
+ bringToFront={emptyFunction}
ContainingCollectionView={undefined} />
</div >);
}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 67784fa81..16818affd 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -19,7 +19,7 @@ import { DocumentView } from "../nodes/DocumentView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
-import { Opt, Field, Doc } from "../../../new_fields/Doc";
+import { Opt, Field, Doc, DocListCast } from "../../../new_fields/Doc";
import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
import { listSpec } from "../../../new_fields/Schema";
import { List } from "../../../new_fields/List";
@@ -111,17 +111,15 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
return applyToDoc(props.Document, script.run);
}}
- OnFillDown={(value: string) => {
+ OnFillDown={async (value: string) => {
let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
if (!script.compiled) {
return;
}
const run = script.run;
//TODO This should be able to be refactored to compile the script once
- const val = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc));
- if (val) {
- val.forEach(doc => applyToDoc(doc, run));
- }
+ const val = await DocListCast(this.props.Document[this.props.fieldKey])
+ val && val.forEach(doc => applyToDoc(doc, run));
}}>
</EditableView>
</div >
@@ -268,6 +266,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
focus={emptyFunction}
parentActive={this.props.active}
whenActiveChanged={this.props.whenActiveChanged}
+ bringToFront={emptyFunction}
/>
</div>
<input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 19d4abc05..411d67ff7 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -23,43 +23,37 @@
margin: 5px 0;
}
- .collection-child {
- margin-top: 10px;
- margin-bottom: 10px;
- }
.no-indent {
padding-left: 0;
}
.bullet {
- position: absolute;
- width: 1.5em;
- display: inline-block;
+ float:left;
+ position: relative;
+ width: 15px;
+ display: block;
color: $intermediate-color;
margin-top: 3px;
transform: scale(1.3,1.3);
}
- .coll-title {
- width:max-content;
- display: block;
- font-size: 24px;
- }
-
.docContainer {
margin-left: 10px;
display: block;
- width: max-content;
+ // width:100%;//width: max-content;
}
-
.docContainer:hover {
- .delete-button {
- display: inline;
- // width: auto;
+ .treeViewItem-openRight {
+ display:inline;
}
}
+
+ .editableView-container {
+ font-weight: bold;
+ }
+
.delete-button {
color: $intermediate-color;
// float: right;
@@ -67,4 +61,28 @@
// margin-top: 3px;
display: inline;
}
+ .treeViewItem-openRight {
+ margin-left: 5px;
+ display:none;
+ }
+ .docContainer:hover {
+ .delete-button {
+ display: inline;
+ // width: auto;
+ }
+ }
+
+ .coll-title {
+ width:max-content;
+ display: block;
+ font-size: 24px;
+ }
+ .collection-child {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ }
+ .collectionTreeView-keyHeader {
+ font-style: italic;
+ font-size: 8pt;
+ }
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 7898d74ce..33787f06b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,5 +1,5 @@
import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
+import { faCaretDown, faCaretRight, faTrashAlt, faAngleRight } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, observable, trace } from "mobx";
import { observer } from "mobx-react";
@@ -10,7 +10,7 @@ import "./CollectionTreeView.scss";
import React = require("react");
import { Document, listSpec } from '../../../new_fields/Schema';
import { Cast, StrCast, BoolCast, FieldValue } from '../../../new_fields/Types';
-import { Doc } from '../../../new_fields/Doc';
+import { Doc, DocListCast } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/RefField';
import { ContextMenu } from '../ContextMenu';
import { undoBatch } from '../../util/UndoManager';
@@ -18,6 +18,9 @@ import { Main } from '../Main';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
import { CollectionDockingView } from './CollectionDockingView';
import { DocumentManager } from '../../util/DocumentManager';
+import { Utils } from '../../../Utils';
+import { List } from '../../../new_fields/List';
+import { indexOf } from 'typescript-collections/dist/lib/arrays';
export interface TreeViewProps {
@@ -34,6 +37,7 @@ export enum BulletType {
}
library.add(faTrashAlt);
+library.add(faAngleRight);
library.add(faCaretDown);
library.add(faCaretRight);
@@ -45,15 +49,27 @@ class TreeView extends React.Component<TreeViewProps> {
@observable _collapsed: boolean = true;
- delete = () => this.props.deleteDoc(this.props.document);
+ @undoBatch delete = () => this.props.deleteDoc(this.props.document);
+
+ @undoBatch openRight = async () => {
+ if (this.props.document.dockingConfig) {
+ Main.Instance.openWorkspace(this.props.document);
+ } else {
+ CollectionDockingView.Instance.AddRightSplit(this.props.document);
+ }
+ };
get children() {
return Cast(this.props.document.data, listSpec(Doc), []); // bcz: needed? .filter(doc => FieldValue(doc));
}
+ onPointerDown = (e: React.PointerEvent) => {
+ e.stopPropagation();
+ }
+
@action
- remove = (document: Document) => {
- let children = Cast(this.props.document.data, listSpec(Doc), []);
+ remove = (document: Document, key: string) => {
+ let children = Cast(this.props.document[key], listSpec(Doc), []);
if (children) {
children.splice(children.indexOf(document), 1);
}
@@ -65,7 +81,7 @@ class TreeView extends React.Component<TreeViewProps> {
return true;
}
//TODO This should check if it was removed
- this.remove(document);
+ this.remove(document, "data");
return addDoc(document);
}
@@ -97,10 +113,19 @@ class TreeView extends React.Component<TreeViewProps> {
return true;
}}
/>);
+ let dataDocs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []);
+ let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
+ <div className="treeViewItem-openRight" onPointerDown={this.onPointerDown} onClick={this.openRight}>
+ <FontAwesomeIcon icon="angle-right" size="lg" />
+ <FontAwesomeIcon icon="angle-right" size="lg" />
+ </div>);
return (
- <div className="docContainer" ref={reference} onPointerDown={onItemDown}>
+ <div className="docContainer" ref={reference} onPointerDown={onItemDown}
+ style={{ background: BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0" }}
+ onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
{editableView(StrCast(this.props.document.title))}
- {/* <div className="delete-button" onClick={this.delete}><FontAwesomeIcon icon="trash-alt" size="xs" /></div> */}
+ {openRight}
+ {/* {<div className="delete-button" onClick={this.delete}><FontAwesomeIcon icon="trash-alt" size="xs" /></div>} */}
</div >);
}
@@ -111,7 +136,11 @@ class TreeView extends React.Component<TreeViewProps> {
if (DocumentManager.Instance.getDocumentViews(this.props.document).length) {
ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) });
}
- ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
+ ContextMenu.Instance.addItem({
+ description: "Delete", event: undoBatch(() => {
+ this.props.deleteDoc(this.props.document);
+ })
+ });
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
e.stopPropagation();
}
@@ -122,21 +151,27 @@ class TreeView extends React.Component<TreeViewProps> {
render() {
let bulletType = BulletType.List;
- let contentElement: JSX.Element | null = (null);
- var children = Cast(this.props.document.data, listSpec(Doc));
- if (children) { // add children for a collection
- if (!this._collapsed) {
- bulletType = BulletType.Collapsible;
- contentElement = <ul>
- {TreeView.GetChildElements(children, this.remove, this.move, this.props.dropAction)}
- </ul >;
- }
- else bulletType = BulletType.Collapsed;
+ let contentElement: (JSX.Element | null)[] = [];
+ let keys = Array.from(Object.keys(this.props.document));
+ if (this.props.document.proto instanceof Doc) {
+ keys.push(...Array.from(Object.keys(this.props.document.proto)));
}
+ keys.map(key => {
+ let docList = Cast(this.props.document[key], listSpec(Doc));
+ if (docList instanceof List && docList.length && docList[0] instanceof Doc) {
+ if (!this._collapsed) {
+ bulletType = BulletType.Collapsible;
+ contentElement.push(<ul key={key + "more"}>
+ {(key === "data") ? (null) :
+ <span className="collectionTreeView-keyHeader" key={key}>{key}</span>}
+ {TreeView.GetChildElements(docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)}
+ </ul >);
+ } else
+ bulletType = BulletType.Collapsed;
+ }
+ });
return <div className="treeViewItem-container"
- style={{ background: BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0" }}
- onContextMenu={this.onWorkspaceContextMenu}
- onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+ onContextMenu={this.onWorkspaceContextMenu}>
<li className="collection-child">
{this.renderBullet(bulletType)}
{this.renderTitle()}
@@ -144,9 +179,9 @@ class TreeView extends React.Component<TreeViewProps> {
</li>
</div>;
}
- public static GetChildElements(docs: Doc[], remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) {
- return docs.filter(child => !child.excludeFromLibrary).filter(doc => FieldValue(doc)).map(child =>
- <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
+ public static GetChildElements(docs: (Doc | Promise<Doc>)[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) {
+ return docs.filter(child => child instanceof Doc && !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child =>
+ <TreeView document={child as Doc} key={(child as Doc)[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
}
}
@@ -168,13 +203,12 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
}
render() {
- trace();
const children = this.children;
let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType;
if (!children) {
return (null);
}
- let childElements = TreeView.GetChildElements(children, this.remove, this.props.moveDocument, dropAction);
+ let childElements = TreeView.GetChildElements(children, false, this.remove, this.props.moveDocument, dropAction);
return (
<div id="body" className="collectionTreeView-dropTarget"
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index b34e0856e..cbfbb1d2c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,4 +1,4 @@
-import { computed, IReactionDisposer, reaction } from "mobx";
+import { computed, IReactionDisposer, reaction, trace } from "mobx";
import { observer } from "mobx-react";
import { Utils } from "../../../../Utils";
import { DocumentManager } from "../../../util/DocumentManager";
@@ -7,7 +7,7 @@ import { CollectionViewProps } from "../CollectionSubView";
import "./CollectionFreeFormLinksView.scss";
import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
import React = require("react");
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc, DocListCast } from "../../../../new_fields/Doc";
import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
import { listSpec } from "../../../../new_fields/Schema";
import { List } from "../../../../new_fields/List";
@@ -18,59 +18,57 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
_brushReactionDisposer?: IReactionDisposer;
componentDidMount() {
- this._brushReactionDisposer = reaction(() => Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).map(doc => NumCast(doc.x)),
+ this._brushReactionDisposer = reaction(
() => {
- let views = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => StrCast(doc.backgroundLayout, "").indexOf("istogram") !== -1);
- for (let i = 0; i < views.length; i++) {
- for (let j = 0; j < views.length; j++) {
- let srcDoc = views[j];
- let dstDoc = views[i];
+ let doclist = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ return { doclist: doclist ? doclist : [], xs: doclist instanceof List ? doclist.map(d => d instanceof Doc && d.x) : [] };
+ },
+ async () => {
+ let doclist = await DocListCast(this.props.Document[this.props.fieldKey]);
+ let views = doclist ? doclist.filter(doc => StrCast(doc.backgroundLayout).indexOf("istogram") !== -1) : [];
+ views.forEach((dstDoc, i) => {
+ views.forEach((srcDoc, j) => {
+ let dstTarg = dstDoc;
+ let srcTarg = srcDoc;
let x1 = NumCast(srcDoc.x);
- let x1w = NumCast(srcDoc.width, -1);
let x2 = NumCast(dstDoc.x);
+ let x1w = NumCast(srcDoc.width, -1);
let x2w = NumCast(dstDoc.width, -1);
- if (x1w < 0 || x2w < 0 || i === j) {
- continue;
- }
- let dstTarg = dstDoc;
- let srcTarg = srcDoc;
- let findBrush = (field: List<Doc>) => field.findIndex(brush => {
- let bdocs = brush ? Cast(brush.brushingDocs, listSpec(Doc), []) : [];
- return (bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false);
- });
- let brushAction = (field: List<Doc>) => {
- let found = findBrush(field);
- if (found !== -1) {
- console.log("REMOVE BRUSH " + srcTarg.Title + " " + dstTarg.Title);
- field.splice(found, 1);
- }
- };
- if (Math.abs(x1 + x1w - x2) < 20) {
- let linkDoc: Doc = new Doc();
- linkDoc.title = "Histogram Brush";
- linkDoc.linkDescription = "Brush between " + StrCast(srcTarg.title) + " and " + StrCast(dstTarg.Title);
- linkDoc.brushingDocs = new List([dstTarg, srcTarg]);
-
- brushAction = (field: List<Doc>) => {
- if (findBrush(field) === -1) {
- console.log("ADD BRUSH " + srcTarg.Title + " " + dstTarg.Title);
- (findBrush(field) === -1) && field.push(linkDoc);
+ if (x1w < 0 || x2w < 0 || i === j) { }
+ else {
+ let findBrush = (field: (Doc | Promise<Doc>)[]) => field.findIndex(brush => {
+ let bdocs = brush instanceof Doc ? Cast(brush.brushingDocs, listSpec(Doc), []) : undefined;
+ return bdocs && bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false;
+ });
+ let brushAction = (field: (Doc | Promise<Doc>)[]) => {
+ let found = findBrush(field);
+ if (found !== -1) {
+ console.log("REMOVE BRUSH " + srcTarg.title + " " + dstTarg.title);
+ field.splice(found, 1);
}
};
- }
- let dstBrushDocs = Cast(dstTarg.brushingDocs, listSpec(Doc));
- if (dstBrushDocs === undefined) {
- dstTarg.brushingDocs = dstBrushDocs = new List<Doc>();
- }
- let srcBrushDocs = Cast(srcTarg.brushingDocs, listSpec(Doc));
- if (srcBrushDocs === undefined) {
- srcTarg.brushingDocs = srcBrushDocs = new List<Doc>();
- }
- brushAction(dstBrushDocs);
- brushAction(srcBrushDocs);
+ if (Math.abs(x1 + x1w - x2) < 20) {
+ let linkDoc: Doc = new Doc();
+ linkDoc.title = "Histogram Brush";
+ linkDoc.linkDescription = "Brush between " + StrCast(srcTarg.title) + " and " + StrCast(dstTarg.Title);
+ linkDoc.brushingDocs = new List([dstTarg, srcTarg]);
- }
- }
+ brushAction = (field: (Doc | Promise<Doc>)[]) => {
+ if (findBrush(field) === -1) {
+ console.log("ADD BRUSH " + srcTarg.title + " " + dstTarg.title);
+ field.push(linkDoc);
+ }
+ };
+ }
+ let dstBrushDocs = Cast(dstTarg.brushingDocs, listSpec(Doc), []);
+ let srcBrushDocs = Cast(srcTarg.brushingDocs, listSpec(Doc), []);
+ if (dstBrushDocs === undefined) dstTarg.brushingDocs = dstBrushDocs = new List<Doc>();
+ else brushAction(dstBrushDocs);
+ if (srcBrushDocs === undefined) srcTarg.brushingDocs = srcBrushDocs = new List<Doc>();
+ else brushAction(srcBrushDocs);
+ }
+ })
+ })
});
}
componentWillUnmount() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index c22f430ac..642118d75 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -22,11 +22,8 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
}
let cursors = Cast(doc.cursors, listSpec(CursorField));
- if (!cursors) {
- doc.cursors = cursors = new List<CursorField>();
- }
- return cursors.filter(cursor => cursor.data.metadata.id !== id);
+ return (cursors || []).filter(cursor => cursor.data.metadata.id !== id);
}
private crosshairs?: HTMLCanvasElement;
@@ -62,7 +59,6 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
}
}
- @computed
get sharedCursors() {
return this.getCursors().map(c => {
let m = c.data.metadata;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index cb849b325..063c9e2cf 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -37,7 +37,9 @@
border-radius: $border-radius;
box-sizing: border-box;
position: absolute;
- overflow: hidden;
+ .marqueeView {
+ overflow: hidden;
+ }
top: 0;
left: 0;
width: 100%;
@@ -61,7 +63,9 @@
border-radius: $border-radius;
box-sizing: border-box;
position:absolute;
- overflow: hidden;
+ .marqueeView {
+ overflow: hidden;
+ }
top: 0;
left: 0;
width: 100%;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 59f7fa442..7ab09987f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -110,15 +110,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onPointerDown = (e: React.PointerEvent): void => {
- let childSelected = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), [] as Doc[]).filter(doc => doc).reduce((childSelected, doc) => {
- var dv = DocumentManager.Instance.getDocumentView(doc);
- return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
- }, false);
if ((CollectionFreeFormView.RIGHT_BTN_DRAG &&
(((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) ||
- (e.button === 0 && e.altKey)) && (childSelected || this.props.active()))) ||
+ (e.button === 0 && e.altKey)) && this.props.active())) ||
(!CollectionFreeFormView.RIGHT_BTN_DRAG &&
- ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && (childSelected || this.props.active())))) {
+ ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && this.props.active()))) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointermove", this.onPointerMove);
@@ -233,7 +229,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return NumCast(doc1.zIndex) - NumCast(doc2.zIndex);
}).forEach((doc, index) => doc.zIndex = index + 1);
doc.zIndex = docs.length + 1;
- return doc;
}
focusDocument = (doc: Doc) => {
@@ -265,7 +260,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
ContainingCollectionView: this.props.CollectionView,
focus: this.focusDocument,
parentActive: this.props.active,
- whenActiveChanged: this.props.active,
+ whenActiveChanged: this.props.whenActiveChanged,
bringToFront: this.bringToFront,
};
}
@@ -274,7 +269,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
get views() {
let curPage = FieldValue(this.Document.curPage, -1);
let docviews = (this.children || []).filter(doc => doc).reduce((prev, doc) => {
- if (!FieldValue(doc)) return prev;
+ if (!(doc instanceof Doc)) return prev;
var page = NumCast(doc.page, -1);
if (page === curPage || page === -1) {
let minim = Cast(doc.isMinimized, "boolean");
@@ -316,20 +311,18 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
</CollectionFreeFormLinksView>
<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
</CollectionFreeFormViewPannableContents>
- <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} {...this.props} />
</MarqueeView>
+ <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} {...this.props} />
</div>
);
}
}
@observer
-class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
+class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
@computed get overlayView() {
- let overlayLayout = Cast(this.props.Document.overlayLayout, "string", "");
- return !overlayLayout ? (null) :
- (<DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
- isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
+ return (<DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
+ isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
}
render() {
return this.overlayView;
@@ -339,10 +332,8 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
@observer
class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
@computed get backgroundView() {
- let backgroundLayout = Cast(this.props.Document.backgroundLayout, "string", "");
- return !backgroundLayout ? (null) :
- (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
- isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
+ return (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
+ isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
}
render() {
return this.backgroundView;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index ae0a9fd48..6e8ec8662 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -21,6 +21,6 @@
white-space:nowrap;
}
.marquee-legend::after {
- content: "Press: C (collection), or Delete"
+ content: "Press: c (collection), s (summary), r (replace) or Delete"
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index a9e627188..37428e88b 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import { Docs } from "../../../documents/Documents";
import { SelectionManager } from "../../../util/SelectionManager";
import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../../util/UndoManager";
import { InkingCanvas } from "../../InkingCanvas";
import { PreviewCursor } from "../../PreviewCursor";
import { CollectionFreeFormView } from "./CollectionFreeFormView";
@@ -13,7 +13,6 @@ import { Utils } from "../../../../Utils";
import { Doc } from "../../../../new_fields/Doc";
import { NumCast, Cast } from "../../../../new_fields/Types";
import { InkField, StrokeData } from "../../../../new_fields/InkField";
-import { Templates } from "../../Templates";
import { List } from "../../../../new_fields/List";
interface MarqueeViewProps {
@@ -48,12 +47,42 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this._visible = false;
}
+ @undoBatch
@action
onKeyPress = (e: KeyboardEvent) => {
//make textbox and add it to this collection
let [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
- let newBox = Docs.TextDocument({ width: 200, height: 100, x: x, y: y, title: "-typed text-" });
- this.props.addLiveTextDocument(newBox);
+ if (e.key === "q" && e.ctrlKey) {
+ e.preventDefault();
+ (async () => {
+ let text = await navigator.clipboard.readText();
+ let ns = text.split("\n").filter(t => t != "\r");
+ for (let i = 0; i < ns.length - 1; i++) {
+ if (ns[i].trim() === "") {
+ ns.splice(i, 1);
+ continue;
+ }
+ while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") ||
+ ns[i].endsWith(";\r") || ns[i].endsWith(";") ||
+ ns[i].endsWith(".\r") || ns[i].endsWith(".") ||
+ ns[i].endsWith(":\r") || ns[i].endsWith(":")) && i < ns.length - 1) {
+ let sub = ns[i].endsWith("\r") ? 1 : 0;
+ let br = ns[i + 1].trim() === "";
+ ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
+ if (br) break;
+ }
+ }
+ ns.map(line => {
+ let indent = line.search(/\S|$/);
+ let newBox = Docs.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line });
+ this.props.addDocument(newBox, false);
+ y += 40 * this.props.getTransform().Scale;
+ })
+ })();
+ } else {
+ let newBox = Docs.TextDocument({ width: 200, height: 100, x: x, y: y, title: "-typed text-" });
+ this.props.addLiveTextDocument(newBox);
+ }
e.stopPropagation();
}
@action
@@ -67,6 +96,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
document.addEventListener("pointermove", this.onPointerMove, true);
document.addEventListener("pointerup", this.onPointerUp, true);
document.addEventListener("keydown", this.marqueeCommand, true);
+ e.stopPropagation();
}
if (e.altKey) {
e.preventDefault();
@@ -146,20 +176,28 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
if (ink) {
this.marqueeInkDelete(ink.inkData);
}
+ SelectionManager.DeselectAll();
this.cleanupInteractions(false);
e.stopPropagation();
}
- if (e.key === "c" || e.key === "r" || e.key === "e") {
+ if (e.key === "c" || e.key === "r" || e.key === "s" || e.key === "e") {
this._commandExecuted = true;
e.stopPropagation();
let bounds = this.Bounds;
let selected = this.marqueeSelect().map(d => {
- if (e.key !== "r") {
+ if (e.key === "s") {
+ let dCopy = Doc.MakeCopy(d);
+ dCopy.x = NumCast(d.x) - bounds.left - bounds.width / 2;
+ dCopy.y = NumCast(d.y) - bounds.top - bounds.height / 2;
+ dCopy.page = -1;
+ return dCopy;
+ }
+ else if (e.key !== "r") {
this.props.removeDocument(d);
+ d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
+ d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
+ d.page = -1;
}
- d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
- d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
- d.page = -1;
return d;
});
let ink = Cast(this.props.container.props.Document.ink, InkField);
@@ -175,21 +213,23 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
width: bounds.width * zoomBasis,
height: bounds.height * zoomBasis,
ink: inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined,
- title: "a nested collection"
+ title: "a nested collection",
});
this.marqueeInkDelete(inkData);
// SelectionManager.DeselectAll();
- if (e.key === "r") {
- let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
- summary.maximizedDocs = new List<Doc>(selected);
- // summary.doc1 = selected[0];
- // if (selected.length > 1)
- // summary.doc2 = selected[1];
- // summary.templates = new List<string>([Templates.Summary.Layout]);
- this.props.addLiveTextDocument(summary);
+ if (e.key === "s" || e.key === "r") {
e.preventDefault();
let scrpt = this.props.getTransform().inverse().transformPoint(bounds.left, bounds.top);
+ let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
+
+ if (e.key === "s") {
+ summary.proto!.maximizeOnRight = true;
+ newCollection.proto!.summaryDoc = summary;
+ selected = [newCollection];
+ }
+ summary.proto!.maximizedDocs = new List<Doc>(selected);
+ summary.proto!.isButton = true;
selected.map(maximizedDoc => {
let maxx = NumCast(maximizedDoc.x, undefined);
let maxy = NumCast(maximizedDoc.y, undefined);
@@ -197,25 +237,28 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
let maxh = NumCast(maximizedDoc.height, undefined);
maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), 0]);
});
+ this.props.addLiveTextDocument(summary);
}
else {
this.props.addDocument(newCollection, false);
+ SelectionManager.DeselectAll();
+ this.props.selectDocuments([newCollection]);
}
this.cleanupInteractions(false);
- }
- if (e.key === "s") {
- this._commandExecuted = true;
- e.stopPropagation();
- e.preventDefault();
- let bounds = this.Bounds;
- let selected = this.marqueeSelect();
- SelectionManager.DeselectAll();
- let summary = Docs.TextDocument({ x: bounds.left + bounds.width + 25, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
- this.props.addLiveTextDocument(summary);
- selected.forEach(select => Doc.MakeLink(summary.proto!, select.proto!));
+ } else
+ if (e.key === "s") {
+ // this._commandExecuted = true;
+ // e.stopPropagation();
+ // e.preventDefault();
+ // let bounds = this.Bounds;
+ // let selected = this.marqueeSelect();
+ // SelectionManager.DeselectAll();
+ // let summary = Docs.TextDocument({ x: bounds.left + bounds.width + 25, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
+ // this.props.addLiveTextDocument(summary);
+ // selected.forEach(select => Doc.MakeLink(summary.proto!, select.proto!));
- this.cleanupInteractions(false);
- }
+ // this.cleanupInteractions(false);
+ }
}
@action
marqueeInkSelect(ink: Map<any, any>) {
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index 4f68b71b0..cb4d1ad87 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -1,7 +1,7 @@
@import url("https://fonts.googleapis.com/css?family=Noto+Sans:400,700|Crimson+Text:400,400i,700");
// colors
$light-color: #fcfbf7;
-$light-color-secondary: rgb(241, 239, 235);
+$light-color-secondary:#f1efeb;
$main-accent: #61aaa3;
// $alt-accent: #cdd5ec;
// $alt-accent: #cdeceb;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 6186cf348..fb182847e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -9,8 +9,10 @@ import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schem
import { FieldValue, Cast, NumCast, BoolCast } from "../../../new_fields/Types";
import { OmitKeys, Utils } from "../../../Utils";
import { SelectionManager } from "../../util/SelectionManager";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym } from "../../../new_fields/Doc";
import { List } from "../../../new_fields/List";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
}
@@ -86,7 +88,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
let scrpt = this.props.ScreenToLocalTransform().transformPoint(values[0], values[1]);
this.animateBetweenIcon(true, scrpt, [values[2], values[3]], values[4], values[5], values[6], this.props.Document, values[7] ? true : false);
}
- });
+ }, { fireImmediately: true });
}
componentWillUnmount() {
@@ -94,6 +96,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
animateBetweenIcon(first: boolean, icon: number[], targ: number[], width: number, height: number, stime: number, target: Doc, maximizing: boolean) {
+ if (first) {
+ if (maximizing) target.width = target.height = 1;
+ }
setTimeout(() => {
let now = Date.now();
let progress = Math.min(1, (now - stime) / 200);
@@ -124,22 +129,25 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public toggleIcon = async (): Promise<void> => {
SelectionManager.DeselectAll();
let isMinimized: boolean | undefined;
- let maximizedDocs = await Cast(this.props.Document.maximizedDocs, listSpec(Doc));
+ let maximizedDocs = await DocListCast(this.props.Document.maximizedDocs);
let minimizedDoc: Doc | undefined = this.props.Document;
if (!maximizedDocs) {
minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
- if (minimizedDoc) maximizedDocs = await Cast(minimizedDoc.maximizedDocs, listSpec(Doc));
+ if (minimizedDoc) maximizedDocs = await DocListCast(minimizedDoc.maximizedDocs);
}
- if (minimizedDoc && maximizedDocs && maximizedDocs instanceof List) {
+ if (minimizedDoc && maximizedDocs) {
let minimizedTarget = minimizedDoc;
- maximizedDocs.map(maximizedDoc => {
+ if (!CollectionFreeFormDocumentView._undoBatch) {
+ CollectionFreeFormDocumentView._undoBatch = UndoManager.StartBatch("iconAnimating");
+ }
+ maximizedDocs.forEach(maximizedDoc => {
let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
if (!iconAnimating || (Date.now() - iconAnimating[6] > 1000)) {
if (isMinimized === undefined) {
isMinimized = BoolCast(maximizedDoc.isMinimized, false);
}
- let minx = NumCast(minimizedTarget.x, undefined) + NumCast(minimizedTarget.width, undefined) / 2;
- let miny = NumCast(minimizedTarget.y, undefined) + NumCast(minimizedTarget.height, undefined) / 2;
+ let minx = NumCast(minimizedTarget.x, undefined) + NumCast(minimizedTarget.width, undefined) * this.getTransform().Scale * this.contentScaling() / 2;
+ let miny = NumCast(minimizedTarget.y, undefined) + NumCast(minimizedTarget.height, undefined) * this.getTransform().Scale * this.contentScaling() / 2;
let maxx = NumCast(maximizedDoc.x, undefined);
let maxy = NumCast(maximizedDoc.y, undefined);
let maxw = NumCast(maximizedDoc.width, undefined);
@@ -147,30 +155,48 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
if (minx !== undefined && miny !== undefined && maxx !== undefined && maxy !== undefined &&
maxw !== undefined && maxh !== undefined) {
let scrpt = this.props.ScreenToLocalTransform().inverse().transformPoint(minx, miny);
- maximizedDoc.isMinimized = false;
- maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), isMinimized ? 1 : 0]);
+ if (isMinimized) maximizedDoc.isMinimized = false;
+ maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), isMinimized ? 1 : 0])
}
}
});
+ setTimeout(() => {
+ CollectionFreeFormDocumentView._undoBatch && CollectionFreeFormDocumentView._undoBatch.end();
+ CollectionFreeFormDocumentView._undoBatch = undefined;
+ }, 500);
}
}
+ static _undoBatch?: UndoManager.Batch = undefined;
onPointerDown = (e: React.PointerEvent): void => {
this._downX = e.clientX;
this._downY = e.clientY;
- e.stopPropagation();
+ // e.stopPropagation();
}
onClick = async (e: React.MouseEvent) => {
e.stopPropagation();
- let ctrlKey = e.ctrlKey;
+ let altKey = e.altKey;
if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
- if (await BoolCast(this.props.Document.isButton, false)) {
- let maximizedDocs = await Cast(this.props.Document.maximizedDocs, listSpec(Doc));
+ if (BoolCast(this.props.Document.isButton, false) || (e.target as any).id === "isBullet") {
+ let maximizedDocs = await DocListCast(this.props.Document.maximizedDocs);
if (maximizedDocs) { // bcz: need a better way to associate behaviors with click events on widget-documents
- if (ctrlKey) {
- this.props.addDocument && maximizedDocs.filter(d => d instanceof Doc).map(maxDoc => this.props.addDocument!(maxDoc, false));
+ if ((altKey && !this.props.Document.maximizeOnRight) || (!altKey && this.props.Document.maximizeOnRight)) {
+ let dataDocs = await DocListCast(CollectionDockingView.Instance.props.Document.data);
+ if (dataDocs) {
+ SelectionManager.DeselectAll();
+ maximizedDocs.forEach(maxDoc => {
+ maxDoc.isMinimized = false;
+ if (!dataDocs || dataDocs.indexOf(maxDoc) == -1) {
+ CollectionDockingView.Instance.AddRightSplit(maxDoc);
+ } else {
+ CollectionDockingView.Instance.CloseRightSplit(maxDoc);
+ }
+ });
+ }
+ } else {
+ this.props.addDocument && maximizedDocs.forEach(async maxDoc => this.props.addDocument!(await maxDoc, false));
+ this.toggleIcon();
}
- this.toggleIcon();
}
}
}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index bbc927b5a..f404b7bc6 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,4 +1,4 @@
-import { computed } from "mobx";
+import { computed, trace } from "mobx";
import { observer } from "mobx-react";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -45,7 +45,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
select: (ctrl: boolean) => void,
layoutKey: string
}> {
- @computed get layout(): string { return Cast(this.props.Document[this.props.layoutKey], "string", "<p>Error loading layout data</p>"); }
+ @computed get layout(): string { return Cast(this.props.Document[this.props.layoutKey], "string", this.props.layoutKey === "layout" ? "<p>Error loading layout data</p>" : ""); }
CreateBindings(): JsxBindings {
return { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit };
@@ -58,20 +58,33 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
}
return new List<string>();
}
- set templates(templates: List<string>) { this.props.Document.templates = templates; }
- get finalLayout() {
+ @computed get finalLayout() {
const baseLayout = this.layout;
let base = baseLayout;
let layout = baseLayout;
- this.templates.forEach(template => {
- layout = template.replace("{layout}", base);
- base = layout;
- });
+ // bcz: templates are intended only for a document's primary layout or overlay (not background). However,
+ // a DocumentContentsView is used to render annotation overlays, so we detect that here
+ // by checking the layoutKey. This should probably be moved into
+ // a prop so that the overlay can explicitly turn off templates.
+ if ((this.props.layoutKey === "overlayLayout" && StrCast(this.props.Document.layout).indexOf("CollectionView") !== -1) ||
+ (this.props.layoutKey === "layout" && StrCast(this.props.Document.layout).indexOf("CollectionView") === -1)) {
+ this.templates.forEach(template => {
+ let self = this;
+ function convertConstantsToNative(match: string, offset: number, x: string) {
+ let px = Number(match.replace("px", ""));
+ return `${px * self.props.ScreenToLocalTransform().Scale}px`;
+ }
+ let nativizedTemplate = template.replace(/([0-9]+)px/g, convertConstantsToNative);
+ layout = nativizedTemplate.replace("{layout}", base);
+ base = layout;
+ });
+ }
return layout;
}
render() {
+ if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
return <ObserverJsxParser
components={{ FormattedTextBox, ImageBox, IconBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
bindings={this.CreateBindings()}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index fd012e7ea..38efeeba5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, runInAction } from "mobx";
+import { action, computed, runInAction, reaction, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
import { emptyFunction, Utils } from "../../../Utils";
import { Docs } from "../../documents/Documents";
@@ -16,10 +16,10 @@ import { Template, Templates } from "./../Templates";
import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import React = require("react");
-import { Opt, Doc, WidthSym, HeightSym } from "../../../new_fields/Doc";
+import { Opt, Doc, WidthSym, HeightSym, DocListCast } from "../../../new_fields/Doc";
import { DocComponent } from "../DocComponent";
import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { FieldValue, StrCast, BoolCast } from "../../../new_fields/Types";
+import { FieldValue, StrCast, BoolCast, Cast } from "../../../new_fields/Types";
import { List } from "../../../new_fields/List";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
@@ -99,6 +99,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
set templates(templates: List<string>) { this.props.Document.templates = templates; }
screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
+ _reactionDisposer?: IReactionDisposer;
@action
componentDidMount() {
if (this._mainCont.current) {
@@ -106,6 +107,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
handlers: { drop: this.drop.bind(this) }
});
}
+ this._reactionDisposer = reaction(() => [this.props.Document.maximizedDocs, this.props.Document.summaryDoc, this.props.Document.summaryDoc instanceof Doc ? this.props.Document.summaryDoc.title : ""],
+ async () => {
+ let maxDoc = await DocListCast(this.props.Document.maximizedDocs);
+ if (maxDoc && StrCast(this.props.Document.layout).indexOf("IconBox") !== -1) {
+ this.props.Document.title = (maxDoc && maxDoc.length === 1 ? maxDoc[0].title + ".icon" : "");
+ }
+ let sumDoc = Cast(this.props.Document.summaryDoc, Doc);
+ if (sumDoc instanceof Doc) {
+ this.props.Document.title = sumDoc.title + ".expanded";
+ }
+ }, { fireImmediately: true });
DocumentManager.Instance.DocumentViews.push(this);
}
@action
@@ -121,9 +133,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@action
componentWillUnmount() {
- if (this._dropDisposer) {
- this._dropDisposer();
- }
+ if (this._reactionDisposer) this._reactionDisposer();
+ if (this._dropDisposer) this._dropDisposer();
DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
}
@@ -165,8 +176,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (e.shiftKey && e.buttons === 1) {
if (this.props.isTopMost) {
this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey ? "alias" : undefined);
- } else {
- CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
+ } else if (this.props.Document) {
+ CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e);
}
e.stopPropagation();
} else if (this.active) {
@@ -198,14 +209,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.props.removeDocument && this.props.removeDocument(this.props.Document);
}
fieldsClicked = (e: React.MouseEvent): void => {
- let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 });
+ let kvp = Docs.KVPDocument(this.props.Document, { title: this.props.Document.title + ".kvp", width: 300, height: 300 });
CollectionDockingView.Instance.AddRightSplit(kvp);
}
makeButton = (e: React.MouseEvent): void => {
- this.props.Document.isButton = !BoolCast(this.props.Document.isButton, false);
- if (this.props.Document.isButton && !this.props.Document.nativeWidth) {
- this.props.Document.nativeWidth = this.props.Document[WidthSym]();
- this.props.Document.nativeHeight = this.props.Document[HeightSym]();
+ let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+ doc.isButton = !BoolCast(doc.isButton, false);
+ if (doc.isButton && !doc.nativeWidth) {
+ doc.nativeWidth = doc[WidthSym]();
+ doc.nativeHeight = doc[HeightSym]();
}
}
fullScreenClicked = (e: React.MouseEvent): void => {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index a1e083b36..613c24fa4 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -17,6 +17,7 @@ import { List } from "../../../new_fields/List";
import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField";
import { IconField } from "../../../new_fields/IconField";
import { RichTextField } from "../../../new_fields/RichTextField";
+import { DateField } from "../../../new_fields/DateField";
//
@@ -77,6 +78,8 @@ export class FieldView extends React.Component<FieldViewProps> {
}
else if (field instanceof AudioField) {
return <AudioBox {...this.props} />;
+ } else if (field instanceof DateField) {
+ return <p>{field.date.toLocaleString()}</p>;
}
else if (field instanceof Doc) {
return (
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index d43aa4e02..458a62c5b 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -11,12 +11,13 @@
}
.formattedTextBox-cont-scroll, .formattedTextBox-cont-hidden {
- background: $light-color-secondary;
+ background: inherit;
padding: 0;
border-width: 0px;
border-radius: inherit;
border-color: $intermediate-color;
box-sizing: border-box;
+ background-color: inherit;
border-style: solid;
overflow-y: scroll;
overflow-x: hidden;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 3873dfd62..a688a6272 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -23,8 +23,9 @@ import { InkingControl } from "../InkingControl";
import { StrCast, Cast, NumCast, BoolCast } from "../../../new_fields/Types";
import { RichTextField } from "../../../new_fields/RichTextField";
import { Id } from "../../../new_fields/RefField";
-const { buildMenuItems } = require("prosemirror-example-setup");
-const { menuBar } = require("prosemirror-menu");
+import { UndoManager } from "../../util/UndoManager";
+import { Transform } from "prosemirror-transform";
+import { Transform as MatrixTransform } from "../../util/Transform";
// FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
//
@@ -128,7 +129,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
);
} else {
this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
- () => this.props.isSelected() && !BoolCast(this.props.Document.isButton, false) && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform));
+ () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform));
}
@@ -150,6 +151,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config),
dispatchTransaction: this.dispatchTransaction
});
+ let text = StrCast(this.props.Document.documentText);
+ if (text.startsWith("@@@")) {
+ this.props.Document.proto!.documentText = undefined;
+ this._editorView.dispatch(this._editorView.state.tr.insertText(text.replace("@@@", "")));
+ }
}
if (this.props.selectOnLoad) {
@@ -273,7 +279,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
});
}
-
+ onBlur = (e: any) => {
+ if (this._undoTyping) {
+ this._undoTyping.end();
+ this._undoTyping = undefined;
+ }
+ }
+ public _undoTyping?: UndoManager.Batch;
onKeyPress = (e: React.KeyboardEvent) => {
if (e.key === "Escape") {
SelectionManager.DeselectAll();
@@ -289,22 +301,24 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
target.title = "-" + titlestr + (str.length > 40 ? "..." : "");
}
+ if (!this._undoTyping) {
+ this._undoTyping = UndoManager.StartBatch("undoTyping");
+ }
}
render() {
let style = this.props.isOverlay ? "scroll" : "hidden";
let rounded = NumCast(this.props.Document.borderRounding) < 0 ? "-rounded" : "";
- let color = StrCast(this.props.Document.backgroundColor);
let interactive = InkingControl.Instance.selectedTool ? "" : "interactive";
return (
<div className={`formattedTextBox-cont-${style}`} ref={this._ref}
style={{
pointerEvents: interactive ? "all" : "none",
- background: color,
}}
onKeyDown={this.onKeyPress}
onKeyPress={this.onKeyPress}
onFocus={this.onFocused}
onClick={this.onClick}
+ onBlur={this.onBlur}
onPointerUp={this.onPointerUp}
onPointerDown={this.onPointerDown}
onMouseDown={this.onMouseDown}
@@ -312,7 +326,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
// tfs: do we need this event handler
onWheel={this.onPointerWheel}
>
- <div className={`formattedTextBox-inner${rounded}`} style={{ pointerEvents: this.props.Document.isButton ? "none" : "all" }} ref={this._proseRef} />
+ <div className={`formattedTextBox-inner${rounded}`} style={{ pointerEvents: this.props.Document.isButton && !this.props.isSelected() ? "none" : "all" }} ref={this._proseRef} />
</div>
);
}
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 3fab10df4..4bcb4c636 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -2,13 +2,12 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction } from "mobx";
+import { computed, observable, runInAction, reaction, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
-import { SelectionManager } from "../../util/SelectionManager";
import { FieldView, FieldViewProps } from './FieldView';
import "./IconBox.scss";
import { Cast, StrCast, BoolCast } from "../../../new_fields/Types";
-import { Doc, WidthSym, HeightSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast } from "../../../new_fields/Doc";
import { IconField } from "../../../new_fields/IconField";
import { ContextMenu } from "../ContextMenu";
import Measure from "react-measure";
@@ -55,7 +54,8 @@ export class IconBox extends React.Component<FieldViewProps> {
let labelField = StrCast(this.props.Document.labelField);
let hideLabel = BoolCast(this.props.Document.hideLabel);
let maxDoc = Cast(this.props.Document.maximizedDocs, listSpec(Doc), []);
- let label = !hideLabel && maxDoc && labelField ? (maxDoc.length === 1 ? maxDoc[0][labelField] : this.props.Document[labelField]) : "";
+ let firstDoc = maxDoc && maxDoc.length > 0 && maxDoc[0] instanceof Doc ? maxDoc[0] as Doc : undefined;
+ let label = !hideLabel && firstDoc && labelField ? firstDoc[labelField] : "";
return (
<div className="iconBox-container" onContextMenu={this.specificContextMenu}>
{this.minimizedIcon}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 235792cbe..5de660d57 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -40,21 +40,24 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
PanelHeight: returnZero,
};
let contents = <FieldView {...props} />;
+ let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
return (
<tr className={this.props.rowStyle}>
<td className="keyValuePair-td-key" style={{ width: `${this.props.keyWidth}%` }}>
<div className="keyValuePair-td-key-container">
<button className="keyValuePair-td-key-delete" onClick={() => {
- let field = FieldValue(props.Document[props.fieldKey]);
- field && (props.Document[props.fieldKey] = undefined);
+ if (Object.keys(props.Document).indexOf(props.fieldKey) !== -1)
+ props.Document[props.fieldKey] = undefined;
+ else props.Document.proto![props.fieldKey] = undefined;
}}>
X
</button>
- <div className="keyValuePair-keyField">{this.props.keyName}</div>
+ <div className="keyValuePair-keyField">{fieldKey}</div>
</div>
</td>
<td className="keyValuePair-td-value" style={{ width: `${100 - this.props.keyWidth}%` }}>
<EditableView contents={contents} height={36} GetValue={() => {
+
let field = FieldValue(props.Document[props.fieldKey]);
if (field) {
//TODO Types
diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx
index f82c6e9cb..71a423338 100644
--- a/src/client/views/nodes/LinkEditor.tsx
+++ b/src/client/views/nodes/LinkEditor.tsx
@@ -24,8 +24,9 @@ export class LinkEditor extends React.Component<Props> {
onSaveButtonPressed = (e: React.PointerEvent): void => {
e.stopPropagation();
- this.props.linkDoc.title = this._nameInput;
- this.props.linkDoc.linkDescription = this._descriptionInput;
+ let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
+ linkDoc.title = this._nameInput;
+ linkDoc.linkDescription = this._descriptionInput;
this.props.showLinks();
}