diff options
Diffstat (limited to 'src/client/views')
| -rw-r--r-- | src/client/views/DocumentDecorations.tsx | 312 | ||||
| -rw-r--r-- | src/client/views/InkControlPtHandles.tsx | 7 | ||||
| -rw-r--r-- | src/client/views/MainView.tsx | 1 | ||||
| -rw-r--r-- | src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/global/globalScripts.ts | 14 | ||||
| -rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/DataVizBox.tsx | 10 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 4 | ||||
| -rw-r--r-- | src/client/views/nodes/PDFBox.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.tsx | 13 | ||||
| -rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 6 |
12 files changed, 167 insertions, 208 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 33886e791..8df5740fa 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -2,18 +2,17 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { IconButton } from 'browndash-components'; -import { action, computed, observable, reaction } from 'mobx'; +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'; import { RichTextField } from '../../fields/RichTextField'; import { ScriptField } from '../../fields/ScriptField'; -import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { GetEffectiveAcl } from '../../fields/util'; -import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils'; +import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils'; import { Docs } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; import { DocumentManager } from '../util/DocumentManager'; @@ -36,6 +35,7 @@ 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 }> { @@ -46,11 +46,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P private _linkBoxHeight = 20 + 3; // link button height + margin private _titleHeight = 20; private _resizeUndo?: UndoManager.Batch; - private _offX = 0; - private _offY = 0; // offset from click pt to inner edge of resize border - private _snapX = 0; - private _snapY = 0; // last snapped location of resize border - private _dragHeights = new Map<Doc, { start: number; lowest: number }>(); + 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 }[] = []; @observable private _accumulatedTitle = ''; @@ -479,29 +476,24 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P 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(); - this._offX = this._resizeHdlId.toLowerCase().includes('left') ? bounds.right - e.clientX : bounds.left - e.clientX; - this._offY = this._resizeHdlId.toLowerCase().includes('top') ? bounds.bottom - e.clientY : bounds.top - e.clientY; + this._offset = { x: this._resizeHdlId.toLowerCase().includes('left') ? bounds.right - e.clientX : bounds.left - e.clientX, y: this._resizeHdlId.toLowerCase().includes('top') ? bounds.bottom - e.clientY : bounds.top - e.clientY }; this._resizeUndo = UndoManager.StartBatch('drag resizing'); - this._snapX = e.pageX; - this._snapY = e.pageY; - const ffviewSet = new Set<CollectionFreeFormView>(); - SelectionManager.Views().forEach(docView => { - docView.CollectionFreeFormView && ffviewSet.add(docView.CollectionFreeFormView); - this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) }); - }); - Array.from(ffviewSet).map(ffview => ffview.dragStarting(false, false)); + this._snapPt = { x: e.pageX, y: e.pageY }; + SelectionManager.Views().forEach(docView => docView.CollectionFreeFormView?.dragStarting(false, false)); }; + _lock: any; 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._offX, y: e.clientY - this._offY }; + 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', ''); - const resizeHdl = this._resizeHdlId.split(' ')[0]; - if (fixedAspect && (resizeHdl === 'documentDecorations-bottomRightResizer' || resizeHdl === 'documentDecorations-topLeftResizer')) { + // 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]]; @@ -513,173 +505,124 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P 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._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1]); + 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._offX, -this._offY, this._offX, this._offY); + thisPt = DragManager.snapDrag(e, -this._offset.x, -this._offset.y, this._offset.x, this._offset.y); } - move[0] = thisPt.x - this._snapX; - move[1] = thisPt.y - this._snapY; - this._snapX = thisPt.x; - this._snapY = thisPt.y; - let dragBottom = false, - dragRight = false, - dragBotRight = false, - dragTop = false; - let dXin = 0, - dYin = 0, - dWin = 0, - dHin = 0; - switch (this._resizeHdlId.split(' ')[0]) { - case '': - break; - case 'documentDecorations-topLeftResizer': - dXin = -1; - dYin = -1; - dWin = -move[0]; - dHin = -move[1]; - break; - case 'documentDecorations-topRightResizer': - dWin = move[0]; - dYin = -1; - dHin = -move[1]; - break; - case 'documentDecorations-topResizer': - dYin = -1; - dHin = -move[1]; - dragTop = true; - break; - case 'documentDecorations-bottomLeftResizer': - dXin = -1; - dWin = -move[0]; - dHin = move[1]; - break; - case 'documentDecorations-bottomRightResizer': - dWin = move[0]; - dHin = move[1]; - dragBotRight = true; - break; - case 'documentDecorations-bottomResizer': - dHin = move[1]; - dragBottom = true; - break; - case 'documentDecorations-leftResizer': - dXin = -1; - dWin = -move[0]; - break; - case 'documentDecorations-rightResizer': - dWin = move[0]; - dragRight = true; - break; - } + const { scale, refPt } = this.getResizeVals(thisPt, dragHdl); + // resize selected docs + !this._lock && runInAction(async () => { + this._lock = true; + this._snapPt = thisPt; + e.ctrlKey && (SelectionManager.Views().forEach(docView => !Doc.NativeHeight(docView.props.Document) && docView.toggleNativeDimensions())); + const fixedAspect = SelectionManager.Docs().some(this.hasFixedAspect); + 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))); + }); // prettier-ignore - const isGroup = first.rootDoc._isGroup ? first.rootDoc : undefined; - const scaleViews = isGroup ? DocListCast(isGroup.data).map(doc => DocumentManager.Instance.getFirstDocumentView(doc)!) : SelectionManager.Views(); - const aggBounds = aggregateBounds(scaleViews.map(view => view.rootDoc) as any, 0, 0); - const refWidth = aggBounds.r - aggBounds.x; - const refHeight = aggBounds.b - aggBounds.y; - const scaleRefPt = first.props - .ScreenToLocalTransform() - .inverse() - .transformPoint( - NumCast(isGroup?._xPadding) + (dXin ? refWidth : 0), // - NumCast(isGroup?._yPadding) + (dYin ? refHeight : 0) - ); - scaleViews.forEach( - action((docView: DocumentView) => { - if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions(); - if (dXin !== 0 || dYin !== 0 || dWin !== 0 || dHin !== 0) { - const doc = docView.rootDoc; - const refCent = docView.props.ScreenToLocalTransform().transformPoint(scaleRefPt[0], scaleRefPt[1]); - - if (doc.nativeHeightUnfrozen && !NumCast(doc.nativeHeight) && doc._nativeWidth !== undefined) { - doc._nativeHeight = (NumCast(doc._height) / NumCast(doc._width, 1)) * docView.nativeWidth; - } - const nwidth = docView.nativeWidth; - const nheight = docView.nativeHeight; - const docwidth = NumCast(doc._width); - let docheight = (hgt => (!hgt || isNaN(hgt) ? 20 : hgt))(NumCast(doc._height) || (nheight / nwidth) * docwidth); - let dW = docwidth * (dWin / refWidth); - let dH = docheight * (dHin / refHeight); - const scale = docView.props.ScreenToLocalTransform().Scale; - const modifyNativeDim = (e.ctrlKey && doc.nativeDimModifiable) || (doc.layout_forceReflow && !dragBottom && !dragTop) || (doc.nativeHeightUnfrozen && (dragBottom || dragTop || e.ctrlKey)); - if (nwidth && nheight) { - if (nwidth / nheight !== docwidth / docheight && !dragBottom && !dragTop) { - docheight = (nheight / nwidth) * docwidth; - } - if (modifyNativeDim && !dragBottom && !dragTop) { - // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction - if (Math.abs(dW) > Math.abs(dH)) dH = (dW * nheight) / nwidth; - else dW = (dH * nwidth) / nheight; - } - } - let actualdW = Math.max(docwidth + dW * scale, 20); - let actualdH = Math.max(docheight + dH * scale, 20); - let dX = !dWin ? 0 : (scale * refCent[0] * -dWin) / refWidth; - let dY = !dHin ? 0 : (scale * refCent[1] * -dHin) / refHeight; - const preserveNativeDim = !doc._nativeHeightUnfrozen && !doc._nativeDimModifiable; - const fixedAspect = nwidth && nheight && (!doc._layout_fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable); - if (fixedAspect) { - if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) { - if (dragRight && modifyNativeDim) { - if (Doc.NativeWidth(doc)) { - doc._nativeWidth = (actualdW / (docwidth || 1)) * Doc.NativeWidth(doc); - } - } else { - if (!doc._layout_fitWidth || preserveNativeDim) { - actualdH = (nheight / nwidth) * actualdW; - dYin && (dY = -dW * scale * (nheight / nwidth)); - doc._height = actualdH; - } else if (!modifyNativeDim || dragBotRight) { - doc._height = actualdH; - } - } - doc._width = actualdW; - } else { - if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._layout_fitWidth))) { - // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height. But they are marked to allow their nativeHeight - // to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match - // a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used) - doc._nativeHeight = (actualdH / (docheight || 1)) * Doc.NativeHeight(doc); - doc._layout_autoHeight = false; - } else { - if (!doc._layout_fitWidth || preserveNativeDim) { - actualdW = (nwidth / nheight) * actualdH; - dXin && (dX = -dH * scale * (nwidth / nheight)); - doc._width = actualdW; - } else if (!modifyNativeDim || dragBotRight) { - doc._width = actualdW; - } - } - if (!modifyNativeDim) { - actualdH = (nheight / nwidth) * NumCast(doc._width); //, actualdH); - } - doc._height = actualdH; - } - } else { - const rotCtr = [docwidth / 2, docheight / 2]; - const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI); - - const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '_scrollHeight']))) * docView.NativeDimScaling(); - dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH); - dW && (doc._width = actualdW); - dH && (doc._layout_autoHeight = false); - - const rotCtr2 = [NumCast(doc._width) / 2, NumCast(doc._height) / 2]; - const tlRotated2 = Utils.rotPt(-rotCtr2[0], -rotCtr2[1], (NumCast(doc._rotation) / 180) * Math.PI); - doc.x = NumCast(doc.x) + tlRotated.x + rotCtr[0] - (tlRotated2.x + rotCtr2[0]); // doc shifts by amount topleft moves because rotation is about center of doc - doc.y = NumCast(doc.y) + tlRotated.y + rotCtr[1] - (tlRotated2.y + rotCtr2[1]); - } - doc.x = NumCast(doc.x) + dX; - doc.y = NumCast(doc.y) + dY; - doc._layout_modificationDate = new DateField(); + return false; + }; + + // + // determines how much to resize, and determines the resize reference point + // + getResizeVals = (thisPt: { x: number; y: number }, dragHdl: string) => { + const [w, h] = [this.Bounds.r - this.Bounds.x, this.Bounds.b - this.Bounds.y]; + const [moveX, moveY] = [thisPt.x - this._snapPt.x, thisPt.y - this._snapPt.y]; + switch (dragHdl) { + case 'topLeft': return { scale: { x: 1 - moveX / w, y: 1 -moveY / h }, refPt: [this.Bounds.r, this.Bounds.b] }; + case 'topRight': return { scale: { x: 1 + moveX / w, y: 1 -moveY / h }, refPt: [this.Bounds.x, this.Bounds.b] }; + case 'top': return { scale: { x: 1, y: 1 -moveY / h }, refPt: [this.Bounds.x, this.Bounds.b] }; + case 'left': return { scale: { x: 1 - moveX / w, y: 1 }, refPt: [this.Bounds.r, this.Bounds.y] }; + case 'bottomLeft': return { scale: { x: 1 - moveX / w, y: 1 + moveY / h }, refPt: [this.Bounds.r, this.Bounds.y] }; + case 'right': return { scale: { x: 1 + moveX / w, y: 1 }, refPt: [this.Bounds.x, this.Bounds.y] }; + case 'bottomRight':return { scale: { x: 1 + moveX / w, y: 1 + moveY / h }, refPt: [this.Bounds.x, this.Bounds.y] }; + case 'bottom': return { scale: { x: 1, y: 1 + moveY / h }, refPt: [this.Bounds.x, this.Bounds.y] }; + default: return { scale: { x: 1, y: 1 }, refPt: [this.Bounds.x, this.Bounds.y] }; + } // prettier-ignore + }; + + // + // determines if anything being dragged directly or via a group has a fixed aspect ratio (in which case we resize uniformly) + // + hasFixedAspect = (doc: Doc): boolean => (doc.isGroup ? DocListCast(doc.data).some(this.hasFixedAspect) : !BoolCast(doc.layout_nativeDimEditable)); + + // + // 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 }) => { + 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; + } 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)]; + + const modifyNativeDim = + (opts.ctrlKey && doc.layout_nativeDimEditable) || // e.g., PDF or web page + (doc.layout_reflowHorizontal && opts.dragHdl !== 'bottom' && opts.dragHdl !== 'top') || // eg rtf or some web pages + (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); + } + + 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); + 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 } - const val = this._dragHeights.get(docView.layoutDoc); - if (val) this._dragHeights.set(docView.layoutDoc, { start: val.start, lowest: Math.min(val.lowest, NumCast(docView.layoutDoc._height)) }); - }) + } + 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); + if (setData) Doc.SetNativeHeight(Doc.GetProto(doc), NumCast(doc._nativeHeight)); + } + + doc._width = NumCast(doc._width) * scaleX; + doc._height = NumCast(doc._height) * scaleY; + 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); + } + }; + + // This realigns the doc's resize reference point with where it was before resizing it. + // This is needed, because the transformation for doc's with a rotation is screwy: + // the top left of the doc is the 'origin', but the rotation happens about the center of the Doc. + // So resizing a rotated doc will cause it to shift -- this counteracts that shift by determine how + // the reference points shifted, and returning a translation to restore the reference point. + realignRefPt = (doc: Doc, refCent: number[], initWidth: number, initHeight: number) => { + const refCentPct = [refCent[0] / initWidth, refCent[1] / initHeight]; + const rotRefStart = Utils.rotPt( + refCent[0] - initWidth / 2, // rotate reference pointe before scaling + refCent[1] - initHeight / 2, + (NumCast(doc._rotation) / 180) * Math.PI ); - return false; + const rotRefEnd = Utils.rotPt( + refCentPct[0] * NumCast(doc._width) - NumCast(doc._width) / 2, // rotate reference point after scaling + refCentPct[1] * NumCast(doc._height) - NumCast(doc._height) / 2, + (NumCast(doc._rotation) / 180) * Math.PI + ); + return { + deltaX: rotRefStart.x + initWidth / 2 - (rotRefEnd.x + NumCast(doc._width) / 2), // + deltaY: rotRefStart.y + initHeight / 2 - (rotRefEnd.y + NumCast(doc._height) / 2), + }; }; @action @@ -691,10 +634,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P SnappingManager.clearSnapLines(); // detect layout_autoHeight gesture and apply - SelectionManager.Views() - .map(docView => ({ doc: docView.layoutDoc, hgts: this._dragHeights.get(docView.layoutDoc) })) - .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20) - .forEach(pair => (pair.doc._layout_autoHeight = true)); + SelectionManager.Docs().forEach(doc => NumCast(doc._height) < 20 && (doc._layout_autoHeight = true)); //need to change points for resize, or else rotation/control points will fail. this._inkDragDocs .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] })) diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index 0d7f7ebd8..e5141b7f4 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -12,6 +12,7 @@ import { UndoManager } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { InkStrokeProperties } from './InkStrokeProperties'; +import { SnappingManager } from '../util/SnappingManager'; export interface InkControlProps { inkDoc: Doc; @@ -190,6 +191,7 @@ export class InkEndPtHandles extends React.Component<InkEndProps> { @action dragRotate = (e: React.PointerEvent, pt1: () => { X: number; Y: number }, pt2: () => { X: number; Y: number }) => { + SnappingManager.SetIsDragging(true); setupMoveUpEvents( this, e, @@ -211,6 +213,7 @@ export class InkEndPtHandles extends React.Component<InkEndProps> { return false; }), action(() => { + SnappingManager.SetIsDragging(false); this.props.inkView.controlUndo?.end(); this.props.inkView.controlUndo = undefined; UndoManager.FilterBatches(['stroke', 'x', 'y', 'width', 'height']); @@ -237,8 +240,8 @@ export class InkEndPtHandles extends React.Component<InkEndProps> { ); return ( <svg> - {hdl('start', this.props.startPt(), (e: React.PointerEvent) => this.dragRotate(e, this.props.startPt, this.props.endPt))} - {hdl('end', this.props.endPt(), (e: React.PointerEvent) => this.dragRotate(e, this.props.endPt, this.props.startPt))} + {hdl('start', this.props.startPt(), e => this.dragRotate(e, this.props.startPt, this.props.endPt))} + {hdl('end', this.props.endPt(), e => this.dragRotate(e, this.props.endPt, this.props.startPt))} </svg> ); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a6f0b9e84..5bed0f923 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -252,6 +252,7 @@ export class MainView extends React.Component { fa.faShare, fa.faTaxi, fa.faDownload, + fa.faPallet, fa.faExpandArrowsAlt, fa.faAmbulance, fa.faLayerGroup, diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 81453c0b8..c2586fb4b 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -295,7 +295,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { const aspect = Doc.NativeAspect(layout, undefined, true); const width = () => this.lookupPixels(layout); const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0); - const docwidth = () => (layout._layout_forceReflow ? width() : Math.min(height() * aspect, width())); + const docwidth = () => (layout._layout_reflowHorizontal ? width() : Math.min(height() * aspect, width())); const docheight = () => Math.min(docwidth() / aspect, height()); const dxf = () => this.lookupIndividualTransform(layout) diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 04cfc5456..249c3551b 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -292,7 +292,7 @@ export class CollectionMultirowView extends CollectionSubView() { const height = () => this.lookupPixels(layout); const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0); const docheight = () => Math.min(width() / aspect, height()); - const docwidth = () => (layout._layout_forceReflow ? width() : Math.min(width(), docheight() * aspect)); + const docwidth = () => (layout._layout_reflowHorizontal ? width() : Math.min(width(), docheight() * aspect)); const dxf = () => this.lookupIndividualTransform(layout) .translate(-NumCast(Document._xMargin) - (width() - docwidth()) / 2, -NumCast(Document._yMargin) - (height() - docheight()) / 2) diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index c690b8623..3af523d73 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -181,16 +181,22 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh map.get(attr)?.setDoc?.(); }); -type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'underline' | 'left' | 'center' | 'right' | 'bullet' | 'decimal'; +type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal'; type attrfuncs = [attrname, { checkResult: () => boolean; toggle: () => any }]; ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) { const textView = RichTextMenu.Instance?.TextView; const editorView = textView?.EditorView; // prettier-ignore - const alignments:attrfuncs[] = (['left','right','center'] as ("left"|"center"|"right")[]).map((where) => - [ where, { checkResult: () =>(editorView ? (RichTextMenu.Instance.textAlign ===where): (Doc.UserDoc().textAlign ===where) ? true:false), - toggle: () => (editorView?.state ? RichTextMenu.Instance.align(editorView, editorView.dispatch, where):(Doc.UserDoc().textAlign = where))}]); + const alignments:attrfuncs[] = (['left','right','center','vcent'] as ("left"|"center"|"right"|"vcent")[]).map((where) => + [ where, { checkResult: () =>(editorView ? (where === 'vcent' ? SelectionManager.Docs().some(doc => doc.layout_centered): + (RichTextMenu.Instance.textAlign === where)): + where === 'vcent' ? BoolCast(Doc.UserDoc().layout_centered): + (Doc.UserDoc().textAlign ===where) ? true:false), + toggle: () => (editorView?.state ? (where === 'vcent' ? SelectionManager.Docs().forEach(doc => doc.layout_centered = !doc.layout_centered): + RichTextMenu.Instance.align(editorView, editorView.dispatch, where)): + where === 'vcent' ? Doc.UserDoc().layout_centered = !Doc.UserDoc().layout_centered: + (Doc.UserDoc().textAlign = where))}]); // prettier-ignore // prettier-ignore const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list => [ list, { checkResult: () => (editorView ? RichTextMenu.Instance.getActiveListStyle() === list:false), diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index a05d6c904..582a5518a 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -62,7 +62,7 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti w_Y = () => this.Y; // prettier-ignore w_Z = () => this.Z; // prettier-ignore w_ZIndex = () => this.ZIndex ?? NumCast(this.props.Document.zIndex); // prettier-ignore - w_Rot = () => this.Rotation ?? NumCast(this.props.Document._rotation); // prettier-ignore + w_Rotation = () => this.Rotation ?? NumCast(this.props.Document._rotation); // prettier-ignore w_Opacity = () => this.Opacity; // prettier-ignore w_BackgroundColor = () => this.BackgroundColor ?? Cast(this.props.Document._backgroundColor, 'string', null); // prettier-ignore w_Color = () => this.Color ?? Cast(this.props.Document._color, 'string', null); // prettier-ignore diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 299494c83..b3f813eda 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -115,8 +115,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { layoutDoc: this.layoutDoc, records: this.records, axes: this.axes, - height: (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9, - width: this.props.PanelWidth() * 0.9, + height: (this.props.PanelHeight() * (this.props.NativeDimScaling?.() || 1) - 32) /* height of 'change view' button */ * 0.9, + width: this.props.PanelWidth() * (this.props.NativeDimScaling?.() || 1) * 0.9, margin: { top: 10, right: 25, bottom: 75, left: 45 }, }; if (!this.records.length) return 'no data/visualization'; @@ -133,6 +133,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }; render() { + const scale = this.props.NativeDimScaling?.() || 1; + return !this.records.length ? ( // displays how to get data into the DataVizBox if its empty <div className="start-message">To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command 'ctrl + p' to bring the data table to your canvas.</div> @@ -141,6 +143,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { className="dataViz" style={{ pointerEvents: this.props.isContentActive() === true ? 'all' : 'none', + width: `${100 / scale}%`, + height: `${100 / scale}%`, + transform: `scale(${scale})`, + position: 'absolute', }} onWheel={e => e.stopPropagation()} ref={r => diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d1caf0acf..e3d6c3fdf 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1495,7 +1495,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); } @computed get panelHeight() { - if (this.effectiveNativeHeight && (!this.layout_fitWidth || !this.layoutDoc.nativeHeightUnfrozen)) { + if (this.effectiveNativeHeight && (!this.layout_fitWidth || !this.layoutDoc.layout_reflowVertical)) { return Math.min(this.props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling); } return this.props.PanelHeight(); @@ -1507,7 +1507,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && - (!this.layoutDoc.nativeHeightUnfrozen || (!this.layout_fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight())) + (!this.layoutDoc.layout_reflowVertical || (!this.layout_fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight())) ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2) : 0; } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index bde1d5fa4..185c6553a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -347,7 +347,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']); const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; - const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth; + const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + NumCast(this.layoutDoc._width)) / NumCast(this.layoutDoc._width); const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); if (preview) { this._previewNativeWidth = nativeWidth * sideratio; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index a452b1cdd..d9ea48c3b 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -707,10 +707,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps icon: 'snowflake', }); funcs.push({ - description: (!this.layoutDoc.layout_forceReflow ? 'Force' : 'Prevent') + ' Reflow', + description: (!this.layoutDoc.layout_reflowHorizontal ? 'Force' : 'Prevent') + ' Reflow', event: () => { - const nw = !this.layoutDoc.layout_forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1); - this.layoutDoc.layout_forceReflow = !nw; + const nw = !this.layoutDoc.layout_reflowHorizontal ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1); + this.layoutDoc.layout_reflowHorizontal = !nw; if (nw) { Doc.SetInPlace(this.layoutDoc, this.fieldKey + '_nativeWidth', nw, true); } @@ -872,13 +872,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps toggleSidebar = action((preview: boolean = false) => { var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']); if (!nativeWidth) { - const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : NumCast(this.Document._width); + const defaultNativeWidth = NumCast(this.rootDoc.nativeWidth, this.rootDoc[this.fieldKey] instanceof WebField ? 850 : NumCast(this.Document._width)); Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth); Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (NumCast(this.Document._height) / NumCast(this.Document._width)) * defaultNativeWidth); nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']); } const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; - const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth; + const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + NumCast(this.layoutDoc.width)) / NumCast(this.layoutDoc.width); const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); if (preview) { this._previewNativeWidth = nativeWidth * sideratio; @@ -890,6 +890,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (!this.layoutDoc._layout_showSidebar && !(this.dataDoc[this.fieldKey] instanceof WebField)) { this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + '_nativeWidth'] = undefined; } else { + !this.layoutDoc._layout_showSidebar && (this.dataDoc[this.fieldKey + '_nativeWidth'] = this.dataDoc[this.fieldKey + '_nativeHeight'] = undefined); this.layoutDoc.nativeWidth = nativeWidth * pdfratio; } } @@ -917,7 +918,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps className={'webBox-cont' + (interactive ? '-interactive' : '')} onKeyDown={e => e.stopPropagation()} style={{ - width: !this.layoutDoc.layout_forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']) || `100%` : '100%', + width: !this.layoutDoc.layout_reflowHorizontal ? NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']) || `100%` : '100%', transform: `scale(${this.zoomScaling()}) translate(${-NumCast(this.layoutDoc.freeform_panX)}px, ${-NumCast(this.layoutDoc.freeform_panY)}px)`, }}> {this._hackHide ? null : this.urlContent} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9ab3189a6..a80c1e030 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -704,14 +704,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps @action toggleSidebar = (preview: boolean = false) => { + const defaultSidebar = 250; const prevWidth = 1 - this.sidebarWidth() / Number(getComputedStyle(this._ref.current!).width.replace('px', '')); if (preview) this._showSidebar = true; else { this.layoutDoc[this.SidebarKey + '_freeform_scale_max'] = 1; - this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%'; + this.layoutDoc._layout_showSidebar = + (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(defaultSidebar / (NumCast(this.layoutDoc._width) + defaultSidebar)) * 100}%` : '0%') !== '0%'; } - this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth); + this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) + defaultSidebar : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth); }; sidebarDown = (e: React.PointerEvent) => { const batch = UndoManager.StartBatch('toggle sidebar'); |
