aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/ImageBox.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-05-22 11:25:32 -0400
committerbobzel <zzzman@gmail.com>2023-05-22 11:25:32 -0400
commitbed3309e1fda6597b2a8fea10ad82cd3a0402051 (patch)
treefe599bbdc5fca2c221e1e0f7a60995b7cd39f870 /src/client/views/nodes/ImageBox.tsx
parent887a4f7e0fc25fde87b20a5de2e7b0aee561cc78 (diff)
parent3d26d5b2654841a9b92f3d66b28d1dc8e36cca6a (diff)
merged physics with master
Diffstat (limited to 'src/client/views/nodes/ImageBox.tsx')
-rw-r--r--src/client/views/nodes/ImageBox.tsx236
1 files changed, 159 insertions, 77 deletions
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index e2ecca0b6..068d39391 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,3 +1,5 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { extname } from 'path';
@@ -8,16 +10,18 @@ import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { createSchema } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { 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, returnEmptyString, returnFalse, returnOne, returnZero, 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';
import { DocumentType } from '../../documents/DocumentTypes';
import { Networking } from '../../Network';
+import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from '../../util/DragManager';
+import { SnappingManager } from '../../util/SnappingManager';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../../views/ContextMenu';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
@@ -26,12 +30,13 @@ 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 { PinProps, PresBox } from './trails';
import React = require('react');
-import { PresBox } from './trails';
-import { DocFocusOptions, DocumentViewProps } from './DocumentView';
+import Color = require('color');
export const pageSchema = createSchema({
googlePhotosUrl: 'string',
@@ -50,9 +55,12 @@ 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;
+ private _overlayIconRef = React.createRef<HTMLDivElement>();
@observable _curSuffix = '';
@observable _uploadIcon = uploadIcons.idle;
@@ -66,33 +74,21 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
};
- @action
- scrollFocus = (anchor: Doc, options: DocFocusOptions) => {
- const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
- return PresBox.restoreTargetDocView(
- this.props.DocumentView?.(), //
- { pinDocLayout: BoolCast(anchor.presPinDocLayout) },
- anchor,
- focusSpeed,
- !anchor.presPinData
- ? {}
- : {
- pannable: true,
- dataannos: anchor.presAnnotations !== undefined,
- dataview: true,
- }
- )
- ? focusSpeed
- : undefined;
- }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
-
- getAnchor = (addAsAnnotation: boolean) => {
+ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
const anchor =
this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
- Docs.Create.ImageanchorDocument({ presTransition: 1000, unrendered: true, annotationOn: this.rootDoc });
+ Docs.Create.ImageConfigDocument({
+ title: 'ImgAnchor:' + this.rootDoc.title,
+ presPanX: NumCast(this.layoutDoc._freeform_panX),
+ presPanY: NumCast(this.layoutDoc._freeform_panY),
+ presViewScale: Cast(this.layoutDoc._freeform_scale, 'number', null),
+ presTransition: 1000,
+ annotationOn: this.rootDoc,
+ });
if (anchor) {
- PresBox.pinDocView(anchor, { pinData: { pannable: true, dataview: true, dataannos: true } }, this.rootDoc);
- addAsAnnotation && this.addDocument(anchor);
+ if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
+ /* addAsAnnotation &&*/ this.addDocument(anchor);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: true } }, this.rootDoc);
return anchor;
}
return this.rootDoc;
@@ -102,7 +98,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._disposers.sizer = reaction(
() => ({
forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes,
- scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._viewScale, 1),
+ scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._freeform_scale, 1),
selected: this.props.isSelected(),
}),
({ forceFull, scrSize, selected }) => (this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 ? '_l' : '_o'),
@@ -118,6 +114,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
},
{ fireImmediately: true }
);
+ this._disposers.scroll = reaction(
+ () => this.layoutDoc.layout_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() {
@@ -128,10 +134,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action
drop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.docDragData) {
- if (de.metaKey) {
+ const targetIsBullseye = (ele: HTMLElement): boolean => {
+ if (!ele) return false;
+ if (ele === this._overlayIconRef.current) return true;
+ return targetIsBullseye(ele.parentElement as HTMLElement);
+ };
+ if (de.metaKey || targetIsBullseye(e.target as HTMLElement)) {
de.complete.docDragData.droppedDocuments.forEach(
action((drop: Doc) => {
Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
+ this.rootDoc[this.fieldKey + '_usePath'] = 'alternate:hover';
e.stopPropagation();
})
);
@@ -151,18 +163,31 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@undoBatch
resolution = () => (this.layoutDoc._showFullRes = !this.layoutDoc._showFullRes);
- @undoBatch
- 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._freeform_scale, 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']) * nh;
+ this.rootDoc._freeform_panX = nh * NumCast(this.rootDoc._freeform_panX);
+ this.rootDoc._freeform_panY = nh * NumCast(this.rootDoc._freeform_panY);
+ this.dataDoc._freeform_panXMax = this.dataDoc._freeform_panXMax ? nh * NumCast(this.dataDoc._freeform_panXMax) : undefined;
+ this.dataDoc._freeform_panXMin = this.dataDoc._freeform_panXMin ? nh * NumCast(this.dataDoc._freeform_panXMin) : undefined;
+ this.dataDoc._freeform_panYMax = this.dataDoc._freeform_panYMax ? nw * NumCast(this.dataDoc._freeform_panYMax) : undefined;
+ this.dataDoc._freeform_panYMin = this.dataDoc._freeform_panYMin ? nw * NumCast(this.dataDoc._freeform_panYMin) : undefined;
+ });
+ @undoBatch
rotate = action(() => {
- const nw = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
- const nh = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
+ const nw = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
+ const nh = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']);
const w = this.layoutDoc._width;
const h = this.layoutDoc._height;
this.dataDoc[this.fieldKey + '-rotation'] = (NumCast(this.dataDoc[this.fieldKey + '-rotation']) + 90) % 360;
- this.dataDoc[this.fieldKey + '-nativeWidth'] = nh;
- this.dataDoc[this.fieldKey + '-nativeHeight'] = nw;
+ this.dataDoc[this.fieldKey + '_nativeWidth'] = nh;
+ this.dataDoc[this.fieldKey + '_nativeHeight'] = nw;
this.layoutDoc._width = h;
this.layoutDoc._height = w;
});
@@ -178,33 +203,38 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const anchy = NumCast(cropping.y);
const anchw = NumCast(cropping._width);
const anchh = NumCast(cropping._height);
- const viewScale = NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']) / anchw;
+ const viewScale = NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']) / anchw;
cropping.title = 'crop: ' + this.rootDoc.title;
cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
cropping.y = NumCast(this.rootDoc.y);
cropping._width = anchw * (this.props.NativeDimScaling?.() || 1);
cropping._height = anchh * (this.props.NativeDimScaling?.() || 1);
- cropping.isLinkButton = undefined;
+ cropping.onClick = undefined;
const croppingProto = Doc.GetProto(cropping);
croppingProto.annotationOn = undefined;
- croppingProto.isPrototype = true;
+ croppingProto.isDataDoc = 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');
croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
- croppingProto['data-nativeWidth'] = anchw;
- croppingProto['data-nativeHeight'] = anchh;
- croppingProto.viewScale = viewScale;
- croppingProto.viewScaleMin = viewScale;
- croppingProto.panX = anchx / viewScale;
- croppingProto.panY = anchy / viewScale;
- croppingProto.panXMin = anchx / viewScale;
- croppingProto.panXMax = anchw / viewScale;
- croppingProto.panYMin = anchy / viewScale;
- croppingProto.panYMax = anchh / viewScale;
+ croppingProto['data_nativeWidth'] = anchw;
+ croppingProto['data_nativeHeight'] = anchh;
+ croppingProto.freeform_scale = viewScale;
+ croppingProto.freeform_scaleMin = viewScale;
+ croppingProto.freeform_panX = anchx / viewScale;
+ croppingProto.freeform_panY = anchy / viewScale;
+ croppingProto.freeform_panXMin = anchx / viewScale;
+ croppingProto.freeform_panXMax = anchw / viewScale;
+ croppingProto.freeform_panYMin = anchy / viewScale;
+ croppingProto.freeform_panYMax = anchh / viewScale;
if (addCrop) {
- DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', '');
+ DocUtils.MakeLink(region, cropping, { link_relationship: '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;
};
@@ -213,10 +243,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const field = Cast(this.dataDoc[this.fieldKey], ImageField);
if (field) {
const funcs: ContextMenuProps[] = [];
- funcs.push({ description: 'Rotate Clockwise 90', event: this.rotate, icon: 'expand-arrows-alt' });
- funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? 'Dynamic Res' : 'Full Res'}`, event: this.resolution, icon: 'expand-arrows-alt' });
- funcs.push({ description: `${this.layoutDoc[this.fieldKey + '-useAlt'] ? 'Show Alternate' : 'Show Primary'}`, event: this.setUseAlt, icon: 'expand-arrows-alt' });
- funcs.push({ description: 'Copy path', event: () => Utils.CopyText(this.choosePath(field.url)), icon: 'expand-arrows-alt' });
+ 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: 'Copy path', event: () => Utils.CopyText(this.choosePath(field.url)), icon: 'copy' });
if (!Doc.noviceMode) {
funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' });
@@ -281,6 +311,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return !tags ? null : <img id={'google-tags'} src={'/assets/google_tags.png'} />;
};
+ getScrollHeight = () => (this.props.layout_fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._freeform_scale, 1) === NumCast(this.rootDoc._freeform_scaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
+
@computed
private get considerDownloadIcon() {
const data = this.dataDoc[this.fieldKey];
@@ -325,11 +357,46 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@computed get nativeSize() {
TraceMobx();
- const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth'], 500));
- const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight'], 1));
+ const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth'], 500));
+ const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight'], 500));
const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '-nativeOrientation'], 1);
return { nativeWidth, nativeHeight, nativeOrientation };
}
+ @computed get overlayImageIcon() {
+ const usePath = this.rootDoc[`_${this.fieldKey}_usePath`];
+ return (
+ <Tooltip
+ title={
+ <div className="dash-tooltip">
+ toggle between
+ <span style={{ color: usePath === undefined ? 'black' : undefined }}>
+ <em> primary, </em>
+ </span>
+ <span style={{ color: usePath === 'alternate' ? 'black' : undefined }}>
+ <em>alternate, </em>
+ </span>
+ and show
+ <span style={{ color: usePath === 'alternate:hover' ? 'black' : undefined }}>
+ <em> alternate on hover</em>
+ </span>
+ </div>
+ }>
+ <div
+ className="imageBox-alternateDropTarget"
+ ref={this._overlayIconRef}
+ onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
+ style={{
+ display: (SnappingManager.GetIsDragging() && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
+ width: 'min(10%, 25px)',
+ height: 'min(10%, 25px)',
+ background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray',
+ color: usePath === undefined ? 'black' : 'white',
+ }}>
+ <FontAwesomeIcon icon="turn-up" size="lg" />
+ </div>
+ </Tooltip>
+ );
+ }
@computed get paths() {
const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
@@ -339,14 +406,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
.filter(url => url)
.map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
- return paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
+ return paths.length ? paths : [Utils.CorsProxy('https://cs.brown.edu/~bcz/noImage.png')];
}
+ @observable _isHovering = false; // flag to switch between primary and alternate images on hover
@computed get content() {
TraceMobx();
- const srcpath = this.paths[0];
- const fadepath = this.paths.lastElement();
+ const backAlpha = DashColor(this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor)).alpha();
+ 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;
@@ -361,15 +430,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
transformOrigin = 'right top';
transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
}
+ const usePath = this.rootDoc[`_${this.fieldKey}_usePath`];
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-cont" onPointerEnter={action(() => (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))} key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
+ <div className="imageBox-fader" style={{ opacity: backAlpha }}>
<img key="paths" src={srcpath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
{fadepath === srcpath ? null : (
- <div
- className={`imageBox-fadeBlocker${(this.props.isHovering?.() && this.layoutDoc[this.fieldKey + '-useAlt'] === undefined) || BoolCast(this.layoutDoc[this.fieldKey + '-useAlt']) ? '-hover' : ''}`}
- style={{ transition: StrCast(this.layoutDoc.viewTransition, 'opacity 1000ms') }}>
+ <div className={`imageBox-fadeBlocker${(this._isHovering && usePath === 'alternate:hover') || usePath === 'alternate' ? '-hover' : ''}`} style={{ transition: StrCast(this.layoutDoc.viewTransition, 'opacity 1000ms') }}>
<img className="imageBox-fadeaway" key="fadeaway" src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
</div>
)}
@@ -377,13 +445,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{!Doc.noviceMode && this.considerDownloadIcon}
{this.considerGooglePhotosLink()}
<FaceRectangles document={this.dataDoc} color={'#0000FF'} backgroundColor={'#0000FF'} />
+ {this.overlayImageIcon}
</div>
);
}
- screenToLocalTransform = this.props.ScreenToLocalTransform;
- contentFunc = () => [this.content];
-
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@observable _marqueeing: number[] | undefined;
@@ -392,8 +458,9 @@ 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._layout_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)) {
+ if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._freeform_scale, 1) <= NumCast(this.rootDoc.freeform_scaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
setupMoveUpEvents(
this,
e,
@@ -414,12 +481,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._marqueeing = undefined;
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);
- };
+ focus = (anchor: Doc, options: DocFocusOptions) => this._ffref.current?.focus(anchor, options);
+ _ffref = React.createRef<CollectionFreeFormView>();
+ savedAnnotations = () => this._savedAnnotations;
render() {
TraceMobx();
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
@@ -429,30 +494,47 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
className="imageBox"
onContextMenu={this.specificContextMenu}
ref={this._mainCont}
+ onScroll={action(e => {
+ if (!this._forcedScroll) {
+ if (this.layoutDoc._layout_scrollTop || this._mainCont.current?.scrollTop) {
+ this._ignoreScroll = true;
+ this.layoutDoc._layout_scrollTop = this._mainCont.current?.scrollTop;
+ this._ignoreScroll = false;
+ }
+ }
+ })}
style={{
+ display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined,
width: this.props.PanelWidth() ? undefined : `100%`,
height: this.props.PanelWidth() ? undefined : `100%`,
pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
borderRadius,
+ overflow: this.layoutDoc.layout_fitWidth || this.props.layout_fitWidth?.(this.rootDoc) ? 'auto' : undefined,
}}>
<CollectionFreeFormView
- {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+ ref={this._ffref}
+ {...this.props}
+ setContentView={emptyFunction}
+ NativeWidth={returnZero}
+ NativeHeight={returnZero}
renderDepth={this.props.renderDepth + 1}
fieldKey={this.annotationKey}
- styleProvider={this.styleProvider}
- CollectionView={undefined}
+ styleProvider={this.props.styleProvider}
isAnnotationOverlay={true}
annotationLayerHostsContent={true}
PanelWidth={this.props.PanelWidth}
PanelHeight={this.props.PanelHeight}
ScreenToLocalTransform={this.screenToLocalTransform}
select={emptyFunction}
+ focus={this.focus}
+ getScrollHeight={this.getScrollHeight}
NativeDimScaling={returnOne}
+ isAnyChildContentActive={returnFalse}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
removeDocument={this.removeDocument}
moveDocument={this.moveDocument}
addDocument={this.addDocument}>
- {this.contentFunc}
+ {this.content}
</CollectionFreeFormView>
{this.annotationLayer}
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (