diff options
Diffstat (limited to 'src/client/views/collections/TreeView.tsx')
| -rw-r--r-- | src/client/views/collections/TreeView.tsx | 259 |
1 files changed, 152 insertions, 107 deletions
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index cb521ea75..93d3be1fc 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -1,6 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, trace } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; +import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss'; import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -9,7 +10,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; @@ -20,11 +21,12 @@ import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { EditableView } from "../EditableView"; -import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; -import { DocumentView } from '../nodes/DocumentView'; +import { DocumentView, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView'; +import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; import { KeyValueBox } from '../nodes/KeyValueBox'; +import { StyleProp, testDocProps } from '../StyleProvider'; import { CollectionTreeView } from './CollectionTreeView'; import { CollectionView, CollectionViewType } from './CollectionView'; import "./TreeView.scss"; @@ -39,17 +41,16 @@ export interface TreeViewProps { removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined; moveDocument: DragManager.MoveFunction; dropAction: dropActionType; - addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean; + addDocTab: (doc: Doc, where: string) => boolean; pinToPres: (document: Doc) => void; panelWidth: () => number; panelHeight: () => number; - ChromeHeight: undefined | (() => number); addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean; indentDocument?: () => void; outdentDocument?: () => void; ScreenToLocalTransform: () => Transform; dontRegisterView?: boolean; - backgroundColor?: (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined; + styleProvider?: StyleProviderFunc | undefined; outerXf: () => { translateX: number, translateY: number }; treeView: CollectionTreeView; parentKey: string; @@ -59,11 +60,13 @@ export interface TreeViewProps { renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle onCheckedClick?: () => ScriptField; onChildClick?: () => ScriptField; - ignoreFields?: string[]; + skipFields?: string[]; firstLevel: boolean; whenActiveChanged: (isActive: boolean) => void; } +const treeBulletWidth = function () { return Number(TREE_BULLET_WIDTH.replace("px", "")); }; + @observer /** * Renders a treeView of a collection of documents @@ -83,7 +86,8 @@ export class TreeView extends React.Component<TreeViewProps> { private _uniqueId = Utils.GenerateGuid(); private _editMaxWidth: number | string = 0; - @observable _dref: ContentFittingDocumentView | undefined | null; + + @observable _dref: DocumentView | undefined | null; @computed get doc() { TraceMobx(); return this.props.document; } get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive @@ -284,9 +288,9 @@ export class TreeView extends React.Component<TreeViewProps> { docWidth = () => { const layoutDoc = this.layoutDoc; const aspect = Doc.NativeAspect(layoutDoc); - if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - 20, layoutDoc[WidthSym]()); - if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - 20)); - return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : Math.min(this.layoutDoc[WidthSym](), this.props.panelWidth() - 20); + if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - treeBulletWidth(), layoutDoc[WidthSym]()); + if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - treeBulletWidth())); + return Math.min(this.props.panelWidth() - treeBulletWidth(), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]()); } docHeight = () => { const layoutDoc = this.layoutDoc; @@ -311,7 +315,7 @@ export class TreeView extends React.Component<TreeViewProps> { doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)); for (const key of Object.keys(ids).slice().sort()) { - if (this.props.ignoreFields?.includes(key) || key === "title" || key === "treeViewOpen") continue; + if (this.props.skipFields?.includes(key) || key === "title" || key === "treeViewOpen") continue; const contents = doc[key]; let contentElement: (JSX.Element | null)[] | JSX.Element = []; @@ -325,9 +329,9 @@ export class TreeView extends React.Component<TreeViewProps> { const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents), this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - 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.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged, this.props.dontRegisterView); + this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, + this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, + [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenActiveChanged, this.props.dontRegisterView); } else { contentElement = <EditableView key="editableView" contents={contents !== undefined ? Field.toString(contents as Field) : "null"} @@ -354,9 +358,9 @@ export class TreeView extends React.Component<TreeViewProps> { return rows; } - rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20); + rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - treeBulletWidth()); rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; - rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20); + rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), treeBulletWidth()); expandPanelHeight = () => { if (this.layoutDoc._fitWidth) return this.docHeight(); const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym](); @@ -376,31 +380,46 @@ export class TreeView extends React.Component<TreeViewProps> { if (["links", "annotations", this.fieldKey].includes(expandKey)) { const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey); const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => { + // if there's a sort ordering specified that can be modified on drop (eg, zorder can be modified, alphabetical can't), + // then the modification would be done here + const ordering = StrCast(this.doc[this.fieldKey + "-sortCriteria"]); + if (ordering === "zorder") { + const docs = TreeView.sortDocs(this.childDocs || ([] as Doc[]), ordering); + doc.zIndex = addBefore ? (before ? NumCast(addBefore.zIndex) - 0.5 : NumCast(addBefore.zIndex) + 0.5) : 1000; + docs.push(doc); + docs.sort((a, b) => NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1); + docs.forEach((d, i) => d.zIndex = i); + } const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true); added && (doc.context = this.doc.context); return added; }; const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs; - const sortKey = `${this.fieldKey}-sortAscending`; + const sortKey = `${this.fieldKey}-sortCriteria`; return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => { - !this.outlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true))); + !this.outlineMode && (this.doc[sortKey] = + (this.doc[sortKey] === "ascending" ? "descending" : + (this.doc[sortKey] === "descending" ? "zorder" : + (this.doc[sortKey] === "zorder" ? undefined : + "ascending")))); e.stopPropagation(); }}> {!docs ? (null) : TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc, this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, 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.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged, this.props.dontRegisterView)} + StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.titleStyleProvider, this.props.ScreenToLocalTransform, + this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, + [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenActiveChanged, this.props.dontRegisterView)} </ul >; } else if (this.treeViewExpandedView === "fields") { - return <ul key={this.doc[Id] + this.doc.title}><div style={{ display: "inline-block" }} > - {this.expandedField} - </div></ul>; - } else { - return this.renderDocument(false); + return <ul key={this.doc[Id] + this.doc.title}> + <div style={{ display: "inline-block" }} > + {this.expandedField} + </div> + </ul>; } + return <ul>{this.renderEmbeddedDocument(false)}</ul>; } get onCheckedClick() { return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); } @@ -422,20 +441,29 @@ export class TreeView extends React.Component<TreeViewProps> { @computed get renderBullet() { TraceMobx(); + const iconType = Doc.toIcon(this.doc); const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined; return <div className={`bullet${this.outlineMode ? "-outline" : ""}`} key={"bullet"} title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"} onClick={this.bulletClick} - style={this.outlineMode ? { opacity: NumCast(this.doc.opacity, 1) } : { + style={this.outlineMode ? { opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity) } : { color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}> - {this.outlineMode && !(this.doc.text as RichTextField)?.Text ? (null) : - <FontAwesomeIcon icon={this.outlineMode ? [this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"] : - checked === "check" ? "check" : - (checked === "x" ? "times" : checked === "unchecked" ? "square" : - !this.treeViewOpen ? (this.childDocs?.length ? "caret-square-right" : "caret-right") : - (this.childDocs?.length ? "caret-square-down" : "caret-down"))} />} + {this.outlineMode ? + !(this.doc.text as RichTextField)?.Text ? (null) : + <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"]} /> : + <div className="treeView-bulletIcons" > + <div className={`treeView-${this.onCheckedClick ? "checkIcon" : "expandIcon"}`}> + <FontAwesomeIcon size="sm" icon={ + checked === "check" ? "check" : + (checked === "x" ? "times" : checked === "unchecked" ? "square" : + !this.treeViewOpen ? "caret-right" : + "caret-down")} /> + </div> + {this.onCheckedClick ? (null) : <FontAwesomeIcon icon={iconType} />} + </div> + } </div>; } @computed get showTitleEditorControl() { return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || ""); } @@ -462,7 +490,7 @@ export class TreeView extends React.Component<TreeViewProps> { showContextMenu = (e: React.MouseEvent) => simulateMouseClick(this._docRef.current?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }]; - truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0); + truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, this.props.panelWidth()); onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); @@ -473,6 +501,26 @@ export class TreeView extends React.Component<TreeViewProps> { e.preventDefault(); } } + titleStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => { + if (!doc || doc !== this.doc) return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView + + switch (property.split(":")[0]) { + case StyleProp.Opacity: return this.outlineMode ? undefined : 1; + case StyleProp.BackgroundColor: return StrCast(doc._backgroundColor, StrCast(doc.backgroundColor)); + case StyleProp.DocContents: return testDocProps(props) && !props?.treeViewDoc ? (null) : + <div className="treeView-label" style={{ // just render a title for a tree view label (identified by treeViewDoc being set in 'props') + maxWidth: props?.PanelWidth() || undefined, + background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor), + }}> + {StrCast(doc?.title)} + </div>; + case StyleProp.Decorations: return (null); + } + } + embeddedStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps | 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 + } onKeyDown = (e: React.KeyboardEvent) => { if (this.doc.treeViewHideHeader || this.outlineMode) { e.stopPropagation(); @@ -497,8 +545,8 @@ export class TreeView extends React.Component<TreeViewProps> { ref={this._docRef} Document={this.doc} DataDoc={undefined} + styleProvider={this.titleStyleProvider} treeViewDoc={this.props.treeView.props.Document} - LibraryPath={emptyPath} addDocument={undefined} addDocTab={this.props.addDocTab} rootSelected={returnTrue} @@ -509,11 +557,11 @@ export class TreeView extends React.Component<TreeViewProps> { moveDocument={this.move} removeDocument={this.props.removeDoc} ScreenToLocalTransform={this.getTransform} - ContentScaling={returnOne} + NativeHeight={returnZero} + NativeWidth={returnZero} PanelWidth={this.truncateTitleWidth} - PanelHeight={returnZero} + PanelHeight={() => 18} contextMenuItems={this.contextMenuItems} - opacity={this.outlineMode ? undefined : returnOne} renderDepth={1} focus={returnTrue} parentActive={returnTrue} @@ -524,7 +572,7 @@ export class TreeView extends React.Component<TreeViewProps> { docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} - ContainingCollectionDoc={this.props.containingCollection} + ContainingCollectionDoc={this.props.treeView.props.Document} />; return <> <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title. Double Click or Drag to Open" @@ -541,15 +589,18 @@ export class TreeView extends React.Component<TreeViewProps> { } renderBulletHeader = (contents: JSX.Element) => { - return <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} key="titleheader" - ref={this._header} - style={{ maxWidth: this._editMaxWidth }} - onClick={this.ignoreEvent} - onPointerDown={this.ignoreEvent} - onPointerEnter={this.onPointerEnter} - onPointerLeave={this.onPointerLeave}> - {contents} - </div>; + return <> + <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} key="titleheader" + ref={this._header} + style={{ maxWidth: this._editMaxWidth }} + onClick={this.ignoreEvent} + onPointerDown={this.ignoreEvent} + onPointerEnter={this.onPointerEnter} + onPointerLeave={this.onPointerLeave}> + {contents} + </div> + {this.renderBorder} + </>; } // renders the text version of a document as the header (e.g., useful for Slide views where the "") @@ -560,26 +611,25 @@ export class TreeView extends React.Component<TreeViewProps> { </>; } - renderDocument = (asText: boolean) => { + renderEmbeddedDocument = (asText: boolean) => { const panelWidth = asText || StrCast(Doc.LayoutField(this.layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.expandPanelWidth; const panelHeight = asText ? this.rtfOutlineHeight : StrCast(Doc.LayoutField(this.layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.expandPanelHeight; - return <ContentFittingDocumentView key={this.doc[Id]} ref={action((r: ContentFittingDocumentView | null) => this._dref = r)} + return <DocumentView key={this.doc[Id]} ref={action((r: DocumentView | null) => this._dref = r)} Document={this.doc} DataDoc={undefined} PanelWidth={panelWidth} PanelHeight={panelHeight} NativeWidth={!asText && this.layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined} NativeHeight={!asText && this.layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined} - fitToBox={!asText && this.isCollectionDoc !== undefined} + fitContentsToDoc={true} + hideTitle={asText} LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined} focus={asText ? this.refocus : returnFalse} dontRegisterView={asText ? undefined : this.props.dontRegisterView} ScreenToLocalTransform={this.docTransform} - LibraryPath={emptyPath} renderDepth={this.props.renderDepth + 1} rootSelected={returnTrue} - treeViewDoc={undefined} - styleProvider={this.props.backgroundColor} + styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} @@ -593,21 +643,20 @@ export class TreeView extends React.Component<TreeViewProps> { addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} - ContentScaling={returnOne} />; } @computed get renderDocumentAsHeader() { return <> {this.renderBullet} - {this.renderDocument(true)} + {this.renderEmbeddedDocument(true)} </>; } @computed get renderBorder() { - const sorting = this.doc[`${this.fieldKey}-sortAscending`]; + const sorting = this.doc[`${this.fieldKey}-sortCriteria`]; return <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} - style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}> + style={{ borderColor: sorting === undefined ? undefined : sorting === "ascending" ? "crimson" : sorting === "descending" ? "blue" : "green" }}> {!this.treeViewOpen ? (null) : this.renderContent} </div>; } @@ -627,24 +676,44 @@ export class TreeView extends React.Component<TreeViewProps> { else this._editMaxWidth = ""; const hideTitle = this.doc.treeViewHideHeader || this.outlineMode; - return hideTitle && !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? - this.renderContent - : - <div className={`treeView-container${this._dref?.docView?.contentsActive() ? "-active" : ""}`} - ref={this.createTreeDropTarget} - onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()} - onKeyDown={this.onKeyDown}> - {hideTitle ? - <li className="collection-child"> - {this.renderBulletHeader(this.renderDocumentAsHeader)} - {this.renderBorder} - </li> : - <li className="collection-child"> - {this.renderBulletHeader(this.renderTitleAsHeader)} - {this.renderBorder} - </li> + return <div className={`treeView-container${this._dref?.contentsActive() ? "-active" : ""}`} + ref={this.createTreeDropTarget} + onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()} + onKeyDown={this.onKeyDown}> + <li className="collection-child"> + {hideTitle && this.doc.type !== DocumentType.RTF ? + this.renderEmbeddedDocument(false) : + this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader : this.renderTitleAsHeader)} + </li> + </div>; + } + + public static sortDocs(childDocs: Doc[], criterion: string | undefined) { + const docs = childDocs.slice(); + if (criterion) { + const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => { + const reN = /[0-9]*$/; + const aA = a.replace(reN, ""); // get rid of trailing numbers + const bA = b.replace(reN, ""); + if (aA === bA) { // if header string matches, then compare numbers numerically + const aN = parseInt(a.match(reN)![0], 10); + const bN = parseInt(b.match(reN)![0], 10); + return aN === bN ? 0 : aN > bN ? 1 : -1; + } else { + return aA > bA ? 1 : -1; } - </div>; + }; + docs.sort(function (d1, d2): 0 | 1 | -1 { + const a = (criterion === "ascending" ? d2 : d1); + const b = (criterion === "ascending" ? d1 : d2); + const first = a[criterion === "zorder" ? "zIndex" : "title"]; + const second = b[criterion === "zorder" ? "zIndex" : "title"]; + if (typeof first === 'number' && typeof second === 'number') return (first - second) > 0 ? 1 : -1; + if (typeof first === 'string' && typeof second === 'string') return sortAlphaNum(first, second); + return criterion ? 1 : -1; + }); + } + return docs; } public static GetChildElements( @@ -661,19 +730,18 @@ export class TreeView extends React.Component<TreeViewProps> { dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => boolean, pinToPres: (document: Doc) => void, - backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined), + styleProvider: undefined | StyleProviderFunc, screenToLocalXf: () => Transform, outerXf: () => { translateX: number, translateY: number }, active: (outsideReaction?: boolean) => boolean, panelWidth: () => number, - ChromeHeight: undefined | (() => number), renderDepth: number, treeViewHideHeaderFields: () => boolean, treeViewPreventOpen: boolean, renderedIds: string[], onCheckedClick: undefined | (() => ScriptField), onChildClick: undefined | (() => ScriptField), - ignoreFields: string[] | undefined, + skipFields: string[] | undefined, firstLevel: boolean, whenActiveChanged: (isActive: boolean) => void, dontRegisterView: boolean | undefined) { @@ -682,31 +750,9 @@ export class TreeView extends React.Component<TreeViewProps> { childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result); } - const docs = childDocs.slice(); - 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 - const bA = b.replace(reN, ""); - if (aA === bA) { // if header string matches, then compare numbers numerically - const aN = parseInt(a.match(reN)![0], 10); - const bN = parseInt(b.match(reN)![0], 10); - return aN === bN ? 0 : aN > bN ? 1 : -1; - } else { - return aA > bA ? 1 : -1; - } - }; - docs.sort(function (a, b): 0 | 1 | -1 { - const first = (ascending ? b : a).title; - const second = (ascending ? a : b).title; - if (typeof first === 'number' && typeof second === 'number') return (first - second) > 0 ? 1 : -1; - if (typeof first === 'string' && typeof second === 'string') return sortAlphaNum(first, second); - return ascending ? 1 : -1; - }); - } + const docs = TreeView.sortDocs(childDocs, StrCast(containingCollection?.[key + "-sortCriteria"])); - const rowWidth = () => panelWidth() - 20; + const rowWidth = () => panelWidth() - treeBulletWidth(); return docs.filter(child => child instanceof Doc).map((child, i) => { const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child); if (!pair.layout || pair.data instanceof Promise) { @@ -756,10 +802,9 @@ export class TreeView extends React.Component<TreeViewProps> { renderDepth={renderDepth} removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove} addDocument={addDocument} - backgroundColor={backgroundColor} + styleProvider={styleProvider} panelWidth={rowWidth} panelHeight={rowHeight} - ChromeHeight={ChromeHeight} dontRegisterView={dontRegisterView} moveDocument={move} dropAction={dropAction} @@ -772,7 +817,7 @@ export class TreeView extends React.Component<TreeViewProps> { treeViewHideHeaderFields={treeViewHideHeaderFields} treeViewPreventOpen={treeViewPreventOpen} renderedIds={renderedIds} - ignoreFields={ignoreFields} + skipFields={skipFields} firstLevel={firstLevel} whenActiveChanged={whenActiveChanged} />; }); |
