aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/TreeView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/TreeView.tsx')
-rw-r--r--src/client/views/collections/TreeView.tsx341
1 files changed, 161 insertions, 180 deletions
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 1d14c63c7..94a19a673 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction, trace, reaction, IReactionDisposer } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
import { observer } from "mobx-react";
import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
@@ -9,7 +9,7 @@ import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, simulateMouseClick, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -22,7 +22,6 @@ import { undoBatch, UndoManager } from '../../util/UndoManager';
import { EditableView } from "../EditableView";
import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss';
import { DocumentView, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
import { KeyValueBox } from '../nodes/KeyValueBox';
@@ -32,43 +31,35 @@ import { CollectionTreeView } from './CollectionTreeView';
import { CollectionView, CollectionViewType } from './CollectionView';
import "./TreeView.scss";
import React = require("react");
-import { ContextMenu } from '../ContextMenu';
-import { ContextMenuProps } from '../ContextMenuItem';
-import { SharingManager } from '../../util/SharingManager';
export interface TreeViewProps {
+ treeView: CollectionTreeView;
+ parentTreeView: TreeView | CollectionTreeView | undefined;
+ prevSibling?: Doc;
document: Doc;
dataDoc?: Doc;
- containingCollection: Doc;
- prevSibling?: Doc;
+ containerCollection: Doc;
renderDepth: number;
dropAction: dropActionType;
addDocTab: (doc: Doc, where: string) => boolean;
- pinToPres: (document: Doc) => void;
panelWidth: () => number;
panelHeight: () => number;
addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined;
moveDocument: DragManager.MoveFunction;
+ isContentActive: (outsideReaction?: boolean) => boolean;
+ whenChildContentsActiveChanged: (isActive: boolean) => void;
indentDocument?: (editTitle: boolean) => void;
outdentDocument?: (editTitle: boolean) => void;
ScreenToLocalTransform: () => Transform;
dontRegisterView?: boolean;
styleProvider?: StyleProviderFunc | undefined;
- layerProvider?: undefined | ((doc: Doc, assign?: boolean) => boolean);
- outerXf: () => { translateX: number, translateY: number };
- treeView: CollectionTreeView;
- parentKey: string;
- active: (outsideReaction?: boolean) => boolean;
treeViewHideHeaderFields: () => boolean;
- treeViewPreventOpen: boolean;
renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
onCheckedClick?: () => ScriptField;
onChildClick?: () => ScriptField;
skipFields?: string[];
firstLevel: boolean;
- whenActiveChanged: (isActive: boolean) => void;
- parentTreeView: TreeView | CollectionTreeView | undefined;
}
const treeBulletWidth = function () { return Number(TREE_BULLET_WIDTH.replace("px", "")); };
@@ -79,49 +70,52 @@ const treeBulletWidth = function () { return Number(TREE_BULLET_WIDTH.replace("p
*
* special fields:
* treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
- * treeViewPreventOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
* treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
*/
export class TreeView extends React.Component<TreeViewProps> {
static _editTitleOnLoad: Opt<{ id: string, parent: TreeView | CollectionTreeView | undefined }>;
static _openTitleScript: Opt<ScriptField | undefined>;
static _openLevelScript: Opt<ScriptField | undefined>;
- private _editTitleScript: (() => ScriptField) | undefined;
- private _openScript: (() => ScriptField) | undefined;
- private _header?: React.RefObject<HTMLDivElement> = React.createRef();
- private _treedropDisposer?: DragManager.DragDropDisposer;
+ private _header: React.RefObject<HTMLDivElement> = React.createRef();
private _tref = React.createRef<HTMLDivElement>();
private _docRef: Opt<DocumentView>;
- private _editMaxWidth: number | string = 0;
private _selDisposer: Opt<IReactionDisposer>;
+ private _editTitleScript: (() => ScriptField) | undefined;
+ private _openScript: (() => ScriptField) | undefined;
+ private _treedropDisposer?: DragManager.DragDropDisposer;
+ get treeViewOpenIsTransient() { return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc); }
set treeViewOpen(c: boolean) {
- if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
- else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
+ if (this.treeViewOpenIsTransient) this._transientOpenState = c;
+ else {
+ this.doc.treeViewOpen = c;
+ this._transientOpenState = false;
+ }
}
- @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
+ @observable _transientOpenState = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
@observable _editTitle: boolean = false;
@observable _dref: DocumentView | undefined | null;
get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive
- get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
- get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.props.treeView.fileSysMode ? (this.doc.isFolder ? "data" : "aliases") : Doc.UserDoc().noviceMode || this.props.treeView.outlineMode ? "layout" : "fields"); }
- get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs && !this.props.treeView.fileSysMode ? this.fieldKey : this.defaultExpandedView); }
-
- @computed get doc() { TraceMobx(); return this.props.document; }
- @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && Doc.GetT(this.doc, "treeViewOpen", "boolean", true)) || this._overrideTreeViewOpen; }
- @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
- @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
+ get defaultExpandedView() {
+ return this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") :
+ this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
+ }
+
+ @computed get doc() { return this.props.document; }
+ @computed get treeViewOpen() { return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, "treeViewOpen", "boolean", true)) || this._transientOpenState; }
+ @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); }
+ @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containerCollection.maxEmbedHeight, 200); }
@computed get dataDoc() { return this.doc[DataSym]; }
@computed get layoutDoc() { return Doc.Layout(this.doc); }
- @computed get fieldKey() { TraceMobx(); const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
- @computed get childDocs() { TraceMobx(); return this.childDocList(this.fieldKey); }
+ @computed get fieldKey() { return Doc.LayoutFieldKey(this.doc); }
+ @computed get childDocs() { return this.childDocList(this.fieldKey); }
@computed get childLinks() { return this.childDocList("links"); }
@computed get childAliases() { return this.childDocList("aliases"); }
@computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
- @computed get selected() { return SelectionManager.Views().length && SelectionManager.Views()[0].props.Document === this.props.document; }
+ @computed get selected() { return SelectionManager.Views().lastElement()?.props.Document === this.props.document; }
childDocList(field: string) {
- const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
+ const layout = Cast(Doc.LayoutField(this.doc), Doc, null);
return (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
(layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
DocListCastOrNull(this.doc[field]); // otherwise use the document's data field
@@ -184,9 +178,9 @@ export class TreeView extends React.Component<TreeViewProps> {
document.removeEventListener("pointermove", this.onDragMove, true);
}
onPointerEnter = (e: React.PointerEvent): void => {
- this.props.active(true) && Doc.BrushDoc(this.dataDoc);
+ this.props.isContentActive(true) && Doc.BrushDoc(this.dataDoc);
if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
- this._header!.current!.className = "treeView-header";
+ this._header.current!.className = "treeView-header";
document.removeEventListener("pointermove", this.onDragMove, true);
document.removeEventListener("pointerup", this.onDragUp, true);
document.addEventListener("pointermove", this.onDragMove, true);
@@ -195,8 +189,8 @@ export class TreeView extends React.Component<TreeViewProps> {
}
onPointerLeave = (e: React.PointerEvent): void => {
Doc.UnBrushDoc(this.dataDoc);
- if (this._header?.current?.className !== "treeView-header-editing") {
- this._header!.current!.className = "treeView-header";
+ if (this._header.current?.className !== "treeView-header-editing") {
+ this._header.current!.className = "treeView-header";
}
document.removeEventListener("pointerup", this.onDragUp, true);
document.removeEventListener("pointermove", this.onDragMove, true);
@@ -204,13 +198,13 @@ export class TreeView extends React.Component<TreeViewProps> {
onDragMove = (e: PointerEvent): void => {
Doc.UnBrushDoc(this.dataDoc);
const pt = [e.clientX, e.clientY];
- const rect = this._header!.current!.getBoundingClientRect();
+ const rect = this._header.current!.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
- this._header!.current!.className = "treeView-header";
- if (inside) this._header!.current!.className += " treeView-header-inside";
- else if (before) this._header!.current!.className += " treeView-header-above";
- else if (!before) this._header!.current!.className += " treeView-header-below";
+ this._header.current!.className = "treeView-header";
+ if (inside) this._header.current!.className += " treeView-header-inside";
+ else if (before) this._header.current!.className += " treeView-header-above";
+ else if (!before) this._header.current!.className += " treeView-header-below";
e.stopPropagation();
}
@@ -218,6 +212,7 @@ export class TreeView extends React.Component<TreeViewProps> {
const bullet = Docs.Create.TextDocument("-text-", {
layout: CollectionView.LayoutString("data"),
title: "-title-", "sidebarColor": "transparent", "sidebarViewType": CollectionViewType.Freeform,
+ treeViewExpandedViewLock: true, treeViewExpandedView: "data",
_viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: "outline",
x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, backgroundColor: "transparent", _width: 1000, _height: 10
});
@@ -229,18 +224,14 @@ export class TreeView extends React.Component<TreeViewProps> {
makeTextCollection = () => {
const bullet = TreeView.makeTextBullet();
- const added = this.props.addDocument(bullet);
TreeView._editTitleOnLoad = { id: bullet[Id], parent: this };
- bullet.context = this.props.treeView.Document;
- return added;
+ return this.props.addDocument(bullet);
}
makeFolder = () => {
const folder = Docs.Create.TreeDocument([], { title: "-folder-", _stayInCollection: true, isFolder: true });
- const added = this.props.addDocument(folder);
- folder.context = this.props.treeView.Document;
TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView };
- return added;
+ return this.props.addDocument(folder);
}
preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
@@ -251,7 +242,7 @@ export class TreeView extends React.Component<TreeViewProps> {
@undoBatch
treeDrop = (e: Event, de: DragManager.DropEvent) => {
const pt = [de.x, de.y];
- const rect = this._header!.current!.getBoundingClientRect();
+ const rect = this._header.current!.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
if (de.complete.linkDragData) {
@@ -265,7 +256,7 @@ export class TreeView extends React.Component<TreeViewProps> {
e.stopPropagation();
if (docDragData.draggedDocuments[0] === this.doc) return true;
const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
- const canAdd = !StrCast((inside ? this.props.document : this.props.containingCollection)?.freezeChildren).includes("add") || docDragData.treeViewDoc === this.props.treeView.props.Document;
+ const canAdd = !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes("add") || docDragData.treeViewDoc === this.props.treeView.props.Document;
const localAdd = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false;
const addDoc = !inside ? parentAddDoc :
(doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean);
@@ -279,7 +270,7 @@ export class TreeView extends React.Component<TreeViewProps> {
refTransform = (ref: HTMLDivElement | undefined | null) => {
if (!ref) return this.props.ScreenToLocalTransform();
const { scale, translateX, translateY } = Utils.GetScreenTransform(ref);
- const outerXf = this.props.outerXf();
+ const outerXf = Utils.GetScreenTransform(this.props.treeView.MainEle());
const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
}
@@ -299,9 +290,9 @@ export class TreeView extends React.Component<TreeViewProps> {
if (aspect) return this.docWidth() / (aspect || 1);
return layoutDoc._fitWidth ?
(!Doc.NativeHeight(this.doc) ?
- NumCast(this.props.containingCollection._height)
+ NumCast(this.props.containerCollection._height)
:
- Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containingCollection._height))
+ Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height))
))
:
(layoutDoc[HeightSym]() || 50);
@@ -328,10 +319,10 @@ export class TreeView extends React.Component<TreeViewProps> {
};
const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
- this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.titleStyleProvider, this.props.layerProvider, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
- this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
- [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenActiveChanged, this.props.dontRegisterView, this);
+ this.props.treeView, this, doc, undefined, this.props.containerCollection, this.props.prevSibling, addDoc, remDoc, this.move,
+ this.props.dropAction, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.isContentActive,
+ this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields,
+ [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged, this.props.dontRegisterView);
} else {
contentElement = <EditableView key="editableView"
contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
@@ -378,18 +369,17 @@ export class TreeView extends React.Component<TreeViewProps> {
TraceMobx();
const expandKey = this.treeViewExpandedView;
if (["links", "annotations", "aliases", this.fieldKey].includes(expandKey)) {
- const key = expandKey === "annotations" ? this.fieldKey + "-annotations" : expandKey;
+ const key = (expandKey === "annotations" ? `${this.fieldKey}-` : "") + expandKey;
const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
// if there's a sort ordering specified that can be modified on drop (eg, zorder can be modified, alphabetical can't),
// then the modification would be done here
- const ordering = StrCast(this.doc[this.fieldKey + "-sortCriteria"]);
+ const ordering = StrCast(this.doc.treeViewSortCriterion);
if (ordering === "Z") {
const docs = TreeView.sortDocs(this.childDocs || ([] as Doc[]), ordering);
- doc.zIndex = addBefore ? (before ? NumCast(addBefore.zIndex) - 0.5 : NumCast(addBefore.zIndex) + 0.5) : 1000;
+ doc.zIndex = addBefore ? NumCast(addBefore.zIndex) + (before ? -0.5 : 0.5) : 1000;
docs.push(doc);
- docs.sort((a, b) => NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1);
- docs.forEach((d, i) => d.zIndex = i);
+ docs.sort((a, b) => NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1).forEach((d, i) => d.zIndex = i);
}
const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
added && (doc.context = this.doc.context);
@@ -397,24 +387,23 @@ export class TreeView extends React.Component<TreeViewProps> {
};
const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
const docs = expandKey === "aliases" ? this.childAliases : expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
- const sortKey = `${this.fieldKey}-sortCriteria`;
let downX = 0, downY = 0;
const sortings = ["up", "down", "Z", undefined];
- const curSort = Math.max(0, sortings.indexOf(Cast(this.doc[sortKey], "string", null)));
+ const curSort = Math.max(0, sortings.indexOf(Cast(this.doc.treeViewSortCriterion, "string", null)));
return <ul key={expandKey + "more"} title={"sort: " + sortings[curSort]} className={this.doc.treeViewHideTitle ? "no-indent" : ""}
onPointerDown={e => { downX = e.clientX; downY = e.clientY; e.stopPropagation(); }}
onClick={(e) => {
- if (this.props.active() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
- !this.props.treeView.outlineMode && (this.doc[sortKey] = sortings[(curSort + 1) % sortings.length]);
+ if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
+ !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortings[(curSort + 1) % sortings.length]);
e.stopPropagation();
}
}}>
{!docs ? (null) :
- TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc,
- this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.titleStyleProvider, this.props.layerProvider, this.props.ScreenToLocalTransform,
- this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
- [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenActiveChanged, this.props.dontRegisterView, this)}
+ TreeView.GetChildElements(docs, this.props.treeView, this, this.layoutDoc,
+ this.dataDoc, this.props.containerCollection, this.props.prevSibling, addDoc, remDoc, this.move,
+ StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform,
+ this.props.isContentActive, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields,
+ [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged, this.props.dontRegisterView)}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
return <ul key={this.doc[Id] + this.doc.title}>
@@ -433,7 +422,7 @@ export class TreeView extends React.Component<TreeViewProps> {
if (this.onCheckedClick) {
this.onCheckedClick?.script.run({
this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
- heading: this.props.containingCollection.title,
+ heading: this.props.containerCollection.title,
checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? "remove" : "check",
containingTreeView: this.props.treeView.props.Document,
}, console.log);
@@ -470,47 +459,49 @@ export class TreeView extends React.Component<TreeViewProps> {
}
</div>;
}
+
+ @action
+ expandNextviewType = () => {
+ if (this.treeViewOpen && !this.doc.isFolder && !this.props.treeView.outlineMode && !this.doc.treeViewExpandedViewLock) {
+ const next = (modes: any[]) => modes[(modes.indexOf(StrCast(this.doc.treeViewExpandedView)) + 1) % modes.length];
+ const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : "";
+ const links = () => DocListCast(this.doc.links).length ? "links" : "";
+ const children = () => this.childDocs ? this.fieldKey : "";
+ this.doc.treeViewExpandedView = next(this.props.treeView.fileSysMode ?
+ (Doc.UserDoc().noviceMode ? ["layout", "aliases"] : ["layout", "aliases", "fields"]) :
+ (Doc.UserDoc().noviceMode ? [children(), "layout"] : [children(), "fields", "layout", links(), annos()]).filter(mode => mode));
+ }
+ this.treeViewOpen = true;
+ }
+
@computed get headerElements() {
- return (Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode) || this.props.treeViewHideHeaderFields() ? (null) :
- <>
+ return this.props.treeViewHideHeaderFields() || Doc.IsSystem(this.doc) ? (null)
+ : <>
<FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
- <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
- onPointerDown={action(() => {
- if (this.props.treeView.fileSysMode) {
- this.doc.treeViewExpandedView = this.doc.isFolder ? this.fieldKey : this.treeViewExpandedView === "layout" ? "aliases" :
- this.treeViewExpandedView === "aliases" && !Doc.UserDoc().noviceMode ? "fields" : "layout";
- } else if (this.treeViewOpen) {
- this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView :
- this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.props.treeView.outlineMode ? "layout" : "fields") :
- this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
- this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
- (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
- this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.props.treeView.outlineMode ? "layout" : "fields");
- }
- this.treeViewOpen = true;
- })}>
- {this.treeViewExpandedView}
- </span>
+ {this.doc.treeViewExpandedViewLock ? (null) :
+ <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}>
+ {this.treeViewExpandedView}
+ </span>}
</>;
}
showContextMenu = (e: React.MouseEvent) => simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
- contextMenuItems = () => this.doc.isFolder ?
- [{ script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" }] : Doc.IsSystem(this.doc) ? [] :
- this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
- [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }] :
- [{ script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }]
+ contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : this.doc.isFolder ?
+ [{ script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" }] :
+ this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
+ [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }] :
+ [{ script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }]
onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
ignoreEvent = (e: any) => {
- if (this.props.active(true)) {
+ if (this.props.isContentActive(true)) {
e.stopPropagation();
e.preventDefault();
}
}
- titleStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => {
+ titleStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
if (!doc || doc !== this.doc) return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
switch (property.split(":")[0]) {
@@ -524,9 +515,10 @@ export class TreeView extends React.Component<TreeViewProps> {
{StrCast(doc?.title)}
</div>;
case StyleProp.Decorations: return (null);
+ default: return this.props?.treeView?.props.styleProvider?.(doc, props, property);
}
}
- embeddedStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => {
+ embeddedStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
if (property.startsWith(StyleProp.Decorations)) return (null);
return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
}
@@ -542,6 +534,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
}
}
+ titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - treeBulletWidth()));
/**
* Renders the EditableView title element for placement into the tree.
@@ -582,8 +575,10 @@ export class TreeView extends React.Component<TreeViewProps> {
Document={this.doc}
DataDoc={undefined}
scriptContext={this}
+ hideDecorationTitle={this.props.treeView.outlineMode}
+ hideResizeHandles={this.props.treeView.outlineMode}
styleProvider={this.titleStyleProvider}
- layerProvider={this.props.layerProvider}
+ layerProvider={returnTrue}
docViewPath={returnEmptyDoclist}
treeViewDoc={this.props.treeView.props.Document}
addDocument={undefined}
@@ -597,17 +592,19 @@ export class TreeView extends React.Component<TreeViewProps> {
removeDocument={this.props.removeDoc}
ScreenToLocalTransform={this.getTransform}
NativeHeight={() => 18}
- NativeWidth={this.props.treeView.truncateTitleWidth}
- PanelWidth={this.props.treeView.truncateTitleWidth}
+ NativeWidth={this.titleWidth}
+ PanelWidth={this.titleWidth}
PanelHeight={() => 18}
contextMenuItems={this.contextMenuItems}
renderDepth={1}
- focus={returnTrue}
- parentActive={returnTrue}
- whenActiveChanged={this.props.whenActiveChanged}
+ isContentActive={this.props.isContentActive}
+ isDocumentActive={this.props.isContentActive}
+ focus={this.refocus}
+ whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
bringToFront={emptyFunction}
- cantBrush={this.props.treeView.props.cantBrush}
- dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews)}
+ disableDocBrushing={this.props.treeView.props.disableDocBrushing}
+ hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
+ dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
docFilters={returnEmptyFilter}
docRangeFilters={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
@@ -621,22 +618,21 @@ export class TreeView extends React.Component<TreeViewProps> {
fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
- pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
+ pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined
}} >
{view}
</div >
<div className={"right-buttons-container"}>
- {this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations)} {/* hide and lock buttons */}
+ {this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ":afterHeader" : ""))} {/* hide and lock buttons */}
{this.headerElements}
</div>
</>;
}
- renderBulletHeader = (contents: JSX.Element) => {
+ renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
return <>
- <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} key="titleheader"
+ <div className={`treeView-header` + (editing ? "-editing" : "")} key="titleheader"
ref={this._header}
- style={{ maxWidth: this._editMaxWidth }}
onClick={this.ignoreEvent}
onPointerDown={this.ignoreEvent}
onPointerEnter={this.onPointerEnter}
@@ -647,13 +643,6 @@ export class TreeView extends React.Component<TreeViewProps> {
</>;
}
- // renders the text version of a document as the header (e.g., useful for Slide views where the "")
- @computed get renderTitleAsHeader() {
- return <>
- {this.renderBullet}
- {this.renderTitle}
- </>;
- }
renderEmbeddedDocument = (asText: boolean) => {
const layout = StrCast(Doc.LayoutField(this.layoutDoc));
@@ -667,34 +656,47 @@ export class TreeView extends React.Component<TreeViewProps> {
PanelHeight={panelHeight}
NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
- fitContentsToDoc={returnTrue}
- hideTitle={asText}
LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined}
- focus={asText ? this.refocus : returnFalse}
- dontRegisterView={asText ? undefined : this.props.dontRegisterView}
+ isContentActive={asText ? this.props.isContentActive : returnFalse}
+ isDocumentActive={asText ? this.props.isContentActive : returnFalse}
+ styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
+ hideTitle={asText}
+ fitContentsToDoc={returnTrue}
+ hideDecorationTitle={this.props.treeView.outlineMode}
+ hideResizeHandles={this.props.treeView.outlineMode}
+ focus={this.refocus}
+ hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
+ dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
ScreenToLocalTransform={this.docTransform}
renderDepth={this.props.renderDepth + 1}
rootSelected={returnTrue}
- styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
- layerProvider={this.props.layerProvider}
+ layerProvider={returnTrue}
docViewPath={this.props.treeView.props.docViewPath}
docFilters={returnEmptyFilter}
docRangeFilters={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionDoc={this.props.containingCollection}
+ ContainingCollectionDoc={this.props.containerCollection}
ContainingCollectionView={undefined}
addDocument={this.props.addDocument}
moveDocument={this.move}
removeDocument={this.props.removeDoc}
- parentActive={this.props.active}
- whenActiveChanged={this.props.whenActiveChanged}
+ whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- cantBrush={this.props.treeView.props.cantBrush}
+ pinToPres={this.props.treeView.props.pinToPres}
+ disableDocBrushing={this.props.treeView.props.disableDocBrushing}
bringToFront={returnFalse}
/>;
}
+ // renders the text version of a document as the header. This is used in the file system mode and in other vanilla tree views.
+ @computed get renderTitleAsHeader() {
+ return <>
+ {this.renderBullet}
+ {this.renderTitle}
+ </>;
+ }
+
+ // renders the document in the header field instead of a text proxy.
@computed get renderDocumentAsHeader() {
return <>
{this.renderBullet}
@@ -712,29 +714,18 @@ export class TreeView extends React.Component<TreeViewProps> {
render() {
TraceMobx();
- if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return "<" + this.doc.title + ">";
- if (this._editTitle) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll
- let par: any = this._header?.current;
- while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode;
- if (par) {
- const par_rect = (par as HTMLElement).getBoundingClientRect();
- const my_recct = this._docRef?.ContentDiv?.getBoundingClientRect();
- this._editMaxWidth = Math.max(100, par_rect.right - (my_recct?.left || 0));
- }
- }
- else this._editMaxWidth = "";
-
const hideTitle = this.doc.treeViewHideHeader || this.props.treeView.outlineMode;
- return <div className={`treeView-container${this._dref?.contentsActive() ? "-active" : ""}`}
- ref={this.createTreeDropTarget}
- onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
- onKeyDown={this.onKeyDown}>
- <li className="collection-child">
- {hideTitle && this.doc.type !== DocumentType.RTF ?
- this.renderEmbeddedDocument(false) :
- this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader : this.renderTitleAsHeader)}
- </li>
- </div>;
+ return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? "<" + this.doc.title + ">" : // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
+ <div className={`treeView-container${this.props.isContentActive() ? "-active" : ""}`}
+ ref={this.createTreeDropTarget}
+ onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()}
+ onKeyDown={this.onKeyDown}>
+ <li className="collection-child">
+ {hideTitle && this.doc.type !== DocumentType.RTF ?
+ this.renderEmbeddedDocument(false) :
+ this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader : this.renderTitleAsHeader, this._editTitle)}
+ </li>
+ </div>;
}
public static sortDocs(childDocs: Doc[], criterion: string | undefined) {
@@ -768,45 +759,40 @@ export class TreeView extends React.Component<TreeViewProps> {
public static GetChildElements(
childDocs: Doc[],
treeView: CollectionTreeView,
- containingCollection: Doc,
+ parentTreeView: CollectionTreeView | TreeView | undefined,
+ conainerCollection: Doc,
dataDoc: Doc | undefined,
- key: string,
parentCollectionDoc: Doc | undefined,
- parentPrevSibling: Doc | undefined,
+ containerPrevSibling: Doc | undefined,
add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean,
remove: undefined | ((doc: Doc | Doc[]) => boolean),
move: DragManager.MoveFunction,
dropAction: dropActionType,
addDocTab: (doc: Doc, where: string) => boolean,
- pinToPres: (document: Doc) => void,
styleProvider: undefined | StyleProviderFunc,
- layerProvider: undefined | ((doc: Doc, assign?: boolean) => boolean),
screenToLocalXf: () => Transform,
- outerXf: () => { translateX: number, translateY: number },
- active: (outsideReaction?: boolean) => boolean,
+ isContentActive: (outsideReaction?: boolean) => boolean,
panelWidth: () => number,
renderDepth: number,
treeViewHideHeaderFields: () => boolean,
- treeViewPreventOpen: boolean,
renderedIds: string[],
onCheckedClick: undefined | (() => ScriptField),
onChildClick: undefined | (() => ScriptField),
skipFields: string[] | undefined,
firstLevel: boolean,
- whenActiveChanged: (isActive: boolean) => void,
+ whenChildContentsActiveChanged: (isActive: boolean) => void,
dontRegisterView: boolean | undefined,
- parentTreeView: CollectionTreeView | TreeView | undefined
) {
- const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
+ const viewSpecScript = Cast(conainerCollection.viewSpecScript, ScriptField);
if (viewSpecScript) {
childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
}
- const docs = TreeView.sortDocs(childDocs, StrCast(containingCollection?.[key + "-sortCriteria"]));
+ const docs = TreeView.sortDocs(childDocs, StrCast(conainerCollection.treeViewSortCriterion));
const rowWidth = () => panelWidth() - treeBulletWidth();
const treeViewRefs = new Map<Doc, TreeView | undefined>();
return docs.filter(child => child instanceof Doc).map((child, i) => {
- const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child);
+ const pair = Doc.GetLayoutDataDocPair(conainerCollection, dataDoc, child);
if (!pair.layout || pair.data instanceof Promise) {
return (null);
}
@@ -823,7 +809,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
};
const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
- const outdent = parentCollectionDoc?._viewType !== CollectionViewType.Tree ? undefined : ((editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, parentPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined));
+ const outdent = parentCollectionDoc?._viewType !== CollectionViewType.Tree ? undefined : ((editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined));
const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
const childLayout = Doc.Layout(pair.layout);
const rowHeight = () => {
@@ -833,7 +819,7 @@ export class TreeView extends React.Component<TreeViewProps> {
return <TreeView key={child[Id]} ref={r => treeViewRefs.set(child, r ? r : undefined)}
document={pair.layout}
dataDoc={pair.data}
- containingCollection={containingCollection}
+ containerCollection={conainerCollection}
prevSibling={docs[i]}
treeView={treeView}
indentDocument={indent}
@@ -841,27 +827,22 @@ export class TreeView extends React.Component<TreeViewProps> {
onCheckedClick={onCheckedClick}
onChildClick={onChildClick}
renderDepth={renderDepth}
- removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove}
+ removeDoc={StrCast(conainerCollection.freezeChildren).includes("remove") ? undefined : remove}
addDocument={addDocument}
styleProvider={styleProvider}
- layerProvider={layerProvider}
panelWidth={rowWidth}
panelHeight={rowHeight}
dontRegisterView={dontRegisterView}
moveDocument={move}
dropAction={dropAction}
addDocTab={addDocTab}
- pinToPres={pinToPres}
ScreenToLocalTransform={screenToLocalXf}
- outerXf={outerXf}
- parentKey={key}
- active={active}
+ isContentActive={isContentActive}
treeViewHideHeaderFields={treeViewHideHeaderFields}
- treeViewPreventOpen={treeViewPreventOpen}
renderedIds={renderedIds}
skipFields={skipFields}
firstLevel={firstLevel}
- whenActiveChanged={whenActiveChanged}
+ whenChildContentsActiveChanged={whenChildContentsActiveChanged}
parentTreeView={parentTreeView} />;
});
}