diff options
| author | bobzel <zzzman@gmail.com> | 2024-03-01 08:23:06 -0500 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2024-03-01 08:23:06 -0500 |
| commit | 25474b83f908732b2618cb7110f1e410030f9280 (patch) | |
| tree | a942453765eb876ffaa3899d623fa77e13a196b4 /src/client/views/collections/TreeView.tsx | |
| parent | 4e837a73f5fae06368416f99c047d78f6b94565b (diff) | |
| parent | 3179048be75fb7662fc472249798b2d103dc5544 (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.tsx | 137 |
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)); -}); |
