aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/TreeView.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-03-01 08:23:06 -0500
committerbobzel <zzzman@gmail.com>2024-03-01 08:23:06 -0500
commit25474b83f908732b2618cb7110f1e410030f9280 (patch)
treea942453765eb876ffaa3899d623fa77e13a196b4 /src/client/views/collections/TreeView.tsx
parent4e837a73f5fae06368416f99c047d78f6b94565b (diff)
parent3179048be75fb7662fc472249798b2d103dc5544 (diff)
Merge branch 'master' into info-ui-observable
Diffstat (limited to 'src/client/views/collections/TreeView.tsx')
-rw-r--r--src/client/views/collections/TreeView.tsx137
1 files changed, 75 insertions, 62 deletions
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 01b80e209..4f2d8b9c0 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -4,7 +4,7 @@ import { IconButton, Size } from 'browndash-components';
import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Utils, emptyFunction, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick } from '../../../Utils';
+import { Utils, emptyFunction, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../Utils';
import { Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
@@ -27,8 +27,8 @@ import { UndoManager, undoBatch, undoable } from '../../util/UndoManager';
import { EditableView } from '../EditableView';
import { ObservableReactComponent } from '../ObservableReactComponent';
import { StyleProp } from '../StyleProvider';
-import { DocumentView, DocumentViewInternal, DocumentViewInternalProps, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
+import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
+import { FieldViewProps, StyleProviderFuncType } from '../nodes/FieldView';
import { KeyValueBox } from '../nodes/KeyValueBox';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
@@ -62,7 +62,7 @@ export interface TreeViewProps {
ScreenToLocalTransform: () => Transform;
contextMenuItems?: { script: ScriptField; filter: ScriptField; icon: string; label: string }[];
dontRegisterView?: boolean;
- styleProvider?: StyleProviderFunc | undefined;
+ styleProvider?: StyleProviderFuncType | undefined;
treeViewHideHeaderFields: () => boolean;
renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
onCheckedClick?: () => ScriptField;
@@ -198,7 +198,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const ind = DocListCast(this.dataDoc[key]).indexOf(doc instanceof Doc ? doc : doc.lastElement());
const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
- res && ind > 0 && DocumentManager.Instance.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView._props.DocumentView?.())?.select(false);
+ res && ind > 0 && DocumentManager.Instance.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView.DocumentView?.())?.select(false);
return res;
};
@@ -282,12 +282,13 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
_treeEle: any;
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this._treedropDisposer?.();
- ele && ((this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this))), this.Document);
+ ele && ((this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), this.Document, this.preTreeDrop.bind(this))), this.Document);
if (this._treeEle) this._props.unobserveHeight(this._treeEle);
this._props.observeHeight((this._treeEle = ele));
};
componentWillUnmount() {
+ this._treedropDisposer?.();
this._renderTimer && clearTimeout(this._renderTimer);
Object.values(this._disposers).forEach(disposer => disposer?.());
this._treeEle && this._props.unobserveHeight(this._treeEle);
@@ -339,11 +340,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const before = pt[1] < rect.top + rect.height / 2;
const inside = pt[0] > rect.left + rect.width * 0.33 || (!before && this.treeViewOpen && this.childDocs?.length);
this._header.current!.className = 'treeView-header';
- if (!this.treeView.outlineMode || DragManager.DocDragData?.treeViewDoc === this.treeView.Document) {
- 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';
- }
+ 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();
};
@@ -367,8 +366,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
_width: 1000,
_height: 10,
});
- Doc.GetProto(bullet).title = ComputedField.MakeFunction('this.text?.Text');
- Doc.GetProto(bullet).data = new List<Doc>([]);
+ const bulletData = bullet[DocData];
+ bulletData.title = ComputedField.MakeFunction('this.text?.Text');
+ bulletData.data = new List<Doc>([]);
DocumentManager.Instance.AddViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.());
return bullet;
@@ -383,12 +383,11 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
makeFolder = () => {
const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true });
TreeView._editTitleOnLoad = { id: folder[Id], parent: this._props.parentTreeView };
- return this._props.addDocument(folder);
+ return this.localAdd(folder);
};
- preTreeDrop = (e: Event, de: DragManager.DropEvent) => {
- const dragData = de.complete.docDragData;
- dragData && (dragData.dropAction = this.treeView.Document === dragData.treeViewDoc ? 'same' : dragData.dropAction);
+ preTreeDrop = (e: Event, de: DragManager.DropEvent, docDropAction: dropActionType) => {
+ // fall through and let the CollectionTreeView handle this since treeView items have no special properties of their own
};
@undoBatch
@@ -415,7 +414,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
docDragData.dropAction,
docDragData.removeDocument,
docDragData.moveDocument,
- docDragData.treeViewDoc === this.treeView.Document
+ docDragData.treeViewDoc === this.treeView.Document,
+ de.embedKey
);
e.stopPropagation();
!added && e.preventDefault();
@@ -424,22 +424,33 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
return false;
};
+ localAdd = (doc: Doc | Doc[]) => {
+ const innerAdd = (doc: Doc) => {
+ const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
+ const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
+ dataIsComputed && Doc.SetContainer(doc, DocCast(this.Document.embedContainer));
+ return added;
+ };
+ return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
+ };
+
dropping: boolean = false;
- dropDocuments(droppedDocuments: Doc[], before: boolean, inside: number | boolean, dropAction: dropActionType, removeDocument: DragManager.RemoveFunction | undefined, moveDocument: DragManager.MoveFunction | undefined, forceAdd: boolean) {
+ dropDocuments(
+ droppedDocuments: Doc[],
+ before: boolean,
+ inside: number | boolean,
+ dropAction: dropActionType | undefined,
+ removeDocument: DragManager.RemoveFunction | undefined,
+ moveDocument: DragManager.MoveFunction | undefined,
+ forceAdd: boolean,
+ canEmbed?: boolean
+ ) {
const parentAddDoc = (doc: Doc | Doc[]) => this._props.addDocument(doc, undefined, undefined, before);
- const localAdd = (doc: Doc | Doc[]) => {
- const innerAdd = (doc: Doc) => {
- const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
- const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
- dataIsComputed && Doc.SetContainer(doc, DocCast(this.Document.embedContainer));
- return added;
- };
- return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
- };
- const addDoc = inside ? localAdd : parentAddDoc;
- const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same' || dropAction === 'inSame') && moveDocument;
- const canAdd = (!this.treeView.outlineMode && !StrCast((inside ? this.Document : this._props.treeViewParent)?.treeView_FreezeChildren).includes('add')) || forceAdd;
+
+ const addDoc = inside ? this.localAdd : parentAddDoc;
+ const canAdd = !StrCast((inside ? this.Document : this._props.treeViewParent)?.treeView_FreezeChildren).includes('add') || forceAdd;
if (canAdd && (dropAction !== 'inSame' || droppedDocuments.every(d => d.embedContainer === this._props.parentTreeView?.Document))) {
+ const move = (!dropAction || canEmbed || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same' || dropAction === 'inSame') && moveDocument;
this._props.parentTreeView instanceof TreeView && (this._props.parentTreeView.dropping = true);
const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
this._props.parentTreeView instanceof TreeView && (this._props.parentTreeView.dropping = false);
@@ -601,7 +612,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
docs.sort((a, b) => (NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1)).forEach((d, i) => (d.zIndex = i));
}
const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
- const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
+ const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false);
!dataIsComputed && added && Doc.SetContainer(doc, this.Document);
return added;
@@ -830,17 +841,16 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
};
contextMenuItems = () => {
const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' };
- const folderOp = this.childDocs?.length ? [makeFolder] : [];
const openEmbedding = { script: ScriptField.MakeFunction(`openDoc(getEmbedding(this), "${OpenWhere.addRight}")`)!, icon: 'copy', label: 'Open New Embedding' };
const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(this)`)!, icon: 'eye', label: 'Focus or Open' };
const reopenDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(this)`)!, icon: 'eye', label: 'Reopen' };
return [
...(this._props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.Document })?.result)),
...(this.Document.isFolder
- ? folderOp
+ ? [makeFolder]
: Doc.IsSystem(this.Document)
? []
- : this.treeView.fileSysMode && this.Document === Doc.GetProto(this.Document)
+ : this.treeView.fileSysMode && this.Document === this.Document[DocData]
? [openEmbedding, makeFolder]
: this.Document._type_collection === CollectionViewType.Docking
? []
@@ -867,7 +877,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
e.preventDefault();
}
};
- titleStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+ titleStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string): any => {
if (!doc || doc !== this.Document) return this._props?.treeView?._props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
const treeView = this.treeView;
@@ -899,7 +909,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
}
return treeView._props.styleProvider?.(doc, props, property);
};
- embeddedStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+ embeddedStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, 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
};
@@ -973,18 +983,18 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
Document={this.Document}
layout_fitWidth={returnTrue}
scriptContext={this}
- hideDecorationTitle={this.treeView.outlineMode}
- hideResizeHandles={this.treeView.outlineMode}
+ hideDecorations={true}
+ hideClickBehaviors={true}
styleProvider={this.titleStyleProvider}
onClickScriptDisable="never" // tree docViews have a script to show fields, etc.
- docViewPath={this.treeView._props.docViewPath}
- treeViewDoc={this.treeView.Document}
+ containerViewPath={this.treeView.childContainerViewPath}
addDocument={undefined}
addDocTab={this._props.addDocTab}
pinToPres={this.treeView._props.pinToPres}
- onClick={this.onChildClick}
- onDoubleClick={this.onChildDoubleClick}
+ onClickScript={this.onChildClick}
+ onDoubleClickScript={this.onChildDoubleClick}
dragAction={this._props.dragAction}
+ dragConfig={this.treeView.dragConfig}
moveDocument={this.move}
removeDocument={this._props.removeDoc}
ScreenToLocalTransform={this.getTransform}
@@ -998,7 +1008,6 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
isDocumentActive={this._props.isContentActive}
focus={this.refocus}
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
- bringToFront={emptyFunction}
disableBrushing={this.treeView._props.disableBrushing}
hideLinkButton={BoolCast(this.treeView.Document.childHideLinkButton)}
dontRegisterView={BoolCast(this.treeView.Document.childDontRegisterViews, this._props.dontRegisterView)}
@@ -1040,7 +1049,19 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
key="titleheader"
ref={this._header}
onClick={this.ignoreEvent}
- onPointerDown={this.ignoreEvent}
+ onPointerDown={e => {
+ this.treeView.isContentActive() &&
+ setupMoveUpEvents(
+ this,
+ e,
+ () => {
+ this._dref?.startDragging(e.clientX, e.clientY, '' as any);
+ return true;
+ },
+ returnFalse,
+ emptyFunction
+ );
+ }}
onPointerEnter={this.onPointerEnter}
onPointerLeave={this.onPointerLeave}>
<div
@@ -1072,22 +1093,21 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
isContentActive={isActive}
isDocumentActive={isActive}
styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
- hideTitle={asText}
fitContentsToBox={returnTrue}
- hideDecorationTitle={this.treeView.outlineMode}
- hideResizeHandles={this.treeView.outlineMode}
- onClick={this.onChildClick}
- focus={this.refocus}
- onKey={this.onKeyDown}
+ hideTitle={asText}
+ hideDecorations={true}
+ hideClickBehaviors={true}
hideLinkButton={BoolCast(this.treeView.Document.childHideLinkButton)}
dontRegisterView={BoolCast(this.treeView.Document.childDontRegisterViews, this._props.dontRegisterView)}
ScreenToLocalTransform={this.docTransform}
renderDepth={this._props.renderDepth + 1}
- treeViewDoc={this.treeView?.Document}
- docViewPath={this.treeView._props.docViewPath}
+ onClickScript={this.onChildClick}
+ onKey={this.onKeyDown}
+ containerViewPath={this.treeView.childContainerViewPath}
childFilters={returnEmptyFilter}
childFiltersByRanges={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
+ focus={this.refocus}
addDocument={this._props.addDocument}
moveDocument={this.move}
removeDocument={this._props.removeDoc}
@@ -1097,7 +1117,6 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
addDocTab={this._props.addDocTab}
pinToPres={this.treeView._props.pinToPres}
disableBrushing={this.treeView._props.disableBrushing}
- bringToFront={returnFalse}
scriptContext={this}
/>
</div>
@@ -1142,7 +1161,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const before = pt[1] < rect.top + rect.height / 2;
const inside = this.treeView.fileSysMode && !this.Document.isFolder ? false : pt[0] > rect.left + rect.width * 0.33 || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
- const docs = this.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, undefined, false));
+ this.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, dropActionType.copy, undefined, undefined, false, false));
};
render() {
@@ -1209,7 +1228,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
move: DragManager.MoveFunction,
dragAction: dropActionType,
addDocTab: (doc: Doc, where: OpenWhere) => boolean,
- styleProvider: undefined | StyleProviderFunc,
+ styleProvider: undefined | StyleProviderFuncType,
screenToLocalXf: () => Transform,
isContentActive: (outsideReaction?: boolean) => boolean,
panelWidth: () => number,
@@ -1311,9 +1330,3 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
});
}
}
-
-ScriptingGlobals.add(function TreeView_addNewFolder() {
- TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined };
- const opts = { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true };
- return Doc.AddDocToList(Doc.MyFilesystem, 'data', Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
-});