aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.scss4
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx222
-rw-r--r--src/client/views/nodes/ColorBox.tsx19
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx1
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx80
-rw-r--r--src/client/views/nodes/DataVizBox/components/PieChart.tsx6
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx2
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx4
-rw-r--r--src/client/views/nodes/DocumentView.scss10
-rw-r--r--src/client/views/nodes/DocumentView.tsx199
-rw-r--r--src/client/views/nodes/EquationBox.tsx17
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx11
-rw-r--r--src/client/views/nodes/ImageBox.scss1
-rw-r--r--src/client/views/nodes/ImageBox.tsx24
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx4
-rw-r--r--src/client/views/nodes/LinkBox.tsx17
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx30
-rw-r--r--src/client/views/nodes/LoadingBox.tsx21
-rw-r--r--src/client/views/nodes/MapBox/MapAnchorMenu.tsx2
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx54
-rw-r--r--src/client/views/nodes/MapBox/MapBox2.tsx53
-rw-r--r--src/client/views/nodes/PDFBox.tsx29
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx9
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx1
-rw-r--r--src/client/views/nodes/VideoBox.scss1
-rw-r--r--src/client/views/nodes/VideoBox.tsx32
-rw-r--r--src/client/views/nodes/WebBox.tsx207
-rw-r--r--src/client/views/nodes/WebBoxRenderer.js113
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx8
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx1
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx80
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx12
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts8
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts1
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx53
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx19
38 files changed, 693 insertions, 666 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.scss b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
index f99011b8f..7f0a39550 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.scss
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
@@ -1,9 +1,9 @@
.collectionFreeFormDocumentView-container {
- transform-origin: left top;
+ transform-origin: 50% 50%;
position: absolute;
background-color: transparent;
touch-action: manipulation;
top: 0;
left: 0;
- //pointer-events: none;
+ pointer-events: none;
}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 085f9f023..421d431b3 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable } from 'mobx';
+import { action, computed, observable, trace } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, Opt } from '../../../fields/Doc';
import { List } from '../../../fields/List';
@@ -6,10 +6,9 @@ import { listSpec } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { numberRange } from '../../../Utils';
+import { numberRange, OmitKeys } from '../../../Utils';
import { DocumentManager } from '../../util/DocumentManager';
import { SelectionManager } from '../../util/SelectionManager';
-import { Transform } from '../../util/Transform';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { DocComponent } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
@@ -17,26 +16,113 @@ import './CollectionFreeFormDocumentView.scss';
import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
import React = require('react');
-export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
- dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; rotation?: number; color?: string; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined;
- sizeProvider?: (doc: Doc, replica: string) => { width: number; height: number } | undefined;
- renderCutoffProvider: (doc: Doc) => boolean;
+export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentViewProps {
+ x: number;
+ y: number;
+ z: number;
+ width: number;
+ height: number;
zIndex?: number;
+ rotation?: number;
+ color?: string;
+ backgroundColor?: string;
+ opacity?: number;
+ highlight?: boolean;
+ transition?: string;
dataTransition?: string;
- replica: string;
+ RenderCutoffProvider: (doc: Doc) => boolean;
+ CollectionFreeFormView: CollectionFreeFormView;
+}
+@observer
+export class CollectionFreeFormDocumentViewWrapper extends DocComponent<CollectionFreeFormDocumentViewWrapperProps>() implements CollectionFreeFormDocumentViewProps {
+ @observable X = this.props.x;
+ @observable Y = this.props.y;
+ @observable Z = this.props.z;
+ @observable ZIndex = this.props.zIndex;
+ @observable Rotation = this.props.rotation;
+ @observable Opacity = this.props.opacity;
+ @observable BackgroundColor = this.props.backgroundColor;
+ @observable Color = this.props.color;
+ @observable Highlight = this.props.highlight;
+ @observable Width = this.props.width;
+ @observable Height = this.props.height;
+ @observable Transition = this.props.transition;
+ @observable DataTransition = this.props.dataTransition;
+ CollectionFreeFormView = this.props.CollectionFreeFormView; // needed for type checking
+ RenderCutoffProvider = this.props.RenderCutoffProvider; // needed for type checking
+
+ @computed get WrapperKeys() {
+ return Object.keys(this).filter(key => key.startsWith('w_')).map(key => key.replace('w_', ''))
+ .map(key => ({upper:key, lower:key[0].toLowerCase() + key.substring(1)})); // prettier-ignore
+ }
+
+ // wrapper functions around prop fields that have been converted to observables to keep 'props' from ever changing.
+ // this way, downstream code only invalidates when it uses a specific prop, not when any prop changes
+ w_X = () => this.X; // prettier-ignore
+ w_Y = () => this.Y; // prettier-ignore
+ w_Z = () => this.Z; // prettier-ignore
+ w_ZIndex = () => this.ZIndex ?? NumCast(this.props.Document.zIndex); // prettier-ignore
+ w_Rotation = () => this.Rotation ?? NumCast(this.props.Document._rotation); // prettier-ignore
+ w_Opacity = () => this.Opacity; // prettier-ignore
+ w_BackgroundColor = () => this.BackgroundColor ?? Cast(this.props.Document._backgroundColor, 'string', null); // prettier-ignore
+ w_Color = () => this.Color ?? Cast(this.props.Document._color, 'string', null); // prettier-ignore
+ w_Highlight = () => this.Highlight; // prettier-ignore
+ w_Width = () => this.Width; // prettier-ignore
+ w_Height = () => this.Height; // prettier-ignore
+ w_Transition = () => this.Transition; // prettier-ignore
+ w_DataTransition = () => this.DataTransition; // prettier-ignore
+
+ PanelWidth = () => this.Width || this.props.PanelWidth?.(); // prettier-ignore
+ PanelHeight = () => this.Height || this.props.PanelHeight?.(); // prettier-ignore
+ @action
+ componentDidUpdate() {
+ this.WrapperKeys.forEach(keys => ((this as any)[keys.upper] = (this.props as any)[keys.lower]));
+ }
+ render() {
+ const layoutProps = this.WrapperKeys.reduce((val, keys) => [(val['w_' + keys.upper] = (this as any)['w_' + keys.upper]), val][1], {} as { [key: string]: Function });
+ return (
+ <CollectionFreeFormDocumentView
+ {...OmitKeys(this.props, this.WrapperKeys.map(keys => keys.lower) ).omit} // prettier-ignore
+ {...layoutProps}
+ PanelWidth={this.PanelWidth}
+ PanelHeight={this.PanelHeight}
+ />
+ );
+ }
+}
+export interface CollectionFreeFormDocumentViewProps {
+ w_X: () => number;
+ w_Y: () => number;
+ w_Z: () => number;
+ w_ZIndex?: () => number;
+ w_Rotation?: () => number;
+ w_Color: () => string;
+ w_BackgroundColor: () => string;
+ w_Opacity: () => number | undefined;
+ w_Highlight: () => boolean | undefined;
+ w_Transition: () => string | undefined;
+ w_Width: () => number;
+ w_Height: () => number;
+ w_DataTransition: () => string | undefined;
+ PanelWidth: () => number;
+ PanelHeight: () => number;
+ RenderCutoffProvider: (doc: Doc) => boolean;
CollectionFreeFormView: CollectionFreeFormView;
}
@observer
-export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps>() {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps & DocumentViewProps>() {
+ get displayName() { // this makes mobx trace() statements more descriptive
+ return 'CollectionFreeFormDocumentView(' + this.rootDoc.title + ')';
+ } // prettier-ignore
public static animFields: { key: string; val?: number }[] = [
- { key: '_height' },
- { key: '_width' },
{ key: 'x' },
{ key: 'y' },
+ { key: 'opacity', val: 1 },
+ { key: '_height' },
+ { key: '_width' },
{ key: '_rotation', val: 0 },
{ key: '_layout_scrollTop' },
- { key: 'opacity', val: 1 },
{ key: '_currentFrame' },
{ key: 'freeform_scale', val: 1 },
{ key: 'freeform_panX' },
@@ -44,52 +130,18 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
]; // fields that are configured to be animatable using animation frames
public static animStringFields = ['backgroundColor', 'color', 'fillColor']; // fields that are configured to be animatable using animation frames
public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
- @observable _animPos: number[] | undefined = undefined;
- @observable _contentView: DocumentView | undefined | null;
- get displayName() {
- // this makes mobx trace() statements more descriptive
- return 'CollectionFreeFormDocumentView(' + this.rootDoc.title + ')';
- }
- get transform() {
- return `translate(${this.X}px, ${this.Y}px) rotate(${NumCast(this.Rot, this.Rot)}deg)`;
- }
- get X() {
- return this.dataProvider?.x ?? NumCast(this.Document.x);
- }
- get Y() {
- return this.dataProvider?.y ?? NumCast(this.Document.y);
- }
- get ZInd() {
- return this.dataProvider?.zIndex ?? NumCast(this.Document.zIndex);
- }
- get Rot() {
- return this.dataProvider?.rotation ?? NumCast(this.Document._rotation);
- }
- get Opacity() {
- return this.dataProvider?.opacity;
- }
- get BackgroundColor() {
- return this.dataProvider?.backgroundColor ?? Cast(this.Document._backgroundColor, 'string', null);
- }
- get Color() {
- return this.dataProvider?.color ?? Cast(this.Document._color, 'string', null);
- }
- @computed get dataProvider() {
- return this.props.dataProvider?.(this.props.Document, this.props.replica);
- }
- @computed get sizeProvider() {
- return this.props.sizeProvider?.(this.props.Document, this.props.replica);
+ get CollectionFreeFormView() {
+ return this.props.CollectionFreeFormView;
}
styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
if (doc === this.layoutDoc) {
- // prettier-ignore
switch (property) {
- case StyleProp.Opacity: return this.Opacity; // only change the opacity for this specific document, not its children
- case StyleProp.BackgroundColor: return this.BackgroundColor;
- case StyleProp.Color: return this.Color;
- }
+ case StyleProp.Opacity: return this.props.w_Opacity(); // only change the opacity for this specific document, not its children
+ case StyleProp.BackgroundColor: return this.props.w_BackgroundColor();
+ case StyleProp.Color: return this.props.w_Color();
+ } // prettier-ignore
}
return this.props.styleProvider?.(doc, props, property);
};
@@ -126,21 +178,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
});
}
- public static setupZoom(doc: Doc, targDoc: Doc) {
- const width = new List<number>();
- const height = new List<number>();
- const top = new List<number>();
- const left = new List<number>();
- width.push(NumCast(targDoc._width));
- height.push(NumCast(targDoc._height));
- top.push(NumCast(targDoc._height) / -2);
- left.push(NumCast(targDoc._width) / -2);
- doc['viewfinder-width-indexed'] = width;
- doc['viewfinder-height-indexed'] = height;
- doc['viewfinder-top-indexed'] = top;
- doc['viewfinder-left-indexed'] = left;
- }
-
public static setupKeyframes(docs: Doc[], currTimecode: number, makeAppear: boolean = false) {
docs.forEach(doc => {
if (doc.appearFrame === undefined) doc.appearFrame = currTimecode;
@@ -182,16 +219,16 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
};
- dragEnding = () => this.props.CollectionFreeFormView?.dragEnding();
- dragStarting = () => this.props.CollectionFreeFormView?.dragStarting(false, true);
-
nudge = (x: number, y: number) => {
- this.props.Document.x = NumCast(this.props.Document.x) + x;
- this.props.Document.y = NumCast(this.props.Document.y) + y;
+ const [locX, locY] = this.props.ScreenToLocalTransform().transformDirection(x, y);
+ this.props.Document.x = this.props.w_X() + locX;
+ this.props.Document.y = this.props.w_Y() + locY;
};
- panelWidth = () => this.sizeProvider?.width || this.props.PanelWidth?.();
- panelHeight = () => this.sizeProvider?.height || this.props.PanelHeight?.();
- screenToLocalTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.X, -this.Y);
+ screenToLocalTransform = () =>
+ this.props
+ .ScreenToLocalTransform()
+ .translate(-this.props.w_X(), -this.props.w_Y())
+ .rotateDeg(-(this.props.w_Rotation?.() || 0));
returnThis = () => this;
/// this indicates whether the doc view is activated because of its relationshop to a group
@@ -200,38 +237,29 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
// 'inactive' - this is a group child but it is not active
// undefined - this is not activated by a group
isGroupActive = () => {
- if (this.props.CollectionFreeFormView.isAnyChildContentActive()) return undefined;
+ if (this.CollectionFreeFormView.isAnyChildContentActive()) return undefined;
const isGroup = this.rootDoc._isGroup && (!this.rootDoc.backgroundColor || this.rootDoc.backgroundColor === 'transparent');
return isGroup ? (this.props.isDocumentActive?.() ? 'group' : this.props.isGroupActive?.() ? 'child' : 'inactive') : this.props.isGroupActive?.() ? 'child' : undefined;
};
+ public static CollectionFreeFormDocViewClassName = 'collectionFreeFormDocumentView-container';
render() {
TraceMobx();
- const divProps: DocumentViewProps = {
- ...this.props,
- CollectionFreeFormDocumentView: this.returnThis,
- styleProvider: this.styleProvider,
- ScreenToLocalTransform: this.screenToLocalTransform,
- PanelWidth: this.panelWidth,
- PanelHeight: this.panelHeight,
- isGroupActive: this.isGroupActive,
- };
+ const passOnProps = OmitKeys(this.props, Object.keys(this.props).filter(key => key.startsWith('w_'))).omit; // prettier-ignore
return (
<div
- className="collectionFreeFormDocumentView-container"
+ className={CollectionFreeFormDocumentView.CollectionFreeFormDocViewClassName}
style={{
- width: this.panelWidth(),
- height: this.panelHeight(),
- transform: this.transform,
- transformOrigin: '50% 50%',
- transition: this.dataProvider?.transition ?? (this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition)),
- zIndex: this.ZInd,
- display: this.sizeProvider?.width ? undefined : 'none',
- pointerEvents: 'none',
+ width: this.props.PanelWidth(),
+ height: this.props.PanelHeight(),
+ transform: `translate(${this.props.w_X()}px, ${this.props.w_Y()}px) rotate(${NumCast(this.props.w_Rotation?.())}deg)`,
+ transition: this.props.w_Transition?.() ?? (this.props.w_DataTransition?.() || this.props.w_Transition?.()),
+ zIndex: this.props.w_ZIndex?.(),
+ display: this.props.w_Width?.() ? undefined : 'none',
}}>
- {this.props.renderCutoffProvider(this.props.Document) ? (
- <div style={{ position: 'absolute', width: this.panelWidth(), height: this.panelHeight(), background: 'lightGreen' }} />
+ {this.props.RenderCutoffProvider(this.props.Document) ? (
+ <div style={{ position: 'absolute', width: this.props.PanelWidth(), height: this.props.PanelHeight(), background: 'lightGreen' }} />
) : (
- <DocumentView {...divProps} ref={action((r: DocumentView | null) => (this._contentView = r))} />
+ <DocumentView {...passOnProps} CollectionFreeFormDocumentView={this.returnThis} styleProvider={this.styleProvider} ScreenToLocalTransform={this.screenToLocalTransform} isGroupActive={this.isGroupActive} />
)}
</div>
);
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 1b6fe5748..23ffd1080 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -3,10 +3,12 @@ import { action } from 'mobx';
import { observer } from 'mobx-react';
import { ColorState, SketchPicker } from 'react-color';
import { Doc } from '../../../fields/Doc';
-import { Height, Width } from '../../../fields/DocSymbols';
+import { Height } from '../../../fields/DocSymbols';
import { InkTool } from '../../../fields/InkField';
-import { StrCast } from '../../../fields/Types';
+import { NumCast, StrCast } from '../../../fields/Types';
+import { DashColor } from '../../../Utils';
import { DocumentType } from '../../documents/DocumentTypes';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from '../../util/SelectionManager';
import { undoBatch } from '../../util/UndoManager';
import { ViewBoxBaseComponent } from '../DocComponent';
@@ -14,8 +16,6 @@ import { ActiveInkColor, ActiveInkWidth, SetActiveInkColor, SetActiveInkWidth }
import './ColorBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
import { RichTextMenu } from './formattedText/RichTextMenu';
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { DashColor } from '../../../Utils';
@observer
export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -51,7 +51,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
render() {
- const scaling = Math.min(this.layoutDoc.layout_fitWidth ? 10000 : this.props.PanelHeight() / this.rootDoc[Height](), this.props.PanelWidth() / this.rootDoc[Width]());
+ const scaling = Math.min(this.layoutDoc.layout_fitWidth ? 10000 : this.props.PanelHeight() / NumCast(this.rootDoc._height), this.props.PanelWidth() / NumCast(this.rootDoc._width));
return (
<div
className={`colorBox-container${this.props.isContentActive() ? '-interactive' : ''}`}
@@ -84,9 +84,6 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
}
-
-ScriptingGlobals.add(
- function interpColors(c1:string, c2:string, weight=0.5) {
- return DashColor(c1).mix(DashColor(c2),weight)
- }
-) \ No newline at end of file
+ScriptingGlobals.add(function interpColors(c1: string, c2: string, weight = 0.5) {
+ return DashColor(c1).mix(DashColor(c2), weight);
+});
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index b09fcd882..ff394e5f5 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -20,7 +20,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(ComparisonBox, fieldKey);
}
- protected _multiTouchDisposer?: import('../../util/InteractionUtils').InteractionUtils.MultiTouchEventDisposer | undefined;
private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined];
@observable _animating = '';
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index f3670622a..e712a25a0 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -41,6 +41,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _ffref = React.createRef<CollectionFreeFormView>();
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+ anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
+ crop: ((region: Doc | undefined, addCrop?: boolean) => Doc | undefined) | undefined;
@observable _marqueeing: number[] | undefined;
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@computed get annotationLayer() {
@@ -255,7 +257,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
// toggles for user to decide which chart type to view the data in
- renderVizView = () => {
+ @computed get renderVizView() {
+ const scale = this.props.NativeDimScaling?.() || 1;
const sharedProps = {
rootDoc: this.rootDoc,
layoutDoc: this.layoutDoc,
@@ -267,16 +270,13 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
if (!this.records.length) return 'no data/visualization';
switch (this.dataVizView) {
- case DataVizView.TABLE:
- return <TableBox {...sharedProps} docView={this.props.DocumentView} selectAxes={this.selectAxes} />;
- case DataVizView.LINECHART:
- return <LineChart vizBox={this} {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} />;
- case DataVizView.HISTOGRAM:
- return <Histogram {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} />;
- case DataVizView.PIECHART:
- return <PieChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} />;
- }
- };
+ case DataVizView.TABLE: return <TableBox {...sharedProps} docView={this.props.DocumentView} selectAxes={this.selectAxes} />;
+ case DataVizView.LINECHART: return <LineChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} />;
+ case DataVizView.HISTOGRAM: return <Histogram {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} />;
+ case DataVizView.PIECHART: return <PieChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)}
+ margin={{ top: 10, right: 15, bottom: 15, left: 15 }} />;
+ } // prettier-ignore
+ }
@action
onPointerDown = (e: React.PointerEvent): void => {
@@ -319,6 +319,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
render() {
+ const scale = this.props.NativeDimScaling?.() || 1;
+
return !this.records.length ? (
// displays how to get data into the DataVizBox if its empty
<div className="start-message">To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command 'ctrl + p' to bring the data table to your canvas.</div>
@@ -328,15 +330,19 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
onPointerDown={this.marqueeDown}
style={{
pointerEvents: this.props.isContentActive() === true ? 'all' : 'none',
+ width: `${100 / scale}%`,
+ height: `${100 / scale}%`,
+ transform: `scale(${scale})`,
+ position: 'absolute',
}}
onWheel={e => e.stopPropagation()}
ref={this._mainCont}
>
<div className={'datatype-button'}>
- <Toggle text={'TABLE'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.TABLE)} toggleStatus={this.layoutDoc._dataViz == DataVizView.TABLE} />
- <Toggle text={'LINECHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.LINECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.LINECHART} />
- <Toggle text={'HISTOGRAM'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.HISTOGRAM)} toggleStatus={this.layoutDoc._dataViz == DataVizView.HISTOGRAM} />
- <Toggle text={'PIE CHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.PIECHART} />
+ <Toggle text={' TABLE '} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.TABLE)} toggleStatus={this.layoutDoc._dataViz === DataVizView.TABLE} />
+ <Toggle text={'LINECHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.LINECHART)} toggleStatus={this.layoutDoc._dataViz === DataVizView.LINECHART} />
+ <Toggle text={'HISTOGRAM'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.HISTOGRAM)} toggleStatus={this.layoutDoc._dataViz === DataVizView.HISTOGRAM} />
+ <Toggle text={'PIE CHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == -DataVizView.PIECHART} />
</div>
<CollectionFreeFormView
@@ -358,7 +364,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
addDocument={this.addDocument}>
</CollectionFreeFormView>
- {this.renderVizView()}
+ {this.renderVizView}
<div className="dataviz-sidebar"
style={{ width: `${this.sidebarWidthPercent}`,
backgroundColor: `${this.sidebarColor}` }}
@@ -383,20 +389,36 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
{this.sidebarHandle}
{this.annotationLayer}
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+ // <MarqueeAnnotator
+ // rootDoc={this.rootDoc}
+ // scrollTop={0}
+ // down={this._marqueeing}
+ // scaling={this.props.NativeDimScaling}
+ // docView={this.props.DocumentView}
+ // addDocument={this.sidebarAddDocument}
+ // finishMarquee={this.finishMarquee}
+ // savedAnnotations={this.savedAnnotations}
+ // selectionText={returnEmptyString}
+ // annotationLayer={this._annotationLayer.current}
+ // mainCont={this._mainCont.current}
+ // highlightDragSrcColor={''}
+ // />
+
<MarqueeAnnotator
- rootDoc={this.rootDoc}
- scrollTop={0}
- down={this._marqueeing}
- scaling={this.props.NativeDimScaling}
- docView={this.props.docViewPath().slice(-1)[0]}
- addDocument={this.sidebarAddDocument}
- finishMarquee={this.finishMarquee}
- savedAnnotations={this.savedAnnotations}
- selectionText={returnEmptyString}
- annotationLayer={this._annotationLayer.current}
- mainCont={this._mainCont.current}
- highlightDragSrcColor={''}
- />
+ rootDoc={this.rootDoc}
+ getPageFromScroll={undefined}
+ anchorMenuClick={this.anchorMenuClick}
+ scrollTop={0}
+ annotationLayerScrollTop={NumCast(this.props.Document._layout_scrollTop)}
+ addDocument={this.sidebarAddDocument}
+ docView={this.props.DocumentView!}
+ finishMarquee={this.finishMarquee}
+ savedAnnotations={this.savedAnnotations}
+ selectionText={returnEmptyString}
+ annotationLayer={this._annotationLayer.current}
+ mainCont={this._mainCont.current}
+ anchorMenuCrop={this.crop}
+ />
)}
</div>
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index df65810b5..4622b470a 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -88,11 +88,7 @@ export class PieChart extends React.Component<PieChartProps> {
componentDidMount = () => {
this._disposers.chartData = reaction(
() => ({ dataSet: this._pieChartData, w: this.width, h: this.height }),
- ({ dataSet, w, h }) => {
- if (dataSet!.length > 0) {
- this.drawChart(dataSet, w, h);
- }
- },
+ ({ dataSet, w, h }) => dataSet!.length > 0 && this.drawChart(dataSet, w, h),
{ fireImmediately: true }
);
};
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 3685a198e..012021dda 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -220,7 +220,7 @@ export class TableBox extends React.Component<TableBoxProps> {
</thead>
<tbody>
{this._tableDataIds
- .filter(rowId => this.startID <= rowId && rowId <= this.endID)
+ .filter((rowId, i) => this.startID <= i && i <= this.endID)
?.map(rowId => (
<tr
key={rowId}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index e1de2fa76..8c8c987fe 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -98,7 +98,7 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
};
render() {
const style: { [key: string]: any } = {};
- const divKeys = OmitKeys(this.props, ['children', 'htmltag', 'RootDoc', 'scaling', 'Document', 'key', 'onInput', 'onClick', '__proto__']).omit;
+ const divKeys = OmitKeys(this.props, ['children', 'dragStarting', 'dragEnding', 'htmltag', 'RootDoc', 'scaling', 'Document', 'key', 'onInput', 'onClick', '__proto__']).omit;
const replacer = (match: any, expr: string, offset: any, string: any) => {
// bcz: this executes a script to convert a propery expression string: { script } into a value
return (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.props.RootDoc, this: this.props.Document, scale: this.props.scaling }).result as string) || '';
@@ -119,7 +119,7 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
@observer
export class DocumentContentsView extends React.Component<
DocumentViewProps & {
- isSelected: (outsideReaction: boolean) => boolean;
+ isSelected: () => boolean;
select: (ctrl: boolean) => void;
NativeDimScaling?: () => number;
setHeight?: (height: number) => void;
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 931594568..406a1b8fb 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -61,7 +61,6 @@
.documentView-htmlOverlayInner {
box-shadow: black 0.2vw 0.2vw 0.8vw;
background: rgb(255, 255, 255);
- overflow: auto;
position: relative;
margin: auto;
padding: 20px;
@@ -120,6 +119,10 @@
display: flex;
justify-content: center;
align-items: center;
+ position: relative; // allows contents to be positioned relative/below title
+ > .formattedTextBox {
+ position: absolute; // position a child text box
+ }
.sharingIndicator {
height: 30px;
@@ -183,12 +186,15 @@
top: 0;
width: 100%;
height: 14;
- background: rgba(0, 0, 0, 0.4);
+ opacity: 0.5;
text-align: center;
text-overflow: ellipsis;
white-space: pre;
position: absolute;
display: flex; // this allows title field dropdown to be inline with editable title
+ &:hover {
+ opacity: 1;
+ }
}
.documentView-titleWrapper-hover {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 98b13f90f..0d6b88392 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,11 +1,11 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { Dropdown, DropdownType, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
-import { AclPrivate, Animation, AudioPlay, DocData, Width } from '../../../fields/DocSymbols';
+import { AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -24,7 +24,6 @@ import { Networking } from '../../Network';
import { DictationManager } from '../../util/DictationManager';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType } from '../../util/DragManager';
-import { InteractionUtils } from '../../util/InteractionUtils';
import { FollowLinkScript } from '../../util/LinkFollower';
import { LinkManager } from '../../util/LinkManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
@@ -64,6 +63,14 @@ declare class MediaRecorder {
constructor(e: any);
}
+export enum OpenWhereMod {
+ none = '',
+ left = 'left',
+ right = 'right',
+ top = 'top',
+ bottom = 'bottom',
+ keyvalue = 'keyValue',
+}
export enum OpenWhere {
lightbox = 'lightbox',
add = 'add',
@@ -79,14 +86,7 @@ export enum OpenWhere {
inParent = 'inParent',
inParentFromScreen = 'inParentFromScreen',
overlay = 'overlay',
-}
-export enum OpenWhereMod {
- none = '',
- left = 'left',
- right = 'right',
- top = 'top',
- bottom = 'bottom',
- rightKeyValue = 'rightKeyValue',
+ addRightKeyvalue = 'add:right:keyValue',
}
export interface DocFocusOptions {
@@ -115,7 +115,7 @@ export interface DocComponentView {
getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
restoreView?: (viewSpec: Doc) => boolean;
scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
- brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void; // highlight a region of a view (used by freeforms)
+ brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number) => void; // highlight a region of a view (used by freeforms)
getView?: (doc: Doc) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
@@ -134,6 +134,7 @@ export interface DocComponentView {
setFocus?: () => void; // sets input focus to the componentView
setData?: (data: Field | Promise<RefField | undefined>) => boolean;
componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
+ dragStarting?: (snapToDraggedDoc: boolean, showGroupDragTarget: boolean, visited: Set<Doc>) => void;
incrementalRendering?: () => void;
layout_fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document (e.g, KeyValueBox always allows pointer events)
@@ -141,7 +142,7 @@ export interface DocComponentView {
annotationKey?: string;
getTitle?: () => string;
getCenter?: (xf: Transform) => { X: number; Y: number };
- screenBounds?: () => { left: number; top: number; right: number; bottom: number; center?: { X: number; Y: number } };
+ screenBounds?: () => Opt<{ left: number; top: number; right: number; bottom: number; center?: { X: number; Y: number } }>;
ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number };
@@ -166,7 +167,6 @@ export interface DocumentViewSharedProps {
childHideDecorationTitle?: () => boolean;
childHideResizeHandles?: () => boolean;
childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar.
- 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>;
setTitleFocus?: () => void;
focus: DocFocusFunc;
@@ -176,7 +176,7 @@ export interface DocumentViewSharedProps {
searchFilterDocs: () => Doc[];
layout_showTitle?: () => string;
whenChildContentsActiveChanged: (isActive: boolean) => void;
- rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
+ rootSelected: () => boolean; // whether the root of a template has been selected
addDocTab: (doc: Doc, where: OpenWhere) => boolean;
filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean;
@@ -191,6 +191,7 @@ export interface DocumentViewSharedProps {
yPadding?: number;
dropAction?: dropActionType;
dontRegisterView?: boolean;
+ dragWhenActive?: boolean;
hideLinkButton?: boolean;
hideCaptions?: boolean;
ignoreAutoHeight?: boolean;
@@ -234,13 +235,15 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
onPointerUp?: () => ScriptField;
onBrowseClick?: () => ScriptField | undefined;
onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
+ dragStarting?: () => void;
+ dragEnding?: () => void;
}
// these props are only available in DocumentViewIntenral
export interface DocumentViewInternalProps extends DocumentViewProps {
NativeWidth: () => number;
NativeHeight: () => number;
- isSelected: (outsideReaction?: boolean) => boolean;
+ isSelected: () => boolean;
select: (ctrlPressed: boolean, shiftPress?: boolean) => void;
DocumentView: () => DocumentView;
viewPath: () => DocumentView[];
@@ -261,8 +264,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
private _mainCont = React.createRef<HTMLDivElement>();
private _titleRef = React.createRef<EditableView>();
private _dropDisposer?: DragManager.DragDropDisposer;
- private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
- protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
@observable _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class
@observable _animateScaleTime: Opt<number>; // milliseconds for animating between views. defaults to 300 if not uset
@@ -320,7 +321,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
@observable _pointerEvents: 'none' | 'all' | 'visiblePainted' | undefined;
@computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
- TraceMobx();
return this._pointerEvents;
}
@computed get finalLayoutKey() {
@@ -333,7 +333,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
return this.props.NativeHeight();
}
@computed get disableClickScriptFunc() {
- TraceMobx();
const onScriptDisable = this.props.onClickScriptDisable ?? this._componentView?.onClickScriptDisable?.() ?? this.layoutDoc.onClickScriptDisable;
// prettier-ignore
return (
@@ -358,7 +357,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
componentWillUnmount() {
this.cleanupHandlers(true);
}
+ @observable _mounted = false; // turn off all pointer events if component isn't yet mounted (enables nested Docs in alternate UI textboxes that appear on hover which otherwise would grab focus from the text box, reverting to the original UI )
+ @action
componentDidMount() {
+ this._mounted = true;
this.setupHandlers();
this._disposers.contentActive = reaction(
() => {
@@ -391,15 +393,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this.cleanupHandlers(false);
if (this._mainCont.current) {
this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document, this.preDropFunc);
- this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this));
- this._holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this));
}
}
@action
cleanupHandlers(unbrush: boolean) {
this._dropDisposer?.();
- this._multiTouchDisposer?.();
- this._holdDisposer?.();
unbrush && Doc.UnBrushDoc(this.props.Document);
Object.values(this._disposers).forEach(disposer => disposer?.());
}
@@ -468,7 +466,7 @@ 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) && (defaultDblclick === undefined || defaultDblclick === 'default')) {
- UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
+ UndoManager.RunInBatch(() => LightboxView.Instance.AddDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
SelectionManager.DeselectAll();
Doc.UnBrushDoc(this.props.Document);
} else {
@@ -568,7 +566,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
//if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._type_collection !== CollectionViewType.Docking) e.preventDefault();
// listen to move events if document content isn't active or document is draggable
- if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || BoolCast(this.rootDoc._dragWhenActive))) {
+ if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || BoolCast(this.rootDoc._dragWhenActive, this.props.dragWhenActive))) {
document.addEventListener('pointermove', this.onPointerMove);
}
}
@@ -588,7 +586,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
};
cleanupPointerEvents = () => {
- this.cleanUpInteractions();
document.removeEventListener('pointermove', this.onPointerMove);
document.removeEventListener('pointerup', this.onPointerUp);
};
@@ -665,7 +662,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
if (de.complete.linkDocument) {
de.complete.linkDocument.layout_isSvg = true;
- this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.addDocument(de.complete.linkDocument);
+ this.props.DocumentView().CollectionFreeFormView?.addDocument(de.complete.linkDocument);
}
}
e.stopPropagation();
@@ -682,7 +679,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (!portalLink) {
DocUtils.MakeLink(
this.props.Document,
- Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _layout_fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }),
+ Docs.Create.FreeformDocument([], {
+ _width: NumCast(this.layoutDoc._width) + 10,
+ _height: Math.max(NumCast(this.layoutDoc._height), NumCast(this.layoutDoc._width) + 10),
+ _isLightbox: true,
+ _layout_fitWidth: true,
+ title: StrCast(this.props.Document.title) + ' [Portal]',
+ }),
{ link_relationship: 'portal to:portal from' }
);
}
@@ -735,7 +738,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (e && !(e.nativeEvent as any).dash) {
const onDisplay = () => {
- if (this.rootDoc.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear.
+ if (this.rootDoc.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected() && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear.
setTimeout(() => simulateMouseClick(document.elementFromPoint(e.clientX, e.clientY), e.clientX, e.clientY, e.screenX, e.screenY));
};
if (navigator.userAgent.includes('Macintosh')) {
@@ -756,14 +759,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (!this.props.Document.isFolder) {
const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layout_fieldKey)], Doc, null);
- const appearance = cm.findByDescription('UI Controls...');
+ const appearance = cm.findByDescription('Appearance...');
const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
if (this.props.renderDepth === 0) {
- appearanceItems.push({ description: 'Open in Lightbox', event: () => LightboxView.SetLightboxDoc(this.rootDoc), icon: 'hand-point-right' });
+ appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => LightboxView.Instance.SetLightboxDoc(this.rootDoc), icon: 'external-link-alt' });
}
+ this.rootDoc.type === DocumentType.PRES && appearanceItems.push({ description: 'Pin', event: () => this.props.pinToPres(this.rootDoc, {}), icon: 'eye' });
!Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
- !appearance && appearanceItems.length && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' });
+ !appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'compass' });
if (!Doc.IsSystem(this.rootDoc) && this.rootDoc.type !== DocumentType.PRES && ![CollectionViewType.Docking, CollectionViewType.Tree].includes(this.rootDoc._type_collection as any)) {
const existingOnClick = cm.findByDescription('OnClick...');
@@ -837,7 +841,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
}
}
- constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'table-columns' });
+ constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, OpenWhere.addRightKeyvalue), icon: 'table-columns' });
cm.addItem({ description: 'General...', noexpand: false, subitems: constantItems, icon: 'question' });
const help = cm.findByDescription('Help...');
@@ -894,13 +898,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
else cm.moveAfter(help);
e?.stopPropagation(); // DocumentViews should stop propagation of this event
- cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
+ cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15, undefined, undefined, undefined);
};
@computed get _rootSelected() {
- return this.props.isSelected(false) || (this.props.Document.rootDocument && this.props.rootSelected?.(false)) || false;
+ return this.props.isSelected() || (this.props.Document.rootDocument && this.props.rootSelected?.()) || false;
}
- rootSelected = (outsideReaction?: boolean) => this._rootSelected;
+ rootSelected = () => this._rootSelected;
panelHeight = () => this.props.PanelHeight() - this.headerMargin;
screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
@@ -927,12 +931,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get contents() {
TraceMobx();
const isInk = this.layoutDoc._layout_isSvg && !this.props.LayoutTemplateString;
- const noBackground = this.rootDoc._isGroup && (!this.rootDoc.backgroundColor || this.rootDoc.backgroundColor === 'transparent');
+ const noBackground = this.rootDoc._isGroup && !this.props.LayoutTemplateString?.includes(KeyValueBox.name) && (!this.rootDoc.backgroundColor || this.rootDoc.backgroundColor === 'transparent');
return (
<div
className="documentView-contentsView"
style={{
- pointerEvents: (isInk || noBackground ? 'none' : this.contentPointerEvents()) ?? 'all',
+ pointerEvents: (isInk || noBackground ? 'none' : this.contentPointerEvents()) ?? (this._mounted ? 'all' : 'none'),
height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
}}>
<DocumentContentsView
@@ -1016,7 +1020,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
removeDocument={this.hideLink(link)}
styleProvider={this.anchorStyleProvider}
LayoutTemplate={undefined}
- LayoutTemplateString={LinkAnchorBox.LayoutString(`link_anchor_${Doc.LinkEndpoint(link, this.rootDoc)}`)}
+ LayoutTemplateString={LinkAnchorBox.LayoutString(`link_anchor_${LinkManager.anchorIndex(link, this.rootDoc)}`)}
/>
</div>
));
@@ -1160,7 +1164,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
const background = StrCast(
this.layoutDoc.layout_headingColor,
- StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(this.layoutDoc.layout_headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor)))
+ StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor))
);
const dropdownWidth = this._titleRef.current?._editing || this._changingTitleField ? Math.max(10, (this._dropDownInnerWidth * this.titleHeight) / 30) : 0;
const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
@@ -1172,6 +1176,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
className={`documentView-titleWrapper${showTitleHover ? '-hover' : ''}`}
key="title"
style={{
+ zIndex: 1,
position: this.headerMargin ? 'relative' : 'absolute',
height: this.titleHeight,
width: 100 - sidebarWidthPercent + '%',
@@ -1232,10 +1237,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this.contents
) : (
<div className="documentView-styleWrapper">
- {' '}
- {!this.headerMargin ? this.contents : titleView}
- {!this.headerMargin ? titleView : this.contents}
- {' ' /* */}
+ {titleView}
+ {this.contents}
{captionView}
</div>
);
@@ -1345,6 +1348,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@observer
export class DocumentView extends React.Component<DocumentViewProps> {
public static ROOT_DIV = 'documentView-effectsWrapper';
+ @observable _selected = false;
+ public get SELECTED() {
+ return this._selected;
+ }
+ public set SELECTED(val) {
+ this._selected = val;
+ }
@observable public static Interacting = false;
@observable public static LongPress = false;
@observable public static ExploreMode = false;
@@ -1354,9 +1364,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
@observable public docView: DocumentViewInternal | undefined | null;
@observable public textHtmlOverlay: Opt<string>;
+ @observable public textHtmlOverlayTime: Opt<number>;
@observable private _isHovering = false;
- public htmlOverlayEffect = '';
+ public htmlOverlayEffect: Opt<Doc>;
public get displayName() {
return 'DocumentView(' + this.props.Document?.title + ')';
} // this makes mobx trace() statements more descriptive
@@ -1408,7 +1419,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
const docId = Doc.CurrentUserEmail + Doc.GetProto(linkAnchor)[Id] + '-pivotish';
// prettier-ignore
DocServer.GetRefField(docId).then(docx =>
- LightboxView.SetLightboxDoc(
+ LightboxView.Instance.SetLightboxDoc(
(docx as Doc) ?? // reuse existing pivot view of documents, or else create a new collection
Docs.Create.StackingDocument([], { title: linkAnchor.title + '-pivot', _width: 500, _height: 500, target: linkAnchor, updateContentsScript: ScriptField.MakeScript('updateLinkCollection(self, self.target)') }, docId)
)
@@ -1485,7 +1496,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth();
}
@computed get panelHeight() {
- if (this.effectiveNativeHeight && (!this.layout_fitWidth || !this.layoutDoc.nativeHeightUnfrozen)) {
+ if (this.effectiveNativeHeight && (!this.layout_fitWidth || !this.layoutDoc.layout_reflowVertical)) {
return Math.min(this.props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling);
}
return this.props.PanelHeight();
@@ -1497,7 +1508,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.effectiveNativeWidth &&
this.effectiveNativeHeight &&
Math.abs(this.Xshift) < 0.001 &&
- (!this.layoutDoc.nativeHeightUnfrozen || (!this.layout_fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight()))
+ (!this.layoutDoc.layout_reflowVertical || (!this.layout_fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight()))
? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2)
: 0;
}
@@ -1508,18 +1519,22 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.props.dontCenter?.includes('y') ? 0 : this.Yshift;
}
+ @computed get CollectionFreeFormView() {
+ return this.CollectionFreeFormDocumentView?.CollectionFreeFormView;
+ }
+ @computed get CollectionFreeFormDocumentView() {
+ return this.props.CollectionFreeFormDocumentView?.();
+ }
+
public toggleNativeDimensions = () => this.docView && this.rootDoc.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
public getBounds = () => {
if (!this.docView?.ContentDiv || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
return undefined;
}
- if (this.docView._componentView?.screenBounds) {
+ if (this.docView._componentView?.screenBounds?.()) {
return this.docView._componentView.screenBounds();
}
- const xf = this.docView.props
- .ScreenToLocalTransform()
- .scale(this.trueNativeWidth() ? this.nativeScaling : 1)
- .inverse();
+ const xf = this.docView.props.ScreenToLocalTransform().scale(this.nativeScaling).inverse();
const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
if (this.docView.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
@@ -1579,15 +1594,18 @@ export class DocumentView extends React.Component<DocumentViewProps> {
scaleToScreenSpace = () => (1 / (this.props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale;
docViewPathFunc = () => this.docViewPath;
- isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction);
+ isSelected = () => this.SELECTED;
select = (extendSelection: boolean, focusSelection?: boolean) => {
- SelectionManager.SelectView(this, extendSelection);
- if (focusSelection) {
- DocumentManager.Instance.showDocument(this.rootDoc, {
- willZoomCentered: true,
- zoomScale: 0.9,
- zoomTime: 500,
- });
+ if (this.isSelected() && SelectionManager.Views().length > 1) SelectionManager.DeselectView(this);
+ else {
+ SelectionManager.SelectView(this, extendSelection);
+ if (focusSelection) {
+ DocumentManager.Instance.showDocument(this.rootDoc, {
+ willZoomCentered: true,
+ zoomScale: 0.9,
+ zoomTime: 500,
+ });
+ }
}
};
NativeWidth = () => this.effectiveNativeWidth;
@@ -1596,17 +1614,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
PanelHeight = () => this.panelHeight;
NativeDimScaling = () => this.nativeScaling;
selfView = () => this;
- trueNativeWidth = () => returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, false));
screenToLocalTransform = () =>
this.props
.ScreenToLocalTransform()
.translate(-this.centeringX, -this.centeringY)
- .scale(this.trueNativeWidth() ? 1 / this.nativeScaling : 1);
+ .scale(1 / this.nativeScaling);
+
+ @action
componentDidMount() {
- this._disposers.updateContentsScript = reaction(
- () => ScriptCast(this.rootDoc.updateContentsScript)?.script?.run({ this: this.props.Document, self: Cast(this.rootDoc, Doc, null) || this.props.Document }).result,
- output => output
- );
+ this.rootDoc[DocViews].add(this);
+ this._disposers.updateContentsScript = reaction(() => ScriptCast(this.rootDoc.updateContentsScript)?.script?.run({ this: this.rootDoc, self: this.rootDoc }).result, emptyFunction);
this._disposers.height = reaction(
// increase max auto height if document has been resized to be greater than current max
() => NumCast(this.layoutDoc._height),
@@ -1617,23 +1634,36 @@ export class DocumentView extends React.Component<DocumentViewProps> {
);
!BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.AddView(this);
}
+ @action
componentWillUnmount() {
+ this.rootDoc[DocViews].delete(this);
Object.values(this._disposers).forEach(disposer => disposer?.());
!BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
}
+ // want the htmloverlay to be able to fade in but we also want it to be display 'none' until it is needed.
+ // unfortunately, CSS can't transition animate any properties for something that is display 'none'.
+ // so we need to first activate the div, then, after a render timeout, start the opacity transition.
+ @observable enableHtmlOverlayTransitions: boolean = false;
@computed get htmlOverlay() {
- return !this.textHtmlOverlay ? null : (
- <div className="documentView-htmlOverlay">
- <div className="documentView-htmlOverlayInner">
- <Fade delay={0} duration={500}>
- {DocumentViewInternal.AnimationEffect(
- <div className="webBox-textHighlight">
- <ObserverJsxParser autoCloseVoidElements={true} key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this.textHtmlOverlay)} />
- </div>,
- { presentation_effect: this.htmlOverlayEffect ?? 'Zoom' } as any as Doc,
- this.rootDoc
- )}{' '}
- </Fade>
+ const effect = StrCast(this.htmlOverlayEffect?.presentation_effect, StrCast(this.htmlOverlayEffect?.followLinkAnimEffect));
+ return (
+ <div
+ className="documentView-htmlOverlay"
+ ref={r => {
+ const val = r?.style.display !== 'none'; // if the outer overlay has been displayed, trigger the innner div to start it's opacity fade in transition
+ if (r && val !== this.enableHtmlOverlayTransitions) {
+ setTimeout(action(() => (this.enableHtmlOverlayTransitions = val)));
+ }
+ }}
+ style={{ display: !this.textHtmlOverlay ? 'none' : undefined }}>
+ <div className="documentView-htmlOverlayInner" style={{ transition: `all 500ms`, opacity: this.enableHtmlOverlayTransitions ? 0.9 : 0 }}>
+ {DocumentViewInternal.AnimationEffect(
+ <div className="webBox-textHighlight">
+ <ObserverJsxParser autoCloseVoidElements={true} key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this.textHtmlOverlay)} />
+ </div>,
+ { ...(this.htmlOverlayEffect ?? {}), presentation_effect: effect ?? PresEffect.Zoom } as any as Doc,
+ this.rootDoc
+ )}
</div>
</div>
);
@@ -1651,7 +1681,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
className="contentFittingDocumentView-previewDoc"
ref={this.ContentRef}
style={{
- transition: this.props.dataTransition,
+ transition: 'inherit', // this.props.dataTransition,
transform: `translate(${this.centeringX}px, ${this.centeringY}px)`,
width: xshift ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`,
height: this.props.forceAutoHeight
@@ -1689,8 +1719,7 @@ ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
});
ScriptingGlobals.add(function deiconifyViewToLightbox(documentView: DocumentView) {
- //documentView.iconify(() =>
- LightboxView.AddDocTab(documentView.rootDoc, OpenWhere.lightbox, 'layout'); //, 0);
+ LightboxView.Instance.AddDocTab(documentView.rootDoc, OpenWhere.lightbox, 'layout'); //, 0);
});
ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
@@ -1700,7 +1729,7 @@ ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuff
ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc, linkSource: Doc) {
const collectedLinks = DocListCast(Doc.GetProto(linkCollection).data);
- let wid = linkSource[Width]();
+ let wid = NumCast(linkSource._width);
let embedding: Doc | undefined;
const links = LinkManager.Links(linkSource);
links.forEach(link => {
@@ -1711,7 +1740,7 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc, linkSour
embedding.x = wid;
embedding.y = 0;
embedding._lockedPosition = false;
- wid += otherdoc[Width]();
+ wid += NumCast(otherdoc._width);
Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', embedding);
}
});
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index a77e4bdd1..d347c285b 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -2,7 +2,6 @@ import EquationEditor from 'equation-editor-react';
import { action, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Width } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { NumCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
@@ -68,7 +67,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
if (e.key === 'Tab') {
const graph = Docs.Create.FunctionPlotDocument([this.rootDoc], {
- x: NumCast(this.layoutDoc.x) + this.layoutDoc[Width](),
+ x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width),
y: NumCast(this.layoutDoc.y),
_width: 400,
_height: 300,
@@ -85,8 +84,18 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
updateSize = () => {
const style = this._ref.current && getComputedStyle(this._ref.current.element.current);
if (style?.width.endsWith('px') && style?.height.endsWith('px')) {
- this.layoutDoc._width = Math.max(35, Number(style.width.replace('px', '')));
- this.layoutDoc._height = Math.max(25, Number(style.height.replace('px', '')));
+ if (this.layoutDoc._nativeWidth) {
+ // if equation has been scaled then editing the expression must also edit the native dimensions to keep the aspect ratio
+ const prevNwidth = NumCast(this.layoutDoc._nativeWidth);
+ const prevNheight = NumCast(this.layoutDoc._nativeHeight);
+ this.layoutDoc._nativeWidth = Math.max(35, Number(style.width.replace('px', '')));
+ this.layoutDoc._nativeHeight = Math.max(25, Number(style.height.replace('px', '')));
+ this.layoutDoc._width = (NumCast(this.layoutDoc._width) * NumCast(this.layoutDoc._nativeWidth)) / prevNwidth;
+ this.layoutDoc._height = (NumCast(this.layoutDoc._height) * NumCast(this.layoutDoc._nativeHeight)) / prevNheight;
+ } else {
+ this.layoutDoc._width = Math.max(35, Number(style.width.replace('px', '')));
+ this.layoutDoc._height = Math.max(25, Number(style.height.replace('px', '')));
+ }
}
};
render() {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index f014f842e..f7f94c546 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -21,7 +21,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
select: (isCtrlPressed: boolean) => void;
isContentActive: (outsideReaction?: boolean) => boolean | undefined;
isDocumentActive?: () => boolean | undefined;
- isSelected: (outsideReaction?: boolean) => boolean;
+ isSelected: () => boolean;
setHeight?: (height: number) => void;
NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to DocumentViewInternalsProps
onBrowseClick?: () => ScriptField | undefined;
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index d2e1293da..de5ad1631 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -46,6 +46,15 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(FontIconBox, fieldKey);
}
+ //
+ // This controls whether fontIconButtons will display labels under their icons or not
+ //
+ public static get ShowIconLabels() {
+ return BoolCast(Doc.UserDoc()._showLabel);
+ }
+ public static set ShowIconLabels(show: boolean) {
+ Doc.UserDoc()._showLabel = show;
+ }
@observable noTooltip = false;
showTemplate = (): void => {
const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
@@ -161,7 +170,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
Doc.UnBrushAllDocs();
})}>
{this.Icon(color)}
- {!this.label || !Doc.GetShowIconLabels() ? null : (
+ {!this.label || !FontIconBox.ShowIconLabels ? null : (
<div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
{' '}
{this.label}{' '}
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 29943e156..3ffda5a35 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -128,6 +128,7 @@
right: 0;
bottom: 0;
z-index: 2;
+ cursor: default;
}
.imageBox-fader img {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 2f4f788d4..103843046 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
import { observer } from 'mobx-react';
import { extname } from 'path';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { DocData, Width } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -50,7 +50,6 @@ const uploadIcons = {
@observer
export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
- protected _multiTouchDisposer?: import('../../util/InteractionUtils').InteractionUtils.MultiTouchEventDisposer | undefined;
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(ImageBox, fieldKey);
}
@@ -71,6 +70,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
private _disposers: { [name: string]: IReactionDisposer } = {};
private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined;
private _overlayIconRef = React.createRef<HTMLDivElement>();
+ private _marqueeref = React.createRef<MarqueeAnnotator>();
@observable _curSuffix = '';
@observable _uploadIcon = uploadIcons.idle;
@@ -85,7 +85,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
};
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
- const visibleAnchor = this._getAnchor?.(this._savedAnnotations, false); // use marquee anchor, otherwise, save zoom/pan as anchor
+ const visibleAnchor = this._getAnchor?.(this._savedAnnotations, true); // use marquee anchor, otherwise, save zoom/pan as anchor
const anchor =
visibleAnchor ??
Docs.Create.ConfigDocument({
@@ -116,7 +116,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
);
const layoutDoc = this.layoutDoc;
this._disposers.path = reaction(
- () => ({ nativeSize: this.nativeSize, width: this.layoutDoc[Width]() }),
+ () => ({ nativeSize: this.nativeSize, width: NumCast(this.layoutDoc._width) }),
({ nativeSize, width }) => {
if (layoutDoc === this.layoutDoc || !this.layoutDoc._height) {
this.layoutDoc._height = (width * nativeSize.nativeHeight) / nativeSize.nativeWidth;
@@ -240,7 +240,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
croppingProto.freeform_panY_max = anchh / viewScale;
if (addCrop) {
DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
- cropping.x = NumCast(this.rootDoc.x) + this.rootDoc[Width]();
+ cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
cropping.y = NumCast(this.rootDoc.y);
this.props.addDocTab(cropping, OpenWhere.inParent);
}
@@ -474,7 +474,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
- @observable _marqueeing: number[] | undefined;
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@computed get annotationLayer() {
TraceMobx();
@@ -488,7 +487,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
e,
action(e => {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
+ this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]);
return true;
}),
returnFalse,
@@ -500,7 +499,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action
finishMarquee = () => {
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
- this._marqueeing = undefined;
+ this._marqueeref.current?.onTerminateSelection();
this.props.select(false);
};
focus = (anchor: Doc, options: DocFocusOptions) => (anchor.type === DocumentType.CONFIG ? undefined : this._ffref.current?.focus(anchor, options));
@@ -558,13 +557,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{this.content}
</CollectionFreeFormView>
{this.annotationLayer}
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+ {!this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
rootDoc={this.rootDoc}
scrollTop={0}
- down={this._marqueeing}
- scaling={this.props.NativeDimScaling}
- docView={this.props.docViewPath().slice(-1)[0]}
+ annotationLayerScrollTop={0}
+ scaling={returnOne}
+ annotationLayerScaling={this.props.NativeDimScaling}
+ docView={this.props.DocumentView!}
addDocument={this.addDocument}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotations}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index f22cb195f..6f99b7c28 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -7,7 +7,7 @@ import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { EditableView } from '../EditableView';
import { DefaultStyleProvider } from '../StyleProvider';
-import { OpenWhere } from './DocumentView';
+import { OpenWhere, OpenWhereMod } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { KeyValueBox } from './KeyValueBox';
import './KeyValueBox.scss';
@@ -50,7 +50,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
if (value instanceof Doc) {
e.stopPropagation();
e.preventDefault();
- ContextMenu.Instance.addItem({ description: 'Open Fields', event: () => this.props.addDocTab(value, ((OpenWhere.addRight as string) + 'KeyValue') as OpenWhere), icon: 'layer-group' });
+ ContextMenu.Instance.addItem({ description: 'Open Fields', event: () => this.props.addDocTab(value, OpenWhere.addRightKeyvalue), icon: 'layer-group' });
ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
}
};
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index e66fed84b..416cb11cc 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -2,7 +2,6 @@ import React = require('react');
import { Bezier } from 'bezier-js';
import { computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
-import { Height, Width } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { DocCast, NumCast, StrCast } from '../../../fields/Types';
import { aggregateBounds, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils';
@@ -33,11 +32,11 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
return DocumentManager.Instance.getDocumentView(anchor_2, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement());
}
screenBounds = () => {
- if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
+ if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.CollectionFreeFormView) {
const a_invXf = this.anchor1.props.ScreenToLocalTransform().inverse();
const b_invXf = this.anchor2.props.ScreenToLocalTransform().inverse();
- const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(this.anchor1.rootDoc[Width](), this.anchor1.rootDoc[Height]()) };
- const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(this.anchor2.rootDoc[Width](), this.anchor2.rootDoc[Height]()) };
+ const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(this.anchor1.rootDoc._width), NumCast(this.anchor1.rootDoc._height)) };
+ const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(this.anchor2.rootDoc._width), NumCast(this.anchor2.rootDoc._height)) };
const pts = [] as number[][];
pts.push([(a_scrBds.tl[0] + a_scrBds.br[0]) / 2, (a_scrBds.tl[1] + a_scrBds.br[1]) / 2]);
@@ -51,23 +50,23 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
return { left: agg.x, top: agg.y, right: agg.r, bottom: agg.b, center: undefined };
}
- return { left: 0, top: 0, right: 0, bottom: 0, center: undefined };
+ return undefined;
};
disposer: IReactionDisposer | undefined;
componentDidMount() {
this.props.setContentView?.(this);
this.disposer = reaction(
() => {
- if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
+ if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.CollectionFreeFormView) {
const a = (this.anchor1 ?? this.anchor2)!;
const b = (this.anchor2 ?? this.anchor1)!;
const parxf = this.props.docViewPath()[this.props.docViewPath().length - 2].ComponentView as CollectionFreeFormView;
- const this_xf = parxf?.getTransform() ?? Transform.Identity; //this.props.ScreenToLocalTransform();
+ const this_xf = parxf?.screenToLocalXf ?? Transform.Identity; //this.props.ScreenToLocalTransform();
const a_invXf = a.props.ScreenToLocalTransform().inverse();
const b_invXf = b.props.ScreenToLocalTransform().inverse();
- const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(a.rootDoc[Width](), a.rootDoc[Height]()) };
- const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(b.rootDoc[Width](), b.rootDoc[Height]()) };
+ const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(a.rootDoc._width), NumCast(a.rootDoc._height)) };
+ const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(b.rootDoc._width), NumCast(b.rootDoc._height)) };
const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) };
const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) };
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 198cbe851..7e4f1da8e 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -3,13 +3,13 @@ import { Tooltip } from '@material-ui/core';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import wiki from 'wikijs';
-import { Doc, DocCastAsync, Opt } from '../../../fields/Doc';
-import { Height, Width } from '../../../fields/DocSymbols';
+import { Doc, Opt } from '../../../fields/Doc';
import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { Docs } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from '../../util/DragManager';
import { LinkFollower } from '../../util/LinkFollower';
import { LinkManager } from '../../util/LinkManager';
@@ -19,7 +19,6 @@ import { Transform } from '../../util/Transform';
import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView';
import './LinkDocPreview.scss';
import React = require('react');
-import { DocumentManager } from '../../util/DocumentManager';
interface LinkDocPreviewProps {
linkDoc?: Doc;
@@ -56,21 +55,20 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
LinkDocPreview._instance = this;
}
- @action init() {
+ @action
+ init() {
var linkTarget = this.props.linkDoc;
this._linkSrc = this.props.linkSrc;
this._linkDoc = this.props.linkDoc;
- const link_anchor_1 = this._linkDoc?.link_anchor_1 as Doc;
- const link_anchor_2 = this._linkDoc?.link_anchor_2 as Doc;
+ const link_anchor_1 = DocCast(this._linkDoc?.link_anchor_1);
+ const link_anchor_2 = DocCast(this._linkDoc?.link_anchor_2);
if (link_anchor_1 && link_anchor_2) {
linkTarget = Doc.AreProtosEqual(link_anchor_1, this._linkSrc) || Doc.AreProtosEqual(link_anchor_1?.annotationOn as Doc, this._linkSrc) ? link_anchor_2 : link_anchor_1;
}
if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) {
- // want to show annotation embedContainer document if annotation is not text
- linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => (this._markerTargetDoc = this._targetDoc = anno)));
- } else {
- this._markerTargetDoc = this._targetDoc = linkTarget;
+ linkTarget = DocCast(linkTarget.annotationOn); // want to show annotation embedContainer document if annotation is not text
}
+ this._markerTargetDoc = this._targetDoc = linkTarget;
this._toolTipText = '';
this.updateHref();
}
@@ -190,17 +188,17 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
width = () => {
if (!this._targetDoc) return 225;
- if (this._targetDoc[Width]() < this._targetDoc?.[Height]()) {
- return (Math.min(225, this._targetDoc[Height]()) * this._targetDoc[Width]()) / this._targetDoc[Height]();
+ if (NumCast(this._targetDoc._width) < NumCast(this._targetDoc._height)) {
+ return (Math.min(225, NumCast(this._targetDoc._height)) * NumCast(this._targetDoc._width)) / NumCast(this._targetDoc._height);
}
- return Math.min(225, NumCast(this._targetDoc?.[Width](), 225));
+ return Math.min(225, NumCast(this._targetDoc._width, 225));
};
height = () => {
if (!this._targetDoc) return 225;
- if (this._targetDoc[Width]() > this._targetDoc?.[Height]()) {
- return (Math.min(225, this._targetDoc[Width]()) * this._targetDoc[Height]()) / this._targetDoc[Width]();
+ if (NumCast(this._targetDoc._width) > NumCast(this._targetDoc._height)) {
+ return (Math.min(225, NumCast(this._targetDoc._width)) * NumCast(this._targetDoc._height)) / NumCast(this._targetDoc._width);
}
- return Math.min(225, NumCast(this._targetDoc?.[Height](), 225));
+ return Math.min(225, NumCast(this._targetDoc._height, 225));
};
@computed get previewHeader() {
return !this._linkDoc || !this._markerTargetDoc || !this._targetDoc || !this._linkSrc ? null : (
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
index bdc074e0c..4bb0f14d2 100644
--- a/src/client/views/nodes/LoadingBox.tsx
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -1,4 +1,4 @@
-import { observable, runInAction } from 'mobx';
+import { action, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import ReactLoading from 'react-loading';
@@ -36,11 +36,28 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(LoadingBox, fieldKey);
}
+ @observable public static CurrentlyLoading: Doc[] = []; // this assignment doesn't work. the actual assignment happens in DocumentManager's constructor
+ // removes from currently loading display
+ @action
+ public static removeCurrentlyLoading(doc: Doc) {
+ if (LoadingBox.CurrentlyLoading) {
+ const index = LoadingBox.CurrentlyLoading.indexOf(doc);
+ index !== -1 && LoadingBox.CurrentlyLoading.splice(index, 1);
+ }
+ }
+
+ // adds doc to currently loading display
+ @action
+ public static addCurrentlyLoading(doc: Doc) {
+ if (LoadingBox.CurrentlyLoading.indexOf(doc) === -1) {
+ LoadingBox.CurrentlyLoading.push(doc);
+ }
+ }
_timer: any;
@observable progress = '';
componentDidMount() {
- if (!Doc.CurrentlyLoading?.includes(this.rootDoc)) {
+ if (!LoadingBox.CurrentlyLoading?.includes(this.rootDoc)) {
this.rootDoc.loadingError = 'Upload interrupted, please try again';
} else {
const updateFunc = async () => {
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index 7af4d9b59..f6680aac0 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -48,7 +48,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
this._disposer = reaction(
() => SelectionManager.Views().slice(),
- selected => MapAnchorMenu.Instance.fadeOut(true)
+ sel => MapAnchorMenu.Instance.fadeOut(true)
);
}
// audioDown = (e: React.PointerEvent) => {
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 50b070e7f..9b75ca7e3 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -5,11 +5,10 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
-import { DocCss, Highlight, Width } from '../../../../fields/DocSymbols';
+import { DocCss, Highlight } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
-import { InkTool } from '../../../../fields/InkField';
import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
import { Docs, DocUtils } from '../../../documents/Documents';
import { DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
@@ -21,7 +20,6 @@ import { undoable, UndoManager } from '../../../util/UndoManager';
import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
-import { MarqueeAnnotator } from '../../MarqueeAnnotator';
import { SidebarAnnos } from '../../SidebarAnnos';
import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
@@ -67,14 +65,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return FieldView.LayoutString(MapBox, fieldKey);
}
private _dragRef = React.createRef<HTMLDivElement>();
- private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
- private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _sidebarRef = React.createRef<SidebarAnnos>();
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
private _disposers: { [key: string]: IReactionDisposer } = {};
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
- @observable private _marqueeing: number[] | undefined;
@observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@computed get allSidebarDocs() {
return DocListCast(this.dataDoc[this.SidebarKey]);
@@ -170,7 +165,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
.ScreenToLocalTransform()
.scale(this.props.NativeDimScaling?.() || 1)
.transformDirection(delta[0], delta[1]);
- const fullWidth = this.layoutDoc[Width]();
+ const fullWidth = NumCast(this.layoutDoc._width);
const mapWidth = fullWidth - this.sidebarWidth();
if (this.sidebarWidth() + localDelta[0] > 0) {
this.layoutDoc._layout_showSidebar = true;
@@ -278,28 +273,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func);
- @action
- onMarqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
- setupMoveUpEvents(
- this,
- e,
- action(e => {
- MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
- return true;
- }),
- returnFalse,
- () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
- false
- );
- }
- };
- @action finishMarquee = (x?: number, y?: number) => {
- this._marqueeing = undefined;
- x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false, this.rootDoc);
- };
-
addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => this.addDocument(doc, annotationKey);
pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none');
@@ -307,8 +280,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
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._layout_scrollTop));
- transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
- opaqueFilter = () => [...this.props.childFilters(), Utils.IsOpaqueFilter()];
+ transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
+ opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
infoWidth = () => this.props.PanelWidth() / 5;
infoHeight = () => this.props.PanelHeight() / 5;
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
@@ -832,23 +805,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
isAnyChildContentActive={this.isAnyChildContentActive}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
/> */}
-
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
- <MarqueeAnnotator
- rootDoc={this.rootDoc}
- anchorMenuClick={this.anchorMenuClick}
- scrollTop={0}
- down={this._marqueeing}
- scaling={returnOne}
- addDocument={this.addDocumentWrapper}
- docView={this.props.docViewPath().lastElement()}
- finishMarquee={this.finishMarquee}
- savedAnnotations={this.savedAnnotations}
- annotationLayer={this._annotationLayer.current}
- selectionText={returnEmptyString}
- mainCont={this._mainCont.current}
- />
- )}
</div>
{/* </LoadScript > */}
<div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 407a91dd0..6bad7d724 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -4,11 +4,9 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, runInAc
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
-import { Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
-import { InkTool } from '../../../../fields/InkField';
import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { SnappingManager } from '../../../util/SnappingManager';
@@ -16,7 +14,6 @@ import { UndoManager } from '../../../util/UndoManager';
import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
-import { MarqueeAnnotator } from '../../MarqueeAnnotator';
import { AnchorMenu } from '../../pdf/AnchorMenu';
import { Annotation } from '../../pdf/Annotation';
import { SidebarAnnos } from '../../SidebarAnnos';
@@ -107,8 +104,6 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable private selectedPlace: Doc | undefined;
@observable private markerMap: { [id: string]: google.maps.Marker } = {};
@observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
- @observable private _marqueeing: number[] | undefined;
- @observable private _isAnnotating = false;
@observable private inputRef = React.createRef<HTMLInputElement>();
@observable private searchMarkers: google.maps.Marker[] = [];
@observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
@@ -120,7 +115,6 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return DocListCast(this.dataDoc[this.annotationKey]);
}
@observable private toggleAddMarker = false;
- private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
@observable _showSidebar = false;
@computed get SidebarShown() {
@@ -368,7 +362,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
.ScreenToLocalTransform()
.scale(this.props.NativeDimScaling?.() || 1)
.transformDirection(delta[0], delta[1]);
- const fullWidth = this.layoutDoc[Width]();
+ const fullWidth = NumCast(this.layoutDoc._width);
const mapWidth = fullWidth - this.sidebarWidth();
if (this.sidebarWidth() + localDelta[0] > 0) {
this._showSidebar = true;
@@ -482,29 +476,6 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
- @action
- onMarqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
- setupMoveUpEvents(
- this,
- e,
- action(e => {
- MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
- return true;
- }),
- returnFalse,
- () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
- false
- );
- }
- };
- @action finishMarquee = (x?: number, y?: number) => {
- this._marqueeing = undefined;
- this._isAnnotating = false;
- x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false, this.props.Document);
- };
-
addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
return this.addDocument(doc, annotationKey);
};
@@ -547,8 +518,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
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._layout_scrollTop));
- transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
- opaqueFilter = () => [...this.props.childFilters(), Utils.IsOpaqueFilter()];
+ transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
+ opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
infoWidth = () => this.props.PanelWidth() / 5;
infoHeight = () => this.props.PanelHeight() / 5;
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
@@ -599,22 +570,6 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
{/* {this.handleMapCenter(this._map)} */}
</GoogleMap>
</div>
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
- <MarqueeAnnotator
- rootDoc={this.rootDoc}
- anchorMenuClick={this.anchorMenuClick}
- scrollTop={0}
- down={this._marqueeing}
- scaling={returnOne}
- addDocument={this.addDocumentWrapper}
- docView={this.props.docViewPath().lastElement()}
- finishMarquee={this.finishMarquee}
- savedAnnotations={this.savedAnnotations}
- annotationLayer={this._annotationLayer.current}
- selectionText={returnEmptyString}
- mainCont={this._mainCont.current}
- />
- )}
</div>
{/* </LoadScript > */}
<div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index c068d9dd7..108fa5ce5 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -4,7 +4,6 @@ import { observer } from 'mobx-react';
import * as Pdfjs from 'pdfjs-dist';
import 'pdfjs-dist/web/pdf_viewer.css';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Height, Width } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { ComputedField } from '../../../fields/ScriptField';
@@ -63,7 +62,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
super(props);
const nw = Doc.NativeWidth(this.Document, this.dataDoc) || 927;
const nh = Doc.NativeHeight(this.Document, this.dataDoc) || 1200;
- !this.Document._layout_fitWidth && (this.Document._height = this.Document[Width]() * (nh / nw));
+ !this.Document._layout_fitWidth && (this.Document._height = NumCast(this.Document._width) * (nh / nw));
if (this.pdfUrl) {
if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => (this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href)));
else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => (this._pdf = pdf)));
@@ -104,15 +103,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
- newDiv.style.width = this.layoutDoc[Width]().toString();
- newDiv.style.height = this.layoutDoc[Height]().toString();
+ newDiv.style.width = NumCast(this.layoutDoc._width).toString();
+ newDiv.style.height = NumCast(this.layoutDoc._height).toString();
this.replaceCanvases(docViewContent, newDiv);
const htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv);
const anchx = NumCast(cropping.x);
const anchy = NumCast(cropping.y);
- const anchw = cropping[Width]() * (this.props.NativeDimScaling?.() || 1);
- const anchh = cropping[Height]() * (this.props.NativeDimScaling?.() || 1);
+ const anchw = NumCast(cropping._width) * (this.props.NativeDimScaling?.() || 1);
+ const anchh = NumCast(cropping._height) * (this.props.NativeDimScaling?.() || 1);
const viewScale = 1;
cropping.title = 'crop: ' + this.rootDoc.title;
cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
@@ -169,8 +168,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
CollectionFreeFormView.UpdateIcon(
filename,
docViewContent,
- this.layoutDoc[Width](),
- this.layoutDoc[Height](),
+ NumCast(this.layoutDoc._width),
+ NumCast(this.layoutDoc._height),
this.props.PanelWidth(),
this.props.PanelHeight(),
NumCast(this.layoutDoc._layout_scrollTop),
@@ -196,7 +195,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
() => this.props.isSelected(),
() => {
document.removeEventListener('keydown', this.onKeyDown);
- this.props.isSelected(true) && document.addEventListener('keydown', this.onKeyDown);
+ this.props.isSelected() && document.addEventListener('keydown', this.onKeyDown);
},
{ fireImmediately: true }
);
@@ -255,8 +254,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.dataDoc[this.props.fieldKey + '_numPages'] = np;
Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), (nw * 96) / 72));
Doc.SetNativeHeight(this.dataDoc, (nh * 96) / 72);
- this.layoutDoc._height = this.layoutDoc[Width]() / (Doc.NativeAspect(this.dataDoc) || 1);
- !this.Document._layout_fitWidth && (this.Document._height = this.Document[Width]() * (nh / nw));
+ this.layoutDoc._height = NumCast(this.layoutDoc._width) / (Doc.NativeAspect(this.dataDoc) || 1);
+ !this.Document._layout_fitWidth && (this.Document._height = NumCast(this.Document._width) * (nh / nw));
};
public search = action((searchString: string, bwd?: boolean, clear: boolean = false) => {
@@ -331,7 +330,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
if (ratio >= 1) {
this.layoutDoc.nativeWidth = nativeWidth * ratio;
- onButton && (this.layoutDoc._width = this.layoutDoc[Width]() + localDelta[0]);
+ onButton && (this.layoutDoc._width = NumCast(this.layoutDoc._width) + localDelta[0]);
this.layoutDoc._show_sidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
return false;
@@ -348,15 +347,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
toggleSidebar = action((preview: boolean = false) => {
const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
- const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
+ const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + NumCast(this.layoutDoc._width)) / NumCast(this.layoutDoc._width);
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
if (preview) {
this._previewNativeWidth = nativeWidth * sideratio;
- this._previewWidth = (this.layoutDoc[Width]() * nativeWidth * sideratio) / curNativeWidth;
+ this._previewWidth = (NumCast(this.layoutDoc._width) * nativeWidth * sideratio) / curNativeWidth;
this._showSidebar = true;
} else {
this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
- this.layoutDoc._width = (this.layoutDoc[Width]() * nativeWidth * pdfratio) / curNativeWidth;
+ this.layoutDoc._width = (NumCast(this.layoutDoc._width) * nativeWidth * pdfratio) / curNativeWidth;
this.layoutDoc._show_sidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
});
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index ebb8a3374..b2512a0e5 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -6,7 +6,6 @@ import { observer } from 'mobx-react';
// import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
import { DateField } from '../../../fields/DateField';
import { Doc } from '../../../fields/Doc';
-import { Height, Width } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { ComputedField } from '../../../fields/ScriptField';
import { Cast, DocCast, NumCast } from '../../../fields/Types';
@@ -18,16 +17,16 @@ import { DocumentType } from '../../documents/DocumentTypes';
import { Networking } from '../../Network';
import { CaptureManager } from '../../util/CaptureManager';
import { SettingsManager } from '../../util/SettingsManager';
+import { TrackMovements } from '../../util/TrackMovements';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline';
import { ContextMenu } from '../ContextMenu';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { media_state } from './AudioBox';
import { FieldView, FieldViewProps } from './FieldView';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import './ScreenshotBox.scss';
import { VideoBox } from './VideoBox';
-import { TrackMovements } from '../../util/TrackMovements';
-import { media_state } from './AudioBox';
declare class MediaRecorder {
constructor(e: any, options?: any); // whatever MediaRecorder has
@@ -145,7 +144,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
if (!nativeWidth || !nativeHeight) {
if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
- this.layoutDoc._height = (this.layoutDoc[Width]() || 0) / aspect;
+ this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
}
};
@@ -295,7 +294,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
dictationTextProto.mediaState = ComputedField.MakeFunction(`self.${textField}_recordingSource.mediaState`);
this.dataDoc[this.fieldKey + '_dictation'] = dictationText;
};
- videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], this.layoutDoc[Height]()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], this.layoutDoc[Width]())) * this.props.PanelWidth();
+ videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc._height)) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc._width))) * this.props.PanelWidth();
formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
render() {
TraceMobx();
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 7c8a1849e..197c520c7 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -27,7 +27,6 @@ const _global = (window /* browser */ || global) /* node */ as any;
@observer
export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
private dropDisposer?: DragManager.DragDropDisposer;
- protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined;
public static LayoutString(fieldStr: string) {
return FieldView.LayoutString(ScriptingBox, fieldStr);
}
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index f803715ad..f90c19050 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -100,6 +100,7 @@
padding: 0 10px 0 7px;
transition: opacity 0.3s;
z-index: 10001;
+ transform-origin: top left;
.timecode-controls {
display: flex;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index d7f7c9b73..8bf2f4ce5 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -4,7 +4,6 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
import { observer } from 'mobx-react';
import { basename } from 'path';
import { Doc, StrListCast } from '../../../fields/Doc';
-import { Height, Width } from '../../../fields/DocSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
@@ -65,6 +64,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
private _youtubeIframeId: number = -1;
private _youtubeContentCreated = false;
private _audioPlayer: HTMLAudioElement | null = null;
+ private _marqueeref = React.createRef<MarqueeAnnotator>();
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); // outermost div
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _playRegionTimer: any = null; // timeout for playback
@@ -72,7 +72,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@observable _stackedTimeline: any; // CollectionStackedTimeline ref
@observable static _nativeControls: boolean; // default html controls
- @observable _marqueeing: number[] | undefined; // coords for marquee selection
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@observable _screenCapture = false;
@observable _clicking = false; // used for transition between showing/hiding timeline
@@ -163,7 +162,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (
// need to include range inputs because after dragging time slider it becomes target element
!(e.target instanceof HTMLInputElement && !(e.target.type === 'range')) &&
- this.props.isSelected(true)
+ this.props.isSelected()
) {
switch (e.key) {
case 'ArrowLeft':
@@ -355,8 +354,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
updateIcon = () => {
const makeIcon = (returnedfilename: string) => {
this.dataDoc.icon = new ImageField(returnedfilename);
- this.dataDoc.icon_nativeWidth = this.layoutDoc[Width]();
- this.dataDoc.icon_nativeHeight = this.layoutDoc[Height]();
+ this.dataDoc.icon_nativeWidth = NumCast(this.layoutDoc._width);
+ this.dataDoc.icon_nativeHeight = NumCast(this.layoutDoc._height);
};
this.Snapshot(undefined, undefined, makeIcon);
};
@@ -627,7 +626,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
() => !this._playing && this.Seek(NumCast(this.layoutDoc._layout_currentTimecode))
);
this._disposers.youtubeReactionDisposer = reaction(
- () => Doc.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentView.Interacting,
+ () => Doc.ActiveTool === InkTool.None && this.props.isSelected() && !SnappingManager.GetIsDragging() && !DocumentView.Interacting,
interactive => (iframe.style.pointerEvents = interactive ? 'all' : 'none'),
{ fireImmediately: true }
);
@@ -868,7 +867,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
e,
action(e => {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
+ this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]);
return true;
}),
returnFalse,
@@ -882,7 +881,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// ends marquee selection
@action
finishMarquee = () => {
- this._marqueeing = undefined;
+ this._marqueeref.current?.onTerminateSelection();
this.props.select(true);
};
@@ -913,7 +912,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
.scale(100 / this.heightPercent);
};
- marqueeFitScaling = () => ((this.props.NativeDimScaling?.() || 1) * this.heightPercent) / 100;
marqueeOffset = () => [((this.panelWidth() / 2) * (1 - this.heightPercent / 100)) / (this.heightPercent / 100), 0];
timelineDocFilter = () => [`_isTimelineLabel:true,${Utils.noRecursionHack}:x`];
@@ -938,8 +936,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div
className="videoBox-ui"
style={{
- transformOrigin: 'top left',
- transform: `rotate(${NumCast(this.rootDoc.rotation)}deg) translate(${-(xRight - xPos) + 10}px, ${yBot - yMid - uiHeight - uiMargin}px)`,
+ transform: `rotate(${this.props.ScreenToLocalTransform().inverse().RotateDeg}deg) translate(${-(xRight - xPos) + 10}px, ${yBot - yMid - uiHeight - uiMargin}px)`,
left: xPos,
top: yMid,
height: uiHeight,
@@ -953,6 +950,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
);
};
+ thumbnails = () => StrListCast(this.dataDoc[this.fieldKey + '_thumbnails']);
// renders CollectionStackedTimeline
@computed get renderTimeline() {
return (
@@ -964,7 +962,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
fieldKey={this.annotationKey}
dictationKey={this.fieldKey + '_dictation'}
mediaPath={this.audiopath}
- thumbnails={() => StrListCast(this.dataDoc[this.fieldKey + '_thumbnails'])}
+ thumbnails={this.thumbnails}
renderDepth={this.props.renderDepth + 1}
startTag={'_timecodeToShow' /* videoStart */}
endTag={'_timecodeToHide' /* videoEnd */}
@@ -1096,13 +1094,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
</CollectionFreeFormView>
</div>
{this.annotationLayer}
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+ {!this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
+ ref={this._marqueeref}
rootDoc={this.rootDoc}
scrollTop={0}
- down={this._marqueeing}
- scaling={this.marqueeFitScaling}
- docView={this.props.docViewPath().slice(-1)[0]}
+ annotationLayerScrollTop={0}
+ scaling={returnOne}
+ annotationLayerScaling={this.props.NativeDimScaling}
+ docView={this.props.DocumentView!}
containerOffset={this.marqueeOffset}
addDocument={this.addDocWithTimecode}
finishMarquee={this.finishMarquee}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2aca314da..e42fb4a03 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,9 +1,8 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as WebRequest from 'web-request';
import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
-import { Height, Width } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { HtmlField } from '../../../fields/HtmlField';
import { InkTool } from '../../../fields/InkField';
@@ -52,6 +51,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
+ private _marqueeref = React.createRef<MarqueeAnnotator>();
private _disposers: { [name: string]: IReactionDisposer } = {};
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _keyInput = React.createRef<HTMLInputElement>();
@@ -67,10 +67,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable private _searching: boolean = false;
@observable private _showSidebar = false;
@observable private _webPageHasBeenRendered = false;
- @observable private _overlayAnnoInfo: Opt<Doc>;
@observable private _marqueeing: number[] | undefined;
- @observable private _isAnnotating = false;
- @observable private _iframeClick: HTMLIFrameElement | undefined = undefined;
+ get marqueeing() {
+ return this._marqueeing;
+ }
+ set marqueeing(val) {
+ val && this._marqueeref.current?.onInitiateSelection(val);
+ !val && this._marqueeref.current?.onTerminateSelection();
+ this._marqueeing = val;
+ }
@observable private _iframe: HTMLIFrameElement | null = null;
@observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@observable private _scrollHeight = NumCast(this.layoutDoc.scrollHeight);
@@ -81,7 +86,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return this._url ? WebBox.urlHash(this._url) + '' : '';
}
@computed get scrollHeight() {
- return Math.max(this.layoutDoc[Height](), this._scrollHeight);
+ return Math.max(NumCast(this.layoutDoc._height), this._scrollHeight);
}
@computed get allAnnotations() {
return DocListCast(this.dataDoc[this.annotationKey]);
@@ -209,7 +214,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
if (!nativeWidth) Doc.SetNativeWidth(this.layoutDoc, 600);
Doc.SetNativeHeight(this.layoutDoc, (nativeWidth || 600) / youtubeaspect);
- this.layoutDoc._height = this.layoutDoc[Width]() / youtubeaspect;
+ this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
}
} // else it's an HTMLfield
} else if (this.webField && !this.dataDoc.text) {
@@ -246,6 +251,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
if (this._mainCont.current && selRange) {
+ if (this.rootDoc[this.props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this.props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`;
const clientRects = selRange.getClientRects();
for (let i = 0; i < clientRects.length; i++) {
const rect = clientRects.item(i);
@@ -262,12 +268,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, 1);
}
}
+ this._mainCont.current.style.transform = '';
}
- //this._selectionText = selRange.cloneContents().textContent || "";
this._selectionContent = selRange?.cloneContents();
this._selectionText = this._selectionContent?.textContent || '';
// clear selection
+ this._textAnnotationCreator = undefined;
if (sel.empty) sel.empty(); // Chrome
else if (sel.removeAllRanges) sel.removeAllRanges(); // Firefox
return this._savedAnnotations;
@@ -276,7 +283,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
focus = (anchor: Doc, options: DocFocusOptions) => {
if (anchor !== this.rootDoc && this._outerRef.current) {
const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
- const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[Height](), NumCast(this.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[Height](), this._scrollHeight));
+ const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), NumCast(anchor._height), NumCast(this.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + NumCast(anchor._height), this._scrollHeight));
if (scrollTo !== undefined) {
if (this._initialScroll === undefined) {
const focusTime = options.zoomTime ?? 500;
@@ -315,7 +322,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
ele.append(contents);
}
} catch (e) {}
- const visibleAnchor = this._getAnchor(this._savedAnnotations, false);
+ const visibleAnchor = this._getAnchor(this._savedAnnotations, true);
const anchor =
visibleAnchor ??
Docs.Create.ConfigDocument({
@@ -335,6 +342,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
iframeUp = (e: PointerEvent) => {
+ this._getAnchor = AnchorMenu.Instance?.GetAnchor; // need to save AnchorMenu's getAnchor since a subsequent selection on another doc will overwrite this value
this._textAnnotationCreator = undefined;
this.props.docViewPath().lastElement()?.docView?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) {
@@ -354,26 +362,27 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
@action
webClipDown = (e: React.PointerEvent) => {
- const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
+ e.stopPropagation();
+ const sel = window.getSelection();
+ this._textAnnotationCreator = undefined;
+ if (sel?.empty) sel.empty(); // Chrome
+ else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
+ // bcz: NEED TO unrotate e.clientX and e.clientY
const word = getWordAtPoint(e.target, e.clientX, e.clientY);
this._setPreviewCursor?.(e.clientX, e.clientY, false, true, this.rootDoc);
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- e.button !== 2 && (this._marqueeing = [e.clientX, e.clientY]);
- if (word || (e.target as any)?.className?.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
- e.stopPropagation();
- setTimeout(
- action(() => (this._marqueeing = undefined)),
- 100
- ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
- } else {
- this._isAnnotating = true;
- this.props.select(false);
- e.stopPropagation();
+
+ if (!word && !(e.target as any)?.className?.includes('rangeslider') && !(e.target as any)?.onclick && !(e.target as any)?.parentNode?.onclick) {
+ if (e.button !== 2) this.marqueeing = [e.clientX, e.clientY];
e.preventDefault();
}
document.addEventListener('pointerup', this.webClipUp);
};
+ @action
webClipUp = (e: PointerEvent) => {
+ if (window.getSelection()?.isCollapsed && this._marqueeref.current?.isEmpty) {
+ this.marqueeing = undefined;
+ }
document.removeEventListener('pointerup', this.webClipUp);
this._getAnchor = AnchorMenu.Instance?.GetAnchor; // need to save AnchorMenu's getAnchor since a subsequent selection on another doc will overwrite this value
const sel = window.getSelection();
@@ -382,7 +391,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._selectionText = sel.toString();
AnchorMenu.Instance.setSelectedText(sel.toString());
this._textAnnotationCreator = () => this.createTextAnnotation(sel, selRange);
- AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
+ (!sel.isCollapsed || this.marqueeing) && AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
// Changing which document to add the annotation to (the currently selected WebBox)
GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
@@ -390,36 +399,26 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
@action
iframeDown = (e: PointerEvent) => {
- const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
- const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
- const word = getWordAtPoint(e.target, e.clientX, e.clientY);
- this._setPreviewCursor?.(e.clientX, e.clientY, false, true, this.rootDoc);
+ this.props.select(false);
+ const locpt = {
+ x: (e.clientX / NumCast(this.rootDoc.nativeWidth)) * this.props.PanelWidth(),
+ y: ((e.clientY - NumCast(this.rootDoc.layout_scrollTop))/ NumCast(this.rootDoc.nativeHeight)) * this.props.PanelHeight() }; // prettier-ignore
+ const scrclick = this.props.DocumentView?.().props.ScreenToLocalTransform().inverse().transformPoint(locpt.x, locpt.y)!;
+ const scrcent = this.props
+ .DocumentView?.()
+ .props.ScreenToLocalTransform()
+ .inverse()
+ .transformPoint(NumCast(this.rootDoc.width) / 2, NumCast(this.rootDoc.height) / 2)!;
+ const theclickoff = Utils.rotPt(scrclick[0] - scrcent[0], scrclick[1] - scrcent[1], -this.props.ScreenToLocalTransform().Rotate);
+ const theclick = [theclickoff.x + scrcent[0], theclickoff.y + scrcent[1]];
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- e.button !== 2 && (this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale]);
- if (word || (e.target as any)?.className?.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
- setTimeout(
- action(() => (this._marqueeing = undefined)),
- 100
- ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
- } else {
- this._iframeClick = this._iframe ?? undefined;
- this._isAnnotating = true;
- this.props.select(false);
- e.stopPropagation();
- e.preventDefault();
- }
-
- // bcz: hack - iframe grabs all events which messes up how we handle contextMenus. So this super naively simulates the event stack to get the specific menu items and the doc view menu items.
- if (e.button === 2 || (e.button === 0 && e.altKey)) {
+ const word = getWordAtPoint(e.target, e.clientX, e.clientY);
+ if (!word && !(e.target as any)?.className?.includes('rangeslider') && !(e.target as any)?.onclick && !(e.target as any)?.parentNode?.onclick) {
+ this.marqueeing = theclick;
e.preventDefault();
- //e.stopPropagation();
- ContextMenu.Instance.closeMenu();
- ContextMenu.Instance.setIgnoreEvents(true);
}
};
isFirefox = () => 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1;
- iframeClick = () => this._iframeClick;
- iframeScaling = () => 1 / this.props.ScreenToLocalTransform().Scale;
addWebStyleSheet(document: any, styleType: string = 'text/css') {
if (document) {
@@ -490,7 +489,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._scrollHeight = Math.max(this._scrollHeight, iframeContent.body.scrollHeight || 0);
if (this._scrollHeight) {
this.rootDoc.nativeHeight = Math.min(NumCast(this.rootDoc.nativeHeight), this._scrollHeight);
- this.layoutDoc.height = Math.min(this.layoutDoc[Height](), (this.layoutDoc[Width]() * NumCast(this.rootDoc.nativeHeight)) / NumCast(this.rootDoc.nativeWidth));
+ this.layoutDoc.height = Math.min(NumCast(this.layoutDoc._height), (NumCast(this.layoutDoc._width) * NumCast(this.rootDoc.nativeHeight)) / NumCast(this.rootDoc.nativeWidth));
}
};
const swidth = Math.max(NumCast(this.layoutDoc.nativeWidth), iframeContent.body.scrollWidth || 0);
@@ -498,7 +497,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const aspectResize = swidth / NumCast(this.rootDoc.nativeWidth);
this.rootDoc.nativeWidth = swidth;
this.rootDoc.nativeHeight = NumCast(this.rootDoc.nativeHeight) * aspectResize;
- this.layoutDoc.height = this.layoutDoc[Height]() * aspectResize;
+ this.layoutDoc.height = NumCast(this.layoutDoc._height) * aspectResize;
}
initHeights();
this._iframetimeout && clearTimeout(this._iframetimeout);
@@ -708,10 +707,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
icon: 'snowflake',
});
funcs.push({
- description: (!this.layoutDoc.layout_forceReflow ? 'Force' : 'Prevent') + ' Reflow',
+ description: (!this.layoutDoc.layout_reflowHorizontal ? 'Force' : 'Prevent') + ' Reflow',
event: () => {
- const nw = !this.layoutDoc.layout_forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
- this.layoutDoc.layout_forceReflow = !nw;
+ const nw = !this.layoutDoc.layout_reflowHorizontal ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
+ this.layoutDoc.layout_reflowHorizontal = !nw;
if (nw) {
Doc.SetInPlace(this.layoutDoc, this.fieldKey + '_nativeWidth', nw, true);
}
@@ -723,38 +722,56 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
};
+ /**
+ * This gets called when some other child of the webbox is selected and a pointer down occurs on the webbox.
+ * it's also called for html clippings when you click outside the bounds of the clipping
+ * @param e
+ */
@action
onMarqueeDown = (e: React.PointerEvent) => {
+ const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
+ this._textAnnotationCreator = undefined;
+ if (sel?.empty) sel.empty(); // Chrome
+ else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
+ this.marqueeing = [e.clientX, e.clientY];
if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
setupMoveUpEvents(
this,
e,
action(e => {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
return true;
}),
returnFalse,
- () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+ action(() => {
+ this.marqueeing = undefined;
+ MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+ }),
false
);
+ } else {
+ this.marqueeing = undefined;
}
};
@action finishMarquee = (x?: number, y?: number, e?: PointerEvent) => {
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
- this._marqueeing = undefined;
- this._isAnnotating = false;
- this._iframeClick = undefined;
+ this.marqueeing = undefined;
+ this._textAnnotationCreator = undefined;
const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
if (sel?.empty) sel.empty(); // Chrome
else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
+ this._setPreviewCursor?.(x ?? 0, y ?? 0, false, !this._marqueeref.current?.isEmpty, this.rootDoc);
if (x !== undefined && y !== undefined) {
- this._setPreviewCursor?.(x, y, false, false, this.rootDoc);
ContextMenu.Instance.closeMenu();
ContextMenu.Instance.setIgnoreEvents(false);
if (e?.button === 2 || e?.altKey) {
- this.specificContextMenu(undefined as any);
- this.props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
+ e?.preventDefault();
+ e?.stopPropagation();
+ setTimeout(() => {
+ // if menu comes up right away, the down event can still be active causing a menu item to be selected
+ this.specificContextMenu(undefined as any);
+ this.props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
+ });
}
}
};
@@ -762,6 +779,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable lighttext = false;
@computed get urlContent() {
+ if (this.props.ScreenToLocalTransform().Scale > 25) return <div></div>;
setTimeout(
action(() => {
if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
@@ -796,7 +814,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
key={this._warning}
className="webBox-iframe"
ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))}
- style={{ pointerEvents: this._isAnyChildContentActive || DocumentView.Interacting ? 'none' : undefined }}
+ style={{ pointerEvents: DocumentView.Interacting ? 'none' : undefined }}
src={url}
onLoad={this.iframeLoaded}
scrolling="no" // ugh.. on windows, I get an inner scroll bar for the iframe's body even though the scrollHeight should be set to the full height of the document.
@@ -838,7 +856,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (ratio >= 1) {
this.layoutDoc.nativeWidth = nativeWidth * ratio;
this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
- onButton && (this.layoutDoc._width = this.layoutDoc[Width]() + localDelta[0]);
+ onButton && (this.layoutDoc._width = NumCast(this.layoutDoc._width) + localDelta[0]);
this.layoutDoc._layout_showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
return false;
@@ -873,24 +891,25 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
toggleSidebar = action((preview: boolean = false) => {
var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
if (!nativeWidth) {
- const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[Width]();
+ const defaultNativeWidth = NumCast(this.rootDoc.nativeWidth, this.rootDoc[this.fieldKey] instanceof WebField ? 850 : NumCast(this.Document._width));
Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth);
- Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[Height]() / this.Document[Width]()) * defaultNativeWidth);
+ Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (NumCast(this.Document._height) / NumCast(this.Document._width)) * defaultNativeWidth);
nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
}
const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
- const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
+ const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + NumCast(this.layoutDoc.width)) / NumCast(this.layoutDoc.width);
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
if (preview) {
this._previewNativeWidth = nativeWidth * sideratio;
- this._previewWidth = (this.layoutDoc[Width]() * nativeWidth * sideratio) / curNativeWidth;
+ this._previewWidth = (NumCast(this.layoutDoc._width) * nativeWidth * sideratio) / curNativeWidth;
this._showSidebar = true;
} else {
this.layoutDoc._layout_showSidebar = !this.layoutDoc._layout_showSidebar;
- this.layoutDoc._width = (this.layoutDoc[Width]() * nativeWidth * pdfratio) / curNativeWidth;
+ this.layoutDoc._width = (NumCast(this.layoutDoc._width) * nativeWidth * pdfratio) / curNativeWidth;
if (!this.layoutDoc._layout_showSidebar && !(this.dataDoc[this.fieldKey] instanceof WebField)) {
this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + '_nativeWidth'] = undefined;
} else {
+ !this.layoutDoc._layout_showSidebar && (this.dataDoc[this.fieldKey + '_nativeWidth'] = this.dataDoc[this.fieldKey + '_nativeHeight'] = undefined);
this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
}
}
@@ -918,7 +937,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
className={'webBox-cont' + (interactive ? '-interactive' : '')}
onKeyDown={e => e.stopPropagation()}
style={{
- width: !this.layoutDoc.layout_forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']) || `100%` : '100%',
+ width: !this.layoutDoc.layout_reflowHorizontal ? NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']) || `100%` : '100%',
transform: `scale(${this.zoomScaling()}) translate(${-NumCast(this.layoutDoc.freeform_panX)}px, ${-NumCast(this.layoutDoc.freeform_panY)}px)`,
}}>
{this._hackHide ? null : this.urlContent}
@@ -940,7 +959,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
{this.inlineTextAnnotations
.sort((a, b) => NumCast(a.y) - NumCast(b.y))
.map(anno => (
- <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+ <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
))}
</div>
);
@@ -1002,7 +1021,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
onWheel={this.onZoomWheel}
onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
onPointerDown={this.onMarqueeDown}>
- <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight) || '100%', pointerEvents }}>
+ <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight > this.props.PanelHeight() && this._scrollHeight) || '100%', pointerEvents }}>
{this.content}
<div style={{ display: SnappingManager.GetCanEmbed() ? 'none' : undefined, mixBlendMode: 'multiply' }}>{this.renderTransparentAnnotations}</div>
{this.renderOpaqueAnnotations}
@@ -1047,14 +1066,13 @@ 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, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func);
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._layout_scrollTop));
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
- transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
- opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.IsOpaqueFilter()])];
+ transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
+ opaqueFilter = () => [...this.props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.OpaqueBackgroundFilter])];
childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
if (doc instanceof Doc && property === StyleProp.PointerEvents) {
if (this.inlineTextAnnotations.includes(doc)) return 'none';
@@ -1065,7 +1083,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
!this._draggingSidebar && this.props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown()
? 'all' //
: 'none';
- annotationPointerEvents = () => (this.props.isContentActive() && (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
+ annotationPointerEvents = () => (this.props.isContentActive() && (SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
render() {
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
@@ -1088,27 +1106,26 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}}
onContextMenu={this.specificContextMenu}>
{this.webpage}
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
- <div style={{ transformOrigin: 'top left', transform: `scale(${1 / scale})` }}>
- <MarqueeAnnotator
- rootDoc={this.rootDoc}
- iframe={this.isFirefox() ? this.iframeClick : undefined}
- iframeScaling={this.isFirefox() ? this.iframeScaling : undefined}
- anchorMenuClick={this.anchorMenuClick}
- scrollTop={0}
- down={this._marqueeing}
- scaling={returnOne}
- addDocument={this.addDocumentWrapper}
- docView={this.props.docViewPath().lastElement()}
- finishMarquee={this.finishMarquee}
- savedAnnotations={this.savedAnnotationsCreator}
- selectionText={this.selectionText}
- annotationLayer={this._annotationLayer.current}
- mainCont={this._mainCont.current}
- />
- </div>
- )}
</div>
+ {!this._mainCont.current || !this._annotationLayer.current ? null : (
+ <div style={{ position: 'absolute', height: '100%', width: '100%', pointerEvents: this.marqueeing ? 'all' : 'none' }}>
+ <MarqueeAnnotator
+ ref={this._marqueeref}
+ rootDoc={this.rootDoc}
+ anchorMenuClick={this.anchorMenuClick}
+ scrollTop={NumCast(this.layoutDoc.layout_scrollTop)}
+ annotationLayerScrollTop={0}
+ scaling={this.props.NativeDimScaling}
+ addDocument={this.addDocumentWrapper}
+ docView={this.props.DocumentView!}
+ finishMarquee={this.finishMarquee}
+ savedAnnotations={this.savedAnnotationsCreator}
+ selectionText={this.selectionText}
+ annotationLayer={this._annotationLayer.current}
+ mainCont={this._mainCont.current}
+ />
+ </div>
+ )}
<div
className="webBox-sideResizer"
style={{
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
index 425ef3e54..914adb404 100644
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -2,7 +2,7 @@
*
* @param {StyleSheetList} styleSheets
*/
-var ForeignHtmlRenderer = function (styleSheets) {
+const ForeignHtmlRenderer = function (styleSheets) {
const self = this;
/**
@@ -10,7 +10,7 @@ var ForeignHtmlRenderer = function (styleSheets) {
* @param {String} binStr
*/
const binaryStringToBase64 = function (binStr) {
- return new Promise(function (resolve) {
+ return new Promise(resolve => {
const reader = new FileReader();
reader.readAsDataURL(binStr);
reader.onloadend = function () {
@@ -31,17 +31,17 @@ var ForeignHtmlRenderer = function (styleSheets) {
* @returns {Promise}
*/
const getResourceAsBase64 = function (webUrl, inurl) {
- return new Promise(function (resolve, reject) {
+ return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
- //const url = inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl;
- //const url = CorsProxy(inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl);// inurl.startsWith("http") ? CorsProxy(inurl) : inurl;
- var url = inurl;
+ // const url = inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl;
+ // const url = CorsProxy(inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl);// inurl.startsWith("http") ? CorsProxy(inurl) : inurl;
+ let url = inurl;
if (inurl.startsWith('/static')) {
url = new URL(webUrl).origin + inurl;
} else if (inurl.startsWith('/') && !inurl.startsWith('//')) {
url = CorsProxy(new URL(webUrl).origin + inurl);
} else if (!inurl.startsWith('http') && !inurl.startsWith('//')) {
- url = CorsProxy(webUrl + '/' + inurl);
+ url = CorsProxy(`${webUrl}/${inurl}`);
} else if (inurl.startsWith('https') && !inurl.startsWith(window.location.origin)) {
url = CorsProxy(inurl);
}
@@ -57,7 +57,7 @@ var ForeignHtmlRenderer = function (styleSheets) {
resourceBase64: resBase64,
});
} else if (xhr.readyState === XMLHttpRequest.DONE) {
- console.log("COULDN'T FIND: " + (inurl.startsWith('/') ? webUrl + inurl : inurl));
+ console.log(`COULDN'T FIND: ${inurl.startsWith('/') ? webUrl + inurl : inurl}`);
resolve({
resourceUrl: '',
resourceBase64: inurl,
@@ -76,7 +76,7 @@ var ForeignHtmlRenderer = function (styleSheets) {
*/
const getMultipleResourcesAsBase64 = function (webUrl, urls) {
const promises = [];
- for (let i = 0; i < urls.length; i++) {
+ for (let i = 0; i < urls.length; i += 1) {
promises.push(getResourceAsBase64(webUrl, urls[i]));
}
return Promise.all(promises);
@@ -98,7 +98,7 @@ var ForeignHtmlRenderer = function (styleSheets) {
}
let val = '';
- for (let i = idx + prefixToken.length; i < str.length; i++) {
+ for (let i = idx + prefixToken.length; i < str.length; i += 1) {
if (suffixTokens.indexOf(str[i]) !== -1) {
break;
}
@@ -114,6 +114,15 @@ var ForeignHtmlRenderer = function (styleSheets) {
/**
*
+ * @param {String} str
+ * @returns {String}
+ */
+ const removeQuotes = function (str) {
+ return str.replace(/["']/g, '');
+ };
+
+ /**
+ *
* @param {String} cssRuleStr
* @returns {String[]}
*/
@@ -127,13 +136,12 @@ var ForeignHtmlRenderer = function (styleSheets) {
break;
}
searchStartIndex = url.foundAtIndex + url.value.length + 1;
- if (mustEndWithQuote && url.value[url.value.length - 1] !== '"') continue;
- const unquoted = removeQuotes(url.value);
- if (!unquoted /* || (!unquoted.startsWith('http')&& !unquoted.startsWith("/") )*/ || unquoted === 'http://' || unquoted === 'https://') {
- continue;
+ if (!mustEndWithQuote || url.value[url.value.length - 1] === '"') {
+ const unquoted = removeQuotes(url.value);
+ if (unquoted /* || (!unquoted.startsWith('http')&& !unquoted.startsWith("/") ) */ && unquoted !== 'http://' && unquoted !== 'https://') {
+ if (unquoted) urlsFound.push(unquoted);
+ }
}
-
- unquoted && urlsFound.push(unquoted);
}
return urlsFound;
@@ -151,15 +159,6 @@ var ForeignHtmlRenderer = function (styleSheets) {
return getUrlsFromCssString(html, 'source=', [' ', '>', '\t'], true);
};
- /**
- *
- * @param {String} str
- * @returns {String}
- */
- const removeQuotes = function (str) {
- return str.replace(/["']/g, '');
- };
-
const escapeRegExp = function (string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
};
@@ -172,8 +171,8 @@ var ForeignHtmlRenderer = function (styleSheets) {
*
* @returns {Promise<String>}
*/
- const buildSvgDataUri = async function (webUrl, contentHtml, width, height, scroll, xoff) {
- return new Promise(async function (resolve, reject) {
+ const buildSvgDataUri = async function (webUrl, inputContentHtml, width, height, scroll, xoff) {
+ return new Promise(async (resolve, reject) => {
/* !! The problems !!
* 1. CORS (not really an issue, expect perhaps for images, as this is a general security consideration to begin with)
* 2. Platform won't wait for external assets to load (fonts, images, etc.)
@@ -181,17 +180,19 @@ var ForeignHtmlRenderer = function (styleSheets) {
// copy styles
let cssStyles = '';
- let urlsFoundInCss = [];
+ const urlsFoundInCss = [];
- for (let i = 0; i < styleSheets.length; i++) {
+ for (let i = 0; i < styleSheets.length; i += 1) {
try {
const rules = styleSheets[i].cssRules;
- for (let j = 0; j < rules.length; j++) {
+ for (let j = 0; j < rules.length; j += 1) {
const cssRuleStr = rules[j].cssText;
urlsFoundInCss.push(...getUrlsFromCssString(cssRuleStr));
cssStyles += cssRuleStr;
}
- } catch (e) {}
+ } catch (e) {
+ /* empty */
+ }
}
// const fetchedResourcesFromStylesheets = await getMultipleResourcesAsBase64(webUrl, urlsFoundInCss);
@@ -202,15 +203,15 @@ var ForeignHtmlRenderer = function (styleSheets) {
// }
// }
- contentHtml = contentHtml
+ let contentHtml = inputContentHtml
.replace(/<source[^>]*>/g, '') // <picture> tags have a <source> which has a srcset field of image refs. instead of converting each, just use the default <img> of the picture
.replace(/noscript/g, 'div')
.replace(/<div class="mediaset"><\/div>/g, '') // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s. But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag
.replace(/<link[^>]*>/g, '') // don't need to keep any linked style sheets because we've already processed all style sheets above
.replace(/srcset="([^ "]*)[^"]*"/g, 'src="$1"'); // instead of converting each item in the srcset to a data url, just convert the first one and use that
- let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml).filter(url => !url.startsWith('data:'));
+ const urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml).filter(url => !url.startsWith('data:'));
const fetchedResources = webUrl ? await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml) : [];
- for (let i = 0; i < fetchedResources.length; i++) {
+ for (let i = 0; i < fetchedResources.length; i += 1) {
const r = fetchedResources[i];
if (r.resourceUrl) {
contentHtml = contentHtml.replace(new RegExp(escapeRegExp(r.resourceUrl), 'g'), r.resourceBase64);
@@ -230,7 +231,7 @@ var ForeignHtmlRenderer = function (styleSheets) {
// contentRootElem.style.transform = "scale(0.08)"
contentRootElem.innerHTML = styleElemString + contentHtml;
contentRootElem.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
- //document.body.appendChild(contentRootElem);
+ // document.body.appendChild(contentRootElem);
const contentRootElemString = new XMLSerializer().serializeToString(contentRootElem);
@@ -256,13 +257,13 @@ var ForeignHtmlRenderer = function (styleSheets) {
* @return {Promise<Image>}
*/
this.renderToImage = async function (webUrl, html, width, height, scroll, xoff) {
- return new Promise(async function (resolve, reject) {
+ return new Promise(async (resolve, reject) => {
const img = new Image();
- console.log('BUILDING SVG for:' + webUrl);
+ console.log(`BUILDING SVG for: ${webUrl}`);
img.src = await buildSvgDataUri(webUrl, html, width, height, scroll, xoff);
img.onload = function () {
- console.log('IMAGE SVG created:' + webUrl);
+ console.log(`IMAGE SVG created: ${webUrl}`);
resolve(img);
};
});
@@ -276,7 +277,7 @@ var ForeignHtmlRenderer = function (styleSheets) {
* @return {Promise<Image>}
*/
this.renderToCanvas = async function (webUrl, html, width, height, scroll, xoff, oversample) {
- return new Promise(async function (resolve, reject) {
+ return new Promise(async (resolve, reject) => {
const img = await self.renderToImage(webUrl, html, width, height, scroll, xoff);
const canvas = document.createElement('canvas');
@@ -298,7 +299,7 @@ var ForeignHtmlRenderer = function (styleSheets) {
* @return {Promise<String>}
*/
this.renderToBase64Png = async function (webUrl, html, width, height, scroll, xoff, oversample) {
- return new Promise(async function (resolve, reject) {
+ return new Promise(async (resolve, reject) => {
const canvas = await self.renderToCanvas(webUrl, html, width, height, scroll, xoff, oversample);
resolve(canvas.toDataURL('image/png'));
});
@@ -320,8 +321,8 @@ export function CreateImage(webUrl, styleSheets, html, width, height, scroll, xo
);
}
-var ClipboardUtils = new (function () {
- var permissions = {
+const ClipboardUtils = new (function () {
+ const permissions = {
'image/bmp': true,
'image/gif': true,
'image/png': true,
@@ -330,8 +331,8 @@ var ClipboardUtils = new (function () {
};
function getType(types) {
- for (var j = 0; j < types.length; ++j) {
- var type = types[j];
+ for (let j = 0; j < types.length; j += 1) {
+ const type = types[j];
if (permissions[type]) {
return type;
}
@@ -339,10 +340,10 @@ var ClipboardUtils = new (function () {
return null;
}
function getItem(items) {
- for (var i = 0; i < items.length; ++i) {
- var item = items[i];
+ for (let i = 0; i < items.length; i += 1) {
+ const item = items[i];
if (item) {
- var type = getType(item.types);
+ const type = getType(item.types);
if (type) {
return item.getType(type);
}
@@ -352,7 +353,7 @@ var ClipboardUtils = new (function () {
}
function loadFile(file, callback) {
if (window.FileReader) {
- var reader = new FileReader();
+ const reader = new FileReader();
reader.onload = function () {
callback(reader.result, null);
};
@@ -366,23 +367,23 @@ var ClipboardUtils = new (function () {
}
this.readImage = function (callback) {
if (navigator.clipboard) {
- var promise = navigator.clipboard.read();
+ const promise = navigator.clipboard.read();
promise
- .then(function (items) {
- var promise = getItem(items);
- if (promise == null) {
+ .then(items => {
+ const promise2 = getItem(items);
+ if (promise2 == null) {
callback(null, null);
return;
}
- promise
- .then(function (result) {
+ promise2
+ .then(result => {
loadFile(result, callback);
})
- .catch(function (error) {
+ .catch(error => {
callback(null, 'Reading clipboard error.');
});
})
- .catch(function (error) {
+ .catch(error => {
callback(null, 'Reading clipboard error.');
});
} else {
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index c5167461b..e8b9a98b7 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -5,7 +5,7 @@ import * as ReactDOM from 'react-dom/client';
import { Doc } from '../../../../fields/Doc';
import { Height, Width } from '../../../../fields/DocSymbols';
import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
+import { emptyFunction, returnFalse, returnTrue, Utils } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs, DocUtils } from '../../../documents/Documents';
import { Transform } from '../../../util/Transform';
@@ -177,6 +177,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
};
componentWillUnmount = () => Object.values(this._disposers).forEach(disposer => disposer?.());
+ isContentActive = () => this.props.tbox.props.isSelected() || this.props.tbox.isAnyChildContentActive?.();
render() {
return !this._dashDoc || !this._finalLayout || this.props.hidden ? null : (
@@ -190,6 +191,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
display: 'inline-block',
left: 0,
top: 0,
+ pointerEvents: this.isContentActive() ? undefined : 'none',
}}
onPointerLeave={this.onPointerLeave}
onPointerEnter={this.onPointerEnter}
@@ -203,7 +205,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
rootSelected={returnFalse} //{this._textBox.props.isSelected}
removeDocument={this.removeDoc}
isDocumentActive={returnFalse}
- isContentActive={emptyFunction}
+ isContentActive={this.isContentActive}
styleProvider={this._textBox.props.styleProvider}
docViewPath={this._textBox.props.docViewPath}
ScreenToLocalTransform={this.getDocTransform}
@@ -213,7 +215,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
PanelWidth={this._finalLayout[Width]}
PanelHeight={this._finalLayout[Height]}
focus={this.outerFocus}
- whenChildContentsActiveChanged={returnFalse}
+ whenChildContentsActiveChanged={this.props.tbox.whenChildContentsActiveChanged}
bringToFront={emptyFunction}
dontRegisterView={false}
childFilters={this.props.tbox?.props.childFilters}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index d5ad128fe..0a64c8062 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -177,6 +177,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
style={{
width: this.props.width,
height: this.props.height,
+ pointerEvents: this.props.tbox.props.isSelected() || this.props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
}}>
{this.props.hideKey ? null : (
<span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 818c0cbe7..717efd5a9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -49,6 +49,7 @@ audiotag:hover {
transition: opacity 1s;
width: 100%;
position: relative;
+ transform-origin: left top;
top: 0;
left: 0;
}
@@ -89,6 +90,7 @@ audiotag:hover {
bottom: 0;
width: 11;
height: 11;
+ cursor: default;
}
.formattedTextBox-outer {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 41b1c59b0..b65c440d1 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,6 +1,7 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
+import { setCORS } from 'google-translate-api-browser';
import { isEqual } from 'lodash';
import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -14,7 +15,7 @@ import { EditorView } from 'prosemirror-view';
import { BsMarkdownFill } from 'react-icons/bs';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, Height, UpdatingFromServer, Width } from '../../../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
@@ -52,6 +53,7 @@ import { AnchorMenu } from '../../pdf/AnchorMenu';
import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup';
import { SidebarAnnos } from '../../SidebarAnnos';
import { StyleProp } from '../../StyleProvider';
+import { media_state } from '../AudioBox';
import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import { LinkDocPreview } from '../LinkDocPreview';
@@ -71,8 +73,6 @@ import { schema } from './schema_rts';
import { SummaryView } from './SummaryView';
import applyDevTools = require('prosemirror-dev-tools');
import React = require('react');
-import { media_state } from '../AudioBox';
-import { setCORS } from 'google-translate-api-browser';
// setting up cors-anywhere server address
const translate = setCORS('http://cors-anywhere.herokuapp.com/');
export const GoogleRef = 'googleDocId';
@@ -93,6 +93,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
static _bulletStyleSheet: any = addStyleSheet();
static _userStyleSheet: any = addStyleSheet();
static _hadSelection: boolean = false;
+ private _selectionHTML: string | undefined;
private _sidebarRef = React.createRef<SidebarAnnos>();
private _sidebarTagRef = React.createRef<React.Component>();
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
@@ -315,7 +316,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), () => this.getAnchor(true), targetCreator), e.pageX, e.pageY);
});
const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
- this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
+ this.props.isSelected() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
+ let ele: Opt<HTMLDivElement> = undefined;
+ try {
+ const contents = window.getSelection()?.getRangeAt(0).cloneContents();
+ if (contents) {
+ ele = document.createElement('div');
+ ele.append(contents);
+ }
+ this._selectionHTML = ele?.innerHTML;
+ } catch (e) {}
};
dispatchTransaction = (tx: Transaction) => {
@@ -434,10 +444,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
var tr = this._editorView.state.tr as any;
const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor;
tr = tr.removeMark(0, tr.doc.content.size, autoAnch);
- DocListCast(Doc.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
+ Doc.MyPublishedDocs.forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
this._editorView?.dispatch(tr);
- // this.prepareForTyping();
}
oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
};
@@ -460,7 +469,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (!(cfield instanceof ComputedField)) {
this.dataDoc.title = (prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '')).trim();
if (str.startsWith('@') && str.length > 1) {
- Doc.AddDocToList(Doc.MyPublishedDocs, undefined, this.rootDoc);
+ Doc.AddToMyPublished(this.rootDoc);
}
}
}
@@ -487,9 +496,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
) ||
DocUtils.MakeLink(this.rootDoc, target, { link_relationship: LinkManager.AutoKeywords })!);
newAutoLinks.add(alink);
+ // DocCast(alink.link_anchor_1).followLinkLocation = 'add:right';
const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
- const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: 'auto term', location: 'add:right' });
+ const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: 'auto term' });
tr = tr.addMark(pos, pos + node.nodeSize, link);
}
});
@@ -579,8 +589,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
// embed document when drag marked as embed
} else if (de.embedKey) {
const node = schema.nodes.dashDoc.create({
- width: draggedDoc[Width](),
- height: draggedDoc[Height](),
+ width: NumCast(draggedDoc._width),
+ height: NumCast(draggedDoc._height),
title: 'dashDoc',
docId: draggedDoc[Id],
float: 'unset',
@@ -704,14 +714,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@action
toggleSidebar = (preview: boolean = false) => {
+ const defaultSidebar = 250;
const prevWidth = 1 - this.sidebarWidth() / Number(getComputedStyle(this._ref.current!).width.replace('px', ''));
if (preview) this._showSidebar = true;
else {
this.layoutDoc[this.SidebarKey + '_freeform_scale_max'] = 1;
- this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
+ this.layoutDoc._layout_showSidebar =
+ (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(defaultSidebar / (NumCast(this.layoutDoc._width) + defaultSidebar)) * 100}%` : '0%') !== '0%';
}
- this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
+ this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) + defaultSidebar : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
};
sidebarDown = (e: React.PointerEvent) => {
const batch = UndoManager.StartBatch('toggle sidebar');
@@ -733,7 +745,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
.scale(this.props.NativeDimScaling?.() || 1)
.transformDirection(delta[0], delta[1]);
const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100;
- const width = this.layoutDoc[Width]() + localDelta[0];
+ const width = NumCast(this.layoutDoc._width) + localDelta[0];
this.layoutDoc._layout_sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
this.layoutDoc.width = width;
this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
@@ -853,29 +865,27 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
icon: !FormattedTextBox._globalHighlights.has(option) ? 'highlighter' : 'remove-format',
})
);
+ const appearance = cm.findByDescription('Appearance...');
+ const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
- const uicontrols: ContextMenuProps[] = [];
- uicontrols.push({
+ appearanceItems.push({
description: !this.Document._layout_noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle',
event: () => (this.layoutDoc._layout_noSidebar = !this.layoutDoc._layout_noSidebar),
icon: !this.Document._layout_noSidebar ? 'eye-slash' : 'eye',
});
- uicontrols.push({
+ appearanceItems.push({
description: (this.Document._layout_enableAltContentUI ? 'Hide' : 'Show') + ' Alt Content UI',
event: () => (this.layoutDoc._layout_enableAltContentUI = !this.layoutDoc._layout_enableAltContentUI),
icon: !this.Document._layout_enableAltContentUI ? 'eye-slash' : 'eye',
});
- !Doc.noviceMode && uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' });
+ !Doc.noviceMode && appearanceItems.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' });
!Doc.noviceMode &&
- uicontrols.push({
+ appearanceItems.push({
description: 'Broadcast Message',
event: () => DocServer.GetRefField('rtfProto').then(proto => proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)),
icon: 'expand-arrows-alt',
});
- cm.addItem({ description: 'UI Controls...', subitems: uicontrols, icon: 'asterisk' });
- const appearance = cm.findByDescription('Appearance...');
- const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
appearanceItems.push({ description: 'Change Style...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
// this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
@@ -909,7 +919,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
},
icon: 'eye',
});
- cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
+ !appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
const options = cm.findByDescription('Options...');
const optionItems = options && 'subitems' in options ? options.subitems : [];
@@ -1054,7 +1064,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
anchor.text = selectedText;
+ anchor.text_html = this._selectionHTML ?? selectedText;
anchor.title = selectedText.substring(0, 30);
+ anchor.presentation_zoomText = true;
return anchor;
}
return anchorDoc ?? this.rootDoc;
@@ -1407,7 +1419,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
view.state.schema.nodes.dashField.create({ fieldKey: 'text', docId: pdfAnchor[Id], hideKey: true, editable: false }, undefined, [
view.state.schema.marks.linkAnchor.create({
allAnchors: [{ href: `/doc/${this.rootDoc[Id]}`, title: this.rootDoc.title, anchorId: `${this.rootDoc[Id]}` }],
- location: 'add:right',
title: `from: ${DocCast(pdfAnchor.embedContainer).title}`,
noPreview: true,
docref: false,
@@ -1440,7 +1451,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const self = this;
return new Plugin({
view(newView) {
- runInAction(() => self.props.isSelected(true) && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView));
+ runInAction(() => self.props.isSelected() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView));
return new RichTextMenuPlugin({ editorProps: this.props });
},
});
@@ -1613,7 +1624,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this._downX = e.clientX;
this._downY = e.clientY;
FormattedTextBoxComment.textBox = this;
- if (e.button === 0 && (this.props.rootSelected(true) || this.props.isSelected(true)) && !e.altKey && !e.ctrlKey && !e.metaKey) {
+ if (e.button === 0 && (this.props.rootSelected() || this.props.isSelected()) && !e.altKey && !e.ctrlKey && !e.metaKey) {
if (e.clientX < this.ProseRef!.getBoundingClientRect().right) {
// stop propagation if not in sidebar, otherwise nested boxes will lose focus to outer boxes.
e.stopPropagation(); // if the text box's content is active, then it consumes all down events
@@ -1642,15 +1653,12 @@ 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 && state.doc.nodeAt(pcords.inside)?.type === state.schema.nodes.dashDoc) {
- return;
- }
}
};
@action
onDoubleClick = (e: React.MouseEvent): void => {
FormattedTextBoxComment.textBox = this;
- if (e.button === 0 && this.props.isSelected(true) && !e.altKey && !e.ctrlKey && !e.metaKey) {
+ if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
if (e.clientX < this.ProseRef!.getBoundingClientRect().right) {
// stop propagation if not in sidebar
e.stopPropagation(); // if the text box is selected, then it consumes all click events
@@ -1661,7 +1669,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
FormattedTextBoxComment.Hide();
- if (e.buttons === 1 && this.props.isSelected(true) && !e.altKey) {
+ if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
e.stopPropagation();
}
};
@@ -1707,7 +1715,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this._editorView!.dispatch(this._editorView!.state.tr.setSelection(NodeSelection.create(this._editorView!.state.doc, pcords.pos)));
}
}
- if (this.props.isSelected(true)) {
+ if (this.props.isSelected()) {
// if text box is selected, then it consumes all click events
(e.nativeEvent as any).handledByInnerReactInstance = true;
this.hitBulletTargets(e.clientX, e.clientY, !this._editorView?.state.selection.empty || this._forceUncollapse, false, this._forceDownNode, e.shiftKey);
@@ -1721,7 +1729,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
clearStyleSheetRules(FormattedTextBox._bulletStyleSheet);
const clickPos = this._editorView!.posAtCoords({ left: x, top: y });
let olistPos = clickPos?.pos;
- if (clickPos && olistPos && this.props.isSelected(true)) {
+ if (clickPos && olistPos && this.props.isSelected()) {
const clickNode = this._editorView?.state.doc.nodeAt(olistPos);
const nodeBef = this._editorView?.state.doc.nodeAt(Math.max(0, olistPos - 1));
olistPos = nodeBef?.type === this._editorView?.state.schema.nodes.ordered_list ? olistPos - 1 : olistPos;
@@ -1770,7 +1778,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
tr && this._editorView.dispatch(tr);
}
}
- if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected(true)) {
+ if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected()) {
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
}
FormattedTextBox._hadSelection = window.getSelection()?.toString() !== '';
@@ -1937,7 +1945,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const color = !annotated ? Colors.WHITE : Colors.BLACK;
const backgroundColor = !annotated ? (this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK) : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ':annotated' : ''));
- return !annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging()) ? null : (
+ return !annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? null : (
<div
className="formattedTextBox-sidebar-handle"
onPointerDown={this.sidebarDown}
@@ -2050,7 +2058,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
@computed get fieldKey() {
const usePath = StrCast(this.rootDoc[`${this.props.fieldKey}_usePath`]);
- return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering) ? `_${usePath.replace(':hover', '')}` : '');
+ return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering || this.props.isContentActive()) ? `_${usePath.replace(':hover', '')}` : '');
}
@observable _isHovering = false;
onPassiveWheel = (e: WheelEvent) => {
@@ -2067,7 +2075,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (this.props.isContentActive()) {
// prevent default if selected || child is active but this doc isn't scrollable
if (
- (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil(this.props.PanelHeight()) && //
+ (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil(this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1)) && //
(this.props.isSelected() || this.isAnyChildContentActive())
) {
e.preventDefault();
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index e3ac4fb9d..712be91c8 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -181,7 +181,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// finds font sizes and families in selection
getActiveAlignment() {
- if (this.view && this.TextView?.props.isSelected(true)) {
+ if (this.view && this.TextView?.props.isSelected()) {
const path = (this.view.state.selection.$from as any).path;
for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) {
if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) {
@@ -194,7 +194,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// finds font sizes and families in selection
getActiveListStyle() {
- if (this.view && this.TextView?.props.isSelected(true)) {
+ if (this.view && this.TextView?.props.isSelected()) {
const path = (this.view.state.selection.$from as any).path;
for (let i = 0; i < path.length; i += 3) {
if (path[i].type === this.view.state.schema.nodes.ordered_list) {
@@ -214,7 +214,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const activeSizes = new Set<string>();
const activeColors = new Set<string>();
const activeHighlights = new Set<string>();
- if (this.view && this.TextView?.props.isSelected(true)) {
+ if (this.view && this.TextView?.props.isSelected()) {
const state = this.view.state;
const pos = this.view.state.selection.$from;
const marks: Mark[] = [...(state.storedMarks ?? [])];
@@ -249,7 +249,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
//finds all active marks on selection in given group
getActiveMarksOnSelection() {
let activeMarks: MarkType[] = [];
- if (!this.view || !this.TextView?.props.isSelected(true)) return activeMarks;
+ if (!this.view || !this.TextView?.props.isSelected()) return activeMarks;
const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type);
@@ -286,7 +286,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
destroy() {
- !this.TextView?.props.isSelected(true) && this.fadeOut(true);
+ !this.TextView?.props.isSelected() && this.fadeOut(true);
}
@action
@@ -443,7 +443,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => {
- if (this.TextView?.props.isSelected(true)) {
+ if (this.TextView?.props.isSelected()) {
var tr = view.state.tr;
view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => {
if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) {
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 6f07588b3..4faef26e2 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -28,7 +28,6 @@ export const marks: { [index: string]: MarkSpec } = {
autoLinkAnchor: {
attrs: {
allAnchors: { default: [] as { href: string; title: string; anchorId: string }[] },
- location: { default: null },
title: { default: null },
},
inclusive: false,
@@ -37,7 +36,6 @@ export const marks: { [index: string]: MarkSpec } = {
tag: 'a[href]',
getAttrs(dom: any) {
return {
- location: dom.getAttribute('location'),
title: dom.getAttribute('title'),
};
},
@@ -46,7 +44,7 @@ export const marks: { [index: string]: MarkSpec } = {
toDOM(node: any) {
const targethrefs = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.href : item.href), '');
const anchorids = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.anchorId : item.anchorId), '');
- return ['a', { class: anchorids, 'data-targethrefs': targethrefs, 'data-noPreview': 'true', 'data-linkdoc': node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0];
+ return ['a', { class: anchorids, 'data-targethrefs': targethrefs, /*'data-noPreview': 'true', */ 'data-linkdoc': node.attrs.linkDoc, title: node.attrs.title, style: `background: lightBlue` }, 0];
},
},
noAutoLinkAnchor: {
@@ -73,7 +71,6 @@ export const marks: { [index: string]: MarkSpec } = {
linkAnchor: {
attrs: {
allAnchors: { default: [] as { href: string; title: string; anchorId: string }[] },
- location: { default: null },
title: { default: null },
noPreview: { default: false },
docref: { default: false }, // flags whether the linked text comes from a document within Dash. If so, an attribution label is appended after the text
@@ -84,7 +81,6 @@ export const marks: { [index: string]: MarkSpec } = {
tag: 'a[href]',
getAttrs(dom: any) {
return {
- location: dom.getAttribute('location'),
title: dom.getAttribute('title'),
noPreview: dom.getAttribute('noPreview'),
};
@@ -108,7 +104,7 @@ export const marks: { [index: string]: MarkSpec } = {
node.attrs.title,
],
]
- : ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline; cursor: default` }, 0];
+ : ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, style: `text-decoration: underline; cursor: default` }, 0];
},
},
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index f27fb18e2..4cd2cee52 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -212,7 +212,6 @@ export const nodes: { [index: string]: NodeSpec } = {
alt: { default: null },
title: { default: null },
float: { default: 'left' },
- location: { default: 'add:right' },
docId: { default: '' },
},
group: 'inline',
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 3d7c68bcd..789509784 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -27,7 +27,6 @@ import { CollectionDockingView } from '../../collections/CollectionDockingView';
import { CollectionFreeFormView, computeTimelineLayout, MarqueeViewBounds } from '../../collections/collectionFreeForm';
import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline';
import { CollectionView } from '../../collections/CollectionView';
-import { TabDocView } from '../../collections/TabDocView';
import { TreeView } from '../../collections/TreeView';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
@@ -204,16 +203,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
this._disposers.editing = reaction(
() => this.layoutDoc.presentation_status === PresStatus.Edit,
- editing => {
- if (editing) {
- this.childDocs.forEach(doc => {
- if (doc.presentation_indexed !== undefined) {
- this.progressivizedItems(doc)?.forEach(indexedDoc => (indexedDoc.opacity = undefined));
- doc.presentation_indexed = Math.min(this.progressivizedItems(doc)?.length ?? 0, 1);
- }
- });
- }
- }
+ editing => editing &&
+ this.childDocs.filter(doc => doc.presentation_indexed !== undefined).forEach(doc => {
+ this.progressivizedItems(doc)?.forEach(indexedDoc => (indexedDoc.opacity = undefined));
+ doc.presentation_indexed = Math.min(this.progressivizedItems(doc)?.length ?? 0, 1);
+ }) // prettier-ignore
);
}
@@ -283,19 +277,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
// go to documents chain
- runSubroutines = (childrenToRun: Doc[], normallyNextSlide: Doc) => {
- console.log(childrenToRun, normallyNextSlide, 'runSUBFUNC');
- if (childrenToRun === undefined) {
- console.log('children undefined');
- return;
+ runSubroutines = (childrenToRun: Opt<Doc[]>, normallyNextSlide: Doc) => {
+ if (childrenToRun && childrenToRun[0] !== normallyNextSlide) {
+ childrenToRun.forEach(child => DocumentManager.Instance.showDocument(child, {}));
}
- if (childrenToRun[0] === normallyNextSlide) {
- return;
- }
-
- childrenToRun.forEach(child => {
- DocumentManager.Instance.showDocument(child, {});
- });
};
// Called when the user activates 'next' - to move to the next part of the pres. trail
@@ -453,7 +438,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem.presentation_targetDoc).embedContainer) : DocCast(activeItem.presentation_targetDoc);
const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext;
if (context) {
- const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.CollectionFreeFormView;
if (ffview?.childDocs) {
PresBox.Instance._keyTimer = CollectionFreeFormView.gotoKeyframe(PresBox.Instance._keyTimer, ffview.childDocs, transTime);
ffview.rootDoc._currentFrame = NumCast(activeFrame);
@@ -626,7 +611,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
changed = true;
const computedScale = NumCast(activeItem.config_zoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height);
activeItem.presentation_movement === PresMovement.Zoom && (bestTarget._freeform_scale = computedScale);
- dv.ComponentView?.brushView?.(viewport, transTime);
+ dv.ComponentView?.brushView?.(viewport, transTime, 2500);
}
} else {
if (bestTarget._freeform_panX !== activeItem.config_panX || bestTarget._freeform_panY !== activeItem.config_panY || bestTarget._freeform_scale !== activeItem.config_viewScale) {
@@ -782,6 +767,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection);
};
+ public static PanelName = 'PRESBOX';
+
static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: () => void) {
if (activeItem.presentation_movement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
(DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
@@ -796,7 +783,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
zoomTime: activeItem.presentation_movement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime),
effect: activeItem,
noSelect: true,
- openLocation: OpenWhere.addLeft,
+ openLocation: targetDoc.type === DocumentType.PRES ? ((OpenWhere.replace + ':' + PresBox.PanelName) as OpenWhere) : OpenWhere.addLeft,
anchorDoc: activeItem,
easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
zoomTextSelections: BoolCast(activeItem.presentation_zoomText),
@@ -806,7 +793,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (activeItem.presentation_openInLightbox) {
const context = DocCast(targetDoc.annotationOn) ?? targetDoc;
if (!DocumentManager.Instance.getLightboxDocumentView(context)) {
- LightboxView.SetLightboxDoc(context);
+ LightboxView.Instance.SetLightboxDoc(context);
}
}
if (targetDoc) {
@@ -815,7 +802,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => {
// if target or the doc it annotates is not in the lightbox, then close the lightbox
if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(targetDoc.annotationOn) ?? targetDoc)) {
- LightboxView.SetLightboxDoc(undefined);
+ LightboxView.Instance.SetLightboxDoc(undefined);
}
DocumentManager.Instance.showDocument(targetDoc, options, finished);
});
@@ -909,7 +896,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
break;
}
});
- LightboxView.SetLightboxDoc(undefined);
+ LightboxView.Instance.SetLightboxDoc(undefined);
Doc.RemFromMyOverlay(this.rootDoc);
return PresStatus.Edit;
};
@@ -1082,7 +1069,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
audio.config_clipStart = NumCast(doc._timecodeToShow /* audioStart */, NumCast(doc._timecodeToShow /* videoStart */));
audio.config_clipEnd = NumCast(doc._timecodeToHide /* audioEnd */, NumCast(doc._timecodeToHide /* videoEnd */));
audio.presentation_duration = audio.config_clipStart - audio.config_clipEnd;
- TabDocView.PinDoc(audio, { audioRange: true });
+ this.props.pinToPres(audio, { audioRange: true });
setTimeout(() => this.removeDocument(doc), 0);
return false;
}
@@ -1653,8 +1640,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
let dataField = Doc.LayoutFieldKey(tagDoc);
if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '_annotations';
- if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc.annotationOn["${dataField}"]`);
- else activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc["${dataField}"]`);
+ if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc.annotationOn?.["${dataField}"]`);
+ else activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc?.["${dataField}"]`);
}}
checked={Cast(activeItem.presentation_indexed, 'number', null) !== undefined ? true : false}
/>
@@ -2237,7 +2224,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const config_data = Cast(this.rootDoc.data, listSpec(Doc));
if (data && config_data) {
data.push(doc);
- TabDocView.PinDoc(doc, {});
+ this.props.pinToPres(doc, {});
this.gotoDocument(this.childDocs.length, this.activeItem);
} else {
this.props.addDocTab(doc, OpenWhere.addRight);
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 82ed9e8d5..c667f126f 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -3,7 +3,6 @@ import { Tooltip } from '@material-ui/core';
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
-import { Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
@@ -15,6 +14,7 @@ import { DragManager } from '../../../util/DragManager';
import { SettingsManager } from '../../../util/SettingsManager';
import { Transform } from '../../../util/Transform';
import { undoable, undoBatch } from '../../../util/UndoManager';
+import { TreeView } from '../../collections/TreeView';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { EditableView } from '../../EditableView';
import { Colors } from '../../global/globalEnums';
@@ -25,9 +25,6 @@ import { PresBox } from './PresBox';
import './PresElementBox.scss';
import { PresMovement } from './PresEnums';
import React = require('react');
-import { TreeView } from '../../collections/TreeView';
-import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
-import { MultiToggle, Type } from 'browndash-components';
/**
* This class models the view a document added to presentation will have in the presentation.
* It involves some functionality for its buttons and options.
@@ -331,7 +328,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
@computed get recordingIsInOverlay() {
- return DocListCast(Doc.MyOverlayDocs.data).some(doc => doc.slides === this.rootDoc);
+ return Doc.MyOverlayDocs.some(doc => doc.slides === this.rootDoc);
}
// a previously recorded video will have timecode defined
@@ -341,14 +338,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
removeAllRecordingInOverlay = () => {
- DocListCast(Doc.MyOverlayDocs.data)
- .filter(doc => doc.slides === this.rootDoc)
- .forEach(Doc.RemFromMyOverlay);
+ Doc.MyOverlayDocs.filter(doc => doc.slides === this.rootDoc).forEach(Doc.RemFromMyOverlay);
};
static removeEveryExistingRecordingInOverlay = () => {
// Remove every recording that already exists in overlay view
- DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
+ Doc.MyOverlayDocs.forEach(doc => {
if (doc.slides !== null) {
// if it's a recording video, don't remove from overlay (user can lose data)
if (PresElementBox.videoIsRecorded(DocCast(doc.slides))) {
@@ -407,8 +402,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
activeItem.recording = recording;
// make recording box appear in the bottom right corner of the screen
- recording.overlayX = window.innerWidth - recording[Width]() - 20;
- recording.overlayY = window.innerHeight - recording[Height]() - 20;
+ recording.overlayX = window.innerWidth - NumCast(recording._width) - 20;
+ recording.overlayY = window.innerHeight - NumCast(recording._height) - 20;
Doc.AddToMyOverlay(recording);
}
};
@@ -551,7 +546,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
return (
<div
- className={`presItem-container`}
+ className='presItem-container'
key={this.props.Document[Id] + this.indexInPres}
ref={this._itemRef}
style={{