aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2019-11-26 13:26:34 -0500
committerbob <bcz@cs.brown.edu>2019-11-26 13:26:34 -0500
commit4cf3b0a4673a00f0e1de107b29a0c0b658266f46 (patch)
tree2721c5b4950b0483f4f2a655397f54cc5f775fce /src
parentef4093854ac9e3b2dadc63ac183792fa49d98f7b (diff)
fixed tree view sorting (a little) and kept title box editable after tabbing.
Diffstat (limited to 'src')
-rw-r--r--src/client/views/collections/CollectionTreeView.scss3
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx72
-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/nodes/CollectionFreeFormDocumentView.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx9
6 files changed, 74 insertions, 42 deletions
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 261354ba8..a21bc6c14 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, faArrowsAltH, faBell, faCamera, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faExpand, faMinus, faPlus, faTrash, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
@@ -82,10 +82,11 @@ 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() { trace(); 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; }
@@ -110,7 +111,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]));
}
@@ -158,22 +159,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(() => {
+ TreeView.loadId = this.dataDoc[Id];
+ 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 => {
@@ -207,7 +216,7 @@ class TreeView extends React.Component<TreeViewProps> {
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 inside = x[0] > bounds[0] + 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,8 +243,8 @@ class TreeView extends React.Component<TreeViewProps> {
docTransform = () => {
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]+ (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0));
+ let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+ let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0));
return finalXf;
}
docWidth = () => {
@@ -258,8 +267,10 @@ class TreeView extends React.Component<TreeViewProps> {
})());
}
- expandedField = (doc: Doc) => {
+ @computed get expandedField() {
+ trace();
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[] = [];
@@ -296,6 +307,7 @@ class TreeView extends React.Component<TreeViewProps> {
noOverlays = (doc: Doc) => ({ title: "", caption: "" });
@computed get renderContent() {
+ trace();
const expandKey = this.treeViewExpandedView === this.fieldKey ? this.fieldKey : this.treeViewExpandedView === "links" ? "links" : undefined;
if (expandKey !== undefined) {
let remDoc = (doc: Doc) => this.remove(doc, expandKey);
@@ -311,7 +323,7 @@ class TreeView extends React.Component<TreeViewProps> {
</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);
@@ -342,6 +354,7 @@ class TreeView extends React.Component<TreeViewProps> {
@computed
get renderBullet() {
+ trace();
return <div className="bullet" title="view inline" onClick={action((e: React.MouseEvent) => { this.treeViewOpen = !this.treeViewOpen; e.stopPropagation(); })} style={{ color: StrCast(this.props.document.color, "black"), opacity: 0.4 }}>
{<FontAwesomeIcon icon={!this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down")} />}
</div>;
@@ -387,6 +400,7 @@ class TreeView extends React.Component<TreeViewProps> {
}
render() {
+ trace();
return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onContextMenu={this.onWorkspaceContextMenu}>
<li className="collection-child">
<div className="treeViewItem-header" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
@@ -400,7 +414,7 @@ class TreeView extends React.Component<TreeViewProps> {
</div>;
}
public static GetChildElements(
- docs: Doc[],
+ childDocs: Doc[],
treeViewId: string,
containingCollection: Doc,
dataDoc: Doc | undefined,
@@ -415,7 +429,7 @@ class TreeView extends React.Component<TreeViewProps> {
outerXf: () => { translateX: number, translateY: number },
active: (outsideReaction?: boolean) => boolean,
panelWidth: () => number,
- ChromeHeight: undefined| (() => number),
+ ChromeHeight: undefined | (() => number),
renderDepth: number,
showHeaderFields: () => boolean,
preventTreeViewOpen: boolean,
@@ -423,12 +437,27 @@ class TreeView extends React.Component<TreeViewProps> {
) {
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;
@@ -438,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
@@ -565,6 +594,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
render() {
+ trace();
let dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
@@ -584,8 +614,8 @@ 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);
})} />
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/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c85b59488..2b079fe50 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -94,7 +94,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
this.layoutDoc.opacity === 0 ? undefined : // if it's not visible, then no shadow
this.layoutDoc.z ? `#9c9396 ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
this.clusterColor ? (`${this.clusterColor} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
- this.layoutDoc.isBackground ? `1px 1px 1px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big
+ this.layoutDoc.isBackground ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
StrCast(this.layoutDoc.boxShadow, ""),
borderRadius: this.borderRounding(),
transform: this.transform,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8486c0f34..057b4eecd 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -687,14 +687,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear;
return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont}
onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
- onPointerEnter={e => {
- console.log("Brush" + this.props.Document.title);
- Doc.BrushDoc(this.props.Document);
- }} onPointerLeave={e => {
- console.log("UnBrush" + this.props.Document.title);
- Doc.UnBrushDoc(this.props.Document);
-
- }}
+ onPointerEnter={e => Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)}
style={{
transition: this.Document.isAnimating ? ".5s linear" : StrCast(this.Document.transition),
pointerEvents: this.ignorePointerEvents ? "none" : "all",