diff options
| author | usodhi <61431818+usodhi@users.noreply.github.com> | 2021-04-04 13:38:55 -0400 |
|---|---|---|
| committer | usodhi <61431818+usodhi@users.noreply.github.com> | 2021-04-04 13:38:55 -0400 |
| commit | 15c1c952c50bb2de972c614e46c5f33c2b2952cc (patch) | |
| tree | e8d7330336b80939e82749290c22ef5a05b01224 /src/client/views/collections | |
| parent | 479dff344ff2cf92ace9c68c3ce6d03e6e6dce22 (diff) | |
| parent | ee17752109ba1238d645a4df7cee1cf60855f8df (diff) | |
merging
Diffstat (limited to 'src/client/views/collections')
23 files changed, 395 insertions, 552 deletions
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index f0b9b5240..b2ae441d6 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -54,7 +54,6 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume PanelHeight={this.panelHeight} ScreenToLocalTransform={this.props.ScreenToLocalTransform} bringToFront={returnFalse} - parentActive={this.props.active} />; }; @@ -127,7 +126,7 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume } @computed get buttons() { - if (!this.props.active()) return null; + if (!this.props.isContentActive()) return null; return <div className="arrow-buttons" > <div key="back" className="carousel3DView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={(e) => this.onArrowClick(e, -1)} diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index f400ac5a2..cc90b9134 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -58,7 +58,6 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) PanelHeight={this.panelHeight} ScreenToLocalTransform={this.props.ScreenToLocalTransform} bringToFront={returnFalse} - parentActive={this.props.active} /> </div> <div className="collectionCarouselView-caption" key="caption" diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 7e89cf55d..388f9a909 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -353,7 +353,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { if (clone) { const cloned = (await Doc.MakeClone(doc)); Array.from(cloned.map.entries()).map(entry => json = json.replace(entry[0], entry[1][Id])); - Doc.SetInPlace(cloned.clone, "dockingConfig", json, true); + Doc.GetProto(cloned.clone).dockingConfig = json; return cloned.clone; } const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); @@ -363,7 +363,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { const origtabdocs = DocListCast(origtab.data); const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true) : Doc.MakeAlias(origtab); const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeAlias(origtabdoc)); - newtabdocs.length && Doc.SetInPlace(newtab, "data", new List<Doc>(newtabdocs), true); + newtabdocs.length && (Doc.GetProto(newtab).data = new List<Doc>(newtabdocs)); json = json.replace(origtab[Id], newtab[Id]); return newtab; }); @@ -437,9 +437,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { } render() { - return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef}> - {this.props.renderDepth > 0 ? "Nested dashboards can't be rendered" : (null)} - </div>; + return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef} />; } } diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index ead0ce6c4..e0b90304b 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -7,12 +7,13 @@ import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { makeInterface } from '../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, returnTrue, Utils, emptyPath, returnEmptyDoclist } from '../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, Utils } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { Transform } from '../../util/Transform'; import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; import { DocumentView } from '../nodes/DocumentView'; import { LinkDescriptionPopup } from '../nodes/LinkDescriptionPopup'; +import { StyleProp } from '../StyleProvider'; import "./CollectionLinearView.scss"; import { CollectionSubView } from './CollectionSubView'; import { CollectionViewType } from './CollectionView'; @@ -37,14 +38,13 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { } componentDidMount() { - // is there any reason this needs to exist? -syip. yes, it handles autoHeight for stacking views (masonry isn't yet supported). - this._widthDisposer = reaction(() => 5 + (this.props.Document.linearViewIsExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10), - width => this.childDocs.length && (this.props.Document._width = width), + this._widthDisposer = reaction(() => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10), + width => this.childDocs.length && (this.layoutDoc._width = width), { fireImmediately: true } ); this._selectedDisposer = reaction( - () => NumCast(this.props.Document.selectedIndex), + () => NumCast(this.layoutDoc.selectedIndex), (i) => runInAction(() => { this._selectedIndex = i; let selected: any = undefined; @@ -71,7 +71,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { } } - dimension = () => NumCast(this.props.Document._height); // 2 * the padding + dimension = () => NumCast(this.rootDoc._height); // 2 * the padding getTransform = (ele: React.RefObject<HTMLDivElement>) => () => { if (!ele.current) return Transform.Identity(); const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current); @@ -109,21 +109,21 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { render() { const guid = Utils.GenerateGuid(); const flexDir: any = StrCast(this.Document.flexDirection); - const backgroundColor = StrCast(this.props.Document.backgroundColor, "black"); - const color = StrCast(this.props.Document.color, "white"); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const menuOpener = <label htmlFor={`${guid}`} style={{ pointerEvents: "all", cursor: "pointer", background: backgroundColor === color ? "black" : backgroundColor, }} onPointerDown={e => e.stopPropagation()} > - <p>{BoolCast(this.props.Document.linearViewIsExpanded) ? "–" : "+"}</p> + <p>{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "–" : "+"}</p> </label>; return <div className="collectionLinearView-outer"> <div className="collectionLinearView" ref={this.createDashEventsTarget} > - <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close menu" : "Open menu"}</div></>} placement="top"> + <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "Close menu" : "Open menu"}</div></>} placement="top"> {menuOpener} </Tooltip> - <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle} - onChange={action(() => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} /> + <input id={`${guid}`} type="checkbox" checked={BoolCast(this.layoutDoc.linearViewIsExpanded)} ref={this.addMenuToggle} + onChange={action(() => this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked)} /> <div className="collectionLinearView-content" style={{ height: this.dimension(), flexDirection: flexDir }}> {this.childLayoutPairs.map((pair, ind) => { @@ -140,6 +140,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { <DocumentView Document={pair.layout} DataDoc={pair.data} + isContentActive={returnFalse} + isDocumentActive={returnTrue} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} addDocTab={this.props.addDocTab} @@ -154,8 +156,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { styleProvider={this.props.styleProvider} layerProvider={this.props.layerProvider} docViewPath={returnEmptyDoclist} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={this.props.docFilters} docRangeFilters={this.props.docRangeFilters} diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 92288c1f2..2d7569d45 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -208,7 +208,7 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap render() { const { childLayoutPairs } = this; - const { Document, fieldKey, active, google } = this.props; + const { Document, fieldKey, isContentActive: active, google } = this.props; const mapLoc = this.getLocation(this.rootDoc, `${fieldKey}-mapCenter`, false); let center = mapLoc; if (center === undefined) { diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index ba701b2a4..a94e706eb 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -281,7 +281,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ""), }}> {this.props.parent.children(this.props.docList)} - {this.props.showHandle && this.props.parent.props.active() ? this.props.parent.columnDragger : (null)} + {this.props.showHandle && this.props.parent.props.isContentActive() ? this.props.parent.columnDragger : (null)} </div> </div>; } diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index aaf243567..623e05b33 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -341,8 +341,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp } @computed get viewModes() { - const excludedViewTypes = Doc.UserDoc().noviceMode ? [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Stacking, CollectionViewType.Map, CollectionViewType.Linear] : - [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear]; + const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear]; const isPres: boolean = (this.document && this.document.type === DocumentType.PRES); return isPres ? (null) : (<div className="collectionViewBaseChrome-viewModes" > <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom"> diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index a265045b8..b33c437a9 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -328,7 +328,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action onWheel(e: React.WheelEvent) { const scale = this.props.ScreenToLocalTransform().Scale; - this.props.active(true) && e.stopPropagation(); + this.props.isContentActive(true) && e.stopPropagation(); } @computed get renderMenuContent() { @@ -410,6 +410,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { rootSelected={this.rootSelected} PanelWidth={this.previewWidth} PanelHeight={this.previewHeight} + isContentActive={returnTrue} + isDocumentActive={returnFalse} ScreenToLocalTransform={this.getPreviewTransform} docFilters={this.docFilters} docRangeFilters={this.docRangeFilters} @@ -422,8 +424,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { moveDocument={this.props.moveDocument} addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} - parentActive={this.props.active} - whenActiveChanged={this.props.whenActiveChanged} + whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} @@ -445,7 +446,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { renderDepth={this.props.renderDepth} moveDocument={this.props.moveDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} - active={this.props.active} + active={this.props.isContentActive} onDrop={this.onExternalDrop} addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} @@ -534,11 +535,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { this.columns = columns; } - onZoomMenu = (e: React.WheelEvent) => this.props.active(true) && e.stopPropagation(); + onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation(); render() { TraceMobx(); - if (!this.props.active()) setTimeout(() => this.closeHeader(), 0); + if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0); const menuContent = this.renderMenuContent; const menu = <div className="collectionSchema-header-menu" onWheel={e => this.onZoomMenu(e)} @@ -554,21 +555,21 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { return <div className={"collectionSchemaView" + (this.props.Document._searchDoc ? "-searchContainer" : "-container")} style={{ overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white", - pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined, + pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined, width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", }} > <div className="collectionSchemaView-tableContainer" style={{ width: `calc(100% - ${this.previewWidth()}px)` }} onContextMenu={this.onSpecificMenu} onPointerDown={this.onPointerDown} - onWheel={e => this.props.active(true) && e.stopPropagation()} + onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} onDrop={e => this.onExternalDrop(e, {})} ref={this.createTarget}> {this.schemaTable} </div> {this.dividerDragger} {!this.previewWidth() ? (null) : this.previewPanel} - {this._headerOpen && this.props.active() ? menu : null} + {this._headerOpen && this.props.isContentActive() ? menu : null} </div>; } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index c0cebf021..6a1242f20 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -32,10 +32,10 @@ export type CollectionStackedTimelineProps = { playFrom: (seekTimeInSeconds: number, endTime?: number) => void; playing: () => boolean; setTime: (time: number) => void; - isChildActive: () => boolean; startTag: string; endTag: string; mediaPath: string; + dictationKey: string; }; @observer @@ -114,7 +114,7 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument onPointerDownTimeline = (e: React.PointerEvent): void => { const rect = this._timeline?.getBoundingClientRect(); const clientX = e.clientX; - if (rect && this.props.active()) { + if (rect && this.props.isContentActive()) { const wasPlaying = this.props.playing(); if (wasPlaying) this.props.Pause(); const wasSelecting = CollectionStackedTimeline.SelectingRegion === this; @@ -145,7 +145,7 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument e.shiftKey && CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime); !wasPlaying && doubleTap && this.props.Play(); }, - this.props.isSelected(true) || this.props.isChildActive(), undefined, + this.props.isSelected(true) || this.props.isContentActive(), undefined, () => !wasPlaying && this.props.setTime((clientX - rect.x) / rect.width * this.duration)); } } @@ -238,18 +238,19 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument timelineContentHeight = () => this.props.PanelHeight() * 2 / 3; dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight()); @computed get renderDictation() { - const dictation = Cast(this.dataDoc[this.props.fieldKey.replace("annotations", "dictation")], Doc, null); + const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null); return !dictation ? (null) : <div style={{ position: "absolute", height: this.dictationHeight(), top: this.timelineContentHeight(), background: "tan" }}> <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} Document={dictation} PanelHeight={this.dictationHeight} isAnnotationOverlay={true} + isDocumentActive={returnFalse} select={emptyFunction} scaling={returnOne} xMargin={25} yMargin={10} ScreenToLocalTransform={this.dictationScreenToLocalTransform} - whenActiveChanged={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} removeDocument={returnFalse} moveDocument={returnFalse} addDocument={returnFalse} @@ -274,7 +275,7 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument const overlaps: { anchorStartTime: number, anchorEndTime: number, level: number }[] = []; const drawAnchors = this.childDocs.map(anchor => ({ level: this.getLevel(anchor, overlaps), anchor })); const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2; - const isActive = this.props.isChildActive() || this.props.isSelected(false); + const isActive = this.props.isContentActive() || this.props.isSelected(false); return <div className="collectionStackedTimeline" ref={(timeline: HTMLDivElement | null) => this._timeline = timeline} onClick={e => isActive && StopEvent(e)} onPointerDown={e => isActive && this.onPointerDownTimeline(e)}> {drawAnchors.map(d => { @@ -323,7 +324,6 @@ interface StackedTimelineAnchorProps { toTimeline: (screen_delta: number, width: number) => number; playLink: (linkDoc: Doc) => void; setTime: (time: number) => void; - isChildActive: () => boolean; startTag: string; endTag: string; renderDepth: number; @@ -395,11 +395,11 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> renderDepth={this.props.renderDepth + 1} LayoutTemplate={undefined} LayoutTemplateString={LabelBox.LayoutString("data")} + isDocumentActive={returnFalse} PanelWidth={() => width} PanelHeight={() => height} ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-x, -y)} focus={focusFunc} - parentActive={out => this.props.isSelected(out) || this.props.isChildActive()} rootSelected={returnFalse} onClick={script} onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 23c63561c..cc5a41c72 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -196,7 +196,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, }); } - styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => { + styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => { if (property === StyleProp.Opacity && doc) { if (this.props.childOpacity) { return this.props.childOpacity(); @@ -207,6 +207,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, } return this.props.styleProvider?.(doc, props, property); } + isContentActive = () => this.props.isSelected() || this.props.isContentActive(); getDisplayDoc(doc: Doc, width: () => number) { const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc; const height = () => this.getDocHeight(doc); @@ -224,6 +225,8 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, layerProvider={this.props.layerProvider} docViewPath={this.props.docViewPath} fitWidth={this.props.childFitWidth} + isContentActive={returnFalse} + isDocumentActive={this.isContentActive} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} freezeDimensions={this.props.childFreezeDimensions} @@ -248,8 +251,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)} - parentActive={this.props.active} - whenActiveChanged={this.props.whenActiveChanged} + whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} addDocTab={this.addDocTab} bringToFront={returnFalse} scriptContext={this.props.scriptContext} @@ -534,14 +536,14 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"} ref={this.createRef} style={{ - overflowY: this.props.active() ? "auto" : "hidden", + overflowY: this.props.isContentActive() ? "auto" : "hidden", background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor), pointerEvents: this.backgroundEvents ? "all" : undefined }} onScroll={action(e => this._scroll = e.currentTarget.scrollTop)} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu} - onWheel={e => this.props.active(true) && e.stopPropagation()} > + onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} > {this.renderedSections} {!this.showAddAGroup ? (null) : <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 28f4f76be..79fab8b62 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -442,25 +442,23 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: }); } } - this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument); - batch.end(); + this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end); } slowLoadDocuments = async (files: File[], options: DocumentOptions, generatedDocuments: Doc[], text: string, completed: (() => void) | undefined, clientX: number, clientY: number, addDocument: (doc: Doc | Doc[]) => boolean) => { - runInAction(() => CollectionSubViewLoader.Waiting = "block"); const disposer = OverlayView.Instance.addElement( <ReactLoading type={"spinningBubbles"} color={"green"} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 }); generatedDocuments.push(...await DocUtils.uploadFilesToDocs(files, options)); if (generatedDocuments.length) { const set = generatedDocuments.length > 1 && generatedDocuments.map(d => DocUtils.iconify(d)); if (set) { - UndoManager.RunInBatch(() => addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!), "drop"); + addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!); } else { - UndoManager.RunInBatch(() => generatedDocuments.forEach(addDocument), "drop"); + generatedDocuments.forEach(addDocument); } completed?.(); } else { if (text && !text.includes("https://")) { - UndoManager.RunInBatch(() => addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop"); + addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 })); } else { alert("Document upload failed - possibly an unsupported file type."); } @@ -472,10 +470,6 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: return CollectionSubView; } -export class CollectionSubViewLoader { - @observable public static Waiting = "none"; -} - import { DragManager, dropActionType } from "../../util/DragManager"; import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 0702febae..f41043179 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -136,7 +136,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { } @computed get contents() { - return <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this.props.active() ? undefined : "none" }} + return <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this.props.isContentActive() ? undefined : "none" }} onClick={this.contentsDown}> <CollectionFreeFormView {...this.props} engineProps={{ pivotField: this.pivotField, docFilters: this.docFilters, docRangeFilters: this.docRangeFilters }} 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 diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 1e693f594..91f159d8e 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -2,26 +2,23 @@ import { action, computed, observable } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app -import { DateField } from '../../../fields/DateField'; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, DocListCastAsync, Field } from '../../../fields/Doc'; +import { Doc, DocListCast } from '../../../fields/Doc'; +import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; -import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; +import { makeInterface } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; -import { Cast, ScriptCast, StrCast, DateCast } from '../../../fields/Types'; -import { denormalizeEmail, distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; -import { returnFalse } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; -import { BranchTask, BranchCreate } from '../../documents/Gitlike'; -import { DocumentType } from '../../documents/DocumentTypes'; +import { Cast, ScriptCast, StrCast } from '../../../fields/Types'; +import { TraceMobx } from '../../../fields/util'; +import { DocUtils } from '../../documents/Documents'; +import { BranchCreate, BranchTask } from '../../documents/Gitlike'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; import { InteractionUtils } from '../../util/InteractionUtils'; -import { UndoManager } from '../../util/UndoManager'; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; -import { Touchable } from '../Touchable'; import { CollectionCarousel3DView } from './CollectionCarousel3DView'; import { CollectionCarouselView } from './CollectionCarouselView'; import { CollectionDockingView } from "./CollectionDockingView"; @@ -66,8 +63,6 @@ export enum CollectionViewType { export interface CollectionViewProps extends FieldViewProps { isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) layoutEngine?: () => string; - parentActive: (outsideReaction: boolean) => boolean; - filterAddDocument?: (doc: Doc | Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; // property overrides for child documents @@ -84,19 +79,20 @@ export interface CollectionViewProps extends FieldViewProps { childClickScript?: ScriptField; childDoubleClickScript?: ScriptField; } + +type CollectionDocument = makeInterface<[typeof documentSchema]>; +const CollectionDocument = makeInterface(documentSchema); @observer -export class CollectionView extends Touchable<CollectionViewProps> { +export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps, CollectionDocument>(CollectionDocument, "") { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } - _isChildActive = false; //TODO should this be observable? - @observable private _curLightboxImg = 0; @observable private static _safeMode = false; public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; } protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; get collectionViewType(): CollectionViewType | undefined { - const viewField = StrCast(this.props.Document._viewType); + const viewField = StrCast(this.layoutDoc._viewType); if (CollectionView._safeMode) { switch (viewField) { case CollectionViewType.Freeform: @@ -107,133 +103,16 @@ export class CollectionView extends Touchable<CollectionViewProps> { return viewField as any as CollectionViewType; } - active = (outsideReaction?: boolean) => { - return this.props.renderDepth === 0 || - this.props.isSelected(outsideReaction) || - this.props.rootSelected(outsideReaction) || - (this.props.layerProvider?.(this.props.Document) !== false && (this.props.Document.forceActive || this.props.Document._isGroup)) || - this._isChildActive ? - true : - false; - } - - whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive); - - /** - * Applies the collection/dashboard's current filter attributes to the doc being added - */ - addFilterAttributes = (doc: Doc) => { - Cast((FilterBox._filterScope === "Current Collection" ? this.props.Document : CurrentUserUtils.ActiveDashboard)._docFilters, listSpec("string"))?.forEach(attribute => { - if (attribute.charAt(0).toUpperCase() === attribute.charAt(0)) { - const fields = attribute.split(':'); - if (fields[2] === "check") doc[DataSym][fields[0]] = fields[1]; - else if (fields[2] === "x" && doc[DataSym][fields[0]] === fields[1]) doc[DataSym][fields[0]] = undefined; - } - }); - } - - @action.bound - addDocument = (doc: Doc | Doc[]): boolean => { - if (this.props.filterAddDocument?.(doc) === false) { - return false; - } - - const docs = doc instanceof Doc ? [doc] : doc; - - if (docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document))) return false; - const targetDataDoc = this.props.Document[DataSym]; - const docList = DocListCast(targetDataDoc[this.props.fieldKey]); - const added = docs.filter(d => !docList.includes(d)); - const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); - - if (added.length) { - if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) { - return false; - } - else { - if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym])) { - added.forEach(d => { - for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true); - //else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true); - //else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); - } - }); - } - - if (effectiveAcl === AclAddonly) { - added.map(doc => { - this.props.layerProvider?.(doc, true);// assigns layer values to the newly added document... testing the utility of this - Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc); - doc.context = this.props.Document; - }); - } - else { - added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => { // only make a pushpin if we have acl's to edit the document - DocUtils.LeavePushpin(doc); - doc._stayInCollection = undefined; - doc.context = this.props.Document; - this.addFilterAttributes(doc); // - }); - added.map(doc => this.props.layerProvider?.(doc, true));// assigns layer values to the newly added document... testing the utility of this - (targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added); - targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); - } - } - } - return true; - } - - @action.bound - removeDocument = (doc: any): boolean => { - const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); - const indocs = doc instanceof Doc ? [doc] : doc as Doc[]; - const docs = indocs.filter(doc => effectiveAcl === AclEdit || effectiveAcl === AclAdmin || GetEffectiveAcl(doc) === AclAdmin); - if (docs.length) { - const targetDataDoc = this.props.Document[DataSym]; - const value = DocListCast(targetDataDoc[this.props.fieldKey]); - const toRemove = value.filter(v => docs.includes(v)); - if (toRemove.length !== 0) { - const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc; - toRemove.forEach(doc => { - const ind = (targetDataDoc[this.props.fieldKey] as List<Doc>).indexOf(doc); - if (ind !== -1) { - Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc); - doc.context = undefined; - recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); - } - }); - return true; - } - } - return false; - } - - // this is called with the document that was dragged and the collection to move it into. - // if the target collection is the same as this collection, then the move will be allowed. - // otherwise, the document being moved must be able to be removed from its container before - // moving it into the target. - @action.bound - moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { - if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { - return true; - } - const first = doc instanceof Doc ? doc : doc[0]; - if (!first?._stayInCollection && addDocument !== returnFalse) { - return UndoManager.RunInTempBatch(() => this.removeDocument(doc) && addDocument(doc)); - } - return false; - } - showIsTagged = () => { return (null); // this section would display an icon in the bototm right of a collection to indicate that all // photos had been processed through Google's content analysis API and Google's tags had been // assigned to the documents googlePhotosTags field. - // const children = DocListCast(this.props.Document[this.props.fieldKey]); + // const children = DocListCast(this.rootDoc[this.props.fieldKey]); // const imageProtos = children.filter(doc => Cast(doc.data, ImageField)).map(Doc.GetProto); // const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags); // return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />; + this.isContentActive(); } screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()); @@ -268,7 +147,6 @@ export class CollectionView extends Touchable<CollectionViewProps> { } subItems.push({ description: "Schema", event: () => func(CollectionViewType.Schema), icon: "th-list" }); subItems.push({ description: "Tree", event: () => func(CollectionViewType.Tree), icon: "tree" }); - !Doc.UserDoc().noviceMode && subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking), icon: "ellipsis-v" }); subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" }); subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" }); subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" }); @@ -279,7 +157,7 @@ export class CollectionView extends Touchable<CollectionViewProps> { !Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" }); subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" }); - if (!Doc.IsSystem(this.props.Document) && !this.props.Document.annotationOn) { + if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.annotationOn) { const existingVm = ContextMenu.Instance.findByDescription(category); const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : []; catItems.push({ description: "Add a Perspective...", addDivider: true, noexpand: true, subitems: subItems, icon: "eye" }); @@ -289,9 +167,9 @@ export class CollectionView extends Touchable<CollectionViewProps> { onContextMenu = (e: React.MouseEvent): void => { const cm = ContextMenu.Instance; - if (cm && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 + if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 this.setupViewTypes("UI Controls...", vtype => { - const newRendition = Doc.MakeAlias(this.props.Document); + const newRendition = Doc.MakeAlias(this.rootDoc); newRendition._viewType = vtype; this.props.addDocTab(newRendition, "add:right"); return newRendition; @@ -299,34 +177,34 @@ export class CollectionView extends Touchable<CollectionViewProps> { const options = cm.findByDescription("Options..."); const optionItems = options && "subitems" in options ? options.subitems : []; - !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }) : null; - if (this.props.Document.childLayout instanceof Doc) { - optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "add:right"), icon: "project-diagram" }); + !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null; + if (this.rootDoc.childLayout instanceof Doc) { + optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, "add:right"), icon: "project-diagram" }); } - if (this.props.Document.childClickedOpenTemplateView instanceof Doc) { - optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" }); + if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) { + optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" }); } - !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" }); + !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" }); optionItems.push({ - description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.props.Document), "add:right"), icon: "project-diagram" + description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram" }); optionItems.push({ - description: "Pull Master", event: () => BranchTask(this.props.Document, "pull"), icon: "project-diagram" + description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram" }); optionItems.push({ - description: "Merge Branches", event: () => BranchTask(this.props.Document, "merge"), icon: "project-diagram" + description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram" }); !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" }); - if (!Doc.UserDoc().noviceMode && !this.props.Document.annotationOn) { + if (!Doc.UserDoc().noviceMode && !this.rootDoc.annotationOn) { const existingOnClick = cm.findByDescription("OnClick..."); const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; const funcs = [{ key: "onChildClick", name: "On Child Clicked" }, { key: "onChildDoubleClick", name: "On Child Double Clicked" }]; funcs.map(func => onClicks.push({ description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => { - const alias = Doc.MakeAlias(this.props.Document); + const alias = Doc.MakeAlias(this.rootDoc); DocUtils.makeCustomViewClicked(alias, undefined, func.key); this.props.addDocTab(alias, "add:right"); } @@ -335,15 +213,15 @@ export class CollectionView extends Touchable<CollectionViewProps> { onClicks.push({ description: `Set child ${childClick.title}`, icon: "edit", - event: () => Doc.GetProto(this.props.Document)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)), + event: () => Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)), })); - !Doc.IsSystem(this.props.Document) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); + !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); } if (!Doc.UserDoc().noviceMode) { const more = cm.findByDescription("More..."); const moreItems = more && "subitems" in more ? more.subitems : []; - moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) }); + moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) }); !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" }); } } @@ -351,28 +229,26 @@ export class CollectionView extends Touchable<CollectionViewProps> { bodyPanelWidth = () => this.props.PanelWidth(); - childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); - @computed get childLayoutString() { return StrCast(this.props.Document.childLayoutString); } + childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null); + @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); } render() { TraceMobx(); const props: SubCollectionViewProps = { ...this.props, addDocument: this.addDocument, - removeDocument: this.removeDocument, moveDocument: this.moveDocument, - active: this.active, - whenActiveChanged: this.whenActiveChanged, - parentActive: this.props.parentActive, + removeDocument: this.removeDocument, + isContentActive: this.isContentActive, PanelWidth: this.bodyPanelWidth, PanelHeight: this.props.PanelHeight, + ScreenToLocalTransform: this.screenToLocalTransform, childLayoutTemplate: this.childLayoutTemplate, childLayoutString: this.childLayoutString, - ScreenToLocalTransform: this.screenToLocalTransform, CollectionView: this, }; return (<div className={"collectionView"} onContextMenu={this.onContextMenu} - style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined }}> + style={{ pointerEvents: this.props.layerProvider?.(this.rootDoc) === false ? "none" : undefined }}> {this.showIsTagged()} {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)} </div>); diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index 4005c751e..0c69ee030 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -15,7 +15,7 @@ import { ComputedField } from "../../../fields/ScriptField"; import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types"; import { ImageField } from "../../../fields/URLField"; import { GetEffectiveAcl } from "../../../fields/util"; -import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse } from "../../../Utils"; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils"; import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; import { CompileScript, Transformer, ts } from "../../util/Scripting"; @@ -559,15 +559,15 @@ export class SchemaTable extends React.Component<SchemaTableProps> { onPointerDown={this.props.onPointerDown} onClick={this.props.onClick} onWheel={e => this.props.active(true) && e.stopPropagation()} onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} > {this.reactTable} - {this.props.Document._chromeHidden ? undefined : <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>} + {this.props.Document._chromeHidden ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} {!this._showDoc ? (null) : - <div className="collectionSchemaView-documentPreview" + <div className="collectionSchemaView-documentPreview" ref="overlay" style={{ position: "absolute", width: 150, height: 150, background: "dimGray", display: "block", top: 0, left: 0, transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)` - }} - ref="overlay"><DocumentView + }} > + <DocumentView Document={this._showDoc} DataDoc={this._showDataDoc} styleProvider={DefaultStyleProvider} @@ -576,7 +576,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> { freezeDimensions={true} focus={DocUtils.DefaultFocus} renderDepth={this.props.renderDepth} - rootSelected={() => false} + rootSelected={returnFalse} + isContentActive={returnTrue} + isDocumentActive={returnFalse} PanelWidth={() => 150} PanelHeight={() => 150} ScreenToLocalTransform={this.getPreviewTransform} @@ -586,8 +588,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} moveDocument={this.props.moveDocument} - parentActive={this.props.active} - whenActiveChanged={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse}> diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index f333c4077..f6aecbb14 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -309,6 +309,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} + isContentActive={returnTrue} PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} layerProvider={this.layerProvider} @@ -322,8 +323,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { ScreenToLocalTransform={this.ScreenToLocalTransform} dontCenter={"y"} rootSelected={returnTrue} - parentActive={this.active} - whenActiveChanged={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} focus={this.focusFunc} docViewPath={returnEmptyDoclist} bringToFront={emptyFunction} @@ -368,7 +368,7 @@ interface TabMinimapViewProps { } @observer export class TabMinimapView extends React.Component<TabMinimapViewProps> { - static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => { + static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => { if (doc) { switch (property.split(":")[0]) { default: return DefaultStyleProvider(doc, props, property); @@ -417,12 +417,11 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { CollectionView={undefined} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} - parentActive={returnFalse} docViewPath={returnEmptyDoclist} childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this. noOverlay={true} // don't render overlay Docs since they won't scale setHeight={returnFalse} - active={returnTrue} + isContentActive={returnTrue} select={emptyFunction} dropAction={undefined} isSelected={returnFalse} @@ -437,7 +436,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { PanelHeight={this.returnMiniSize} ScreenToLocalTransform={Transform.Identity} renderDepth={0} - whenActiveChanged={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} focus={DocUtils.DefaultFocus} styleProvider={TabMinimapView.miniStyleProvider} layerProvider={undefined} diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index 2f74a49bb..5b0c04f33 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -13,14 +13,15 @@ .treeView-container-active { .bullet-outline { position: relative; - width: 15px; + width: $TREE_BULLET_WIDTH; color: $intermediate-color; transform: scale(0.5); - display: inline-block; + display: inline-flex; + align-items: center; } .treeView-bulletIcons { - width: 15px; + width: $TREE_BULLET_WIDTH; .treeView-expandIcon { display: none; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 1d14c63c7..94a19a673 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, runInAction, trace, reaction, IReactionDisposer } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; @@ -9,7 +9,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, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, simulateMouseClick, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; @@ -22,7 +22,6 @@ import { undoBatch, UndoManager } from '../../util/UndoManager'; import { EditableView } from "../EditableView"; import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss'; 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'; @@ -32,43 +31,35 @@ import { CollectionTreeView } from './CollectionTreeView'; import { CollectionView, CollectionViewType } from './CollectionView'; import "./TreeView.scss"; import React = require("react"); -import { ContextMenu } from '../ContextMenu'; -import { ContextMenuProps } from '../ContextMenuItem'; -import { SharingManager } from '../../util/SharingManager'; export interface TreeViewProps { + treeView: CollectionTreeView; + parentTreeView: TreeView | CollectionTreeView | undefined; + prevSibling?: Doc; document: Doc; dataDoc?: Doc; - containingCollection: Doc; - prevSibling?: Doc; + containerCollection: Doc; renderDepth: number; dropAction: dropActionType; addDocTab: (doc: Doc, where: string) => boolean; - pinToPres: (document: Doc) => void; panelWidth: () => number; panelHeight: () => number; addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean; removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined; moveDocument: DragManager.MoveFunction; + isContentActive: (outsideReaction?: boolean) => boolean; + whenChildContentsActiveChanged: (isActive: boolean) => void; indentDocument?: (editTitle: boolean) => void; outdentDocument?: (editTitle: boolean) => void; ScreenToLocalTransform: () => Transform; dontRegisterView?: boolean; styleProvider?: StyleProviderFunc | undefined; - layerProvider?: undefined | ((doc: Doc, assign?: boolean) => boolean); - outerXf: () => { translateX: number, translateY: number }; - treeView: CollectionTreeView; - parentKey: string; - active: (outsideReaction?: boolean) => boolean; treeViewHideHeaderFields: () => boolean; - treeViewPreventOpen: boolean; renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle onCheckedClick?: () => ScriptField; onChildClick?: () => ScriptField; skipFields?: string[]; firstLevel: boolean; - whenActiveChanged: (isActive: boolean) => void; - parentTreeView: TreeView | CollectionTreeView | undefined; } const treeBulletWidth = function () { return Number(TREE_BULLET_WIDTH.replace("px", "")); }; @@ -79,49 +70,52 @@ const treeBulletWidth = function () { return Number(TREE_BULLET_WIDTH.replace("p * * special fields: * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden - * treeViewPreventOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document) * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree */ export class TreeView extends React.Component<TreeViewProps> { static _editTitleOnLoad: Opt<{ id: string, parent: TreeView | CollectionTreeView | undefined }>; static _openTitleScript: Opt<ScriptField | undefined>; static _openLevelScript: Opt<ScriptField | undefined>; - private _editTitleScript: (() => ScriptField) | undefined; - private _openScript: (() => ScriptField) | undefined; - private _header?: React.RefObject<HTMLDivElement> = React.createRef(); - private _treedropDisposer?: DragManager.DragDropDisposer; + private _header: React.RefObject<HTMLDivElement> = React.createRef(); private _tref = React.createRef<HTMLDivElement>(); private _docRef: Opt<DocumentView>; - private _editMaxWidth: number | string = 0; private _selDisposer: Opt<IReactionDisposer>; + private _editTitleScript: (() => ScriptField) | undefined; + private _openScript: (() => ScriptField) | undefined; + private _treedropDisposer?: DragManager.DragDropDisposer; + get treeViewOpenIsTransient() { return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc); } set treeViewOpen(c: boolean) { - if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; - else this.doc.treeViewOpen = this._overrideTreeViewOpen = c; + if (this.treeViewOpenIsTransient) this._transientOpenState = c; + else { + this.doc.treeViewOpen = c; + this._transientOpenState = false; + } } - @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state + @observable _transientOpenState = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state @observable _editTitle: boolean = false; @observable _dref: DocumentView | undefined | null; get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive - get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; } - get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.props.treeView.fileSysMode ? (this.doc.isFolder ? "data" : "aliases") : Doc.UserDoc().noviceMode || this.props.treeView.outlineMode ? "layout" : "fields"); } - get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs && !this.props.treeView.fileSysMode ? this.fieldKey : this.defaultExpandedView); } - - @computed get doc() { TraceMobx(); return this.props.document; } - @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && Doc.GetT(this.doc, "treeViewOpen", "boolean", true)) || this._overrideTreeViewOpen; } - @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); } - @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } + get defaultExpandedView() { + return this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") : + this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields"); + } + + @computed get doc() { return this.props.document; } + @computed get treeViewOpen() { return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, "treeViewOpen", "boolean", true)) || this._transientOpenState; } + @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); } + @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containerCollection.maxEmbedHeight, 200); } @computed get dataDoc() { return this.doc[DataSym]; } @computed get layoutDoc() { return Doc.Layout(this.doc); } - @computed get fieldKey() { TraceMobx(); const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; } - @computed get childDocs() { TraceMobx(); return this.childDocList(this.fieldKey); } + @computed get fieldKey() { return Doc.LayoutFieldKey(this.doc); } + @computed get childDocs() { return this.childDocList(this.fieldKey); } @computed get childLinks() { return this.childDocList("links"); } @computed get childAliases() { return this.childDocList("aliases"); } @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); } - @computed get selected() { return SelectionManager.Views().length && SelectionManager.Views()[0].props.Document === this.props.document; } + @computed get selected() { return SelectionManager.Views().lastElement()?.props.Document === this.props.document; } childDocList(field: string) { - const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined; + const layout = Cast(Doc.LayoutField(this.doc), Doc, null); return (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields DocListCastOrNull(this.doc[field]); // otherwise use the document's data field @@ -184,9 +178,9 @@ export class TreeView extends React.Component<TreeViewProps> { document.removeEventListener("pointermove", this.onDragMove, true); } onPointerEnter = (e: React.PointerEvent): void => { - this.props.active(true) && Doc.BrushDoc(this.dataDoc); + this.props.isContentActive(true) && Doc.BrushDoc(this.dataDoc); if (e.buttons === 1 && SnappingManager.GetIsDragging()) { - this._header!.current!.className = "treeView-header"; + this._header.current!.className = "treeView-header"; document.removeEventListener("pointermove", this.onDragMove, true); document.removeEventListener("pointerup", this.onDragUp, true); document.addEventListener("pointermove", this.onDragMove, true); @@ -195,8 +189,8 @@ export class TreeView extends React.Component<TreeViewProps> { } onPointerLeave = (e: React.PointerEvent): void => { Doc.UnBrushDoc(this.dataDoc); - if (this._header?.current?.className !== "treeView-header-editing") { - this._header!.current!.className = "treeView-header"; + if (this._header.current?.className !== "treeView-header-editing") { + this._header.current!.className = "treeView-header"; } document.removeEventListener("pointerup", this.onDragUp, true); document.removeEventListener("pointermove", this.onDragMove, true); @@ -204,13 +198,13 @@ export class TreeView extends React.Component<TreeViewProps> { onDragMove = (e: PointerEvent): void => { Doc.UnBrushDoc(this.dataDoc); const pt = [e.clientX, e.clientY]; - const rect = this._header!.current!.getBoundingClientRect(); + const rect = this._header.current!.getBoundingClientRect(); const before = pt[1] < rect.top + rect.height / 2; const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length); - this._header!.current!.className = "treeView-header"; - 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"; + this._header.current!.className = "treeView-header"; + 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(); } @@ -218,6 +212,7 @@ export class TreeView extends React.Component<TreeViewProps> { const bullet = Docs.Create.TextDocument("-text-", { layout: CollectionView.LayoutString("data"), title: "-title-", "sidebarColor": "transparent", "sidebarViewType": CollectionViewType.Freeform, + treeViewExpandedViewLock: true, treeViewExpandedView: "data", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: "outline", x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, backgroundColor: "transparent", _width: 1000, _height: 10 }); @@ -229,18 +224,14 @@ export class TreeView extends React.Component<TreeViewProps> { makeTextCollection = () => { const bullet = TreeView.makeTextBullet(); - const added = this.props.addDocument(bullet); TreeView._editTitleOnLoad = { id: bullet[Id], parent: this }; - bullet.context = this.props.treeView.Document; - return added; + return this.props.addDocument(bullet); } makeFolder = () => { const folder = Docs.Create.TreeDocument([], { title: "-folder-", _stayInCollection: true, isFolder: true }); - const added = this.props.addDocument(folder); - folder.context = this.props.treeView.Document; TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView }; - return added; + return this.props.addDocument(folder); } preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => { @@ -251,7 +242,7 @@ export class TreeView extends React.Component<TreeViewProps> { @undoBatch treeDrop = (e: Event, de: DragManager.DropEvent) => { const pt = [de.x, de.y]; - const rect = this._header!.current!.getBoundingClientRect(); + const rect = this._header.current!.getBoundingClientRect(); const before = pt[1] < rect.top + rect.height / 2; const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length); if (de.complete.linkDragData) { @@ -265,7 +256,7 @@ export class TreeView extends React.Component<TreeViewProps> { e.stopPropagation(); if (docDragData.draggedDocuments[0] === this.doc) return true; const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before); - const canAdd = !StrCast((inside ? this.props.document : this.props.containingCollection)?.freezeChildren).includes("add") || docDragData.treeViewDoc === this.props.treeView.props.Document; + const canAdd = !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes("add") || docDragData.treeViewDoc === this.props.treeView.props.Document; const localAdd = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false; const addDoc = !inside ? parentAddDoc : (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean); @@ -279,7 +270,7 @@ export class TreeView extends React.Component<TreeViewProps> { refTransform = (ref: HTMLDivElement | undefined | null) => { if (!ref) return this.props.ScreenToLocalTransform(); const { scale, translateX, translateY } = Utils.GetScreenTransform(ref); - const outerXf = this.props.outerXf(); + const outerXf = Utils.GetScreenTransform(this.props.treeView.MainEle()); const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); } @@ -299,9 +290,9 @@ export class TreeView extends React.Component<TreeViewProps> { if (aspect) return this.docWidth() / (aspect || 1); return layoutDoc._fitWidth ? (!Doc.NativeHeight(this.doc) ? - NumCast(this.props.containingCollection._height) + NumCast(this.props.containerCollection._height) : - Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containingCollection._height)) + Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height)) )) : (layoutDoc[HeightSym]() || 50); @@ -328,10 +319,10 @@ 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.titleStyleProvider, this.props.layerProvider, 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, this); + this.props.treeView, this, doc, undefined, this.props.containerCollection, this.props.prevSibling, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.isContentActive, + this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, + [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged, this.props.dontRegisterView); } else { contentElement = <EditableView key="editableView" contents={contents !== undefined ? Field.toString(contents as Field) : "null"} @@ -378,18 +369,17 @@ export class TreeView extends React.Component<TreeViewProps> { TraceMobx(); const expandKey = this.treeViewExpandedView; if (["links", "annotations", "aliases", this.fieldKey].includes(expandKey)) { - const key = expandKey === "annotations" ? this.fieldKey + "-annotations" : expandKey; + const key = (expandKey === "annotations" ? `${this.fieldKey}-` : "") + expandKey; const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key); 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"]); + const ordering = StrCast(this.doc.treeViewSortCriterion); if (ordering === "Z") { const docs = TreeView.sortDocs(this.childDocs || ([] as Doc[]), ordering); - doc.zIndex = addBefore ? (before ? NumCast(addBefore.zIndex) - 0.5 : NumCast(addBefore.zIndex) + 0.5) : 1000; + doc.zIndex = addBefore ? NumCast(addBefore.zIndex) + (before ? -0.5 : 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); + docs.sort((a, b) => NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1).forEach((d, i) => d.zIndex = i); } const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); added && (doc.context = this.doc.context); @@ -397,24 +387,23 @@ 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); const docs = expandKey === "aliases" ? this.childAliases : expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs; - const sortKey = `${this.fieldKey}-sortCriteria`; let downX = 0, downY = 0; const sortings = ["up", "down", "Z", undefined]; - const curSort = Math.max(0, sortings.indexOf(Cast(this.doc[sortKey], "string", null))); + const curSort = Math.max(0, sortings.indexOf(Cast(this.doc.treeViewSortCriterion, "string", null))); return <ul key={expandKey + "more"} title={"sort: " + sortings[curSort]} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onPointerDown={e => { downX = e.clientX; downY = e.clientY; e.stopPropagation(); }} onClick={(e) => { - if (this.props.active() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) { - !this.props.treeView.outlineMode && (this.doc[sortKey] = sortings[(curSort + 1) % sortings.length]); + if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) { + !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortings[(curSort + 1) % sortings.length]); 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.titleStyleProvider, this.props.layerProvider, 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, this)} + TreeView.GetChildElements(docs, this.props.treeView, this, this.layoutDoc, + this.dataDoc, this.props.containerCollection, this.props.prevSibling, addDoc, remDoc, this.move, + StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, + this.props.isContentActive, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, + [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged, this.props.dontRegisterView)} </ul >; } else if (this.treeViewExpandedView === "fields") { return <ul key={this.doc[Id] + this.doc.title}> @@ -433,7 +422,7 @@ export class TreeView extends React.Component<TreeViewProps> { if (this.onCheckedClick) { this.onCheckedClick?.script.run({ this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc, - heading: this.props.containingCollection.title, + heading: this.props.containerCollection.title, checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? "remove" : "check", containingTreeView: this.props.treeView.props.Document, }, console.log); @@ -470,47 +459,49 @@ export class TreeView extends React.Component<TreeViewProps> { } </div>; } + + @action + expandNextviewType = () => { + if (this.treeViewOpen && !this.doc.isFolder && !this.props.treeView.outlineMode && !this.doc.treeViewExpandedViewLock) { + const next = (modes: any[]) => modes[(modes.indexOf(StrCast(this.doc.treeViewExpandedView)) + 1) % modes.length]; + const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : ""; + const links = () => DocListCast(this.doc.links).length ? "links" : ""; + const children = () => this.childDocs ? this.fieldKey : ""; + this.doc.treeViewExpandedView = next(this.props.treeView.fileSysMode ? + (Doc.UserDoc().noviceMode ? ["layout", "aliases"] : ["layout", "aliases", "fields"]) : + (Doc.UserDoc().noviceMode ? [children(), "layout"] : [children(), "fields", "layout", links(), annos()]).filter(mode => mode)); + } + this.treeViewOpen = true; + } + @computed get headerElements() { - return (Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode) || this.props.treeViewHideHeaderFields() ? (null) : - <> + return this.props.treeViewHideHeaderFields() || Doc.IsSystem(this.doc) ? (null) + : <> <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} /> - <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView} - onPointerDown={action(() => { - if (this.props.treeView.fileSysMode) { - this.doc.treeViewExpandedView = this.doc.isFolder ? this.fieldKey : this.treeViewExpandedView === "layout" ? "aliases" : - this.treeViewExpandedView === "aliases" && !Doc.UserDoc().noviceMode ? "fields" : "layout"; - } else if (this.treeViewOpen) { - this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView : - this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.props.treeView.outlineMode ? "layout" : "fields") : - this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : - this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : - (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : - this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.props.treeView.outlineMode ? "layout" : "fields"); - } - this.treeViewOpen = true; - })}> - {this.treeViewExpandedView} - </span> + {this.doc.treeViewExpandedViewLock ? (null) : + <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}> + {this.treeViewExpandedView} + </span>} </>; } showContextMenu = (e: React.MouseEvent) => simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); - contextMenuItems = () => this.doc.isFolder ? - [{ script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" }] : Doc.IsSystem(this.doc) ? [] : - this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ? - [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }] : - [{ script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }] + contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : this.doc.isFolder ? + [{ script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" }] : + this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ? + [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }] : + [{ script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }] onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); ignoreEvent = (e: any) => { - if (this.props.active(true)) { + if (this.props.isContentActive(true)) { e.stopPropagation(); e.preventDefault(); } } - titleStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => { + titleStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, 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]) { @@ -524,9 +515,10 @@ export class TreeView extends React.Component<TreeViewProps> { {StrCast(doc?.title)} </div>; case StyleProp.Decorations: return (null); + default: return this.props?.treeView?.props.styleProvider?.(doc, props, property); } } - embeddedStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => { + embeddedStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, 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 } @@ -542,6 +534,7 @@ export class TreeView extends React.Component<TreeViewProps> { } } } + titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - treeBulletWidth())); /** * Renders the EditableView title element for placement into the tree. @@ -582,8 +575,10 @@ export class TreeView extends React.Component<TreeViewProps> { Document={this.doc} DataDoc={undefined} scriptContext={this} + hideDecorationTitle={this.props.treeView.outlineMode} + hideResizeHandles={this.props.treeView.outlineMode} styleProvider={this.titleStyleProvider} - layerProvider={this.props.layerProvider} + layerProvider={returnTrue} docViewPath={returnEmptyDoclist} treeViewDoc={this.props.treeView.props.Document} addDocument={undefined} @@ -597,17 +592,19 @@ export class TreeView extends React.Component<TreeViewProps> { removeDocument={this.props.removeDoc} ScreenToLocalTransform={this.getTransform} NativeHeight={() => 18} - NativeWidth={this.props.treeView.truncateTitleWidth} - PanelWidth={this.props.treeView.truncateTitleWidth} + NativeWidth={this.titleWidth} + PanelWidth={this.titleWidth} PanelHeight={() => 18} contextMenuItems={this.contextMenuItems} renderDepth={1} - focus={returnTrue} - parentActive={returnTrue} - whenActiveChanged={this.props.whenActiveChanged} + isContentActive={this.props.isContentActive} + isDocumentActive={this.props.isContentActive} + focus={this.refocus} + whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} bringToFront={emptyFunction} - cantBrush={this.props.treeView.props.cantBrush} - dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews)} + disableDocBrushing={this.props.treeView.props.disableDocBrushing} + hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)} + dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} @@ -621,22 +618,21 @@ export class TreeView extends React.Component<TreeViewProps> { fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined, textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined, outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined, - pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined + pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined }} > {view} </div > <div className={"right-buttons-container"}> - {this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations)} {/* hide and lock buttons */} + {this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ":afterHeader" : ""))} {/* hide and lock buttons */} {this.headerElements} </div> </>; } - renderBulletHeader = (contents: JSX.Element) => { + renderBulletHeader = (contents: JSX.Element, editing: boolean) => { return <> - <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} key="titleheader" + <div className={`treeView-header` + (editing ? "-editing" : "")} key="titleheader" ref={this._header} - style={{ maxWidth: this._editMaxWidth }} onClick={this.ignoreEvent} onPointerDown={this.ignoreEvent} onPointerEnter={this.onPointerEnter} @@ -647,13 +643,6 @@ export class TreeView extends React.Component<TreeViewProps> { </>; } - // renders the text version of a document as the header (e.g., useful for Slide views where the "") - @computed get renderTitleAsHeader() { - return <> - {this.renderBullet} - {this.renderTitle} - </>; - } renderEmbeddedDocument = (asText: boolean) => { const layout = StrCast(Doc.LayoutField(this.layoutDoc)); @@ -667,34 +656,47 @@ export class TreeView extends React.Component<TreeViewProps> { PanelHeight={panelHeight} NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined} NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined} - fitContentsToDoc={returnTrue} - hideTitle={asText} LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined} - focus={asText ? this.refocus : returnFalse} - dontRegisterView={asText ? undefined : this.props.dontRegisterView} + isContentActive={asText ? this.props.isContentActive : returnFalse} + isDocumentActive={asText ? this.props.isContentActive : returnFalse} + styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider} + hideTitle={asText} + fitContentsToDoc={returnTrue} + hideDecorationTitle={this.props.treeView.outlineMode} + hideResizeHandles={this.props.treeView.outlineMode} + focus={this.refocus} + hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)} + dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)} ScreenToLocalTransform={this.docTransform} renderDepth={this.props.renderDepth + 1} rootSelected={returnTrue} - styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider} - layerProvider={this.props.layerProvider} + layerProvider={returnTrue} docViewPath={this.props.treeView.props.docViewPath} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionDoc={this.props.containingCollection} + ContainingCollectionDoc={this.props.containerCollection} ContainingCollectionView={undefined} addDocument={this.props.addDocument} moveDocument={this.move} removeDocument={this.props.removeDoc} - parentActive={this.props.active} - whenActiveChanged={this.props.whenActiveChanged} + whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} addDocTab={this.props.addDocTab} - pinToPres={this.props.pinToPres} - cantBrush={this.props.treeView.props.cantBrush} + pinToPres={this.props.treeView.props.pinToPres} + disableDocBrushing={this.props.treeView.props.disableDocBrushing} bringToFront={returnFalse} />; } + // renders the text version of a document as the header. This is used in the file system mode and in other vanilla tree views. + @computed get renderTitleAsHeader() { + return <> + {this.renderBullet} + {this.renderTitle} + </>; + } + + // renders the document in the header field instead of a text proxy. @computed get renderDocumentAsHeader() { return <> {this.renderBullet} @@ -712,29 +714,18 @@ export class TreeView extends React.Component<TreeViewProps> { render() { TraceMobx(); - if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return "<" + this.doc.title + ">"; - if (this._editTitle) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll - let par: any = this._header?.current; - while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode; - if (par) { - const par_rect = (par as HTMLElement).getBoundingClientRect(); - const my_recct = this._docRef?.ContentDiv?.getBoundingClientRect(); - this._editMaxWidth = Math.max(100, par_rect.right - (my_recct?.left || 0)); - } - } - else this._editMaxWidth = ""; - const hideTitle = this.doc.treeViewHideHeader || this.props.treeView.outlineMode; - 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>; + return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? "<" + this.doc.title + ">" : // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles + <div className={`treeView-container${this.props.isContentActive() ? "-active" : ""}`} + ref={this.createTreeDropTarget} + onPointerDown={e => this.props.isContentActive(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, this._editTitle)} + </li> + </div>; } public static sortDocs(childDocs: Doc[], criterion: string | undefined) { @@ -768,45 +759,40 @@ export class TreeView extends React.Component<TreeViewProps> { public static GetChildElements( childDocs: Doc[], treeView: CollectionTreeView, - containingCollection: Doc, + parentTreeView: CollectionTreeView | TreeView | undefined, + conainerCollection: Doc, dataDoc: Doc | undefined, - key: string, parentCollectionDoc: Doc | undefined, - parentPrevSibling: Doc | undefined, + containerPrevSibling: Doc | undefined, add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean, remove: undefined | ((doc: Doc | Doc[]) => boolean), move: DragManager.MoveFunction, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => boolean, - pinToPres: (document: Doc) => void, styleProvider: undefined | StyleProviderFunc, - layerProvider: undefined | ((doc: Doc, assign?: boolean) => boolean), screenToLocalXf: () => Transform, - outerXf: () => { translateX: number, translateY: number }, - active: (outsideReaction?: boolean) => boolean, + isContentActive: (outsideReaction?: boolean) => boolean, panelWidth: () => number, renderDepth: number, treeViewHideHeaderFields: () => boolean, - treeViewPreventOpen: boolean, renderedIds: string[], onCheckedClick: undefined | (() => ScriptField), onChildClick: undefined | (() => ScriptField), skipFields: string[] | undefined, firstLevel: boolean, - whenActiveChanged: (isActive: boolean) => void, + whenChildContentsActiveChanged: (isActive: boolean) => void, dontRegisterView: boolean | undefined, - parentTreeView: CollectionTreeView | TreeView | undefined ) { - const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField); + const viewSpecScript = Cast(conainerCollection.viewSpecScript, ScriptField); if (viewSpecScript) { childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result); } - const docs = TreeView.sortDocs(childDocs, StrCast(containingCollection?.[key + "-sortCriteria"])); + const docs = TreeView.sortDocs(childDocs, StrCast(conainerCollection.treeViewSortCriterion)); const rowWidth = () => panelWidth() - treeBulletWidth(); const treeViewRefs = new Map<Doc, TreeView | undefined>(); return docs.filter(child => child instanceof Doc).map((child, i) => { - const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child); + const pair = Doc.GetLayoutDataDocPair(conainerCollection, dataDoc, child); if (!pair.layout || pair.data instanceof Promise) { return (null); } @@ -823,7 +809,7 @@ export class TreeView extends React.Component<TreeViewProps> { } }; const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1])); - const outdent = parentCollectionDoc?._viewType !== CollectionViewType.Tree ? undefined : ((editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, parentPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined)); + const outdent = parentCollectionDoc?._viewType !== CollectionViewType.Tree ? undefined : ((editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined)); const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false); const childLayout = Doc.Layout(pair.layout); const rowHeight = () => { @@ -833,7 +819,7 @@ export class TreeView extends React.Component<TreeViewProps> { return <TreeView key={child[Id]} ref={r => treeViewRefs.set(child, r ? r : undefined)} document={pair.layout} dataDoc={pair.data} - containingCollection={containingCollection} + containerCollection={conainerCollection} prevSibling={docs[i]} treeView={treeView} indentDocument={indent} @@ -841,27 +827,22 @@ export class TreeView extends React.Component<TreeViewProps> { onCheckedClick={onCheckedClick} onChildClick={onChildClick} renderDepth={renderDepth} - removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove} + removeDoc={StrCast(conainerCollection.freezeChildren).includes("remove") ? undefined : remove} addDocument={addDocument} styleProvider={styleProvider} - layerProvider={layerProvider} panelWidth={rowWidth} panelHeight={rowHeight} dontRegisterView={dontRegisterView} moveDocument={move} dropAction={dropAction} addDocTab={addDocTab} - pinToPres={pinToPres} ScreenToLocalTransform={screenToLocalXf} - outerXf={outerXf} - parentKey={key} - active={active} + isContentActive={isContentActive} treeViewHideHeaderFields={treeViewHideHeaderFields} - treeViewPreventOpen={treeViewPreventOpen} renderedIds={renderedIds} skipFields={skipFields} firstLevel={firstLevel} - whenActiveChanged={whenActiveChanged} + whenChildContentsActiveChanged={whenChildContentsActiveChanged} parentTreeView={parentTreeView} />; }); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f0d99611a..c623ce653 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -36,7 +36,6 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveIn import { LightboxView } from "../../LightboxView"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView"; -import { FieldViewProps } from "../../nodes/FieldView"; import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox"; import { pageSchema } from "../../nodes/ImageBox"; import { PresBox } from "../../nodes/PresBox"; @@ -67,7 +66,6 @@ export const panZoomSchema = createSchema({ type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof collectionSchema, typeof documentSchema, typeof pageSchema]>; const PanZoomDocument = makeInterface(panZoomSchema, collectionSchema, documentSchema, pageSchema); export type collectionFreeformViewProps = { - parentActive: (outsideReaction: boolean) => boolean; annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) viewDefDivClick?: ScriptField; childPointerEvents?: boolean; @@ -117,7 +115,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); } - @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.active() || this.props.active()); } + @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.isContentActive() || this.props.isContentActive()); } @computed get fitToContentVals() { return { bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 }, @@ -152,7 +150,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P getKeyFrameEditing = () => this._keyframeEditing; onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); - parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.props.parentActive?.(outsideReaction) || this.backgroundActive || this.layoutDoc._viewType === CollectionViewType.Pile ? true : false; elementFunc = () => this._layoutElements; shrinkWrap = () => { const vals = this.fitToContentVals; @@ -422,7 +419,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } } - getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => { + getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => { let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1 if (property !== StyleProp.BackgroundColor) return styleProp; const cluster = NumCast(doc?.cluster); @@ -459,7 +456,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return; } this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)); - if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.active(true)) { + if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -484,7 +481,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const pt = me.changedTouches[0]; if (pt) { this._hitCluster = this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)); - if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) { + if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) { this.removeMoveListeners(); this.addMoveListeners(); this.removeEndListeners(); @@ -625,8 +622,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P onClick = (e: React.MouseEvent) => { if ((Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) { - if (e.shiftKey && Date.now() - this._lastTap < 300) { // reset zoom of freeform view to 1-to-1 on a shift + double click - this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1); + if (e.shiftKey) { + if (Date.now() - this._lastTap < 300) { // reset zoom of freeform view to 1-to-1 on a shift + double click + this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1); + } e.stopPropagation(); e.preventDefault(); } @@ -647,7 +646,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if (this.props.Document._isGroup) return; // groups don't pan when dragged -- instead let the event go through to allow the group itself to drag if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return; if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - if (this.props.active(true)) e.stopPropagation(); + if (this.props.isContentActive(true)) e.stopPropagation(); } else if (!e.cancelBubble) { if (CurrentUserUtils.SelectedTool === InkTool.None) { if (this.tryDragCluster(e, this._hitCluster)) { @@ -735,7 +734,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @action handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => { - if (!e.nativeEvent.cancelBubble && this.props.active(true)) { + if (!e.nativeEvent.cancelBubble && this.props.isContentActive(true)) { // const pt1: React.Touch | null = e.targetTouches.item(0); // const pt2: React.Touch | null = e.targetTouches.item(1); // // if (!pt1 || !pt2) return; @@ -819,7 +818,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } - else if (this.props.active(true) && !this.Document._isGroup) { + else if (this.props.isContentActive(true) && !this.Document._isGroup) { e.stopPropagation(); e.preventDefault(); this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc? @@ -1007,6 +1006,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P }; } + isContentActive = () => this.props.isSelected() || this.props.isContentActive(); + getChildDocView(entry: PoolData) { const childLayout = entry.pair.layout; const childData = entry.pair.data; @@ -1030,14 +1031,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P docFilters={this.freeformDocFilters} docRangeFilters={this.freeformRangeDocFilters} searchFilterDocs={this.searchFilterDocs} + isContentActive={this.isAnnotationOverlay ? this.props.isContentActive : returnFalse} + isDocumentActive={this.isContentActive} focus={this.focusDocument} addDocTab={this.addDocTab} addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} moveDocument={this.props.moveDocument} pinToPres={this.props.pinToPres} - whenActiveChanged={this.props.whenActiveChanged} - parentActive={this.parentActive} + whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} docViewPath={this.props.docViewPath} styleProvider={this.getClusterColor} layerProvider={this.props.layerProvider} @@ -1444,7 +1446,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}> <div ref={this._marqueeRef}> - {this.layoutDoc["_backgroundGrid-show"] && (!SnappingManager.GetIsDragging() || !Doc.UserDoc().showSnapLines) ? this.backgroundGrid : (null)} + {this.layoutDoc["_backgroundGrid-show"] ? this.backgroundGrid : (null)} <CollectionFreeFormViewPannableContents isAnnotationOverlay={this.isAnnotationOverlay} transform={this.contentTransform} @@ -1488,7 +1490,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P width: `${100 / (this.contentScaling || 1)}%`, height: this.isAnnotationOverlay && this.Document.scrollHeight ? this.Document.scrollHeight : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}> - {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ? + {this.Document._freeformLOD && !this.props.isContentActive() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ? this.placeholder : this.marqueeView} {this.props.noOverlay ? (null) : <CollectionFreeFormOverlayView elements={this.elementFunc} />} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index af391a078..d14b68fa7 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -132,18 +132,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque }); })(); e.stopPropagation(); - } else if (e.key === "f" && e.ctrlKey) { - e.preventDefault(); - const root = Docs.Create.TreeDocument([], { title: "folder", _stayInCollection: true, isFolder: true }); - const folder = Docs.Create.TreeDocument([root], { title: "root", isFolder: true, treeViewType: "fileSystem", treeViewTruncateTitleWidth: 150 }); - Doc.GetProto(folder).isFolder = true; - folder.x = x; - folder.y = y; - folder._width = 200; - folder._height = 300; - this.props.addDocument?.(folder); - //setTimeout(() => SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(slide)!, false)); - e.stopPropagation(); } else if (e.key === "b" && e.ctrlKey) { // e.preventDefault(); // navigator.clipboard.readText().then(text => { @@ -167,7 +155,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) { FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : ""; FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch"); - this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xMargin === 0, this.props.isAnnotationOverlay ? this.props.Document : undefined)); + this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xMargin === 0)); e.stopPropagation(); } } @@ -366,6 +354,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque newCollection._width = this.Bounds.width; newCollection._height = this.Bounds.height; newCollection._isGroup = makeGroup; + newCollection.forceActive = makeGroup; newCollection.x = this.Bounds.left; newCollection.y = this.Bounds.top; selected.forEach(d => d.context = newCollection); diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index e2feff5ed..b0030471d 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -171,7 +171,6 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { ScreenToLocalTransform={dxf} onClick={this.onChildClickHandler} renderDepth={this.props.renderDepth + 1} - parentActive={this.props.active} dontCenter={"y"} />; } @@ -296,7 +295,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { * Handles text document creation on double click. */ onPointerDown = (e: React.PointerEvent) => { - if (this.props.active(true)) { + if (this.props.isContentActive(true)) { setupMoveUpEvents(this, e, returnFalse, returnFalse, (e: PointerEvent, doubleTap?: boolean) => { if (doubleTap) { @@ -316,7 +315,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { render() { return ( <div className="collectionGridView-contents" ref={this.createDashEventsTarget} - style={{ pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined }} + style={{ pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined }} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onDrop={this.onExternalDrop} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 0c0dbef9f..8b5c02b75 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -223,6 +223,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu LayoutTemplateString={this.props.childLayoutString} freezeDimensions={this.props.childFreezeDimensions} renderDepth={this.props.renderDepth + 1} + isContentActive={returnFalse} PanelWidth={width} PanelHeight={height} rootSelected={this.rootSelected} @@ -239,8 +240,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} - parentActive={this.props.active} - whenActiveChanged={this.props.whenActiveChanged} + whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} addDocTab={this.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 0a1000a20..2c5e40d02 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -232,6 +232,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) ScreenToLocalTransform={dxf} focus={this.props.focus} docFilters={this.docFilters} + isContentActive={returnFalse} docRangeFilters={this.docRangeFilters} searchFilterDocs={this.searchFilterDocs} ContainingCollectionDoc={this.props.CollectionView?.props.Document} @@ -239,8 +240,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} - parentActive={this.props.active} - whenActiveChanged={this.props.whenActiveChanged} + whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} addDocTab={this.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} |
