diff options
Diffstat (limited to 'src/client/views/collections')
29 files changed, 353 insertions, 287 deletions
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 0eb61a0b2..ea02bcd4c 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -4,7 +4,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, Opt } from '../../../fields/Doc'; import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { returnFalse, returnZero, StopEvent } from '../../../Utils'; +import { emptyFunction, returnFalse, returnZero, StopEvent } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; @@ -60,6 +60,8 @@ export class CollectionCarouselView extends CollectionSubView() { NativeHeight={returnZero} onDoubleClick={this.onContentDoubleClick} onClick={this.onContentClick} + isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.props.isContentActive} + isContentActive={this.props.childContentsActive ?? this.props.isContentActive() === false ? returnFalse : emptyFunction} hideCaptions={show_captions ? true : false} renderDepth={this.props.renderDepth + 1} LayoutTemplate={this.props.childLayoutTemplate} diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index 78e44dfa2..4c15d5eed 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -15,6 +15,12 @@ cursor: grab; color: $black; } +.collectiondockingview-container .lm_splitter { + opacity: 0.2; + &:hover { + opacity: 1; + } +} .lm_title.focus-visible { -webkit-appearance: none; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 14f03044e..32fb4d8df 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -29,6 +29,7 @@ import { CollectionFreeFormView } from './collectionFreeForm'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; import { TabDocView } from './TabDocView'; import React = require('react'); +import { DocumentManager } from '../../util/DocumentManager'; import { DocAcl } from '../../../fields/DocSymbols'; const _global = (window /* browser */ || global) /* node */ as any; @@ -360,6 +361,7 @@ export class CollectionDockingView extends CollectionSubView() { } catch (e) {} this._goldenLayout?.destroy(); window.removeEventListener('resize', this.onResize); + window.removeEventListener('mouseup', this.onPointerUp); this._reactionDisposer?.(); this._lightboxReactionDisposer?.(); @@ -410,7 +412,14 @@ export class CollectionDockingView extends CollectionSubView() { if (!htmlTarget.closest('*.lm_content') && (htmlTarget.closest('*.lm_tab') || htmlTarget.closest('*.lm_stack'))) { const className = typeof htmlTarget.className === 'string' ? htmlTarget.className : ''; if (className.includes('lm_maximise')) this._flush = UndoManager.StartBatch('tab maximize'); - else if (!className.includes('lm_close')) DocServer.UPDATE_SERVER_CACHE(); + else { + const tabTarget = (e.target as HTMLElement)?.parentElement?.className.includes('lm_tab') ? (e.target as HTMLElement).parentElement : (e.target as HTMLElement); + const map = Array.from(this.tabMap).find(tab => tab.element[0] === tabTarget); + if (map?.DashDoc && DocumentManager.Instance.getFirstDocumentView(map.DashDoc)) { + SelectionManager.SelectView(DocumentManager.Instance.getFirstDocumentView(map.DashDoc), false); + } + if (!className.includes('lm_close')) DocServer.UPDATE_SERVER_CACHE(); + } } } if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 64f9c6a87..6f88f6727 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -98,14 +98,15 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr rowDrop = action((e: Event, de: DragManager.DropEvent) => { this._createEmbeddingSelected = false; if (de.complete.docDragData) { - this.props.parent.Document.dropConverter instanceof ScriptField && this.props.parent.Document.dropConverter.script.run({ dragData: de.complete.docDragData }); const key = this.props.pivotField; const castedValue = this.getValue(this.heading); const onLayoutDoc = this.onLayoutDoc(key); - de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, !onLayoutDoc)); - this.props.parent.onInternalDrop(e, de); - e.stopPropagation(); + if (this.props.parent.onInternalDrop(e, de)) { + de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, !onLayoutDoc)); + } + return true; } + return false; }); getValue = (value: string): any => { diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index fc3f713ce..7d71bce13 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -427,8 +427,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu if (docDragData?.draggedDocuments.length) { this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate(docDragData.draggedDocuments || [])); e.stopPropagation(); + return true; } - return true; + return false; } dragViewDown = (e: React.PointerEvent) => { diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 675f23970..53a42d2a6 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -10,7 +10,7 @@ import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils'; +import { emptyFunction, returnFalse, returnZero, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DragManager, dropActionType } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -47,7 +47,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { @observable _cursor: CursorProperty = 'grab'; @observable _scroll = 0; @computed get chromeHidden() { - return BoolCast(this.layoutDoc.chromeHidden); + return BoolCast(this.layoutDoc.chromeHidden) || this.props.onBrowseClick?.() ? true : false; } // columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns @computed get colHeaderData() { @@ -192,7 +192,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { if (found) { const top = found.getBoundingClientRect().top; const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top); - if (Math.floor(localTop[1]) !== 0) { + if (Math.floor(localTop[1]) !== 0 && Math.ceil(this.props.PanelHeight()) < (this._mainCont?.scrollHeight || 0)) { let focusSpeed = options.zoomTime ?? 500; smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); return focusSpeed; @@ -250,8 +250,9 @@ export class CollectionNoteTakingView extends CollectionSubView() { dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} rootSelected={this.rootSelected} layout_showTitle={this.props.childlayout_showTitle} - dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType} + dragAction={StrCast(this.layoutDoc.childDragAction) as dropActionType} onClick={this.onChildClickHandler} + onBrowseClick={this.props.onBrowseClick} onDoubleClick={this.onChildDoubleClickHandler} ScreenToLocalTransform={noteTakingDocTransform} focus={this.focusDocument} @@ -441,23 +442,24 @@ export class CollectionNoteTakingView extends CollectionSubView() { docs.splice(previousDocIndex + 1, 0, ...newDocs); } } + return true; } } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) { const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' }); - this.props.addDocument?.(source); + if (!this.props.addDocument?.(source)) e.preventDefault(); de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed e.stopPropagation(); - } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData); + return true; + } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) { + return this.internalAnchorAnnoDrop(e, de.complete.annoDragData); + } return false; }; @undoBatch internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData) { const dropCreator = annoDragData.dropDocCreator; - annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => { - const dropDoc = dropCreator(annotationOn); - return dropDoc || this.rootDoc; - }; + annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => dropCreator(annotationOn) || this.rootDoc; return true; } @@ -619,7 +621,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { } @computed get backgroundEvents() { - return SnappingManager.GetIsDragging(); + return this.props.isContentActive() === false ? 'none' : undefined; } observer: any; diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx index 2f28ecd00..3286d60bd 100644 --- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx +++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx @@ -92,6 +92,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu columnDrop = action((e: Event, de: DragManager.DropEvent) => { const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) }; drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false)); + return true; }); getValue = (value: string): any => { diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 57d9bbb49..91be31289 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -52,20 +52,25 @@ export class CollectionPileView extends CollectionSubView() { @computed get toggleIcon() { return ScriptField.MakeScript('documentView.iconify()', { documentView: 'any' }); } + @computed get contentEvents() { + const isStarburst = this.layoutEngine() === computeStarburstLayout.name; + return this.props.isContentActive() && isStarburst ? undefined : 'none'; + } // returns the contents of the pileup in a CollectionFreeFormView @computed get contents() { - const isStarburst = this.layoutEngine() === computeStarburstLayout.name; return ( - <div className="collectionPileView-innards" style={{ pointerEvents: isStarburst || SnappingManager.GetIsDragging() ? undefined : 'none' }}> + <div className="collectionPileView-innards" style={{ pointerEvents: this.contentEvents }}> <CollectionFreeFormView - {...this.props} - childContentsActive={returnFalse} + {...this.props} // layoutEngine={this.layoutEngine} addDocument={this.addPileDoc} - childCanEmbedOnDrag={true} - childClickScript={this.toggleIcon} moveDocument={this.removePileDoc} + // pile children never have their contents active, but will be document active whenever the entire pile is. + childContentsActive={returnFalse} + childDocumentsActive={this.props.isDocumentActive} + childDragAction="move" + childClickScript={this.toggleIcon} /> </div> ); @@ -103,7 +108,6 @@ export class CollectionPileView extends CollectionSubView() { _undoBatch: UndoManager.Batch | undefined; pointerDown = (e: React.PointerEvent) => { let dist = 0; - SnappingManager.SetIsDragging(true); setupMoveUpEvents( this, e, @@ -126,7 +130,6 @@ export class CollectionPileView extends CollectionSubView() { () => { this._undoBatch?.end(); this._undoBatch = undefined; - SnappingManager.SetIsDragging(false); }, emptyFunction, e.shiftKey && this.layoutEngine() === computePassLayout.name, diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index a55b70e22..a19d8e696 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -104,7 +104,7 @@ .collectionStackedTimeline-left-resizer, .collectionStackedTimeline-resizer { - background: $medium-gray; + background: $dark-gray; position: absolute; top: 0; height: 100%; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 9d5cb257a..ad84d859d 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -119,8 +119,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack // onClick play scripts CollectionStackedTimeline.RangeScript = CollectionStackedTimeline.RangeScript || - ScriptField.MakeFunction(`setTimeout(() => scriptContext.clickAnchor(this, clientX))`, { - // setTimeout is a hack to run script in its own properly named undo group (instead of being part of the generic onClick) + ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, { self: Doc.name, scriptContext: 'any', clientX: 'number', @@ -370,22 +369,22 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack // handles dragging and dropping markers in timeline @action internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number) { - if (!de.embedKey && this.props.Document._isGroup) return false; - if (!super.onInternalDrop(e, de)) return false; - - // determine x coordinate of drop and assign it to the documents being dragged --- see internalDocDrop of collectionFreeFormView.tsx for how it's done when dropping onto a 2D freeform view - const localPt = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); - const x = localPt[0] - docDragData.offset[0]; - const timelinePt = this.toTimeline(x + this._scroll, this.timelineContentWidth); - docDragData.droppedDocuments.forEach(drop => { - const anchorEnd = this.anchorEnd(drop); - if (anchorEnd !== undefined) { - Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false); - } - Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : 'timecodeToShow', timelinePt, false); - }); + if (super.onInternalDrop(e, de)) { + // determine x coordinate of drop and assign it to the documents being dragged --- see internalDocDrop of collectionFreeFormView.tsx for how it's done when dropping onto a 2D freeform view + const localPt = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); + const x = localPt[0] - docDragData.offset[0]; + const timelinePt = this.toTimeline(x + this._scroll, this.timelineContentWidth); + docDragData.droppedDocuments.forEach(drop => { + const anchorEnd = this.anchorEnd(drop); + if (anchorEnd !== undefined) { + Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false); + } + Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : 'timecodeToShow', timelinePt, false); + }); - return true; + return true; + } + return false; } onInternalDrop = (e: Event, de: DragManager.DropEvent) => { @@ -406,7 +405,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any, _label_minFontSize: 12, _label_maxFontSize: 24, - _stayInCollection: true, + _dragOnlyWithinContainer: true, backgroundColor: 'rgba(128, 128, 128, 0.5)', layout_hideLinkButton: true, onClick: FollowLinkScript(), @@ -528,6 +527,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack ); } + @computed get timelineEvents() { + return this.props.isContentActive() ? 'all' : this.props.isContentActive() === false ? 'none' : undefined; + } render() { const overlaps: { anchorStartTime: number; @@ -540,7 +542,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack })); const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2; return this.clipDuration === 0 ? null : ( - <div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : undefined }}> + <div ref={this.createDashEventsTarget} style={{ pointerEvents: this.timelineEvents }}> <div className="collectionStackedTimeline-timelineContainer" style={{ width: this.props.PanelWidth(), cursor: SnappingManager.GetIsDragging() ? 'grab' : '' }} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 3c0d8cbc3..ec529afc3 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -17,7 +17,7 @@ import { CollectionViewType } from '../../documents/DocumentTypes'; import { DragManager, dropActionType } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; -import { undoBatch } from '../../util/UndoManager'; +import { undoBatch, UndoManager } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from '../EditableView'; @@ -31,6 +31,7 @@ import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow'; import './CollectionStackingView.scss'; import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn'; import { CollectionSubView } from './CollectionSubView'; +import { Colors } from '../global/globalEnums'; const _global = (window /* browser */ || global) /* node */ as any; export type collectionStackingViewProps = { @@ -84,7 +85,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); } @computed get xMargin() { - return NumCast(this.layoutDoc._xMargin, Math.min(3, 0.05 * this.props.PanelWidth())); + return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this.props.PanelWidth())); } @computed get yMargin() { return this.props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this.props.PanelWidth())); @@ -137,7 +138,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection // assuming we need to get rowSpan because we might be dealing with many columns. Grid gap makes sense if multiple columns const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); // just getting the style - const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` }; + const style = this.isStackingView ? { background: Colors.MEDIUM_GRAY, width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` }; // So we're choosing whether we're going to render a column or a masonry doc return ( <div className={`collectionStackingView-${this.isStackingView ? 'columnDoc' : 'masonryDoc'}`} key={d[Id]} style={style}> @@ -295,7 +296,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection return this.addDocument?.(newDoc); } }; - isContentActive = () => (this.props.isSelected() || this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined); + isContentActive = () => (this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined); @observable _renderCount = 5; isChildContentActive = () => @@ -341,7 +342,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't layout_autoHeight resize if dontRegisterView is set, but they need to. rootSelected={this.rootSelected} layout_showTitle={this.props.childlayout_showTitle} - dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType} + dragAction={(this.layoutDoc.childDragAction ?? this.props.childDragAction) as dropActionType} onClick={this.onChildClickHandler} onDoubleClick={this.onChildDoubleClickHandler} ScreenToLocalTransform={stackedDocTransform} @@ -402,11 +403,15 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection // This following three functions must be from the view Mehek showed columnDividerDown = (e: React.PointerEvent) => { runInAction(() => (this._cursor = 'grabbing')); + const batch = UndoManager.StartBatch('stacking width'); setupMoveUpEvents( this, e, this.onDividerMove, - action(() => (this._cursor = 'grab')), + action(() => { + this._cursor = 'grab'; + batch.end(); + }), emptyFunction ); }; @@ -455,18 +460,24 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection const docs = this.childDocList; // still figuring out where to add the document if (docs && newDocs.length) { + newDocs.forEach(newdoc => docs.indexOf(newdoc) !== -1 && docs.splice(docs.indexOf(newdoc), 1)); const insertInd = dropInd === -1 ? docs.length : dropInd + dropAfter; const offset = newDocs.reduce((off, ndoc) => (this.filteredChildren.find((fdoc, i) => ndoc === fdoc && i < insertInd) ? off + 1 : off), 0); newDocs.filter(ndoc => docs.indexOf(ndoc) !== -1).forEach(ndoc => docs.splice(docs.indexOf(ndoc), 1)); docs.splice(insertInd - offset, 0, ...newDocs); } + return true; } } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) { const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' }); - this.props.addDocument?.(source); + if (!this.props.addDocument?.(source)) e.preventDefault(); de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed e.stopPropagation(); - } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData); + return true; + } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) { + return this.internalAnchorAnnoDrop(e, de.complete.annoDragData); + } + e.preventDefault(); return false; }; @@ -700,7 +711,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection } @computed get backgroundEvents() { - return SnappingManager.GetIsDragging(); + return this.props.isContentActive() === false ? 'none' : undefined; } observer: any; render() { @@ -710,7 +721,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection SetValue: this.addGroup, contents: '+ ADD A GROUP', }; - const buttonMenu = this.rootDoc.buttonMenu; + const buttonMenu = this.rootDoc.layout_headerButton; const noviceExplainer = this.rootDoc.layout_explainer; return ( <> @@ -727,7 +738,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection style={{ overflowY: this.isContentActive() ? 'auto' : 'hidden', background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor), - pointerEvents: (this.props.pointerEvents?.() as any) ?? (this.backgroundEvents ? 'all' : undefined), + pointerEvents: (this.props.pointerEvents?.() as any) ?? this.backgroundEvents, }} onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))} onDrop={this.onExternalDrop.bind(this)} diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 6be9cb72d..ebb4ba5a1 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -94,6 +94,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC columnDrop = action((e: Event, de: DragManager.DropEvent) => { const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) }; this.props.pivotField && drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false)); + return true; }); getValue = (value: string): any => { const parsed = parseInt(value); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index d1b7f6ff6..78789247f 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -185,12 +185,14 @@ export function CollectionSubView<X>(moreProps?: X) { @undoBatch protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {} - protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) { + protected onInternalPreDrop(e: Event, de: DragManager.DropEvent) { if (de.complete.docDragData) { - // if targetDropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction. - // otherwise, the targetAction should become the actual action (which can still be overridden by the userDropAction -eg, shift/ctrl keys) - if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) { - de.complete.docDragData.dropAction = targetAction; + // override the dropEvent's dropAction + const dropAction = this.layoutDoc.dropAction as dropActionType; + // if the dropEvent's dragAction is, say 'embed', but we're just dragging within a collection, we may not actually want to make an embedding. + // so we check if our collection has a dropAction set on it and if so, we use that instead. + if (dropAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) { + de.complete.docDragData.dropAction = dropAction; } e.stopPropagation(); } @@ -203,7 +205,7 @@ export function CollectionSubView<X>(moreProps?: X) { protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { const docDragData = de.complete.docDragData; if (docDragData) { - let added = false; + let added = undefined; const dropAction = docDragData.dropAction || docDragData.userDropAction; const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]); const someMoved = !dropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag)); @@ -212,22 +214,23 @@ export function CollectionSubView<X>(moreProps?: X) { const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d); const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d); if (movedDocs.length) { - const canAdd = this.props.Document._type_collection === CollectionViewType.Pile || de.embedKey || this.props.Document.allowOverlayDrop || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document); - added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse); + const canAdd = de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc); + const moved = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse); + added = canAdd || moved ? moved : undefined; } else { - ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData }); + ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData }); added = addedDocs.length ? this.addDocument(addedDocs) : true; } - added && e.stopPropagation(); - return added; } else { - ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData }); + ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData }); added = this.addDocument(docDragData.droppedDocuments); + !added && alert('You cannot perform this move'); } - !added && alert('You cannot perform this move'); - e.stopPropagation(); - return added; + added === false && !this.props.isAnnotationOverlay && e.preventDefault(); + added === true && e.stopPropagation(); + return added ? true : false; } else if (de.complete.annoDragData) { + e.stopPropagation(); const dropCreator = de.complete.annoDragData.dropDocCreator; de.complete.annoDragData.dropDocCreator = () => { const dropped = dropCreator(this.props.isAnnotationOverlay ? this.rootDoc : undefined); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 60e6815e5..192d48c64 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -34,8 +34,6 @@ export class CollectionTimeView extends CollectionSubView() { async componentDidMount() { this.props.setContentView?.(this); - //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), ""); - ///const childText = "const embedding = getEmbedding(self); switchView(embedding, detailView); embedding.dropAction='embed'; useRightSplit(embedding, shiftKey); "; runInAction(() => { this._childClickedScript = ScriptField.MakeScript('openInLightbox(self)', { this: Doc.name }); this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' }); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ed1e0c067..4cd3885f5 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -3,7 +3,6 @@ import { observer } from 'mobx-react'; import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; import { DocData, Height, Width } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; -import { InkTool } from '../../../fields/InkField'; import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; @@ -90,8 +89,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent @observable _isAnyChildContentActive = false; whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive))); - isContentActive = (outsideReaction?: boolean) => - Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction) ? true : false; + isContentActive = (outsideReaction?: boolean) => (this._isAnyChildContentActive ? true : this.props.isContentActive() ? true : false); componentWillUnmount() { this._isDisposing = true; @@ -142,11 +140,16 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree if ((this._mainEle = ele)) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this)); }; - protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => { + protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent) => { + const dropAction = this.layoutDoc.dropAction as dropActionType; const dragData = de.complete.docDragData; if (dragData) { - const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.embedContainer === this.doc && this.childDocs.includes(d)); - dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? 'same' : dragData.dropAction; + const sameTree = Doc.AreProtosEqual(dragData.treeViewDoc, this.rootDoc) ? true : false; + const isAlreadyInTree = () => sameTree || dragData.draggedDocuments.some(d => d.embedContainer === this.doc && this.childDocs.includes(d)); + if (isAlreadyInTree() !== sameTree) { + console.log('WHAAAT'); + } + dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree ? 'same' : dragData.dropAction; } }; @@ -261,7 +264,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree @observable _renderCount = 1; @computed get treeViewElements() { TraceMobx(); - const dropAction = StrCast(this.doc.childDropAction) as dropActionType; + const dragAction = StrCast(this.doc.childDragAction) 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; if (this._renderCount < this.treeChildren.length) setTimeout(action(() => (this._renderCount = Math.min(this.treeChildren.length, this._renderCount + 20)))); @@ -276,7 +279,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree addDoc, this.remove, moveDoc, - dropAction, + dragAction, this.props.addDocTab, this.props.styleProvider, this.screenToLocalTransform, @@ -383,7 +386,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree @observable _headerHeight = 0; @computed get content() { const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor); - const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined); + const pointerEvents = () => (this.props.isContentActive() === false ? 'none' : undefined); const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar; return ( <div style={{ display: 'flex', flexDirection: 'column', height: '100%', pointerEvents: 'all' }}> @@ -396,6 +399,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree <div className="collectionTreeView-contents" key="tree" + ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)} style={{ ...(!titleBar ? { marginLeft: this.marginX(), paddingTop: this.marginTop() } : {}), overflow: 'auto', @@ -419,8 +423,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree minHeight: '100%', }} onWheel={e => e.stopPropagation()} - onDrop={this.onTreeDrop} - ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}> + onDrop={this.onTreeDrop}> <ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul> </div> </div> @@ -440,12 +443,12 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree setContentView={emptyFunction} NativeWidth={returnZero} NativeHeight={returnZero} - pointerEvents={SnappingManager.GetIsDragging() ? returnAll : returnNone} + pointerEvents={this.props.isContentActive() && SnappingManager.GetIsDragging() ? returnAll : returnNone} isAnnotationOverlay={true} isAnnotationOverlayScrollable={true} childDocumentsActive={this.props.isDocumentActive} fieldKey={this.props.fieldKey + '_annotations'} - dropAction={'move'} + dropAction="move" select={emptyFunction} addDocument={this.addAnnotationDocument} removeDocument={this.remAnnotationDocument} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 7913d3188..c33519afb 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -9,6 +9,7 @@ import { TraceMobx } from '../../../fields/util'; import { returnEmptyString } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { CollectionViewType } from '../../documents/DocumentTypes'; +import { dropActionType } from '../../util/DragManager'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; import { InteractionUtils } from '../../util/InteractionUtils'; import { ContextMenu } from '../ContextMenu'; @@ -55,7 +56,7 @@ interface CollectionViewProps_ extends FieldViewProps { childHideDecorationTitle?: () => boolean; childHideResizeHandles?: () => boolean; childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection - childCanEmbedOnDrag?: boolean; + childDragAction?: dropActionType; childXPadding?: number; childYPadding?: number; childLayoutString?: string; @@ -220,7 +221,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles); childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle); childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null); - isContentActive = (outsideReaction?: boolean) => this.props.isContentActive() || this.isAnyChildContentActive(); + isContentActive = (outsideReaction?: boolean) => (this.isAnyChildContentActive() ? true : this.props.isContentActive()); render() { TraceMobx(); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 3b99339af..4d780f46b 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { clamp } from 'lodash'; -import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableSet, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as ReactDOM from 'react-dom/client'; import { Doc, Opt } from '../../../fields/Doc'; @@ -43,12 +43,21 @@ interface TabDocViewProps { } @observer export class TabDocView extends React.Component<TabDocViewProps> { + static _allTabs = new ObservableSet<TabDocView>(); _mainCont: HTMLDivElement | null = null; _tabReaction: IReactionDisposer | undefined; @observable _activated: boolean = false; @observable _panelWidth = 0; @observable _panelHeight = 0; + @observable _hovering = false; @observable _isActive: boolean = false; + @observable _isAnyChildContentActive = false; + @computed get _isUserActivated() { + return SelectionManager.Views().some(view => view.rootDoc === this._document) || this._isAnyChildContentActive; + } + @computed get _isContentActive() { + return this._isUserActivated || this._hovering; + } @observable _document: Doc | undefined; @observable _view: DocumentView | undefined; @@ -61,9 +70,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { return 'transparent'; } @computed get tabColor() { - let tabColor = StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); - if (tabColor === 'transparent') return 'black'; - return tabColor; + return this._isUserActivated ? Colors.WHITE : this._hovering ? Colors.LIGHT_GRAY : Colors.MEDIUM_GRAY; } @computed get tabTextColor() { return this._document?.type === DocumentType.PRES ? 'black' : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color))); @@ -191,7 +198,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { } }); tab._disposers.selectionDisposer = reaction( - () => SelectionManager.Views().some(v => v.topMost && v.props.Document === doc), + () => SelectionManager.Views().some(view => view.rootDoc === this._document), action(selected => { if (selected) this._activated = true; const toggle = tab.element[0].children[2].children[0] as HTMLInputElement; @@ -305,6 +312,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs } + @action componentDidMount() { new _global.ResizeObserver( action((entries: any) => { @@ -319,14 +327,17 @@ export class TabDocView extends React.Component<TabDocViewProps> { // this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }), // ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""), // { fireImmediately: true }); + TabDocView._allTabs.add(this); } componentDidUpdate() { this._view && DocumentManager.Instance.AddView(this._view); } + @action componentWillUnmount() { this._tabReaction?.(); this._view && DocumentManager.Instance.RemoveView(this._view); + TabDocView._allTabs.delete(this); this.props.glContainer.layoutManager.off('activeContentItemChanged', this.onActiveContentItemChanged); } @@ -335,6 +346,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { private onActiveContentItemChanged(contentItem: any) { if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) { this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab; + if (!this._view) setTimeout(() => SelectionManager.SelectView(this._view, false)); !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one. } } @@ -404,11 +416,11 @@ export class TabDocView extends React.Component<TabDocViewProps> { }; PanelWidth = () => this._panelWidth; PanelHeight = () => this._panelHeight; - miniMapColor = () => this.tabColor; + miniMapColor = () => Colors.MEDIUM_GRAY; tabView = () => this._view; disableMinimap = () => !this._document || this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._type_collection !== CollectionViewType.Freeform; - hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.layout_hideMinimap); - + whenChildContentActiveChanges = (isActive: boolean) => (this._isAnyChildContentActive = isActive); + isContentActive = () => this._isContentActive; @computed get docView() { return !this._activated || !this._document ? null : ( <> @@ -426,7 +438,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { DataDoc={!Doc.AreProtosEqual(this._document[DocData], this._document) ? this._document[DocData] : undefined} onBrowseClick={MainView.Instance.exploreMode} waitForDoubleClickToClick={MainView.Instance.waitForDoubleClick} - isContentActive={returnTrue} + isContentActive={this.isContentActive} isDocumentActive={returnFalse} PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} @@ -440,30 +452,15 @@ export class TabDocView extends React.Component<TabDocViewProps> { ScreenToLocalTransform={this.ScreenToLocalTransform} dontCenter={'y'} rootSelected={returnTrue} - whenChildContentsActiveChanged={emptyFunction} + whenChildContentsActiveChanged={this.whenChildContentActiveChanges} focus={this.focusFunc} docViewPath={returnEmptyDoclist} bringToFront={emptyFunction} pinToPres={TabDocView.PinDoc} /> - <TabMinimapView key="minimap" hideMinimap={this.hideMinimap} addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} /> - <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.layout_hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}> - <div - className="miniMap-hidden" - style={{ - display: this.disableMinimap() || this._document._type_collection !== 'freeform' ? 'none' : undefined, - color: this._document.layout_hideMinimap ? Colors.BLACK : Colors.WHITE, - backgroundColor: this._document.layout_hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE, - boxShadow: this._document.layout_hideMinimap ? Shadows.STANDARD_SHADOW : undefined, - }} - onPointerDown={e => e.stopPropagation()} - onClick={action(e => { - e.stopPropagation(); - this._document!.layout_hideMinimap = !this._document!.layout_hideMinimap; - })}> - <FontAwesomeIcon icon={'globe-asia'} size="lg" /> - </div> - </Tooltip> + {this.disableMinimap() || this._document._type_collection !== CollectionViewType.Freeform ? null : ( + <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} /> + )} </> ); } @@ -475,6 +472,10 @@ export class TabDocView extends React.Component<TabDocViewProps> { style={{ fontFamily: Doc.UserDoc().renderStyle === 'comic' ? 'Comic Sans MS' : undefined, }} + onPointerEnter={action(() => (this._hovering = true))} + onPointerLeave={action(() => (this._hovering = false))} + onDragOver={action(() => (this._hovering = true))} + onDragLeave={action(() => (this._hovering = false))} ref={ref => { if ((this._mainCont = ref)) { if (this._lastTab) { @@ -494,7 +495,6 @@ export class TabDocView extends React.Component<TabDocViewProps> { interface TabMinimapViewProps { document: Doc; - hideMinimap: () => boolean; tabView: () => DocumentView | undefined; addDocTab: (doc: Doc, where: OpenWhere) => boolean; PanelWidth: () => number; @@ -569,47 +569,67 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { if (!this.renderBounds) return null; const miniWidth = (this.props.PanelWidth() / NumCast(this.props.document._freeform_scale, 1) / this.renderBounds.dim) * 100; const miniHeight = (this.props.PanelHeight() / NumCast(this.props.document._freeform_scale, 1) / this.renderBounds.dim) * 100; - const miniLeft = 50 + ((NumCast(this.props.document._freeform_) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2; + const miniLeft = 50 + ((NumCast(this.props.document._freeform_panX) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2; const miniTop = 50 + ((NumCast(this.props.document._freeform_panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2; const miniSize = this.returnMiniSize(); - return this.props.hideMinimap() ? null : ( - <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}> - <CollectionFreeFormView - Document={this.props.document} - 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} - isContentActive={emptyFunction} - isAnyChildContentActive={returnFalse} - select={emptyFunction} - dropAction={undefined} - isSelected={returnFalse} - dontRegisterView={true} - fieldKey={Doc.LayoutFieldKey(this.props.document)} - bringToFront={emptyFunction} - rootSelected={returnTrue} - addDocument={returnFalse} - moveDocument={returnFalse} - removeDocument={returnFalse} - PanelWidth={this.returnMiniSize} - PanelHeight={this.returnMiniSize} - ScreenToLocalTransform={Transform.Identity} - renderDepth={0} - whenChildContentsActiveChanged={emptyFunction} - focus={emptyFunction} - styleProvider={TabMinimapView.miniStyleProvider} - addDocTab={this.props.addDocTab} - pinToPres={TabDocView.PinDoc} - childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist} - childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist} - searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist} - fitContentsToBox={returnTrue} - /> - <div className="miniOverlay" onPointerDown={this.miniDown}> - <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% ` }} /> - </div> - </div> + return ( + <> + {' '} + {this.props.document?.layout_hideMinimap ? null : ( + <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}> + <CollectionFreeFormView + Document={this.props.document} + 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} + isContentActive={emptyFunction} + isAnyChildContentActive={returnFalse} + select={emptyFunction} + isSelected={returnFalse} + dontRegisterView={true} + fieldKey={Doc.LayoutFieldKey(this.props.document)} + bringToFront={emptyFunction} + rootSelected={returnTrue} + addDocument={returnFalse} + moveDocument={returnFalse} + removeDocument={returnFalse} + PanelWidth={this.returnMiniSize} + PanelHeight={this.returnMiniSize} + ScreenToLocalTransform={Transform.Identity} + renderDepth={0} + whenChildContentsActiveChanged={emptyFunction} + focus={emptyFunction} + styleProvider={TabMinimapView.miniStyleProvider} + addDocTab={this.props.addDocTab} + pinToPres={TabDocView.PinDoc} + childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist} + childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist} + searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist} + fitContentsToBox={returnTrue} + /> + <div className="miniOverlay" onPointerDown={this.miniDown}> + <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% ` }} /> + </div> + </div> + )} + <Tooltip key="ttip" title={<div className="dash-tooltip">{this.props.document.layout_hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}> + <div + className="miniMap-hidden" + style={{ + color: this.props.document.layout_hideMinimap ? Colors.BLACK : Colors.WHITE, + backgroundColor: this.props.document.layout_hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE, + boxShadow: this.props.document.layout_hideMinimap ? Shadows.STANDARD_SHADOW : undefined, + }} + onPointerDown={e => e.stopPropagation()} + onClick={action(e => { + e.stopPropagation(); + this.props.document!.layout_hideMinimap = !this.props.document!.layout_hideMinimap; + })}> + <FontAwesomeIcon icon="globe-asia" size="lg" /> + </div> + </Tooltip> + </> ); } } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 6bc30c451..8d8d895c6 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -21,7 +21,7 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; -import { undoBatch, UndoManager } from '../../util/UndoManager'; +import { undoable, undoBatch, UndoManager } from '../../util/UndoManager'; import { EditableView } from '../EditableView'; import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss'; import { DocumentView, DocumentViewInternal, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView'; @@ -45,7 +45,7 @@ export interface TreeViewProps { dataDoc?: Doc; treeViewParent: Doc; renderDepth: number; - dropAction: dropActionType; + dragAction: dropActionType; addDocTab: (doc: Doc, where: OpenWhere) => boolean; panelWidth: () => number; panelHeight: () => number; @@ -276,7 +276,7 @@ export class TreeView extends React.Component<TreeViewProps> { }; onPointerEnter = (e: React.PointerEvent): void => { this.props.isContentActive(true) && Doc.BrushDoc(this.dataDoc); - if (e.buttons === 1 && SnappingManager.GetIsDragging()) { + if (e.buttons === 1 && SnappingManager.GetIsDragging() && this.props.isContentActive()) { this._header.current!.className = 'treeView-header'; document.removeEventListener('pointermove', this.onDragMove, true); document.removeEventListener('pointerup', this.onDragUp, true); @@ -341,13 +341,13 @@ export class TreeView extends React.Component<TreeViewProps> { }; makeFolder = () => { - const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true }); + const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true }); TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView }; return this.props.addDocument(folder); }; deleteItem = () => this.props.removeDoc?.(this.doc); - preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => { + preTreeDrop = (e: Event, de: DragManager.DropEvent) => { const dragData = de.complete.docDragData; dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? 'same' : dragData.dropAction); }; @@ -355,7 +355,7 @@ export class TreeView extends React.Component<TreeViewProps> { @undoBatch treeDrop = (e: Event, de: DragManager.DropEvent) => { const pt = [de.x, de.y]; - if (!this._header.current) return; + if (!this._header.current) return false; 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 * 0.75) || (!before && this.treeViewOpen && this.childDocs?.length ? true : false); @@ -364,14 +364,25 @@ export class TreeView extends React.Component<TreeViewProps> { const destDoc = this.doc; DocUtils.MakeLink(sourceDoc, destDoc, { link_relationship: 'tree link' }); e.stopPropagation(); + return true; } const docDragData = de.complete.docDragData; if (docDragData && pt[0] < rect.left + rect.width) { if (docDragData.draggedDocuments[0] === this.doc) return true; - if (this.dropDocuments(docDragData.droppedDocuments, before, inside, docDragData.dropAction, docDragData.removeDocument, docDragData.moveDocument, docDragData.treeViewDoc === this.props.treeView.props.Document)) { - e.stopPropagation(); - } + const added = this.dropDocuments( + docDragData.droppedDocuments, // + before, + inside, + docDragData.dropAction, + docDragData.removeDocument, + docDragData.moveDocument, + docDragData.treeViewDoc === this.props.treeView.props.Document + ); + e.stopPropagation(); + !added && e.preventDefault(); + return added; } + return false; }; dropping: boolean = false; @@ -391,7 +402,7 @@ export class TreeView extends React.Component<TreeViewProps> { const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeViewFreezeChildren).includes('add')) || forceAdd; if (canAdd) { this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true); - const res = UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false)); + const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false); this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = false); return res; } @@ -460,7 +471,7 @@ export class TreeView extends React.Component<TreeViewProps> { addDoc, remDoc, moveDoc, - this.props.dropAction, + this.props.dragAction, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, @@ -585,12 +596,12 @@ export class TreeView extends React.Component<TreeViewProps> { downY = e.clientY; e.stopPropagation(); }} - onClick={e => { + onClick={undoable(e => { if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) { !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]); e.stopPropagation(); } - }}> + }, 'sort order')}> {!docs ? null : TreeView.GetChildElements( @@ -604,7 +615,7 @@ export class TreeView extends React.Component<TreeViewProps> { addDoc, remDoc, moveDoc, - StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, + StrCast(this.doc.childDragAction, this.props.dragAction) as dropActionType, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, @@ -904,7 +915,6 @@ export class TreeView extends React.Component<TreeViewProps> { hideDecorationTitle={this.props.treeView.outlineMode} hideResizeHandles={this.props.treeView.outlineMode} styleProvider={this.titleStyleProvider} - enableDragWhenActive={true} onClickScriptDisable="never" // tree docViews have a script to show fields, etc. docViewPath={this.props.treeView.props.docViewPath} treeViewDoc={this.props.treeView.props.Document} @@ -914,7 +924,7 @@ export class TreeView extends React.Component<TreeViewProps> { pinToPres={emptyFunction} onClick={this.onChildClick} onDoubleClick={this.onChildDoubleClick} - dropAction={this.props.dropAction} + dragAction={this.props.dragAction} moveDocument={this.move} removeDocument={this.props.removeDoc} ScreenToLocalTransform={this.getTransform} @@ -925,7 +935,7 @@ export class TreeView extends React.Component<TreeViewProps> { PanelHeight={return18} contextMenuItems={this.contextMenuItems} renderDepth={1} - isContentActive={this.props.isContentActive} + isContentActive={emptyFunction} //this.props.isContentActive} isDocumentActive={this.props.isContentActive} focus={this.refocus} whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} @@ -950,7 +960,7 @@ 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 === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined, - pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined, + pointerEvents: !this.props.isContentActive() ? 'none' : undefined, }}> {view} </div> @@ -1132,7 +1142,7 @@ export class TreeView extends React.Component<TreeViewProps> { add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean, remove: undefined | ((doc: Doc | Doc[]) => boolean), move: DragManager.MoveFunction, - dropAction: dropActionType, + dragAction: dropActionType, addDocTab: (doc: Doc, where: OpenWhere) => boolean, styleProvider: undefined | StyleProviderFunc, screenToLocalXf: () => Transform, @@ -1218,7 +1228,7 @@ export class TreeView extends React.Component<TreeViewProps> { panelHeight={rowHeight} dontRegisterView={dontRegisterView} moveDocument={move} - dropAction={dropAction} + dragAction={dragAction} addDocTab={addDocTab} ScreenToLocalTransform={screenToLocalXf} isContentActive={isContentActive} @@ -1239,6 +1249,6 @@ export class TreeView extends React.Component<TreeViewProps> { ScriptingGlobals.add(function TreeView_addNewFolder() { TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined }; - const opts = { title: 'Untitled folder', _stayInCollection: true, isFolder: true }; + const opts = { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true }; return Doc.AddDocToList(Doc.MyFilesystem, 'data', Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id)); }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 36472dd2e..ba31916a7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -418,15 +418,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @undoBatch internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) { if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) { - // dragged document is a child of this collection - if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.embedContainer !== this.props.Document) { - // if the source doc view's embedContainer isn't this same freeformcollectionlinkDragData.dragDocument.embedContainer === this.props.Document + let added = false; + // do nothing if link is dropped into any freeform view parent of dragged document + if (!linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.rootDoc) { const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' }); - this.props.addDocument?.(source); + added = this.props.addDocument?.(source) ? true : false; de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed } - e.stopPropagation(); // do nothing if link is dropped into any freeform view parent of dragged document - return true; + e.stopPropagation(); + !added && e.preventDefault(); + return added; } return false; } @@ -1052,7 +1053,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection if (this.layoutDoc._Transform || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; e.stopPropagation(); const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.rootDoc) + '_nativeHeight'], this.nativeHeight); - const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling; + const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling + 1e-4; switch (!e.ctrlKey ? Doc.UserDoc().freeformScrollMode : freeformScrollMode.Pan) { case freeformScrollMode.Pan: // if ctrl is selected then zoom @@ -1240,7 +1241,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; }; - isContentActive = () => this.props.isSelected() || this.props.isContentActive(); + isContentActive = () => this.props.isContentActive(); @undoBatch @action @@ -1267,7 +1268,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); const pointerEvents = DocumentDecorations.Instance.Interacting ? 'none' - : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.()); + : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.()); return pointerEvents; }; getChildDocView(entry: PoolData) { @@ -1297,8 +1298,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection childFilters={this.childDocFilters} childFiltersByRanges={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} - isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive} - isContentActive={this.props.childContentsActive ?? emptyFunction} + isDocumentActive={this.props.childDocumentsActive?.() || this.rootDoc._isGroup ? this.props.isDocumentActive : this.isContentActive} + isContentActive={this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction} focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus} addDocTab={this.addDocTab} addDocument={this.props.addDocument} @@ -1308,10 +1309,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} docViewPath={this.props.docViewPath} styleProvider={this.getClusterColor} - canEmbedOnDrag={this.props.childCanEmbedOnDrag} + dragAction={(this.rootDoc.childDragAction ?? this.props.childDragAction) as dropActionType} dataProvider={this.childDataProvider} sizeProvider={this.childSizeProvider} - dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} bringToFront={this.bringToFront} layout_showTitle={this.props.childlayout_showTitle} dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView} @@ -1987,7 +1987,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection onDragOver={e => e.preventDefault()} onContextMenu={this.onContextMenu} style={{ - pointerEvents: SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement()) ? 'all' : (this.props.pointerEvents?.() as any), + pointerEvents: this.props.isContentActive() && SnappingManager.GetIsDragging() ? 'all' : (this.props.pointerEvents?.() as any), textAlign: this.isAnnotationOverlay ? 'initial' : undefined, transform: `scale(${this.nativeDimScaling || 1})`, width: `${100 / (this.nativeDimScaling || 1)}%`, @@ -2256,22 +2256,20 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY: number) { const browseTransitionTime = 500; SelectionManager.DeselectAll(); - if ( - dv.props.focus(dv.props.Document, { - willZoomCentered: true, - zoomScale: 0.8, - zoomTime: browseTransitionTime, - }) === undefined - ) { - const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; - let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; - while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; - const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview - ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5, browseTransitionTime); - Doc.linkFollowHighlight(dv?.props.Document, false); - } else { - DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }); - } + DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => { + if (!focused) { + const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; + let containers = dv.props.docViewPath(); + let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + for (var cont of containers) { + parFfview = parFfview ?? cont.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + } + while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview + ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5, browseTransitionTime); + Doc.linkFollowHighlight(dv?.props.Document, false); + } + }); } ScriptingGlobals.add(CollectionBrowseClick); ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 430e8ecd5..9b0abc48b 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -385,8 +385,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque newCollection._width = this.Bounds.width; newCollection._height = this.Bounds.height; newCollection._isGroup = makeGroup; + newCollection._dragWhenActive = makeGroup; newCollection.forceActive = makeGroup; - newCollection.enableDragWhenActive = makeGroup; newCollection.x = this.Bounds.left; newCollection.y = this.Bounds.top; newCollection.layout_fitWidth = true; diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index e8ae88ae5..cd8b7a0cc 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -368,7 +368,7 @@ export class CollectionGridView extends CollectionSubView() { <div className="collectionGridView-contents" ref={this.createDashEventsTarget} - style={{ pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined }} + style={{ pointerEvents: !this.props.isContentActive() ? 'none' : undefined }} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onDrop={this.onExternalDrop}> diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 65578f214..8f90e4444 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; -import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, Opt } from '../../../../fields/Doc'; @@ -50,34 +50,10 @@ export class CollectionLinearView extends CollectionSubView() { width => this.childDocs.length && (this.layoutDoc._width = width), { fireImmediately: true } ); - - this._selectedDisposer = reaction( - () => NumCast(this.layoutDoc.selectedIndex), - i => - runInAction(() => { - this._selectedIndex = i; - let selected: any = undefined; - this.childLayoutPairs.map(async (pair, ind) => { - const isSelected = this._selectedIndex === ind; - if (isSelected) { - selected = pair; - } else { - ScriptCast(DocCast(pair.layout.proto)?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log); - } - }); - if (selected && selected.layout) { - ScriptCast(DocCast(selected.layout.proto)?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log); - } - }), - { fireImmediately: true } - ); } protected createDashEventsTarget = (ele: HTMLDivElement | null) => { - //used for stacking and masonry view - this._dropDisposer && this._dropDisposer(); - if (ele) { - this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); - } + this._dropDisposer?.(); + if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); }; dimension = () => NumCast(this.rootDoc._height); @@ -89,12 +65,8 @@ export class CollectionLinearView extends CollectionSubView() { @action exitLongLinks = () => { - if (DocumentLinksButton.StartLink) { - if (DocumentLinksButton.StartLink.Document) { - action((e: React.PointerEvent<HTMLDivElement>) => { - Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc); - }); - } + if (DocumentLinksButton.StartLink?.Document) { + action((e: React.PointerEvent<HTMLDivElement>) => Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc)); } DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; @@ -201,7 +173,7 @@ export class CollectionLinearView extends CollectionSubView() { moveDocument={this.props.moveDocument} addDocTab={this.props.addDocTab} pinToPres={emptyFunction} - dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType} + dragAction={(this.layoutDoc.childDragAction ?? this.props.childDragAction) as dropActionType} rootSelected={this.props.isSelected} removeDocument={this.props.removeDocument} ScreenToLocalTransform={docXf} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss index 821c8d804..f87a06033 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss @@ -8,6 +8,11 @@ display: flex; flex-direction: column; width: 100%; + align-items: center; + + .contentFittingDocumentView { + width: unset; + } .label-wrapper { display: flex; @@ -15,7 +20,6 @@ justify-content: center; height: 20px; } - } .multiColumnResizer { @@ -30,5 +34,4 @@ transition: 0.5s background-color ease; } } - -}
\ No newline at end of file +} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 34fa0343d..10532b9d9 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -5,7 +5,6 @@ import { Doc, DocListCast } from '../../../../fields/Doc'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { returnFalse } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; -import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { DocumentView } from '../../nodes/DocumentView'; @@ -194,11 +193,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { let dropInd = -1; if (de.complete.docDragData && this._mainCont) { let curInd = -1; - de.complete.docDragData?.droppedDocuments.forEach( - action((d: Doc) => { - curInd = this.childDocs.indexOf(d); - }) - ); + de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d))); Array.from(this._mainCont.children).forEach((child, index) => { const brect = child.getBoundingClientRect(); if (brect.x < de.x && brect.x + brect.width > de.x) { @@ -225,6 +220,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { } }) ); + return true; } } return false; @@ -234,8 +230,15 @@ export class CollectionMulticolumnView extends CollectionSubView() { onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick); isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive(); - isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false); - getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { + isChildContentActive = () => { + const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive; + return this.props.isContentActive?.() === false || childDocsActive === false + ? false // + : this.props.isDocumentActive?.() && childDocsActive + ? true + : undefined; + }; + getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number, shouldNotScale: () => boolean) => { return ( <DocumentView Document={layout} @@ -247,8 +250,9 @@ export class CollectionMulticolumnView extends CollectionSubView() { renderDepth={this.props.renderDepth + 1} PanelWidth={width} PanelHeight={height} + shouldNotScale={shouldNotScale} rootSelected={this.rootSelected} - dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} + dragAction={(this.props.Document.childDragAction ?? this.props.childDragAction) as dropActionType} onClick={this.onChildClickHandler} onDoubleClick={this.onChildDoubleClickHandler} suppressSetHeight={true} @@ -284,15 +288,19 @@ export class CollectionMulticolumnView extends CollectionSubView() { const collector: JSX.Element[] = []; for (let i = 0; i < childLayoutPairs.length; i++) { const { layout } = childLayoutPairs[i]; + const aspect = Doc.NativeAspect(layout, undefined, true); + const width = () => this.lookupPixels(layout); + const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0); + const docwidth = () => (layout._layout_forceReflow ? width() : Math.min(height() * aspect, width())); + const docheight = () => Math.min(docwidth() / aspect, height()); const dxf = () => this.lookupIndividualTransform(layout) - .translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin)) + .translate(-NumCast(Document._xMargin) - (width() - docwidth()) / 2, -NumCast(Document._yMargin)) .scale(this.props.NativeDimScaling?.() || 1); - const width = () => this.lookupPixels(layout); - const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0); + const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox); collector.push( - <div className={'document-wrapper'} key={'wrapper' + i} style={{ width: width() }}> - {this.getDisplayDoc(layout, dxf, width, height)} + <div className="document-wrapper" key={'wrapper' + i} style={{ width: width() }}> + {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)} <WidthLabel layout={layout} collectionDoc={Document} /> </div>, <ResizeBar diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss index 79fb195e8..ec7200a03 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss @@ -9,6 +9,12 @@ display: flex; flex-direction: row; height: 100%; + align-items: center; + margin: auto; + + .contentFittingDocumentView { + height: unset; + } .label-wrapper { display: flex; @@ -16,7 +22,6 @@ justify-content: center; height: 20px; } - } .multiRowResizer { @@ -31,5 +36,4 @@ transition: 0.5s background-color ease; } } - -}
\ No newline at end of file +} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index bab77ed48..04cfc5456 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -5,7 +5,6 @@ import { Doc, DocListCast } from '../../../../fields/Doc'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { returnFalse } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; -import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { DocumentView } from '../../nodes/DocumentView'; @@ -194,11 +193,7 @@ export class CollectionMultirowView extends CollectionSubView() { let dropInd = -1; if (de.complete.docDragData && this._mainCont) { let curInd = -1; - de.complete.docDragData?.droppedDocuments.forEach( - action((d: Doc) => { - curInd = this.childDocs.indexOf(d); - }) - ); + de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d))); Array.from(this._mainCont.children).forEach((child, index) => { const brect = child.getBoundingClientRect(); if (brect.y < de.y && brect.y + brect.height > de.y) { @@ -225,6 +220,7 @@ export class CollectionMultirowView extends CollectionSubView() { } }) ); + return true; } } return false; @@ -234,8 +230,15 @@ export class CollectionMultirowView extends CollectionSubView() { onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick); isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive(); - isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false); - getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { + isChildContentActive = () => { + const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive; + return this.props.isContentActive?.() === false || childDocsActive === false + ? false // + : this.props.isDocumentActive?.() && childDocsActive + ? true + : undefined; + }; + getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number, shouldNotScale: () => boolean) => { return ( <DocumentView Document={layout} @@ -247,8 +250,9 @@ export class CollectionMultirowView extends CollectionSubView() { renderDepth={this.props.renderDepth + 1} PanelWidth={width} PanelHeight={height} + shouldNotScale={shouldNotScale} rootSelected={this.rootSelected} - dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} + dropAction={StrCast(this.rootDoc.childDragAction) as dropActionType} onClick={this.onChildClickHandler} onDoubleClick={this.onChildDoubleClickHandler} ScreenToLocalTransform={dxf} @@ -257,6 +261,7 @@ export class CollectionMultirowView extends CollectionSubView() { hideResizeHandles={this.props.childHideResizeHandles?.()} hideDecorationTitle={this.props.childHideDecorationTitle?.()} fitContentsToBox={this.props.fitContentsToBox} + dragAction={this.props.childDragAction} focus={this.props.focus} childFilters={this.childDocFilters} childFiltersByRanges={this.childDocRangeFilters} @@ -283,15 +288,19 @@ export class CollectionMultirowView extends CollectionSubView() { const collector: JSX.Element[] = []; for (let i = 0; i < childLayoutPairs.length; i++) { const { layout } = childLayoutPairs[i]; + const aspect = Doc.NativeAspect(layout, undefined, true); + const height = () => this.lookupPixels(layout); + const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0); + const docheight = () => Math.min(width() / aspect, height()); + const docwidth = () => (layout._layout_forceReflow ? width() : Math.min(width(), docheight() * aspect)); const dxf = () => this.lookupIndividualTransform(layout) - .translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin)) + .translate(-NumCast(Document._xMargin) - (width() - docwidth()) / 2, -NumCast(Document._yMargin) - (height() - docheight()) / 2) .scale(this.props.NativeDimScaling?.() || 1); - const height = () => this.lookupPixels(layout); - const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0); + const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox); collector.push( - <div className={'document-wrapper'} style={{ height: height() }} key={'wrapper' + i}> - {this.getDisplayDoc(layout, dxf, width, height)} + <div className="document-wrapper" style={{ height: height() }} key={'wrapper' + i}> + {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)} <HeightLabel layout={layout} collectionDoc={Document} /> </div>, <ResizeBar diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 15424de98..ee5bf82ed 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -434,7 +434,6 @@ export class CollectionSchemaView extends CollectionSubView() { @action onInternalDrop = (e: Event, de: DragManager.DropEvent) => { if (de.complete.columnDragData) { - e.stopPropagation(); const mouseX = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0]; const index = this.findDropIndex(mouseX); this.moveColumn(de.complete.columnDragData.colIndex, index ?? de.complete.columnDragData.colIndex); @@ -448,6 +447,7 @@ export class CollectionSchemaView extends CollectionSubView() { }); }); + e.stopPropagation(); return true; } const draggedDocs = de.complete.docDragData?.draggedDocuments; @@ -462,7 +462,6 @@ export class CollectionSchemaView extends CollectionSubView() { if (draggedView) DocumentManager.Instance.RemoveView(draggedView); DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true)); }); - e.stopPropagation(); return true; } return false; @@ -952,7 +951,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP styleProvider={DefaultStyleProvider} waitForDoubleClickToClick={returnNever} defaultDoubleClick={returnIgnore} - enableDragWhenActive={true} + dragAction="move" onClickScriptDisable="always" focus={this.props.schema.focusDocument} childFilters={this.props.schema.childDocFilters} @@ -970,7 +969,6 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP hideLinkAnchors={true} layout_fitWidth={returnTrue} scriptContext={this} - canEmbedOnDrag={true} /> </div> ); diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 4f3503751..e8e606030 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -53,13 +53,13 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() { }; onPointerEnter = (e: any) => { - if (!SnappingManager.GetIsDragging()) return; - document.removeEventListener('pointermove', this.onPointerMove); - document.addEventListener('pointermove', this.onPointerMove); + if (SnappingManager.GetIsDragging() && this.props.isContentActive()) { + document.removeEventListener('pointermove', this.onPointerMove); + document.addEventListener('pointermove', this.onPointerMove); + } }; onPointerMove = (e: any) => { - if (!SnappingManager.GetIsDragging()) return; const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.embedContainer === this.schemaDoc); // this.schemaView?._selectedDocs.has(doc) ?? false; if (this._ref && dragIsRow) { diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 97264508c..a958f53ea 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -76,7 +76,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { isSelected: returnFalse, setHeight: returnFalse, select: emptyFunction, - dropAction: 'embed', + dragAction: 'move', bringToFront: emptyFunction, renderDepth: 1, isContentActive: returnFalse, |
