aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/CollectionTreeView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/CollectionTreeView.tsx')
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx283
1 files changed, 192 insertions, 91 deletions
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index a7733ab5f..6ee48f11b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -3,14 +3,14 @@ import { faAngleRight, faArrowsAltH, faBell, faCamera, faCaretDown, faCaretRight
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, runInAction, untracked } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, Field, HeightSym, WidthSym } from '../../../new_fields/Doc';
+import { Doc, DocListCast, Field, HeightSym, WidthSym, DataSym, Opt } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
-import { Document, listSpec } from '../../../new_fields/Schema';
+import { Document, listSpec, createSchema, makeInterface } from '../../../new_fields/Schema';
import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { emptyFunction, emptyPath, returnFalse, Utils } from '../../../Utils';
+import { emptyFunction, emptyPath, returnFalse, Utils, returnOne, returnZero, returnTransparent, returnTrue } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from '../../util/DocumentManager';
@@ -29,12 +29,12 @@ import { ImageBox } from '../nodes/ImageBox';
import { KeyValueBox } from '../nodes/KeyValueBox';
import { ScriptBox } from '../ScriptBox';
import { Templates } from '../Templates';
-import { CollectionSubView } from "./CollectionSubView";
+import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
import "./CollectionTreeView.scss";
import React = require("react");
import { CollectionViewType } from './CollectionView';
import { RichTextField } from '../../../new_fields/RichTextField';
-import { ObjectField } from '../../../new_fields/ObjectField';
+import { DocumentView } from '../nodes/DocumentView';
export interface TreeViewProps {
@@ -46,8 +46,8 @@ export interface TreeViewProps {
renderDepth: number;
deleteDoc: (doc: Doc) => boolean;
moveDocument: DragManager.MoveFunction;
- dropAction: "alias" | "copy" | undefined;
- addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
+ dropAction: dropActionType;
+ addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
pinToPres: (document: Doc) => void;
panelWidth: () => number;
panelHeight: () => number;
@@ -56,14 +56,17 @@ export interface TreeViewProps {
indentDocument?: () => void;
outdentDocument?: () => void;
ScreenToLocalTransform: () => Transform;
+ backgroundColor?: (doc: Doc) => string | undefined;
outerXf: () => { translateX: number, translateY: number };
treeViewId: Doc;
parentKey: string;
active: (outsideReaction?: boolean) => boolean;
- hideHeaderFields: () => boolean;
- preventTreeViewOpen: boolean;
+ treeViewHideHeaderFields: () => boolean;
+ treeViewPreventOpen: boolean;
renderedIds: string[];
onCheckedClick?: ScriptField;
+ onChildClick?: ScriptField;
+ ignoreFields?: string[];
}
library.add(faTrashAlt);
@@ -84,21 +87,21 @@ library.add(faPlus, faMinus);
*
* special fields:
* treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
- * preventTreeViewOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
+ * 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
*/
class TreeView extends React.Component<TreeViewProps> {
- static loadId = "";
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
private _dref = React.createRef<HTMLDivElement>();
+ private _tref = 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 = this._overrideTreeViewOpen = c; }
- @computed get treeViewOpen() { return (!this.props.preventTreeViewOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; }
+ set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; }
+ @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && BoolCast(this.props.document.treeViewOpen)) || 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; }
@@ -128,7 +131,7 @@ class TreeView extends React.Component<TreeViewProps> {
}
@undoBatch delete = () => this.props.deleteDoc(this.props.document);
- @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath);
+ @undoBatch openRight = () => this.props.addDocTab(this.props.dropAction === "alias" ? Doc.MakeAlias(this.props.document) : this.props.document, "onRight", this.props.libraryPath);
@undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete();
@undoBatch move = (doc: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => {
return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
@@ -171,39 +174,49 @@ class TreeView extends React.Component<TreeViewProps> {
editableView = (key: string, style?: string) => (<EditableView
oneLine={true}
display={"inline-block"}
- editing={this.dataDoc[Id] === TreeView.loadId}
+ editing={true /*this.dataDoc[Id] === EditableView.loadId*/}
contents={StrCast(this.props.document[key])}
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)}
+ SetValue={undoBatch((value: string) => {
+ Doc.SetInPlace(this.props.document, key, value, false) || true;
+ this.props.document.editTitle = undefined;
+ })}
OnFillDown={undoBatch((value: string) => {
Doc.SetInPlace(this.props.document, key, value, false);
- const layoutDoc = this.props.document.layout_custom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layout_custom)) : undefined;
- const 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];
+ const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ //EditableView.loadId = doc[Id];
+ this.props.document.editTitle = undefined;
+ doc.editTitle = true;
return this.props.addDocument(doc);
})}
+ onClick={() => {
+ SelectionManager.DeselectAll();
+ Doc.UserDoc().SelectedDocs = new List([this.props.document]);
+ return false;
+ }}
OnTab={undoBatch((shift?: boolean) => {
- TreeView.loadId = this.dataDoc[Id];
+ EditableView.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 = "";
+ EditableView.loadId = "";
}, 0);
})}
/>)
onWorkspaceContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view
+ const sort = this.props.document[`${this.fieldKey}-sortAscending`];
if (this.props.document === CurrentUserUtils.UserDocument.recentlyClosed) {
ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.GetProto(CurrentUserUtils.UserDocument.recentlyClosed as Doc).data = new List<Doc>(), icon: "plus" });
} else if (this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
- ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab", this.props.libraryPath), icon: "folder" });
- ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath), icon: "caret-square-right" });
+ ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab", this.props.libraryPath), icon: "folder" });
+ ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath), icon: "caret-square-right" });
if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.props.document)), icon: "camera" });
}
@@ -212,7 +225,9 @@ class TreeView extends React.Component<TreeViewProps> {
ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
}
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: (sort ? "Sort Descending" : (sort === false ? "Unsort" : "Sort Ascending")), event: () => this.props.document[`${this.fieldKey}-sortAscending`] = (sort ? false : (sort === false ? undefined : true)), icon: "minus" });
+ ContextMenu.Instance.addItem({ description: "Toggle Theme Colors", event: () => this.props.document.darkScheme = !this.props.document.darkScheme, icon: "minus" });
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, "onRight"); }, icon: "layer-group" });
ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
e.stopPropagation();
@@ -229,7 +244,7 @@ class TreeView extends React.Component<TreeViewProps> {
if (de.complete.linkDragData) {
const sourceDoc = de.complete.linkDragData.linkSourceDocument;
const destDoc = this.props.document;
- DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc });
+ DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link");
e.stopPropagation();
}
if (de.complete.docDragData) {
@@ -256,6 +271,13 @@ class TreeView extends React.Component<TreeViewProps> {
const finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0));
return finalXf;
}
+ getTransform = () => {
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(this._tref.current!);
+ const outerXf = this.props.outerXf();
+ const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+ const finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
+ return finalXf;
+ }
docWidth = () => {
const layoutDoc = Doc.Layout(this.props.document);
const aspect = NumCast(layoutDoc._nativeHeight) / NumCast(layoutDoc._nativeWidth);
@@ -283,6 +305,7 @@ class TreeView extends React.Component<TreeViewProps> {
const rows: JSX.Element[] = [];
for (const key of Object.keys(ids).slice().sort()) {
+ if (this.props.ignoreFields?.includes(key)) continue;
const contents = doc[key];
let contentElement: (JSX.Element | null)[] | JSX.Element = [];
@@ -291,13 +314,13 @@ class TreeView extends React.Component<TreeViewProps> {
const 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, 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.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
- [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick);
+ this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
+ this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
+ [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields);
} else {
contentElement = <EditableView
key="editableView"
- contents={contents !== undefined ? contents.toString() : "null"}
+ contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
height={13}
fontSize={12}
GetValue={() => Field.toKeyValueString(doc, key)}
@@ -334,9 +357,9 @@ class TreeView extends React.Component<TreeViewProps> {
{!docs ? (null) :
TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
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.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
- [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick)}
+ this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+ this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
+ [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields)}
</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}>
@@ -350,6 +373,7 @@ class TreeView extends React.Component<TreeViewProps> {
DataDocument={this.templateDataDoc}
LibraryPath={emptyPath}
renderDepth={this.props.renderDepth + 1}
+ backgroundColor={this.props.backgroundColor}
fitToBox={this.boundsOfCollectionDocument !== undefined}
PanelWidth={this.docWidth}
PanelHeight={this.docHeight}
@@ -386,7 +410,7 @@ class TreeView extends React.Component<TreeViewProps> {
@computed
get renderBullet() {
const checked = this.props.document.type === DocumentType.COL ? undefined : this.props.onCheckedClick ? (this.props.document.treeViewChecked ? this.props.document.treeViewChecked : "unchecked") : undefined;
- return <div className="bullet" title="view inline" onClick={this.bulletClick} style={{ color: StrCast(this.props.document.color, checked === "unchecked" ? "white" : "black"), opacity: 0.4 }}>
+ return <div className="bullet" title="view inline" onClick={this.bulletClick} style={{ color: StrCast(this.props.document.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}>
{<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down"))} />}
</div>;
}
@@ -395,8 +419,8 @@ class TreeView extends React.Component<TreeViewProps> {
*/
@computed
get renderTitle() {
- const reference = React.createRef<HTMLDivElement>();
- const onItemDown = SetupDrag(reference, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId[Id], true);
+ const onItemDown = SetupDrag(this._tref, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId[Id], true);
+ const editTitle = ScriptField.MakeFunction("this.editTitle=true", { this: Doc.name });
const headerElements = (
<span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
@@ -415,17 +439,43 @@ class TreeView extends React.Component<TreeViewProps> {
<FontAwesomeIcon title="open in pane on right" icon="angle-right" size="lg" />
</div>);
return <>
- <div className="docContainer" title="click to edit title" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown}
+ <div className="docContainer" ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`} onPointerDown={onItemDown}
style={{
- color: this.props.document.isMinimized ? "red" : "black",
background: Doc.IsHighlighted(this.props.document) ? "orange" : Doc.IsBrushed(this.props.document) ? "#06121212" : "0",
fontWeight: this.props.document.searchMatch ? "bold" : undefined,
outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none"
}} >
- {this.editableView("title")}
+ {this.props.document.editTitle ?
+ this.editableView("title") :
+ <DocumentView
+ Document={this.props.document}
+ DataDoc={undefined}
+ LibraryPath={this.props.libraryPath || []}
+ addDocument={undefined}
+ addDocTab={this.props.addDocTab}
+ pinToPres={emptyFunction}
+ onClick={this.props.onChildClick || editTitle}
+ dropAction={this.props.dropAction}
+ moveDocument={this.props.moveDocument}
+ removeDocument={undefined}
+ ScreenToLocalTransform={this.getTransform}
+ ContentScaling={returnOne}
+ PanelWidth={returnZero}
+ PanelHeight={returnZero}
+ renderDepth={1}
+ focus={emptyFunction}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildren)}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ zoomToScale={emptyFunction}
+ getScale={returnOne}
+ />}
</div >
- {this.props.hideHeaderFields() ? (null) : headerElements}
+ {this.props.treeViewHideHeaderFields() ? (null) : headerElements}
{openRight}
</>;
}
@@ -456,19 +506,22 @@ class TreeView extends React.Component<TreeViewProps> {
remove: ((doc: Doc) => boolean),
move: DragManager.MoveFunction,
dropAction: dropActionType,
- addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean,
+ addDocTab: (doc: Doc, where: string) => boolean,
pinToPres: (document: Doc) => void,
+ backgroundColor: undefined | ((document: Doc) => string | undefined),
screenToLocalXf: () => Transform,
outerXf: () => { translateX: number, translateY: number },
active: (outsideReaction?: boolean) => boolean,
panelWidth: () => number,
ChromeHeight: undefined | (() => number),
renderDepth: number,
- hideHeaderFields: () => boolean,
- preventTreeViewOpen: boolean,
+ treeViewHideHeaderFields: () => boolean,
+ treeViewPreventOpen: boolean,
renderedIds: string[],
libraryPath: Doc[] | undefined,
- onCheckedClick: ScriptField | undefined
+ onCheckedClick: ScriptField | undefined,
+ onChildClick: ScriptField | undefined,
+ ignoreFields: string[] | undefined
) {
const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
if (viewSpecScript) {
@@ -476,10 +529,8 @@ class TreeView extends React.Component<TreeViewProps> {
}
const docs = childDocs.slice();
- const dataExtension = containingCollection[key + "_ext"] as Doc;
- const ascending = dataExtension && BoolCast(dataExtension.sortAscending, null);
+ const ascending = containingCollection?.[key + "-sortAscending"];
if (ascending !== undefined) {
-
const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
const reN = /[0-9]*$/;
const aA = a.replace(reN, ""); // get rid of trailing numbers
@@ -560,9 +611,11 @@ class TreeView extends React.Component<TreeViewProps> {
indentDocument={indent}
outdentDocument={outdent}
onCheckedClick={onCheckedClick}
+ onChildClick={onChildClick}
renderDepth={renderDepth}
deleteDoc={remove}
addDocument={addDocument}
+ backgroundColor={backgroundColor}
panelWidth={rowWidth}
panelHeight={rowHeight}
ChromeHeight={ChromeHeight}
@@ -574,15 +627,23 @@ class TreeView extends React.Component<TreeViewProps> {
outerXf={outerXf}
parentKey={key}
active={active}
- hideHeaderFields={hideHeaderFields}
- preventTreeViewOpen={preventTreeViewOpen}
- renderedIds={renderedIds} />;
+ treeViewHideHeaderFields={treeViewHideHeaderFields}
+ treeViewPreventOpen={treeViewPreventOpen}
+ renderedIds={renderedIds}
+ ignoreFields={ignoreFields} />;
});
}
}
+export type collectionTreeViewProps = {
+ treeViewHideTitle?: boolean;
+ treeViewHideHeaderFields?: boolean;
+ onCheckedClick?: ScriptField;
+ onChildClick?: ScriptField;
+};
+
@observer
-export class CollectionTreeView extends CollectionSubView(Document) {
+export class CollectionTreeView extends CollectionSubView(Document, undefined as any as collectionTreeViewProps) {
private treedropDisposer?: DragManager.DragDropDisposer;
private _mainEle?: HTMLDivElement;
@@ -591,7 +652,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this.treedropDisposer && this.treedropDisposer();
if (this._mainEle = ele) {
- this.treedropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+ this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
}
}
@@ -602,13 +663,24 @@ export class CollectionTreeView extends CollectionSubView(Document) {
@action
remove = (document: Document): boolean => {
- const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ const children = Cast(this.props.Document[DataSym][this.props.fieldKey], listSpec(Doc), []);
if (children.indexOf(document) !== -1) {
children.splice(children.indexOf(document), 1);
return true;
}
return false;
}
+ @action
+ addDoc = (doc: Document, relativeTo: Opt<Doc>, before?: boolean): boolean => {
+ const doAddDoc = () =>
+ Doc.AddDocToList(this.props.Document[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false);
+ if (this.props.Document.resolvedDataDoc instanceof Promise) {
+ this.props.Document.resolvedDataDoc.then((resolved: any) => doAddDoc());
+ } else {
+ doAddDoc();
+ }
+ return true;
+ }
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.workspaces) {
@@ -624,44 +696,59 @@ export class CollectionTreeView extends CollectionSubView(Document) {
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
} else {
const layoutItems: ContextMenuProps[] = [];
- layoutItems.push({ description: (this.props.Document.preventTreeViewOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
- layoutItems.push({ description: (this.props.Document.hideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.hideHeaderFields = !this.props.Document.hideHeaderFields, icon: "paint-brush" });
+ layoutItems.push({ description: (this.props.Document.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.treeViewPreventOpen = !this.props.Document.treeViewPreventOpen, icon: "paint-brush" });
+ layoutItems.push({ description: (this.props.Document.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.treeViewHideHeaderFields = !this.props.Document.treeViewHideHeaderFields, icon: "paint-brush" });
+ layoutItems.push({ description: (this.props.Document.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.props.Document.treeViewHideTitle = !this.props.Document.treeViewHideTitle, icon: "paint-brush" });
ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
}
ContextMenu.Instance.addItem({
description: "Buxton Layout", icon: "eye", event: () => {
DocListCast(this.dataDoc[this.props.fieldKey]).map(d => {
DocListCast(d.data).map((img, i) => {
- const caption = (d.captions as any)[i]?.data;
- if (caption instanceof ObjectField) {
- Doc.GetProto(img).caption = ObjectField.MakeCopy(caption as ObjectField);
+ const caption = (d.captions as any)[i];
+ if (caption) {
+ Doc.GetProto(img).caption = caption;
}
- img._hideSidebar = true;
- d.captions = undefined;
});
});
- const { TextDocument, ImageDocument, CarouselDocument } = Docs.Create;
+ const { TextDocument, ImageDocument, CarouselDocument, TreeDocument } = Docs.Create;
const { Document } = this.props;
const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif";
- const detailedTemplate = `{ "doc": { "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "short_description" } } ] }, { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "year" } } ] }, { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "company" } } ] } ] }, "selection":{"type":"text","anchor":1,"head":1},"storedMarks":[] }`;
+ const detailedTemplate = `{ "doc": { "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "year" } } ] }, { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "company" } } ] } ] }, "selection":{"type":"text","anchor":1,"head":1},"storedMarks":[] }`;
const textDoc = TextDocument("", { title: "details", _autoHeight: true });
- const detailedLayout = Docs.Create.StackingDocument([
+ const detailView = Docs.Create.StackingDocument([
CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" }),
textDoc,
- ], { _chromeStatus: "disabled", title: "detailed layout stack" });
- textDoc.data = new RichTextField(detailedTemplate, "short_description year company");
- detailedLayout.isTemplateDoc = makeTemplate(detailedLayout);
-
- const cardLayout = ImageDocument(fallbackImg, { title: "cardLayout", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work?
- cardLayout.proto!.layout = ImageBox.LayoutString("hero");
- cardLayout.showTitle = "title";
- cardLayout.showTitleHover = "titlehover";
-
- Document.childLayout = cardLayout;
- Document.childDetailed = detailedLayout;
- Document._viewType = CollectionViewType.Pivot;
- Document.pivotField = "company";
+ TextDocument("", { title: "shortDescription", _autoHeight: true }),
+ TreeDocument([], { title: "narratives", _height: 75, treeViewHideTitle: true })
+ ], { _chromeStatus: "disabled", _width: 300, _height: 300, _autoHeight: true, title: "detailView" });
+ textDoc.data = new RichTextField(detailedTemplate, "year company");
+ detailView.isTemplateDoc = makeTemplate(detailView);
+
+ const heroView = ImageDocument(fallbackImg, { title: "heroView", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work?
+ heroView.proto!.layout = ImageBox.LayoutString("hero");
+ heroView._showTitle = "title";
+ heroView._showTitleHover = "titlehover";
+
+ Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data",
+ Docs.Create.FontIconDocument({
+ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ dragFactory: heroView, removeDropProperties: new List<string>(["dropAction"]), title: "hero view", icon: "portrait"
+ }));
+
+ Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data",
+ Docs.Create.FontIconDocument({
+ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ dragFactory: detailView, removeDropProperties: new List<string>(["dropAction"]), title: "detail view", icon: "file-alt"
+ }));
+
+ Document.childLayout = heroView;
+ Document.childDetailed = detailView;
+ Document._viewType = CollectionViewType.Time;
+ Document._forceActive = true;
+ Document._pivotField = "company";
+ Document.childDropAction = "alias";
}
});
const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
@@ -673,7 +760,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
!existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
}
outerXf = () => Utils.GetScreenTransform(this._mainEle!);
- onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {});
+ onTreeDrop = (e: React.DragEvent) => this.onExternalDrop(e, {});
@computed get renderClearButton() {
return <div id="toolbar" key="toolbar">
@@ -685,18 +772,25 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
render() {
- const dropAction = StrCast(this.props.Document._dropAction) as dropActionType;
- const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
+ const dropAction = StrCast(this.props.Document.childDropAction) as dropActionType;
+ const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
- return !this.childDocs ? (null) : (
+ const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
+ return !childDocs ? (null) : (
<div className="collectionTreeView-dropTarget" id="body"
- style={{ background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document._yMargin, 20)}px` }}
+ style={{
+ background: this.props.backgroundColor?.(this.props.Document),
+ paddingLeft: `${NumCast(this.props.Document._xPadding, 10)}px`,
+ paddingRight: `${NumCast(this.props.Document._xPadding, 10)}px`,
+ paddingTop: `${NumCast(this.props.Document._yPadding, 20)}px`
+ }}
onContextMenu={this.onContextMenu}
onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
onDrop={this.onTreeDrop}
ref={this.createTreeDropTarget}>
- {(this.props.Document.treeViewHideTitle ? (null) : <EditableView
+ {(this.props.treeViewHideTitle || this.props.Document.treeViewHideTitle ? (null) : <EditableView
contents={this.dataDoc.title}
+ editing={false}
display={"block"}
maxHeight={72}
height={"auto"}
@@ -704,18 +798,18 @@ 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);
- const layoutDoc = this.props.Document.layout_custom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layout_custom)) : undefined;
- const 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);
+ const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ EditableView.loadId = doc[Id];
+ this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
})} />)}
{this.props.Document.allowClear ? this.renderClearButton : (null)}
<ul className="no-indent" style={{ width: "max-content" }} >
{
- TreeView.GetChildElements(this.childDocs, this.props.Document, 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.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.hideHeaderFields),
- BoolCast(this.props.Document.preventTreeViewOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick))
+ TreeView.GetChildElements(childDocs, this.props.Document, 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.backgroundColor, this.props.ScreenToLocalTransform,
+ this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.props.Document.treeViewHideHeaderFields),
+ BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick || ScriptCast(this.props.Document.onCheckedClick),
+ this.props.onChildClick || ScriptCast(this.props.Document.onChildClick), this.props.ignoreFields)
}
</ul>
</div >
@@ -728,7 +822,14 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey
const facetValues = Array.from(allCollectionDocs.reduce((set, child) =>
set.add(Field.toString(child[facetHeader] as Field)), new Set<string>()));
- const facetValueDocSet = facetValues.sort().map(facetValue =>
+ let nonNumbers = 0;
+ facetValues.map(val => {
+ const num = Number(val);
+ if (Number.isNaN(num)) {
+ nonNumbers++;
+ }
+ });
+ const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue =>
Docs.Create.TextDocument("", {
title: facetValue.toString(),
treeViewChecked: ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)",
@@ -739,7 +840,7 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey
});
Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
- const docFilters = Cast(layoutDoc._docFilter, listSpec("string"), []);
+ const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
for (let i = 0; i < docFilters.length; i += 3) {
const [header, value, state] = docFilters.slice(i, i + 3);
if (header === facetHeader && value === facetValue) {