diff options
Diffstat (limited to 'src')
23 files changed, 278 insertions, 317 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index bd5fa5d78..029653204 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -514,7 +514,17 @@ export namespace Docs { DocumentType.COL, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { _layout_fitWidth: true, freeform: '', _freeform_panX: 0, _freeform_panY: 0, _freeform_scale: 1, systemIcon: 'BsFillCollectionFill' }, + options: { + _layout_fitWidth: true, + freeform: '', + _freeform_panX: 0, + _freeform_panY: 0, + _freeform_scale: 1, + layout_nativeDimEditable: true, + layout_reflowHorizontal: true, + layout_reflowVertical: true, + systemIcon: 'BsFillCollectionFill', + }, }, ], [ @@ -535,7 +545,7 @@ export namespace Docs { DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, layout_fitWidth: true, layout_reflowHorizontal: true, layout_nativeDimEditable: true, systemIcon: 'BsFillVolumeUpFill' }, + options: { _height: 100, layout_fitWidth: true, layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsFillVolumeUpFill' }, }, ], [ @@ -615,14 +625,25 @@ export namespace Docs { DocumentType.EQUATION, { layout: { view: EquationBox, dataField: 'text' }, - options: { layout_nativeDimEditable: true, fontSize: '14px', layout_hideResizeHandles: true, layout_hideDecorationTitle: true, systemIcon: 'BsCalculatorFill' }, ///systemIcon: 'BsSuperscript' + BsSubscript + options: { + fontSize: '14px', + layout_reflowHorizontal: true, + layout_reflowVertical: true, + layout_nativeDimEditable: true, + layout_hideDecorationTitle: true, + systemIcon: 'BsCalculatorFill', + }, ///systemIcon: 'BsSuperscript' + BsSubscript }, ], [ DocumentType.FUNCPLOT, { layout: { view: FunctionPlotBox, dataField: defaultDataKey }, - options: { layout_nativeDimEditable: true }, + options: { + layout_reflowHorizontal: true, + layout_reflowVertical: true, + layout_nativeDimEditable: true, + }, }, ], [ @@ -695,7 +716,7 @@ export namespace Docs { { data: '', layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { backgroundColor: 'gray', dropAction: 'move', waitForDoubleClickToClick: 'always', systemIcon: 'BsLayoutSplit' }, + options: { backgroundColor: 'gray', dropAction: 'move', waitForDoubleClickToClick: 'always', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsLayoutSplit' }, }, ], [ diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index e59e847c2..f928a1bf9 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -57,7 +57,7 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () export namespace DragManager { let dragDiv: HTMLDivElement; let dragLabel: HTMLDivElement; - export let StartWindowDrag: Opt<(e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void>; + export let StartWindowDrag: Opt<(e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => boolean>; export let CompleteWindowDrag: Opt<(aborted: boolean) => void>; export function Root() { @@ -490,7 +490,7 @@ export namespace DragManager { if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.ctrlKey ? 'embed' : dragData.defaultDropAction; } - if (((e.target as any)?.className === 'lm_tabs' || (e.target as any)?.className === 'lm_header' || e?.shiftKey) && dragData.draggedDocuments.length === 1) { + if (((e.target as any)?.className === 'lm_tabs' || (e.target as any)?.className === 'lm_header') && dragData.draggedDocuments.length === 1) { if (!startWindowDragTimer) { startWindowDragTimer = setTimeout(async () => { startWindowDragTimer = undefined; diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index fcf705ac0..e864458d8 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -21,18 +21,13 @@ export namespace SelectionManager { manager.SelectedSchemaDocument = doc; } @action - SelectView(docView: DocumentView, ctrlPressed: boolean): void { - // if doc is not in SelectedDocuments, add it + SelectView(docView: DocumentView, extendSelection: boolean): void { if (!docView.SELECTED) { - if (!ctrlPressed) this.DeselectAll(); + if (!extendSelection) this.DeselectAll(); manager.SelectedViews.push(docView); - } else if (!ctrlPressed && (manager.SelectedViews.length > 1 || manager.SelectedSchemaDocument)) { - manager.SelectedViews.filter(dv => dv !== docView).forEach(dv => dv.props.whenChildContentsActiveChanged(false)); - manager.SelectedSchemaDocument = undefined; - manager.SelectedViews.length = 0; + docView.SELECTED = true; + docView.props.whenChildContentsActiveChanged(true); } - docView.SELECTED = true; - docView.props.whenChildContentsActiveChanged(true); } @action DeselectView(docView?: DocumentView): void { diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts index c0cd94067..fce43eef6 100644 --- a/src/client/util/SnappingManager.ts +++ b/src/client/util/SnappingManager.ts @@ -3,6 +3,8 @@ import { Doc } from '../../fields/Doc'; export namespace SnappingManager { class Manager { + @observable ShiftKey = false; + @observable CtrlKey = false; @observable IsDragging: boolean = false; @observable IsResizing: Doc | undefined; @observable CanEmbed: boolean = false; @@ -33,6 +35,12 @@ export namespace SnappingManager { return manager.vertSnapLines; } + export function SetShiftKey(down: boolean) { + runInAction(() => (manager.ShiftKey = down)); + } + export function SetCtrlKey(down: boolean) { + runInAction(() => (manager.CtrlKey = down)); + } export function SetIsDragging(dragging: boolean) { runInAction(() => (manager.IsDragging = dragging)); } @@ -42,6 +50,12 @@ export namespace SnappingManager { export function SetCanEmbed(canEmbed: boolean) { runInAction(() => (manager.CanEmbed = canEmbed)); } + export function GetShiftKey() { + return manager.ShiftKey; + } + export function GetCtrlKey() { + return manager.CtrlKey; + } export function GetIsDragging() { return manager.IsDragging; } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index fa43b86bb..9ff27d9a0 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -50,10 +50,10 @@ interface ViewBoxBaseProps { DataDoc?: Doc; DocumentView?: () => DocumentView; fieldKey: string; - isSelected: (outsideReaction?: boolean) => boolean; + isSelected: () => boolean; isContentActive: () => boolean | undefined; renderDepth: number; - rootSelected: (outsideReaction?: boolean) => boolean; + rootSelected: () => boolean; } export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { class Component extends React.Component<React.PropsWithChildren<P>> { @@ -89,8 +89,8 @@ export interface ViewBoxAnnotatableProps { isContentActive: () => boolean | undefined; select: (isCtrlPressed: boolean) => void; whenChildContentsActiveChanged: (isActive: boolean) => void; - isSelected: (outsideReaction?: boolean) => boolean; - rootSelected: (outsideReaction?: boolean) => boolean; + isSelected: () => boolean; + rootSelected: () => boolean; renderDepth: number; isAnnotationOverlay?: boolean; } diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index f41cf1385..bbd951481 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -4,7 +4,6 @@ $linkGap: 3px; $headerHeight: 20px; $resizeHandler: 8px; -.documentDecorations-Dark, .documentDecorations { position: absolute; z-index: 2000; @@ -40,14 +39,12 @@ $resizeHandler: 8px; border-radius: 50%; } } -.documentDecorations-Dark { - background: dimgray; -} .documentDecorations-container { position: absolute; top: 0; left: 0; + transform-origin: 50% calc(50% + 10px); display: grid; grid-template-rows: $headerHeight $resizeHandler 1fr $resizeHandler; grid-template-columns: $resizeHandler 1fr $resizeHandler; @@ -60,6 +57,7 @@ $resizeHandler: 8px; flex-direction: row; gap: 2px; pointer-events: all; + color: black; cursor: move; .documentDecorations-openButton { @@ -169,7 +167,6 @@ $resizeHandler: 8px; } } - .documentDecorations-title-Dark, .documentDecorations-title { opacity: 1; width: calc(100% - 60px); // = margin-left + margin-right @@ -188,22 +185,13 @@ $resizeHandler: 8px; opacity: 1; } - .documentDecorations-titleSpan, - .documentDecorations-titleSpan-Dark { + .documentDecorations-titleSpan { width: 100%; border-radius: 8px; background: $light-gray; display: inline-block; cursor: move; } - .documentDecorations-titleSpan-Dark { - background: hsla(0, 0%, 0%, 0.412); - } - } - - .documentDecorations-title-Dark { - color: white; - background: black; } .documentDecorations-titleBackground { @@ -326,11 +314,6 @@ $resizeHandler: 8px; } } - .documentDecorations-resizer-Dark { - background: $light-gray; - opacity: 0.2; - } - .documentDecorations-topLeftResizer, .documentDecorations-leftResizer, .documentDecorations-bottomLeftResizer { @@ -357,6 +340,7 @@ $resizeHandler: 8px; background: $medium-gray; height: 10; width: 10; + opacity: 0.5; pointer-events: all; cursor: nwse-resize; } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 8df5740fa..dc62e0450 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -5,6 +5,7 @@ import { IconButton } from 'browndash-components'; import { action, computed, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { FaUndo } from 'react-icons/fa'; +import { DateField } from '../../fields/DateField'; import { Doc, DocListCast, Field, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, DocData } from '../../fields/DocSymbols'; import { InkField } from '../../fields/InkField'; @@ -35,7 +36,6 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; import React = require('react'); import _ = require('lodash'); -import { DateField } from '../../fields/DateField'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -49,64 +49,39 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P private _offset = { x: 0, y: 0 }; // offset from click pt to inner edge of resize border private _snapPt = { x: 0, y: 0 }; // last snapped location of resize border private _inkDragDocs: { doc: Doc; x: number; y: number; width: number; height: number }[] = []; + private _interactionLock?: boolean; + @observable _showNothing = true; @observable private _accumulatedTitle = ''; @observable private _titleControlString: string = '#title'; @observable private _editingTitle = false; @observable private _hidden = false; - @observable public AddToSelection = false; // if Shift is pressed, then this should be set so that clicking on the selection background is ignored so overlapped documents can be added to the selection set. - @observable public pushIcon: IconProp = 'arrow-alt-circle-up'; - @observable public pullIcon: IconProp = 'arrow-alt-circle-down'; - @observable public pullColor: string = 'white'; @observable private _isRotating: boolean = false; @observable private _isRounding: boolean = false; - @observable private showLayoutAcl: boolean = false; + @observable private _showLayoutAcl: boolean = false; + @observable private _showRotCenter = false; // whether to show a draggable green dot that represents the center of rotation + @observable private _rotCenter = [0, 0]; // the center of rotation in object coordinates (0,0) = object center (not top left!) constructor(props: any) { super(props); DocumentDecorations.Instance = this; - reaction( - () => SelectionManager.Views().slice(), - action(views => { - this._showNothing = !DocumentView.LongPress && views.length === 1; // show decorations if multiple docs are selected or we're long pressing - this._editingTitle = false; - }) - ); - document.addEventListener( - // show decorations whenever pointer moves outside of selection bounds. - 'pointermove', - action(e => { - if (this.Bounds.x || this.Bounds.y || this.Bounds.r || this.Bounds.b) { - if (this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX + 10 || this.Bounds.r < e.clientX - 10 || this.Bounds.y > e.clientY + 10 || this.Bounds.b < e.clientY - 10)) { - this._showNothing = false; - } else { - this._showNothing = true; - } - } - }) - ); + document.addEventListener('pointermove', // show decorations whenever pointer moves outside of selection bounds. + action(e => (this._showNothing = !(this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX + 10 || this.Bounds.r < e.clientX - 10 || this.Bounds.y > e.clientY + 10 || this.Bounds.b < e.clientY - 10))))); // prettier-ignore } - @computed - get Bounds() { - if (LinkFollower.IsFollowing || DocumentView.ExploreMode) return { x: 0, y: 0, r: 0, b: 0 }; - const views = SelectionManager.Views(); - return views - .filter(dv => dv.props.renderDepth > 0) - .map(dv => dv.getBounds()) - .reduce( - (bounds, rect) => - !rect - ? bounds - : { - x: Math.min(rect.left, bounds.x), - y: Math.min(rect.top, bounds.y), - r: Math.max(rect.right, bounds.r), - b: Math.max(rect.bottom, bounds.b), - c: views.length === 1 ? rect.center : undefined, - }, - { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as { X: number; Y: number } | undefined } - ); + @computed get Bounds() { + return (LinkFollower.IsFollowing || DocumentView.ExploreMode) ? + { x: 0, y: 0, r: 0, b: 0 } + : SelectionManager.Views() + .filter(dv => dv.props.renderDepth > 0) + .map(dv => dv.getBounds()) + .reduce((bounds, rect) => !rect ? bounds + : { x: Math.min(rect.left, bounds.x), + y: Math.min(rect.top, bounds.y), + r: Math.max(rect.right, bounds.r), + b: Math.max(rect.bottom, bounds.b), + c: SelectionManager.Views().length === 1 ? rect.center : undefined }, + { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as { X: number; Y: number } | undefined }); // prettier-ignore } @action @@ -162,48 +137,43 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } }; - @action onContainerDown = (e: React.PointerEvent): void => { - const first = SelectionManager.Views()[0]; - const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc); + onContainerDown = (e: React.PointerEvent) => { + const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].rootDoc); if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) { - setupMoveUpEvents( - this, - e, - e => this.onBackgroundMove(true, e), - e => {}, - emptyFunction - ); + setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), emptyFunction, emptyFunction); + e.stopPropagation(); } }; - @action onTitleDown = (e: React.PointerEvent): void => { - const first = SelectionManager.Views()[0]; - const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc); + onTitleDown = (e: React.PointerEvent) => { + const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].rootDoc); if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) { setupMoveUpEvents( this, e, e => this.onBackgroundMove(true, e), - e => {}, + emptyFunction, action(e => { !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('#') ? this.selectionTitle : this._titleControlString); this._editingTitle = true; this._keyinput.current && setTimeout(this._keyinput.current.focus); }) ); + e.stopPropagation(); } }; - onBackgroundDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction); - + onBackgroundDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction); + e.stopPropagation(); + }; @action onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => { - const first = SelectionManager.Views()[0]; - const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc); + const dragDocView = SelectionManager.Views()[0]; + const effectiveLayoutAcl = GetEffectiveAcl(dragDocView.rootDoc); if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment) { return false; } - const dragDocView = SelectionManager.Views()[0]; const containers = new Set<Doc | undefined>(); SelectionManager.Views().forEach(v => containers.add(DocCast(v.rootDoc.embedContainer))); if (containers.size > 1) return false; @@ -234,9 +204,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P _deleteAfterIconify = false; _iconifyBatch: UndoManager.Batch | undefined; onCloseClick = (forceDeleteOrIconify: boolean | undefined) => { - const views = SelectionManager.Views() - .slice() - .filter(v => v && v.props.renderDepth > 0); + const views = SelectionManager.Views().filter(v => v && v.props.renderDepth > 0); if (forceDeleteOrIconify === false && this._iconifyBatch) return; this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false; var iconifyingCount = views.length; @@ -251,7 +219,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P iconView.props.removeDocument?.(iconView.props.Document); } }); - views.forEach(v => SelectionManager.DeselectView()); + views.forEach(SelectionManager.DeselectView); } this._iconifyBatch?.end(); this._iconifyBatch = undefined; @@ -267,21 +235,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (forceDeleteOrIconify) finished(forceDeleteOrIconify); else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished)); }; + onMaximizeDown = (e: React.PointerEvent) => { - setupMoveUpEvents( - this, - e, - () => { - DragManager.StartWindowDrag?.(e, [SelectionManager.Views().slice(-1)[0].rootDoc]); - return true; - }, - emptyFunction, - this.onMaximizeClick, - false, - false - ); + setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views().lastElement().rootDoc]) ?? false, emptyFunction, this.onMaximizeClick, false, false); + e.stopPropagation(); }; - onMaximizeClick = (e: any): void => { const selectedDocs = SelectionManager.Views(); if (selectedDocs.length) { @@ -319,63 +277,51 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P SelectionManager.DeselectAll(); }; - onSelectorClick = () => SelectionManager.Views()?.[0]?.props.docViewPath?.().lastElement()?.select(false); + onSelectContainerDocClick = () => SelectionManager.Views()?.[0]?.props.docViewPath?.().lastElement()?.select(false); /** - * Handles setting up events when user clicks on the border radius editor - * @param e PointerEvent + * sets up events when user clicks on the border radius editor */ @action onRadiusDown = (e: React.PointerEvent): void => { this._isRounding = DocumentView.Interacting = true; this._resizeUndo = UndoManager.StartBatch('DocDecs set radius'); - // Call util move event function setupMoveUpEvents( - this, // target - e, // pointerEvent - (e, down) => { - const x = this.Bounds.x + 3; - const y = this.Bounds.y + 3; + this, + e, + e => { + const [x, y] = [this.Bounds.x + 3, this.Bounds.y + 3]; const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); - let dist = Math.sqrt((e.clientX - x) * (e.clientX - x) + (e.clientY - y) * (e.clientY - y)); - if (e.clientX < x && e.clientY < y) dist = 0; - SelectionManager.Views() - .map(dv => dv.props.Document) - .map(doc => { - const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2); - const ratio = dist / maxDist; - const radius = Math.min(1, ratio) * docMax; - doc.layout_borderRounding = `${radius}px`; - }); + const dist = e.clientX < x && e.clientY < y ? 0 : Math.sqrt((e.clientX - x) * (e.clientX - x) + (e.clientY - y) * (e.clientY - y)); + SelectionManager.Docs().map(doc => { + const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2); + const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box + doc.layout_borderRounding = `${radius}px`; + }); return false; - }, // moveEvent + }, action(e => { DocumentView.Interacting = this._isRounding = false; this._resizeUndo?.end(); }), // upEvent - e => {}, // clickEvent, + emptyFunction, true ); + e.stopPropagation(); }; @action onLockDown = (e: React.PointerEvent): void => { - // Call util move event function setupMoveUpEvents( - this, // target - e, // pointerEvent - returnFalse, // moveEvent - emptyFunction, // upEvent - e => { - UndoManager.RunInBatch( - () => - SelectionManager.Views().map(dv => { - dv.rootDoc._lockedPosition = !dv.rootDoc._lockedPosition; - dv.rootDoc._pointerEvents = dv.rootDoc._lockedPosition ? 'none' : undefined; - }), - 'toggleBackground' - ); - } // clickEvent + this, + e, + returnFalse, // don't care about move or up event, + emptyFunction, // just care about whether we get a click event + e => UndoManager.RunInBatch( + () => SelectionManager.Docs().forEach(doc => + doc._pointerEvents = (doc._lockedPosition = !doc._lockedPosition)? 'none' : undefined ), + 'toggleBackground' ) // prettier-ignore ); + e.stopPropagation(); }; setRotateCenter = (seldocview: DocumentView, rotCenter: number[]) => { @@ -393,16 +339,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P setupMoveUpEvents( this, e, - action((e: PointerEvent, down: number[], delta: number[]) => { - this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]); - return false; - }), // moveEvent - action(action(() => (this._isRotating = false))), // upEvent - action((e, doubleTap) => { - seldocview.rootDoc.rotation_centerX = 0; - seldocview.rootDoc.rotation_centerY = 0; - }) - ); + (e: PointerEvent, down: number[], delta: number[]) => // return false to keep getting events + this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]) as any as boolean, + action(e => (this._isRotating = false)), // upEvent + action(e => (seldocview.rootDoc.rotation_centerX = seldocview.rootDoc.rotation_centerY = 0)) + ); // prettier-ignore }; @action @@ -473,6 +414,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P onPointerDown = (e: React.PointerEvent): void => { SnappingManager.SetIsResizing(SelectionManager.Docs().lastElement()); setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); + e.stopPropagation(); DocumentView.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them this._resizeHdlId = e.currentTarget.className; const bounds = e.currentTarget.getBoundingClientRect(); @@ -482,46 +424,44 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P SelectionManager.Views().forEach(docView => docView.CollectionFreeFormView?.dragStarting(false, false)); }; - _lock: any; + projectDragToAspect = (e: PointerEvent, docView: DocumentView, fixedAspect: number) => { + // need to generalize for bl and tr drag handles + const project = (p: number[], a: number[], b: number[]) => { + const atob = [b[0] - a[0], b[1] - a[1]]; + const atop = [p[0] - a[0], p[1] - a[1]]; + const len = atob[0] * atob[0] + atob[1] * atob[1]; + let dot = atop[0] * atob[0] + atop[1] * atob[1]; + const t = dot / len; + dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]); + return [a[0] + atob[0] * t, a[1] + atob[1] * t]; + }; + const tl = docView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + return project([e.clientX + this._offset.x, e.clientY + this._offset.y], tl, [tl[0] + fixedAspect, tl[1] + 1]); + }; onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { const first = SelectionManager.Views()[0]; const effectiveAcl = GetEffectiveAcl(first.rootDoc); if (!(effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment)) return false; if (!first) return false; - let thisPt = { x: e.clientX - this._offset.x, y: e.clientY - this._offset.y }; var fixedAspect = Doc.NativeAspect(first.layoutDoc); const dragHdl = this._resizeHdlId.split(' ')[0].replace('documentDecorations-', '').replace('Resizer', ''); - // do snapping of drag point - if (fixedAspect && (dragHdl === 'bottomRight' || dragHdl === 'topLeft')) { - // need to generalize for bl and tr drag handles - const project = (p: number[], a: number[], b: number[]) => { - const atob = [b[0] - a[0], b[1] - a[1]]; - const atop = [p[0] - a[0], p[1] - a[1]]; - const len = atob[0] * atob[0] + atob[1] * atob[1]; - let dot = atop[0] * atob[0] + atop[1] * atob[1]; - const t = dot / len; - dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]); - return [a[0] + atob[0] * t, a[1] + atob[1] * t]; - }; - const tl = first.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - const drag = project([e.clientX + this._offset.x, e.clientY + this._offset.y], tl, [tl[0] + fixedAspect, tl[1] + 1]); - thisPt = DragManager.snapDragAspect(drag, fixedAspect); - } else { - thisPt = DragManager.snapDrag(e, -this._offset.x, -this._offset.y, this._offset.x, this._offset.y); - } + const thisPt = // do snapping of drag point + fixedAspect && (dragHdl === 'bottomRight' || dragHdl === 'topLeft') + ? DragManager.snapDragAspect(this.projectDragToAspect(e, first, fixedAspect), fixedAspect) + : DragManager.snapDrag(e, -this._offset.x, -this._offset.y, this._offset.x, this._offset.y); const { scale, refPt } = this.getResizeVals(thisPt, dragHdl); - // resize selected docs - !this._lock && runInAction(async () => { - this._lock = true; + + !this._interactionLock && runInAction(async () => { // resize selected docs if we're not in the middle of a resize (ie, throttle input events to frame rate) + this._interactionLock = true; this._snapPt = thisPt; e.ctrlKey && (SelectionManager.Views().forEach(docView => !Doc.NativeHeight(docView.props.Document) && docView.toggleNativeDimensions())); const fixedAspect = SelectionManager.Docs().some(this.hasFixedAspect); + const scaleAspect = {x:scale.x === 1 && fixedAspect ? scale.y : scale.x, y: scale.x !== 1 && fixedAspect ? scale.x : scale.y}; SelectionManager.Views().forEach(docView => - this.resizeView(docView, refPt, scale.x === 1 && fixedAspect ? scale.y : scale.x, - scale.x !== 1 && fixedAspect ? scale.x : scale.y, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore - await new Promise<any>(res => setTimeout(() => res(this._lock = undefined))); + this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore + await new Promise<any>(res => setTimeout(() => res(this._interactionLock = undefined))); }); // prettier-ignore return false; @@ -554,16 +494,15 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P // // resize a single DocumentView about the specified reference point, possibly setting/updating the native dimensions of the Doc // - resizeView = (docView: DocumentView, refPt: number[], scaleX: number, scaleY: number, opts: { dragHdl: string; ctrlKey: boolean }) => { + resizeView = (docView: DocumentView, refPt: number[], scale: { x: number; y: number }, opts: { dragHdl: string; ctrlKey: boolean }) => { const doc = docView.rootDoc; if (doc.isGroup) { DocListCast(doc.data) .map(member => DocumentManager.Instance.getDocumentView(member, docView)!) - .forEach(member => this.resizeView(member, refPt, scaleX, scaleY, opts)); - doc.xPadding = NumCast(doc.xPadding) * scaleX; - doc.yPadding = NumCast(doc.yPadding) * scaleY; + .forEach(member => this.resizeView(member, refPt, scale, opts)); + doc.xPadding = NumCast(doc.xPadding) * scale.x; + doc.yPadding = NumCast(doc.yPadding) * scale.y; } else { - const doc = docView.rootDoc; const refCent = docView.props.ScreenToLocalTransform().transformPoint(refPt[0], refPt[1]); // fixed reference point for resize (ie, a point that doesn't move) const [nwidth, nheight] = [docView.nativeWidth, docView.nativeHeight]; const [initWidth, initHeight] = [NumCast(doc._width, 1), NumCast(doc._height)]; @@ -574,12 +513,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P (doc.layout_reflowVertical && (opts.dragHdl === 'bottom' || opts.dragHdl === 'top' || opts.ctrlKey)); // eg rtf, web, pdf if (nwidth && nheight && !modifyNativeDim) { // eg., dragging right resizer on PDF -- enforce native dimensions because not expliclty overridden with ctrl or bottom resize drag - scaleX === 1 ? (scaleX = scaleY) : (scaleY = scaleX); + scale.x === 1 ? (scale.x = scale.y) : (scale.x = scale.x); } if (['right', 'left'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeWidth(doc)) { const setData = Doc.NativeWidth(Doc.GetProto(doc)) === doc.nativeWidth; - doc.nativeWidth = scaleX * Doc.NativeWidth(doc); + doc.nativeWidth = scale.x * Doc.NativeWidth(doc); if (setData) Doc.SetNativeWidth(Doc.GetProto(doc), NumCast(doc.nativeWidth)); if (doc.layout_reflowVertical && !NumCast(doc.nativeHeight)) { doc._nativeHeight = (initHeight / initWidth) * nwidth; // initializes the nativeHeight for a PDF @@ -587,18 +526,18 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } if (['bottom', 'top'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeHeight(doc)) { const setData = Doc.NativeHeight(Doc.GetProto(doc)) === doc.nativeHeight; - doc._nativeHeight = scaleY * Doc.NativeHeight(doc); + doc._nativeHeight = scale.y * Doc.NativeHeight(doc); if (setData) Doc.SetNativeHeight(Doc.GetProto(doc), NumCast(doc._nativeHeight)); } - doc._width = NumCast(doc._width) * scaleX; - doc._height = NumCast(doc._height) * scaleY; + doc._width = NumCast(doc._width) * scale.x; + doc._height = NumCast(doc._height) * scale.y; const { deltaX, deltaY } = this.realignRefPt(doc, refCent, initWidth, initHeight); doc.x = NumCast(doc.x) + deltaX; doc.y = NumCast(doc.y) + deltaY; doc._layout_modificationDate = new DateField(); - scaleY !== 1 && (doc._layout_autoHeight = undefined); + scale.y !== 1 && (doc._layout_autoHeight = undefined); } }; @@ -628,10 +567,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onPointerUp = (e: PointerEvent): void => { SnappingManager.SetIsResizing(undefined); - this._resizeHdlId = ''; + SnappingManager.clearSnapLines(); DocumentView.Interacting = false; + this._resizeHdlId = ''; this._resizeUndo?.end(); - SnappingManager.clearSnapLines(); // detect layout_autoHeight gesture and apply SelectionManager.Docs().forEach(doc => NumCast(doc._height) < 20 && (doc._layout_autoHeight = true)); @@ -639,16 +578,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P this._inkDragDocs .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] })) .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => { - Doc.GetProto(doc).data = new InkField( - inkPts.map( - ( - ipt // (new x — oldx) + newWidth * (oldxpoint /oldWidth) - ) => ({ - X: NumCast(doc.x) - x + (NumCast(doc.width) * ipt.X) / width, - Y: NumCast(doc.y) - y + (NumCast(doc.height) * ipt.Y) / height, - }) - ) - ); + Doc.GetProto(doc).data = new InkField(inkPts.map( + (ipt) => ({// (new x — oldx) + newWidth * (oldxpoint /oldWidth) + X: NumCast(doc.x) - x + (NumCast(doc.width) * ipt.X) / width, + Y: NumCast(doc.y) - y + (NumCast(doc.height) * ipt.Y) / height, + }))); // prettier-ignore Doc.SetNativeWidth(doc, undefined); Doc.SetNativeHeight(doc, undefined); }); @@ -676,8 +610,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P return SelectionManager.Views().some(docView => docView.rootDoc.layout_fieldKey === 'layout_icon'); } - @observable _showRotCenter = false; - @observable _rotCenter = [0, 0]; @computed get rotCenter() { if (SelectionManager.Views().length) { const seldocview = SelectionManager.Views()[0]; @@ -694,19 +626,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P return this._rotCenter; } - @observable _showNothing = true; - render() { const { b, r, x, y } = this.Bounds; - const bounds = { b, r, x, y }; const seldocview = SelectionManager.Views().lastElement(); - if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { + if (SnappingManager.GetIsDragging() || r - x < 1 || x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(r) || isNaN(b) || isNaN(x) || isNaN(y)) { setTimeout(action(() => (this._showNothing = true))); return null; } // sharing - const acl = GetEffectiveAcl(!this.showLayoutAcl ? Doc.GetProto(seldocview.rootDoc) : seldocview.rootDoc); + const acl = GetEffectiveAcl(!this._showLayoutAcl ? Doc.GetProto(seldocview.rootDoc) : seldocview.rootDoc); const docShareMode = HierarchyMapping.get(acl)!.name; const shareMode = StrCast(docShareMode); var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image; @@ -745,6 +674,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P </Tooltip> ); + const bounds = { b, r, x, y }; const leftBounds = this.props.boundsLeft; const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop; bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2; @@ -757,8 +687,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const useRotation = !hideResizers && seldocview.rootDoc.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate? const rotation = SelectionManager.Views().length == 1 ? NumCast(seldocview.rootDoc._rotation) : 0; - const resizerScheme = ''; - // Radius constants const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView; const borderRadius = numberValue(Cast(seldocview.rootDoc.layout_borderRounding, 'string', null)); @@ -801,7 +729,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P onPointerDown={e => e.stopPropagation()} /> ) : ( - <div className="documentDecorations-title" key="title" onPointerDown={e => e.stopPropagation}> + <div className="documentDecorations-title" key="title"> {hideTitle ? null : ( <span className="documentDecorations-titleSpan" onPointerDown={this.onTitleDown}> {this.selectionTitle} @@ -810,7 +738,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P {sharingMenu} {!useLock ? null : ( <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top"> - <div className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown} onContextMenu={e => e.preventDefault()}> + <div className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown}> <FontAwesomeIcon size="sm" icon="lock" /> </div> </Tooltip> @@ -827,30 +755,24 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P height: bounds.b - bounds.y + this._resizeBorderWidth + 'px', left: bounds.x - this._resizeBorderWidth / 2, top: bounds.y - this._resizeBorderWidth / 2, - pointerEvents: DocumentDecorations.Instance.AddToSelection || DocumentView.Interacting ? 'none' : 'all', + background: SnappingManager.GetShiftKey() ? undefined : 'yellow', + pointerEvents: SnappingManager.GetShiftKey() || DocumentView.Interacting ? 'none' : 'all', display: SelectionManager.Views().length <= 1 || hideDecorations ? 'none' : undefined, }} onPointerDown={this.onBackgroundDown} - onContextMenu={e => { - e.preventDefault(); - e.stopPropagation(); - }} /> {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? null : ( <div> <div className={`documentDecorations-container ${this._showNothing ? 'showNothing' : ''}`} - key="container" style={{ transform: `translate(${bounds.x - this._resizeBorderWidth / 2}px, ${bounds.y - this._resizeBorderWidth / 2 - this._titleHeight}px) rotate(${rotation}deg)`, - transformOrigin: `50% calc(50% + 10px)`, width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', height: bounds.b - bounds.y + this._resizeBorderWidth + (this._showNothing ? 0 : this._titleHeight) + 'px', }}> <div className="documentDecorations-topbar" style={{ - color: 'black', display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined, }} onPointerDown={this.onContainerDown}> @@ -861,37 +783,34 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P </div> {hideResizers ? null : ( <> - <div key="tl" className={`documentDecorations-topLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="t" className={`documentDecorations-topResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="tr" className={`documentDecorations-topRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="l" className={`documentDecorations-leftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="c" className={`documentDecorations-centerCont ${resizerScheme}`}></div> - <div key="r" className={`documentDecorations-rightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="bl" className={`documentDecorations-bottomLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - <div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> - - {seldocview.props.renderDepth <= 1 || !seldocview.props.docViewPath().lastElement() ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')} + <div key="tl" className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} /> + <div key="t" className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} /> + <div key="tr" className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} /> + <div key="l" className="documentDecorations-leftResizer" onPointerDown={this.onPointerDown} /> + <div key="c" className="documentDecorations-centerCont" /> + <div key="r" className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} /> + <div key="bl" className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} /> + <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} /> + <div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} /> + {seldocview.props.renderDepth <= 1 || !seldocview.props.docViewPath().lastElement() + ? null + : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectContainerDocClick, 'tap to select containing document')} </> )} {useRounding && ( <div - key="rad" className="documentDecorations-borderRadius" style={{ - opacity: 0.5, background: `${this._isRounding ? Colors.MEDIUM_BLUE : SettingsManager.userColor}`, transform: `translate(${radiusHandleLocation ?? 0}px, ${(radiusHandleLocation ?? 0) + (this._showNothing ? 0 : this._titleHeight)}px)`, }} onPointerDown={this.onRadiusDown} - onContextMenu={e => e.preventDefault()} /> )} {hideDocumentButtonBar || this._showNothing ? null : ( <div className="link-button-container" - key="links" style={{ transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `, }}> diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 13791b763..87f81fe76 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -51,12 +51,18 @@ export class KeyManager { public unhandle = action((e: KeyboardEvent) => { e.key === 'Control' && (CtrlKey = false); - if (e.key?.toLowerCase() === 'shift') runInAction(() => (DocumentDecorations.Instance.AddToSelection = false)); + }); + public handleModifiers = action((e: KeyboardEvent) => { + if (e.shiftKey) SnappingManager.SetShiftKey(true); + if (e.ctrlKey) SnappingManager.SetCtrlKey(true); + }); + public unhandleModifiers = action((e: KeyboardEvent) => { + if (!e.shiftKey) SnappingManager.SetShiftKey(false); + if (!e.ctrlKey) SnappingManager.SetCtrlKey(false); }); public handle = action((e: KeyboardEvent) => { e.key === 'Control' && (CtrlKey = true); - if (e.key?.toLowerCase() === 'shift') DocumentDecorations.Instance.AddToSelection = true; //if (!Doc.noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true); const keyname = e.key && e.key.toLowerCase(); this.handleGreedy(keyname); @@ -94,28 +100,21 @@ export class KeyManager { switch (keyname) { case 'u': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - const ungroupings = SelectionManager.Views().slice(); + const ungroupings = SelectionManager.Views(); UndoManager.RunInBatch(() => ungroupings.map(dv => (dv.layoutDoc.group = undefined)), 'ungroup'); SelectionManager.DeselectAll(); } break; case 'g': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - const groupings = SelectionManager.Views().slice(); - const randomGroup = random(0, 1000); - const collectionView = groupings.reduce((col, g) => (col === null || g.CollectionFreeFormView === col ? g.CollectionFreeFormView : undefined), null as null | undefined | CollectionFreeFormView); + const selected = SelectionManager.Views(); + const collectionView = selected.reduce((col, dv) => (col === null || dv.CollectionFreeFormView === col ? dv.CollectionFreeFormView : undefined), null as null | undefined | CollectionFreeFormView); if (collectionView) { - UndoManager.RunInBatch(() => { - collectionView._marqueeViewRef.current?.collection( - e, - true, - groupings.map(g => g.rootDoc) - ); - }, 'grouping'); + UndoManager.RunInBatch(() => + collectionView._marqueeViewRef.current?.collection(e, true, SelectionManager.Docs()) + , 'grouping'); break; } - UndoManager.RunInBatch(() => groupings.map(dv => (dv.layoutDoc.group = randomGroup)), 'group'); - SelectionManager.DeselectAll(); } break; case ' ': @@ -183,11 +182,16 @@ export class KeyManager { case 'arrowright': return this.nudge(10, 0, 'nudge right'); case 'arrowup': return this.nudge(0, -10, 'nudge up'); case 'arrowdown': return this.nudge(0, 10, 'nudge down'); + case 'u' : + if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { + UndoManager.RunInBatch(() => SelectionManager.Docs().forEach(doc => (doc.group = undefined)), 'unggroup'); + SelectionManager.DeselectAll(); + } + break; case 'g': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - const groupings = SelectionManager.Views().slice(); const randomGroup = random(0, 1000); - UndoManager.RunInBatch(() => groupings.map(dv => (dv.layoutDoc.group = randomGroup)), 'group'); + UndoManager.RunInBatch(() => SelectionManager.Docs().forEach(doc => (doc.group = randomGroup)), 'group'); SelectionManager.DeselectAll(); } break; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5bed0f923..f40f1f3e8 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -4,7 +4,7 @@ import * as far from '@fortawesome/free-regular-svg-icons'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import 'browndash-components/dist/styles/global.min.css'; -import { action, computed, configure, observable, runInAction } from 'mobx'; +import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import 'normalize.css'; import * as React from 'react'; @@ -143,6 +143,11 @@ export class MainView extends React.Component { mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight(); componentDidMount() { + reaction( + // when a multi-selection occurs, remove focus from all active elements to allow keyboad input to go only to global key manager to act upon selection + () => SelectionManager.Views().slice(), + views => views.length > 1 && (document.activeElement as any)?.blur !== undefined && (document.activeElement as any)!.blur() + ); const scriptTag = document.createElement('script'); scriptTag.setAttribute('type', 'text/javascript'); scriptTag.setAttribute('src', 'https://www.bing.com/api/maps/mapcontrol?callback=makeMap'); @@ -196,6 +201,10 @@ export class MainView extends React.Component { tag.src = 'https://www.youtube.com/iframe_api'; const firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); + window.removeEventListener('keydown', KeyManager.Instance.handleModifiers, true); + window.addEventListener('keydown', KeyManager.Instance.handleModifiers, true); + window.removeEventListener('keyup', KeyManager.Instance.unhandleModifiers); + window.addEventListener('keyup', KeyManager.Instance.unhandleModifiers); window.removeEventListener('keydown', KeyManager.Instance.handle); window.addEventListener('keydown', KeyManager.Instance.handle); window.removeEventListener('keyup', KeyManager.Instance.unhandle); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 9c644e2cc..f155e64b5 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -85,6 +85,7 @@ export class CollectionDockingView extends CollectionSubView() { const dragSource = CollectionDockingView.Instance?._goldenLayout.createDragSource(document.createElement('div'), config); this.tabDragStart(dragSource, finishDrag); dragSource._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }); + return true; }; tabItemDropped = () => DragManager.CompleteWindowDrag?.(false); diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 7c61bc4da..584098d35 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -270,7 +270,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack !wasPlaying && doubleTap && this.props.Play(); } }, - this.props.isSelected(true) || this.props.isContentActive(), + this.props.isSelected() || this.props.isContentActive(), undefined, () => { if (shiftKey) { @@ -681,7 +681,7 @@ interface StackedTimelineAnchorProps { _timeline: HTMLDivElement | null; focus: DocFocusFunc; currentTimecode: () => number; - isSelected: (outsideReaction?: boolean) => boolean; + isSelected: () => boolean; stackedTimeline: CollectionStackedTimeline; trimStart: number; trimEnd: number; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index f70c85dcf..328b060c4 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -60,9 +60,7 @@ export function CollectionSubView<X>(moreProps?: X) { return this.props.DataDoc instanceof Doc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) : this.props.Document.resolvedDataDoc ? this.props.Document : Doc.GetProto(this.props.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template } - rootSelected = (outsideReaction?: boolean) => { - return this.props.isSelected(outsideReaction) || (this.rootDoc && this.props.rootSelected(outsideReaction)); - }; + rootSelected = () => this.props.isSelected() || (this.rootDoc && this.props.rootSelected()); // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc. // When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2801c1a4a..ba4e30a9b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -104,10 +104,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection return (this.props.viewField ?? '') + '_freeform_autoReset'; } - @computed get _layoutElements(): ViewDefResult[] { - const computation = this.doInternalLayoutComputation; - return this.doLayoutComputation(computation.newPool, computation.computedElementData); - } + @observable _layoutElements: ViewDefResult[] = []; @observable _panZoomTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0 @observable _firstRender = false; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown. could be used for performance improvement @observable _showAnimTimeline = false; @@ -169,7 +166,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @computed get screenToLocalXf() { return this.props .ScreenToLocalTransform() - .scale(this.props.isAnnotationOverlay ? 1 : 1 / this.nativeDim()) + .scale(this.props.isAnnotationOverlay ? 1 : 1) .translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY) .transform(this.panZoomXf); } @@ -551,7 +548,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } @action - updateCluster(doc: Doc) { + updateCluster = (doc: Doc) => { const childLayouts = this.childLayoutPairs.map(pair => pair.layout); if (this.props.Document._freeform_useClusters) { this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)); @@ -580,7 +577,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this._clusterSets[doc.layout_cluster ?? 0].push(doc); } } - } + }; clusterStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => { let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1 @@ -1186,7 +1183,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ? 'none' : this.props.childPointerEvents?.() ?? (this.props.viewDefDivClick || // - (engine === computePassLayout.name && !this.props.isSelected(true)) || + (engine === computePassLayout.name && !this.props.isSelected()) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.()); @@ -1471,7 +1468,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ? 'none' : this.props.childPointerEvents?.() ?? (this.props.viewDefDivClick || // - (engine === computePassLayout.name && !this.props.isSelected(true)) || + (engine === computePassLayout.name && !this.props.isSelected()) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.()); @@ -1486,6 +1483,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ); }) ); + this._disposers.layoutElements = reaction( + // layoutElements can't be a computed value because doLayoutComputation() is an action that has side effect of updating clusters + () => this.doInternalLayoutComputation, + computation => (this._layoutElements = this.doLayoutComputation(computation.newPool, computation.computedElementData)), + { fireImmediately: true } + ); } static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) { @@ -1570,7 +1573,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } componentWillUnmount() { - this.rootDoc[this.autoResetFieldKey] && this.resetView() + this.rootDoc[this.autoResetFieldKey] && this.resetView(); Object.values(this._disposers).forEach(disposer => disposer?.()); } diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index cd8b7a0cc..274012000 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -359,7 +359,7 @@ export class CollectionGridView extends CollectionSubView() { }, false ); - if (this.props.isSelected(true)) e.stopPropagation(); + if (this.props.isSelected()) e.stopPropagation(); } }; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index c4703a47c..8c8c987fe 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -119,7 +119,7 @@ export class HTMLtag extends React.Component<HTMLtagProps> { @observer export class DocumentContentsView extends React.Component< DocumentViewProps & { - isSelected: (outsideReaction: boolean) => boolean; + isSelected: () => boolean; select: (ctrl: boolean) => void; NativeDimScaling?: () => number; setHeight?: (height: number) => void; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e3d6c3fdf..a61396275 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -176,7 +176,7 @@ export interface DocumentViewSharedProps { searchFilterDocs: () => Doc[]; layout_showTitle?: () => string; whenChildContentsActiveChanged: (isActive: boolean) => void; - rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected + rootSelected: () => boolean; // whether the root of a template has been selected addDocTab: (doc: Doc, where: OpenWhere) => boolean; filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; @@ -243,7 +243,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps { export interface DocumentViewInternalProps extends DocumentViewProps { NativeWidth: () => number; NativeHeight: () => number; - isSelected: (outsideReaction?: boolean) => boolean; + isSelected: () => boolean; select: (ctrlPressed: boolean, shiftPress?: boolean) => void; DocumentView: () => DocumentView; viewPath: () => DocumentView[]; @@ -738,7 +738,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps if (e && !(e.nativeEvent as any).dash) { const onDisplay = () => { - if (this.rootDoc.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear. + if (this.rootDoc.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected() && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear. setTimeout(() => simulateMouseClick(document.elementFromPoint(e.clientX, e.clientY), e.clientX, e.clientY, e.screenX, e.screenY)); }; if (navigator.userAgent.includes('Macintosh')) { @@ -902,7 +902,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps }; @computed get _rootSelected() { - return this.props.isSelected(false) || (this.props.Document.rootDocument && this.props.rootSelected?.(false)) || false; + return this.props.isSelected() || (this.props.Document.rootDocument && this.props.rootSelected?.()) || false; } rootSelected = () => this._rootSelected; panelHeight = () => this.props.PanelHeight() - this.headerMargin; @@ -1598,13 +1598,16 @@ export class DocumentView extends React.Component<DocumentViewProps> { docViewPathFunc = () => this.docViewPath; isSelected = () => SelectionManager.IsSelected(this); select = (extendSelection: boolean, focusSelection?: boolean) => { - SelectionManager.SelectView(this, extendSelection); - if (focusSelection) { - DocumentManager.Instance.showDocument(this.rootDoc, { - willZoomCentered: true, - zoomScale: 0.9, - zoomTime: 500, - }); + if (this.isSelected() && SelectionManager.Views().length > 1) SelectionManager.DeselectView(this); + else { + SelectionManager.SelectView(this, extendSelection); + if (focusSelection) { + DocumentManager.Instance.showDocument(this.rootDoc, { + willZoomCentered: true, + zoomScale: 0.9, + zoomTime: 500, + }); + } } }; NativeWidth = () => this.effectiveNativeWidth; diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx index d8b8bcb27..d347c285b 100644 --- a/src/client/views/nodes/EquationBox.tsx +++ b/src/client/views/nodes/EquationBox.tsx @@ -84,8 +84,18 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() { updateSize = () => { const style = this._ref.current && getComputedStyle(this._ref.current.element.current); if (style?.width.endsWith('px') && style?.height.endsWith('px')) { - this.layoutDoc._width = Math.max(35, Number(style.width.replace('px', ''))); - this.layoutDoc._height = Math.max(25, Number(style.height.replace('px', ''))); + if (this.layoutDoc._nativeWidth) { + // if equation has been scaled then editing the expression must also edit the native dimensions to keep the aspect ratio + const prevNwidth = NumCast(this.layoutDoc._nativeWidth); + const prevNheight = NumCast(this.layoutDoc._nativeHeight); + this.layoutDoc._nativeWidth = Math.max(35, Number(style.width.replace('px', ''))); + this.layoutDoc._nativeHeight = Math.max(25, Number(style.height.replace('px', ''))); + this.layoutDoc._width = (NumCast(this.layoutDoc._width) * NumCast(this.layoutDoc._nativeWidth)) / prevNwidth; + this.layoutDoc._height = (NumCast(this.layoutDoc._height) * NumCast(this.layoutDoc._nativeHeight)) / prevNheight; + } else { + this.layoutDoc._width = Math.max(35, Number(style.width.replace('px', ''))); + this.layoutDoc._height = Math.max(25, Number(style.height.replace('px', ''))); + } } }; render() { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index f014f842e..f7f94c546 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -21,7 +21,7 @@ export interface FieldViewProps extends DocumentViewSharedProps { select: (isCtrlPressed: boolean) => void; isContentActive: (outsideReaction?: boolean) => boolean | undefined; isDocumentActive?: () => boolean | undefined; - isSelected: (outsideReaction?: boolean) => boolean; + isSelected: () => boolean; setHeight?: (height: number) => void; NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to DocumentViewInternalsProps onBrowseClick?: () => ScriptField | undefined; diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx index 7af4d9b59..f6680aac0 100644 --- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx @@ -48,7 +48,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> { componentDidMount() { this._disposer = reaction( () => SelectionManager.Views().slice(), - selected => MapAnchorMenu.Instance.fadeOut(true) + sel => MapAnchorMenu.Instance.fadeOut(true) ); } // audioDown = (e: React.PointerEvent) => { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 185c6553a..108fa5ce5 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -195,7 +195,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps () => this.props.isSelected(), () => { document.removeEventListener('keydown', this.onKeyDown); - this.props.isSelected(true) && document.addEventListener('keydown', this.onKeyDown); + this.props.isSelected() && document.addEventListener('keydown', this.onKeyDown); }, { fireImmediately: true } ); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index a80c1e030..c828297b3 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -315,7 +315,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), () => this.getAnchor(true), targetCreator), e.pageX, e.pageY); }); const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to); - this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); + this.props.isSelected() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); }; dispatchTransaction = (tx: Transaction) => { @@ -1439,7 +1439,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const self = this; return new Plugin({ view(newView) { - runInAction(() => self.props.isSelected(true) && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView)); + runInAction(() => self.props.isSelected() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView)); return new RichTextMenuPlugin({ editorProps: this.props }); }, }); @@ -1612,7 +1612,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this._downX = e.clientX; this._downY = e.clientY; FormattedTextBoxComment.textBox = this; - if (e.button === 0 && (this.props.rootSelected(true) || this.props.isSelected(true)) && !e.altKey && !e.ctrlKey && !e.metaKey) { + if (e.button === 0 && (this.props.rootSelected() || this.props.isSelected()) && !e.altKey && !e.ctrlKey && !e.metaKey) { if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar, otherwise nested boxes will lose focus to outer boxes. e.stopPropagation(); // if the text box's content is active, then it consumes all down events @@ -1649,7 +1649,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps @action onDoubleClick = (e: React.MouseEvent): void => { FormattedTextBoxComment.textBox = this; - if (e.button === 0 && this.props.isSelected(true) && !e.altKey && !e.ctrlKey && !e.metaKey) { + if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar e.stopPropagation(); // if the text box is selected, then it consumes all click events @@ -1660,7 +1660,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } FormattedTextBoxComment.Hide(); - if (e.buttons === 1 && this.props.isSelected(true) && !e.altKey) { + if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { e.stopPropagation(); } }; @@ -1706,7 +1706,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this._editorView!.dispatch(this._editorView!.state.tr.setSelection(NodeSelection.create(this._editorView!.state.doc, pcords.pos))); } } - if (this.props.isSelected(true)) { + if (this.props.isSelected()) { // if text box is selected, then it consumes all click events (e.nativeEvent as any).handledByInnerReactInstance = true; this.hitBulletTargets(e.clientX, e.clientY, !this._editorView?.state.selection.empty || this._forceUncollapse, false, this._forceDownNode, e.shiftKey); @@ -1720,7 +1720,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps clearStyleSheetRules(FormattedTextBox._bulletStyleSheet); const clickPos = this._editorView!.posAtCoords({ left: x, top: y }); let olistPos = clickPos?.pos; - if (clickPos && olistPos && this.props.isSelected(true)) { + if (clickPos && olistPos && this.props.isSelected()) { const clickNode = this._editorView?.state.doc.nodeAt(olistPos); const nodeBef = this._editorView?.state.doc.nodeAt(Math.max(0, olistPos - 1)); olistPos = nodeBef?.type === this._editorView?.state.schema.nodes.ordered_list ? olistPos - 1 : olistPos; @@ -1769,7 +1769,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps tr && this._editorView.dispatch(tr); } } - if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected(true)) { + if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected()) { RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined); } FormattedTextBox._hadSelection = window.getSelection()?.toString() !== ''; diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index e3ac4fb9d..712be91c8 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -181,7 +181,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { // finds font sizes and families in selection getActiveAlignment() { - if (this.view && this.TextView?.props.isSelected(true)) { + if (this.view && this.TextView?.props.isSelected()) { const path = (this.view.state.selection.$from as any).path; for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) { if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) { @@ -194,7 +194,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { // finds font sizes and families in selection getActiveListStyle() { - if (this.view && this.TextView?.props.isSelected(true)) { + if (this.view && this.TextView?.props.isSelected()) { const path = (this.view.state.selection.$from as any).path; for (let i = 0; i < path.length; i += 3) { if (path[i].type === this.view.state.schema.nodes.ordered_list) { @@ -214,7 +214,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { const activeSizes = new Set<string>(); const activeColors = new Set<string>(); const activeHighlights = new Set<string>(); - if (this.view && this.TextView?.props.isSelected(true)) { + if (this.view && this.TextView?.props.isSelected()) { const state = this.view.state; const pos = this.view.state.selection.$from; const marks: Mark[] = [...(state.storedMarks ?? [])]; @@ -249,7 +249,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { //finds all active marks on selection in given group getActiveMarksOnSelection() { let activeMarks: MarkType[] = []; - if (!this.view || !this.TextView?.props.isSelected(true)) return activeMarks; + if (!this.view || !this.TextView?.props.isSelected()) return activeMarks; const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript]; if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type); @@ -286,7 +286,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { } destroy() { - !this.TextView?.props.isSelected(true) && this.fadeOut(true); + !this.TextView?.props.isSelected() && this.fadeOut(true); } @action @@ -443,7 +443,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { } align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => { - if (this.TextView?.props.isSelected(true)) { + if (this.TextView?.props.isSelected()) { var tr = view.state.tr; view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => { if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) { diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 18cf633f4..d68859e88 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -64,7 +64,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { componentDidMount() { this._disposer = reaction( () => SelectionManager.Views().slice(), - selected => AnchorMenu.Instance.fadeOut(true) + sel => AnchorMenu.Instance.fadeOut(true) ); } |