diff options
Diffstat (limited to 'src/client/views/collections')
15 files changed, 206 insertions, 258 deletions
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 57ff1b292..a266c9207 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { Doc } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { OmitKeys, returnFalse, Utils } from '../../../Utils'; +import { returnFalse, returnZero, Utils } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { DocumentView } from '../nodes/DocumentView'; import { StyleProp } from '../StyleProvider'; @@ -43,7 +43,10 @@ export class CollectionCarousel3DView extends CollectionSubView() { const displayDoc = (childPair: { layout: Doc; data: Doc }) => { return ( <DocumentView - {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'childLayoutTemplate', 'childLayoutString']).omit} + {...this.props} + NativeWidth={returnZero} + NativeHeight={returnZero} + suppressSetHeight={true} onDoubleClick={this.onChildDoubleClick} renderDepth={this.props.renderDepth + 1} LayoutTemplate={this.props.childLayoutTemplate} diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 32f6207ed..c0220f804 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 { OmitKeys, returnFalse } from '../../../Utils'; +import { emptyFunction, returnFalse, returnZero } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; @@ -47,7 +47,7 @@ export class CollectionCarouselView extends CollectionSubView() { @computed get content() { const index = NumCast(this.layoutDoc._itemIndex); const curDoc = this.childLayoutPairs?.[index]; - const captionProps = { ...OmitKeys(this.props, ['setHeight']).omit, fieldKey: 'caption' }; + const captionProps = { ...this.props, fieldKey: 'caption', setHeight: undefined }; const marginX = NumCast(this.layoutDoc['caption-xMargin']); const marginY = NumCast(this.layoutDoc['caption-yMargin']); const showCaptions = StrCast(this.layoutDoc._showCaption); @@ -55,7 +55,9 @@ export class CollectionCarouselView extends CollectionSubView() { <> <div className="collectionCarouselView-image" key="image"> <DocumentView - {...OmitKeys(this.props, ['setHeight', 'NativeWidth', 'NativeHeight', 'childLayoutTemplate', 'childLayoutString']).omit} + {...this.props} + NativeWidth={returnZero} + NativeHeight={returnZero} onDoubleClick={this.onContentDoubleClick} onClick={this.onContentClick} hideCaptions={showCaptions ? true : false} diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 121260680..cb5be990d 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -441,7 +441,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) { const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' }); this.props.addDocument?.(source); - de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed + de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: '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 false; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 4941bc722..642d29d2a 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -9,7 +9,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { Cast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; -import { emptyFunction, formatTime, OmitKeys, returnFalse, returnNone, returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils'; +import { emptyFunction, formatTime, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; @@ -23,7 +23,6 @@ import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { AudioWaveform } from '../AudioWaveform'; import { CollectionSubView } from '../collections/CollectionSubView'; -import { Colors } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { LabelBox } from '../nodes/LabelBox'; @@ -512,37 +511,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack currentTimecode = () => this.currentTime; - @computed get renderDictation() { - const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null); - return !dictation ? null : ( - <div - style={{ - position: 'absolute', - height: '100%', - top: this.timelineContentHeight, - background: Colors.LIGHT_BLUE, - }}> - <DocumentView - {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit} - Document={dictation} - PanelHeight={this.dictationHeight} - isAnnotationOverlay={true} - isDocumentActive={returnFalse} - select={emptyFunction} - NativeDimScaling={returnOne} - xMargin={25} - yMargin={10} - ScreenToLocalTransform={this.dictationScreenToLocalTransform} - whenChildContentsActiveChanged={emptyFunction} - removeDocument={returnFalse} - moveDocument={returnFalse} - addDocument={returnFalse} - CollectionView={undefined} - renderDepth={this.props.renderDepth + 1}></DocumentView> - </div> - ); - } - // renders selection region on timeline @computed get selectionContainer() { const markerEnd = CollectionStackedTimeline.SelectingRegion === this ? this.currentTime : this._markerEnd; @@ -638,7 +606,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack PanelWidth={this.timelineContentWidth} /> )} - {/* {this.renderDictation} */} <div className="collectionStackedTimeline-hover" @@ -817,16 +784,22 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> // renders anchor LabelBox renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) { const anchor = observable({ view: undefined as any }); - const focusFunc = (doc: Doc, options: DocFocusOptions) => this.props.playLink(mark); + const focusFunc = (doc: Doc, options: DocFocusOptions): number | undefined => { + this.props.playLink(mark); + return undefined; + }; return { anchor, view: ( <DocumentView key="view" - {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit} + {...this.props} + NativeWidth={returnZero} + NativeHeight={returnZero} ref={action((r: DocumentView | null) => (anchor.view = r))} Document={mark} DataDoc={undefined} + docViewPath={returnEmptyDoclist} pointerEvents={this.noEvents ? returnNone : undefined} styleProvider={this.props.styleProvider} renderDepth={this.props.renderDepth + 1} @@ -837,7 +810,16 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> PanelHeight={height} fitWidth={returnTrue} ScreenToLocalTransform={screenXf} + addDocTab={returnFalse} + pinToPres={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} focus={focusFunc} + isContentActive={returnFalse} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + searchFilterDocs={returnEmptyDoclist} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} rootSelected={returnFalse} onClick={script} onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 64ec419ec..1e02fc9d4 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -3,14 +3,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { CursorProperty } from 'csstype'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; -import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { CollectionViewType } from '../../documents/DocumentTypes'; import { DragManager, dropActionType } from '../../util/DragManager'; @@ -460,7 +460,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) { const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' }); this.props.addDocument?.(source); - de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed + de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: '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 false; @@ -623,11 +623,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection onContextMenu = (e: React.MouseEvent): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout if (!e.isPropagationStopped()) { - const subItems: ContextMenuProps[] = []; - subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' }); - subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' }); - subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' }); - ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' }); + const cm = ContextMenu.Instance; + const options = cm.findByDescription('Options...'); + const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; + optionItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' }); + optionItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' }); + optionItems.push({ description: 'Clear All', event: () => (this.dataDoc[this.fieldKey ?? 'data'] = new List([])), icon: 'times' }); + !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' }); } }; @@ -735,14 +737,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection <EditableView {...editableViewProps} /> </div> )} - {/* {this.chromeHidden || !this.props.isSelected() ? (null) : - <Switch - onChange={this.onToggle} - onClick={this.onToggle} - defaultChecked={true} - checkedChildren="edit" - unCheckedChildren="view" - />} */} </div> </div> </> diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5100d8d67..132ed6fb6 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -335,7 +335,7 @@ export function CollectionSubView<X>(moreProps?: X) { const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any; if (focusNode) { const anchor = srcWeb?.ComponentView?.getAnchor?.(true); - anchor && DocUtils.MakeLink({ doc: htmlDoc }, { doc: anchor }); + anchor && DocUtils.MakeLink(htmlDoc, anchor, {}); } } } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 553967b95..4a11e8f0b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -7,7 +7,7 @@ import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../Utils'; +import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from '../../util/DragManager'; @@ -384,12 +384,12 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false; @observable _headerHeight = 0; - contentFunc = () => { + @computed get content() { const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor); const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined); const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar; - return [ - <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> + return ( + <div style={{ display: 'flex', flexDirection: 'column', height: '100%', pointerEvents: 'all' }}> {!this.buttonMenu && !this.noviceExplainer ? null : ( <div className="documentButtonMenu" ref={action((r: HTMLDivElement | null) => r && (this._headerHeight = Number(getComputedStyle(r).height.replace(/px/, ''))))}> {this.buttonMenu} @@ -428,9 +428,9 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree </div> </div> </div> - </div>, - ]; - }; + </div> + ); + } render() { TraceMobx(); @@ -439,7 +439,11 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}> {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? ( <CollectionFreeFormView - {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit} + {...this.props} + setContentView={emptyFunction} + NativeWidth={returnZero} + NativeHeight={returnZero} + pointerEvents={SnappingManager.GetIsDragging() ? returnAll : returnNone} isAnnotationOverlay={true} isAnnotationOverlayScrollable={true} childDocumentsActive={this.props.isDocumentActive} @@ -451,10 +455,10 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree moveDocument={this.moveAnnotationDocument} bringToFront={emptyFunction} renderDepth={this.props.renderDepth + 1}> - {this.contentFunc} + {this.content} </CollectionFreeFormView> ) : ( - this.contentFunc() + this.content )} </div> ); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 51624689e..bc25ad43a 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -45,7 +45,7 @@ interface CollectionViewProps_ extends FieldViewProps { // property overrides for child documents childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox) - childDocumentsActive?: () => boolean; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode) + childDocumentsActive?: () => boolean | undefined; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode) childFitWidth?: (child: Doc) => boolean; childShowTitle?: () => string; childOpacity?: () => number; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 99b7549c0..75e76019e 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -359,7 +359,7 @@ export class TreeView extends React.Component<TreeViewProps> { if (de.complete.linkDragData) { const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor(); const destDoc = this.doc; - DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, 'tree link', ''); + DocUtils.MakeLink(sourceDoc, destDoc, { linkRelationship: 'tree link' }); e.stopPropagation(); } const docDragData = de.complete.docDragData; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index aed3683d4..9d4a788db 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { Bezier } from 'bezier-js'; import { Colors } from 'browndash-components'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { DateField } from '../../../../fields/DateField'; @@ -53,10 +53,14 @@ import { MarqueeView } from './MarqueeView'; import React = require('react'); export type collectionFreeformViewProps = { + noPointerWheel?: () => boolean; // turn off pointerwheel interactions (see PDFViewer) + NativeWidth?: () => number; + NativeHeight?: () => number; + originTopLeft?: boolean; annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) viewDefDivClick?: ScriptField; childPointerEvents?: string; - scaleField?: string; + viewField?: string; noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale) engineProps?: any; getScrollHeight?: () => number | undefined; @@ -97,8 +101,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; } - private get scaleFieldKey() { - return this.props.scaleField || '_viewScale'; + public get scaleFieldKey() { + return this.props.viewField ? this.props.viewField + '-viewScale' : '_viewScale'; + } + private get panXFieldKey() { + return this.props.viewField ? this.props.viewField + '-panX' : '_panX'; + } + private get panYFieldKey() { + return this.props.viewField ? this.props.viewField + '-panY' : '_panY'; } private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; @@ -149,21 +159,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ); } @computed get nativeWidth() { - return this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); + return this.props.NativeWidth?.() || (this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null))); } @computed get nativeHeight() { - return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); + return this.props.NativeHeight?.() || (this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null))); } @computed get cachedCenteringShiftX(): number { const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling; - return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections + return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections } @computed get cachedCenteringShiftY(): number { const dv = this.props.DocumentView?.(); const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling; // if freeform has a native aspect, then the panel height needs to be adjusted to match it const aspect = dv?.nativeWidth && dv?.nativeHeight && !dv.layoutDoc.fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth(); - return this.props.isAnnotationOverlay ? 0 : (aspect * this.props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections + return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : (aspect * this.props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections } @computed get cachedGetLocalTransform(): Transform { return Transform.Identity() @@ -236,13 +246,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection reverseNativeScaling = () => (this.fitContentsToBox ? true : false); // panx, pany, zoomscale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document. // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image - panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1)); - panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1)); + panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1)); + panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1)); zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1)); contentTransform = () => - !this.cachedCenteringShiftX && !this.cachedCenteringShiftY && this.zoomScaling() === 1 - ? '' - : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`; + this.props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`; getTransform = () => this.cachedGetTransform.copy(); getLocalTransform = () => this.cachedGetLocalTransform.copy(); getContainerTransform = () => this.cachedGetContainerTransform.copy(); @@ -293,7 +301,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } groupFocus = (anchor: Doc, options: DocFocusOptions) => { - options.docTransform = new Transform(-NumCast(this.rootDoc.panX) + NumCast(anchor.x), -NumCast(this.rootDoc.panY) + NumCast(anchor.y), 1); + options.docTransform = new Transform(-NumCast(this.rootDoc[this.panXFieldKey]) + NumCast(anchor.x), -NumCast(this.rootDoc[this.panYFieldKey]) + NumCast(anchor.y), 1); const res = this.props.focus(this.rootDoc, options); options.docTransform = undefined; return res; @@ -301,7 +309,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection focus = (anchor: Doc, options: DocFocusOptions) => { const xfToCollection = options?.docTransform ?? Transform.Identity(); - const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; + 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); const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? options?.zoomScale || 0.75 : undefined); @@ -389,7 +397,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' }); this.props.addDocument?.(source); - de.complete.linkDocument = DocUtils.MakeLink({ doc: linkDragData.linkSourceGetAnchor() }, { doc: source }, 'annotated by:annotation of', ''); // TODODO this is where in text links get passed + de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { linkRelationship: '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; @@ -656,8 +664,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ActiveIsInkMask(), { title: 'ink stroke', - x: B.x - ActiveInkWidth() / 2, - y: B.y - ActiveInkWidth() / 2, + x: B.x - (ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale) / 2, + y: B.y - (ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale) / 2, _width: B.width + ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale, _height: B.height + ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale, } @@ -772,7 +780,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection PresBox.Instance?.pauseAutoPres(); const dx = e.deltaX; const dy = e.deltaY; - this.setPan(NumCast(this.Document._panX) - dx, NumCast(this.Document._panY) - dy, 0, true); + this.setPan(NumCast(this.Document[this.panXFieldKey]) - dx, NumCast(this.Document[this.panYFieldKey]) - dy, 0, true); }; @action @@ -780,7 +788,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection PresBox.Instance?.pauseAutoPres(); this.props.DocumentView?.().clearViewTransition(); const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); - this.setPan(NumCast(this.Document._panX) - dx, NumCast(this.Document._panY) - dy, 0, true); + this.setPan(NumCast(this.Document[this.panXFieldKey]) - dx, NumCast(this.Document[this.panYFieldKey]) - dy, 0, true); this._lastX = e.clientX; this._lastY = e.clientY; }; @@ -979,13 +987,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection if (localTransform.Scale >= 0.05 || localTransform.Scale > this.zoomScaling()) { const safeScale = Math.min(Math.max(0.05, localTransform.Scale), 20); this.props.Document[this.scaleFieldKey] = Math.abs(safeScale); - this.setPan(-localTransform.TranslateX / safeScale, NumCast(this.props.Document.scrollTop) * safeScale || -localTransform.TranslateY / safeScale); + this.setPan(-localTransform.TranslateX / safeScale, (this.props.originTopLeft ? undefined : NumCast(this.props.Document.scrollTop) * safeScale) || -localTransform.TranslateY / safeScale); } }; @action onPointerWheel = (e: React.WheelEvent): void => { - if (this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom + if (this.props.noPointerWheel?.() || this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom PresBox.Instance?.pauseAutoPres(); if (this.layoutDoc._Transform || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; e.stopPropagation(); @@ -995,7 +1003,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection // if ctrl is selected then zoom if (e.ctrlKey) { if (this.props.isContentActive(true)) { - !this.props.isAnnotationOverlayScrollable && this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc? + this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc? } } // otherwise pan else if (this.props.isContentActive(true)) { @@ -1039,16 +1047,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) }, }), { - xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }, - yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }, + xrange: { min: this.props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE }, + yrange: { min: this.props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE }, } ); - const panelDim = [this.props.PanelWidth() / this.zoomScaling(), this.props.PanelHeight() / this.zoomScaling()]; - if (ranges.xrange.min >= panX + panelDim[0] / 2) panX = ranges.xrange.max + panelDim[0] / 2; // snaps pan position of range of content goes out of bounds - else if (ranges.xrange.max <= panX - panelDim[0] / 2) panX = ranges.xrange.min - panelDim[0] / 2; - if (ranges.yrange.min >= panY + panelDim[1] / 2) panY = ranges.yrange.max + panelDim[1] / 2; - else if (ranges.yrange.max <= panY - panelDim[1] / 2) panY = ranges.yrange.min - panelDim[1] / 2; + const panelWidMax = (this.props.PanelWidth() / this.zoomScaling()) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1); + const panelWidMin = (this.props.PanelWidth() / this.zoomScaling()) * (this.props.originTopLeft ? 0 : 1); + const panelHgtMax = (this.props.PanelHeight() / this.zoomScaling()) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1); + const panelHgtMin = (this.props.PanelHeight() / this.zoomScaling()) * (this.props.originTopLeft ? 0 : 1); + if (ranges.xrange.min >= panX + panelWidMax / 2) panX = ranges.xrange.max + (this.props.originTopLeft ? 0 : panelWidMax / 2); + else if (ranges.xrange.max <= panX - panelWidMin / 2) panX = ranges.xrange.min - (this.props.originTopLeft ? panelWidMax / 2 : panelWidMin / 2); + if (ranges.yrange.min >= panY + panelHgtMax / 2) panY = ranges.yrange.max + (this.props.originTopLeft ? 0 : panelHgtMax / 2); + else if (ranges.yrange.max <= panY - panelHgtMin / 2) panY = ranges.yrange.min - (this.props.originTopLeft ? panelHgtMax / 2 : panelHgtMin / 2); } } if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(Doc.MyOverlayDocs?.data).includes(this.Document)) { @@ -1078,8 +1089,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }, 10); newPanY = minPanY; } - !this.Document._verticalScroll && (this.Document._panX = this.isAnnotationOverlay ? newPanX : panX); - !this.Document._horizontalScroll && (this.Document._panY = this.isAnnotationOverlay ? newPanY : panY); + !this.Document._verticalScroll && (this.Document[this.panXFieldKey] = this.isAnnotationOverlay ? newPanX : panX); + !this.Document._horizontalScroll && (this.Document[this.panYFieldKey] = this.isAnnotationOverlay ? newPanY : panY); } } @@ -1087,8 +1098,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection nudge = (x: number, y: number, nudgeTime: number = 500) => { if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || this.props.ContainingCollectionDoc._panX !== undefined) { this.setPan( - NumCast(this.layoutDoc._panX) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale - NumCast(this.layoutDoc._panY) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(), + NumCast(this.layoutDoc[this.panXFieldKey]) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale + NumCast(this.layoutDoc[this.panYFieldKey]) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(), nudgeTime, true ); @@ -1106,12 +1117,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } else { const docs = this.childLayoutPairs.map(pair => pair.layout).slice(); docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); - let zlast = docs.length ? Math.max(docs.length, NumCast(docs[docs.length - 1].zIndex)) : 1; - if (zlast - docs.length > 100) { - for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1; - zlast = docs.length + 1; + let zlast = docs.length ? Math.max(docs.length, NumCast(docs.lastElement().zIndex)) : 1; + if (docs.lastElement() !== doc) { + if (zlast - docs.length > 100) { + for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1; + zlast = docs.length + 1; + } + doc.zIndex = zlast + 1; } - doc.zIndex = zlast + 1; } }; @@ -1134,8 +1147,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const newScreenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]); const scrDelta = { x: screenXY[0] - newScreenXY[0], y: screenXY[1] - newScreenXY[1] }; const newpan = this.getTransform().transformDirection(scrDelta.x, scrDelta.y); - this.layoutDoc._panX = NumCast(this.layoutDoc._panX) - newpan[0]; - this.layoutDoc._panY = NumCast(this.layoutDoc._panY) - newpan[1]; + this.layoutDoc[this.panXFieldKey] = NumCast(this.layoutDoc[this.panXFieldKey]) - newpan[0]; + this.layoutDoc[this.panYFieldKey] = NumCast(this.layoutDoc[this.panYFieldKey]) - newpan[1]; } calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => { @@ -1158,8 +1171,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const panelHeight = this.props.isAnnotationOverlay ? this.nativeHeight : this.props.PanelHeight(); const pw = panelWidth / NumCast(this.layoutDoc._viewScale, 1); const ph = panelHeight / NumCast(this.layoutDoc._viewScale, 1); - const cx = NumCast(this.layoutDoc._panX) + (this.props.isAnnotationOverlay ? pw / 2 : 0); - const cy = NumCast(this.layoutDoc._panY) + (this.props.isAnnotationOverlay ? ph / 2 : 0); + const cx = NumCast(this.layoutDoc[this.panXFieldKey]) + (this.props.isAnnotationOverlay ? pw / 2 : 0); + const cy = NumCast(this.layoutDoc[this.panYFieldKey]) + (this.props.isAnnotationOverlay ? ph / 2 : 0); const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 }; const maxYShift = Math.max(0, screen.bot - screen.top - (bounds.bot - bounds.top)); const phborder = bounds.top < screen.top || bounds.bot > screen.bot ? Math.min(ph / 10, maxYShift / 2) : 0; @@ -1171,8 +1184,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; } return { - panX: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc._panX) : cx) + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right), - panY: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc._panY) : cy) + Math.min(0, bounds.top - phborder - screen.top) + Math.max(0, bounds.bot + phborder - screen.bot), + panX: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc[this.panXFieldKey]) : cx) + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right), + panY: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc[this.panYFieldKey]) : cy) + Math.min(0, bounds.top - phborder - screen.top) + Math.max(0, bounds.bot + phborder - screen.bot), }; }; @@ -1395,6 +1408,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } doFreeformLayout(poolData: Map<string, PoolData>) { + if (this.layoutDoc._autoArrange) { + const sorted = this.childLayoutPairs.slice().sort((a, b) => (NumCast(a.layout.y) < NumCast(b.layout.y) ? -1 : 1)); + if (sorted.length > 1) { + const deltay = sorted.length > 1 ? NumCast(sorted[1].layout.y) - (NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height)) : 0; + const deltax = sorted.length > 1 ? NumCast(sorted[1].layout.x) - NumCast(sorted[0].layout.x) : 0; + + let lastx = NumCast(sorted[0].layout.x); + let lasty = NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height); + setTimeout( + action(() => + sorted.slice(1).forEach((pair, i) => { + lastx = pair.layout.x = lastx + deltax; + lasty = (pair.layout.y = lasty + deltay) + NumCast(pair.layout._height); + }) + ) + ); + } + } this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions({ pair, index: i, collection: this.Document }))); return [] as ViewDefResult[]; } @@ -1459,7 +1490,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection if (this.props.isAnnotationOverlay && this.props.Document[this.scaleFieldKey]) { // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar - if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, this.zoomScaling()); + if (this.props.viewField) this.props.Document[this.scaleFieldKey] = Math.min(1, this.zoomScaling()); else this.props.Document[this.scaleFieldKey] = Math.max(1, this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey])); } @@ -1501,7 +1532,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection cbounds => { if (cbounds) { const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2]; - const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)]; + const p = [NumCast(this.layoutDoc[this.panXFieldKey]), NumCast(this.layoutDoc[this.panYFieldKey])]; const pbounds = { x: cbounds.x - p[0] + c[0], y: cbounds.y - p[1] + c[1], @@ -1511,8 +1542,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection if (Number.isFinite(pbounds.r - pbounds.x) && Number.isFinite(pbounds.b - pbounds.y)) { this.layoutDoc._width = pbounds.r - pbounds.x; this.layoutDoc._height = pbounds.b - pbounds.y; - this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2; - this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2; + this.layoutDoc[this.panXFieldKey] = (cbounds.r + cbounds.x) / 2; + this.layoutDoc[this.panYFieldKey] = (cbounds.b + cbounds.y) / 2; this.layoutDoc.x = pbounds.x; this.layoutDoc.y = pbounds.y; } @@ -1634,8 +1665,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const deltaX = dragX - bounds.left < 25 ? -(25 + (bounds.left - dragX)) : bounds.right - dragX < 25 ? 25 - (bounds.right - dragX) : 0; const deltaY = dragY - bounds.top < 25 ? -(25 + (bounds.top - dragY)) : bounds.bottom - dragY < 25 ? 25 - (bounds.bottom - dragY) : 0; if (deltaX !== 0 || deltaY !== 0) { - this.Document._panY = NumCast(this.Document._panY) + deltaY / 2; - this.Document._panX = NumCast(this.Document._panX) + deltaX / 2; + this.Document[this.panYFieldKey] = NumCast(this.Document[this.panYFieldKey]) + deltaY / 2; + this.Document[this.panXFieldKey] = NumCast(this.Document[this.panXFieldKey]) + deltaX / 2; } } e.stopPropagation(); @@ -1659,8 +1690,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const height = Math.max(...docs.map(doc => NumCast(doc._height))) + 20; const dim = Math.ceil(Math.sqrt(docs.length)); docs.forEach((doc, i) => { - doc.x = NumCast(this.Document._panX) + (i % dim) * width - (width * dim) / 2; - doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - (height * dim) / 2; + doc.x = NumCast(this.Document[this.panXFieldKey]) + (i % dim) * width - (width * dim) / 2; + doc.y = NumCast(this.Document[this.panYFieldKey]) + Math.floor(i / dim) * height - (height * dim) / 2; }); }; @@ -1675,11 +1706,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection appearanceItems.push({ description: 'Reset View', event: () => { - this.props.Document._panX = this.props.Document._panY = 0; + this.props.Document[this.panXFieldKey] = this.props.Document[this.panYFieldKey] = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: 'compress-arrows-alt', }); + appearanceItems.push({ + description: 'Toggle auto arrange', + event: () => (this.layoutDoc._autoArrange = !this.layoutDoc._autoArrange), + icon: 'compress-arrows-alt', + }); + if (this.props.setContentView === emptyFunction) { + !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' }); + return; + } !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: 'Reset default note style', event: () => (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' }); appearanceItems.push({ description: `${this.fitContentsToBox ? 'Make Zoomable' : 'Scale to Window'}`, @@ -1687,7 +1727,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection icon: !this.fitContentsToBox ? 'expand-arrows-alt' : 'compress-arrows-alt', }); appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' }); - //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" }); + !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' }); appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' }); this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: () => this.transcribeStrokes(false), icon: 'font' }); @@ -1717,27 +1757,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection !options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); const mores = ContextMenu.Instance.findByDescription('More...'); const moreItems = mores && 'subitems' in mores ? mores.subitems : []; - moreItems.push({ description: 'Import exported collection', icon: 'upload', event: ({ x, y }) => this.importDocument(e.clientX, e.clientY) }); !mores && ContextMenu.Instance.addItem({ description: 'More...', subitems: moreItems, icon: 'eye' }); }; - importDocument = (x: number, y: number) => { - const input = document.createElement('input'); - input.type = 'file'; - input.accept = '.zip'; - input.onchange = _e => { - input.files && - Doc.importDocument(input.files[0]).then(doc => { - if (doc instanceof Doc) { - const [xx, yy] = this.getTransform().transformPoint(x, y); - (doc.x = xx), (doc.y = yy); - this.props.addDocument?.(doc); - } - }); - }; - input.click(); - }; - @undoBatch @action transcribeStrokes = (math: boolean) => { @@ -1795,11 +1817,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1); }); - children = () => { + get children() { this.incrementalRender(); - const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : []; + const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : this.props.children ? [this.props.children] : []; return [...children, ...this.views, <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />]; - }; + } @computed get placeholder() { return ( @@ -1843,6 +1865,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection PanelHeight={this.props.PanelHeight} panX={this.panX} panY={this.panY} + nativeDimScaling={this.nativeDim} zoomScaling={this.zoomScaling} layoutDoc={this.layoutDoc} isAnnotationOverlay={this.isAnnotationOverlay} @@ -1877,6 +1900,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const wscale = nw ? this.props.PanelWidth() / nw : 1; return wscale < hscale || this.layoutDoc.fitWidth ? wscale : hscale; } + nativeDim = () => this.nativeDimScaling; private groupDropDisposer?: DragManager.DragDropDisposer; protected createGroupEventsTarget = (ele: HTMLDivElement) => { @@ -1912,7 +1936,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ref={r => { this.createDashEventsTarget(r); // prevent wheel events from passivly propagating up through containers - r?.addEventListener('wheel', (e: WheelEvent) => this.props.isSelected() && e.preventDefault(), { passive: false }); + !this.props.isAnnotationOverlay && r?.addEventListener('wheel', (e: WheelEvent) => this.props.isSelected() && e.preventDefault(), { passive: false }); }} onWheel={this.onPointerWheel} onClick={this.onClick} @@ -1985,7 +2009,8 @@ interface CollectionFreeFormViewPannableContentsProps { transform: () => string; zoomScaling: () => number; viewDefDivClick?: ScriptField; - children: () => JSX.Element[]; + children?: React.ReactNode | undefined; + //children: () => JSX.Element[]; transition?: string; presPaths: () => JSX.Element | null; presPinView?: boolean; @@ -2079,7 +2104,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF width: this.props.isAnnotationOverlay ? undefined : 0, // if not an overlay, then this will be the size of the collection, but panning and zooming will move it outside the visible border of the collection and make it selectable. This problem shows up after zooming/panning on a background collection -- you can drag the collection by clicking on apparently empty space outside the collection //willChange: "transform" }}> - {this.props.children()} + {this.props.children} {!this.props.brushView.width ? null : ( <div className="collectionFreeFormView-brushView" @@ -2107,6 +2132,7 @@ interface CollectionFreeFormViewBackgroundGridProps { PanelWidth: () => number; PanelHeight: () => number; isAnnotationOverlay?: boolean; + nativeDimScaling: () => number; zoomScaling: () => number; layoutDoc: Doc; cachedCenteringShiftX: number; @@ -2124,10 +2150,10 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor const shiftX = (this.props.isAnnotationOverlay ? 0 : (-this.props.panX() % gridSpace) - gridSpace) * this.props.zoomScaling(); const shiftY = (this.props.isAnnotationOverlay ? 0 : (-this.props.panY() % gridSpace) - gridSpace) * this.props.zoomScaling(); const renderGridSpace = gridSpace * this.props.zoomScaling(); - const w = this.props.PanelWidth() + 2 * renderGridSpace; - const h = this.props.PanelHeight() + 2 * renderGridSpace; + const w = this.props.PanelWidth() / this.props.nativeDimScaling() + 2 * renderGridSpace; + const h = this.props.PanelHeight() / this.props.nativeDimScaling() + 2 * renderGridSpace; const strokeStyle = Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'rgba(255,255,255,0.5)' : 'rgba(0, 0,0,0.5)'; - return ( + return !this.props.nativeDimScaling() ? null : ( <canvas className="collectionFreeFormView-grid" width={w} @@ -2174,7 +2200,7 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY 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.props.scaleField || '_viewScale'] !== 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 + 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 { @@ -2193,11 +2219,17 @@ ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) { if (readOnly) return selView[0].ComponentView?.getKeyFrameEditing?.() ? Colors.MEDIUM_BLUE : 'transparent'; runInAction(() => selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.())); }); -ScriptingGlobals.add(function pinWithView(readOnly: boolean, pinDocContent: boolean) { - !readOnly && - SelectionManager.Views().forEach(view => - TabDocView.PinDoc(view.rootDoc, { currentFrame: Cast(view.rootDoc.currentFrame, 'number', null), pinDocContent, pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()) }) - ); +ScriptingGlobals.add(function pinWithView(pinContent: boolean) { + SelectionManager.Views().forEach(view => + view.props.pinToPres(view.rootDoc, { + currentFrame: Cast(view.rootDoc.currentFrame, 'number', null), + pinData: { + poslayoutview: pinContent, + dataview: pinContent, + }, + pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()), + }) + ); }); ScriptingGlobals.add(function bringToFront() { SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc)); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index f16371592..d443df0f3 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -237,6 +237,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque @action onPointerDown = (e: React.PointerEvent): void => { + // if (this.props.pointerEvents?.() === 'none') return; this._downX = this._lastX = e.clientX; this._downY = this._lastY = e.clientY; if (!(e.nativeEvent as any).marqueeHit) { @@ -345,6 +346,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque @action onClick = (e: React.MouseEvent): void => { + if (this.props.pointerEvents?.() === 'none') return; if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { if (Doc.ActiveTool === InkTool.None) { if (!(e.nativeEvent as any).marqueeHit) { @@ -394,6 +396,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque newCollection._height = this.Bounds.height; newCollection._isGroup = makeGroup; newCollection.forceActive = makeGroup; + newCollection.enableDragWhenActive = makeGroup; newCollection.x = this.Bounds.left; newCollection.y = this.Bounds.top; newCollection.fitWidth = true; @@ -525,7 +528,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque }); const summary = Docs.Create.TextDocument('', { backgroundColor: '#e2ad32', x: this.Bounds.left, y: this.Bounds.top, followLinkToggle: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: 'overview' }); const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' }); - DocUtils.MakeLink({ doc: summary }, { doc: portal }, 'summary of:summarized by', ''); + DocUtils.MakeLink(summary, portal, { linkRelationship: 'summary of:summarized by' }); portal.hidden = true; this.props.addDocument?.(portal); diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 9468c5f06..e8ae88ae5 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { Doc, Opt } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../../Utils'; +import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; @@ -186,7 +186,10 @@ export class CollectionGridView extends CollectionSubView() { getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return ( <DocumentView - {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit} + {...this.props} + NativeWidth={returnZero} + NativeHeight={returnZero} + setContentView={emptyFunction} Document={layout} DataDoc={layout.resolvedDataDoc as Doc} isContentActive={this.isChildContentActive} @@ -196,7 +199,7 @@ export class CollectionGridView extends CollectionSubView() { whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} onClick={this.onChildClickHandler} renderDepth={this.props.renderDepth + 1} - dontCenter={this.props.Document.centerY ? '' : 'y'} + dontCenter={this.props.Document.centerY ? undefined : 'y'} /> ); } diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index a062c65fc..c7d9b6619 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -44,7 +44,7 @@ export class CollectionLinearView extends CollectionSubView() { componentDidMount() { this._widthDisposer = reaction( - () => 5 + this.dimension() + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[WidthSym]() || this.dimension()) + tot + 4, 0) : 0), + () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[WidthSym]() || this.dimension()) + tot + 4, 0) : 0), width => this.childDocs.length && (this.layoutDoc._width = width), { fireImmediately: true } ); @@ -78,7 +78,7 @@ export class CollectionLinearView extends CollectionSubView() { } }; - dimension = () => NumCast(this.rootDoc._height); // 2 * the padding + dimension = () => NumCast(this.rootDoc._height); getTransform = (ele: Opt<HTMLDivElement>) => { if (!ele) return Transform.Identity(); const { scale, translateX, translateY } = Utils.GetScreenTransform(ele); diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 967e10434..632abd6cf 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,23 +1,20 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, ObservableMap, trace, untracked } from 'mobx'; +import { action, computed, observable, ObservableMap, untracked } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast, Field, StrListCast } from '../../../../fields/Doc'; +import { computedFn } from 'mobx-utils'; +import { Doc, Field, StrListCast } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; -import { RichTextField } from '../../../../fields/RichTextField'; import { listSpec } from '../../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { ImageField } from '../../../../fields/URLField'; import { emptyFunction, returnDefault, returnEmptyDoclist, returnEmptyString, returnFalse, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils'; import { Docs, DocUtils } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { SelectionManager } from '../../../util/SelectionManager'; -import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; -import { ContextMenuProps } from '../../ContextMenuItem'; import { EditableView } from '../../EditableView'; import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; @@ -214,9 +211,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - addNewKey = (key: string, defaultVal: any) => { - this.childDocs.forEach(doc => (doc[key] = defaultVal)); - }; + addNewKey = (key: string, defaultVal: any) => this.childDocs.forEach(doc => (doc[key] = defaultVal)); @undoBatch @action @@ -302,9 +297,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - addRowRef = (doc: Doc, ref: HTMLDivElement) => { - this._rowEles.set(doc, ref); - }; + addRowRef = (doc: Doc, ref: HTMLDivElement) => this._rowEles.set(doc, ref); @action setColRef = (index: number, ref: HTMLDivElement) => { @@ -402,68 +395,12 @@ export class CollectionSchemaView extends CollectionSubView() { menuCallback = (x: number, y: number) => { ContextMenu.Instance.clearItems(); - const layoutItems: ContextMenuProps[] = []; - const docItems: ContextMenuProps[] = []; - const dataDoc = this.props.DataDoc || this.props.Document; - - DocUtils.addDocumentCreatorMenuItems( - doc => { - FormattedTextBox.SelectOnLoad = StrCast(doc[Id]); - return this.addRow(doc); - }, - this.addRow, - x, - y, - true - ); - Array.from(Object.keys(Doc.GetProto(dataDoc))) - .filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof dataDoc[fieldKey] === 'string') - .map(fieldKey => - docItems.push({ - description: ':' + fieldKey, - event: () => { - const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.Document)); - if (created) { - if (this.props.Document.isTemplateDoc) { - Doc.MakeMetadataFieldTemplate(created, this.props.Document); - } - return this.addRow(created); - } - }, - icon: 'compress-arrows-alt', - }) - ); - Array.from(Object.keys(Doc.GetProto(dataDoc))) - .filter(fieldKey => DocListCast(dataDoc[fieldKey]).length) - .map(fieldKey => - docItems.push({ - description: ':' + fieldKey, - event: () => { - const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey }); - if (created) { - const container = this.props.Document.resolvedDataDoc ? Doc.GetProto(this.props.Document) : this.props.Document; - if (container.isTemplateDoc) { - Doc.MakeMetadataFieldTemplate(created, container); - return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created); - } - return this.addRow(created) || false; - } - }, - icon: 'compress-arrows-alt', - }) - ); - !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' }); - !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' }); + DocUtils.addDocumentCreatorMenuItems(doc => this.addRow(doc), this.addRow, x, y, true); + ContextMenu.Instance.setDefaultItem('::', (name: string): void => { Doc.GetProto(this.props.Document)[name] = ''; - const created = Docs.Create.TextDocument('', { title: name, _autoHeight: true }); - if (created) { - if (this.props.Document.isTemplateDoc) { - Doc.MakeMetadataFieldTemplate(created, this.props.Document); - } - this.addRow(created); - } + this.addRow(Docs.Create.TextDocument('', { title: name, _autoHeight: true })); }); ContextMenu.Instance.displayMenu(x, y, undefined, true); }; @@ -540,9 +477,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - closeColumnMenu = () => { - this._columnMenuIndex = undefined; - }; + closeColumnMenu = () => (this._columnMenuIndex = undefined); @action openFilterMenu = (index: number) => { @@ -593,19 +528,14 @@ export class CollectionSchemaView extends CollectionSubView() { getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field); removeFieldFilters = (field: string) => { - this.getFieldFilters(field).forEach(filter => { - Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove'); - }); + this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove')); }; onFilterKeyDown = (e: React.KeyboardEvent) => { + //prettier-ignore switch (e.key) { - case 'Enter': - this.closeFilterMenu(true); - break; - case 'Escape': - this.closeFilterMenu(false); - break; + case 'Enter' : this.closeFilterMenu(true); break; + case 'Escape': this.closeFilterMenu(false);break; } }; @@ -900,6 +830,7 @@ interface CollectionSchemaViewDocsProps { class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsProps> { tableWidthFunc = () => this.props.schema.tableWidth; rowHeightFunc = () => CollectionSchemaView._rowHeight; + childScreenToLocal = computedFn((index: number) => () => this.props.schema.props.ScreenToLocalTransform().translate(0, -CollectionSchemaView._rowHeight - index * this.rowHeightFunc())); render() { return ( <div className="schema-table-content"> @@ -929,7 +860,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP docRangeFilters={this.props.schema.childDocRangeFilters} searchFilterDocs={this.props.schema.searchFilterDocs} rootSelected={this.props.schema.rootSelected} - ScreenToLocalTransform={Transform.Identity} + ScreenToLocalTransform={this.childScreenToLocal(index)} bringToFront={emptyFunction} isDocumentActive={this.props.schema.props.childDocumentsActive?.() ? this.props.schema.props.isDocumentActive : this.props.schema.isContentActive} isContentActive={emptyFunction} diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx index 43895fc9c..b133347cf 100644 --- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx +++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx @@ -41,18 +41,12 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> @action onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction); + setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction, false); }; render() { return ( - <div - className="schema-column-header" - style={{ width: this.props.columnWidths[this.props.columnIndex] }} - onPointerDown={this.onPointerDown} - ref={(col: HTMLDivElement | null) => { - col && this.props.setColRef(this.props.columnIndex, col); - }}> + <div className="schema-column-header" style={{ width: this.props.columnWidths[this.props.columnIndex] }} onPointerDown={this.onPointerDown} ref={col => col && this.props.setColRef(this.props.columnIndex, col)}> <div className="schema-column-resizer left" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex)}></div> <div className="schema-column-title">{this.fieldKey}</div> |
