aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts6
-rw-r--r--src/client/util/DocumentManager.ts6
-rw-r--r--src/client/util/DragManager.ts20
-rw-r--r--src/client/util/RichTextSchema.tsx81
-rw-r--r--src/client/util/TooltipTextMenu.scss30
-rw-r--r--src/client/util/TooltipTextMenu.tsx150
-rw-r--r--src/client/views/DocumentDecorations.tsx70
-rw-r--r--src/client/views/InkingCanvas.tsx7
-rw-r--r--src/client/views/InkingControl.tsx3
-rw-r--r--src/client/views/Main.scss17
-rw-r--r--src/client/views/MainOverlayTextBox.scss7
-rw-r--r--src/client/views/MainOverlayTextBox.tsx32
-rw-r--r--src/client/views/MainView.tsx7
-rw-r--r--src/client/views/Templates.tsx14
-rw-r--r--src/client/views/_nodeModuleOverrides.scss1
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx89
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx130
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx107
-rw-r--r--src/client/views/collections/CollectionStackingView.scss7
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx43
-rw-r--r--src/client/views/collections/CollectionSubView.tsx13
-rw-r--r--src/client/views/collections/CollectionTreeView.scss7
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx162
-rw-r--r--src/client/views/collections/CollectionView.tsx16
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx39
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx4
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx13
-rw-r--r--src/client/views/nodes/DocumentView.tsx43
-rw-r--r--src/client/views/nodes/FieldView.tsx15
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss2
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx53
-rw-r--r--src/client/views/nodes/ImageBox.tsx43
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx4
-rw-r--r--src/client/views/nodes/PDFBox.scss11
-rw-r--r--src/client/views/nodes/PDFBox.tsx28
-rw-r--r--src/client/views/pdf/PDFViewer.tsx16
-rw-r--r--src/client/views/presentationview/PresentationElement.tsx11
-rw-r--r--src/new_fields/Doc.ts32
-rw-r--r--src/scraping/buxton/scraper.py331
-rw-r--r--src/scraping/buxton/source/Bill_Notes_Bill_Notes_CyKey.docxbin0 -> 1675500 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_Braun_T3.docxbin0 -> 1671968 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_CasioC801.docxbin0 -> 574664 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_Casio_Mini.docxbin0 -> 581069 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_FingerWorks_Prototype.docxbin0 -> 585090 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_Fingerworks_TouchStream.docxbin0 -> 1722555 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_FrogPad.docxbin0 -> 840173 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_Gavilan_SC.docxbin0 -> 1695290 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_Grandjean_Stenotype.docxbin0 -> 2094142 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_Matias.docxbin0 -> 590407 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_MousePen.docxbin0 -> 505322 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_NewO.docxbin0 -> 2264571 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_OLPC.docxbin0 -> 6883659 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_PARCkbd.docxbin0 -> 631959 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_Philco_Mystery_Control.docxbin0 -> 1994439 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_TASA_Kbd.docxbin0 -> 461199 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_The_Tap.docxbin0 -> 711321 bytes
57 files changed, 1167 insertions, 507 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index b04fc401a..ac8a892b8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -344,7 +344,7 @@ export namespace Docs {
{layout}
</div>
<div style="height:(100% + 25px); width:100%; position:absolute">
- <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+ <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} renderDepth={renderDepth}/>
</div>
</div>
`);
@@ -356,7 +356,7 @@ export namespace Docs {
{layout}
</div>
<div style="height:25px; width:100%; position:absolute">
- <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+ <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} renderDepth={renderDepth}/>
</div>
</div>
`);
@@ -379,7 +379,7 @@ export namespace Docs {
{layout}
</div>
<div style="height:15%; width:100%; position:absolute">
- <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+ <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} renderDepth={renderDepth}/>
</div>
</div>
`);
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 16b88b027..dc4385fae 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -138,7 +138,7 @@ export class DocumentManager {
docContext.panTransformType = "Ease";
targetContextView.props.focus(docDelegate, willZoom);
} else {
- (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext);
+ (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext, docContext);
setTimeout(() => {
this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage);
}, 10);
@@ -147,7 +147,7 @@ export class DocumentManager {
const actualDoc = Doc.MakeAlias(docDelegate);
actualDoc.libraryBrush = true;
if (linkPage !== undefined) actualDoc.curPage = linkPage;
- (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc);
+ (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc, actualDoc);
}
} else {
let contextView: DocumentView | null;
@@ -156,7 +156,7 @@ export class DocumentManager {
contextDoc.panTransformType = "Ease";
contextView.props.focus(docDelegate, willZoom);
} else {
- (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc);
+ (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc, contextDoc);
setTimeout(() => {
this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage);
}, 10);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index b707dbe57..706b9b677 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -15,7 +15,8 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
document.removeEventListener("pointermove", onRowMove);
document.removeEventListener('pointerup', onRowUp);
- var dragData = new DragManager.DocumentDragData([await docFunc()]);
+ let doc = await docFunc();
+ var dragData = new DragManager.DocumentDragData([doc], [doc]);
dragData.dropAction = dropAction;
dragData.moveDocument = moveFunc;
dragData.options = options;
@@ -27,17 +28,15 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
document.removeEventListener('pointerup', onRowUp);
};
let onItemDown = async (e: React.PointerEvent) => {
- // if (this.props.isSelected() || this.props.isTopMost) {
if (e.button === 0) {
e.stopPropagation();
if (e.shiftKey && CollectionDockingView.Instance) {
- CollectionDockingView.Instance.StartOtherDrag([await docFunc()], e);
+ CollectionDockingView.Instance.StartOtherDrag(e, [await docFunc()]);
} else {
document.addEventListener("pointermove", onRowMove);
document.addEventListener("pointerup", onRowUp);
}
}
- //}
};
return onItemDown;
}
@@ -59,7 +58,8 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n
let doc = await Cast(draggedDoc.annotationOn, Doc);
if (doc) moddrag.push(doc);
}
- let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
+ let linkDocs = moddrag.length ? moddrag : draggedDocs;
+ let dragData = new DragManager.DocumentDragData(linkDocs, linkDocs); // bcz: dataDocs?
DragManager.StartDocumentDrag([dragEle], dragData, x, y, {
handlers: {
dragComplete: action(emptyFunction),
@@ -143,13 +143,15 @@ export namespace DragManager {
export type MoveFunction = (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
export class DocumentDragData {
- constructor(dragDoc: Doc[]) {
+ constructor(dragDoc: Doc[], dragDataDocs: (Doc | undefined)[]) {
this.draggedDocuments = dragDoc;
+ this.draggedDataDocs = dragDataDocs;
this.droppedDocuments = dragDoc;
this.xOffset = 0;
this.yOffset = 0;
}
draggedDocuments: Doc[];
+ draggedDataDocs: (Doc | undefined)[];
droppedDocuments: Doc[];
xOffset: number;
yOffset: number;
@@ -240,6 +242,8 @@ export namespace DragManager {
const docs: Doc[] =
dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
+ const datadocs: (Doc | undefined)[] =
+ dragData instanceof DocumentDragData ? dragData.draggedDataDocs : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
let dragElements = eles.map(ele => {
const w = ele.offsetWidth,
h = ele.offsetHeight;
@@ -316,12 +320,12 @@ export namespace DragManager {
}
if (((options && !options.withoutShiftDrag) || !options) && e.shiftKey && CollectionDockingView.Instance) {
AbortDrag();
- CollectionDockingView.Instance.StartOtherDrag(docs, {
+ CollectionDockingView.Instance.StartOtherDrag({
pageX: e.pageX,
pageY: e.pageY,
preventDefault: emptyFunction,
button: 0
- });
+ }, docs, datadocs);
}
//TODO: Why can't we use e.movementX and e.movementY?
let moveX = e.pageX - lastX;
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 943cdb4d1..f3f6655af 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1,10 +1,11 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType, Slice } from "prosemirror-model";
+import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType, Slice, Mark, Node } from "prosemirror-model";
import { joinUp, lift, setBlockType, toggleMark, wrapIn, selectNodeForward, deleteSelection } from 'prosemirror-commands';
import { redo, undo } from 'prosemirror-history';
import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list';
import { EditorState, Transaction, NodeSelection, TextSelection, Selection, } from "prosemirror-state";
import { EditorView, } from "prosemirror-view";
+import { View } from '@react-pdf/renderer';
const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -89,7 +90,7 @@ export const nodes: { [index: string]: NodeSpec } = {
inline: true,
attrs: {
visibility: { default: false },
- oldtext: { default: undefined },
+ text: { default: undefined },
oldtextslice: { default: undefined },
oldtextlen: { default: 0 }
@@ -280,8 +281,8 @@ export const marks: { [index: string]: MarkSpec } = {
toDOM: () => ['sup']
},
- collapse: {
- parseDOM: [{ style: 'color: blue' }],
+ highlight: {
+ parseDOM: [{ style: 'background: #d9dbdd' }],
toDOM() {
return ['span', {
style: 'color: blue'
@@ -349,6 +350,16 @@ export const marks: { [index: string]: MarkSpec } = {
/** FONT SIZES */
+ pFontSize: {
+ attrs: {
+ fontSize: { default: 10 }
+ },
+ inclusive: false,
+ parseDOM: [{ style: 'font-size: 10px;' }],
+ toDOM: (node) => ['span', {
+ style: `font-size: ${node.attrs.fontSize}px;`
+ }]
+ },
p10: {
parseDOM: [{ style: 'font-size: 10px;' }],
@@ -479,6 +490,7 @@ export class ImageResizeView {
export class SummarizedView {
// TODO: highlight text that is summarized. to find end of region, walk along mark
_collapsed: HTMLElement;
+ _view: any;
constructor(node: any, view: any, getPos: any) {
this._collapsed = document.createElement("span");
this._collapsed.textContent = "㊉";
@@ -487,24 +499,32 @@ export class SummarizedView {
this._collapsed.style.position = "relative";
this._collapsed.style.width = "40px";
this._collapsed.style.height = "20px";
+ let self = this;
+ this._view = view;
this._collapsed.onpointerdown = function (e: any) {
console.log("star pressed!");
if (node.attrs.visibility) {
node.attrs.visibility = !node.attrs.visibility;
console.log("content is visible");
let y = getPos();
- view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1 + node.attrs.oldtextlen)));
- view.dispatch(view.state.tr.deleteSelection(view.state, () => { }));
- //this._collapsed.textContent = "㊀";
+ let { from, to } = self.updateSummarizedText(y + 1, view.state.schema.marks.highlight);
+ let length = to - from;
+ let newSelection = TextSelection.create(view.state.doc, y + 1, y + 1 + length);
+ node.attrs.text = newSelection.content();
+ view.dispatch(view.state.tr.setSelection(newSelection).deleteSelection(view.state, () => { }));
+ self._collapsed.textContent = "㊉";
} else {
node.attrs.visibility = !node.attrs.visibility;
console.log("content is invisible");
let y = getPos();
- let mark = view.state.schema.mark(view.state.schema.marks.underline);
- console.log("PASTING " + node.attrs.oldtext.toString());
+ console.log(y);
+ let mark = view.state.schema.mark(view.state.schema.marks.highlight);
+ console.log("PASTING " + node.attrs.text.toString());
view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1)));
- view.dispatch(view.state.tr.replaceSelection(node.attrs.oldtext).addMark(view.state.selection.from, view.state.selection.from + node.attrs.oldtextlen, mark));
- //this._collapsed.textContent = "㊉";
+ const from = view.state.selection.from;
+ let size = node.attrs.text.size;
+ view.dispatch(view.state.tr.replaceSelection(node.attrs.text).addMark(from, from + size, mark).removeStoredMark(mark));
+ self._collapsed.textContent = "㊀";
}
e.preventDefault();
e.stopPropagation();
@@ -515,6 +535,45 @@ export class SummarizedView {
selectNode() {
}
+ updateSummarizedText(start?: any, mark?: any) {
+ let $start = this._view.state.doc.resolve(start);
+ let endPos = start;
+ //let first_startPos = $start.start(), endPos = first_startPos;
+ // let startIndex = $start.index(), endIndex = $start.indexAfter();
+ // let nodeAfter = $start.nodeAfter;
+ // while (startIndex > 0 && mark.isInSet($start.parent.child(startIndex - 1).marks)) startIndex--;
+ // while (endIndex < $start.parent.childCount && mark.isInSet($start.parent.child(endIndex).marks)) endIndex++;
+ // let startPos = $start.start(), endPos = startPos;
+ // for (let i = 0; i < endIndex; i++) {
+ // let size = $start.parent.child(i).nodeSize;
+ // console.log($start.parent.child(i).childCount);
+ // if (i < startIndex) startPos += size;
+ // endPos += size;
+ // }
+ let _mark = this._view.state.schema.mark(this._view.state.schema.marks.highlight);
+ // first_startPos = start;
+ // endPos = first_startPos;
+ let visited = new Set();
+ for (let i: number = start + 1; i < this._view.state.doc.nodeSize - 1; i++) {
+ console.log("ITER:", i);
+ let skip = false;
+ this._view.state.doc.nodesBetween(start, i, (node: Node, pos: number, parent: Node, index: number) => {
+ if (node.isLeaf && !visited.has(node) && !skip) {
+ if (node.marks.includes(_mark)) {
+ visited.add(node);
+ //endPos += node.nodeSize + 1;
+ endPos = i + node.nodeSize - 1;
+ console.log("node contains mark!");
+ }
+ else skip = true;
+ }
+ });
+ }
+ console.log(start);
+ console.log(endPos);
+ return { from: start, to: endPos };
+ }
+
deselectNode() {
}
}
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index 4d4eb386d..b10573b3e 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -18,6 +18,7 @@
.ProseMirror-menuitem {
margin-right: 3px;
display: inline-block;
+ z-index: 100000;
}
.ProseMirror-menuseparator {
@@ -59,7 +60,6 @@
}
.ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu {
- position: absolute;
background: $dark-color;
color:white;
border: 1px solid rgb(223, 223, 223);
@@ -67,9 +67,10 @@
}
.ProseMirror-menu-dropdown-menu {
- z-index: 15;
+ z-index: 100000;
min-width: 6em;
background: white;
+ position: absolute;
}
.linking {
@@ -80,6 +81,7 @@
cursor: pointer;
padding: 2px 8px 2px 4px;
width: auto;
+ z-index: 100000;
}
.ProseMirror-menu-dropdown-item:hover {
@@ -233,19 +235,20 @@
}
.tooltipMenu {
- position: absolute;
- z-index: 50;
- background: whitesmoke;
+ position: relative;
+ z-index: 2000;
+ background: #121721;
border: 1px solid silver;
border-radius: 15px;
- padding: 2px 10px;
- //margin-bottom: 100px;
+ //height: 60px;
+ //padding: 2px 10px;
+ //margin-top: 100px;
//-webkit-transform: translateX(-50%);
//transform: translateX(-50%);
- transform: translateY(-50%);
+ transform: translateY(-85px);
pointer-events: all;
- height: auto;
- width:inherit;
+ height: 30px;
+ width:500px;
.ProseMirror-example-setup-style hr {
padding: 2px 10px;
border: none;
@@ -306,3 +309,10 @@
font-size: 12px;
padding-right: 0px;
}
+ .summarize{
+ //margin-left: 15px;
+ color: white;
+ height: 20px;
+ // background-color: white;
+ text-align: center;
+ } \ No newline at end of file
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index cbabadd64..1c96bb1d3 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -7,6 +7,7 @@ import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemir
import { EditorView } from "prosemirror-view";
import { schema } from "./RichTextSchema";
import { Schema, NodeType, MarkType, Mark } from "prosemirror-model";
+import { Node as ProsNode } from "prosemirror-model";
import React = require("react");
import "./TooltipTextMenu.scss";
const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
@@ -29,6 +30,8 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie
import { DocumentManager } from "./DocumentManager";
import { Id } from "../../new_fields/FieldSymbols";
import { Utils } from "../../Utils";
+import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
+import { text } from "body-parser";
// import { wrap } from "module";
const SVG = "http://www.w3.org/2000/svg";
@@ -42,7 +45,7 @@ export class TooltipTextMenu {
private fontStyles: MarkType[];
private fontSizes: MarkType[];
private listTypes: NodeType[];
- private editorProps: FieldViewProps;
+ private editorProps: FieldViewProps & FormattedTextBoxProps;
private state: EditorState;
private fontSizeToNum: Map<MarkType, number>;
private fontStylesToName: Map<MarkType, string>;
@@ -58,7 +61,7 @@ export class TooltipTextMenu {
private fontStyleDom?: Node;
private listTypeBtnDom?: Node;
- constructor(view: EditorView, editorProps: FieldViewProps) {
+ constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) {
this.view = view;
this.state = view.state;
this.editorProps = editorProps;
@@ -66,7 +69,7 @@ export class TooltipTextMenu {
this.tooltip.className = "tooltipMenu";
//add the div which is the tooltip
- view.dom.parentNode!.parentNode!.appendChild(this.tooltip);
+ //view.dom.parentNode!.parentNode!.appendChild(this.tooltip);
//add additional icons
library.add(faListUl);
@@ -131,11 +134,21 @@ export class TooltipTextMenu {
this.link = document.createElement("a");
this.link.target = "_blank";
this.link.style.color = "white";
- this.tooltip.appendChild(this.link);
+ //this.tooltip.appendChild(this.link);
this.tooltip.appendChild(this.createLink().render(this.view).dom);
+ this.tooltip.appendChild(this.createStar().render(this.view).dom);
+
this.update(view, undefined);
+
+ //view.dom.parentNode!.parentNode!.insertBefore(this.tooltip, view.dom.parentNode);
+
+ // quick and dirty null check
+ const outer_div = this.editorProps.outer_div;
+ outer_div && outer_div(this.tooltip);
+
+ console.log("hi");
}
//label of dropdown will change to given label
@@ -189,13 +202,13 @@ export class TooltipTextMenu {
this.linkText.style.whiteSpace = "nowrap";
this.linkText.style.width = "150px";
this.linkText.style.overflow = "hidden";
- this.linkText.style.color = "black";
+ this.linkText.style.color = "white";
this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); };
let linkBtn = document.createElement("div");
linkBtn.textContent = ">>";
linkBtn.style.width = "10px";
linkBtn.style.height = "10px";
- linkBtn.style.color = "black";
+ linkBtn.style.color = "white";
linkBtn.style.cssFloat = "left";
linkBtn.onpointerdown = (e: PointerEvent) => {
let node = this.view.state.selection.$from.nodeAfter;
@@ -209,7 +222,7 @@ export class TooltipTextMenu {
if (DocumentManager.Instance.getDocumentView(f)) {
DocumentManager.Instance.getDocumentView(f)!.props.focus(f, false);
}
- else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f);
+ else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f, f);
}
}));
}
@@ -239,26 +252,10 @@ export class TooltipTextMenu {
hideSource: false
});
};
- this.linkEditor.appendChild(this.linkDrag);
- this.linkEditor.appendChild(this.linkText);
- this.linkEditor.appendChild(linkBtn);
- this.tooltip.appendChild(this.linkEditor);
-
- let starButton = document.createElement("span");
- // starButton.style.width = '10px';
- // starButton.style.height = '10px';
- starButton.style.marginLeft = '10px';
- starButton.textContent = "Summarize";
- starButton.style.color = 'black';
- starButton.style.height = '20px';
- starButton.style.backgroundColor = 'white';
- starButton.style.textAlign = 'center';
- starButton.onclick = () => {
- let state = this.view.state;
- this.insertStar(state, this.view.dispatch);
- };
-
- this.tooltip.appendChild(starButton);
+ // this.linkEditor.appendChild(this.linkDrag);
+ // this.linkEditor.appendChild(this.linkText);
+ // this.linkEditor.appendChild(linkBtn);
+ //this.tooltip.appendChild(this.linkEditor);
}
let node = this.view.state.selection.$from.nodeAfter;
@@ -286,16 +283,16 @@ export class TooltipTextMenu {
insertStar(state: EditorState<any>, dispatch: any) {
console.log("creating star...");
- let newNode = schema.nodes.star.create({ visibility: false, oldtext: state.selection.content(), oldtextslice: state.selection.content().toJSON(), oldtextlen: state.selection.to - state.selection.from });
+ let newNode = schema.nodes.star.create({ visibility: false, text: state.selection.content(), oldtextslice: state.selection.content().toJSON(), oldtextlen: state.selection.to - state.selection.from });
if (dispatch) {
- console.log(newNode.attrs.oldtext.toString());
+ //console.log(newNode.attrs.text.toString());
dispatch(state.tr.replaceSelectionWith(newNode));
}
return true;
}
//will display a remove-list-type button if selection is in list, otherwise will show list type dropdown
- updateListItemDropdown(label: string, listTypeBtn: Node) {
+ updateListItemDropdown(label: string, listTypeBtn: any) {
//remove old btn
if (listTypeBtn) { this.tooltip.removeChild(listTypeBtn); }
@@ -373,6 +370,21 @@ export class TooltipTextMenu {
});
}
+ createStar() {
+ return new MenuItem({
+ title: "Summarize",
+ label: "Summarize",
+ icon: icons.join,
+ css: "color:white;",
+ class: "summarize",
+ execEvent: "",
+ run: (state, dispatch, view) => {
+ this.insertStar(state, dispatch);
+ }
+
+ });
+ }
+
createLink() {
let markType = schema.marks.link;
return new MenuItem({
@@ -438,7 +450,7 @@ export class TooltipTextMenu {
span.className = name + " menuicon";
span.title = title;
span.textContent = text;
- span.style.color = "black";
+ span.style.color = "white";
return span;
}
@@ -502,34 +514,34 @@ export class TooltipTextMenu {
// Hide the tooltip if the selection is empty
if (state.selection.empty) {
- this.tooltip.style.display = "none";
- return;
+ //this.tooltip.style.display = "none";
+ //return;
}
- let linksInSelection = this.activeMarksOnSelection([schema.marks.link]);
- if (linksInSelection.length > 0) {
- let attributes = this.getMarksInSelection(this.view.state, [schema.marks.link])[0].attrs;
- this.link.href = attributes.href;
- this.link.textContent = attributes.title;
- this.link.style.visibility = "visible";
- } else this.link.style.visibility = "hidden";
+ //let linksInSelection = this.activeMarksOnSelection([schema.marks.link]);
+ // if (linksInSelection.length > 0) {
+ // let attributes = this.getMarksInSelection(this.view.state, [schema.marks.link])[0].attrs;
+ // this.link.href = attributes.href;
+ // this.link.textContent = attributes.title;
+ // this.link.style.visibility = "visible";
+ // } else this.link.style.visibility = "hidden";
// Otherwise, reposition it and update its content
- this.tooltip.style.display = "";
+ //this.tooltip.style.display = "";
let { from, to } = state.selection;
let start = view.coordsAtPos(from), end = view.coordsAtPos(to);
// The box in which the tooltip is positioned, to use as base
- let box = this.tooltip.offsetParent!.getBoundingClientRect();
+ //let box = this.tooltip.offsetParent!.getBoundingClientRect();
// Find a center-ish x position from the selection endpoints (when
// crossing lines, end may be more to the left)
let left = Math.max((start.left + end.left) / 2, start.left + 3);
- this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px";
+ //this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px";
let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale;
let mid = Math.min(start.left, end.left) + width;
//this.tooltip.style.width = 225 + "px";
- this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px";
- this.tooltip.style.top = "-100px";
+ // this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px";
+ // this.tooltip.style.top = "-100px";
//this.tooltip.style.height = "100px";
// let transform = this.editorProps.ScreenToLocalTransform();
@@ -573,17 +585,47 @@ export class TooltipTextMenu {
let { empty, $cursor, ranges } = this.view.state.selection as TextSelection;
let state = this.view.state;
let dispatch = this.view.dispatch;
-
- let activeMarks = markGroup.filter(mark => {
- if (dispatch) {
- let has = false, tr = state.tr;
- for (let i = 0; !has && i < ranges.length; i++) {
- let { $from, $to } = ranges[i];
- return state.doc.rangeHasMark($from.pos, $to.pos, mark);
+ let activeMarks: MarkType[];
+ if (!empty) {
+ activeMarks = markGroup.filter(mark => {
+ if (dispatch) {
+ let has = false, tr = state.tr;
+ for (let i = 0; !has && i < ranges.length; i++) {
+ let { $from, $to } = ranges[i];
+ return state.doc.rangeHasMark($from.pos, $to.pos, mark);
+ }
}
+ return false;
+ });
+ }
+ else {
+ let pos = this.view.state.selection.$from;
+ let ref_node: ProsNode;
+ if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) {
+ ref_node = pos.nodeAfter;
}
- return false;
- });
+ else if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) {
+ ref_node = pos.nodeBefore;
+ }
+ else {
+ return [];
+ }
+ let text_node_type: NodeType;
+ if (ref_node.isText) {
+ text_node_type = ref_node.type;
+ }
+ else {
+ return [];
+ }
+
+ activeMarks = markGroup.filter(mark_type => {
+ if (dispatch) {
+ let mark = state.schema.mark(mark_type);
+ return ref_node.marks.includes(mark);
+ }
+ return false;
+ });
+ }
return activeMarks;
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2c0e18bbb..557db3639 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -6,7 +6,8 @@ import { observer } from "mobx-react";
import { Doc } from "../../new_fields/Doc";
import { List } from "../../new_fields/List";
import { listSpec } from "../../new_fields/Schema";
-import { Cast, NumCast, StrCast, BoolCast } from "../../new_fields/Types";
+import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
+import { URLField } from '../../new_fields/URLField';
import { emptyFunction, Utils } from "../../Utils";
import { Docs } from "../documents/Documents";
import { DocumentManager } from "../util/DocumentManager";
@@ -24,7 +25,6 @@ import { LinkMenu } from "./nodes/LinkMenu";
import { TemplateMenu } from "./TemplateMenu";
import { Template, Templates } from "./Templates";
import React = require("react");
-import { URLField } from '../../new_fields/URLField';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -73,6 +73,33 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
if (text[0] === '#') {
this._fieldKey = text.slice(1, text.length);
this._title = this.selectionTitle;
+ } else if (text.startsWith(">")) {
+ let field = SelectionManager.SelectedDocuments()[0];
+ let collection = field.props.ContainingCollectionView!.props.Document;
+
+ let collectionKey = field.props.ContainingCollectionView!.props.fieldKey;
+ let collectionKeyProp = `fieldKey={"${collectionKey}"}`;
+ let metaKey = text.slice(1, text.length);
+ let metaKeyProp = `fieldKey={"${metaKey}"}`;
+
+ let template = Doc.MakeAlias(field.props.Document);
+ template.proto = collection;
+ template.title = metaKey;
+ template.nativeWidth = Cast(field.nativeWidth, "number");
+ template.nativeHeight = Cast(field.nativeHeight, "number");
+ template.embed = true;
+ template.isTemplate = true;
+ template.templates = new List<string>([Templates.TitleBar(metaKey)]);
+ if (field.props.Document.backgroundLayout) {
+ let metaAnoKeyProp = `fieldKey={"${metaKey}"} fieldExt={"annotations"}`;
+ let collectionAnoKeyProp = `fieldKey={"annotations"}`;
+ template.layout = StrCast(field.props.Document.layout).replace(collectionAnoKeyProp, metaAnoKeyProp);
+ template.backgroundLayout = StrCast(field.props.Document.backgroundLayout).replace(collectionKeyProp, metaKeyProp);
+ } else {
+ template.layout = StrCast(field.props.Document.layout).replace(collectionKeyProp, metaKeyProp);
+ }
+ Doc.AddDocToList(collection, collectionKey, template);
+ SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument && dv.props.removeDocument(dv.props.Document));
}
else {
if (SelectionManager.SelectedDocuments().length > 0) {
@@ -122,7 +149,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@computed
get Bounds(): { x: number, y: number, b: number, r: number } {
return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => {
- if (documentView.props.isTopMost) {
+ if (documentView.props.renderDepth === 0) {
return bounds;
}
let transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
@@ -149,7 +176,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let dragDocView = SelectionManager.SelectedDocuments()[0];
const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0);
const [xoff, yoff] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
- let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
+ let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document), SelectionManager.SelectedDocuments().map(dv => dv.props.DataDoc ? dv.props.DataDoc : dv.props.Document));
dragData.xOffset = xoff;
dragData.yOffset = yoff;
dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument;
@@ -468,9 +495,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let doc = PositionDocument(element.props.Document);
let nwidth = doc.nativeWidth || 0;
let nheight = doc.nativeHeight || 0;
- let zoomBasis = NumCast(doc.zoomBasis, 1);
- let width = (doc.width || 0) / zoomBasis;
- let height = (doc.height || (nheight / nwidth * width)) / zoomBasis;
+ let width = (doc.width || 0);
+ let height = (doc.height || (nheight / nwidth * width));
let scale = element.props.ScreenToLocalTransform().Scale;
let actualdW = Math.max(width + (dW * scale), 20);
let actualdH = Math.max(height + (dH * scale), 20);
@@ -485,25 +511,27 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
if (nwidth > 0 && nheight > 0) {
if (Math.abs(dW) > Math.abs(dH)) {
- if (!fixedAspect) proto.nativeWidth = zoomBasis * actualdW / (doc.width || 1) * NumCast(proto.nativeWidth);
- doc.width = zoomBasis * actualdW;
- // doc.zoomBasis = zoomBasis * width / actualdW;
+ if (!fixedAspect) {
+ Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true);
+ }
+ doc.width = actualdW;
if (fixedAspect) doc.height = nheight / nwidth * doc.width;
- else doc.height = zoomBasis * actualdH;
- proto.nativeHeight = (doc.height || 0) / doc.width * NumCast(proto.nativeWidth);
+ else doc.height = actualdH;
+ Doc.SetInPlace(element.props.Document, "nativeHeight", (doc.height || 0) / doc.width * (doc.nativeWidth || 0), true);
}
else {
- if (!fixedAspect) proto.nativeHeight = zoomBasis * actualdH / (doc.height || 1) * NumCast(proto.nativeHeight);
- doc.height = zoomBasis * actualdH;
- //doc.zoomBasis = zoomBasis * height / actualdH;
+ if (!fixedAspect) {
+ Doc.SetInPlace(element.props.Document, "nativeHeight", actualdH / (doc.height || 1) * (doc.nativeHeight || 0), true);
+ }
+ doc.height = actualdH;
if (fixedAspect) doc.width = nwidth / nheight * doc.height;
- else doc.width = zoomBasis * actualdW;
- proto.nativeWidth = (doc.width || 0) / doc.height * NumCast(proto.nativeHeight);
+ else doc.width = actualdW;
+ Doc.SetInPlace(element.props.Document, "nativeWidth", (doc.width || 0) / doc.height * (doc.nativeHeight || 0), true);
}
} else {
- dW && (doc.width = zoomBasis * actualdW);
- dH && (doc.height = zoomBasis * actualdH);
- proto.autoHeight = undefined;
+ dW && (doc.width = actualdW);
+ dH && (doc.height = actualdH);
+ Doc.SetInPlace(element.props.Document, "autoHeight", undefined, true);
}
}
});
@@ -617,7 +645,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
left: bounds.x - this._resizeBorderWidth / 2,
top: bounds.y - this._resizeBorderWidth / 2,
pointerEvents: this.Interacting ? "none" : "all",
- zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0,
+ zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0,
}} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
</div>
<div className="documentDecorations-container" style={{
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 5d4ea76cd..fd7e5b07d 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -14,6 +14,7 @@ import { Cast, PromiseValue, NumCast } from "../../new_fields/Types";
interface InkCanvasProps {
getScreenTransform: () => Transform;
Document: Doc;
+ inkFieldKey: string;
children: () => JSX.Element[];
}
@@ -40,7 +41,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
}
componentDidMount() {
- PromiseValue(Cast(this.props.Document.ink, InkField)).then(ink => runInAction(() => {
+ PromiseValue(Cast(this.props.Document[this.props.inkFieldKey], InkField)).then(ink => runInAction(() => {
if (ink) {
let bounds = Array.from(ink.inkData).reduce(([mix, max, miy, may], [id, strokeData]) =>
strokeData.pathData.reduce(([mix, max, miy, may], p) =>
@@ -55,12 +56,12 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
@computed
get inkData(): Map<string, StrokeData> {
- let map = Cast(this.props.Document.ink, InkField);
+ let map = Cast(this.props.Document[this.props.inkFieldKey], InkField);
return !map ? new Map : new Map(map.inkData);
}
set inkData(value: Map<string, StrokeData>) {
- Doc.GetProto(this.props.Document).ink = new InkField(value);
+ Doc.GetProto(this.props.Document)[this.props.inkFieldKey] = new InkField(value);
}
@action
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 0837e07a9..18128f72c 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -8,6 +8,7 @@ import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-s
import { SelectionManager } from "../util/SelectionManager";
import { InkTool } from "../../new_fields/InkField";
import { Doc } from "../../new_fields/Doc";
+import { InkingCanvas } from "./InkingCanvas";
library.add(faPen, faHighlighter, faEraser, faBan);
@@ -39,7 +40,7 @@ export class InkingControl extends React.Component {
@action
switchColor = (color: ColorResult): void => {
this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
- SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = this._selectedColor);
+ if (InkingControl.Instance.selectedTool === InkTool.None) SelectionManager.SelectedDocuments().forEach(doc => (doc.props.Document.isTemplate ? doc.props.Document : Doc.GetProto(doc.props.Document)).backgroundColor = this._selectedColor);
}
@action
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 690139341..0271edcd2 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -20,15 +20,7 @@ div {
-ms-user-select: none;
}
-#dash-title {
- position: absolute;
- right: 46.5%;
- letter-spacing: 3px;
- top: 9px;
- font-size: 12px;
- color: $alt-accent;
- z-index: 9999;
-}
+
.jsx-parser {
width: 100%;
@@ -43,8 +35,8 @@ p {
::-webkit-scrollbar {
-webkit-appearance: none;
- height: 10px;
- width: 10px;
+ height: 8px;
+ width: 8px;
}
::-webkit-scrollbar-thumb {
@@ -200,7 +192,7 @@ button:hover {
position: absolute;
top: 0;
left: 0;
- overflow: scroll;
+ overflow: auto;
z-index: 1;
}
@@ -210,6 +202,7 @@ button:hover {
position: absolute;
top: 0;
left: 0;
+ overflow: hidden;
}
#add-options-content {
diff --git a/src/client/views/MainOverlayTextBox.scss b/src/client/views/MainOverlayTextBox.scss
index f6a746e63..f636ca070 100644
--- a/src/client/views/MainOverlayTextBox.scss
+++ b/src/client/views/MainOverlayTextBox.scss
@@ -1,7 +1,7 @@
@import "globalCssVariables";
.mainOverlayTextBox-textInput {
background-color: rgba(248, 6, 6, 0.001);
- width: 200px;
+ width: 400px;
height: 200px;
position:absolute;
overflow: visible;
@@ -17,4 +17,9 @@
top: 0;
left: 0;
}
+}
+.mainOverlayTextBox-.unscaled_div{
+ z-index: 10000;
+ position: absolute;
+ pointer-events: none;
} \ No newline at end of file
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index b4ad5f4d7..b5680bd68 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -24,9 +24,11 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
private _textHideOnLeave?: boolean;
private _textTargetDiv: HTMLDivElement | undefined;
private _textProxyDiv: React.RefObject<HTMLDivElement>;
+ private _outerdiv = (dominus: HTMLElement | null) => this._dominus && dominus && dominus.appendChild(this._dominus);
private _textBottom: boolean | undefined;
private _textAutoHeight: boolean | undefined;
private _textBox: FormattedTextBox | undefined;
+ private _dominus?: HTMLElement;
@observable public TextDoc?: Doc;
constructor(props: MainOverlayTextBoxProps) {
@@ -37,7 +39,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
(box?: FormattedTextBox) => {
this._textBox = box;
if (box) {
- this.TextDoc = box.props.Document;
+ this.TextDoc = box.props.DataDoc;
let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined);
let xf = () => { box.props.ScreenToLocalTransform(); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); };
this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content");
@@ -82,10 +84,10 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
}
@action
textBoxMove = (e: PointerEvent) => {
- if (e.movementX > 1 || e.movementY > 1) {
+ if ((e.movementX > 1 || e.movementY > 1) && FormattedTextBox.InputBoxOverlay) {
document.removeEventListener("pointermove", this.textBoxMove);
document.removeEventListener('pointerup', this.textBoxUp);
- let dragData = new DragManager.DocumentDragData(FormattedTextBox.InputBoxOverlay ? [FormattedTextBox.InputBoxOverlay.props.Document] : []);
+ let dragData = new DragManager.DocumentDragData([FormattedTextBox.InputBoxOverlay.props.Document], [FormattedTextBox.InputBoxOverlay.props.DataDoc]);
const [left, top] = this._textXf().inverse().transformPoint(0, 0);
dragData.xOffset = e.clientX - left;
dragData.yOffset = e.clientY - top;
@@ -102,9 +104,9 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
document.removeEventListener('pointerup', this.textBoxUp);
}
- addDocTab = (doc: Doc, location: string) => {
+ addDocTab = (doc: Doc, dataDoc: Doc, location: string) => {
if (true) { // location === "onRight") { need to figure out stack to add "inTab"
- CollectionDockingView.Instance.AddRightSplit(doc);
+ CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
}
}
render() {
@@ -114,14 +116,18 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
let s = this._textXf().Scale;
let location = this._textBottom ? textRect.bottom : textRect.top;
let hgt = this._textAutoHeight || this._textBottom ? "auto" : this._textTargetDiv.clientHeight;
- return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${textRect.left}px, ${location}px) scale(${1 / s},${1 / s})`, width: "auto", height: "0px" }} >
- <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
- style={{ width: `${textRect.width * s}px`, height: "0px" }}>
- <div style={{ height: hgt, width: "100%", position: "absolute", bottom: this._textBottom ? "0px" : undefined }}>
- <FormattedTextBox color={`${this._textColor}`} fieldKey={this.TextFieldKey} hideOnLeave={this._textHideOnLeave} isOverlay={true} Document={FormattedTextBox.InputBoxOverlay.props.Document}
- isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true}
- ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
- ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} />
+ return <div ref={this._outerdiv} className="mainOverlayTextBox-unscaled_div" style={{ transform: `translate(${textRect.left}px, ${location}px)` }} >
+ <div className="mainOverlayTextBox-textInput" style={{ transform: `scale(${1 / s},${1 / s})`, width: "auto", height: "0px" }} >
+ <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
+ style={{ width: `${textRect.width * s}px`, height: "0px" }}>
+ <div style={{ height: hgt, width: "100%", position: "absolute", bottom: this._textBottom ? "0px" : undefined }}>
+ <FormattedTextBox color={`${this._textColor}`} fieldKey={this.TextFieldKey} fieldExt="" hideOnLeave={this._textHideOnLeave} isOverlay={true}
+ Document={FormattedTextBox.InputBoxOverlay.props.Document}
+ DataDoc={FormattedTextBox.InputBoxOverlay.props.DataDoc}
+ isSelected={returnTrue} select={emptyFunction} renderDepth={0} selectOnLoad={true}
+ ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
+ ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} outer_div={(dominus: HTMLElement) => this._dominus = dominus} />
+ </div>
</div>
</div>
</ div>;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 10feaa79c..aeaafab67 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -144,7 +144,7 @@ export class MainView extends React.Component {
const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc));
if (list) {
let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` });
- var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] };
+ var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }, id);
list.push(mainDoc);
// bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
@@ -177,7 +177,7 @@ export class MainView extends React.Component {
openNotifsCol = () => {
if (this._notifsCol && CollectionDockingView.Instance) {
- CollectionDockingView.Instance.AddRightSplit(this._notifsCol);
+ CollectionDockingView.Instance.AddRightSplit(this._notifsCol, this._notifsCol);
}
}
@@ -203,6 +203,7 @@ export class MainView extends React.Component {
let mainCont = this.mainContainer;
let content = !mainCont ? (null) :
<DocumentView Document={mainCont}
+ DataDoc={undefined}
addDocument={undefined}
addDocTab={emptyFunction}
removeDocument={undefined}
@@ -210,7 +211,7 @@ export class MainView extends React.Component {
ContentScaling={returnOne}
PanelWidth={this.getPWidth}
PanelHeight={this.getPHeight}
- isTopMost={true}
+ renderDepth={0}
selectOnLoad={false}
focus={emptyFunction}
parentActive={returnTrue}
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 3d5f7b6ea..5bb8d454a 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -49,8 +49,8 @@ export namespace Templates {
export const Title = new Template("Title", TemplatePosition.InnerTop,
`<div>
- <div style="height:25px; width:100%; 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 style="height:25px; width:100%; background-color: rgba(0, 0, 0, .4); color: white; z-index: 100">
+ <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">{props.DataDoc.title}</span>
</div>
<div style="height:calc(100% - 25px);">
<div style="width:100%;overflow:auto">{layout}</div>
@@ -84,6 +84,16 @@ export namespace Templates {
</div > `);
}
+ export function TitleBar(datastring: string) {
+ return (`<div>
+ <div style="height:25px; width:100%; background-color: rgba(0, 0, 0, .4); color: white; z-index: 100">
+ <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">${datastring}</span>
+ </div>
+ <div style="height:calc(100% - 25px);">
+ <div style="width:100%;overflow:auto">{layout}</div>
+ </div>
+ </div>` );
+ }
export const TemplateList: Template[] = [Title, Header, Caption, Bullet];
export function sortTemplates(a: Template, b: Template) {
diff --git a/src/client/views/_nodeModuleOverrides.scss b/src/client/views/_nodeModuleOverrides.scss
index 6f97e60f8..3594ac9f4 100644
--- a/src/client/views/_nodeModuleOverrides.scss
+++ b/src/client/views/_nodeModuleOverrides.scss
@@ -3,7 +3,6 @@
// goldenlayout stuff
div .lm_header {
background: $dark-color;
- min-height: 2em;
}
.lm_tab {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index ae77ef42d..879898018 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -1,16 +1,16 @@
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast, Opt } from '../../../new_fields/Doc';
+import { Doc } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { listSpec } from '../../../new_fields/Schema';
-import { Cast, FieldValue, NumCast, PromiseValue } from '../../../new_fields/Types';
+import { BoolCast, Cast, NumCast, PromiseValue } from '../../../new_fields/Types';
+import { DocumentManager } from '../../util/DocumentManager';
import { SelectionManager } from '../../util/SelectionManager';
import { ContextMenu } from '../ContextMenu';
import { FieldViewProps } from '../nodes/FieldView';
import './CollectionBaseView.scss';
-import { DocumentManager } from '../../util/DocumentManager';
export enum CollectionViewType {
Invalid,
@@ -36,7 +36,6 @@ export interface CollectionViewProps extends FieldViewProps {
contentRef?: React.Ref<HTMLDivElement>;
}
-
@observer
export class CollectionBaseView extends React.Component<CollectionViewProps> {
@observable private static _safeMode = false;
@@ -60,10 +59,12 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
}
}
+ @computed get dataDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc ? this.props.DataDoc : this.props.Document : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
+ @computed get dataField() { return this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; }
+
active = (): boolean => {
var isSelected = this.props.isSelected();
- var topMost = this.props.isTopMost;
- return isSelected || this._isChildActive || topMost;
+ return isSelected || this._isChildActive || this.props.renderDepth === 0;
}
//TODO should this be observable?
@@ -73,57 +74,21 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
this.props.whenActiveChanged(isActive);
}
- createsCycle(documentToAdd: Doc, containerDocument: Doc): boolean {
- if (!(documentToAdd instanceof Doc)) {
- return false;
- }
- let data = DocListCast(documentToAdd.data);
- for (const doc of data) {
- if (this.createsCycle(doc, containerDocument)) {
- return true;
- }
- }
- let annots = DocListCast(documentToAdd.annotations);
- for (const annot of annots) {
- if (this.createsCycle(annot, containerDocument)) {
- return true;
- }
- }
- for (let containerProto: Opt<Doc> = containerDocument; containerProto !== undefined; containerProto = FieldValue(containerProto.proto)) {
- if (containerProto[Id] === documentToAdd[Id]) {
- return true;
- }
- }
- return false;
- }
- @computed get isAnnotationOverlay() { return this.props.fieldKey === "annotations"; }
-
@action.bound
addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
- let props = this.props;
- var curPage = NumCast(props.Document.curPage, -1);
+ var curPage = NumCast(this.props.Document.curPage, -1);
Doc.GetProto(doc).page = curPage;
if (curPage >= 0) {
- Doc.GetProto(doc).annotationOn = props.Document;
+ Doc.GetProto(doc).annotationOn = this.props.Document;
}
- 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 instanceof Doc && v[Id] === doc[Id])) {
- alreadyAdded = false;
- value.push(doc);
- }
- } else {
- 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 (!alreadyAdded && (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid)) {
- let zoom = NumCast(this.props.Document.scale, 1);
- // Doc.GetProto(doc).zoomBasis = zoom;
+ allowDuplicates = true;
+ const value = Cast(this.dataDoc[this.dataField], listSpec(Doc));
+ if (value !== undefined) {
+ if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) {
+ value.push(doc);
}
+ } else {
+ Doc.GetProto(this.dataDoc)[this.dataField] = new List([doc]);
}
return true;
}
@@ -132,22 +97,12 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
removeDocument(doc: Doc): boolean {
let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
docView && SelectionManager.DeselectDoc(docView);
- const props = this.props;
//TODO This won't create the field if it doesn't already exist
- const value = Cast(props.Document[props.fieldKey], listSpec(Doc), []);
- let index = -1;
- for (let i = 0; i < value.length; i++) {
- let v = value[i];
- if (v instanceof Doc && v[Id] === doc[Id]) {
- index = i;
- break;
- }
- }
- PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => {
- if (annotationOn === props.Document) {
- doc.annotationOn = undefined;
- }
- });
+ const value = Cast(this.dataDoc[this.dataField], listSpec(Doc), []);
+ let index = value.reduce((p, v, i) => (v instanceof Doc && v[Id] === doc[Id]) ? i : p, -1);
+ PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn =>
+ annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined)
+ );
if (index !== -1) {
value.splice(index, 1);
@@ -161,7 +116,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
@action.bound
moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
- if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
+ if (Doc.AreProtosEqual(this.dataDoc, targetCollection)) {
return true;
}
if (this.removeDocument(doc)) {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index dd99649b8..324096ed7 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -32,7 +32,7 @@ library.add(faFile);
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
public static Instance: CollectionDockingView;
- public static makeDocumentConfig(document: Doc, width?: number) {
+ public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number) {
return {
type: 'react-component',
component: 'DocumentFrameRenderer',
@@ -40,6 +40,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
width: width,
props: {
documentId: document[Id],
+ dataDocumentId: dataDoc ? dataDoc[Id] : ""
//collectionDockingView: CollectionDockingView.Instance
}
};
@@ -60,19 +61,19 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
hack: boolean = false;
undohack: any = null;
- public StartOtherDrag(dragDocs: Doc[], e: any) {
+ public StartOtherDrag(e: any, dragDocs: Doc[], dragDataDocs?: (Doc | undefined)[]) {
this.hack = true;
this.undohack = UndoManager.StartBatch("goldenDrag");
- dragDocs.map(dragDoc =>
- this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.
+ dragDocs.map((dragDoc, i) =>
+ this.AddRightSplit(dragDoc, dragDataDocs ? dragDataDocs[i] : dragDoc, true).contentItems[0].tab._dragListener.
onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
}
@action
- public OpenFullScreen(document: Doc) {
+ public OpenFullScreen(document: Doc, dataDoc: Doc) {
let newItemStackConfig = {
type: 'stack',
- content: [CollectionDockingView.makeDocumentConfig(document)]
+ content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
};
var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
this._goldenLayout.root.contentItems[0].addChild(docconfig);
@@ -127,14 +128,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
@action
- public AddRightSplit = (document: Doc, minimize: boolean = false) => {
+ public AddRightSplit = (document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) => {
let docs = Cast(this.props.Document.data, listSpec(Doc));
if (docs) {
docs.push(document);
}
let newItemStackConfig = {
type: 'stack',
- content: [CollectionDockingView.makeDocumentConfig(document)]
+ content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
};
var newContentItem = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
@@ -165,12 +166,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
return newContentItem;
}
@action
- public AddTab = (stack: any, document: Doc) => {
+ public AddTab = (stack: any, document: Doc, dataDocument: Doc) => {
let docs = Cast(this.props.Document.data, listSpec(Doc));
if (docs) {
docs.push(document);
}
- let docContentConfig = CollectionDockingView.makeDocumentConfig(document);
+ let docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument);
var newContentItem = stack.layoutManager.createContentItem(docContentConfig, this._goldenLayout);
stack.addChild(newContentItem.contentItems[0], undefined);
this.layoutChanged();
@@ -286,14 +287,16 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
let x = e.clientX;
let y = e.clientY;
let docid = (e.target as any).DashDocId;
+ let datadocid = (e.target as any).DashDataDocId;
let tab = (e.target as any).parentElement as HTMLElement;
let glTab = (e.target as any).Tab;
if (glTab && glTab.contentItem && glTab.contentItem.parent) {
glTab.contentItem.parent.setActiveContentItem(glTab.contentItem);
}
- DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
+ DocServer.GetRefField(docid).then(action(async (f: Opt<Field>) => {
if (f instanceof Doc) {
- DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y,
+ let dataDoc = (datadocid !== docid) ? await DocServer.GetRefField(datadocid) : f;
+ DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f], [dataDoc instanceof Doc ? dataDoc : f]), x, y,
{
handlers: {
dragComplete: emptyFunction,
@@ -344,45 +347,45 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
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 dragSpan = document.createElement("span");
- dragSpan.style.position = "relative";
- dragSpan.style.bottom = "6px";
- dragSpan.style.paddingLeft = "4px";
- dragSpan.style.paddingRight = "2px";
- let upDiv = document.createElement("span");
- const stack = tab.contentItem.parent;
- // shifts the focus to this tab when another tab is dragged over it
- tab.element[0].onmouseenter = (e: any) => {
- if (!this._isPointerDown) return;
- var activeContentItem = tab.header.parent.getActiveContentItem();
- if (tab.contentItem !== activeContentItem) {
- tab.header.parent.setActiveContentItem(tab.contentItem);
- }
- tab.setActive(true);
- };
- ReactDOM.render(<span onPointerDown={
- e => {
- e.preventDefault();
- e.stopPropagation();
- DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
- handlers: { dragComplete: emptyFunction },
- hideSource: false
- });
- }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
- ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={doc => CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv);
- tab.reactComponents = [dragSpan, upDiv];
- tab.element.append(dragSpan);
- tab.element.append(upDiv);
- tab.reactionDisposer = reaction(() => [doc.title],
- () => {
- tab.titleElement[0].textContent = doc.title;
- }, { fireImmediately: true });
- //TODO why can't this just be doc instead of the id?
- tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
- }
- });
+ let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
+ let dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
+ if (doc instanceof Doc && dataDoc instanceof Doc) {
+ let dragSpan = document.createElement("span");
+ dragSpan.style.position = "relative";
+ dragSpan.style.bottom = "6px";
+ dragSpan.style.paddingLeft = "4px";
+ dragSpan.style.paddingRight = "2px";
+ let upDiv = document.createElement("span");
+ const stack = tab.contentItem.parent;
+ // shifts the focus to this tab when another tab is dragged over it
+ tab.element[0].onmouseenter = (e: any) => {
+ if (!this._isPointerDown) return;
+ var activeContentItem = tab.header.parent.getActiveContentItem();
+ if (tab.contentItem !== activeContentItem) {
+ tab.header.parent.setActiveContentItem(tab.contentItem);
+ }
+ tab.setActive(true);
+ };
+ ReactDOM.render(<span onPointerDown={
+ e => {
+ e.preventDefault();
+ e.stopPropagation();
+ DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc], [dataDoc]), e.clientX, e.clientY, {
+ handlers: { dragComplete: emptyFunction },
+ hideSource: false
+ });
+ }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
+ ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={doc => CollectionDockingView.Instance.AddTab(stack, doc, dataDoc)} />, upDiv);
+ tab.reactComponents = [dragSpan, upDiv];
+ tab.element.append(dragSpan);
+ tab.element.append(upDiv);
+ tab.reactionDisposer = reaction(() => [doc.title],
+ () => {
+ tab.titleElement[0].textContent = doc.title;
+ }, { fireImmediately: true });
+ //TODO why can't this just be doc instead of the id?
+ tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
+ }
}
tab.titleElement[0].Tab = tab;
tab.closeElement.off('click') //unbind the current click handler
@@ -445,6 +448,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
interface DockedFrameProps {
documentId: FieldId;
+ dataDocumentId: FieldId;
//collectionDockingView: CollectionDockingView
}
@observer
@@ -453,6 +457,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
@observable private _document: Opt<Doc>;
+ @observable private _dataDoc: Opt<Doc>;
get _stack(): any {
let parent = (this.props as any).glContainer.parent.parent;
if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) {
@@ -462,7 +467,12 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
constructor(props: any) {
super(props);
- DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => this._document = f as Doc));
+ DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => {
+ this._dataDoc = this._document = f as Doc;
+ if (this.props.dataDocumentId && this.props.documentId !== this.props.dataDocumentId) {
+ DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt<Field>) => this._dataDoc = f as Doc));
+ }
+ }));
}
nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth);
@@ -499,23 +509,25 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; }
- addDocTab = (doc: Doc, location: string) => {
+ addDocTab = (doc: Doc, dataDoc: Doc, location: string) => {
if (doc.dockingConfig) {
MainView.Instance.openWorkspace(doc);
} else if (location === "onRight") {
- CollectionDockingView.Instance.AddRightSplit(doc);
+ CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
} else {
- CollectionDockingView.Instance.AddTab(this._stack, doc);
+ CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
}
}
get content() {
- if (!this._document) {
+ if (!this._document || !this._dataDoc) {
return (null);
}
return (
<div className="collectionDockingView-content" ref={this._mainCont}
- style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier}, ${this.scaleToFitMultiplier})` }}>
- <DocumentView key={this._document[Id]} Document={this._document}
+ style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier})` }}>
+ <DocumentView key={this._document[Id]}
+ Document={this._document}
+ DataDoc={this._dataDoc}
bringToFront={emptyFunction}
addDocument={undefined}
removeDocument={undefined}
@@ -523,7 +535,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
PanelWidth={this.nativeWidth}
PanelHeight={this.nativeHeight}
ScreenToLocalTransform={this.ScreenToLocalTransform}
- isTopMost={true}
+ renderDepth={0}
selectOnLoad={false}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 97781f722..98bf513bb 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -11,10 +11,10 @@ import { Doc, DocListCast, DocListCastAsync, Field } from "../../../new_fields/D
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
+import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
import { Docs } from "../../documents/Documents";
import { Gateway } from "../../northstar/manager/Gateway";
-import { SetupDrag } from "../../util/DragManager";
+import { SetupDrag, DragManager } from "../../util/DragManager";
import { CompileScript } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
import { COLLECTION_BORDER_WIDTH, MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss';
@@ -29,6 +29,8 @@ import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
import { CollectionVideoView } from "./CollectionVideoView";
import { CollectionView } from "./CollectionView";
+import { undoBatch } from "../../util/UndoManager";
+import { timesSeries } from "async";
library.add(faCog);
@@ -98,11 +100,13 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
renderCell = (rowProps: CellInfo) => {
let props: FieldViewProps = {
Document: rowProps.original,
+ DataDoc: rowProps.original,
fieldKey: rowProps.column.id as string,
+ fieldExt: "",
ContainingCollectionView: this.props.CollectionView,
isSelected: returnFalse,
select: emptyFunction,
- isTopMost: false,
+ renderDepth: this.props.renderDepth + 1,
selectOnLoad: false,
ScreenToLocalTransform: Transform.Identity,
focus: emptyFunction,
@@ -114,9 +118,10 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
};
let fieldContentView = <FieldView {...props} />;
let reference = React.createRef<HTMLDivElement>();
- let onItemDown = (e: React.PointerEvent) =>
+ let onItemDown = (e: React.PointerEvent) => {
(this.props.CollectionView.props.isSelected() ?
SetupDrag(reference, () => props.Document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e) : undefined);
+ };
let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => {
const res = run({ this: doc });
if (!res.success) return false;
@@ -280,9 +285,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@computed
get previewDocument(): Doc | undefined {
- const children = DocListCast(this.props.Document[this.props.fieldKey]);
- const selected = children.length > this._selectedIndex ? FieldValue(children[this._selectedIndex]) : undefined;
- return selected ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
+ const selected = this.childDocs.length > this._selectedIndex ? this.childDocs[this._selectedIndex] : undefined;
+ let pdc = selected ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
+ return pdc;
}
getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(
@@ -346,22 +351,26 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@computed
get previewPanel() {
- trace();
- return <CollectionSchemaPreview
- Document={this.previewDocument}
- width={this.previewWidth}
- height={this.previewHeight}
- getTransform={this.getPreviewTransform}
- CollectionView={this.props.CollectionView}
- moveDocument={this.props.moveDocument}
- addDocument={this.props.addDocument}
- removeDocument={this.props.removeDocument}
- active={this.props.active}
- whenActiveChanged={this.props.whenActiveChanged}
- addDocTab={this.props.addDocTab}
- setPreviewScript={this.setPreviewScript}
- previewScript={this.previewScript}
- />;
+ return <div ref={this.createTarget}>
+ <CollectionSchemaPreview
+ Document={this.previewDocument}
+ DataDocument={BoolCast(this.props.Document.isTemplate) ? this.previewDocument : this.props.DataDoc}
+ childDocs={this.childDocs}
+ renderDepth={this.props.renderDepth}
+ width={this.previewWidth}
+ height={this.previewHeight}
+ getTransform={this.getPreviewTransform}
+ CollectionView={this.props.CollectionView}
+ moveDocument={this.props.moveDocument}
+ addDocument={this.props.addDocument}
+ removeDocument={this.props.removeDocument}
+ active={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ addDocTab={this.props.addDocTab}
+ setPreviewScript={this.setPreviewScript}
+ previewScript={this.previewScript}
+ />
+ </div>;
}
@action
setPreviewScript = (script: string) => {
@@ -383,6 +392,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
interface CollectionSchemaPreviewProps {
Document?: Doc;
+ DataDocument?: Doc;
+ childDocs?: Doc[];
+ renderDepth: number;
width: () => number;
height: () => number;
CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView;
@@ -392,13 +404,15 @@ interface CollectionSchemaPreviewProps {
removeDocument: (document: Doc) => boolean;
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
- addDocTab: (document: Doc, where: string) => void;
+ addDocTab: (document: Doc, dataDoc: Doc, where: string) => void;
setPreviewScript: (script: string) => void;
previewScript?: string;
}
@observer
export class CollectionSchemaPreview extends React.Component<CollectionSchemaPreviewProps>{
+ private dropDisposer?: DragManager.DragDropDisposer;
+ _mainCont?: HTMLDivElement;
private get nativeWidth() { return NumCast(this.props.Document!.nativeWidth, this.props.width()); }
private get nativeHeight() { return NumCast(this.props.Document!.nativeHeight, this.props.height()); }
private contentScaling = () => {
@@ -408,26 +422,55 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
}
return wscale;
}
- private PanelWidth = () => this.nativeWidth * this.contentScaling();
- private PanelHeight = () => this.nativeHeight * this.contentScaling();
+ protected createDropTarget = (ele: HTMLDivElement) => {
+ }
+ private createTarget = (ele: HTMLDivElement) => {
+ this._mainCont = ele;
+ this.dropDisposer && this.dropDisposer();
+ if (ele) {
+ this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ }
+ }
+
+ @undoBatch
+ @action
+ drop = (e: Event, de: DragManager.DropEvent) => {
+ if (de.data instanceof DragManager.DocumentDragData) {
+ let docDrag = de.data;
+ this.props.childDocs && this.props.childDocs.map(otherdoc => {
+ Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(docDrag.draggedDocuments[0]);
+ });
+ e.stopPropagation();
+ }
+ return true;
+ }
+ private PanelWidth = () => this.nativeWidth ? this.nativeWidth * this.contentScaling() : this.props.width();
+ private PanelHeight = () => this.nativeHeight ? this.nativeHeight * this.contentScaling() : this.props.height();
private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling());
- get centeringOffset() { return (this.props.width() - this.nativeWidth * this.contentScaling()) / 2; }
+ get centeringOffset() { return this.nativeWidth ? (this.props.width() - this.nativeWidth * this.contentScaling()) / 2 : 0; }
@action
onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.props.setPreviewScript(e.currentTarget.value);
}
render() {
let input = this.props.previewScript === undefined ? (null) :
- <input className="collectionSchemaView-input" value={this.props.previewScript} onChange={this.onPreviewScriptChange}
- style={{ left: `calc(50% - ${Math.min(75, (this.props.Document ? this.PanelWidth() / 2 : 75))}px)` }} />;
+ <div ref={this.createTarget}><input className="collectionSchemaView-input" value={this.props.previewScript} onChange={this.onPreviewScriptChange}
+ style={{ left: `calc(50% - ${Math.min(75, (this.props.Document ? this.PanelWidth() / 2 : 75))}px)` }} /></div>;
return (<div className="collectionSchemaView-previewRegion" style={{ width: this.props.width(), height: "100%" }}>
{!this.props.Document || !this.props.width ? (null) : (
<div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.centeringOffset}px, 0px)`, height: "100%" }}>
- <DocumentView Document={this.props.Document} isTopMost={false} selectOnLoad={false}
- addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} moveDocument={this.props.moveDocument}
+ <DocumentView
+ DataDoc={this.props.Document.layout instanceof Doc ? this.props.Document : this.props.DataDocument}
+ Document={this.props.Document}
+ renderDepth={this.props.renderDepth + 1}
+ selectOnLoad={false}
+ addDocument={this.props.addDocument}
+ removeDocument={this.props.removeDocument}
+ moveDocument={this.props.moveDocument}
ScreenToLocalTransform={this.getTransform}
ContentScaling={this.contentScaling}
- PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight}
+ PanelWidth={this.PanelWidth}
+ PanelHeight={this.PanelHeight}
ContainingCollectionView={this.props.CollectionView}
focus={emptyFunction}
parentActive={this.props.active}
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 485ecf1de..034a09eaa 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -39,6 +39,13 @@
color: $light-color;
}
+ .collectionStackingView-columnDragger {
+ width: 15;
+ height: 15;
+ position: absolute;
+ margin-left: -5;
+ }
+
.collectionStackingView-columnDoc,
.collectionStackingView-masonryDoc {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 597e4795c..6c4ea18a1 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,20 +1,20 @@
import React = require("react");
-import { action, computed, IReactionDisposer, reaction, trace } from "mobx";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { BoolCast, NumCast } from "../../../new_fields/Types";
-import { emptyFunction, returnOne, Utils } from "../../../Utils";
+import { emptyFunction, Utils } from "../../../Utils";
import { ContextMenu } from "../ContextMenu";
-import { DocumentView } from "../nodes/DocumentView";
import { CollectionSchemaPreview } from "./CollectionSchemaView";
import "./CollectionStackingView.scss";
import { CollectionSubView } from "./CollectionSubView";
-import { Transform } from "../../util/Transform";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
_masonryGridRef: HTMLDivElement | null = null;
+ _draggerRef = React.createRef<HTMLDivElement>();
_heightDisposer?: IReactionDisposer;
_gridSize = 1;
@computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); }
@@ -76,6 +76,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
style={{ width: width(), height: height() }} >
<CollectionSchemaPreview
Document={d}
+ DataDocument={this.props.Document.layout instanceof Doc ? this.props.Document : this.props.DataDoc}
+ renderDepth={this.props.renderDepth}
width={width}
height={height}
getTransform={dxf}
@@ -107,6 +109,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
style={{ gridRowEnd: `span ${rowSpan}` }} >
<CollectionSchemaPreview
Document={d}
+ DataDocument={this.props.Document.layout instanceof Doc ? this.props.Document : this.props.DataDoc}
+ renderDepth={this.props.renderDepth}
CollectionView={this.props.CollectionView}
addDocument={this.props.addDocument}
moveDocument={this.props.moveDocument}
@@ -123,6 +127,35 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
</div>);
});
}
+
+ _columnStart: number = 0;
+ columnDividerDown = (e: React.PointerEvent) => {
+ e.stopPropagation();
+ e.preventDefault();
+ document.addEventListener("pointermove", this.onDividerMove);
+ document.addEventListener('pointerup', this.onDividerUp);
+ this._columnStart = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+ }
+ @action
+ onDividerMove = (e: PointerEvent): void => {
+ let dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+ let delta = dragPos - this._columnStart;
+ this._columnStart = dragPos;
+
+ this.props.Document.columnWidth = this.columnWidth + delta;
+ }
+
+ @action
+ onDividerUp = (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onDividerMove);
+ document.removeEventListener('pointerup', this.onDividerUp);
+ }
+
+ @computed get columnDragger() {
+ return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ left: `${this.columnWidth + this.xMargin}px` }} >
+ <FontAwesomeIcon icon={"caret-down"} />
+ </div>;
+ }
onContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
ContextMenu.Instance.addItem({
@@ -139,7 +172,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
return (
<div className="collectionStackingView" ref={this.createRef} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
<div className={`collectionStackingView-masonry${this.singleColumn ? "Single" : "Grid"}`}
-
style={{
padding: this.singleColumn ? `${this.yMargin}px ${this.xMargin}px ${this.yMargin}px ${this.xMargin}px` : `${this.yMargin}px ${this.xMargin}px`,
margin: "auto",
@@ -152,6 +184,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}}
>
{this.singleColumn ? this.singleColumnChildren : this.children}
+ {this.singleColumn ? (null) : this.columnDragger}
</div>
</div>
);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 699bddc7c..3b3bbdbd9 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,10 +1,11 @@
-import { action } from "mobx";
+import { action, computed } from "mobx";
import * as rp from 'request-promise';
import CursorField from "../../../new_fields/CursorField";
import { Doc, DocListCast, Opt } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
-import { Cast, PromiseValue } from "../../../new_fields/Types";
+import { BoolCast, Cast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { RouteStore } from "../../../server/RouteStore";
import { DocServer } from "../../DocServer";
@@ -13,12 +14,11 @@ import { DragManager } from "../../util/DragManager";
import { undoBatch, UndoManager } from "../../util/UndoManager";
import { DocComponent } from "../DocComponent";
import { FieldViewProps } from "../nodes/FieldView";
+import { FormattedTextBox } from "../nodes/FormattedTextBox";
import { CollectionPDFView } from "./CollectionPDFView";
import { CollectionVideoView } from "./CollectionVideoView";
import { CollectionView } from "./CollectionView";
import React = require("react");
-import { FormattedTextBox } from "../nodes/FormattedTextBox";
-import { Id } from "../../../new_fields/FieldSymbols";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
@@ -45,10 +45,13 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
this.createDropTarget(ele);
}
+ @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
+
get childDocs() {
+ let self = this;
//TODO tfs: This might not be what we want?
//This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
- return DocListCast(this.props.Document[this.props.fieldKey]);
+ return DocListCast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]);
}
@action
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index a85604e58..14cc1e216 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -43,13 +43,12 @@
display: inline;
}
-
- .coll-title {
- width: max-content;
+ .editableView-input, .editableView-container-editing {
display: block;
+ text-overflow: ellipsis;
font-size: 24px;
+ white-space: nowrap;
}
-
}
.collectionTreeView-keyHeader {
font-style: italic;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 18654905f..47ea278e1 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,7 +1,7 @@
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt, faCaretSquareRight, faCaretSquareDown } from '@fortawesome/free-solid-svg-icons';
+import { faAngleRight, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable, computed } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
@@ -29,10 +29,13 @@ import React = require("react");
export interface TreeViewProps {
document: Doc;
+ dataDoc?: Doc;
+ containingCollection: Doc;
+ renderDepth: number;
deleteDoc: (doc: Doc) => boolean;
moveDocument: DragManager.MoveFunction;
dropAction: "alias" | "copy" | undefined;
- addDocTab: (doc: Doc, where: string) => void;
+ addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void;
panelWidth: () => number;
panelHeight: () => number;
addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
@@ -59,9 +62,32 @@ class TreeView extends React.Component<TreeViewProps> {
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
private _dref = React.createRef<HTMLDivElement>();
- @observable _chosenKey: string = "data";
+ @observable __chosenKey: string = "";
+ @computed get _chosenKey() { return this.__chosenKey ? this.__chosenKey : this.fieldKey; }
@observable _collapsed: boolean = true;
+ @computed get fieldKey() {
+ let keys = Array.from(Object.keys(this.resolvedDataDoc));
+ if (this.resolvedDataDoc.proto instanceof Doc) {
+ keys.push(...Array.from(Object.keys(this.resolvedDataDoc.proto)));
+ while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
+ }
+ let keyList: string[] = [];
+ keys.map(key => {
+ let docList = Cast(this.resolvedDataDoc[key], listSpec(Doc));
+ if (docList && docList.length > 0) {
+ keyList.push(key);
+ }
+ });
+ let layout = StrCast(this.props.document.layout);
+ if (layout.indexOf("fieldKey={\"") !== -1) {
+ return layout.split("fieldKey={\"")[1].split("\"")[0];
+ }
+ return keyList.length ? keyList[0] : "data";
+ }
+
+ @computed get resolvedDataDoc() { return BoolCast(this.props.document.isTemplate) && this.props.dataDoc ? this.props.dataDoc : this.props.document; }
+
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this._treedropDisposer && this._treedropDisposer();
if (ele) {
@@ -69,8 +95,8 @@ class TreeView extends React.Component<TreeViewProps> {
}
}
- @undoBatch delete = () => this.props.deleteDoc(this.props.document);
- @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "onRight");
+ @undoBatch delete = () => this.props.deleteDoc(this.resolvedDataDoc);
+ @undoBatch openRight = async () => this.props.addDocTab(this.props.document, this.props.document, "onRight");
onPointerDown = (e: React.PointerEvent) => e.stopPropagation();
onPointerEnter = (e: React.PointerEvent): void => {
@@ -101,7 +127,7 @@ class TreeView extends React.Component<TreeViewProps> {
@action
remove = (document: Document, key: string): boolean => {
- let children = Cast(this.props.document[key], listSpec(Doc), []);
+ let children = Cast(this.resolvedDataDoc[key], listSpec(Doc), []);
if (children.indexOf(document) !== -1) {
children.splice(children.indexOf(document), 1);
return true;
@@ -117,8 +143,8 @@ class TreeView extends React.Component<TreeViewProps> {
indent = () => this.props.addDocument(this.props.document) && this.delete()
renderBullet() {
- let docList = Cast(this.props.document.data, listSpec(Doc));
- let doc = Cast(this.props.document.data, Doc);
+ let docList = Cast(this.resolvedDataDoc[this.fieldKey], listSpec(Doc));
+ let doc = Cast(this.resolvedDataDoc[this.fieldKey], Doc);
let isDoc = doc instanceof Doc || docList;
return <div className="bullet" onClick={action(() => this._collapsed = !this._collapsed)}>
{<FontAwesomeIcon icon={this._collapsed ? (isDoc ? "caret-square-right" : "caret-right") : (isDoc ? "caret-square-down" : "caret-down")} />}
@@ -136,15 +162,15 @@ class TreeView extends React.Component<TreeViewProps> {
editableView = (key: string, style?: string) => (<EditableView
oneLine={true}
display={"inline"}
- editing={this.props.document[Id] === TreeView.loadId}
+ editing={this.resolvedDataDoc[Id] === TreeView.loadId}
contents={StrCast(this.props.document[key])}
onClick={this.titleClicked}
height={36}
fontStyle={style}
GetValue={() => StrCast(this.props.document[key])}
- SetValue={(value: string) => (Doc.GetProto(this.props.document)[key] = value) ? true : true}
+ SetValue={(value: string) => (Doc.GetProto(this.resolvedDataDoc)[key] = value) ? true : true}
OnFillDown={(value: string) => {
- Doc.GetProto(this.props.document)[key] = value;
+ Doc.GetProto(this.resolvedDataDoc)[key] = value;
let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
TreeView.loadId = doc[Id];
return this.props.addDocument(doc);
@@ -153,23 +179,17 @@ class TreeView extends React.Component<TreeViewProps> {
/>)
@computed get keyList() {
- 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)));
+ let keys = Array.from(Object.keys(this.resolvedDataDoc));
+ if (this.resolvedDataDoc.proto instanceof Doc) {
+ keys.push(...Array.from(Object.keys(this.resolvedDataDoc.proto)));
while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
}
- let keyList: string[] = [];
- keys.map(key => {
- let docList = Cast(this.props.document[key], listSpec(Doc));
- let doc = Cast(this.props.document[key], Doc);
- if (doc instanceof Doc || docList) {
- keyList.push(key);
- }
- });
- if (keyList.indexOf("data") !== -1) {
- keyList.splice(keyList.indexOf("data"), 1);
+ let keyList: string[] = keys.reduce((l, key) => Cast(this.resolvedDataDoc[key], listSpec(Doc)) ? [...l, key] : l, [] as string[]);
+ keys.map(key => Cast(this.resolvedDataDoc[key], Doc) instanceof Doc && keyList.push(key));
+ if (keyList.indexOf(this.fieldKey) !== -1) {
+ keyList.splice(keyList.indexOf(this.fieldKey), 1);
}
- keyList.splice(0, 0, "data");
+ keyList.splice(0, 0, this.fieldKey);
return keyList;
}
/**
@@ -177,19 +197,19 @@ class TreeView extends React.Component<TreeViewProps> {
*/
renderTitle() {
let reference = React.createRef<HTMLDivElement>();
- let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true);
+ let onItemDown = SetupDrag(reference, () => this.resolvedDataDoc, this.move, this.props.dropAction, this.props.treeViewId, true);
let headerElements = (
<span className="collectionTreeView-keyHeader" key={this._chosenKey}
onPointerDown={action(() => {
let ind = this.keyList.indexOf(this._chosenKey);
ind = (ind + 1) % this.keyList.length;
- this._chosenKey = this.keyList[ind];
+ this.__chosenKey = this.keyList[ind];
})} >
{this._chosenKey}
</span>);
- let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : [];
- let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
+ let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document[this.fieldKey], listSpec(Doc), []) : [];
+ let openRight = dataDocs && dataDocs.indexOf(this.resolvedDataDoc) !== -1 ? (null) : (
<div className="treeViewItem-openRight" onPointerDown={this.onPointerDown} onClick={this.openRight}>
<FontAwesomeIcon icon="angle-right" size="lg" />
</div>);
@@ -210,13 +230,13 @@ class TreeView extends React.Component<TreeViewProps> {
onWorkspaceContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.props.document)) });
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.KVPDocument(this.props.document, { width: 300, height: 300 }), "onRight"), icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)) });
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" });
if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) {
- ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab"), icon: "folder" });
- ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight"), icon: "caret-square-right" });
- 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, false)) });
+ ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" });
+ ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" });
+ if (DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).length) {
+ ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).map(view => view.props.focus(this.props.document, true)) });
}
ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
} else {
@@ -233,9 +253,9 @@ class TreeView extends React.Component<TreeViewProps> {
let before = x[1] < bounds[1];
let inside = x[0] > bounds[0] + 75 || (!before && !this._collapsed);
if (de.data instanceof DragManager.DocumentDragData) {
- let addDoc = (doc: Doc) => this.props.addDocument(doc, this.props.document, before);
+ let addDoc = (doc: Doc) => this.props.addDocument(doc, this.resolvedDataDoc, before);
if (inside) {
- let docList = Cast(this.props.document.data, listSpec(Doc));
+ let docList = Cast(this.resolvedDataDoc.data, listSpec(Doc));
if (docList !== undefined) {
addDoc = (doc: Doc) => { docList && docList.push(doc); return true; };
}
@@ -243,10 +263,10 @@ class TreeView extends React.Component<TreeViewProps> {
e.stopPropagation();
let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments);
return (de.data.dropAction || de.data.userDropAction) ?
- de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before) || added, false)
+ de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.resolvedDataDoc, before) || added, false)
: (de.data.moveDocument) ?
- movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, this.props.document, addDoc) || added, false)
- : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before), false);
+ movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, this.resolvedDataDoc, addDoc) || added, false)
+ : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.resolvedDataDoc, before), false);
}
return false;
}
@@ -261,21 +281,24 @@ class TreeView extends React.Component<TreeViewProps> {
render() {
let contentElement: (JSX.Element | null) = null;
- let docList = Cast(this.props.document[this._chosenKey], listSpec(Doc));
+ let docList = Cast(this.resolvedDataDoc[this._chosenKey], listSpec(Doc));
let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey);
- let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before);
- let doc = Cast(this.props.document[this._chosenKey], Doc);
+ let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.resolvedDataDoc, this._chosenKey, doc, addBefore, before);
+ let doc = Cast(this.resolvedDataDoc[this._chosenKey], Doc);
let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5;
if (!this._collapsed) {
if (!this.props.document.embed) {
contentElement = <ul key={this._chosenKey + "more"}>
- {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this._chosenKey, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)}
+ {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.props.dataDoc, this._chosenKey, addDoc, remDoc, this.move,
+ this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth)}
</ul >;
} else {
+ console.log("PW = " + this.props.panelWidth());
contentElement = <div ref={this._dref} style={{ display: "inline-block", height: this.props.panelHeight() }} key={this.props.document[Id]}>
<CollectionSchemaPreview
Document={this.props.document}
+ DataDocument={this.resolvedDataDoc}
+ renderDepth={this.props.renderDepth}
width={docWidth}
height={this.props.panelHeight}
getTransform={this.docTransform}
@@ -306,18 +329,21 @@ class TreeView extends React.Component<TreeViewProps> {
public static GetChildElements(
docs: Doc[],
treeViewId: string,
+ containingCollection: Doc,
+ dataDoc: Doc | undefined,
key: string,
add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean,
remove: ((doc: Doc) => boolean),
move: DragManager.MoveFunction,
dropAction: dropActionType,
- addDocTab: (doc: Doc, where: string) => void,
+ addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void,
screenToLocalXf: () => Transform,
outerXf: () => { translateX: number, translateY: number },
active: () => boolean,
panelWidth: () => number,
+ renderDepth: number
) {
- let docList = docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized));
+ let docList = docs.filter(child => !child.excludeFromLibrary);
let rowWidth = () => panelWidth() - 20;
return docList.map((child, i) => {
let indent = i === 0 ? undefined : () => {
@@ -337,9 +363,12 @@ class TreeView extends React.Component<TreeViewProps> {
};
return <TreeView
document={child}
+ dataDoc={dataDoc}
+ containingCollection={containingCollection}
treeViewId={treeViewId}
key={child[Id]}
indentDocument={indent}
+ renderDepth={renderDepth}
deleteDoc={remove}
addDocument={addDocument}
panelWidth={rowWidth}
@@ -388,6 +417,8 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
}
+ @computed get resolvedDataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; }
+
outerXf = () => Utils.GetScreenTransform(this._mainEle!);
onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {});
@@ -398,28 +429,27 @@ export class CollectionTreeView extends CollectionSubView(Document) {
return !this.childDocs ? (null) : (
<div id="body" className="collectionTreeView-dropTarget"
+ style={{ overflow: "auto" }}
onContextMenu={this.onContextMenu}
- onWheel={(e: React.WheelEvent) => this.props.isSelected() && e.stopPropagation()}
+ onWheel={(e: React.WheelEvent) => (e.target as any).scrollHeight > (e.target as any).clientHeight && e.stopPropagation()}
onDrop={this.onTreeDrop}
ref={this.createTreeDropTarget}>
- <div className="coll-title">
- <EditableView
- contents={this.props.Document.title}
- display={"inline"}
- height={72}
- GetValue={() => StrCast(this.props.Document.title)}
- SetValue={(value: string) => (Doc.GetProto(this.props.Document).title = value) ? true : true}
- OnFillDown={(value: string) => {
- Doc.GetProto(this.props.Document).title = value;
- let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
- TreeView.loadId = doc[Id];
- Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true);
- }} />
- </div>
- <ul className="no-indent">
+ <EditableView
+ contents={this.resolvedDataDoc.title}
+ display={"block"}
+ height={72}
+ GetValue={() => StrCast(this.resolvedDataDoc.title)}
+ SetValue={(value: string) => (Doc.GetProto(this.resolvedDataDoc).title = value) ? true : true}
+ OnFillDown={(value: string) => {
+ Doc.GetProto(this.props.Document).title = value;
+ let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ TreeView.loadId = doc[Id];
+ Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true);
+ }} />
+ <ul className="no-indent" style={{ width: "max-content" }} >
{
- TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove,
- moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth)
+ TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove,
+ moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth)
}
</ul>
</div >
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 68eefab4c..0517c17bf 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -2,8 +2,10 @@ import { library } from '@fortawesome/fontawesome-svg-core';
import { faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
import { observer } from "mobx-react";
import * as React from 'react';
+import { Doc } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
+import { Docs } from '../../documents/Documents';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from '../ContextMenuItem';
@@ -29,7 +31,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
let props = { ...this.props, ...renderProps };
- switch (type) {
+ switch (this.isAnnotationOverlay ? CollectionViewType.Freeform : type) {
case CollectionViewType.Schema: return (<CollectionSchemaView {...props} CollectionView={this} />);
case CollectionViewType.Docking: return (<CollectionDockingView {...props} CollectionView={this} />);
case CollectionViewType.Tree: return (<CollectionTreeView {...props} CollectionView={this} />);
@@ -41,7 +43,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
return (null);
}
- get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; } // bcz: ? Why do we need to compare Id's?
+ get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; }
onContextMenu = (e: React.MouseEvent): void => {
if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
@@ -54,6 +56,16 @@ export class CollectionView extends React.Component<FieldViewProps> {
subItems.push({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree), icon: "tree" });
subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "th-list" });
ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems });
+ ContextMenu.Instance.addItem({
+ description: "Apply Template", event: undoBatch(() => {
+ let otherdoc = new Doc();
+ otherdoc.width = 100;
+ otherdoc.height = 50;
+ Doc.GetProto(otherdoc).title = "applied(" + this.props.Document.title + ")";
+ Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document);
+ this.props.addDocTab && this.props.addDocTab(otherdoc, otherdoc, "onRight");
+ }), icon: "project-diagram"
+ });
}
}
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index f11af04a3..fa7058d5a 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -9,7 +9,7 @@ import { CollectionDockingView } from "./CollectionDockingView";
import { NumCast } from "../../../new_fields/Types";
import { CollectionViewType } from "./CollectionBaseView";
-type SelectorProps = { Document: Doc, addDocTab(doc: Doc, location: string): void };
+type SelectorProps = { Document: Doc, addDocTab(doc: Doc, dataDoc: Doc, location: string): void };
@observer
export class SelectorContextMenu extends React.Component<SelectorProps> {
@observable private _docs: { col: Doc, target: Doc }[] = [];
@@ -43,7 +43,7 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
col.panX = newPanX;
col.panY = newPanY;
}
- this.props.addDocTab(col, "inTab");
+ this.props.addDocTab(col, col, "inTab"); // bcz: dataDoc?
};
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 663fa5f9d..996032b1d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,6 @@
import { action, computed } from "mobx";
import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym, DocListCastAsync } from "../../../../new_fields/Doc";
+import { Doc, DocListCastAsync, HeightSym, WidthSym } from "../../../../new_fields/Doc";
import { Id } from "../../../../new_fields/FieldSymbols";
import { InkField, StrokeData } from "../../../../new_fields/InkField";
import { createSchema, makeInterface } from "../../../../new_fields/Schema";
@@ -13,11 +13,13 @@ import { SelectionManager } from "../../../util/SelectionManager";
import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss";
+import { ContextMenu } from "../../ContextMenu";
import { InkingCanvas } from "../../InkingCanvas";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
import { DocumentContentsView } from "../../nodes/DocumentContentsView";
import { DocumentViewProps, positionSchema } from "../../nodes/DocumentView";
import { pageSchema } from "../../nodes/ImageBox";
+import PDFMenu from "../../pdf/PDFMenu";
import { CollectionSubView } from "../CollectionSubView";
import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
@@ -25,8 +27,6 @@ import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import v5 = require("uuid/v5");
-import PDFMenu from "../../pdf/PDFMenu";
-import { ContextMenu } from "../../ContextMenu";
export const panZoomSchema = createSchema({
panX: "number",
@@ -47,7 +47,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed get nativeWidth() { return this.Document.nativeWidth || 0; }
@computed get nativeHeight() { return this.Document.nativeHeight || 0; }
- public get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; }
+ public get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
private panX = () => this.Document.panX || 0;
private panY = () => this.Document.panY || 0;
@@ -163,7 +163,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
[range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
}, [[minx, maxx], [miny, maxy]]);
- let ink = Cast(this.props.Document.ink, InkField);
+ let ink = Cast(this.extensionDoc.ink, InkField);
if (ink && ink.inkData) {
ink.inkData.forEach((value: StrokeData, key: string) => {
let bounds = InkingCanvas.StrokeRect(value);
@@ -198,7 +198,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
var dv = DocumentManager.Instance.getDocumentView(doc);
return dv && SelectionManager.IsSelected(dv) ? true : false;
});
- if (!this.props.isSelected() && !childSelected && !this.props.isTopMost) {
+ if (!this.props.isSelected() && !childSelected && this.props.renderDepth > 0) {
return;
}
e.stopPropagation();
@@ -326,17 +326,22 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
- getDocumentViewProps(document: Doc): DocumentViewProps {
+ getDocumentViewProps(layoutDoc: Doc): DocumentViewProps {
+ let datadoc = BoolCast(this.props.Document.isTemplate) || this.props.DataDoc === this.props.Document ? undefined : this.props.DataDoc;
+ if (Cast(layoutDoc.layout, Doc) instanceof Doc) { // if this document is using a template to render, then set the dataDoc for the template to be this document
+ datadoc = layoutDoc;
+ }
return {
- Document: document,
+ DataDoc: datadoc,
+ Document: layoutDoc,
addDocument: this.props.addDocument,
removeDocument: this.props.removeDocument,
moveDocument: this.props.moveDocument,
ScreenToLocalTransform: this.getTransform,
- isTopMost: false,
- selectOnLoad: document[Id] === this._selectOnLoaded,
- PanelWidth: document[WidthSym],
- PanelHeight: document[HeightSym],
+ renderDepth: this.props.renderDepth + 1,
+ selectOnLoad: layoutDoc[Id] === this._selectOnLoaded,
+ PanelWidth: layoutDoc[WidthSym],
+ PanelHeight: layoutDoc[HeightSym],
ContentScaling: returnOne,
ContainingCollectionView: this.props.CollectionView,
focus: this.focusDocument,
@@ -402,12 +407,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
private childViews = () => [
- <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
+ <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} DataDoc={this.props.DataDoc} />,
...this.views
]
render() {
const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
const easing = () => this.props.Document.panTransformType === "Ease";
+ if (this.props.fieldExt) Doc.UpdateDocumentExtensionForField(this.extensionDoc, this.props.fieldKey);
return (
<div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
style={{ borderRadius: "inherit" }}
@@ -419,7 +425,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
easing={easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
<CollectionFreeFormLinksView {...this.props} key="freeformLinks">
- <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
+ <InkingCanvas getScreenTransform={this.getTransform} Document={this.extensionDoc} inkFieldKey={this.props.fieldExt ? "ink" : this.props.fieldKey + "_ink"} >
{this.childViews}
</InkingCanvas>
</CollectionFreeFormLinksView>
@@ -436,7 +442,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
@computed get overlayView() {
return (<DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
- isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
+ renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />);
}
render() {
return this.overlayView;
@@ -446,8 +452,9 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps &
@observer
class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
@computed get backgroundView() {
+ let props = this.props;
return (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
- isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
+ renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />);
}
render() {
return this.props.Document.backgroundLayout ? this.backgroundView : (null);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3f7efcb66..796ff029c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -50,7 +50,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
document.removeEventListener("pointerup", this.onPointerUp, true);
document.removeEventListener("pointermove", this.onPointerMove, true);
}
- if (all) {
+ if (rem_keydown) {
document.removeEventListener("keydown", this.marqueeCommand, true);
}
this._visible = false;
@@ -95,7 +95,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
});
} else if (!e.ctrlKey) {
- let newBox = Docs.TextDocument({ width: 200, height: 30, x: x, y: y, title: "-typed text-" });
+ let newBox = Docs.TextDocument({ width: 200, height: 50, x: x, y: y, title: "-typed text-" });
newBox.proto!.autoHeight = true;
this.props.addLiveTextDocument(newBox);
}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 02396c3af..0da4888a1 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -23,6 +23,7 @@ import { FieldViewProps } from "./FieldView";
import { Without, OmitKeys } from "../../../Utils";
import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
import { List } from "../../../new_fields/List";
+import { Doc } from "../../../new_fields/Doc";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -47,11 +48,12 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
hideOnLeave?: boolean
}> {
@computed get layout(): string {
- const layout = Cast(this.props.Document[this.props.layoutKey], "string");
+ let layoutDoc = this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
+ const layout = Cast(layoutDoc[this.props.layoutKey], "string");
if (layout === undefined) {
return this.props.Document.data ?
"<FieldView {...props} fieldKey='data' />" :
- KeyValueBox.LayoutString(this.props.Document.proto ? "proto" : "");
+ KeyValueBox.LayoutString(layoutDoc.proto ? "proto" : "");
} else if (typeof layout === "string") {
return layout;
} else {
@@ -59,8 +61,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
}
}
- CreateBindings(): JsxBindings {
- return { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit };
+ CreateBindings(layoutDoc?: Doc): JsxBindings {
+ return { props: { ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, Document: layoutDoc } };
}
@computed get templates(): List<string> {
@@ -101,10 +103,11 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
}
render() {
+ if (this.props.renderDepth > 7) return (null);
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()}
+ bindings={this.CreateBindings(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document)}
jsx={this.finalLayout}
showWarnings={true}
onError={(test: any) => { console.log(test); }}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 10d777865..7026e5de7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -67,11 +67,12 @@ const LinkDoc = makeInterface(linkSchema);
export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
Document: Doc;
+ DataDoc?: Doc;
addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
removeDocument?: (doc: Doc) => boolean;
moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
- isTopMost: boolean;
+ renderDepth: number;
ContentScaling: () => number;
PanelWidth: () => number;
PanelHeight: () => number;
@@ -80,7 +81,7 @@ export interface DocumentViewProps {
parentActive: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
bringToFront: (doc: Doc) => void;
- addDocTab: (doc: Doc, where: string) => void;
+ addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void;
collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void;
zoomToScale: (scale: number) => void;
getScale: () => number;
@@ -123,7 +124,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
public get ContentDiv() { return this._mainCont.current; }
@computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
- @computed get topMost(): boolean { return this.props.isTopMost; }
+ @computed get topMost(): boolean { return this.props.renderDepth === 0; }
@computed get templates(): List<string> {
let field = this.props.Document.templates;
if (field && field instanceof List) {
@@ -212,11 +213,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
e.stopPropagation();
}
+ get dataDoc() { return this.props.DataDoc ? this.props.DataDoc : this.props.Document; }
startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) {
if (this._mainCont.current) {
let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
+ let alldataConnected = [this.dataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
- let dragData = new DragManager.DocumentDragData(allConnected);
+ let dragData = new DragManager.DocumentDragData(allConnected, alldataConnected);
const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
dragData.dropAction = dropAction;
dragData.xOffset = xoff;
@@ -270,8 +273,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
e.stopPropagation();
let altKey = e.altKey;
let ctrlKey = e.ctrlKey;
- if (this._doubleTap && !this.props.isTopMost) {
- this.props.addDocTab(this.props.Document, "inTab");
+ if (this._doubleTap && this.props.renderDepth) {
+ let fullScreenAlias = Doc.MakeAlias(this.props.Document);
+ fullScreenAlias.templates = new List<string>();
+ this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab");
SelectionManager.DeselectAll();
this.props.Document.libraryBrush = false;
}
@@ -314,7 +319,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (dataDocs) {
expandedDocs.forEach(maxDoc =>
(!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
- this.props.addDocTab(getDispDoc(maxDoc), maxLocation)));
+ this.props.addDocTab(getDispDoc(maxDoc), getDispDoc(maxDoc), maxLocation)));
}
} else {
let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
@@ -337,7 +342,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (!linkedFwdDocs.some(l => l instanceof Promise)) {
let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab");
let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
- DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext);
+ DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, document => this.props.addDocTab(document, document, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext);
}
}
}
@@ -348,7 +353,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this._downY = e.clientY;
this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0;
if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) {
- CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e);
+ CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.dataDoc]);
e.stopPropagation();
} else {
if (this.active) e.stopPropagation(); // events stop at the lowest document that is active.
@@ -379,7 +384,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); };
- fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight"); };
+ fieldsClicked = (): void => { let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); };
makeBtnClicked = (): void => {
let doc = Doc.GetProto(this.props.Document);
doc.isButton = !BoolCast(doc.isButton, false);
@@ -393,7 +398,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
fullScreenClicked = (): void => {
- CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeCopy(this.props.Document, false));
+ CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeAlias(this.props.Document), this.dataDoc);
SelectionManager.DeselectAll();
}
@@ -476,10 +481,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const cm = ContextMenu.Instance;
let subitems: ContextMenuProps[] = [];
subitems.push({ description: "Open Full Screen", event: this.fullScreenClicked, icon: "desktop" });
- subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "inTab"), icon: "folder" });
- subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "inTab"), icon: "folder" });
- subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "onRight"), icon: "caret-square-right" });
- subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" });
+ subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "inTab"), icon: "folder" });
+ subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "inTab"), icon: "folder" });
+ subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "onRight"), icon: "caret-square-right" });
+ subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" });
subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" });
@@ -489,7 +494,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
cm.addItem({
description: "Find aliases", event: async () => {
const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document);
- this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), "onRight");
+ this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), Docs.SchemaDocument(["title"], aliases, {}), "onRight"); // bcz: dataDoc?
}, icon: "search"
});
cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
@@ -546,11 +551,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (this.Document.hidden) {
return null;
}
+ let backgroundColor = this.props.Document.layout instanceof Doc ? StrCast(this.props.Document.layout.backgroundColor) : this.Document.backgroundColor;
var scaling = this.props.ContentScaling();
var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%";
var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
return (
- <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
+ <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
ref={this._mainCont}
style={{
outlineColor: "maroon",
@@ -560,14 +566,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
`${1 * this.props.ScreenToLocalTransform().Scale}px`
: "0px",
borderRadius: "inherit",
- background: this.Document.backgroundColor || "",
+ background: backgroundColor || "",
width: nativeWidth,
height: nativeHeight,
transform: `scale(${scaling}, ${scaling})`,
opacity: NumCast(this.props.Document.opacity, 1)
}}
onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
-
onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
>
{this.contents}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 1f1582f22..55f61ddff 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -7,18 +7,17 @@ import { IconField } from "../../../new_fields/IconField";
import { List } from "../../../new_fields/List";
import { RichTextField } from "../../../new_fields/RichTextField";
import { AudioField, ImageField, VideoField } from "../../../new_fields/URLField";
-import { emptyFunction, returnFalse, returnOne } from "../../../Utils";
import { Transform } from "../../util/Transform";
import { CollectionPDFView } from "../collections/CollectionPDFView";
import { CollectionVideoView } from "../collections/CollectionVideoView";
import { CollectionView } from "../collections/CollectionView";
import { AudioBox } from "./AudioBox";
-import { DocumentContentsView } from "./DocumentContentsView";
import { FormattedTextBox } from "./FormattedTextBox";
import { IconBox } from "./IconBox";
import { ImageBox } from "./ImageBox";
-import { VideoBox } from "./VideoBox";
import { PDFBox } from "./PDFBox";
+import { VideoBox } from "./VideoBox";
+import { Id } from "../../../new_fields/FieldSymbols";
//
@@ -28,14 +27,16 @@ import { PDFBox } from "./PDFBox";
//
export interface FieldViewProps {
fieldKey: string;
+ fieldExt: string;
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
Document: Doc;
+ DataDoc?: Doc;
isSelected: () => boolean;
select: (isCtrlPressed: boolean) => void;
- isTopMost: boolean;
+ renderDepth: number;
selectOnLoad: boolean;
addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
- addDocTab: (document: Doc, where: string) => void;
+ addDocTab: (document: Doc, dataDoc: Doc, where: string) => void;
removeDocument?: (document: Doc) => boolean;
moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
@@ -85,7 +86,7 @@ export class FieldView extends React.Component<FieldViewProps> {
return <p>{field.date.toLocaleString()}</p>;
}
else if (field instanceof Doc) {
- return <p><b>{field.title}</b></p>;
+ return <p><b>{field.title + " + " + field[Id]}</b></p>;
// let returnHundred = () => 100;
// return (
// <DocumentContentsView Document={field}
@@ -96,7 +97,7 @@ export class FieldView extends React.Component<FieldViewProps> {
// ContentScaling={returnOne}
// PanelWidth={returnHundred}
// PanelHeight={returnHundred}
- // isTopMost={true} //TODO Why is this top most?
+ // renderDepth={0} //TODO Why is renderDepth reset?
// selectOnLoad={false}
// focus={emptyFunction}
// isSelected={this.props.isSelected}
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 4a29c1949..d3045ae2f 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -1,7 +1,7 @@
@import "../globalCssVariables";
.ProseMirror {
width: 100%;
- height: auto;
+ height: 100%;
min-height: 100%;
font-family: $serif;
}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 376b5a574..f0b89cbef 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,6 +1,6 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faEdit, faSmile } from '@fortawesome/free-solid-svg-icons';
-import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import { action, IReactionDisposer, observable, reaction, runInAction, computed } from "mobx";
import { observer } from "mobx-react";
import { baseKeymap } from "prosemirror-commands";
import { history } from "prosemirror-history";
@@ -45,6 +45,7 @@ export interface FormattedTextBoxProps {
hideOnLeave?: boolean;
height?: string;
color?: string;
+ outer_div?: (domminus: HTMLElement) => void;
}
const richTextSchema = createSchema({
@@ -60,6 +61,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
private _ref: React.RefObject<HTMLDivElement>;
+ private _outerdiv?: (dominus: HTMLElement) => void;
private _proseRef?: HTMLDivElement;
private _editorView: Opt<EditorView>;
private _toolTipTextMenu: TooltipTextMenu | undefined = undefined;
@@ -97,6 +99,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
constructor(props: FieldViewProps) {
super(props);
+ if (this.props.outer_div) {
+ this._outerdiv = this.props.outer_div;
+ console.log("yay");
+ }
this._ref = React.createRef();
if (this.props.isOverlay) {
@@ -104,19 +110,21 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
+ @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; }
+
dispatchTransaction = (tx: Transaction) => {
if (this._editorView) {
const state = this._editorView.state.apply(tx);
this._editorView.updateState(state);
this._applyingChange = true;
- Doc.SetOnPrototype(this.props.Document, this.props.fieldKey, new RichTextField(JSON.stringify(state.toJSON())));
- Doc.SetOnPrototype(this.props.Document, "documentText", state.doc.textBetween(0, state.doc.content.size, "\n\n"));
+ Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON()));
+ Doc.GetProto(this.dataDoc)[this.props.fieldKey + "_text"] = state.doc.textBetween(0, state.doc.content.size, "\n\n");
this._applyingChange = false;
- let title = StrCast(this.props.Document.title);
+ let title = StrCast(this.dataDoc.title);
if (title && title.startsWith("-") && this._editorView) {
let str = this._editorView.state.doc.textContent;
let titlestr = str.substr(0, Math.min(40, str.length));
- let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+ let target = this.dataDoc.proto ? this.dataDoc.proto : this.dataDoc;
target.title = "-" + titlestr + (str.length > 40 ? "..." : "");
}
}
@@ -144,14 +152,14 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
e.stopPropagation();
} else {
if (de.data instanceof DragManager.DocumentDragData) {
- let ldocs = Cast(this.props.Document.subBulletDocs, listSpec(Doc));
+ let ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
if (!ldocs) {
- this.props.Document.subBulletDocs = new List<Doc>([]);
+ this.dataDoc.subBulletDocs = new List<Doc>([]);
}
- ldocs = Cast(this.props.Document.subBulletDocs, listSpec(Doc));
+ ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
if (!ldocs) return;
if (!ldocs || !ldocs[0] || ldocs[0] instanceof Promise || StrCast((ldocs[0] as Doc).layout).indexOf("CollectionView") === -1) {
- ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.props.Document.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 }));
+ ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.dataDoc.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 }));
this.props.addDocument && this.props.addDocument(ldocs[0] as Doc);
this.props.Document.templates = new List<string>([Templates.Bullet.Layout]);
this.props.Document.isBullet = true;
@@ -201,21 +209,26 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._reactionDisposer = reaction(
() => {
- const field = this.props.Document ? Cast(this.props.Document[this.props.fieldKey], RichTextField) : undefined;
+ const field = this.dataDoc ? Cast(this.dataDoc[this.props.fieldKey], RichTextField) : undefined;
return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`;
},
field => this._editorView && !this._applyingChange &&
this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)))
);
- this.setupEditor(config, this.props.Document, this.props.fieldKey);
+ this.setupEditor(config, this.dataDoc, this.props.fieldKey);
}
private setupEditor(config: any, doc: Doc, fieldKey: string) {
let field = doc ? Cast(doc[fieldKey], RichTextField) : undefined;
let startup = StrCast(doc.documentText);
startup = startup.startsWith("@@@") ? startup.replace("@@@", "") : "";
- if (!startup && !field && doc) {
- startup = StrCast(doc[fieldKey]);
+ if (!field && doc) {
+ let text = StrCast(doc[fieldKey]);
+ if (text) {
+ startup = text;
+ } else if (Cast(doc[fieldKey], "number")) {
+ startup = NumCast(doc[fieldKey], 99).toString();
+ }
}
if (this._proseRef) {
this._editorView = new EditorView(this._proseRef, {
@@ -254,7 +267,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
e.stopPropagation();
if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip) {
- this._toolTipTextMenu.tooltip.style.opacity = "0";
+ //this._toolTipTextMenu.tooltip.style.opacity = "0";
}
}
let ctrlKey = e.ctrlKey;
@@ -268,7 +281,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._linkClicked = href.replace(DocServer.prepend("/doc/"), "").split("?")[0];
if (this._linkClicked) {
DocServer.GetRefField(this._linkClicked).then(f => {
- (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, "inTab"));
+ (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, false, document => this.props.addDocTab(document, document, "inTab"));
});
e.stopPropagation();
e.preventDefault();
@@ -289,7 +302,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
onPointerUp = (e: React.PointerEvent): void => {
if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip) {
- this._toolTipTextMenu.tooltip.style.opacity = "1";
+ //this._toolTipTextMenu.tooltip.style.opacity = "1";
}
if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
e.stopPropagation();
@@ -360,10 +373,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
// stop propagation doesn't seem to stop propagation of native keyboard events.
// so we set a flag on the native event that marks that the event's been handled.
(e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
- if (StrCast(this.props.Document.title).startsWith("-") && this._editorView) {
+ if (StrCast(this.dataDoc.title).startsWith("-") && this._editorView) {
let str = this._editorView.state.doc.textContent;
let titlestr = str.substr(0, Math.min(40, str.length));
- let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+ let target = this.dataDoc.proto ? this.dataDoc.proto : this.dataDoc;
target.title = "-" + titlestr + (str.length > 40 ? "..." : "");
}
if (!this._undoTyping) {
@@ -372,11 +385,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (this.props.isOverlay && this.props.Document.autoHeight) {
let xf = this._ref.current!.getBoundingClientRect();
let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, xf.height);
- let nh = NumCast(this.props.Document.nativeHeight, 0);
+ let nh = NumCast(this.dataDoc.nativeHeight, 0);
let dh = NumCast(this.props.Document.height, 0);
let sh = scrBounds.height;
this.props.Document.height = nh ? dh / nh * sh : sh;
- this.props.Document.proto!.nativeHeight = nh ? sh : undefined;
+ this.dataDoc.proto!.nativeHeight = nh ? sh : undefined;
}
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index f56a2d926..0e6c1ee19 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,13 +1,13 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faImage } from '@fortawesome/free-solid-svg-icons';
-import { action, observable } from 'mobx';
+import { action, observable, computed } from 'mobx';
import { observer } from "mobx-react";
import Lightbox from 'react-image-lightbox';
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
import { Doc, HeightSym, WidthSym } from '../../../new_fields/Doc';
import { List } from '../../../new_fields/List';
import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
-import { Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types';
+import { Cast, FieldValue, NumCast, StrCast, BoolCast } from '../../../new_fields/Types';
import { ImageField } from '../../../new_fields/URLField';
import { Utils } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
@@ -36,7 +36,7 @@ const ImageDocument = makeInterface(pageSchema, positionSchema);
@observer
export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageDocument) {
- public static LayoutString() { return FieldView.LayoutString(ImageBox); }
+ public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
private _downX: number = 0;
private _downY: number = 0;
@@ -46,6 +46,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
private dropDisposer?: DragManager.DragDropDisposer;
+ @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; }
+
protected createDropTarget = (ele: HTMLDivElement) => {
if (this.dropDisposer) {
@@ -66,19 +68,24 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
drop = (e: Event, de: DragManager.DropEvent) => {
if (de.data instanceof DragManager.DocumentDragData) {
de.data.droppedDocuments.forEach(action((drop: Doc) => {
- let layout = StrCast(drop.backgroundLayout);
- if (layout.indexOf(ImageBox.name) !== -1) {
- let imgData = this.props.Document[this.props.fieldKey];
- if (imgData instanceof ImageField) {
- Doc.SetOnPrototype(this.props.Document, "data", new List([imgData]));
- }
- let imgList = Cast(this.props.Document[this.props.fieldKey], listSpec(ImageField), [] as any[]);
- if (imgList) {
- let field = drop.data;
- if (field instanceof ImageField) imgList.push(field);
- else if (field instanceof List) imgList.concat(field);
- }
+ if (/*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) {
+ Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(drop.data.url);
e.stopPropagation();
+ } else {
+ let layout = StrCast(drop.backgroundLayout);
+ if (layout.indexOf(ImageBox.name) !== -1) {
+ let imgData = this.dataDoc[this.props.fieldKey];
+ if (imgData instanceof ImageField) {
+ Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new List([imgData]);
+ }
+ let imgList = Cast(this.dataDoc[this.props.fieldKey], listSpec(ImageField), [] as any[]);
+ if (imgList) {
+ let field = drop.data;
+ if (field instanceof ImageField) imgList.push(field);
+ else if (field instanceof List) imgList.concat(field);
+ }
+ e.stopPropagation();
+ }
}
}));
// de.data.removeDocument() bcz: need to implement
@@ -206,7 +213,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"];
// this._curSuffix = "";
// if (w > 20) {
- let field = this.Document[this.props.fieldKey];
+ let field = this.dataDoc[this.props.fieldKey];
// if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s";
// else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m";
// else if (this._largeRetryCount < 10) this._curSuffix = "_l";
@@ -214,8 +221,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => this.choosePath((p as ImageField).url));
// }
let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
- let rotation = NumCast(this.props.Document.rotation, 0);
- let aspect = (rotation % 180) ? this.props.Document[HeightSym]() / this.props.Document[WidthSym]() : 1;
+ let rotation = NumCast(this.dataDoc.rotation, 0);
+ let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1;
let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
return (
<div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index da0aa6ac4..ede4e3858 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -27,11 +27,13 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
render() {
let props: FieldViewProps = {
Document: this.props.doc,
+ DataDoc: this.props.doc,
ContainingCollectionView: undefined,
fieldKey: this.props.keyName,
+ fieldExt: "",
isSelected: returnFalse,
select: emptyFunction,
- isTopMost: false,
+ renderDepth: 1,
selectOnLoad: false,
active: returnFalse,
whenActiveChanged: emptyFunction,
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 8bcae4f1e..67f0b817c 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -32,10 +32,15 @@
height: 100px;
}
-.pdfBox-cont {
- pointer-events: none;
+.pdfBox-cont, .pdfBox-cont-interactive {
display: flex;
flex-direction: row;
+ height: 100%;
+ overflow-y: scroll;
+ overflow-x: hidden;
+}
+.pdfBox-cont {
+ pointer-events: none;
.textlayer {
pointer-events: none;
span {
@@ -49,8 +54,6 @@
.pdfBox-cont-interactive {
pointer-events: all;
- display: flex;
- flex-direction: row;
.textlayer {
span {
pointer-events: all !important;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index d2de1cb1c..b863ee4fc 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,9 +1,9 @@
-import { action, IReactionDisposer, observable, reaction, trace, untracked } from 'mobx';
+import { action, IReactionDisposer, observable, reaction, trace, untracked, computed } from 'mobx';
import { observer } from "mobx-react";
import 'react-image-lightbox/style.css';
import { WidthSym } from "../../../new_fields/Doc";
import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Cast, NumCast, BoolCast } from "../../../new_fields/Types";
import { PdfField } from "../../../new_fields/URLField";
//@ts-ignore
// import { Document, Page } from "react-pdf";
@@ -27,6 +27,8 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
@observable private _alt = false;
@observable private _scrollY: number = 0;
+ @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; }
+
private _reactionDisposer?: IReactionDisposer;
componentDidMount() {
@@ -34,20 +36,20 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
}
public GetPage() {
- return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1;
+ return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.dataDoc.pdfHeight)) + 1;
}
public BackPage() {
- let cp = Math.ceil(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1;
+ let cp = Math.ceil(NumCast(this.props.Document.scrollY) / NumCast(this.dataDoc.pdfHeight)) + 1;
cp = cp - 1;
if (cp > 0) {
this.props.Document.curPage = cp;
- this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight);
+ this.props.Document.scrollY = (cp - 1) * NumCast(this.dataDoc.pdfHeight);
}
}
public GotoPage(p: number) {
if (p > 0 && p <= NumCast(this.props.Document.numPages)) {
this.props.Document.curPage = p;
- this.props.Document.scrollY = (p - 1) * NumCast(this.Document.pdfHeight);
+ this.props.Document.scrollY = (p - 1) * NumCast(this.dataDoc.pdfHeight);
}
}
@@ -55,7 +57,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
let cp = this.GetPage() + 1;
if (cp <= NumCast(this.props.Document.numPages)) {
this.props.Document.curPage = cp;
- this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight);
+ this.props.Document.scrollY = (cp - 1) * NumCast(this.dataDoc.pdfHeight);
}
}
@@ -68,7 +70,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
loaded = (nw: number, nh: number, np: number) => {
if (this.props.Document) {
- let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+ let doc = this.dataDoc;
doc.numPages = np;
if (doc.nativeWidth && doc.nativeHeight) return;
let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1);
@@ -96,19 +98,19 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
render() {
// uses mozilla pdf as default
- const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"));
+ const pdfUrl = Cast(this.props.Document.data, PdfField);
+ if (!(pdfUrl instanceof PdfField)) return <div>{`pdf, ${this.props.Document.data}, not found`}</div>;
let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
return (
- <div onScroll={this.onScroll}
+ <div className={classname}
+ onScroll={this.onScroll}
style={{
- height: "100%",
- overflowY: "scroll", overflowX: "hidden",
marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px`
}}
ref={this.createRef}
onWheel={(e: React.WheelEvent) => {
e.stopPropagation();
- }} className={classname}>
+ }}>
<PDFViewer url={pdfUrl.url.pathname} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
{/* <div style={{ width: "100px", height: "300px" }}></div> */}
</div>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6adead626..1e6ad110e 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -230,10 +230,14 @@ class Viewer extends React.Component<IViewerProps> {
if (this._isPage[page] !== "image") {
this._isPage[page] = "image";
const address = this.props.url;
- let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
- runInAction(() => this._visibleElements[page] =
- <img key={res.path} src={res.path} onError={handleError}
- style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />);
+ try {
+ let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
+ runInAction(() => this._visibleElements[page] =
+ <img key={res.path} src={res.path} onError={handleError}
+ style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />);
+ } catch (e) {
+
+ }
}
}
@@ -488,7 +492,7 @@ interface IAnnotationProps {
// <DocumentView Document={targetDoc}
// ContainingCollectionView={undefined}
// ScreenToLocalTransform={this.props.parent.props.parent.props.ScreenToLocalTransform}
-// isTopMost={false}
+// renderDepth={1} // what should renderDepth be?
// ContentScaling={() => 1}
// PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)}
// PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)}
@@ -567,7 +571,7 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
if (e.button === 0) {
let targetDoc = Cast(this.props.document.target, Doc, null);
if (targetDoc) {
- DocumentManager.Instance.jumpToDocument(targetDoc);
+ DocumentManager.Instance.jumpToDocument(targetDoc, true);
}
}
if (e.button === 2) {
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index 07cdcd43a..43cb9cb86 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -8,19 +8,16 @@ import "./PresentationView.scss";
import { Utils } from "../../../Utils";
import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faFile as fileSolid, faLocationArrow, faArrowUp, faSearch } from '@fortawesome/free-solid-svg-icons';
-import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons';
+import { faFile as fileSolid, faFileDownload, faLocationArrow, faArrowUp, faSearch } from '@fortawesome/free-solid-svg-icons';
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
-
-
library.add(faArrowUp);
library.add(fileSolid);
-library.add(fileRegular);
library.add(faLocationArrow);
library.add(faSearch);
+library.add(faFileDownload);
interface PresentationElementProps {
mainDocument: Doc;
@@ -391,8 +388,8 @@ export default class PresentationElement extends React.Component<PresentationEle
<button title="Zoom" className={this.selectedButtons[buttonIndex.Show] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onZoomDocumentClick}><FontAwesomeIcon icon={"search"} /></button>
<button title="Navigate" className={this.selectedButtons[buttonIndex.Navigate] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onNavigateDocumentClick}><FontAwesomeIcon icon={"location-arrow"} /></button>
<button title="Hide Document Till Presented" className={this.selectedButtons[buttonIndex.HideTillPressed] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onHideDocumentUntilPressClick}><FontAwesomeIcon icon={fileSolid} /></button>
- <button title="Fade Document After Presented" className={this.selectedButtons[buttonIndex.FadeAfter] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onFadeDocumentAfterPresentedClick}><FontAwesomeIcon icon={fileRegular} color={"gray"} /></button>
- <button title="Hide Document After Presented" className={this.selectedButtons[buttonIndex.HideAfter] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onHideDocumentAfterPresentedClick}><FontAwesomeIcon icon={fileRegular} /></button>
+ <button title="Fade Document After Presented" className={this.selectedButtons[buttonIndex.FadeAfter] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onFadeDocumentAfterPresentedClick}><FontAwesomeIcon icon={faFileDownload} color={"gray"} /></button>
+ <button title="Hide Document After Presented" className={this.selectedButtons[buttonIndex.HideAfter] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onHideDocumentAfterPresentedClick}><FontAwesomeIcon icon={faFileDownload} /></button>
<button title="Group With Up" className={this.selectedButtons[buttonIndex.Group] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={(e) => {
e.stopPropagation();
this.changeGroupStatus();
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index cce4fff5d..c1256a6a3 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -175,6 +175,14 @@ export namespace Doc {
export function IsPrototype(doc: Doc) {
return GetT(doc, "isPrototype", "boolean", true);
}
+ export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
+ let hasProto = doc.proto instanceof Doc;
+ let onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
+ let onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
+ if (onDeleg || !hasProto || (!onProto && !defaultProto)) {
+ doc[key] = value;
+ } else doc.proto![key] = value;
+ }
export async function SetOnPrototype(doc: Doc, key: string, value: Field) {
const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") === -1 ? doc.proto : doc;
@@ -241,6 +249,30 @@ export namespace Doc {
return true;
}
+ //
+ // Resolves a reference to a field by returning 'doc' if o field extension is specified,
+ // otherwise, it returns the extension document stored in doc.<fieldKey>_ext.
+ // This mechanism allows any fields to be extended with an extension document that can
+ // be used to capture field-specific metadata. For example, an image field can be extended
+ // to store annotations, ink, and other data.
+ //
+ export function resolvedFieldDataDoc(doc: Doc, fieldKey: string, fieldExt: string) {
+ return fieldExt && doc[fieldKey + "_ext"] instanceof Doc ? doc[fieldKey + "_ext"] as Doc : doc;
+ }
+
+ export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) {
+ if (doc[fieldKey + "_ext"] === undefined) {
+ setTimeout(() => {
+ let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
+ docExtensionForField.title = "Extension of " + doc.title + "'s field:" + fieldKey;
+ let proto: Doc | undefined = doc;
+ while (proto && !Doc.IsPrototype(proto)) {
+ proto = proto.proto;
+ }
+ (proto ? proto : doc)[fieldKey + "_ext"] = docExtensionForField;
+ }, 0);
+ }
+ }
export function MakeAlias(doc: Doc) {
if (!GetT(doc, "isPrototype", "boolean", true)) {
return Doc.MakeCopy(doc);
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
new file mode 100644
index 000000000..97af10519
--- /dev/null
+++ b/src/scraping/buxton/scraper.py
@@ -0,0 +1,331 @@
+import os
+import docx2txt
+from docx import Document
+from docx.opc.constants import RELATIONSHIP_TYPE as RT
+import re
+from pymongo import MongoClient
+import shutil
+import uuid
+import datetime
+from PIL import Image
+import math
+import sys
+
+source = "./source"
+dist = "../../server/public/files"
+
+db = MongoClient("localhost", 27017)["Dash"]
+schema_guids = []
+
+
+def extract_links(fileName):
+ links = []
+ doc = Document(fileName)
+ rels = doc.part.rels
+ for rel in rels:
+ item = rels[rel]
+ if item.reltype == RT.HYPERLINK and ".aspx" not in item._target:
+ links.append(item._target)
+ return listify(links)
+
+
+def extract_value(kv_string):
+ pieces = kv_string.split(":")
+ return (pieces[1] if len(pieces) > 1 else kv_string).strip()
+
+
+def mkdir_if_absent(path):
+ try:
+ if not os.path.exists(path):
+ os.mkdir(path)
+ except OSError:
+ print("failed to create the appropriate directory structures for %s" % file_name)
+
+
+def guid():
+ return str(uuid.uuid4())
+
+
+def listify(list):
+ return {
+ "fields": list,
+ "__type": "list"
+ }
+
+
+def protofy(fieldId):
+ return {
+ "fieldId": fieldId,
+ "__type": "proxy"
+ }
+
+
+def write_schema(parse_results, display_fields):
+ view_guids = parse_results["child_guids"]
+
+ data_doc = parse_results["schema"]
+ fields = data_doc["fields"]
+
+ view_doc_guid = guid()
+
+ view_doc = {
+ "_id": view_doc_guid,
+ "fields": {
+ "proto": protofy(data_doc["_id"]),
+ "x": 10,
+ "y": 10,
+ "width": 900,
+ "height": 600,
+ "panX": 0,
+ "panY": 0,
+ "zoomBasis": 0.5,
+ "zIndex": 2,
+ "libraryBrush": False,
+ "viewType": 2
+ },
+ "__type": "Doc"
+ }
+
+ fields["proto"] = protofy("collectionProto")
+ fields["data"] = listify(proxify_guids(view_guids))
+ fields["schemaColumns"] = listify(display_fields)
+ fields["backgroundColor"] = "white"
+ fields["scale"] = 0.5
+ fields["viewType"] = 2
+ fields["author"] = "Bill Buxton"
+ fields["creationDate"] = {
+ "date": datetime.datetime.utcnow().microsecond,
+ "__type": "date"
+ }
+ fields["isPrototype"] = True
+ fields["page"] = -1
+
+ db.newDocuments.insert_one(data_doc)
+ db.newDocuments.insert_one(view_doc)
+
+ data_doc_guid = data_doc["_id"]
+ print(f"inserted view document ({view_doc_guid})")
+ print(f"inserted data document ({data_doc_guid})\n")
+
+ return view_doc_guid
+
+
+def write_image(folder, name):
+ path = f"http://localhost:1050/files/{folder}/{name}"
+
+ data_doc_guid = guid()
+ view_doc_guid = guid()
+
+ view_doc = {
+ "_id": view_doc_guid,
+ "fields": {
+ "proto": protofy(data_doc_guid),
+ "x": 10,
+ "y": 10,
+ "width": 300,
+ "zIndex": 2,
+ "libraryBrush": False
+ },
+ "__type": "Doc"
+ }
+
+ image = Image.open(f"{dist}/{folder}/{name}")
+ native_width, native_height = image.size
+
+ data_doc = {
+ "_id": data_doc_guid,
+ "fields": {
+ "proto": protofy("imageProto"),
+ "data": {
+ "url": path,
+ "__type": "image"
+ },
+ "title": name,
+ "nativeWidth": native_width,
+ "author": "Bill Buxton",
+ "creationDate": {
+ "date": datetime.datetime.utcnow().microsecond,
+ "__type": "date"
+ },
+ "isPrototype": True,
+ "page": -1,
+ "nativeHeight": native_height,
+ "height": native_height
+ },
+ "__type": "Doc"
+ }
+
+ db.newDocuments.insert_one(view_doc)
+ db.newDocuments.insert_one(data_doc)
+
+ return view_doc_guid
+
+
+def parse_document(file_name: str):
+ print(f"parsing {file_name}...")
+ pure_name = file_name.split(".")[0]
+
+ result = {}
+
+ dir_path = dist + "/" + pure_name
+ mkdir_if_absent(dir_path)
+
+ raw = str(docx2txt.process(source + "/" + file_name, dir_path))
+
+ view_guids = []
+ count = 0
+ for image in os.listdir(dir_path):
+ count += 1
+ view_guids.append(write_image(pure_name, image))
+ os.rename(dir_path + "/" + image, dir_path +
+ "/" + image.replace(".", "_m.", 1))
+ print(f"extracted {count} images...")
+
+ def sanitize(line): return re.sub("[\n\t]+", "", line).replace(u"\u00A0", " ").replace(
+ u"\u2013", "-").replace(u"\u201c", '''"''').replace(u"\u201d", '''"''').strip()
+
+ def sanitize_price(raw: str):
+ raw = raw.replace(",", "")
+ start = raw.find("$")
+ if start > -1:
+ i = start + 1
+ while (i < len(raw) and re.match(r"[0-9\.]", raw[i])):
+ i += 1
+ price = raw[start + 1: i + 1]
+ return float(price)
+ elif (raw.lower().find("nfs")):
+ return -1
+ else:
+ return math.nan
+
+ def remove_empty(line): return len(line) > 1
+
+ lines = list(map(sanitize, raw.split("\n")))
+ lines = list(filter(remove_empty, lines))
+
+ result["file_name"] = file_name
+ result["title"] = lines[2].strip()
+ result["short_description"] = lines[3].strip().replace(
+ "Short Description: ", "")
+
+ cur = 5
+ notes = ""
+ while lines[cur] != "Device Details":
+ notes += lines[cur] + " "
+ cur += 1
+ result["buxton_notes"] = notes.strip()
+
+ cur += 1
+ clean = list(
+ map(lambda data: data.strip().split(":"), lines[cur].split("|")))
+ result["company"] = clean[0][len(clean[0]) - 1].strip()
+ result["year"] = clean[1][len(clean[1]) - 1].strip()
+ result["original_price"] = sanitize_price(
+ clean[2][len(clean[2]) - 1].strip())
+
+ cur += 1
+ result["degrees_of_freedom"] = extract_value(
+ lines[cur]).replace("NA", "N/A")
+ cur += 1
+
+ dimensions = lines[cur].lower()
+ if dimensions.startswith("dimensions"):
+ dim_concat = dimensions[11:].strip()
+ cur += 1
+ while lines[cur] != "Key Words":
+ dim_concat += (" " + lines[cur].strip())
+ cur += 1
+ result["dimensions"] = dim_concat
+ else:
+ result["dimensions"] = "N/A"
+
+ cur += 1
+ result["primary_key"] = extract_value(lines[cur])
+ cur += 1
+ result["secondary_key"] = extract_value(lines[cur])
+
+ while lines[cur] != "Links":
+ result["secondary_key"] += (" " + extract_value(lines[cur]).strip())
+ cur += 1
+
+ cur += 1
+ link_descriptions = []
+ while lines[cur] != "Image":
+ link_descriptions.append(lines[cur].strip())
+ cur += 1
+ result["link_descriptions"] = listify(link_descriptions)
+
+ result["hyperlinks"] = extract_links(source + "/" + file_name)
+
+ images = []
+ captions = []
+ cur += 3
+ while cur + 1 < len(lines) and lines[cur] != "NOTES:":
+ images.append(lines[cur])
+ captions.append(lines[cur + 1])
+ cur += 2
+ result["images"] = listify(images)
+ result["captions"] = listify(captions)
+
+ notes = []
+ if (cur < len(lines) and lines[cur] == "NOTES:"):
+ cur += 1
+ while cur < len(lines):
+ notes.append(lines[cur])
+ cur += 1
+ if len(notes) > 0:
+ result["notes"] = listify(notes)
+
+ print("writing child schema...")
+
+ return {
+ "schema": {
+ "_id": guid(),
+ "fields": result,
+ "__type": "Doc"
+ },
+ "child_guids": view_guids
+ }
+
+
+def proxify_guids(guids):
+ return list(map(lambda guid: {"fieldId": guid, "__type": "proxy"}, guids))
+
+
+if os.path.exists(dist):
+ shutil.rmtree(dist)
+while os.path.exists(dist):
+ pass
+os.mkdir(dist)
+mkdir_if_absent(source)
+
+candidates = 0
+for file_name in os.listdir(source):
+ if file_name.endswith('.docx'):
+ candidates += 1
+ schema_guids.append(write_schema(
+ parse_document(file_name), ["title", "data"]))
+
+print("writing parent schema...")
+parent_guid = write_schema({
+ "schema": {
+ "_id": guid(),
+ "fields": {},
+ "__type": "Doc"
+ },
+ "child_guids": schema_guids
+}, ["title", "short_description", "original_price"])
+
+print("appending parent schema to main workspace...\n")
+db.newDocuments.update_one(
+ {"fields.title": "WS collection 1"},
+ {"$push": {"fields.data.fields": {"fieldId": parent_guid, "__type": "proxy"}}}
+)
+
+print("rewriting .gitignore...\n")
+lines = ['*', '!.gitignore']
+with open(dist + "/.gitignore", 'w') as f:
+ f.write('\n'.join(lines))
+
+suffix = "" if candidates == 1 else "s"
+print(f"conversion complete. {candidates} candidate{suffix} processed.")
diff --git a/src/scraping/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx b/src/scraping/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx
new file mode 100644
index 000000000..06094b4d3
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_Braun_T3.docx b/src/scraping/buxton/source/Bill_Notes_Braun_T3.docx
new file mode 100644
index 000000000..356697092
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_Braun_T3.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_CasioC801.docx b/src/scraping/buxton/source/Bill_Notes_CasioC801.docx
new file mode 100644
index 000000000..cd89fb97b
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_CasioC801.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_Casio_Mini.docx b/src/scraping/buxton/source/Bill_Notes_Casio_Mini.docx
new file mode 100644
index 000000000..a503cddfc
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_Casio_Mini.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_FingerWorks_Prototype.docx b/src/scraping/buxton/source/Bill_Notes_FingerWorks_Prototype.docx
new file mode 100644
index 000000000..4d13a8cf5
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_FingerWorks_Prototype.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx b/src/scraping/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx
new file mode 100644
index 000000000..578a1be08
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_FrogPad.docx b/src/scraping/buxton/source/Bill_Notes_FrogPad.docx
new file mode 100644
index 000000000..d01e1bf5c
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_FrogPad.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_Gavilan_SC.docx b/src/scraping/buxton/source/Bill_Notes_Gavilan_SC.docx
new file mode 100644
index 000000000..7bd28b376
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_Gavilan_SC.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_Grandjean_Stenotype.docx b/src/scraping/buxton/source/Bill_Notes_Grandjean_Stenotype.docx
new file mode 100644
index 000000000..0615c4953
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_Grandjean_Stenotype.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_Matias.docx b/src/scraping/buxton/source/Bill_Notes_Matias.docx
new file mode 100644
index 000000000..547603256
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_Matias.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_MousePen.docx b/src/scraping/buxton/source/Bill_Notes_MousePen.docx
new file mode 100644
index 000000000..4e1056636
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_MousePen.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_NewO.docx b/src/scraping/buxton/source/Bill_Notes_NewO.docx
new file mode 100644
index 000000000..a514926d2
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_NewO.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_OLPC.docx b/src/scraping/buxton/source/Bill_Notes_OLPC.docx
new file mode 100644
index 000000000..bfca0a9bb
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_OLPC.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_PARCkbd.docx b/src/scraping/buxton/source/Bill_Notes_PARCkbd.docx
new file mode 100644
index 000000000..c0cf6ba9a
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_PARCkbd.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_Philco_Mystery_Control.docx b/src/scraping/buxton/source/Bill_Notes_Philco_Mystery_Control.docx
new file mode 100644
index 000000000..ad06903f3
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_Philco_Mystery_Control.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_TASA_Kbd.docx b/src/scraping/buxton/source/Bill_Notes_TASA_Kbd.docx
new file mode 100644
index 000000000..e4c659de9
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_TASA_Kbd.docx
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_The_Tap.docx b/src/scraping/buxton/source/Bill_Notes_The_Tap.docx
new file mode 100644
index 000000000..8ceebc71e
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_The_Tap.docx
Binary files differ