diff options
Diffstat (limited to 'src/client/views/collections/CollectionTreeView.tsx')
-rw-r--r-- | src/client/views/collections/CollectionTreeView.tsx | 198 |
1 files changed, 100 insertions, 98 deletions
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index f5eb534aa..37600fa4d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -27,6 +27,8 @@ import { TreeView } from "./TreeView"; import React = require("react"); export type collectionTreeViewProps = { + treeViewExpandedView?: "fields" | "layout" | "links" | "data"; + treeViewOpen?: boolean; treeViewHideTitle?: boolean; treeViewHideHeaderFields?: boolean; treeViewSkipFields?: string[]; // prevents specific fields from being displayed (see LinkBox) @@ -38,12 +40,19 @@ export type collectionTreeViewProps = { export class CollectionTreeView extends CollectionSubView<Document, Partial<collectionTreeViewProps>>(Document) { private treedropDisposer?: DragManager.DragDropDisposer; - private _isChildActive = false; private _mainEle?: HTMLDivElement; - public _uniqueId = Utils.GenerateGuid(); - - @computed get doc() { TraceMobx(); return this.props.Document; } + MainEle = () => this._mainEle; + @computed get doc() { return this.props.Document; } @computed get dataDoc() { return this.props.DataDoc || this.doc; } + @computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth()); } + @computed get treeChildren() { return this.props.childDocuments || this.childDocs; } + @computed get outlineMode() { return this.doc.treeViewType === "outline"; } + @computed get fileSysMode() { return this.doc.treeViewType === "fileSystem"; } + + componentWillUnmount() { + super.componentWillUnmount(); + this.treedropDisposer?.(); + } protected createTreeDropTarget = (ele: HTMLDivElement) => { this.treedropDisposer?.(); @@ -53,19 +62,13 @@ export class protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => { const dragData = de.complete.docDragData; if (dragData) { - if (targetAction && !dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d))) { - dragData.dropAction = targetAction; - } else dragData.dropAction = this.doc === dragData?.treeViewDoc ? "same" : dragData.dropAction; + const isInTree = () => dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d)); + dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? "same" : dragData.dropAction; } } - componentWillUnmount() { - super.componentWillUnmount(); - this.treedropDisposer?.(); - } - - @undoBatch - remove = action((doc: Doc | Doc[]): boolean => { + @action + remove = (doc: Doc | Doc[]): boolean => { const docs = doc instanceof Doc ? [doc] : doc; const targetDataDoc = this.doc[DataSym]; const value = DocListCast(targetDataDoc[this.props.fieldKey]); @@ -83,27 +86,24 @@ export class return true; } return false; - }); + } + @action - addDoc = (doc: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => { + addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => { const doAddDoc = (doc: Doc | Doc[]) => - (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => - flg && Doc.AddDocToList(this.doc[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false), true); - if (this.doc.resolvedDataDoc instanceof Promise) { - this.doc.resolvedDataDoc.then((resolved: any) => doAddDoc(doc)); - } else if (relativeTo === undefined) { - this.props.addDocument?.(doc); - } else { - doAddDoc(doc); - (doc instanceof Doc ? [doc] : doc).forEach(d => d.context = this.props.Document); - } - return true; + (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => { + const res = flg && Doc.AddDocToList(this.doc[DataSym], this.props.fieldKey, doc, relativeTo, before); + res && (doc.context = this.props.Document); + return res; + }, true); + if (this.doc.resolvedDataDoc instanceof Promise) return false; + return relativeTo === undefined ? this.props.addDocument?.(docs) || false : doAddDoc(docs); } 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 (!Doc.UserDoc().noviceMode) { const layoutItems: ContextMenuProps[] = []; - layoutItems.push({ description: (this.doc.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.doc.treeViewPreventOpen = !this.doc.treeViewPreventOpen, icon: "paint-brush" }); + layoutItems.push({ description: "Make tree state " + (this.doc.treeViewOpenIsTransient ? "persistent" : "transient"), event: () => this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient, icon: "paint-brush" }); layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" }); layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, icon: "paint-brush" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" }); @@ -113,50 +113,30 @@ export class !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); } } - outerXf = () => Utils.GetScreenTransform(this._mainEle!); onTreeDrop = (e: React.DragEvent) => this.onExternalDrop(e, {}); - @computed get renderClearButton() { - return !this.doc.allowClear ? (null) : <div key="toolbar"> - <button className="toolbar-button round-button" title="Empty" onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}> - <FontAwesomeIcon icon={"trash"} size="sm" /> - </button> - </div >; - } - @undoBatch - makeTextCollection = action((childDocs: Doc[]) => { - Doc.SetInPlace(this.doc, "editTitle", undefined, false); - const bullet = TreeView.makeTextBullet(); - bullet.context = this.doc; - this.addDoc(bullet, childDocs.length ? childDocs[0] : undefined, true); - }); + makeTextCollection = (childDocs: Doc[]) => { + this.addDoc(TreeView.makeTextBullet(), childDocs.length ? childDocs[0] : undefined, true); + } editableTitle = (childDocs: Doc[]) => { - return !this.dataDoc ? (null) : <EditableView - contents={this.dataDoc.title} - editing={false} - display={"block"} - maxHeight={72} - height={"auto"} - GetValue={() => StrCast(this.dataDoc.title)} - SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => { - if (enter) { - switch (this.props.Document.treeViewType) { - case "outline": this.makeTextCollection(childDocs); break; - case "fileSystem": break; - } - } - return Doc.SetInPlace(this.dataDoc, "title", value, false); - })} />; + return !this.dataDoc ? (null) : + <EditableView + contents={this.dataDoc.title} + display={"block"} + maxHeight={72} + height={"auto"} + GetValue={() => StrCast(this.dataDoc.title)} + SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => { + if (enter && this.props.Document.treeViewType === "outline") this.makeTextCollection(childDocs); + this.dataDoc.title = value; + return true; + })} />; } - - rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.PanelWidth() - 20); - rtfOutlineHeight = () => Math.min(this.layoutDoc?.[HeightSym](), (StrCast(this.layoutDoc?._fontSize) ? Number(StrCast(this.layoutDoc?._fontSize, "32px").replace("px", "")) : NumCast(this.layoutDoc?._fontSize)) * 2); - titleTransform = () => this.props.ScreenToLocalTransform().translate(-NumCast(this.doc._xPadding, 10), -NumCast(this.doc._yPadding, 20)); documentTitle = (childDocs: Doc[]) => { - return <div style={{ display: "inline-block", width: "100%", height: this.rtfOutlineHeight() }} key={this.doc[Id]} + return <div style={{ display: "inline-block", width: "100%", height: this.documentTitleHeight() }} key={this.doc[Id]} onKeyDown={e => { e.stopPropagation(); e.key === "Enter" && this.makeTextCollection(childDocs); @@ -166,15 +146,15 @@ export class DataDoc={undefined} LayoutTemplateString={FormattedTextBox.LayoutString("text")} renderDepth={this.props.renderDepth + 1} + isContentActive={this.isContentActive} rootSelected={returnTrue} - //dontRegisterView={true} docViewPath={this.props.docViewPath} styleProvider={this.props.styleProvider} layerProvider={this.props.layerProvider} - PanelWidth={this.rtfWidth} - PanelHeight={this.rtfOutlineHeight} - NativeWidth={this.rtfWidth} - NativeHeight={this.rtfOutlineHeight} + PanelWidth={this.documentTitleWidth} + PanelHeight={this.documentTitleHeight} + NativeWidth={this.documentTitleWidth} + NativeHeight={this.documentTitleHeight} focus={this.props.focus} ScreenToLocalTransform={this.titleTransform} docFilters={returnEmptyFilter} @@ -185,54 +165,77 @@ export class addDocument={this.props.addDocument} moveDocument={returnFalse} removeDocument={returnFalse} - parentActive={this.props.active} - whenActiveChanged={this.props.whenActiveChanged} + whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} /> </div>; } - @computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth()); } - truncateTitleWidth = () => this.treeViewtruncateTitleWidth; - @computed get outlineMode() { return this.doc.treeViewType === "outline"; } - @computed get fileSysMode() { return this.doc.treeViewType === "fileSystem"; } - onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); - whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); }; - active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive; - panelWidth = () => this.props.PanelWidth() - 20; // bcz: 20 is the 10 + 10 for the left and right padding. - @computed get treeChildren() { - TraceMobx(); - return this.props.childDocuments || this.childDocs; - } @computed get treeViewElements() { TraceMobx(); const dropAction = StrCast(this.doc.childDropAction) as dropActionType; const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument?.(d, target, addDoc) || false; - return TreeView.GetChildElements(this.treeChildren, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, - moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.styleProvider, returnTrue, this.props.ScreenToLocalTransform, - this.outerXf, this.active, this.panelWidth, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields), - BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick, - this.onChildClick, this.props.treeViewSkipFields, true, this.whenActiveChanged, this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, "boolean", null), this); + return TreeView.GetChildElements( + this.treeChildren, + this, + this, + this.doc, + this.props.DataDoc, + this.props.ContainingCollectionDoc, + undefined, + addDoc, + this.remove, + moveDoc, + dropAction, + this.props.addDocTab, + this.props.styleProvider, + this.props.ScreenToLocalTransform, + this.props.isContentActive, + this.panelWidth, + this.props.renderDepth, + () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields), + [], + this.props.onCheckedClick, + this.onChildClick, + this.props.treeViewSkipFields, + true, + this.props.whenChildContentsActiveChanged, + this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, "boolean", null)); } @computed get titleBar() { const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle; return hideTitle ? (null) : (this.doc.treeViewType === "outline" ? this.documentTitle : this.editableTitle)(this.treeChildren); } + + @computed get renderClearButton() { + return !this.doc.treeViewShowClearButton ? (null) : <div key="toolbar"> + <button className="toolbar-button round-button" title="Empty" onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}> + <FontAwesomeIcon icon={"trash"} size="sm" /> + </button> + </div >; + } + + paddingX = () => NumCast(this.doc._xPadding, 15); + documentTitleWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.panelWidth()); + documentTitleHeight = () => Math.min(this.layoutDoc?.[HeightSym](), (StrCast(this.layoutDoc?._fontSize) ? Number(StrCast(this.layoutDoc?._fontSize, "32px").replace("px", "")) : NumCast(this.layoutDoc?._fontSize)) * 2); + titleTransform = () => this.props.ScreenToLocalTransform().translate(-NumCast(this.doc._xPadding, 10), -NumCast(this.doc._yPadding, 20)); + truncateTitleWidth = () => this.treeViewtruncateTitleWidth; + onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); + panelWidth = () => this.props.PanelWidth() - 2 * this.paddingX(); + isContentActive = () => this.props.isContentActive() || this.props.isSelected(); render() { TraceMobx(); - if (!(this.doc instanceof Doc)) return (null); - const background = this.props.treeViewHideTitle && this.props.treeViewHideHeaderFields ? "#9F9F9F" : this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor); - const paddingX = `${NumCast(this.doc._xPadding, 15)}px`; - const paddingTop = `${NumCast(this.doc._yPadding, 20)}px`; - const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined; + const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor); + const paddingTop = () => `${NumCast(this.doc._yPadding, 20)}px`; + const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined; - return !this.treeChildren ? (null) : ( + return !(this.doc instanceof Doc) || !this.treeChildren ? (null) : <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}> <div className="collectionTreeView-dropTarget" - style={{ background, paddingLeft: paddingX, paddingRight: paddingX, paddingTop, pointerEvents }} - onWheel={(e) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()} + style={{ background: background(), paddingLeft: `${this.paddingX()}px`, paddingRight: `${this.paddingX()}px`, paddingTop: paddingTop(), pointerEvents: pointerEvents() }} + onWheel={e => e.stopPropagation()} onDrop={this.onTreeDrop} ref={this.createTreeDropTarget}> {this.titleBar} @@ -241,7 +244,6 @@ export class {this.treeViewElements} </ul> </div > - </div> - ); + </div>; } }
\ No newline at end of file |