aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-11-26 20:18:56 -0500
committerSam Wilkins <samwilkins333@gmail.com>2019-11-26 20:18:56 -0500
commit1954025ccd458c541596521ec0a74bf801b165a0 (patch)
treefa47590186c64b2906830e6aa4dcf696bc9ed07d /src/client/views/collections
parent0e5445c6eb3cb04b2657d5b5abeb89e0b1538220 (diff)
parent6cd6e035fc67812afd7a40f8abd0f07f8874f04a (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into server_refactor
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx41
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx129
-rw-r--r--src/client/views/collections/CollectionStackingView.scss5
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx9
-rw-r--r--src/client/views/collections/CollectionTreeView.scss3
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx113
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx15
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx15
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx52
10 files changed, 226 insertions, 158 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 75d92105b..3040e74b0 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -146,8 +146,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) {
child.contentItems[j].remove();
child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0);
- let docs = Cast(instance.props.Document.data, listSpec(Doc));
- docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1);
return true;
}
return false;
@@ -171,14 +169,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this.stateChanged();
}
- public Has = (document: Doc) => {
- let docs = Cast(this.props.Document.data, listSpec(Doc));
- if (!docs) {
- return false;
- }
- return docs.includes(document);
- }
-
//
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
@@ -187,10 +177,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) {
if (!CollectionDockingView.Instance) return false;
let instance = CollectionDockingView.Instance;
- let docs = Cast(instance.props.Document.data, listSpec(Doc));
- if (docs) {
- docs.push(document);
- }
let newItemStackConfig = {
type: 'stack',
content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
@@ -227,10 +213,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@action
public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => {
Doc.GetProto(document).lastOpened = new DateField;
- let docs = Cast(this.props.Document.data, listSpec(Doc));
- if (docs) {
- docs.push(document);
- }
let docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument);
if (stack === undefined) {
let stack: any = this._goldenLayout.root;
@@ -369,15 +351,22 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
}
+ updateDataField = async (json: string) => {
+ let matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+ let docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
+
+ if (docids) {
+ let docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
+ Doc.GetProto(this.props.Document)[this.props.fieldKey] = new List<Doc>(docs);
+ }
+ }
+
@undoBatch
stateChanged = () => {
- let docs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc));
- CollectionDockingView.Instance._removedDocs.map(theDoc =>
- docs && docs.indexOf(theDoc) !== -1 &&
- docs.splice(docs.indexOf(theDoc), 1));
- CollectionDockingView.Instance._removedDocs.length = 0;
var json = JSON.stringify(this._goldenLayout.toConfig());
this.props.Document.dockingConfig = json;
+ this.updateDataField(json);
+
if (this.undohack && !this.hack) {
this.undohack.end();
this.undohack = undefined;
@@ -649,6 +638,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
return Transform.Identity();
}
get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
+ get widthpercent() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; }
addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string) => {
SelectionManager.DeselectAll();
@@ -697,9 +687,10 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
(<div className="collectionDockingView-content" ref={ref => this._mainCont = ref}
style={{
transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`,
- height: this.layoutDoc && this.layoutDoc.fitWidth ? undefined : "100%"
+ height: this.layoutDoc && this.layoutDoc.fitWidth ? undefined : "100%",
+ width: this.widthpercent
}}>
{this.docView}
</div >);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 52ebfafd3..df4b00b3a 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faPalette } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable } from "mobx";
+import { action, observable, computed } from "mobx";
import { observer } from "mobx-react";
import Measure from "react-measure";
import { Doc } from "../../../new_fields/Doc";
@@ -20,7 +20,6 @@ import { anchorPoints, Flyout } from "../DocumentDecorations";
import { EditableView } from "../EditableView";
import { CollectionStackingView } from "./CollectionStackingView";
import "./CollectionStackingView.scss";
-import { undo } from "prosemirror-history";
library.add(faPalette);
@@ -258,12 +257,44 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
}
}
-
- render() {
+ @computed get contentLayout() {
let rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap))));
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let style = this.props.parent; const collapsed = this._collapsed;
+ let chromeStatus = this.props.parent.props.Document.chromeStatus;
+ let newEditableViewProps = {
+ GetValue: () => "",
+ SetValue: this.addDocument,
+ contents: "+ NEW",
+ HeadingObject: this.props.headingObject,
+ HeadingsHack: this._headingsHack,
+ toggle: this.toggleVisibility,
+ color: this._color
+ };
+ return collapsed ? (null) :
+ <div style={{ position: "relative" }}>
+ <div className={`collectionStackingView-masonryGrid`}
+ ref={this._contRef}
+ style={{
+ padding: `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px`,
+ width: this.props.parent.NodeWidth,
+ gridGap: this.props.parent.gridGap,
+ gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ""),
+ }}>
+ {this.props.parent.children(this.props.docList)}
+ {this.props.parent.columnDragger}
+ </div>
+ {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
+ <div className="collectionStackingView-addDocumentButton"
+ style={{ width: style.columnWidth / style.numGroupColumns }}>
+ <EditableView {...newEditableViewProps} />
+ </div> : null
+ }
+ </div>;
+ }
+
+ @computed get headingView() {
let heading = this._heading;
- let style = this.props.parent;
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
let evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
let headerEditableViewProps = {
GetValue: () => evContents,
@@ -275,30 +306,17 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
toggle: this.toggleVisibility,
color: this._color
};
- let newEditableViewProps = {
- GetValue: () => "",
- SetValue: this.addDocument,
- contents: "+ NEW",
- HeadingObject: this.props.headingObject,
- HeadingsHack: this._headingsHack,
- toggle: this.toggleVisibility,
- color: this._color
- };
- let headingView = this.props.parent.props.Document.miniHeaders ?
- <div className="collectionStackingView-miniHeader" style={{ width: "100%" }}>
- {<EditableView {...headerEditableViewProps} />}
+ return this.props.parent.props.Document.miniHeaders ?
+ <div className="collectionStackingView-miniHeader">
+ <EditableView {...headerEditableViewProps} />
</div> :
- this.props.headingObject ?
+ !this.props.headingObject ? (null) :
<div className="collectionStackingView-sectionHeader" ref={this._headerRef} >
<div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
title={evContents === `NO ${key.toUpperCase()} VALUE` ?
`Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
- style={{
- width: "100%",
- background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "lightgrey",
- color: "grey"
- }}>
- {<EditableView {...headerEditableViewProps} />}
+ style={{ background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "lightgrey", }}>
+ <EditableView {...headerEditableViewProps} />
{evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
<div className="collectionStackingView-sectionColor">
<Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
@@ -321,47 +339,26 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
</div>
}
</div>
- </div > : (null);
+ </div>;
+ }
+ render() {
const background = this._background; //to account for observables in Measure
- const collapsed = this._collapsed;
- let chromeStatus = this.props.parent.props.Document.chromeStatus;
- return (
- <Measure offset onResize={this.handleResize}>
- {({ measureRef }) => {
- return <div ref={measureRef}>
- <div className="collectionStackingView-masonrySection"
- key={heading = "empty"}
- style={{ width: this.props.parent.NodeWidth, background }}
- ref={this.createRowDropRef}
- onPointerEnter={this.pointerEnteredRow}
- onPointerLeave={this.pointerLeaveRow}
- >
- {headingView}
- {collapsed ? (null) :
- < div style={{ position: "relative" }}>
- <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`}
- ref={this._contRef}
- style={{
- padding: `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px`,
- width: this.props.parent.NodeWidth,
- gridGap: this.props.parent.gridGap,
- gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ""),
- }}>
- {this.props.parent.children(this.props.docList)}
- {this.props.parent.columnDragger}
- </div>
- {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
- <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
- style={{ width: style.columnWidth / style.numGroupColumns }}>
- <EditableView {...newEditableViewProps} />
- </div> : null
- }
- </div>
- }
- </div >
- </div>;
- }}
- </Measure>
- );
+ let contentlayout = this.contentLayout;
+ let headingview = this.headingView;
+ return <Measure offset onResize={this.handleResize}>
+ {({ measureRef }) => {
+ return <div ref={measureRef}>
+ <div className="collectionStackingView-masonrySection"
+ style={{ width: this.props.parent.NodeWidth, background }}
+ ref={this.createRowDropRef}
+ onPointerEnter={this.pointerEnteredRow}
+ onPointerLeave={this.pointerLeaveRow}
+ >
+ {headingview}
+ {contentlayout}
+ </div >
+ </div>;
+ }}
+ </Measure>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 29178b909..e1577cfee 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -97,6 +97,7 @@
.collectionStackingView-columnDoc {
display: inline-block;
+ margin: auto;
}
.collectionStackingView-masonryDoc {
@@ -177,7 +178,9 @@
.collectionStackingView-sectionHeader-subCont {
outline: none;
border: 0px;
- color: $light-color;
+ color: $light-color;
+ width: 100%;
+ color: grey;
letter-spacing: 2px;
font-size: 75%;
transition: transform 0.2s;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index be3bfca0a..e564f1193 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -40,7 +40,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
@computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); }
@computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); }
- @computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); }
+ @computed get yMargin() { return Math.max(this.props.Document.showTitle ? 30 : 0, NumCast(this.props.Document.yMargin, 2 * this.gridGap)); }
@computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); }
@computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
@computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
@@ -64,7 +64,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
this._docXfs.push({ dxf: dxf, width: width, height: height });
let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
- let style = this.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+ let style = this.isStackingView ? { width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
{this.getDisplayDoc(pair.layout || d, pair.data, dxf, width)}
</div>;
@@ -229,7 +229,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
@computed get columnDragger() {
- return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px` }} >
+ return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef}
+ style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }} >
<FontAwesomeIcon icon={"arrows-alt-h"} />
</div>;
}
@@ -317,7 +318,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
return this.props.ScreenToLocalTransform().
- translate(offset[0], offset[1] + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0)).
+ translate(offset[0], offset[1] + (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0)).
scale(NumCast(doc.width, 1) / this.columnWidth);
}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 7d0c900a6..8b12395a7 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -114,6 +114,9 @@
.treeViewItem-header {
border: transparent 1px solid;
display: flex;
+ .editableView-container-editing-oneLine {
+ min-width: 15px;
+ }
}
.treeViewItem-header-above {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8b993820b..42cdd1455 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -34,6 +34,7 @@ export interface TreeViewProps {
document: Doc;
dataDoc?: Doc;
containingCollection: Doc;
+ prevSibling?: Doc;
renderDepth: number;
deleteDoc: (doc: Doc) => boolean;
ruleProvider: Doc | undefined;
@@ -43,14 +44,16 @@ export interface TreeViewProps {
pinToPres: (document: Doc) => void;
panelWidth: () => number;
panelHeight: () => number;
+ ChromeHeight: undefined | (() => number);
addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
indentDocument?: () => void;
+ outdentDocument?: () => void;
ScreenToLocalTransform: () => Transform;
outerXf: () => { translateX: number, translateY: number };
treeViewId: string;
parentKey: string;
active: (outsideReaction?: boolean) => boolean;
- showHeaderFields: () => boolean;
+ hideHeaderFields: () => boolean;
preventTreeViewOpen: boolean;
renderedIds: string[];
}
@@ -81,10 +84,13 @@ class TreeView extends React.Component<TreeViewProps> {
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
private _dref = React.createRef<HTMLDivElement>();
+
+ get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive
+
get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, "fields"); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
set treeViewOpen(c: boolean) { if (this.props.preventTreeViewOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = c; }
- @computed get treeViewOpen() { return (BoolCast(this.props.document.treeViewOpen) && !this.props.preventTreeViewOpen) || this._overrideTreeViewOpen; }
+ @computed get treeViewOpen() { return (this.props.document.treeViewOpen && !this.props.preventTreeViewOpen) || this._overrideTreeViewOpen; }
@computed get treeViewExpandedView() { return StrCast(this.props.document.treeViewExpandedView, this.defaultExpandedView); }
@computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.document.maxEmbedHeight, 300); }
@computed get dataDoc() { return this.templateDataDoc ? this.templateDataDoc : this.props.document; }
@@ -109,7 +115,7 @@ class TreeView extends React.Component<TreeViewProps> {
return this.props.dataDoc;
}
@computed get boundsOfCollectionDocument() {
- return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 ? undefined :
+ return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined :
Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
}
@@ -143,11 +149,10 @@ class TreeView extends React.Component<TreeViewProps> {
}
onDragMove = (e: PointerEvent): void => {
Doc.UnBrushDoc(this.dataDoc);
- let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+ let pt = [e.clientX, e.clientY]
let rect = this._header!.current!.getBoundingClientRect();
- let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
- let before = x[1] < bounds[1];
- let inside = x[0] > bounds[0] + 75;
+ let before = pt[1] < rect.top + rect.height / 2;
+ let inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
this._header!.current!.className = "treeViewItem-header";
if (inside) this._header!.current!.className += " treeViewItem-header-inside";
else if (before) this._header!.current!.className += " treeViewItem-header-above";
@@ -157,22 +162,30 @@ class TreeView extends React.Component<TreeViewProps> {
editableView = (key: string, style?: string) => (<EditableView
oneLine={true}
- display={"inline"}
+ display={"inline-block"}
editing={this.dataDoc[Id] === TreeView.loadId}
contents={StrCast(this.props.document[key])}
- height={36}
+ height={12}
fontStyle={style}
fontSize={12}
GetValue={() => StrCast(this.props.document[key])}
SetValue={undoBatch((value: string) => Doc.SetInPlace(this.props.document, key, value, false) || true)}
OnFillDown={undoBatch((value: string) => {
Doc.SetInPlace(this.props.document, key, value, false);
- let doc = this.props.document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layoutCustom)) : undefined;
- if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ let layoutDoc = this.props.document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layoutCustom)) : undefined;
+ let doc = layoutDoc || Docs.Create.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);
})}
- OnTab={() => { TreeView.loadId = ""; this.props.indentDocument && this.props.indentDocument(); }}
+ OnTab={undoBatch((shift?: boolean) => {
+ TreeView.loadId = this.dataDoc[Id];
+ shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
+ setTimeout(() => { // unsetting/setting brushing for this doc will recreate & refocus this editableView after all other treeview changes have been made to the Dom (which may remove focus from this document).
+ Doc.UnBrushDoc(this.props.document);
+ Doc.BrushDoc(this.props.document);
+ TreeView.loadId = "";
+ }, 0);
+ })}
/>)
onWorkspaceContextMenu = (e: React.MouseEvent): void => {
@@ -202,11 +215,10 @@ class TreeView extends React.Component<TreeViewProps> {
@undoBatch
treeDrop = (e: Event, de: DragManager.DropEvent) => {
- let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+ let pt = [de.x, de.y];
let rect = this._header!.current!.getBoundingClientRect();
- let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
- let before = x[1] < bounds[1];
- let inside = x[0] > bounds[0] + 75 || (!before && this.treeViewOpen);
+ let before = pt[1] < rect.top + rect.height / 2;
+ let inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
if (de.data instanceof DragManager.LinkDragData) {
let sourceDoc = de.data.linkSourceDocument;
let destDoc = this.props.document;
@@ -234,7 +246,7 @@ class TreeView extends React.Component<TreeViewProps> {
let { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!);
let outerXf = this.props.outerXf();
let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
- let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
+ let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0));
return finalXf;
}
docWidth = () => {
@@ -257,8 +269,9 @@ class TreeView extends React.Component<TreeViewProps> {
})());
}
- expandedField = (doc: Doc) => {
+ @computed get expandedField() {
let ids: { [key: string]: string } = {};
+ let doc = this.props.document;
doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
let rows: JSX.Element[] = [];
@@ -270,9 +283,9 @@ class TreeView extends React.Component<TreeViewProps> {
let remDoc = (doc: Doc) => this.remove(doc, key);
let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
- DocListCast(contents), this.props.treeViewId, doc, undefined, key, addDoc, remDoc, this.move,
+ DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
- this.props.panelWidth, this.props.renderDepth, this.props.showHeaderFields, this.props.preventTreeViewOpen,
+ this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
[...this.props.renderedIds, doc[Id]]);
} else {
contentElement = <EditableView
@@ -303,14 +316,14 @@ class TreeView extends React.Component<TreeViewProps> {
return <ul key={expandKey + "more"}>
{!docs ? (null) :
TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
- this.templateDataDoc, expandKey, addDoc, remDoc, this.move,
+ this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
- this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth, this.props.showHeaderFields, this.props.preventTreeViewOpen,
+ this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
[...this.props.renderedIds, this.props.document[Id]])}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
return <ul><div ref={this._dref} style={{ display: "inline-block" }} key={this.props.document[Id] + this.props.document.title}>
- {this.expandedField(this.props.document)}
+ {this.expandedField}
</div></ul>;
} else {
let layoutDoc = Doc.Layout(this.props.document);
@@ -380,7 +393,7 @@ class TreeView extends React.Component<TreeViewProps> {
}} >
{this.editableView("title")}
</div >
- {this.props.showHeaderFields() ? headerElements : (null)}
+ {this.props.hideHeaderFields() ? (null) : headerElements}
{openRight}
</>;
}
@@ -399,11 +412,13 @@ class TreeView extends React.Component<TreeViewProps> {
</div>;
}
public static GetChildElements(
- docs: Doc[],
+ childDocs: Doc[],
treeViewId: string,
containingCollection: Doc,
dataDoc: Doc | undefined,
key: string,
+ parentCollectionDoc: Doc | undefined,
+ parentPrevSibling: Doc | undefined,
add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean,
remove: ((doc: Doc) => boolean),
move: DragManager.MoveFunction,
@@ -414,19 +429,35 @@ class TreeView extends React.Component<TreeViewProps> {
outerXf: () => { translateX: number, translateY: number },
active: (outsideReaction?: boolean) => boolean,
panelWidth: () => number,
+ ChromeHeight: undefined | (() => number),
renderDepth: number,
- showHeaderFields: () => boolean,
+ hideHeaderFields: () => boolean,
preventTreeViewOpen: boolean,
renderedIds: string[]
) {
const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
if (viewSpecScript) {
- docs = docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
+ childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
}
- let ascending = Cast(containingCollection.sortAscending, "boolean", null);
+ let docs = childDocs.slice();
+ let dataExtension = containingCollection[key + "_ext"] as Doc;
+ let ascending = dataExtension && BoolCast(dataExtension.sortAscending, null);
if (ascending !== undefined) {
- docs.sort(function (a, b): 1 | -1 {
+
+ let sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
+ var reN = /[0-9]*$/;
+ var aA = a.replace(reN, ""); // get rid of trailing numbers
+ var bA = b.replace(reN, "");
+ if (aA === bA) { // if header string matches, then compare numbers numerically
+ var aN = parseInt(a.match(reN)![0], 10);
+ var bN = parseInt(b.match(reN)![0], 10);
+ return aN === bN ? 0 : aN > bN ? 1 : -1;
+ } else {
+ return aA > bA ? 1 : -1;
+ }
+ }
+ docs.sort(function (a, b): 0 | 1 | -1 {
let descA = ascending ? b : a;
let descB = ascending ? a : b;
let first = descA.title;
@@ -436,7 +467,7 @@ class TreeView extends React.Component<TreeViewProps> {
return (first - second) > 0 ? 1 : -1;
}
if (typeof first === 'string' && typeof second === 'string') {
- return first > second ? 1 : -1;
+ return sortAlphaNum(first, second);
}
if (typeof first === 'boolean' && typeof second === 'boolean') {
// if (first === second) { // bugfixing?: otherwise, the list "flickers" because the list is resorted during every load
@@ -466,6 +497,15 @@ class TreeView extends React.Component<TreeViewProps> {
}
}
};
+ let outdent = !parentCollectionDoc ? undefined : () => {
+ if (StrCast(parentCollectionDoc.layout).indexOf("fieldKey") !== -1) {
+ let fieldKeysub = StrCast(parentCollectionDoc.layout).split("fieldKey")[1];
+ let fieldKey = fieldKeysub.split("\"")[1];
+ Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
+ parentCollectionDoc.treeViewOpen = true;
+ remove(child);
+ }
+ };
let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
return add(doc, relativeTo ? relativeTo : docs[i], before !== undefined ? before : false);
};
@@ -478,15 +518,18 @@ class TreeView extends React.Component<TreeViewProps> {
document={pair.layout}
dataDoc={pair.data}
containingCollection={containingCollection}
+ prevSibling={docs[i]}
treeViewId={treeViewId}
ruleProvider={containingCollection.isRuleProvider && pair.layout.type !== DocumentType.TEXT ? containingCollection : containingCollection.ruleProvider as Doc}
key={child[Id]}
indentDocument={indent}
+ outdentDocument={outdent}
renderDepth={renderDepth}
deleteDoc={remove}
addDocument={addDocument}
panelWidth={rowWidth}
panelHeight={rowHeight}
+ ChromeHeight={ChromeHeight}
moveDocument={move}
dropAction={dropAction}
addDocTab={addDocTab}
@@ -495,7 +538,7 @@ class TreeView extends React.Component<TreeViewProps> {
outerXf={outerXf}
parentKey={key}
active={active}
- showHeaderFields={showHeaderFields}
+ hideHeaderFields={hideHeaderFields}
preventTreeViewOpen={preventTreeViewOpen}
renderedIds={renderedIds} />;
});
@@ -581,17 +624,17 @@ export class CollectionTreeView extends CollectionSubView(Document) {
SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
OnFillDown={undoBatch((value: string) => {
Doc.SetInPlace(this.dataDoc, "title", value, false);
- let doc = this.props.Document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layoutCustom)) : undefined;
- if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ let layoutDoc = this.props.Document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layoutCustom)) : undefined;
+ let doc = layoutDoc || Docs.Create.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, false, false, false);
})} />
{this.props.Document.allowClear ? this.renderClearButton : (null)}
<ul className="no-indent" style={{ width: "max-content" }} >
{
- TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove,
+ TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
- this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth, () => !this.props.Document.hideHeaderFields,
+ this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => !this.props.Document.hideHeaderFields,
BoolCast(this.props.Document.preventTreeViewOpen), [])
}
</ul>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8387e95df..4c49054d2 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -111,7 +111,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
componentWillUnmount = () => this._reactionDisposer && this._reactionDisposer();
// bcz: Argh? What's the height of the collection chromes??
- chromeHeight = () => (this.props.ChromeHeight ? this.props.ChromeHeight() : 0) + (this.props.Document.chromeStatus === "enabled" ? -60 : 0);
+ chromeHeight = () => (this.props.Document.chromeStatus === "enabled" ? -60 : 0);
active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index cfc6c2a3f..06fca7c38 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -624,12 +624,19 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
@observer
export class CollectionTreeViewChrome extends React.Component<CollectionViewChromeProps> {
- @computed private get descending() { return Cast(this.props.CollectionView.props.Document.sortAscending, "boolean", null); }
+ get dataExtension() {
+ return this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey + "_ext"] as Doc;
+ }
+ @computed private get descending() {
+ return this.dataExtension && Cast(this.dataExtension.sortAscending, "boolean", null);
+ }
@action toggleSort = () => {
- if (this.props.CollectionView.props.Document.sortAscending) this.props.CollectionView.props.Document.sortAscending = undefined;
- else if (this.props.CollectionView.props.Document.sortAscending === undefined) this.props.CollectionView.props.Document.sortAscending = false;
- else this.props.CollectionView.props.Document.sortAscending = true;
+ if (this.dataExtension) {
+ if (this.dataExtension.sortAscending) this.dataExtension.sortAscending = undefined;
+ else if (this.dataExtension.sortAscending === undefined) this.dataExtension.sortAscending = false;
+ else this.dataExtension.sortAscending = true;
+ }
}
render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 73b45edc6..b00728079 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -6,8 +6,9 @@ import "./CollectionFreeFormLinkView.scss";
import React = require("react");
import v5 = require("uuid/v5");
import { DocumentType } from "../../../documents/DocumentTypes";
-import { observable, action, reaction, IReactionDisposer } from "mobx";
+import { observable, action, reaction, IReactionDisposer, trace } from "mobx";
import { StrCast, Cast } from "../../../../new_fields/Types";
+import { TraceMobx } from "../../../../new_fields/util";
export interface CollectionFreeFormLinkViewProps {
A: DocumentView;
@@ -17,14 +18,14 @@ export interface CollectionFreeFormLinkViewProps {
@observer
export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
- @observable _opacity: number = 1;
- @observable _update: number = 0;
+ @observable _opacity: number = 0;
_anchorDisposer: IReactionDisposer | undefined;
@action
componentDidMount() {
- setTimeout(action(() => this._opacity = 0.05), 750);
this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform()],
- () => {
+ action(() => {
+ setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
+ setTimeout(action(() => this._opacity = 0.05), 750); // this will unhighlight the link line.
let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
let adiv = (acont.length ? acont[0] : this.props.A.ContentDiv!);
@@ -45,8 +46,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
this.props.A.props.Document[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100;
this.props.A.props.Document[bfield + "_x"] = (bpt.point.x - bbounds.left) / bbounds.width * 100;
this.props.A.props.Document[bfield + "_y"] = (bpt.point.y - bbounds.top) / bbounds.height * 100;
- this._update++;
- }
+ })
, { fireImmediately: true });
}
@action
@@ -55,7 +55,6 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
}
render() {
- let y = this._update;
let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
let a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9bbaa20e7..9506ce084 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -26,7 +26,7 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"
import { ContextMenu } from "../../ContextMenu";
import { ContextMenuProps } from "../../ContextMenuItem";
import { InkingControl } from "../../InkingControl";
-import { CreatePolyline } from "../../InkingStroke";
+import { CreatePolyline, InkingStroke } from "../../InkingStroke";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
import { DocumentViewProps } from "../../nodes/DocumentView";
import { FormattedTextBox } from "../../nodes/FormattedTextBox";
@@ -283,20 +283,43 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointermove", this.onPointerMove);
document.addEventListener("pointerup", this.onPointerUp);
- if (InkingControl.Instance.selectedTool === InkTool.None) {
+ // if physically using a pen or we're in pen or highlighter mode
+ if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) {
+ e.stopPropagation();
+ e.preventDefault();
+ let point = this.getTransform().transformPoint(e.pageX, e.pageY);
+ this._points.push({ x: point[0], y: point[1] });
+ }
+ // if not using a pen and in no ink mode
+ else if (InkingControl.Instance.selectedTool === InkTool.None) {
this._lastX = e.pageX;
this._lastY = e.pageY;
}
+ // eraser or scrubber plus anything else mode
else {
e.stopPropagation();
e.preventDefault();
-
- if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
- let point = this.getTransform().transformPoint(e.pageX, e.pageY);
- this._points.push({ x: point[0], y: point[1] });
- }
}
}
+ // if (e.button === 0 && !e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) {
+ // document.removeEventListener("pointermove", this.onPointerMove);
+ // document.removeEventListener("pointerup", this.onPointerUp);
+ // document.addEventListener("pointermove", this.onPointerMove);
+ // document.addEventListener("pointerup", this.onPointerUp);
+ // if (InkingControl.Instance.selectedTool === InkTool.None) {
+ // this._lastX = e.pageX;
+ // this._lastY = e.pageY;
+ // }
+ // else {
+ // e.stopPropagation();
+ // e.preventDefault();
+
+ // if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
+ // let point = this.getTransform().transformPoint(e.pageX, e.pageY);
+ // this._points.push({ x: point[0], y: point[1] });
+ // }
+ // }
+ // }
}
@action
@@ -309,7 +332,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onPointerUp = (e: PointerEvent): void => {
- if (InteractionUtils.IsType(e, InteractionUtils.TOUCH) && this._points.length <= 1) return;
+ if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && this._points.length <= 1) return;
if (this._points.length > 1) {
let B = this.svgBounds;
@@ -365,14 +388,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onPointerMove = (e: PointerEvent): void => {
- if (InteractionUtils.IsType(e, InteractionUtils.TOUCH)) {
+ if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
if (this.props.active(true)) {
e.stopPropagation();
}
return;
}
if (!e.cancelBubble) {
- if (InkingControl.Instance.selectedTool === InkTool.None) {
+ const selectedTool = InkingControl.Instance.selectedTool;
+ if (selectedTool === InkTool.Highlighter || selectedTool === InkTool.Pen || InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
+ let point = this.getTransform().transformPoint(e.clientX, e.clientY);
+ this._points.push({ x: point[0], y: point[1] });
+ }
+ else if (selectedTool === InkTool.None) {
if (this._hitCluster && this.tryDragCluster(e)) {
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
@@ -382,10 +410,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
this.pan(e);
}
- else if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
- let point = this.getTransform().transformPoint(e.clientX, e.clientY);
- this._points.push({ x: point[0], y: point[1] });
- }
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
}