diff options
author | bobzel <zzzman@gmail.com> | 2024-09-23 03:45:04 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2024-09-23 03:45:04 -0400 |
commit | 124c69af0941bd1ed5b03f3275aef37007f06ea7 (patch) | |
tree | cfa505e509f267836d78d2a5ae8a40228e97b2d6 | |
parent | f29740d79ef0b43b445eb4ab86baef77c9d51158 (diff) |
made dropping links marks them as svgs. fixed doc drag interactions when items are selected to not have decorations bounce. fixed card deck to not shrink cards when card deck is short small and items are selected. fixied dragging cards when colletcion is nested to not have decorations bounce, and to allow rapid selection and dragging of cards. fixed dragging docs into/out of card view.
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 11 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCardDeckView.tsx | 83 | ||||
-rw-r--r-- | src/client/views/linking/LinkMenuItem.tsx | 2 |
3 files changed, 62 insertions, 34 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c3bcfdcca..fc4b1f0af 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { IconButton } from 'browndash-components'; -import { action, computed, makeObservable, observable, runInAction } from 'mobx'; +import { action, computed, makeObservable, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { FaUndo } from 'react-icons/fa'; @@ -36,6 +36,7 @@ import { ImageBox } from './nodes/ImageBox'; import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { TagsView } from './TagsView'; +import { setTime } from 'react-datepicker/dist/date_utils'; interface DocumentDecorationsProps { PanelWidth: number; @@ -643,7 +644,9 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora return this._rotCenter; } + @observable forceRender = 0; render() { + this.forceRender; const { b, r, x, y } = this.Bounds; const seldocview = DocumentView.Selected().lastElement(); if (SnappingManager.IsDragging || r - x < 1 || x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(r) || isNaN(b) || isNaN(x) || isNaN(y)) { @@ -656,6 +659,12 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora return null; } + if (seldocview && !seldocview?.ContentDiv?.getBoundingClientRect().width) { + setTimeout(action(() => this.forceRender++)); + return null; + } + console.log('Renderedr'); + // sharing const acl = GetEffectiveAcl(!this._showLayoutAcl ? Doc.GetProto(seldocview.Document) : seldocview.Document); const docShareMode = HierarchyMapping.get(acl)!.name; diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 70b751f4c..e5a6ebc7f 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -1,4 +1,4 @@ -import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx'; +import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, trace } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { ClientUtils, DashColor, returnFalse, returnZero } from '../../../ClientUtils'; @@ -46,8 +46,9 @@ export class CollectionCardView extends CollectionSubView() { private _dropDisposer?: DragManager.DragDropDisposer; private _disposers: { [key: string]: IReactionDisposer } = {}; private _textToDoc = new Map<string, Doc>(); + private _dropped = false; // indicate when a card doc has just moved; - @observable _forceChildXf = false; + @observable _forceChildXf = 0; @observable _hoveredNodeIndex = -1; @observable _docRefs = new ObservableMap<Doc, DocumentView>(); @observable _maxRowCount = 10; @@ -99,6 +100,7 @@ export class CollectionCardView extends CollectionSubView() { }; componentDidMount() { + this._props.setContentViewBox?.(this); // Reaction to cardSort changes this._disposers.sort = reaction( () => GPTPopup.Instance.visible, @@ -110,6 +112,17 @@ export class CollectionCardView extends CollectionSubView() { } } ); + // if card deck moves, then the child doc views are hidden so their screen to local transforms will return empty rectangles + // when inquired from the dom (below in childScreenToLocal). When the doc is actually renders, we need to act like the + // dash data just changed and trigger a React involidation with the correct data (read from the dom). + this._disposers.child = reaction( + () => [this.Document.x, this.Document.y], + () => { + if (!Array.from(this._docRefs.values()).every(dv => dv.ContentDiv?.getBoundingClientRect().width)) { + setTimeout(action(() => this._forceChildXf++)); + } + } + ); } componentWillUnmount() { @@ -174,6 +187,7 @@ export class CollectionCardView extends CollectionSubView() { inactiveDocs = () => this.childDocsWithoutLinks.filter(d => !DocumentView.SelectedDocs().includes(d)); childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, this._props.PanelWidth() / 2); + childPanelHeight = () => this._props.PanelHeight() * this.fitContentScale; onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick); isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive(); isChildContentActive = () => !!this.isContentActive(); @@ -265,14 +279,7 @@ export class CollectionCardView extends CollectionSubView() { @action onPointerMove = (x: number, y: number) => { - this._docDraggedIndex = -1; - if (DragManager.docsBeingDragged.length) { - const newIndex = this.findCardDropIndex(x, y); - - if (newIndex !== this._docDraggedIndex && newIndex != -1) { - this._docDraggedIndex = newIndex; - } - } + this._docDraggedIndex = DragManager.docsBeingDragged.length ? this.findCardDropIndex(x, y) : -1; }; /** @@ -304,6 +311,7 @@ export class CollectionCardView extends CollectionSubView() { if (de.complete.docDragData.removeDocument?.(draggedDoc)) { this.dataDoc[this.fieldKey] = new List<Doc>(sorted); } + this._dropped = true; } e.stopPropagation(); return true; @@ -338,7 +346,8 @@ export class CollectionCardView extends CollectionSubView() { * @param isDesc * @returns */ - sort = (docs: Doc[], sortType: cardSortings, isDesc: boolean, dragIndex: number) => { + sort = (docsIn: Doc[], sortType: cardSortings, isDesc: boolean, dragIndex: number) => { + const docs = docsIn.slice(); // need make new object list since sort() modifies the incoming list which confuses mobx caching sortType && docs.sort((docA, docB) => { const [typeA, typeB] = (() => { @@ -362,21 +371,21 @@ export class CollectionCardView extends CollectionSubView() { const out = typeA < typeB ? -1 : typeA > typeB ? 1 : 0; return isDesc ? out : -out; }); - if (dragIndex != -1) { + if (dragIndex !== -1) { const draggedDoc = DragManager.docsBeingDragged[0]; const originalIndex = docs.findIndex(doc => doc === draggedDoc); originalIndex !== -1 && docs.splice(originalIndex, 1); - docs.splice(dragIndex, 0, draggedDoc); + draggedDoc && docs.splice(dragIndex, 0, draggedDoc); } - return [...docs]; // need to spread docs into a new object list since sort() modifies the incoming list which confuses mobx caching + return docs; }; displayDoc = (doc: Doc, screenToLocalTransform: () => Transform) => ( <DocumentView {...this._props} - ref={action((r: DocumentView) => r?.ContentDiv && this._docRefs.set(doc, r))} + ref={action((r: DocumentView) => (!r?.ContentDiv ? this._docRefs.delete(doc) : this._docRefs.set(doc, r)))} Document={doc} NativeWidth={returnZero} NativeHeight={returnZero} @@ -385,11 +394,12 @@ export class CollectionCardView extends CollectionSubView() { renderDepth={this._props.renderDepth + 1} LayoutTemplate={this._props.childLayoutTemplate} LayoutTemplateString={this._props.childLayoutString} + containerViewPath={this.childContainerViewPath} ScreenToLocalTransform={screenToLocalTransform} // makes sure the box wrapper thing is in the right spot isContentActive={emptyFunction} isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive} PanelWidth={this.childPanelWidth} - PanelHeight={() => this._props.PanelHeight() * this.fitContentScale} + PanelHeight={this.childPanelHeight} dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice. dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType} showTags={true} @@ -591,7 +601,10 @@ export class CollectionCardView extends CollectionSubView() { const isSelected = view?.ComponentView?.isAnyChildContentActive?.() || view?.IsSelected ? true : false; const childScreenToLocal = () => { + // need to explicitly trigger an invalidation since we're reading everything from the Dom this._forceChildXf; + this._props.ScreenToLocalTransform(); + const dref = this._docRefs.get(doc); const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv); return new Transform(-translateX + (dref?.centeringX || 0) * scale, @@ -606,25 +619,30 @@ export class CollectionCardView extends CollectionSubView() { return (rowCenterIndex - indexInRow) * 100 - 50; }; const aspect = NumCast(doc.height) / NumCast(doc.width, 1); - const vscale = Math.min((this._props.PanelHeight() * 0.95 * this.fitContentScale) / (aspect * this.childPanelWidth()), - (this._props.PanelHeight() - 80) / (aspect * (this._props.PanelWidth() / 10))); // prettier-ignore + const vscale = Math.max(1,Math.min((this._props.PanelHeight() * 0.95 * this.fitContentScale) / (aspect * this.childPanelWidth()), + (this._props.PanelHeight() - 80) / (aspect * (this._props.PanelWidth() / 10)))); // prettier-ignore const hscale = Math.min(this.sortedDocs.length, this._maxRowCount) / 2; // bcz: hack - the grid is divided evenly into maxRowCount cells, so the max scaling would be maxRowCount -- but making things that wide is ugly, so cap it off at half the window size return ( <div key={doc[Id]} className={`card-item${isSelected ? '-active' : anySelected ? '-inactive' : ''}`} - onPointerUp={() => { - if (DocumentView.SelectedDocs().includes(doc)) return; - // this turns off documentDecorations during a transition, then turns them back on afterward. - SnappingManager.SetIsResizing(doc[Id]); - setTimeout( - action(() => { - SnappingManager.SetIsResizing(undefined); - this._forceChildXf = !this._forceChildXf; - }), - 900 - ); - }} + onPointerUp={action(() => { + // if a card doc has just moved, or a card is selected and in front, then ignore this event + if (DocumentView.SelectedDocs().includes(doc) || this._dropped) { + this._dropped = false; + } else { + // otherwise, turn off documentDecorations becase we're in a selection transition and want to avoid artifacts. + // Turn them back on when the animation has completed and the render and backend structures are in synch + SnappingManager.SetIsResizing(doc[Id]); + setTimeout( + action(() => { + SnappingManager.SetIsResizing(undefined); + this._forceChildXf++; + }), + 600 + ); + } + })} style={{ width: this.childPanelWidth(), height: 'max-content', @@ -633,7 +651,7 @@ export class CollectionCardView extends CollectionSubView() { rotate(${!isSelected ? this.rotate(amCards, calcRowIndex) : 0}deg) scale(${isSelected ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.05 : 1})`, }} // prettier-ignore - onMouseEnter={() => this.setHoveredNodeIndex(index)}> + onPointerEnter={() => !SnappingManager.IsDragging && this.setHoveredNodeIndex(index)}> {this.displayDoc(doc, childScreenToLocal)} </div> ); @@ -647,7 +665,8 @@ export class CollectionCardView extends CollectionSubView() { <div className="collectionCardView-outer" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} - onPointerMove={e => this.onPointerMove(e.clientX, e.clientY)} + onPointerLeave={action(() => (this._docDraggedIndex = -1))} + onPointerMove={e => this.onPointerMove(...this._props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY))} onDrop={this.onExternalDrop.bind(this)} style={{ background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string, diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index f54d8311d..b24fca8e2 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -90,7 +90,7 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> { moveEv => { const dragData = new DragManager.DocumentDragData([this._props.linkDoc], dropActionType.embed); dragData.dropPropertiesToRemove = ['hidden']; - DragManager.StartDocumentDrag([this._editRef.current!], dragData, moveEv.x, moveEv.y); + DragManager.StartDocumentDrag([this._editRef.current!], dragData, moveEv.x, moveEv.y, undefined, e => (this._props.linkDoc._layout_isSvg = true)); return true; }, emptyFunction, |