diff options
author | bobzel <zzzman@gmail.com> | 2023-09-29 10:37:14 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2023-09-29 10:37:14 -0400 |
commit | c5c511fe51e858ca52615aaedb193dc947b90932 (patch) | |
tree | 7ea2ab13f1156d1fccfc4085b284ac41ad1fd269 | |
parent | 442cb6eb721008191bccb4eae7fcf576aa460461 (diff) |
fixed autoHeight text scrolling in its own tab and elsewhere. update ChildDrag to GroupChildDrag and simplified documentView code. fixed MapAnchorMenu to allow linking by dragging text icon.
-rw-r--r-- | src/client/util/DragManager.ts | 19 | ||||
-rw-r--r-- | src/client/views/InkingStroke.tsx | 2 | ||||
-rw-r--r-- | src/client/views/MarqueeAnnotator.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCarousel3DView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCarouselView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss | 9 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 75 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/SchemaTableCell.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 20 | ||||
-rw-r--r-- | src/client/views/nodes/FieldView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/MapBox/MapAnchorMenu.tsx | 62 | ||||
-rw-r--r-- | src/client/views/nodes/MapBox/MapBox.tsx | 36 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 10 | ||||
-rw-r--r-- | src/client/views/pdf/AnchorMenu.tsx | 3 |
15 files changed, 121 insertions, 125 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index f86f9a3e5..4f30e92ce 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -4,7 +4,7 @@ import { Doc, Field, Opt, StrListCast } from '../../fields/Doc'; import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { ScriptField } from '../../fields/ScriptField'; -import { ScriptCast, StrCast } from '../../fields/Types'; +import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; import { emptyFunction, Utils } from '../../Utils'; import { Docs, DocUtils } from '../documents/Documents'; import * as globalCssVariables from '../views/global/globalCssVariables.scss'; @@ -108,9 +108,11 @@ export namespace DragManager { constructor(dragDoc: Doc[], dropAction?: dropActionType) { this.draggedDocuments = dragDoc; this.droppedDocuments = []; + this.draggedViews = []; this.offset = [0, 0]; this.dropAction = dropAction; } + draggedViews: DocumentView[]; draggedDocuments: Doc[]; droppedDocuments: Doc[]; treeViewDoc?: Doc; @@ -189,6 +191,13 @@ export namespace DragManager { // drag a document and drop it (or make an embed/copy on drop) export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => any) { + dragData.draggedViews.forEach( + action(view => { + const ffview = view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + ffview && (ffview.GroupChildDrag = BoolCast(ffview.Document._isGroup)); + ffview?.setupDragLines(false); + }) + ); const addAudioTag = (dropDoc: any) => { dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField()); dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc); @@ -196,6 +205,14 @@ export namespace DragManager { }; const finishDrag = async (e: DragCompleteEvent) => { const docDragData = e.docDragData; + setTimeout(() => + dragData.draggedViews.forEach( + action(view => { + const ffview = view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + ffview && (ffview.GroupChildDrag = false); + }) + ) + ); onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails if (docDragData && !docDragData.droppedDocuments.length) { docDragData.dropAction = dragData.userDropAction || dragData.dropAction; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index f63a27426..9b52f5870 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -446,7 +446,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() { mask, highlightColor ); - const fsize = +StrCast(this.props.Document._text_fontSize, '12px').replace('px', ''); // bootsrap 3 style sheet sets line height to be 20px for default 14 point font size. // this attempts to figure out the lineHeight ratio by inquiring the body's lineHeight and dividing by the fontsize which should yield 1.428571429 // see: https://bibwild.wordpress.com/2019/06/10/bootstrap-3-to-4-changes-in-how-font-size-line-height-and-spacing-is-done-or-what-happened-to-line-height-computed/ @@ -493,7 +492,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() { yPadding={10} xPadding={10} fieldKey="text" - fontSize={fsize} dontRegisterView={true} noSidebar={true} dontScale={true} diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index a958607de..0987b0867 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -211,7 +211,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DocData]); const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations); addAsAnnotation && !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc); - return (annotationDoc as Doc) ?? undefined; + return annotationDoc as Doc; }; public static previewNewAnnotation = action((savedAnnotations: ObservableMap<number, HTMLDivElement[]>, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => { diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 4bc72772a..a8d080953 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -28,7 +28,6 @@ export class CollectionCarousel3DView extends CollectionSubView() { } protected createDashEventsTarget = (ele: HTMLDivElement | null) => { - //used for stacking and masonry view this._dropDisposer?.(); if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 040a584b8..33a92d406 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -21,7 +21,6 @@ export class CollectionCarouselView extends CollectionSubView() { } protected createDashEventsTarget = (ele: HTMLDivElement | null) => { - //used for stacking and masonry view this._dropDisposer?.(); if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); @@ -86,7 +85,7 @@ export class CollectionCarouselView extends CollectionSubView() { marginLeft: this.marginX, width: `calc(100% - ${this.marginX * 2}px)`, }}> - <FormattedTextBox key={index} {...captionProps} allowScroll={true} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} /> + <FormattedTextBox key={index} {...captionProps} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} /> </div> </> ); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 26272d2ee..158f9d8ee 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -31,7 +31,6 @@ export function CollectionSubView<X>(moreProps?: X) { @observable _focusFilters: Opt<string[]>; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it @observable _focusRangeFilters: Opt<string[]>; // childFiltersByRanges that are overridden when previewing a link to an anchor which has childFiltersByRanges set on it protected createDashEventsTarget = (ele: HTMLDivElement | null) => { - //used for stacking and masonry view this.dropDisposer?.(); this.gestureDisposer?.(); this._multiTouchDisposer?.(); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index e4ae251c8..c90fdf013 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -11,6 +11,15 @@ touch-action: none; border-radius: inherit; } +.collectionFreeForm-groupDropper { + width: 10000; + height: 10000; + left: -5000; + top: -5000; + position: absolute; + background: transparent; + pointer-events: all; +} .collectionfreeformview-grid { transform-origin: top left; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a64ccbb2c..676e96714 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -124,7 +124,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @observable _timelineRef = React.createRef<Timeline>(); @observable _marqueeRef: HTMLDivElement | null = null; @observable _marqueeViewRef = React.createRef<MarqueeView>(); - @observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. + @observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. @observable _brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 }; // highlighted region of freeform canvas used by presentations to indicate a region constructor(props: any) { @@ -342,7 +342,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @action internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) { - if (!de.embedKey && !this.ChildDrag && this.rootDoc._isGroup) return false; if (!super.onInternalDrop(e, de)) return false; const refDoc = docDragData.droppedDocuments[0]; const [xpo, ypo] = this.getContainerTransform().transformPoint(de.x, de.y); @@ -1763,17 +1762,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @undoBatch toggleNativeDimensions = () => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight); + /// + /// resetView restores a freeform collection to unit scale and centered at (0,0) UNLESS + /// the view is a group, in which case this does nothing (since Groups calculate their own scale and center) + /// + @undoBatch + resetView = () => { + if (!this.props.Document._isGroup) { + this.props.Document[this.panXFieldKey] = this.props.Document[this.panYFieldKey] = 0; + this.props.Document[this.scaleFieldKey] = 1; + } + }; + onContextMenu = (e: React.MouseEvent) => { if (this.props.isAnnotationOverlay || !ContextMenu.Instance) return; const appearance = ContextMenu.Instance.findByDescription('Appearance...'); const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : []; - !this.props.Document._isGroup && - appearanceItems.push({ - description: 'Reset View', - event: () => CollectionFreeFormView.ResetView(this), - icon: 'compress-arrows-alt', - }); + !this.props.Document._isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' }); !Doc.noviceMode && appearanceItems.push({ description: 'Toggle auto arrange', @@ -1975,15 +1981,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } nativeDim = () => this.nativeDimScaling; - private groupDropDisposer?: DragManager.DragDropDisposer; - protected createGroupEventsTarget = (ele: HTMLDivElement) => { - //used for stacking and masonry view - this.groupDropDisposer?.(); - if (ele) { - this.groupDropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this)); - } - }; - @action brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number) => { this._brushtimer1 && clearTimeout(this._brushtimer1); @@ -2068,51 +2065,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection {/* // 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" /> - ))} + {(this._hLines ?? []) + .map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />) // + .concat((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.GroupChildDrag ? <div className="collectionFreeForm-groupDropper" /> : null} </> )} </div> ); } - /// resetView restores a freeform collection to unit scale and centered at (0,0) UNLESS - // the view is a group, in which case this does nothing (since Groups calculate their own scale and center) - static ResetView(view?: CollectionFreeFormView) { - if (view) { - if (view.props.Document._isGroup) return; - view.props.Document[view.panXFieldKey] = view.props.Document[view.panYFieldKey] = 0; - view.props.Document[view.scaleFieldKey] = 1; - } else { - SelectionManager.Docs() - .filter(doc => !doc._isGroup) - .forEach(doc => { - doc._freeform_panX = doc._freeform_panY = 0; - doc._freeform_scale = 1; - }); - } - } } interface CollectionFreeFormOverlayViewProps { @@ -2358,6 +2322,3 @@ ScriptingGlobals.add(function bringToFront() { ScriptingGlobals.add(function sendToBack(doc: Doc) { SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc, true)); }); -ScriptingGlobals.add(function resetView() { - CollectionFreeFormView.ResetView(); -}); diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 4092d9722..9d5b533d1 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -272,7 +272,7 @@ export class SchemaRTFCell extends React.Component<SchemaTableCellProps> { fieldProps.isContentActive = this.selectedFunc; return ( <div className="schemaRTFCell" style={{ display: 'flex', fontStyle: this.selected ? undefined : 'italic', width: '100%', height: '100%', position: 'relative', color, textDecoration, cursor, pointerEvents }}> - {this.selected ? <FormattedTextBox allowScroll={true} {...fieldProps} DataDoc={this.props.Document} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))} + {this.selected ? <FormattedTextBox {...fieldProps} DataDoc={this.props.Document} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))} </div> ); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ef96e64be..e4fc6c4a2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -394,18 +394,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps dragData.treeViewDoc = this.props.treeViewDoc; dragData.removeDocument = this.props.removeDocument; dragData.moveDocument = this.props.moveDocument; + dragData.draggedViews = [this.props.DocumentView()]; dragData.canEmbed = this.rootDoc.dragAction ?? this.props.dragAction ? true : false; - const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; - ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView())); DragManager.StartDocumentDrag( selected.map(dv => dv.docView!._mainCont.current!), dragData, x, y, - { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) }, - () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined))) + { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) } ); // this needs to happen after the drop event is processed. - ffview?.setupDragLines(false); } } @@ -495,11 +492,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } const sendToBack = e.altKey; this._singleClickFunc = - clickFunc ?? - (() => - sendToBack - ? this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.bringToFront(this.rootDoc, true) - : this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ?? this.props.select(e.ctrlKey || e.metaKey || e.shiftKey)); + // prettier-ignore + clickFunc ?? (() => (sendToBack ? this.props.DocumentView().props.bringToFront(this.rootDoc, true) : + this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ?? + this.props.select(e.ctrlKey || e.metaKey || e.shiftKey))); const waitFordblclick = this.props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick; if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') { this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout); @@ -1099,7 +1095,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption'); @computed get innards() { TraceMobx(); - const ffscale = () => this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1; const showTitle = this.layout_showTitle?.split(':')[0]; const showTitleHover = this.layout_showTitle?.includes(':hover'); const captionView = !this.layout_showCaption ? null : ( @@ -1107,8 +1102,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps className="documentView-captionWrapper" style={{ pointerEvents: this.rootDoc.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined, - minWidth: 50 * ffscale(), - maxHeight: `max(100%, ${20 * ffscale()}px)`, background: StrCast(this.layoutDoc._backgroundColor, 'rgba(0,0,0,0.2)'), color: lightOrDark(StrCast(this.layoutDoc._backgroundColor, 'black')), }}> @@ -1117,7 +1110,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps yPadding={10} xPadding={10} fieldKey={this.layout_showCaption} - fontSize={12 * Math.max(1, (2 * ffscale()) / 3)} styleProvider={this.captionStyleProvider} dontRegisterView={true} noSidebar={true} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 4ebf22ddf..f014f842e 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -34,7 +34,6 @@ export interface FieldViewProps extends DocumentViewSharedProps { backgroundColor?: string; treeViewDoc?: Doc; color?: string; - fontSize?: number; height?: number; width?: number; noSidebar?: boolean; diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx index f0827936b..7af4d9b59 100644 --- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx @@ -1,41 +1,32 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction } from 'mobx'; +import { IReactionDisposer, ObservableMap, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { ColorState } from 'react-color'; import { Doc, Opt } from '../../../../fields/Doc'; -import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../../Utils'; +import { returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../../Utils'; import { SelectionManager } from '../../../util/SelectionManager'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; -import { LinkPopup } from '../../linking/LinkPopup'; -import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT'; // import { GPTPopup, GPTPopupMode } from './../../GPTPopup/GPTPopup'; -import { EditorView } from 'prosemirror-view'; -import './MapAnchorMenu.scss'; -import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; -import { StrCast } from '../../../../fields/Types'; -import { DocumentType } from '../../../documents/DocumentTypes'; +import { IconButton } from 'browndash-components'; import { SettingsManager } from '../../../util/SettingsManager'; +import './MapAnchorMenu.scss'; @observer export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> { static Instance: MapAnchorMenu; private _disposer: IReactionDisposer | undefined; - private _disposer2: IReactionDisposer | undefined; - private _commentCont = React.createRef<HTMLButtonElement>(); + private _commentRef = React.createRef<HTMLDivElement>(); public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search public Center: () => void = unimplementedFunction; - // public OnClick: (e: PointerEvent) => void = unimplementedFunction; + public OnClick: (e: PointerEvent) => void = unimplementedFunction; // public OnAudio: (e: PointerEvent) => void = unimplementedFunction; - // public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; - // public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; + public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined; public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => undefined; public Delete: () => void = unimplementedFunction; - public LinkNote: () => void = unimplementedFunction; // public MakeTargetToggle: () => void = unimplementedFunction; // public ShowTargetTrail: () => void = unimplementedFunction; public IsTargetToggler: () => boolean = returnFalse; @@ -52,23 +43,12 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> { componentWillUnmount() { this._disposer?.(); - this._disposer2?.(); } componentDidMount() { - this._disposer2 = reaction( - () => this._opacity, - opacity => { - if (!opacity) { - } - }, - { fireImmediately: true } - ); this._disposer = reaction( () => SelectionManager.Views().slice(), - selected => { - MapAnchorMenu.Instance.fadeOut(true); - } + selected => MapAnchorMenu.Instance.fadeOut(true) ); } // audioDown = (e: React.PointerEvent) => { @@ -87,6 +67,18 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> { // e => this.OnCrop?.(e) // ); // }; + notePointerDown = (e: React.PointerEvent) => { + setupMoveUpEvents( + this, + e, + (e: PointerEvent) => { + this.StartDrag(e, this._commentRef.current!); + return true; + }, + returnFalse, + e => this.OnClick(e) + ); + }; static top = React.createRef<HTMLDivElement>(); // public get Top(){ @@ -105,12 +97,14 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> { /> } { - <IconButton - tooltip="Link Note to Pin" // - onPointerDown={this.LinkNote} - icon={<FontAwesomeIcon icon="sticky-note" />} - color={SettingsManager.userColor} - /> + <div ref={this._commentRef}> + <IconButton + tooltip="Link Note to Pin" // + onPointerDown={this.notePointerDown} + icon={<FontAwesomeIcon icon="sticky-note" />} + color={SettingsManager.userColor} + /> + </div> } { <IconButton diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index d7469e530..a419858fc 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -6,6 +6,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; import { DocCss, Highlight, Width } from '../../../../fields/DocSymbols'; +import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; @@ -24,6 +25,7 @@ import { MarqueeAnnotator } from '../../MarqueeAnnotator'; import { SidebarAnnos } from '../../SidebarAnnos'; import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; +import { FormattedTextBox } from '../formattedText/FormattedTextBox'; import { PinProps, PresBox } from '../trails'; import { MapAnchorMenu } from './MapAnchorMenu'; import './MapBox.scss'; @@ -215,6 +217,37 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); }; + startAnchorDrag = (e: PointerEvent, ele: HTMLElement) => { + e.preventDefault(); + e.stopPropagation(); + + const sourceAnchorCreator = action(() => { + const note = this.getAnchor(true); + if (note && this.selectedPin) { + note.latitude = this.selectedPin.latitude; + note.longitude = this.selectedPin.longitude; + note.map = this.selectedPin.map; + } + return note as Doc; + }); + + const targetCreator = (annotationOn: Doc | undefined) => { + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow'); + FormattedTextBox.SelectOnLoad = target[Id]; + return target; + }; + const docView = this.props.DocumentView?.(); + docView && + DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { + dragComplete: e => { + if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { + e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.Document; + e.annoDragData.linkSourceDoc.followLinkZoom = false; + } + }, + }); + }; + createNoteAnnotation = () => { const createFunc = undoable( action(() => { @@ -388,7 +421,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps MapAnchorMenu.Instance.Delete = this.deleteSelectedPin; MapAnchorMenu.Instance.Center = this.centerOnSelectedPin; - MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation; + MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation; + MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag; const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude)); const x = point.x + (this.props.PanelWidth() - this.sidebarWidth()) / 2; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 6a92d09d9..90ebf5206 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -78,9 +78,7 @@ const translate = setCORS('http://cors-anywhere.herokuapp.com/'); export const GoogleRef = 'googleDocId'; type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void; -export interface FormattedTextBoxProps { - allowScroll?: boolean; -} +export interface FormattedTextBoxProps {} @observer export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps & FormattedTextBoxProps>() { public static LayoutString(fieldStr: string) { @@ -562,7 +560,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps drop = (e: Event, de: DragManager.DropEvent) => { if (de.complete.annoDragData) { de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true); - e.stopPropagation(); return true; } const dragData = de.complete.docDragData; @@ -2067,10 +2064,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) - if (this.props.isContentActive() && !this.props.allowScroll) { + if (this.props.isContentActive()) { // prevent default if selected || child is active but this doc isn't scrollable if ( - (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil(Number(this.layoutDoc._height)) && // + (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil(this.props.PanelHeight()) && // (this.props.isSelected() || this.isAnyChildContentActive()) ) { e.preventDefault(); @@ -2131,7 +2128,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ref={this._ref} style={{ cursor: this.props.isContentActive() ? 'text' : undefined, - overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined), pointerEvents: Doc.ActiveTool === InkTool.None && !this.props.onBrowseClick?.() ? undefined : 'none', }} diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 85c1cce8c..18cf633f4 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -5,16 +5,15 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio import { observer } from 'mobx-react'; import { ColorState } from 'react-color'; import { Doc, Opt } from '../../../fields/Doc'; -import { StrCast } from '../../../fields/Types'; import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../Utils'; import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT'; import { DocumentType } from '../../documents/DocumentTypes'; import { SelectionManager } from '../../util/SelectionManager'; +import { SettingsManager } from '../../util/SettingsManager'; import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu'; import { LinkPopup } from '../linking/LinkPopup'; import './AnchorMenu.scss'; import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup'; -import { SettingsManager } from '../../util/SettingsManager'; @observer export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { |