aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEric <ericmabr@gmail.com>2023-02-22 17:18:24 -0500
committerEric <ericmabr@gmail.com>2023-02-22 17:18:24 -0500
commit2e3f56ed2bfc97191f514c5edbf618897dfb6a9d (patch)
tree0ffb7f51352db766c3ef7a076a74b92a9b18c89c /src
parentef7fa18c57cfd2fdfd076eab29cd7a6ea8a1008e (diff)
parent32f5040c44dc302e3dd53cecd9be4cd51a474d3f (diff)
Merge branch 'master' into UI_Update_Eric_Ma
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts4
-rw-r--r--src/client/util/CurrentUserUtils.ts6
-rw-r--r--src/client/util/InteractionUtils.tsx9
-rw-r--r--src/client/util/SettingsManager.tsx12
-rw-r--r--src/client/views/DocumentDecorations.tsx13
-rw-r--r--src/client/views/GestureOverlay.tsx22
-rw-r--r--src/client/views/GlobalKeyHandler.ts29
-rw-r--r--src/client/views/InkStrokeProperties.ts11
-rw-r--r--src/client/views/InkTranscription.tsx2
-rw-r--r--src/client/views/InkingStroke.tsx29
-rw-r--r--src/client/views/LightboxView.tsx64
-rw-r--r--src/client/views/PropertiesButtons.tsx10
-rw-r--r--src/client/views/PropertiesView.tsx2
-rw-r--r--src/client/views/StyleProvider.tsx4
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/TabDocView.tsx16
-rw-r--r--src/client/views/collections/TreeView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx63
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx4
-rw-r--r--src/client/views/nodes/DocumentView.tsx21
-rw-r--r--src/client/views/nodes/ImageBox.tsx71
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx1
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx152
-rw-r--r--src/client/views/nodes/PDFBox.tsx1
-rw-r--r--src/client/views/nodes/WebBox.tsx12
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx2
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx4
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx24
-rw-r--r--src/client/views/nodes/trails/PresElementBox.scss6
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx12
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx1
-rw-r--r--src/client/views/pdf/PDFViewer.tsx17
-rw-r--r--src/fields/util.ts2
35 files changed, 419 insertions, 220 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 692d09629..3faa6e11d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -149,6 +149,8 @@ export class DocumentOptions {
_height?: NUMt = new NumInfo('displayed height of document');
_nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)');
_nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)');
+ _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers');
+ _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
_dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height");
_dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
@@ -848,7 +850,7 @@ export namespace Docs {
export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) {
const imgField = url instanceof ImageField ? url : new ImageField(url);
- return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options }, undefined, undefined, undefined, overwriteDoc);
+ return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { _nativeDimModifiable: false, _nativeHeightUnfrozen: false, title: basename(imgField.url.href), ...options }, undefined, undefined, undefined, overwriteDoc);
}
export function PresDocument(options: DocumentOptions = {}) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 5f183cf91..f678c8936 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -183,7 +183,7 @@ export class CurrentUserUtils {
const allopts = {system: true, ...opts};
return DocUtils.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ?
DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
- {onClick:"deiconifyView(documentView)"});
+ {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView"});
};
const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
@@ -803,7 +803,9 @@ export class CurrentUserUtils {
doc._showLabel ?? (doc._showLabel = true);
doc.textAlign ?? (doc.textAlign = "left");
doc.activeTool = InkTool.None;
- doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");;
+ doc.openInkInLightbox ?? (doc.openInkInLightbox = false);
+ doc.activeInkHideTextLabels ?? (doc.activeInkHideTextLabels = false);
+ doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");
doc.activeInkWidth ?? (doc.activeInkWidth = 1);
doc.activeInkBezier ?? (doc.activeInkBezier = "0");
doc.activeFillColor ?? (doc.activeFillColor = "");
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 3cdf4dbd2..9591dbed3 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -107,7 +107,8 @@ export namespace InteractionUtils {
pevents: string,
opacity: number,
nodefs: boolean,
- downHdlr?: (e: React.PointerEvent) => void
+ downHdlr?: (e: React.PointerEvent) => void,
+ mask?: boolean
) {
const pts = shape ? makePolygon(shape, points) : points;
@@ -133,6 +134,11 @@ export namespace InteractionUtils {
{/* setting the svg fill sets the arrowStart fill */}
{nodefs ? null : (
<defs>
+ {!mask ? null : (
+ <filter id={`mask${defGuid}`} x="-1" y="-1" width="500%" height="500%">
+ <feGaussianBlur result="blurOut" in="offOut" stdDeviation="5"></feGaussianBlur>
+ </filter>
+ )}
{arrowStart !== 'dot' && arrowEnd !== 'dot' ? null : (
<marker id={`dot${defGuid}`} orient="auto" markerUnits="userSpaceOnUse" refX={0} refY="0" overflow="visible">
<circle r={strokeWidth * arrowWidthFactor} fill="context-stroke" />
@@ -168,6 +174,7 @@ export namespace InteractionUtils {
style={{
// filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
fill: fill && fill !== 'transparent' ? fill : 'none',
+ filter: mask ? `url(#mask${defGuid})` : undefined,
opacity: 1.0,
// opacity: strokeWidth !== width ? 0.5 : undefined,
pointerEvents: pevents as any,
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index a3d76591f..6c823e80a 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -192,8 +192,16 @@ export class SettingsManager extends React.Component<{}> {
<div className="preferences-check">Show button labels</div>
</div>
<div>
- <input type="checkbox" onChange={e => FontIconBox.SetRecognizeGesturs(!FontIconBox.GetRecognizeGestures())} checked={FontIconBox.GetRecognizeGestures()} />
- <div className="preferences-check">Recognize ink Gesturs</div>
+ <input type="checkbox" onChange={e => FontIconBox.SetRecognizeGestures(!FontIconBox.GetRecognizeGestures())} checked={FontIconBox.GetRecognizeGestures()} />
+ <div className="preferences-check">Recognize ink Gestures</div>
+ </div>
+ <div>
+ <input type="checkbox" onChange={e => (Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels)} checked={BoolCast(Doc.UserDoc().activeInkHideTextLabels)} />
+ <div className="preferences-check">Hide Labels In Ink Shapes</div>
+ </div>
+ <div>
+ <input type="checkbox" onChange={e => (Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox)} checked={BoolCast(Doc.UserDoc().openInkInLightbox)} />
+ <div className="preferences-check">Open Ink Docs in Lightbox</div>
</div>
</div>
);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d1f0bf2ac..41f4a17fb 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -581,7 +581,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}
let actualdW = Math.max(width + dW * scale, 20);
let actualdH = Math.max(height + dH * scale, 20);
- const fixedAspect = nwidth && nheight && (!doc._fitWidth || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
+ const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false;
+ const fixedAspect = nwidth && nheight && (!doc._fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
if (fixedAspect) {
if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) {
if (dragRight && modifyNativeDim) {
@@ -589,7 +590,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
doc._nativeWidth = (actualdW / (doc._width || 1)) * Doc.NativeWidth(doc);
}
} else {
- if (!doc._fitWidth) {
+ if (!doc._fitWidth || preserveNativeDim) {
actualdH = (nheight / nwidth) * actualdW;
doc._height = actualdH;
} else if (!modifyNativeDim || dragBotRight) doc._height = actualdH;
@@ -597,11 +598,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
doc._width = actualdW;
} else {
if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._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)
+ // 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 / (doc._height || 1)) * Doc.NativeHeight(doc);
doc._autoHeight = false;
} else {
- if (!doc._fitWidth) {
+ if (!doc._fitWidth || preserveNativeDim) {
actualdW = (nwidth / nheight) * actualdH;
doc._width = actualdW;
} else if (!modifyNativeDim || dragBotRight) doc._width = actualdW;
@@ -615,7 +618,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
const rotCtr = [NumCast(doc._width) / 2, NumCast(doc._height) / 2];
const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI);
- const maxHeight = doc.nativHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
+ 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._autoHeight = false);
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index e3328fb4c..6058eaaf9 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -1,9 +1,9 @@
import React = require('react');
import * as fitCurve from 'fit-curve';
-import { action, computed, observable, runInAction, trace } from 'mobx';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
import { Doc, Opt } from '../../fields/Doc';
import { InkData, InkTool } from '../../fields/InkField';
-import { List } from '../../fields/List';
import { ScriptField } from '../../fields/ScriptField';
import { Cast, FieldValue, NumCast } from '../../fields/Types';
import MobileInkOverlay from '../../mobile/MobileInkOverlay';
@@ -14,7 +14,6 @@ import { CognitiveServices } from '../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../documents/Documents';
import { InteractionUtils } from '../util/InteractionUtils';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
-import { SelectionManager } from '../util/SelectionManager';
import { Transform } from '../util/Transform';
import './GestureOverlay.scss';
import {
@@ -39,7 +38,6 @@ import { RadialMenu } from './nodes/RadialMenu';
import HorizontalPalette from './Palette';
import { Touchable } from './Touchable';
import TouchScrollableMenu, { TouchScrollableMenuItem } from './TouchScrollableMenu';
-import { observer } from 'mobx-react';
interface GestureOverlayProps {
isActive: boolean;
@@ -47,6 +45,7 @@ interface GestureOverlayProps {
@observer
export class GestureOverlay extends Touchable<GestureOverlayProps> {
static Instance: GestureOverlay;
+ static Instances: GestureOverlay[] = [];
@observable public InkShape: Opt<GestureUtils.Gestures>;
@observable public SavedColor?: string;
@@ -66,6 +65,8 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
@observable private _clipboardDoc?: JSX.Element;
@observable private _possibilities: JSX.Element[] = [];
+ public static DownDocView: DocumentView | undefined;
+
@computed private get height(): number {
return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 100, 100);
}
@@ -89,7 +90,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
constructor(props: any) {
super(props);
- GestureOverlay.Instance = this;
+ GestureOverlay.Instances.push(this);
}
static setupThumbButtons(doc: Doc) {
@@ -154,7 +155,13 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
}
return Cast(userDoc.thumbDoc, Doc);
}
+
+ componentWillUnmount() {
+ GestureOverlay.Instances.splice(GestureOverlay.Instances.indexOf(this), 1);
+ GestureOverlay.Instance = GestureOverlay.Instances.lastElement();
+ }
componentDidMount = () => {
+ GestureOverlay.Instance = this;
this._thumbDoc = FieldValue(Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc));
this._inkToTextDoc = FieldValue(Cast(this._thumbDoc?.inkToTextDoc, Doc));
};
@@ -627,6 +634,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
}
@action
onPointerUp = (e: PointerEvent) => {
+ GestureOverlay.DownDocView = undefined;
if (this._points.length > 1) {
const B = this.svgBounds;
const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top }));
@@ -906,8 +914,8 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
}
@computed get elements() {
- const selView = SelectionManager.Views().lastElement();
- const width = (Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1)) / (selView?.props.ScreenToLocalTransform().Scale || 1);
+ const selView = GestureOverlay.DownDocView;
+ const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1);
const rect = this._overlayRef.current?.getBoundingClientRect();
const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(this._points, true);
B.left = B.left - width / 2;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 5e700e281..6c8a078ec 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -1,12 +1,12 @@
import { random } from 'lodash';
-import { action, observable, runInAction } from 'mobx';
+import { action, runInAction } from 'mobx';
import { DateField } from '../../fields/DateField';
import { Doc, DocListCast } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
import { InkTool } from '../../fields/InkField';
import { List } from '../../fields/List';
import { ScriptField } from '../../fields/ScriptField';
-import { Cast, DocCast, PromiseValue } from '../../fields/Types';
+import { Cast, PromiseValue } from '../../fields/Types';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { DocumentType } from '../documents/DocumentTypes';
@@ -18,6 +18,7 @@ import { SharingManager } from '../util/SharingManager';
import { SnappingManager } from '../util/SnappingManager';
import { undoBatch, UndoManager } from '../util/UndoManager';
import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
import { CollectionFreeFormViewChrome } from './collections/CollectionMenu';
import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline';
import { ContextMenu } from './ContextMenu';
@@ -103,6 +104,20 @@ export class KeyManager {
const groupings = SelectionManager.Views().slice();
const randomGroup = random(0, 1000);
+ const collectionView = groupings.reduce(
+ (col, g) => (col === null || g.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView === col ? g.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView : undefined),
+ null as null | undefined | CollectionFreeFormView
+ );
+ if (collectionView) {
+ UndoManager.RunInBatch(() => {
+ collectionView._marqueeViewRef.current?.collection(
+ e,
+ true,
+ groupings.map(g => g.rootDoc)
+ );
+ }, 'grouping');
+ break;
+ }
UndoManager.RunInBatch(() => groupings.map(dv => (dv.layoutDoc.group = randomGroup)), 'group');
SelectionManager.DeselectAll();
break;
@@ -192,6 +207,16 @@ export class KeyManager {
case 'arrowdown':
UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 10)), 'nudge down');
break;
+ case 'g':
+ if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
+ return { stopPropagation: false, preventDefault: false };
+ }
+
+ const groupings = SelectionManager.Views().slice();
+ const randomGroup = random(0, 1000);
+ UndoManager.RunInBatch(() => groupings.map(dv => (dv.layoutDoc.group = randomGroup)), 'group');
+ SelectionManager.DeselectAll();
+ break;
}
return {
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index d19a916f9..1d8d52425 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -342,12 +342,19 @@ export class InkStrokeProperties {
if (ink) {
const screenDragPt = inkView.ComponentView?.ptToScreen?.(ink[controlIndex]);
if (screenDragPt) {
+ if (controlIndex === ink.length - 1) {
+ const firstPtScr = inkView.ComponentView?.ptToScreen?.(ink[0]);
+ if (firstPtScr && Math.sqrt((firstPtScr.X - screenDragPt.X) * (firstPtScr.X - screenDragPt.X) + (firstPtScr.Y - screenDragPt.Y) * (firstPtScr.Y - screenDragPt.Y)) < 7) {
+ const deltaX = ink[0].X - ink[controlIndex].X;
+ const deltaY = ink[0].Y - ink[controlIndex].Y;
+ return this.moveControlPtHandle(inkView, deltaX, deltaY, controlIndex, ink.slice());
+ }
+ }
const snapData = this.snapToAllCurves(screenDragPt, inkView, { nearestPt: { X: 0, Y: 0 }, distance: 10 }, ink, controlIndex);
if (snapData.distance < 10) {
const deltaX = snapData.nearestPt.X - ink[controlIndex].X;
const deltaY = snapData.nearestPt.Y - ink[controlIndex].Y;
- const res = this.moveControlPtHandle(inkView, deltaX, deltaY, controlIndex, ink.slice());
- return res;
+ return this.moveControlPtHandle(inkView, deltaX, deltaY, controlIndex, ink.slice());
}
}
}
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index bf0e8081d..246b887a6 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -67,7 +67,7 @@ export class InkTranscription extends React.Component {
: null;
}
- r.addEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
+ r?.addEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
return (this._mathRef = r);
};
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 319a9419e..8291ff3f1 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -27,7 +27,7 @@ import { Doc, HeightSym, WidthSym } from '../../fields/Doc';
import { InkData, InkField } from '../../fields/InkField';
import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types';
import { TraceMobx } from '../../fields/util';
-import { OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils';
+import { DashColor, OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils';
import { CognitiveServices } from '../cognitive_services/CognitiveServices';
import { InteractionUtils } from '../util/InteractionUtils';
import { SnappingManager } from '../util/SnappingManager';
@@ -54,7 +54,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
return FieldView.LayoutString(InkingStroke, fieldStr);
}
public static IsClosed(inkData: InkData) {
- return inkData && inkData.lastElement().X === inkData[0].X && inkData.lastElement().Y === inkData[0].Y;
+ return inkData?.length && inkData.lastElement().X === inkData[0].X && inkData.lastElement().Y === inkData[0].Y;
}
private _handledClick = false; // flag denoting whether ink stroke has handled a psuedo-click onPointerUp so that the real onClick event can be stopPropagated
private _disposers: { [key: string]: IReactionDisposer } = {};
@@ -264,9 +264,13 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
.map(p => ({ X: p[0], Y: p[1] }));
const { distance, nearestT, nearestSeg, nearestPt } = InkStrokeProperties.nearestPtToStroke(screenPts, { X: e.clientX, Y: e.clientY });
- this._nearestT = nearestT;
- this._nearestSeg = nearestSeg;
- this._nearestScrPt = nearestPt;
+ if (distance < 40) {
+ this._nearestT = nearestT;
+ this._nearestSeg = nearestSeg;
+ this._nearestScrPt = nearestPt;
+ } else {
+ this._nearestT = this._nearestSeg = this._nearestScrPt = undefined;
+ }
};
/**
@@ -351,7 +355,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
const markerScale = NumCast(this.layoutDoc.strokeMarkerScale, 1);
const closed = InkingStroke.IsClosed(inkData);
const isInkMask = BoolCast(this.layoutDoc.isInkMask);
- const fillColor = isInkMask ? Colors.WHITE : StrCast(this.layoutDoc.fillColor, 'transparent');
+ const fillColor = isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : StrCast(this.layoutDoc.fillColor, 'transparent');
const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color);
// bcz: Hack!! Not really sure why, but having fractional values for width/height of mask ink strokes causes the dragging clone (see DragManager) to be offset from where it should be.
@@ -389,7 +393,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
const highlightIndex = highlight?.highlightIndex;
const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : StrCast(this.layoutDoc.strokeOutlineColor, !closed && fillColor && fillColor !== 'transparent' ? StrCast(this.layoutDoc.color, 'transparent') : 'transparent');
// Invisible polygonal line that enables the ink to be selected by the user.
- const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, suppressFill: boolean = false) =>
+ const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, suppressFill: boolean = false, mask: boolean = false) =>
InteractionUtils.CreatePolyline(
inkData,
inkLeft,
@@ -400,7 +404,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
StrCast(this.layoutDoc.strokeLineJoin),
StrCast(this.layoutDoc.strokeLineCap),
StrCast(this.layoutDoc.strokeBezier),
- !closed ? 'none' : fillColor === 'transparent' || suppressFill ? 'none' : fillColor,
+ !closed ? 'none' : !isInkMask && (fillColor === 'transparent' || suppressFill) ? 'none' : fillColor,
'',
'',
markerScale,
@@ -411,7 +415,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
this.props.pointerEvents?.() ?? (this.rootDoc._lockedPosition ? 'none' : 'visiblepainted'),
0.0,
false,
- downHdlr
+ downHdlr,
+ mask
);
const fsize = +StrCast(this.props.Document.fontSize, '12px').replace('px', '');
// bootsrap 3 style sheet sets line height to be 20px for default 14 point font size.
@@ -439,10 +444,10 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
cursor: this.props.isSelected() ? 'default' : undefined,
}}
{...interactions}>
- {clickableLine(this.onPointerDown)}
- {inkLine}
+ {clickableLine(this.onPointerDown, undefined, isInkMask)}
+ {isInkMask ? null : inkLine}
</svg>
- {!closed || (!RTFCast(this.rootDoc.text)?.Text && !this.props.isSelected()) ? null : (
+ {!closed || (!RTFCast(this.rootDoc.text)?.Text && (!this.props.isSelected() || Doc.UserDoc().activeInkHideTextLabels)) ? null : (
<div
className="inkStroke-text"
style={{
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 3627aa783..e531bf71c 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -27,6 +27,13 @@ interface LightboxViewProps {
maxBorder: number[];
}
+type LightboxSavedState = {
+ panX: Opt<number>;
+ panY: Opt<number>;
+ scale: Opt<number>;
+ scrollTop: Opt<number>;
+ layoutKey: Opt<string>;
+};
@observer
export class LightboxView extends React.Component<LightboxViewProps> {
@computed public static get LightboxDoc() {
@@ -34,21 +41,22 @@ export class LightboxView extends React.Component<LightboxViewProps> {
}
private static LightboxDocTemplate = () => LightboxView._layoutTemplate;
@observable private static _layoutTemplate: Opt<Doc>;
+ @observable private static _layoutTemplateString: Opt<string>;
@observable private static _doc: Opt<Doc>;
@observable private static _docTarget: Opt<Doc>;
@observable private static _docFilters: string[] = []; // filters
- private static _savedState: Opt<{ panX: Opt<number>; panY: Opt<number>; scale: Opt<number>; scrollTop: Opt<number> }>;
+ private static _savedState: Opt<LightboxSavedState>;
private static _history: Opt<{ doc: Doc; target?: Doc }[]> = [];
@observable private static _future: Opt<Doc[]> = [];
@observable private static _docView: Opt<DocumentView>;
- private static openInTabFunc: any;
- static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<{ panX: Opt<number>; panY: Opt<number>; scale: Opt<number>; scrollTop: Opt<number> }> }[] = [];
- @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc) {
+ static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<LightboxSavedState> }[] = [];
+ @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) {
if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) {
- this.LightboxDoc._panX = this._savedState.panX;
- this.LightboxDoc._panY = this._savedState.panY;
- this.LightboxDoc._scrollTop = this._savedState.scrollTop;
- this.LightboxDoc._viewScale = this._savedState.scale;
+ if (this._savedState.panX !== undefined) this.LightboxDoc._panX = this._savedState.panX;
+ if (this._savedState.panY !== undefined) this.LightboxDoc._panY = this._savedState.panY;
+ if (this._savedState.scrollTop !== undefined) this.LightboxDoc._scrollTop = this._savedState.scrollTop;
+ if (this._savedState.scale !== undefined) this.LightboxDoc._viewScale = this._savedState.scale;
+ this.LightboxDoc.layoutKey = this._savedState.layoutKey;
}
if (!doc) {
this._docFilters && (this._docFilters.length = 0);
@@ -69,10 +77,11 @@ export class LightboxView extends React.Component<LightboxViewProps> {
this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]);
if (doc !== LightboxView.LightboxDoc) {
this._savedState = {
- panX: Cast(doc._panX, 'number', null),
- panY: Cast(doc._panY, 'number', null),
- scale: Cast(doc._viewScale, 'number', null),
- scrollTop: Cast(doc._scrollTop, 'number', null),
+ layoutKey: StrCast(doc.layoutKey),
+ panX: Cast(doc.panX, 'number', null),
+ panY: Cast(doc.panY, 'number', null),
+ scale: Cast(doc.viewScale, 'number', null),
+ scrollTop: Cast(doc.scrollTop, 'number', null),
};
}
}
@@ -87,7 +96,10 @@ export class LightboxView extends React.Component<LightboxViewProps> {
];
}
this._doc = doc;
- this._layoutTemplate = layoutTemplate;
+ this._layoutTemplate = layoutTemplate instanceof Doc ? layoutTemplate : undefined;
+ if (doc && (typeof layoutTemplate === 'string' ? layoutTemplate : undefined)) {
+ doc.layoutKey = layoutTemplate;
+ }
this._docTarget = target || doc;
return true;
@@ -132,7 +144,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`);
}
}
- public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc, openInTabFunc?: any) => {
+ public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) => {
if (location !== OpenWhere.lightbox) {
const inPlaceView = DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined;
if (inPlaceView) {
@@ -140,12 +152,13 @@ export class LightboxView extends React.Component<LightboxViewProps> {
return true;
}
}
- LightboxView.openInTabFunc = openInTabFunc;
SelectionManager.DeselectAll();
return LightboxView.SetLightboxDoc(
doc,
undefined,
- [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']), ...(LightboxView._future ?? [])].sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)),
+ [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']).filter(anno => anno.annotationOn !== doc), ...(LightboxView._future ?? [])].sort(
+ (a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)
+ ),
layoutTemplate
);
};
@@ -193,8 +206,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc);
if (docView) {
LightboxView._docTarget = target;
- if (!target) docView.ComponentView?.shrinkWrap?.();
- else docView.focus(target, { willPanZoom: true, zoomScale: 0.9 });
+ target && docView.focus(target, { willPanZoom: true, zoomScale: 0.9 });
} else {
LightboxView.SetLightboxDoc(doc, target);
}
@@ -218,7 +230,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
.filter(doc => doc)
.map(doc => doc!);
LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links);
- TabDocView.PinDoc(coll, { hidePresBox: true });
}
};
@@ -251,19 +262,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
<GestureOverlay isActive={true}>
<DocumentView
- ref={action((r: DocumentView | null) => {
- LightboxView._docView = r !== null ? r : undefined;
- r &&
- setTimeout(
- action(() => {
- const target = LightboxView._docTarget;
- const doc = LightboxView._doc;
- //const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target);
- //if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.();
- //else target?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here? it breaks smooth navigation in lightbox using 'next' button
- })
- );
- })}
+ ref={action((r: DocumentView | null) => (LightboxView._docView = r !== null ? r : undefined))}
Document={LightboxView.LightboxDoc}
DataDoc={undefined}
PanelWidth={this.lightboxWidth}
@@ -332,7 +331,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
onClick={e => {
e.stopPropagation();
CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, OpenWhereMod.none);
- //LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace");
SelectionManager.DeselectAll();
LightboxView.SetLightboxDoc(undefined);
}}>
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 66c3ed439..65a950711 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -77,6 +77,14 @@ export class PropertiesButtons extends React.Component<{}, {}> {
(dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc)
);
}
+ @computed get hideImageButton() {
+ return this.propertyToggleBtn(
+ 'Background',
+ '_hideImage',
+ on => (on ? 'Show Image' : 'Show Background'),
+ on => 'portrait'
+ );
+ }
@computed get clustersButton() {
return this.propertyToggleBtn(
'Clusters',
@@ -383,6 +391,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)];
const isText = layoutField instanceof RichTextField;
const isInk = layoutField instanceof InkField;
+ const isImage = layoutField instanceof ImageField;
const isMap = this.selectedDoc?.type === DocumentType.MAP;
const isCollection = this.selectedDoc?.type === DocumentType.COL;
//TODO: will likely need to create separate note-taking view type here
@@ -410,6 +419,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
{toggle(this.inPlaceContainerButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
{toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
{toggle(this.maskButton, { display: !isInk ? 'none' : '' })}
+ {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })}
{toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })}
{toggle(this.gridButton, { display: !isCollection ? 'none' : '' })}
{toggle(this.groupButton, { display: isTabView || !isCollection ? 'none' : '' })}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 411f51d84..a2bc37095 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -143,7 +143,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
: layoutDoc._fitWidth
? !Doc.NativeHeight(this.dataDoc)
? NumCast(this.props.height)
- : Math.min((this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height))
+ : Math.min((this.docWidth() * Doc.NativeHeight(layoutDoc)) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height))
: NumCast(layoutDoc._height) || 50
)
);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index ece224c68..3cb920ba0 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -269,9 +269,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
default:
return doc.z
? `#9c9396 ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
- : props?.ContainingCollectionDoc?._useClusters && doc.type !== DocumentType.INK
+ : props?.ContainingCollectionDoc?._useClusters
? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
- : NumCast(doc.group, -1) !== -1 && doc.type !== DocumentType.INK
+ : NumCast(doc.group, -1) !== -1
? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
: isBackground()
? undefined // if it's a background & has a cluster color, make the shadow spread really big
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a28b1ca19..90406ed3c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -256,7 +256,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle);
childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
@computed get childLayoutString() {
- return StrCast(this.rootDoc.childLayoutString);
+ return StrCast(this.rootDoc.childLayoutString, this.props.childLayoutString);
}
isContentActive = (outsideReaction?: boolean) => this.props.isContentActive();
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 25fccd89c..d6bc0a4b2 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -248,7 +248,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return;
}
const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false);
- const pinDoc = Doc.MakeAlias(anchorDoc ?? doc);
+ const pinDoc = Doc.MakeDelegate(anchorDoc ?? doc);
pinDoc.presentationTargetDoc = anchorDoc ?? doc;
pinDoc.title = doc.title + ' - Slide';
pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children. the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
@@ -274,7 +274,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
pinDoc.presStartTime = NumCast(doc.clipStart);
pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
}
- PresBox.pinDocView(pinDoc, pinProps.pinDocContent ? { ...pinProps, pinData: PresBox.pinDataTypes(doc) } : pinProps, pinDoc);
+ PresBox.pinDocView(pinDoc, pinProps.pinDocContent ? { ...pinProps, pinData: PresBox.pinDataTypes(doc) } : pinProps, DocCast(pinDoc.proto));
pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)');
Doc.AddDocToList(curPres, 'data', pinDoc, presSelected);
//save position
@@ -359,7 +359,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
// prettier-ignore
switch (whereFields[0]) {
case OpenWhere.inPlace: // fall through to lightbox
- case OpenWhere.lightbox: return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab);
+ case OpenWhere.lightbox: return LightboxView.AddDocTab(doc, location);
case OpenWhere.dashboard: return DashboardView.openDashboard(doc);
case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc);
case OpenWhere.close: return CollectionDockingView.CloseSplit(doc, whereMods);
@@ -382,14 +382,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
};
@action
focusFunc = (doc: Doc, options: DocFocusOptions) => {
- const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap;
- if (options?.willPanZoom !== false && shrinkwrap && this._document) {
- const focusSpeed = options.zoomTime ?? 500;
- shrinkwrap();
- this._view?.setViewTransition('transform', focusSpeed, () => options?.afterFocus?.(false));
- } else {
- options?.afterFocus?.(false);
- }
+ options?.afterFocus?.(false);
+
if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) {
this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index bd326f917..2398d8f58 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -410,7 +410,7 @@ export class TreeView extends React.Component<TreeViewProps> {
if (aspect) return this.embeddedPanelWidth() / (aspect || 1);
return layoutDoc._fitWidth
? !Doc.NativeHeight(layoutDoc)
- ? NumCast(layoutDoc._height) //this.props.containerCollection._height)
+ ? NumCast(layoutDoc._height)
: Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height)))
: (this.embeddedPanelWidth() * layoutDoc[HeightSym]()) / layoutDoc[WidthSym]();
})()
@@ -957,6 +957,7 @@ export class TreeView extends React.Component<TreeViewProps> {
);
};
+ fitWidthFilter = (doc: Doc) => (doc.type === DocumentType.IMG ? false : undefined);
renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => {
return (
<div style={{ height: this.embeddedPanelHeight(), width: this.embeddedPanelWidth() }}>
@@ -965,6 +966,7 @@ export class TreeView extends React.Component<TreeViewProps> {
ref={action((r: DocumentView | null) => (this._dref = r))}
Document={this.doc}
DataDoc={undefined}
+ fitWidth={this.fitWidthFilter}
PanelWidth={this.embeddedPanelWidth}
PanelHeight={this.embeddedPanelHeight}
LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 7a7ae3f40..c6419885b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -33,7 +33,7 @@
}
.collectionfreeformview-mask {
mix-blend-mode: multiply;
- background-color: rgba(0, 0, 0, 0.7);
+ background-color: rgba(0, 0, 0, 0.8);
}
.collectionfreeformview-viewdef {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7aae5ed5f..d6e95f97f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -16,7 +16,7 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro
import { ImageField } from '../../../../fields/URLField';
import { TraceMobx } from '../../../../fields/util';
import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { aggregateBounds, emptyFunction, intersectRect, returnFalse, returnTransparent, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -61,6 +61,7 @@ export type collectionFreeformViewProps = {
scaleField?: string;
noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
engineProps?: any;
+ getScrollHeight?: () => number | undefined;
dontScaleFilter?: (doc: Doc) => boolean; // whether this collection should scale documents to fit their panel vs just scrolling them
dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are transparent or not.
// However, this screws up interactions since only the top layer gets events. so we render the freeformview a 3rd time with all documents in order to get interaction events (eg., marquee) but we don't actually want to display the documents.
@@ -363,10 +364,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const grouping = this.props.Document._useClusters ? NumCast(cd.cluster, -1) : NumCast(cd.group, -1);
if (grouping !== -1) {
const layoutDoc = Doc.Layout(cd);
- const cx = NumCast(cd.x) - this._clusterDistance;
- const cy = NumCast(cd.y) - this._clusterDistance;
- const cw = NumCast(layoutDoc._width) + 2 * this._clusterDistance;
- const ch = NumCast(layoutDoc._height) + 2 * this._clusterDistance;
+ const cx = NumCast(cd.x) - this._clusterDistance / 2;
+ const cy = NumCast(cd.y) - this._clusterDistance / 2;
+ const cw = NumCast(layoutDoc._width) + this._clusterDistance;
+ const ch = NumCast(layoutDoc._height) + this._clusterDistance;
return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
}
return cluster;
@@ -545,7 +546,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
case InkTool.None:
if (!(this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
- setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, false);
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, this._hitCluster !== -1 ? true : false);
}
break;
}
@@ -771,7 +772,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
} else if (!e.cancelBubble) {
if (this.tryDragCluster(e, this._hitCluster)) {
return true;
- } else this.pan(e);
+ }
+ this.pan(e);
}
return false;
};
@@ -1004,6 +1006,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (deltaScale * invTransform.Scale > 20) {
deltaScale = 20 / invTransform.Scale;
}
+ if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) {
+ return;
+ }
if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) {
deltaScale = NumCast(this.rootDoc._viewScaleMin, 1) / invTransform.Scale;
}
@@ -1012,29 +1017,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (localTransform.Scale >= 0.05 || localTransform.Scale > this.zoomScaling()) {
const safeScale = Math.min(Math.max(0.05, localTransform.Scale), 20);
this.props.Document[this.scaleFieldKey] = Math.abs(safeScale);
- this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
+ this.setPan(-localTransform.TranslateX / safeScale, NumCast(this.props.Document.scrollTop) * safeScale || -localTransform.TranslateY / safeScale);
}
};
@action
onPointerWheel = (e: React.WheelEvent): void => {
+ if (this.Document._isGroup) return; // group style collections neither pan nor zoom
PresBox.Instance?.pauseAutoPres();
if (this.layoutDoc._Transform || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
e.stopPropagation();
e.preventDefault();
switch (!e.ctrlKey ? Doc.UserDoc().freeformScrollMode : freeformScrollMode.Pan) {
case freeformScrollMode.Pan:
- // if shift is selected then zoom
+ // if ctrl is selected then zoom
if (e.ctrlKey) {
- if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
- // things that can scroll vertically should do that instead of zooming
- } else if (this.props.isContentActive(true) && !this.Document._isGroup) {
+ if (this.props.isContentActive(true)) {
!this.props.isAnnotationOverlayScrollable && this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
}
- // otherwise pan
- } else if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
- // things that can scroll vertically should do that instead of zooming
- } else if (this.props.isContentActive(true) && !this.Document._isGroup) {
+ } // otherwise pan
+ else if (this.props.isContentActive(true)) {
const dx = -e.deltaX;
const dy = -e.deltaY;
if (e.shiftKey) {
@@ -1046,9 +1048,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
break;
default:
case freeformScrollMode.Zoom:
- if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
- // things that can scroll vertically should do that instead of zooming
- } else if (this.props.isContentActive(true) && !this.Document._isGroup) {
+ if (this.props.isContentActive(true)) {
!this.props.isAnnotationOverlayScrollable && this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
}
break;
@@ -1096,7 +1096,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const minPanX = NumCast(this.rootDoc._panXMin, 0);
const minPanY = NumCast(this.rootDoc._panYMin, 0);
const newPanX = Math.min(minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._panXMax, this.nativeWidth), Math.max(minPanX, panX));
- const newPanY = Math.min(this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : minPanY + (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, this.nativeHeight), Math.max(minPanY, panY));
+ const fitYscroll = (((this.nativeHeight / this.nativeWidth) * this.props.PanelWidth() - this.props.PanelHeight()) * this.props.ScreenToLocalTransform().Scale) / minScale;
+ const nativeHeight = (this.props.PanelHeight() / this.props.PanelWidth() / (this.nativeHeight / this.nativeWidth)) * this.nativeHeight;
+ const maxScrollTop = this.nativeHeight / this.props.ScreenToLocalTransform().Scale - this.props.PanelHeight();
+ const maxPanY =
+ minPanY + // minPanY + scrolling introduced by view scaling + scrolling introduced by fitWidth
+ (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, nativeHeight) +
+ (!this.props.getScrollHeight?.() ? fitYscroll : 0); // when not zoomed, scrolling is handled via a scrollbar, not panning
+ let newPanY = Math.max(minPanY, Math.min(maxPanY, panY));
+ if (NumCast(this.rootDoc.scrollTop) && NumCast(this.rootDoc._viewScale, minScale) !== minScale) {
+ const relTop = NumCast(this.rootDoc.scrollTop) / maxScrollTop;
+ this.rootDoc.scrollTop = undefined;
+ newPanY = minPanY + relTop * (maxPanY - minPanY);
+ } else if (fitYscroll && this.rootDoc.scrollTop === undefined && NumCast(this.rootDoc._viewScale, minScale) === minScale) {
+ const maxPanY = minPanY + fitYscroll;
+ const relTop = (panY - minPanY) / (maxPanY - minPanY);
+ setTimeout(() => {
+ this.rootDoc.scrollTop = relTop * maxScrollTop;
+ }, 10);
+ newPanY = minPanY;
+ }
!this.Document._verticalScroll && (this.Document._panX = this.isAnnotationOverlay ? newPanX : panX);
!this.Document._horizontalScroll && (this.Document._panY = this.isAnnotationOverlay ? newPanY : panY);
}
@@ -1169,7 +1188,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) {
// SelectionManager.DeselectAll();
// }
- if (this.props.Document.scrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined || doc.type === DocumentType.MARKER) {
+ if (this.props.getScrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined || doc.type === DocumentType.MARKER) {
this.props.focus(doc, options);
} else {
const xfToCollection = options?.docTransform ?? Transform.Identity();
@@ -2033,7 +2052,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
: (this.props.pointerEvents?.() as any),
transform: `scale(${this.nativeDimScaling || 1})`,
width: `${100 / (this.nativeDimScaling || 1)}%`,
- height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.nativeDimScaling || 1)}%`, // : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
+ height: this.props.getScrollHeight?.() ?? `${100 / (this.nativeDimScaling || 1)}%`,
}}>
{this._firstRender ? this.placeholder : this.marqueeView}
{this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 0b7854926..bc3b17cd9 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -431,8 +431,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@undoBatch
@action
- collection = (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => {
- const selected = this.marqueeSelect(false);
+ collection = (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean, selection?: Doc[]) => {
+ const selected = selection ?? this.marqueeSelect(false);
const activeFrame = selected.reduce((v, d) => v ?? Cast(d._activeFrame, 'number', null), undefined as number | undefined);
if (e instanceof KeyboardEvent ? 'cg'.includes(e.key) : true) {
this.props.removeDocument?.(selected);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3bab8bfd5..44cedab4c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -56,6 +56,7 @@ import { ScriptingBox } from './ScriptingBox';
import { PresEffect, PresEffectDirection } from './trails';
import { PinProps } from './trails/PresBox';
import React = require('react');
+import { GestureOverlay } from '../GestureOverlay';
const { Howl } = require('howler');
interface Window {
@@ -139,7 +140,6 @@ export interface DocComponentView {
fieldKey?: string;
annotationKey?: string;
getTitle?: () => string;
- getScrollHeight?: () => number;
getCenter?: (xf: Transform) => { X: number; Y: number };
ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
@@ -170,7 +170,7 @@ export interface DocumentViewSharedProps {
dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt
styleProvider: Opt<StyleProviderFunc>;
focus: DocFocusFunc;
- fitWidth?: (doc: Doc) => boolean;
+ fitWidth?: (doc: Doc) => boolean | undefined;
docFilters: () => string[];
docRangeFilters: () => string[];
searchFilterDocs: () => Doc[];
@@ -637,8 +637,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
console.log
);
UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
- } else if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isLinkButton) {
- UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.(), this.props.addDocTab), 'double tap');
+ } else if (!Doc.IsSystem(this.rootDoc) && (![DocumentType.INK].includes(this.rootDoc.type as any) || Doc.UserDoc().openInkInLightbox) && !this.rootDoc.isLinkButton) {
+ UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap');
SelectionManager.DeselectAll();
Doc.UnBrushDoc(this.props.Document);
}
@@ -669,7 +669,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this._timeout = setTimeout(() => {
this._timeout = undefined;
clickFunc();
- }, 350);
+ }, 150);
} else clickFunc();
} else if (!this._longPress && this.allLinks.length && this.Document.type !== DocumentType.LINK && !isScriptBox() && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
SelectionManager.DeselectAll();
@@ -698,6 +698,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@action
onPointerDown = (e: React.PointerEvent): void => {
+ if (!(e.nativeEvent as any).DownDocView) (e.nativeEvent as any).DownDocView = GestureOverlay.DownDocView = this.props.DocumentView();
if (this.rootDoc.type === DocumentType.INK && Doc.ActiveTool === InkTool.Eraser) return;
// continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool))) {
@@ -1640,7 +1641,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.docView?.LayoutFieldKey || 'layout';
}
get fitWidth() {
- return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth;
+ return this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth;
}
@computed get hideLinkButton() {
@@ -1688,8 +1689,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
@computed get panelHeight() {
if (this.effectiveNativeHeight && !this.layoutDoc.nativeHeightUnfrozen) {
- const scrollHeight = this.fitWidth ? Math.max(this.ComponentView?.getScrollHeight?.() ?? NumCast(this.layoutDoc.scrollHeight)) : 0;
- return Math.min(this.props.PanelHeight(), Math.max(scrollHeight, this.effectiveNativeHeight) * this.nativeScaling);
+ return Math.min(this.props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling);
}
return this.props.PanelHeight();
}
@@ -1902,6 +1902,11 @@ ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
documentView.select(false);
});
+ScriptingGlobals.add(function deiconifyViewToLightbox(documentView: DocumentView) {
+ //documentView.iconify(() =>
+ LightboxView.AddDocTab(documentView.rootDoc, OpenWhere.lightbox, 'layout'); //, 0);
+});
+
ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
if (dv.Document.layoutKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout');
else dv.switchViews(true, detailLayoutKeySuffix, undefined, true);
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index e93bab149..ae851d6a6 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -11,7 +11,7 @@ import { ComputedField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
+import { DashColor, emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../../documents/Documents';
@@ -26,12 +26,15 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp
import { MarqueeAnnotator } from '../MarqueeAnnotator';
import { AnchorMenu } from '../pdf/AnchorMenu';
import { StyleProp } from '../StyleProvider';
+import { DocFocusOptions, OpenWhere } from './DocumentView';
import { FaceRectangles } from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import './ImageBox.scss';
-import React = require('react');
import { PresBox } from './trails';
-import { DocFocusOptions, DocumentViewProps } from './DocumentView';
+import React = require('react');
+import Color = require('color');
+import { LinkDocPreview } from './LinkDocPreview';
+import { DocumentManager } from '../../util/DocumentManager';
export const pageSchema = createSchema({
googlePhotosUrl: 'string',
@@ -50,6 +53,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(ImageBox, fieldKey);
}
+ private _ignoreScroll = false;
+ private _forcedScroll = false;
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined;
@@ -118,6 +123,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
},
{ fireImmediately: true }
);
+ this._disposers.scroll = reaction(
+ () => this.layoutDoc._scrollTop,
+ s_top => {
+ this._forcedScroll = true;
+ !this._ignoreScroll && this._mainCont.current && (this._mainCont.current.scrollTop = NumCast(s_top));
+ this._mainCont.current?.scrollTo({ top: NumCast(s_top) });
+ this._forcedScroll = false;
+ },
+ { fireImmediately: true }
+ );
}
componentWillUnmount() {
@@ -155,6 +170,21 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
setUseAlt = () => (this.layoutDoc[this.fieldKey + '-useAlt'] = !this.layoutDoc[this.fieldKey + '-useAlt']);
@undoBatch
+ setNativeSize = action(() => {
+ const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._viewScale, 1);
+ const nscale = NumCast(this.props.PanelWidth()) / scaling;
+ const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
+ const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
+ this.dataDoc[this.fieldKey + '-nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']) * nh;
+ this.dataDoc[this.fieldKey + '-nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']) * nw;
+ this.rootDoc._panX = nh * NumCast(this.rootDoc._panX);
+ this.rootDoc._panY = nw * NumCast(this.rootDoc._panY);
+ this.dataDoc._panXMax = this.dataDoc._panXMax ? nh * NumCast(this.dataDoc._panXMax) : undefined;
+ this.dataDoc._panXMin = this.dataDoc._panXMin ? nh * NumCast(this.dataDoc._panXMin) : undefined;
+ this.dataDoc._panYMax = this.dataDoc._panYMax ? nw * NumCast(this.dataDoc._panYMax) : undefined;
+ this.dataDoc._panYMin = this.dataDoc._panYMin ? nw * NumCast(this.dataDoc._panYMin) : undefined;
+ });
+ @undoBatch
rotate = action(() => {
const nw = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
const nh = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
@@ -188,6 +218,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const croppingProto = Doc.GetProto(cropping);
croppingProto.annotationOn = undefined;
croppingProto.isPrototype = true;
+ croppingProto.backgroundColor = undefined;
croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
croppingProto.type = DocumentType.IMG;
croppingProto.layout = ImageBox.LayoutString('data');
@@ -204,7 +235,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
croppingProto.panYMax = anchh / viewScale;
if (addCrop) {
DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', '');
+ cropping.x = NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]();
+ cropping.y = NumCast(this.rootDoc.y);
+ this.props.addDocTab(cropping, OpenWhere.inParent);
}
+ DocumentManager.Instance.AddViewRenderedCb(cropping, dv => setTimeout(() => (dv.ComponentView as ImageBox).setNativeSize(), 200));
this.props.bringToFront(cropping);
return cropping;
};
@@ -215,6 +250,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const funcs: ContextMenuProps[] = [];
funcs.push({ description: 'Rotate Clockwise 90', event: this.rotate, icon: 'redo-alt' });
funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? 'Dynamic Res' : 'Full Res'}`, event: this.resolution, icon: 'expand' });
+ funcs.push({ description: 'Set Native Pixel Size', event: this.setNativeSize, icon: 'expand-arrows-alt' });
funcs.push({ description: `${this.layoutDoc[this.fieldKey + '-useAlt'] ? 'Show Alternate' : 'Show Primary'}`, event: this.setUseAlt, icon: 'image' });
funcs.push({ description: 'Copy path', event: () => Utils.CopyText(this.choosePath(field.url)), icon: 'copy' });
if (!Doc.noviceMode) {
@@ -281,6 +317,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return !tags ? null : <img id={'google-tags'} src={'/assets/google_tags.png'} />;
};
+ getScrollHeight = () => (this.props.fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._viewScale, 1) === NumCast(this.rootDoc._viewScaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
+
@computed
private get considerDownloadIcon() {
const data = this.dataDoc[this.fieldKey];
@@ -345,8 +383,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@computed get content() {
TraceMobx();
- const srcpath = this.paths[0];
- const fadepath = this.paths.lastElement();
+ const col = DashColor(this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor));
+ const srcpath = this.layoutDoc.hideImage ? '' : this.paths[0];
+ const fadepath = this.layoutDoc.hideImage ? '' : this.paths.lastElement();
const { nativeWidth, nativeHeight, nativeOrientation } = this.nativeSize;
const rotation = NumCast(this.dataDoc[this.fieldKey + '-rotation']);
const aspect = rotation % 180 ? nativeHeight / nativeWidth : 1;
@@ -364,7 +403,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return (
<div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
- <div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? 'auto' : undefined }}>
+ <div className="imageBox-fader" style={{ opacity: col.alpha() }}>
<img key="paths" src={srcpath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
{fadepath === srcpath ? null : (
<div
@@ -381,7 +420,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
);
}
- screenToLocalTransform = this.props.ScreenToLocalTransform;
contentFunc = () => [this.content];
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
@@ -392,6 +430,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
TraceMobx();
return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
}
+ screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop) * this.props.ScreenToLocalTransform().Scale);
marqueeDown = (e: React.PointerEvent) => {
if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale, 1) <= NumCast(this.rootDoc.viewScaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
setupMoveUpEvents(
@@ -415,11 +454,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this.props.select(false);
};
savedAnnotations = () => this._savedAnnotations;
- styleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
- if (property === StyleProp.BoxShadow) return undefined;
- return this.props.styleProvider?.(doc, props, property);
- };
-
render() {
TraceMobx();
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
@@ -429,17 +463,27 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
className="imageBox"
onContextMenu={this.specificContextMenu}
ref={this._mainCont}
+ onScroll={action(e => {
+ if (!this._forcedScroll) {
+ if (this.layoutDoc._scrollTop || this._mainCont.current?.scrollTop) {
+ this._ignoreScroll = true;
+ this.layoutDoc._scrollTop = this._mainCont.current?.scrollTop;
+ this._ignoreScroll = false;
+ }
+ }
+ })}
style={{
width: this.props.PanelWidth() ? undefined : `100%`,
height: this.props.PanelWidth() ? undefined : `100%`,
pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
borderRadius,
+ overflow: this.layoutDoc.fitWidth || this.props.fitWidth?.(this.rootDoc) ? 'auto' : undefined,
}}>
<CollectionFreeFormView
{...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
renderDepth={this.props.renderDepth + 1}
fieldKey={this.annotationKey}
- styleProvider={this.styleProvider}
+ styleProvider={this.props.styleProvider}
CollectionView={undefined}
isAnnotationOverlay={true}
annotationLayerHostsContent={true}
@@ -447,6 +491,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
PanelHeight={this.props.PanelHeight}
ScreenToLocalTransform={this.screenToLocalTransform}
select={emptyFunction}
+ getScrollHeight={this.getScrollHeight}
NativeDimScaling={returnOne}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
removeDocument={this.removeDocument}
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index eba9e281d..1b37dc7ab 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -60,6 +60,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
} else {
this.rootDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100;
this.rootDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100;
+ this.rootDoc.linkAutoMove = false;
}
}
return false;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 95cb49037..039f11cd1 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,5 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
+import BingMapsReact from 'bingmaps-react';
import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -43,20 +44,22 @@ const mapContainerStyle = {
};
const defaultCenter = {
- lat: 38.685,
- lng: -115.234,
+ lat: 42.360081,
+ lng: -71.058884,
};
const mapOptions = {
fullscreenControl: false,
};
+const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/ and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
const apiKey = process.env.GOOGLE_MAPS;
const script = document.createElement('script');
script.defer = true;
script.async = true;
script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
+console.log(script.src);
document.head.appendChild(script);
/**
@@ -84,6 +87,7 @@ const options = {
@observer
export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
+ static UseBing = true;
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -542,8 +546,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// }
};
- panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
- panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+ panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
+ panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
@@ -551,69 +555,99 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
infoHeight = () => this.props.PanelHeight() / 5;
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
savedAnnotations = () => this._savedAnnotations;
+
+ _bingSearchManager: any;
+ _bingMap: any;
+ get MicrosoftMaps() {
+ return (window as any).Microsoft.Maps;
+ }
+ // uses Bing Search to retrieve lat/lng for a location. eg.,
+ // const results = this.geocodeQuery(map.map, 'Philadelphia, PA');
+ // to move the map to that location:
+ // const location = await this.geocodeQuery(this._bingMap, 'Philadelphia, PA');
+ // this._bingMap.current.setView({
+ // mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
+ // center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
+ // });
+ //
+ bingGeocode = (map: any, query: string) => {
+ return new Promise<{ latitude: number; longitude: number }>((res, reject) => {
+ //If search manager is not defined, load the search module.
+ if (!this._bingSearchManager) {
+ //Create an instance of the search manager and call the geocodeQuery function again.
+ this.MicrosoftMaps.loadModule('Microsoft.Maps.Search', () => {
+ this._bingSearchManager = new this.MicrosoftMaps.Search.SearchManager(map.current);
+ res(this.bingGeocode(map, query));
+ });
+ } else {
+ this._bingSearchManager.geocode({
+ where: query,
+ callback: action((r: any) => {
+ res(r.results[0].location);
+ }),
+ errorCallback: (e: any) => reject(),
+ });
+ }
+ });
+ };
+
+ bingViewOptions = {
+ center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },
+ mapTypeId: 'grayscale',
+ };
+ bingMapOptions = {
+ navigationBarMode: 'square',
+ };
+ bingMapReady = (map: any) => (this._bingMap = map.map);
render() {
const renderAnnotations = (docFilters?: () => string[]) => null;
- // bcz: commmented this out. Otherwise, any documents that are rendered with an InfoWindow of a marker
- // will also be rendered as freeform annotations on the map. However, it doesn't seem that rendering
- // freeform documents on the map does anything anyway, so getting rid of it for now. Also, since documents
- // are rendered twice, adding a new note to the InfoWindow loses focus immediately on creation since it gets
- // shifted to the non-visible view of the document in this freeform view.
- // <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
- // renderDepth={this.props.renderDepth + 1}
- // isAnnotationOverlay={true}
- // fieldKey={this.annotationKey}
- // CollectionView={undefined}
- // setPreviewCursor={this.setPreviewCursor}
- // PanelWidth={this.panelWidth}
- // PanelHeight={this.panelHeight}
- // ScreenToLocalTransform={this.scrollXf}
- // scaling={returnOne}
- // dropAction={"alias"}
- // docFilters={docFilters || this.props.docFilters}
- // dontRenderDocuments={docFilters ? false : true}
- // select={emptyFunction}
- // bringToFront={emptyFunction}
- // whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- // removeDocument={this.removeDocument}
- // moveDocument={this.moveDocument}
- // addDocument={this.sidebarAddDocument}
- // childPointerEvents={"all"}
- // pointerEvents={Doc.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
return (
<div className="mapBox" ref={this._ref}>
- {/*console.log(apiKey)*/}
- {/* <LoadScript
- googleMapsApiKey={apiKey!}
- libraries={['places', 'drawing']}
- > */}
- <div className="mapBox-wrapper" onWheel={e => e.stopPropagation()} onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
+ <div
+ className="mapBox-wrapper"
+ onWheel={e => e.stopPropagation()}
+ onPointerDown={async e => {
+ e.button === 0 && !e.ctrlKey && e.stopPropagation();
+ // just a simple test of bing maps geocode api
+ // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA');
+ // this._bingMap.current.setView({
+ // mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
+ // center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
+ // zoom: 15,
+ // });
+ }}
+ style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
<div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
{renderAnnotations(this.opaqueFilter)}
{SnappingManager.GetIsDragging() ? null : renderAnnotations()}
{this.annotationLayer}
- <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
- <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
- <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
- </Autocomplete>
-
- {this.renderMarkers()}
- {this.allMapMarkers
- .filter(marker => marker.infoWindowOpen)
- .map(marker => (
- <MapBoxInfoWindow
- key={marker[Id]}
- {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
- place={marker}
- markerMap={this.markerMap}
- PanelWidth={this.infoWidth}
- PanelHeight={this.infoHeight}
- moveDocument={this.moveDocument}
- isAnyChildContentActive={this.isAnyChildContentActive}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- />
- ))}
- {/* {this.handleMapCenter(this._map)} */}
- </GoogleMap>
+
+ {!MapBox.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
+ <div style={{ display: MapBox.UseBing ? 'none' : undefined }}>
+ <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
+ <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
+ <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
+ </Autocomplete>
+
+ {this.renderMarkers()}
+ {this.allMapMarkers
+ .filter(marker => marker.infoWindowOpen)
+ .map(marker => (
+ <MapBoxInfoWindow
+ key={marker[Id]}
+ {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+ place={marker}
+ markerMap={this.markerMap}
+ PanelWidth={this.infoWidth}
+ PanelHeight={this.infoHeight}
+ moveDocument={this.moveDocument}
+ isAnyChildContentActive={this.isAnyChildContentActive}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+ />
+ ))}
+ {/* {this.handleMapCenter(this._map)} */}
+ </GoogleMap>
+ </div>
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
rootDoc={this.rootDoc}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index b88ac113e..5890c536d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -222,6 +222,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const anchor = Docs.Create.TextanchorDocument({
title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
unrendered: true,
+ annotationOn: this.rootDoc,
});
PresBox.pinDocView(anchor, { pinData: { scrollable: true, pannable: true } }, this.rootDoc);
return anchor;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index d0d638e98..c392f3577 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -295,12 +295,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
if (doc !== this.rootDoc && this._outerRef.current) {
const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
- const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(doc.y) + doc[HeightSym](), this.getScrollHeight()));
+ const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(doc.y) + doc[HeightSym](), this._scrollHeight));
if (scrollTo !== undefined && this._initialScroll === undefined) {
const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
this.goTo(scrollTo, focusSpeed, options.easeFunc);
return focusSpeed;
- } else if (!this._webPageHasBeenRendered || !this.getScrollHeight() || this._initialScroll !== undefined) {
+ } else if (!this._webPageHasBeenRendered || !this._scrollHeight || this._initialScroll !== undefined) {
this._initialScroll = scrollTo;
}
}
@@ -322,6 +322,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
y: NumCast(this.layoutDoc._scrollTop),
unrendered: true,
+ annotationOn: this.rootDoc,
});
anchor.text = ele?.textContent ?? '';
anchor.textHtml = ele?.innerHTML;
@@ -376,9 +377,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
ContextMenu.Instance.setIgnoreEvents(true);
}
};
-
- getScrollHeight = () => this._scrollHeight;
-
isFirefox = () => {
return 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1;
};
@@ -969,8 +967,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
- panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
- panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+ panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth;
+ panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 9ef32014b..9d67283a0 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -90,7 +90,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
static GetRecognizeGestures() {
return BoolCast(Doc.UserDoc()._recognizeGestures);
}
- static SetRecognizeGesturs(show: boolean) {
+ static SetRecognizeGestures(show: boolean) {
Doc.UserDoc()._recognizeGestures = show;
}
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 722d0cc4f..19ee1fc7b 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -12,6 +12,7 @@ import { Transform } from '../../../util/Transform';
import { DocumentView } from '../DocumentView';
import { FormattedTextBox } from './FormattedTextBox';
import React = require('react');
+import { SelectionManager } from '../../../util/SelectionManager';
export class DashDocView {
dom: HTMLSpanElement; // container for label and value
@@ -192,10 +193,10 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
Document={this._finalLayout}
DataDoc={this._resolvedDataDoc}
addDocument={returnFalse}
- rootSelected={this._textBox.props.isSelected}
+ rootSelected={returnFalse} //{this._textBox.props.isSelected}
removeDocument={this.removeDoc}
isDocumentActive={returnFalse}
- isContentActive={this._textBox.props.isContentActive}
+ isContentActive={emptyFunction}
styleProvider={this._textBox.props.styleProvider}
docViewPath={this._textBox.props.docViewPath}
ScreenToLocalTransform={this.getDocTransform}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 619c59f0e..49acd8a57 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -994,7 +994,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
return this._didScroll ? this._focusSpeed : didToggle ? 1 : undefined; // if we actually scrolled, then return some focusSpeed
};
- getScrollHeight = () => this.scrollHeight;
// if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc.
// Since we also monitor all component height changes, this will update the document's height.
resetNativeHeight = (scrollHeight: number) => {
@@ -1488,6 +1487,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
while (target && !target.dataset?.targethrefs) target = target.parentElement;
FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
+ if (pcords && pcords.inside > 0 && this._editorView.state.doc.nodeAt(pcords.inside)?.type === this._editorView.state.schema.nodes.dashDoc) {
+ return;
+ }
}
if (e.button === 0 && this.props.isSelected(true) && !e.altKey) {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 427911bd3..e07517113 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -14,7 +14,7 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Ty
import { AudioField } from '../../../../fields/URLField';
import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
-import { Docs } from '../../../documents/Documents';
+import { Docs, DocumentOptions } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
@@ -35,6 +35,7 @@ import { ScriptingBox } from '../ScriptingBox';
import './PresBox.scss';
import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline';
+import { PresElementBox } from './PresElementBox';
const { Howl } = require('howler');
export interface PinProps {
@@ -383,9 +384,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List<Doc>([...DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered), ...DocListCast(activeItem.presAnnotations)]);
}
if (pinDataTypes.dataview && activeItem.presData !== undefined) {
+ bestTarget._dataTransition = `all ${transTime}ms`;
const fkey = Doc.LayoutFieldKey(bestTarget);
Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt;
+ setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
}
if (pinDataTypes.textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
if (pinDataTypes.poslayoutview) {
@@ -866,13 +869,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
presDocView && SelectionManager.SelectView(presDocView, false);
};
+ focusElement = (doc: Doc, options: DocFocusOptions) => this.selectElement(doc);
+
//Regular click
@action
- selectElement = async (doc: Doc) => {
+ selectElement = async (doc: Doc, noNav = false) => {
CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
- this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
- // if (doc.presPinView) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
- // else
+ !noNav && this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
this.updateCurrentPresentation(DocCast(doc.context));
};
@@ -908,19 +911,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
//regular click
@action
- regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => {
+ regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true, noNav = false) => {
this.clearSelectedArray();
this.addToSelectedArray(doc);
this._eleArray.splice(0, this._eleArray.length, ref);
this._dragArray.splice(0, this._dragArray.length, drag);
- focus && this.selectElement(doc);
+ focus && this.selectElement(doc, noNav);
selectPres && this.selectPres();
};
- modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => {
+ modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean, noNav: boolean = false) => {
if (cmdClick) this.multiSelect(doc, ref, drag);
else if (shiftClick) this.shiftSelect(doc, ref, drag);
- else this.regularSelect(doc, ref, drag, focus);
+ else this.regularSelect(doc, ref, drag, focus, noNav);
};
static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance?.keyEvents(e);
@@ -2244,12 +2247,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
ignoreUnrendered={true}
//childFitWidth={returnTrue}
childOpacity={returnOne}
+ //childLayoutString={PresElementBox.LayoutString('data')}
childLayoutTemplate={this.childLayoutTemplate}
childXPadding={Doc.IsComicStyle(this.rootDoc) ? 20 : undefined}
filterAddDocument={this.addDocumentFilter}
removeDocument={returnFalse}
dontRegisterView={true}
- focus={this.selectElement}
+ focus={this.focusElement}
scriptContext={this}
ScreenToLocalTransform={this.getTransform}
AddToMap={this.AddToMap}
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index 415253af1..4f95f0c1f 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
@@ -44,6 +44,12 @@ $slide-active: #5b9fdd;
display: flex;
align-items: center;
+ .presItem-number {
+ cursor: pointer;
+ &:hover {
+ background-color: $light-blue;
+ }
+ }
.presItem-name {
display: flex;
min-width: 20px;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 788900b46..f265c1315 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -592,7 +592,17 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
width: `calc(100% ${this.rootDoc.presExpandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
cursor: isSelected ? 'text' : 'grab',
}}>
- <div>{`${this.indexInPres + 1}. `}</div>
+ <div
+ className="presItem-number"
+ title="select without navigation"
+ style={{ pointerEvents: this.presBoxView?.isContentActive() ? 'all' : undefined }}
+ onPointerDown={e => {
+ e.stopPropagation();
+ if (this._itemRef.current && this._dragRef.current) {
+ this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, false, false, false, true);
+ }
+ }}
+ onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}. `}</div>
<EditableView ref={this._titleRef} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
</div>
{/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index c53cc608c..4a7f5d038 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -11,6 +11,7 @@ import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
import { LinkPopup } from '../linking/LinkPopup';
import { ButtonDropdown } from '../nodes/formattedText/RichTextMenu';
import './AnchorMenu.scss';
+import { LightboxView } from '../LightboxView';
@observer
export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index b0b7816b8..9610a71ac 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -144,6 +144,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
}
};
+ @observable _scrollHeight = 0;
+
@action
initialLoad = async () => {
if (this._pageSizes.length === 0) {
@@ -164,8 +166,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
)
)
);
- this.props.Document.scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72;
}
+ runInAction(() => (this._scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72));
};
_scrollStopper: undefined | (() => void);
@@ -177,7 +179,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
let focusSpeed: Opt<number>;
if (doc !== this.props.rootDoc && mainCont) {
const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
- const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, NumCast(this.props.Document.scrollHeight));
+ const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, this._scrollHeight);
if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc };
else if (!options.instant) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper);
@@ -453,10 +455,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
}
};
- scrollXf = () => {
- return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._scrollTop)) : this.props.ScreenToLocalTransform();
- };
-
onClick = (e: React.MouseEvent) => {
this._scrollStopper?.();
if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
@@ -504,10 +502,12 @@ export class PDFViewer extends React.Component<IViewerProps> {
);
}
+ getScrollHeight = () => this._scrollHeight;
showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
+ scrollXf = () => (this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._scrollTop)) : this.props.ScreenToLocalTransform());
overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1));
- panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
- panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+ panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1);
+ panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
@@ -533,6 +533,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
isAnnotationOverlay={true}
fieldKey={this.props.fieldKey + '-annotations'}
CollectionView={undefined}
+ getScrollHeight={this.getScrollHeight}
setPreviewCursor={this.setPreviewCursor}
setBrushViewer={this.setBrushViewer}
PanelHeight={this.panelHeight}
diff --git a/src/fields/util.ts b/src/fields/util.ts
index dc0b41276..3a7484cfd 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -350,7 +350,7 @@ export function getter(target: any, prop: string | symbol, proxy: any): any {
return target[prop];
case AclSym : return target[AclSym];
case $mobx: return target.__fields[prop];
- case LayoutSym: return target.__Layout__;
+ case LayoutSym: return target.__LAYOUT__;
case HeightSym: case WidthSym: if (GetEffectiveAcl(target) === AclPrivate) return returnZero;
default :
if (typeof prop === 'symbol') return target[prop];