diff options
| author | mehekj <mehek.jethani@gmail.com> | 2022-02-03 18:03:52 -0500 | 
|---|---|---|
| committer | mehekj <mehek.jethani@gmail.com> | 2022-02-03 18:03:52 -0500 | 
| commit | 922747ad959e95b592b4cde951b31f5503b8970b (patch) | |
| tree | e25d747ba02b4cbfd76910b862b6aac104614daf /src/client/views/collections/CollectionTreeView.tsx | |
| parent | 30369cd78c1815a81bfe153c5a2d4551ad90dbe0 (diff) | |
| parent | 4cdfa6c29701d372064eb4dc612807a27cb19857 (diff) | |
Merge branch 'master' into temporalmedia-mehek
Diffstat (limited to 'src/client/views/collections/CollectionTreeView.tsx')
| -rw-r--r-- | src/client/views/collections/CollectionTreeView.tsx | 244 | 
1 files changed, 138 insertions, 106 deletions
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 3852987b9..ea077ea40 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,4 +1,3 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';  import { action, computed, IReactionDisposer, observable, reaction } from "mobx";  import { observer } from "mobx-react";  import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc'; @@ -8,13 +7,14 @@ import { Document, listSpec } from '../../../fields/Schema';  import { ScriptField } from '../../../fields/ScriptField';  import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';  import { TraceMobx } from '../../../fields/util'; -import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, emptyFunction } from '../../../Utils'; +import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnOne } from '../../../Utils';  import { DocUtils } from '../../documents/Documents';  import { CurrentUserUtils } from '../../util/CurrentUserUtils';  import { DocumentManager } from '../../util/DocumentManager';  import { DragManager, dropActionType } from "../../util/DragManager";  import { SelectionManager } from '../../util/SelectionManager';  import { SnappingManager } from '../../util/SnappingManager'; +import { Transform } from '../../util/Transform';  import { undoBatch, UndoManager } from '../../util/UndoManager';  import { ContextMenu } from '../ContextMenu';  import { ContextMenuProps } from '../ContextMenuItem'; @@ -22,11 +22,11 @@ import { EditableView } from "../EditableView";  import { DocumentView } from '../nodes/DocumentView';  import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';  import { StyleProp } from '../StyleProvider'; +import { CollectionFreeFormView } from './collectionFreeForm';  import { CollectionSubView } from "./CollectionSubView";  import "./CollectionTreeView.scss";  import { TreeView } from "./TreeView";  import React = require("react"); -import { Transform } from '../../util/Transform';  const _global = (window /* browser */ || global /* node */) as any;  export type collectionTreeViewProps = { @@ -41,10 +41,14 @@ export type collectionTreeViewProps = {  @observer  export class CollectionTreeView extends CollectionSubView<Document, Partial<collectionTreeViewProps>>(Document) { -    private treedropDisposer?: DragManager.DragDropDisposer; +    private _treedropDisposer?: DragManager.DragDropDisposer;      private _mainEle?: HTMLDivElement; +    private _titleRef?: HTMLDivElement | HTMLInputElement | null;      private _disposers: { [name: string]: IReactionDisposer } = {}; -    MainEle = () => this._mainEle; +    private _isDisposing = false; // notes that instance is in process of being disposed +    private refList: Set<any> = new Set(); // list of tree view items to monitor for height changes +    private observer: any; // observer for monitoring tree view items. +    private static expandViewLabelSize = 20;      @computed get doc() { return this.props.Document; }      @computed get dataDoc() { return this.props.DataDoc || this.doc; } @@ -54,6 +58,10 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll      @computed get fileSysMode() { return this.doc.treeViewType === "fileSystem"; }      @computed get dashboardMode() { return this.doc === Doc.UserDoc().myDashboards; } +    @observable _explainerHeight = 0; // height of the description of the tree view + +    MainEle = () => this._mainEle; +      // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent      @observable _isAnyChildContentActive = false;      whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive)); @@ -62,11 +70,10 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll              this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||              this.props.rootSelected(outsideReaction)) ? true : false) -    isDisposing = false;      componentWillUnmount() { -        this.isDisposing = true; +        this._isDisposing = true;          super.componentWillUnmount(); -        this.treedropDisposer?.(); +        this._treedropDisposer?.();          Object.values(this._disposers).forEach(disposer => disposer?.());      } @@ -76,13 +83,13 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll              { fireImmediately: true });      } -    refList: Set<any> = new Set(); -    observer: any;      computeHeight = () => { -        if (this.isDisposing) return; -        const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.paddingTop() + this.paddingBot()); -        this.layoutDoc._autoHeightMargins = bodyHeight; -        this.props.setHeight(this.documentTitleHeight() + bodyHeight); +        if (!this._isDisposing) { +            const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace("px", "")); +            const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.marginBot()); +            this.layoutDoc._autoHeightMargins = bodyHeight; +            this.props.setHeight(bodyHeight + titleHeight); +        }      }      unobserveHeight = (ref: any) => {          this.refList.delete(ref); @@ -101,8 +108,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll          }      }      protected createTreeDropTarget = (ele: HTMLDivElement) => { -        this.treedropDisposer?.(); -        if (this._mainEle = ele) this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this)); +        this._treedropDisposer?.(); +        if (this._mainEle = ele) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));      }      protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => { @@ -165,60 +172,44 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll          this.addDoc(TreeView.makeTextBullet(), childDocs.length ? childDocs[0] : undefined, true);      } -    editableTitle = (childDocs: Doc[]) => { -        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; -                })} />; +    get editableTitle() { +        return <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(this.treeChildren); +                this.dataDoc.title = value; +                return true; +            })} />;      } -    documentTitle = (childDocs: Doc[]) => { -        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); -            }}> -            <DocumentView -                Document={this.doc} -                DataDoc={undefined} -                LayoutTemplateString={FormattedTextBox.LayoutString("text")} -                renderDepth={this.props.renderDepth + 1} -                isContentActive={this.isContentActive} -                isDocumentActive={this.isContentActive} -                rootSelected={returnTrue} -                docViewPath={this.props.docViewPath} -                styleProvider={this.props.styleProvider} -                layerProvider={this.props.layerProvider} -                PanelWidth={this.documentTitleWidth} -                PanelHeight={this.documentTitleHeight} -                NativeWidth={this.documentTitleWidth} -                NativeHeight={this.documentTitleHeight} -                focus={this.props.focus} -                treeViewDoc={this.props.Document} -                ScreenToLocalTransform={this.titleTransform} -                docFilters={returnEmptyFilter} -                docRangeFilters={returnEmptyFilter} -                searchFilterDocs={returnEmptyDoclist} -                ContainingCollectionDoc={this.doc} -                ContainingCollectionView={this.props.CollectionView} -                addDocument={this.props.addDocument} -                moveDocument={returnFalse} -                removeDocument={returnFalse} -                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} -                addDocTab={this.props.addDocTab} -                pinToPres={this.props.pinToPres} -                bringToFront={returnFalse} -            /> -        </div>; +    get documentTitle() { +        return <FormattedTextBox +            {...this.props} +            fieldKey={"text"} +            renderDepth={this.props.renderDepth + 1} +            isContentActive={this.isContentActive} +            isDocumentActive={this.isContentActive} +            rootSelected={returnTrue} +            forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight +            PanelWidth={this.documentTitleWidth} +            PanelHeight={this.documentTitleHeight} +            scaling={returnOne} +            docFilters={returnEmptyFilter} +            docRangeFilters={returnEmptyFilter} +            searchFilterDocs={returnEmptyDoclist} +            ContainingCollectionDoc={this.doc} +            ContainingCollectionView={this.props.CollectionView} +            addDocument={returnFalse} +            moveDocument={returnFalse} +            removeDocument={returnFalse} +            whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} +            bringToFront={returnFalse} +        />;      }      childContextMenuItems = () => {          const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []); @@ -263,21 +254,31 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll          );      }      @computed get titleBar() { -        const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle; -        return hideTitle ? (null) : (this.outlineMode ? this.documentTitle : this.editableTitle)(this.treeChildren); +        return this.dataDoc === null ? (null) : +            <div className="collectionTreeView-titleBar" key={this.doc[Id]} +                style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}} +                ref={r => this._titleRef = r} +                onKeyDown={e => { +                    if (this.outlineMode) { +                        e.stopPropagation(); +                        e.key === "Enter" && this.makeTextCollection(this.treeChildren); +                    } +                }}> +                {this.outlineMode ? this.documentTitle : this.editableTitle} +            </div>; +    } + +    @computed get noviceExplainer() { +        return !Doc.UserDoc().noviceMode || !this.rootDoc.explainer ? (null) : +            <div className="documentExplanation"> {this.rootDoc.explainer} </div>;      }      return35 = () => 35;      @computed get buttonMenu() { -        const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null); +        const menuDoc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);          // To create a multibutton menu add a CollectionLinearView -        if (menuDoc) { - -            const width: number = NumCast(menuDoc._width, 30); -            const height: number = NumCast(menuDoc._height, 30); -            console.log(menuDoc.title, width, height); -            return (<div className="buttonMenu-docBtn" -                style={{ width: width, height: height }}> +        return !menuDoc ? null : +            (<div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>                  <DocumentView                      Document={menuDoc}                      DataDoc={menuDoc} @@ -306,11 +307,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll                      ContainingCollectionDoc={undefined}                  />              </div>); -        }      } -    @observable _explainerHeight: number = 0; -      @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); }      @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); } @@ -321,47 +319,81 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll          const wscale = nw ? this.props.PanelWidth() / nw : 1;          return wscale < hscale ? wscale : hscale;      } -    paddingX = () => NumCast(this.doc._xPadding, 15); -    paddingTop = () => NumCast(this.doc._yPadding, 20); -    paddingBot = () => NumCast(this.doc._yPadding, 20); +    marginX = () => NumCast(this.doc._xMargin); +    marginTop = () => NumCast(this.doc._yMargin); +    marginBot = () => NumCast(this.doc._yMargin);      documentTitleWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.panelWidth());      documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.autoHeightMargins); -    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()) * (this.props.scaling?.() || 1); -    render() { -        TraceMobx(); +    panelWidth = () => Math.max(0, this.props.PanelWidth() - this.marginX() - CollectionTreeView.expandViewLabelSize) * (this.props.scaling?.() || 1); + +    addAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.addDocument(doc, `${this.props.fieldKey}-annotations`) || false; +    remAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false; +    moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) => +        this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false + +    contentFunc = () => {          const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);          const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined; -        const buttonMenu = this.rootDoc.buttonMenu; -        const noviceExplainer = this.rootDoc.explainer; - -        return !(this.doc instanceof Doc) || !this.treeChildren ? (null) : -            <> -                {this.titleBar} +        const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? (null) : this.titleBar; +        return [ +            <div className="collectionTreeView-contents" key="tree" style={{ +                ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}), +                overflow: "auto", +                height: this.layoutDoc._autoHeight ? "max-content" : "100%" +            }} > +                {titleBar}                  <div className="collectionTreeView-container" -                    style={this.outlineMode ? { transform: `scale(${this.contentScaling})`, width: `calc(${100 / this.contentScaling}%)` } : {}} +                    style={{ +                        transform: this.outlineMode ? `scale(${this.contentScaling})` : "", +                        paddingLeft: `${this.marginX()}px`, +                        height: "max-content", +                        width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : "" +                    }}                      onContextMenu={this.onContextMenu}> -                    {buttonMenu || noviceExplainer ? <div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}> -                        {buttonMenu ? this.buttonMenu : null} -                        {Doc.UserDoc().noviceMode && noviceExplainer ? -                            <div className="documentExplanation"> -                                {noviceExplainer} -                            </div> -                            : null -                        } -                    </div> : null} +                    {!this.buttonMenu && !this.noviceExplainer ? (null) : +                        <div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}> +                            {this.buttonMenu} +                            {this.noviceExplainer} +                        </div> +                    }                      <div className="collectionTreeView-dropTarget" -                        style={{ background: background(), height: `calc(100% - ${this._explainerHeight}px)`, paddingLeft: `${this.paddingX()}px`, paddingRight: `${this.paddingX()}px`, paddingBottom: `${this.paddingBot()}px`, paddingTop: `${this.paddingTop()}px`, pointerEvents: pointerEvents() }} +                        style={{ +                            background: background(), +                            height: `calc(100% - ${this._explainerHeight}px)`, +                            pointerEvents: pointerEvents() +                        }}                          onWheel={e => e.stopPropagation()}                          onDrop={this.onTreeDrop} -                        ref={this.createTreeDropTarget}> +                        ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}>                          <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >                              {this.treeViewElements}                          </ul>                      </div >                  </div> -            </>; +            </div> +        ]; +    } +    render() { +        TraceMobx(); + +        return !(this.doc instanceof Doc) || !this.treeChildren ? (null) : +            this.doc.treeViewHasOverlay ? +                <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} +                    isAnnotationOverlay={true} +                    isAnnotationOverlayScrollable={true} +                    childDocumentsActive={this.props.isDocumentActive} +                    fieldKey={this.props.fieldKey + "-annotations"} +                    dropAction={"move"} +                    select={emptyFunction} +                    addDocument={this.addAnnotationDocument} +                    removeDocument={this.remAnnotationDocument} +                    moveDocument={this.moveAnnotationDocument} +                    bringToFront={emptyFunction} +                    renderDepth={this.props.renderDepth + 1} > +                    {this.contentFunc} +                </CollectionFreeFormView> : +                this.contentFunc();      }  }
\ No newline at end of file  | 
