diff options
| author | bobzel <zzzman@gmail.com> | 2023-04-27 22:56:56 -0400 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2023-04-27 22:56:56 -0400 |
| commit | c27fcf300d72248e82d722e1b7ded9e0ca07f657 (patch) | |
| tree | e6c99b49324c60713b59c65c8963b018f59dc1ec /src/client/views/collections/collectionFreeForm | |
| parent | d3dc9938b38e89b2215d13fbc5bc92d33502e818 (diff) | |
| parent | 66e5fe4d8c4c6fae768305e31b45735f563b7500 (diff) | |
merged with master
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
3 files changed, 96 insertions, 54 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 81b0c4d8a..c1f3c5aa6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -93,6 +93,7 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc width: layout[WidthSym](), height: layout[HeightSym](), pair: { layout, data }, + transition: 'all .3s', replica: '', }); }); @@ -100,28 +101,28 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc } export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) { - const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel const docMap = new Map<string, PoolData>(); - const docSize = mustFit ? panelDim[0] * 0.33 : 75; // assume an icon sized at 75 - const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]) - docSize, NumCast(pivotDoc._starburstRadius, panelDim[1]) - docSize]; - const scaleDim = [burstRadius[0] * 2 + docSize, burstRadius[1] * 2 + docSize]; + const burstDiam = [NumCast(pivotDoc._width), NumCast(pivotDoc._height)]; + const burstScale = NumCast(pivotDoc._starburstDocScale, 1); childPairs.forEach(({ layout, data }, i) => { - const docSize = layout.layoutKey === 'layout_icon' ? (mustFit ? panelDim[0] * 0.33 : 75) : 400; // assume a icon sized at 75 + const aspect = layout[HeightSym]() / layout[WidthSym](); + const docSize = Math.min(Math.min(400, layout[WidthSym]()), Math.min(400, layout[WidthSym]()) / aspect) * burstScale; const deg = (i / childPairs.length) * Math.PI * 2; docMap.set(layout[Id], { - x: Math.cos(deg) * burstRadius[0] - docSize / 2, - y: Math.sin(deg) * burstRadius[1] - (docSize * layout[HeightSym]()) / layout[WidthSym]() / 2, - width: docSize, //layout[WidthSym](), - height: (docSize * layout[HeightSym]()) / layout[WidthSym](), + x: Math.min(burstDiam[0] / 2 - docSize, Math.max(-burstDiam[0] / 2, (Math.cos(deg) * burstDiam[0]) / 2 - docSize / 2)), + y: Math.min(burstDiam[1] / 2 - docSize * aspect, Math.max(-burstDiam[1] / 2, (Math.sin(deg) * burstDiam[1]) / 2 - (docSize / 2) * aspect)), + width: docSize, + height: docSize * aspect, zIndex: NumCast(layout.zIndex), pair: { layout, data }, replica: '', color: 'white', backgroundColor: 'white', + transition: 'all 0.3s', }); }); - const divider = { type: 'div', color: 'transparent', x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined }; - return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]); + const divider = { type: 'div', color: 'transparent', x: -burstDiam[0] / 2, y: -burstDiam[1] / 2, width: 15, height: 15, payload: undefined }; + return normalizeResults(burstDiam, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]); } export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) { @@ -424,6 +425,7 @@ function normalizeResults( color: newPosRaw.color, pair: ele[1].pair, }; + if (newPosRaw.transition) newPos.transition = newPosRaw.transition; poolData.set(newPos.pair.layout[Id] + (newPos.replica || ''), { transition: 'all 1s', ...newPos }); } }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ff0d01f29..719a39e8d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -15,7 +15,7 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro import { ImageField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; -import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { aggregateBounds, emptyFunction, intersectRect, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; @@ -141,7 +141,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @computed get fitToContentVals() { return { bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 }, - scale: !this.childDocs.length ? 1 : Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)), + scale: + !this.childDocs.length || !Number.isFinite(this.contentBounds.b - this.contentBounds.y) || !Number.isFinite(this.contentBounds.r - this.contentBounds.x) + ? 1 + : Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)), }; } @computed get fitContentsToBox() { @@ -308,6 +311,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; focus = (anchor: Doc, options: DocFocusOptions) => { + if (this._lightboxDoc) return; const xfToCollection = options?.docTransform ?? Transform.Identity(); const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; const cantTransform = this.fitContentsToBox || ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc); @@ -327,7 +331,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection getView = async (doc: Doc): Promise<Opt<DocumentView>> => { return new Promise<Opt<DocumentView>>(res => { - doc.hidden && (doc.hidden = false); + if (doc.hidden && this._lightboxDoc !== doc) doc.hidden = false; const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv)); findDoc(dv => res(dv)); }); @@ -778,7 +782,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this._batch?.end(); }; + @action onClick = (e: React.MouseEvent) => { + if (this._lightboxDoc) this._lightboxDoc = undefined; if (this.onBrowseClickHandler()) { if (this.props.DocumentView?.()) { this.onBrowseClickHandler().script.run({ documentView: this.props.DocumentView(), clientX: e.clientX, clientY: e.clientY }); @@ -1274,7 +1280,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive} - isContentActive={emptyFunction} + isContentActive={this.props.childContentsActive ?? emptyFunction} focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus} addDocTab={this.addDocTab} addDocument={this.props.addDocument} @@ -1320,13 +1326,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection case undefined: case OpenWhere.lightbox: if (this.layoutDoc._isLightbox) { - // _isLightbox docs have a script that will unset this overlay onClick - this.layoutDoc[this.props.fieldKey] = new List<Doc>(doc instanceof Doc ? [doc] : doc); + this._lightboxDoc = doc; + return true; + } else if (this.childDocList?.includes(doc)) { + if (doc.hidden) doc.hidden = false; return true; } } return this.props.addDocTab(doc, where); }); + @observable _lightboxDoc: Opt<Doc>; getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData { const childDoc = params.pair.layout; @@ -1936,7 +1945,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection 1000 ); }; - + lightboxPanelWidth = () => Math.max(0, this.props.PanelWidth() - 30); + lightboxPanelHeight = () => Math.max(0, this.props.PanelHeight() - 30); + lightboxScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-15, -15); render() { TraceMobx(); return ( @@ -1961,40 +1972,70 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection : SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement()) ? 'all' : (this.props.pointerEvents?.() as any), + textAlign: this.isAnnotationOverlay ? 'initial' : undefined, transform: `scale(${this.nativeDimScaling || 1})`, width: `${100 / (this.nativeDimScaling || 1)}%`, height: this.props.getScrollHeight?.() ?? `${100 / (this.nativeDimScaling || 1)}%`, }}> - {this._firstRender ? this.placeholder : this.marqueeView} - {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />} - - {/* // uncomment to show snap lines */} - <div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}> - <svg style={{ width: '100%', height: '100%' }}> - {this._hLines?.map(l => ( - <line x1="0" y1={l} x2="1000" y2={l} stroke="black" /> - ))} - {this._vLines?.map(l => ( - <line y1="0" x1={l} y2="1000" x2={l} stroke="black" /> - ))} - </svg> - </div> + {this._lightboxDoc ? ( + <div style={{ padding: 15, width: '100%', height: '100%' }}> + <DocumentView + {...this.props} + Document={this._lightboxDoc} + DataDoc={undefined} + PanelWidth={this.lightboxPanelWidth} + PanelHeight={this.lightboxPanelHeight} + NativeWidth={returnZero} + NativeHeight={returnZero} + onClick={this.onChildClickHandler} + onKey={this.onKeyDown} + onDoubleClick={this.onChildDoubleClickHandler} + onBrowseClick={this.onBrowseClickHandler} + docFilters={this.childDocFilters} + docRangeFilters={this.childDocRangeFilters} + searchFilterDocs={this.searchFilterDocs} + isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive} + isContentActive={this.props.childContentsActive ?? emptyFunction} + addDocTab={this.addDocTab} + ScreenToLocalTransform={this.lightboxScreenToLocal} + fitContentsToBox={undefined} + focus={this.focus} + /> + </div> + ) : ( + <> + {this._firstRender ? this.placeholder : this.marqueeView} + {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />} + + {/* // uncomment to show snap lines */} + <div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}> + <svg style={{ width: '100%', height: '100%' }}> + {this._hLines?.map(l => ( + <line x1="0" y1={l} x2="1000" y2={l} stroke="black" /> + ))} + {this._vLines?.map(l => ( + <line y1="0" x1={l} y2="1000" x2={l} stroke="black" /> + ))} + </svg> + </div> - {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ? ( - <div - className="collectionFreeForm-groupDropper" - ref={this.createGroupEventsTarget} - style={{ - width: this.ChildDrag ? '10000' : '100%', - height: this.ChildDrag ? '10000' : '100%', - left: this.ChildDrag ? '-5000' : 0, - top: this.ChildDrag ? '-5000' : 0, - position: 'absolute', - background: '#0009930', - pointerEvents: 'all', - }} - /> - ) : null} + {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ? ( + <div + className="collectionFreeForm-groupDropper" + ref={this.createGroupEventsTarget} + style={{ + width: this.ChildDrag ? '10000' : '100%', + height: this.ChildDrag ? '10000' : '100%', + left: this.ChildDrag ? '-5000' : 0, + top: this.ChildDrag ? '-5000' : 0, + position: 'absolute', + background: '#0009930', + pointerEvents: 'all', + }} + /> + ) : null} + </> + )} </div> ); } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index d443df0f3..e5f47823c 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -371,10 +371,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque @undoBatch @action - delete = () => { + delete = (e?: React.PointerEvent<Element> | KeyboardEvent | undefined, hide?: boolean) => { const selected = this.marqueeSelect(false); SelectionManager.DeselectAll(); - selected.forEach(doc => this.props.removeDocument?.(doc)); + selected.forEach(doc => (hide ? (doc.hidden = true) : this.props.removeDocument?.(doc))); this.cleanupInteractions(false); MarqueeOptionsMenu.Instance.fadeOut(true); @@ -425,9 +425,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque */ @undoBatch @action - pinWithView = async () => { - const doc = this.props.Document; - TabDocView.PinDoc(doc, { pinViewport: this.Bounds }); + pinWithView = () => { + TabDocView.PinDoc(this.props.Document, { pinViewport: this.Bounds }); MarqueeOptionsMenu.Instance.fadeOut(true); this.hideMarquee(); }; @@ -550,11 +549,11 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque if (this._commandExecuted || (e as any).propagationIsStopped) { return; } - if (e.key === 'Backspace' || e.key === 'Delete' || e.key === 'd') { + if (e.key === 'Backspace' || e.key === 'Delete' || e.key === 'd' || e.key === 'h') { this._commandExecuted = true; e.stopPropagation(); (e as any).propagationIsStopped = true; - this.delete(); + this.delete(e, e.key === 'h'); e.stopPropagation(); } if ('cbtsSpg'.indexOf(e.key) !== -1) { |
