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/AudioBox.scss10
-rw-r--r--src/client/views/nodes/AudioBox.tsx91
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx115
-rw-r--r--src/client/views/nodes/ColorBox.scss22
-rw-r--r--src/client/views/nodes/ColorBox.tsx89
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx52
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx26
-rw-r--r--src/client/views/nodes/DataVizBox/components/Chart.scss2
-rw-r--r--src/client/views/nodes/DataVizBox/components/Histogram.tsx105
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx70
-rw-r--r--src/client/views/nodes/DataVizBox/components/PieChart.tsx93
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx86
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx102
-rw-r--r--src/client/views/nodes/DocumentIcon.tsx38
-rw-r--r--src/client/views/nodes/DocumentLinksButton.scss2
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx107
-rw-r--r--src/client/views/nodes/DocumentView.scss2
-rw-r--r--src/client/views/nodes/DocumentView.tsx513
-rw-r--r--src/client/views/nodes/EquationBox.scss2
-rw-r--r--src/client/views/nodes/EquationBox.tsx28
-rw-r--r--src/client/views/nodes/FaceRectangle.tsx19
-rw-r--r--src/client/views/nodes/FaceRectangles.tsx22
-rw-r--r--src/client/views/nodes/FieldView.tsx3
-rw-r--r--src/client/views/nodes/FontIconBox/ButtonInterface.ts10
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.scss2
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx58
-rw-r--r--src/client/views/nodes/FunctionPlotBox.tsx38
-rw-r--r--src/client/views/nodes/ImageBox.tsx235
-rw-r--r--src/client/views/nodes/KeyValueBox.scss54
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx42
-rw-r--r--src/client/views/nodes/KeyValuePair.scss3
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx48
-rw-r--r--src/client/views/nodes/LabelBox.tsx50
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx44
-rw-r--r--src/client/views/nodes/LinkBox.tsx51
-rw-r--r--src/client/views/nodes/LinkDescriptionPopup.scss8
-rw-r--r--src/client/views/nodes/LinkDescriptionPopup.tsx2
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx102
-rw-r--r--src/client/views/nodes/LoadingBox.tsx31
-rw-r--r--src/client/views/nodes/MapBox/AnimationUtility.ts508
-rw-r--r--src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx69
-rw-r--r--src/client/views/nodes/MapBox/MapAnchorMenu.tsx340
-rw-r--r--src/client/views/nodes/MapBox/MapBox.scss2
-rw-r--r--src/client/views/nodes/MapBox/MapBox2.tsx1194
-rw-r--r--src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx185
-rw-r--r--src/client/views/nodes/MapBox/MapPushpinBox.tsx14
-rw-r--r--src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx66
-rw-r--r--src/client/views/nodes/PDFBox.scss16
-rw-r--r--src/client/views/nodes/PDFBox.tsx120
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx9
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx2
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx49
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx5
-rw-r--r--src/client/views/nodes/RadialMenu.scss14
-rw-r--r--src/client/views/nodes/RadialMenu.tsx86
-rw-r--r--src/client/views/nodes/RadialMenuItem.tsx71
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx39
-rw-r--r--src/client/views/nodes/ScriptingBox.scss15
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx61
-rw-r--r--src/client/views/nodes/TaskCompletedBox.tsx35
-rw-r--r--src/client/views/nodes/VideoBox.scss8
-rw-r--r--src/client/views/nodes/VideoBox.tsx148
-rw-r--r--src/client/views/nodes/WebBox.scss2
-rw-r--r--src/client/views/nodes/WebBox.tsx157
-rw-r--r--src/client/views/nodes/audio/AudioWaveform.scss17
-rw-r--r--src/client/views/nodes/audio/AudioWaveform.tsx122
-rw-r--r--src/client/views/nodes/audio/WaveCanvas.tsx100
-rw-r--r--src/client/views/nodes/formattedText/DashDocCommentView.tsx4
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx5
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.scss2
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx59
-rw-r--r--src/client/views/nodes/formattedText/EquationEditor.scss468
-rw-r--r--src/client/views/nodes/formattedText/EquationEditor.tsx87
-rw-r--r--src/client/views/nodes/formattedText/EquationView.tsx9
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss4
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx364
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx6
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts4
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.scss11
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx66
-rw-r--r--src/client/views/nodes/formattedText/SummaryView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/TooltipTextMenu.scss102
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts2
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts2
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFill.tsx8
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx2
-rw-r--r--src/client/views/nodes/importBox/ImportElementBox.tsx3
-rw-r--r--src/client/views/nodes/trails/PresBox.scss2
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx76
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx61
90 files changed, 3804 insertions, 3376 deletions
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index d40537776..4337401e3 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -1,4 +1,4 @@
-@import "../global/globalCssVariables.scss";
+@import '../global/globalCssVariables.module.scss';
.audiobox-container {
width: 100%;
@@ -116,18 +116,18 @@
width: 10px;
}
- input[type="range"] {
+ input[type='range'] {
width: 50px;
-webkit-appearance: none;
background: none;
margin: 5px;
}
- input[type="range"]:focus {
+ input[type='range']:focus {
outline: none;
}
- input[type="range"]::-webkit-slider-runnable-track {
+ input[type='range']::-webkit-slider-runnable-track {
width: 100%;
height: 6px;
cursor: pointer;
@@ -136,7 +136,7 @@
border-radius: 3px;
}
- input[type="range"]::-webkit-slider-thumb {
+ input[type='range']::-webkit-slider-thumb {
box-shadow: 0;
border: 0;
height: 10px;
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 807a77233..567cf193e 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -1,8 +1,8 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, runInAction } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, computed, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { DateField } from '../../../fields/DateField';
import { Doc } from '../../../fields/Doc';
import { ComputedField } from '../../../fields/ScriptField';
@@ -17,7 +17,7 @@ import { undoBatch } from '../../util/UndoManager';
import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
import './AudioBox.scss';
import { DocFocusOptions } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
@@ -49,12 +49,18 @@ export enum media_state {
}
@observer
-export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(AudioBox, fieldKey);
}
+
public static Enabled = false;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
static topControlsHeight = 30; // height of upper controls above timeline
static bottomControlsHeight = 20; // height of lower controls below timeline
@@ -68,7 +74,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
_stream: MediaStream | undefined; // passed to MediaRecorder, records device input audio
_play: any = null; // timeout for playback
- @observable _stackedTimeline: CollectionStackedTimeline | null | undefined; // CollectionStackedTimeline ref
+ @observable _stackedTimeline: CollectionStackedTimeline | null | undefined = undefined; // CollectionStackedTimeline ref
@observable _finished: boolean = false; // has playback reached end of clip
@observable _volume: number = 1;
@observable _muted: boolean = false;
@@ -84,7 +90,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// if you get rid of it and set the value to 0 the timeline and waveform will set their bounds incorrectly
@computed get miniPlayer() {
- return this.props.PanelHeight() < 50;
+ return this._props.PanelHeight() < 50;
} // used to collapse timeline when node is shrunk
@computed get links() {
return LinkManager.Links(this.dataDoc);
@@ -94,7 +100,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
@computed get path() {
// returns the path of the audio file
- const path = Cast(this.props.Document[this.fieldKey], AudioField, null)?.url.href || '';
+ const path = Cast(this.Document[this.fieldKey], AudioField, null)?.url.href || '';
return path === nullAudio ? '' : path;
}
set mediaState(value) {
@@ -115,7 +121,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
if (this.path) {
this.mediaState = media_state.Paused;
this.setPlayheadTime(NumCast(this.layoutDoc.clipStart));
@@ -139,17 +145,17 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const timecode = Cast(this.layoutDoc._layout_currentTimecode, 'number', null);
const anchor = addAsAnnotation
? CollectionStackedTimeline.createAnchor(
- this.rootDoc,
+ this.Document,
this.dataDoc,
this.annotationKey,
- this._ele?.currentTime || Cast(this.props.Document._layout_currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
+ this._ele?.currentTime || Cast(this.Document._layout_currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
undefined,
undefined,
addAsAnnotation
- ) || this.rootDoc
- : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.rootDoc });
+ ) || this.Document
+ : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.Document });
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.Document);
return anchor;
};
@@ -186,13 +192,16 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._ele.play();
this.mediaState = media_state.Playing;
this.addCurrentlyPlaying();
- this._play = setTimeout(() => {
- // need to keep track of if end of clip is reached so on next play, clip restarts
- if (fullPlay) this._finished = true;
- // removes from currently playing if playback has reached end of range marker
- else this.removeCurrentlyPlaying();
- this.Pause();
- }, (end - start) * 1000);
+ this._play = setTimeout(
+ () => {
+ // need to keep track of if end of clip is reached so on next play, clip restarts
+ if (fullPlay) this._finished = true;
+ // removes from currently playing if playback has reached end of range marker
+ else this.removeCurrentlyPlaying();
+ this.Pause();
+ },
+ (end - start) * 1000
+ );
} else {
this.Pause();
}
@@ -202,7 +211,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// removes from currently playing display
@action
removeCurrentlyPlaying = () => {
- const docView = this.props.DocumentView?.();
+ const docView = this._props.DocumentView?.();
if (CollectionStackedTimeline.CurrentlyPlaying && docView) {
const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView);
index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
@@ -212,7 +221,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// adds doc to currently playing display
@action
addCurrentlyPlaying = () => {
- const docView = this.props.DocumentView?.();
+ const docView = this._props.DocumentView?.();
if (!CollectionStackedTimeline.CurrentlyPlaying) {
CollectionStackedTimeline.CurrentlyPlaying = [];
}
@@ -240,7 +249,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._recorder.ondataavailable = async (e: any) => {
const [{ result }] = await Networking.UploadFilesToServer({ file: e.data });
if (!(result instanceof Error)) {
- this.props.Document[this.fieldKey] = new AudioField(result.accessPaths.agnostic.client);
+ this.Document[this.fieldKey] = new AudioField(result.accessPaths.agnostic.client);
}
};
this._recordStart = new Date().getTime();
@@ -363,17 +372,17 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
returnFalse,
returnFalse,
action(() => {
- const newDoc = DocUtils.GetNewTextDoc('', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
+ const newDoc = DocUtils.GetNewTextDoc('', NumCast(this.Document.x), NumCast(this.Document.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
const textField = Doc.LayoutFieldKey(newDoc);
Doc.GetProto(newDoc)[`${textField}_recordingSource`] = this.dataDoc;
Doc.GetProto(newDoc)[`${textField}_recordingStart`] = ComputedField.MakeFunction(`this.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction(`this.${textField}_recordingSource.mediaState`);
- if (Doc.IsInMyOverlay(this.rootDoc)) {
- newDoc.overlayX = this.rootDoc.x;
- newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
+ if (Doc.IsInMyOverlay(this.Document)) {
+ newDoc.overlayX = this.Document.x;
+ newDoc.overlayY = NumCast(this.Document.y) + NumCast(this.layoutDoc._height);
Doc.AddToMyOverlay(newDoc);
} else {
- this.props.addDocument?.(newDoc);
+ this._props.addDocument?.(newDoc);
}
}),
false
@@ -424,7 +433,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// plays link
playLink = (link: Doc, options: DocFocusOptions) => {
- if (link.annotationOn === this.rootDoc) {
+ if (link.annotationOn === this.Document) {
if (!this.layoutDoc.dontAutoPlayFollowedLinks) {
this.playFrom(this.timeline?.anchorStart(link) || 0, this.timeline?.anchorEnd(link));
} else {
@@ -449,9 +458,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
};
@action
- timelineWhenChildContentsActiveChanged = (isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive));
+ timelineWhenChildContentsActiveChanged = (isActive: boolean) => this._props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive));
- timelineScreenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -AudioBox.topControlsHeight);
+ timelineScreenToLocal = () => this._props.ScreenToLocalTransform().translate(0, -AudioBox.topControlsHeight);
setPlayheadTime = (time: number) => (this._ele!.currentTime /*= this.layoutDoc._layout_currentTimecode*/ = time);
@@ -460,8 +469,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
isActiveChild = () => this._isAnyChildContentActive;
// timeline dimensions
- timelineWidth = () => this.props.PanelWidth();
- timelineHeight = () => this.props.PanelHeight() - (AudioBox.topControlsHeight + AudioBox.bottomControlsHeight);
+ timelineWidth = () => this._props.PanelWidth();
+ timelineHeight = () => this._props.PanelHeight() - (AudioBox.topControlsHeight + AudioBox.bottomControlsHeight);
// ends trim, hides trim controls and displays new clip
@undoBatch
@@ -555,7 +564,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._dropDisposer = DragManager.MakeDropTarget(
r,
(e, de) => {
- const [xp, yp] = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+ const [xp, yp] = this._props.ScreenToLocalTransform().transformPoint(de.x, de.y);
de.complete.docDragData && this.timeline?.internalDocDrop(e, de, de.complete.docDragData, xp);
},
this.layoutDoc,
@@ -597,7 +606,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div
className="audiobox-file"
style={{
- pointerEvents: this._isAnyChildContentActive || this.props.isContentActive() ? 'all' : 'none',
+ pointerEvents: this._isAnyChildContentActive || this._props.isContentActive() ? 'all' : 'none',
flexDirection: this.miniPlayer ? 'row' : 'column',
justifyContent: this.miniPlayer ? 'flex-start' : 'space-between',
}}>
@@ -670,7 +679,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div className="audiobox-timecodes">
<div className="timecode-current">{this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._layout_currentTimecode) - NumCast(this.timeline.clipStart)))}</div>
{this.miniPlayer ? (
- <div>/</div>
+ <div />
) : (
<div className="bottom-controls-middle">
<FontAwesomeIcon icon="search-plus" />
@@ -699,13 +708,13 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return (
<CollectionStackedTimeline
ref={action((r: CollectionStackedTimeline | null) => (this._stackedTimeline = r))}
- {...this.props}
+ {...this._props}
CollectionFreeFormDocumentView={undefined}
dataFieldKey={this.fieldKey}
fieldKey={this.annotationKey}
dictationKey={this.fieldKey + '_dictation'}
mediaPath={this.path}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
startTag={'_timecodeToShow' /* audioStart */}
endTag={'_timecodeToHide' /* audioEnd */}
bringToFront={emptyFunction}
@@ -719,7 +728,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
ScreenToLocalTransform={this.timelineScreenToLocal}
Play={this.Play}
Pause={this.Pause}
- isContentActive={this.props.isContentActive}
+ isContentActive={this._props.isContentActive}
isAnyChildContentActive={this.isAnyChildContentActive}
playLink={this.playLink}
PanelWidth={this.timelineWidth}
@@ -734,7 +743,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return (
<audio
ref={this.setRef}
- className={`audiobox-control${this.props.isContentActive() ? '-interactive' : ''}`}
+ className={`audiobox-control${this._props.isContentActive() ? '-interactive' : ''}`}
onLoadedData={action(e => this._ele?.duration && this._ele?.duration !== Infinity && (this.dataDoc[this.fieldKey + '_duration'] = this._ele.duration))}>
<source src={this.path} type="audio/mpeg" />
Not supported.
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 5b4fb3e29..548734dab 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,21 +1,21 @@
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
+import { OmitKeys, numberRange } from '../../../Utils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { numberRange, OmitKeys } from '../../../Utils';
import { DocumentManager } from '../../util/DocumentManager';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from '../../util/SelectionManager';
-import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { DocComponent } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import './CollectionFreeFormDocumentView.scss';
import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
-import React = require('react');
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentViewProps {
x: number;
@@ -36,7 +36,11 @@ export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentView
CollectionFreeFormView: CollectionFreeFormView;
}
@observer
-export class CollectionFreeFormDocumentViewWrapper extends DocComponent<CollectionFreeFormDocumentViewWrapperProps>() implements CollectionFreeFormDocumentViewProps {
+export class CollectionFreeFormDocumentViewWrapper extends DocComponent<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>() implements CollectionFreeFormDocumentViewProps {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
@observable X = this.props.x;
@observable Y = this.props.y;
@observable Z = this.props.z;
@@ -48,6 +52,7 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti
@observable Highlight = this.props.highlight;
@observable Width = this.props.width;
@observable Height = this.props.height;
+ @observable AutoDim = this.props.autoDim;
@observable Transition = this.props.transition;
@observable DataTransition = this.props.dataTransition;
CollectionFreeFormView = this.props.CollectionFreeFormView; // needed for type checking
@@ -63,28 +68,30 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti
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_ZIndex = () => this.ZIndex ?? NumCast(this.Document.zIndex); // prettier-ignore
+ w_Rotation = () => this.Rotation ?? NumCast(this.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_BackgroundColor = () => this.BackgroundColor ?? Cast(this.Document._backgroundColor, 'string', null); // prettier-ignore
+ w_Color = () => this.Color ?? Cast(this.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_AutoDim = () => this.AutoDim; // prettier-ignore
w_Transition = () => this.Transition; // prettier-ignore
w_DataTransition = () => this.DataTransition; // prettier-ignore
- PanelWidth = () => this.props.autoDim ? this.props.PanelWidth?.() : this.Width; // prettier-ignore
- PanelHeight = () => this.props.autoDim ? this.props.PanelHeight?.() : this.Height; // prettier-ignore
- @action
- componentDidUpdate() {
- this.WrapperKeys.forEach(keys => ((this as any)[keys.upper] = (this.props as any)[keys.lower]));
+ PanelWidth = () => this._props.autoDim ? this._props.PanelWidth?.() : this.Width; // prettier-ignore
+ PanelHeight = () => this._props.autoDim ? this._props.PanelHeight?.() : this.Height; // prettier-ignore
+
+ componentDidUpdate(prevProps: Readonly<React.PropsWithChildren<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>>) {
+ super.componentDidUpdate(prevProps);
+ this.WrapperKeys.forEach(action(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
+ {...OmitKeys(this._props, this.WrapperKeys.map(keys => keys.lower) ).omit} // prettier-ignore
{...layoutProps}
PanelWidth={this.PanelWidth}
PanelHeight={this.PanelHeight}
@@ -114,6 +121,10 @@ export interface CollectionFreeFormDocumentViewProps {
@observer
export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps & DocumentViewProps>() {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
get displayName() { // this makes mobx trace() statements more descriptive
return 'CollectionFreeFormDocumentView(' + this.Document.title + ')';
} // prettier-ignore
@@ -134,32 +145,38 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
get CollectionFreeFormView() {
- return this.props.CollectionFreeFormView;
+ return this._props.CollectionFreeFormView;
}
styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
if (doc === this.layoutDoc) {
switch (property) {
- 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();
+ 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);
+ return this._props.styleProvider?.(doc, props, property);
};
public static getValues(doc: Doc, time: number, fillIn: boolean = true) {
- return CollectionFreeFormDocumentView.animFields.reduce((p, val) => {
- p[val.key] = Cast(doc[`${val.key}_indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number);
- return p;
- }, {} as { [val: string]: Opt<number> });
+ return CollectionFreeFormDocumentView.animFields.reduce(
+ (p, val) => {
+ p[val.key] = Cast(doc[`${val.key}_indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number);
+ return p;
+ },
+ {} as { [val: string]: Opt<number> }
+ );
}
public static getStringValues(doc: Doc, time: number) {
- return CollectionFreeFormDocumentView.animStringFields.reduce((p, val) => {
- p[val] = Cast(doc[`${val}_indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as string);
- return p;
- }, {} as { [val: string]: Opt<string> });
+ return CollectionFreeFormDocumentView.animStringFields.reduce(
+ (p, val) => {
+ p[val] = Cast(doc[`${val}_indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as string);
+ return p;
+ },
+ {} as { [val: string]: Opt<string> }
+ );
}
public static setStringValues(time: number, d: Doc, vals: { [val: string]: Opt<string> }) {
@@ -211,7 +228,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@action public float = () => {
const topDoc = this.Document;
- const containerDocView = this.props.docViewPath().lastElement();
+ const containerDocView = this._props.docViewPath().lastElement();
const screenXf = containerDocView?.screenToLocalTransform();
if (screenXf) {
SelectionManager.DeselectAll();
@@ -220,8 +237,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
topDoc.z = 0;
topDoc.x = spt[0];
topDoc.y = spt[1];
- this.props.removeDocument?.(topDoc);
- this.props.addDocTab(topDoc, OpenWhere.inParentFromScreen);
+ this._props.removeDocument?.(topDoc);
+ this._props.addDocTab(topDoc, OpenWhere.inParentFromScreen);
} else {
const spt = this.screenToLocalTransform().inverse().transformPoint(0, 0);
const fpt = screenXf.transformPoint(spt[0], spt[1]);
@@ -234,15 +251,15 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
};
nudge = (x: number, y: number) => {
- 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;
+ 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;
};
screenToLocalTransform = () =>
- this.props
+ this._props
.ScreenToLocalTransform()
- .translate(-this.props.w_X(), -this.props.w_Y())
- .rotateDeg(-(this.props.w_Rotation?.() || 0));
+ .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
@@ -252,26 +269,26 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
// undefined - this is not activated by a group
isGroupActive = () => {
if (this.CollectionFreeFormView.isAnyChildContentActive()) return undefined;
- const isGroup = this.Document.isGroup && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
- return isGroup ? (this.props.isDocumentActive?.() ? 'group' : this.props.isGroupActive?.() ? 'child' : 'inactive') : this.props.isGroupActive?.() ? 'child' : undefined;
+ const isGroup = this.dataDoc.isGroup && (!this.layoutDoc.backgroundColor || this.layoutDoc.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 passOnProps = OmitKeys(this.props, Object.keys(this.props).filter(key => key.startsWith('w_'))).omit; // prettier-ignore
+ const passOnProps = OmitKeys(this._props, Object.keys(this._props).filter(key => key.startsWith('w_'))).omit; // prettier-ignore
return (
<div
className={CollectionFreeFormDocumentView.CollectionFreeFormDocViewClassName}
style={{
- 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',
+ 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.props.PanelWidth(), height: this.props.PanelHeight(), background: 'lightGreen' }} />
+ {this._props.RenderCutoffProvider(this._props.Document) ? (
+ <div style={{ position: 'absolute', width: this._props.PanelWidth(), height: this._props.PanelHeight(), background: 'lightGreen' }} />
) : (
<DocumentView {...passOnProps} CollectionFreeFormDocumentView={this.returnThis} styleProvider={this.styleProvider} ScreenToLocalTransform={this.screenToLocalTransform} isGroupActive={this.isGroupActive} />
)}
diff --git a/src/client/views/nodes/ColorBox.scss b/src/client/views/nodes/ColorBox.scss
deleted file mode 100644
index d5f2a7ec7..000000000
--- a/src/client/views/nodes/ColorBox.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-.colorBox-container, .colorBox-container-interactive {
- width:100%;
- height:100%;
- position: relative;
- pointer-events: none;
- transform-origin: top left;
-
- .sketch-picker {
- div {
- cursor: crosshair;
- }
- .flexbox-fix {
- cursor: pointer;
- div {
- cursor:pointer;
- }
- }
- }
-}
-.colorBox-container-interactive {
- pointer-events:all;
-} \ No newline at end of file
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
deleted file mode 100644
index 4c7ea4002..000000000
--- a/src/client/views/nodes/ColorBox.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-import React = require('react');
-import { action } from 'mobx';
-import { observer } from 'mobx-react';
-import { ColorState, SketchPicker } from 'react-color';
-import { Doc } from '../../../fields/Doc';
-import { Height } from '../../../fields/DocSymbols';
-import { InkTool } from '../../../fields/InkField';
-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';
-import { ActiveInkColor, ActiveInkWidth, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke';
-import './ColorBox.scss';
-import { FieldView, FieldViewProps } from './FieldView';
-import { RichTextMenu } from './formattedText/RichTextMenu';
-
-@observer
-export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(ColorBox, fieldKey);
- }
-
- @undoBatch
- @action
- static switchColor(color: ColorState) {
- SetActiveInkColor(color.hex);
-
- SelectionManager.Views().map(view => {
- const targetDoc =
- view.props.Document.dragFactory instanceof Doc
- ? view.props.Document.dragFactory
- : view.props.Document.layout instanceof Doc
- ? view.props.Document.layout
- : view.props.Document.isTemplateForField
- ? view.props.Document
- : Doc.GetProto(view.props.Document);
- if (targetDoc) {
- if (view.props.LayoutTemplate?.() || view.props.LayoutTemplateString) {
- // this situation typically occurs when you have a link dot
- targetDoc.backgroundColor = color.hex; // bcz: don't know how to change the color of an inline template...
- } else if (RichTextMenu.Instance?.TextViewFieldKey && window.getSelection()?.toString() !== '') {
- Doc.Layout(view.props.Document)[RichTextMenu.Instance.TextViewFieldKey + '-color'] = color.hex;
- } else {
- Doc.Layout(view.props.Document)._backgroundColor = color.hex + (color.rgb.a ? Math.round(color.rgb.a * 255).toString(16) : ''); // '_backgroundColor' is template specific. 'backgroundColor' would apply to all templates, but has no UI at the moment
- }
- }
- });
- }
-
- render() {
- const scaling = Math.min(this.layoutDoc.layout_fitWidth ? 10000 : this.props.PanelHeight() / NumCast(this.Document._height), this.props.PanelWidth() / NumCast(this.Document._width));
- return (
- <div
- className={`colorBox-container${this.props.isContentActive() ? '-interactive' : ''}`}
- onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
- onClick={e => e.stopPropagation()}
- style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }}>
- <SketchPicker
- onChange={c => Doc.ActiveTool === InkTool.None && ColorBox.switchColor(c)}
- color={StrCast(SelectionManager.Views()?.[0]?.rootDoc?._backgroundColor, ActiveInkColor())}
- presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
- />
-
- <div style={{ width: this.props.PanelWidth() / scaling, display: 'flex', paddingTop: '10px' }}>
- <div> {ActiveInkWidth()}</div>
- <input
- type="range"
- defaultValue={ActiveInkWidth()}
- min={1}
- max={100}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
- SetActiveInkWidth(e.target.value);
- SelectionManager.Views()
- .filter(i => StrCast(i.rootDoc.type) === DocumentType.INK)
- .map(i => (i.rootDoc.stroke_width = Number(e.target.value)));
- }}
- />
- </div>
- </div>
- );
- }
-}
-
-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 ff394e5f5..de382fca5 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -1,10 +1,11 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
+import { DocUtils, Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
@@ -13,7 +14,6 @@ import './ComparisonBox.scss';
import { DocumentView, DocumentViewProps } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { PinProps, PresBox } from './trails';
-import React = require('react');
@observer
export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -21,6 +21,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
return FieldView.LayoutString(ComparisonBox, fieldKey);
}
private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined];
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
@observable _animating = '';
@@ -28,10 +32,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
return NumCast(this.layoutDoc[this.clipWidthKey], 50);
}
get clipWidthKey() {
- return '_' + this.props.fieldKey + '_clipWidth';
+ return '_' + this._props.fieldKey + '_clipWidth';
}
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
}
protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
this._disposers[disposerId]?.();
@@ -44,7 +48,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
private internalDrop = (e: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => {
if (dropEvent.complete.docDragData) {
const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments;
- const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocs, this.rootDoc, (doc: Doc | Doc[]) => this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey));
+ const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocs, this.Document, (doc: Doc | Doc[]) => this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey));
Doc.SetContainer(droppedDocs.lastElement(), this.dataDoc);
!added && e.preventDefault();
e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
@@ -72,7 +76,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
if (this._isAnyChildContentActive) return;
this._animating = 'all 200ms';
// on click, animate slider movement to the targetWidth
- this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this.props.PanelWidth();
+ this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this._props.PanelWidth();
setTimeout(
action(() => (this._animating = '')),
200
@@ -84,27 +88,27 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
@action
private onPointerMove = ({ movementX }: PointerEvent) => {
- const width = movementX * this.props.ScreenToLocalTransform().Scale + (this.clipWidth / 100) * this.props.PanelWidth();
- if (width && width > 5 && width < this.props.PanelWidth()) {
- this.layoutDoc[this.clipWidthKey] = (width * 100) / this.props.PanelWidth();
+ const width = movementX * this._props.ScreenToLocalTransform().Scale + (this.clipWidth / 100) * this._props.PanelWidth();
+ if (width && width > 5 && width < this._props.PanelWidth()) {
+ this.layoutDoc[this.clipWidthKey] = (width * 100) / this._props.PanelWidth();
}
return false;
};
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
const anchor = Docs.Create.ConfigDocument({
- title: 'CompareAnchor:' + this.rootDoc.title,
+ title: 'CompareAnchor:' + this.Document.title,
// set presentation timing properties for restoring view
presentation_transition: 1000,
- annotationOn: this.rootDoc,
+ annotationOn: this.Document,
});
if (anchor) {
if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
/* addAsAnnotation &&*/ this.addDocument(anchor);
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.rootDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.Document);
return anchor;
}
- return this.rootDoc;
+ return this.Document;
};
@undoBatch
@@ -146,7 +150,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
};
docStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
if (property === StyleProp.PointerEvents) return 'none';
- return this.props.styleProvider?.(doc, props, property);
+ return this._props.styleProvider?.(doc, props, property);
};
moveDoc1 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_1'), true);
moveDoc2 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true);
@@ -171,9 +175,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
return targetDoc ? (
<>
<DocumentView
- {...this.props}
+ {...this._props}
Document={targetDoc}
- DataDoc={undefined}
+ TemplateDataDocument={undefined}
moveDocument={which.endsWith('1') ? this.moveDoc1 : this.moveDoc2}
removeDocument={which.endsWith('1') ? this.remDoc1 : this.remDoc2}
NativeWidth={returnZero}
@@ -181,7 +185,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
isContentActive={emptyFunction}
isDocumentActive={returnFalse}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- styleProvider={this._isAnyChildContentActive ? this.props.styleProvider : this.docStyleProvider}
+ styleProvider={this._isAnyChildContentActive ? this._props.styleProvider : this.docStyleProvider}
hideLinkButton={true}
pointerEvents={this._isAnyChildContentActive ? undefined : returnNone}
/>
@@ -195,15 +199,15 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
};
const displayBox = (which: string, index: number, cover: number) => {
return (
- <div className={`${index === 0 ? 'before' : 'after'}Box-cont`} key={which} style={{ width: this.props.PanelWidth() }} onPointerDown={e => this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}>
+ <div className={`${index === 0 ? 'before' : 'after'}Box-cont`} key={which} style={{ width: this._props.PanelWidth() }} onPointerDown={e => this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}>
{displayDoc(which)}
</div>
);
};
return (
- <div className={`comparisonBox${this.props.isContentActive() ? '-interactive' : ''}` /* change className to easily disable/enable pointer events in CSS */}>
- {displayBox(`${this.fieldKey}_2`, 1, this.props.PanelWidth() - 3)}
+ <div className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}` /* change className to easily disable/enable pointer events in CSS */}>
+ {displayBox(`${this.fieldKey}_2`, 1, this._props.PanelWidth() - 3)}
<div className="clip-div" style={{ width: this.clipWidth + '%', transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, 'gray') }}>
{displayBox(`${this.fieldKey}_1`, 0, 0)}
</div>
@@ -212,9 +216,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
className="slide-bar"
style={{
left: `calc(${this.clipWidth + '%'} - 0.5px)`,
- cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined,
+ cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this._props.PanelWidth() - 5) / this._props.PanelWidth() ? 'w-resize' : undefined,
}}
- onPointerDown={e => !this._isAnyChildContentActive && this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
+ onPointerDown={e => !this._isAnyChildContentActive && this.registerSliding(e, this._props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
>
<div className="slide-handle" />
</div>
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 1cb0a50f3..e31c04c40 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -35,7 +35,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// all CSV records in the dataset (that aren't an empty row)
@computed.struct get records() {
- var records = DataVizBox.dataset.get(CsvCast(this.rootDoc[this.fieldKey]).url.href);
+ var records = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href);
return records?.filter(record => Object.keys(record).some(key => record[key])) ?? [];
}
@@ -75,7 +75,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
getAnchor = (addAsAnnotation?: boolean, pinProps?: PinProps) => {
const anchor = !pinProps
- ? this.rootDoc
+ ? this.Document
: this._vizRenderer?.getAnchor(pinProps) ??
Docs.Create.ConfigDocument({
// when we clear selection -> we should have it so chartBox getAnchor returns undefined
@@ -98,31 +98,31 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
componentDidMount() {
- this.props.setContentView?.(this);
- if (!DataVizBox.dataset.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) this.fetchData();
+ this._props.setContentView?.(this);
+ if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey]).url.href)) this.fetchData();
}
fetchData = () => {
- DataVizBox.dataset.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests
+ DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests
fetch('/csvData?uri=' + this.dataUrl?.url.href) //
- .then(res => res.json().then(action(res => !res.errno && DataVizBox.dataset.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res))));
+ .then(res => res.json().then(action(res => !res.errno && DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, res))));
};
// toggles for user to decide which chart type to view the data in
@computed get renderVizView() {
- const scale = this.props.NativeDimScaling?.() || 1;
+ const scale = this._props.NativeDimScaling?.() || 1;
const sharedProps = {
- rootDoc: this.rootDoc,
+ Document: this.Document,
layoutDoc: this.layoutDoc,
records: this.records,
axes: this.axes,
- height: (this.props.PanelHeight() / scale - 32) /* height of 'change view' button */ * 0.9,
- width: (this.props.PanelWidth() / scale) * 0.9,
+ height: (this._props.PanelHeight() / scale - 32) /* height of 'change view' button */ * 0.9,
+ width: (this._props.PanelWidth() / scale) * 0.9,
margin: { top: 10, right: 25, bottom: 75, left: 45 },
};
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.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)}
@@ -131,7 +131,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
render() {
- const scale = this.props.NativeDimScaling?.() || 1;
+ const scale = this._props.NativeDimScaling?.() || 1;
return !this.records.length ? (
// displays how to get data into the DataVizBox if its empty
@@ -140,7 +140,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<div
className="dataViz"
style={{
- pointerEvents: this.props.isContentActive() === true ? 'all' : 'none',
+ pointerEvents: this._props.isContentActive() === true ? 'all' : 'none',
width: `${100 / scale}%`,
height: `${100 / scale}%`,
transform: `scale(${scale})`,
diff --git a/src/client/views/nodes/DataVizBox/components/Chart.scss b/src/client/views/nodes/DataVizBox/components/Chart.scss
index c788a64c2..c0c0f10a2 100644
--- a/src/client/views/nodes/DataVizBox/components/Chart.scss
+++ b/src/client/views/nodes/DataVizBox/components/Chart.scss
@@ -1,4 +1,4 @@
-@import '../../../global/globalCssVariables';
+@import '../../../global/globalCssVariables.module.scss';
.chart-container {
display: flex;
flex-direction: column;
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index e67e2bf31..d786e84ad 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -1,7 +1,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components';
import * as d3 from 'd3';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { FaFillDrip } from 'react-icons/fa';
@@ -10,14 +10,14 @@ import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
import { Docs } from '../../../../documents/Documents';
-import { LinkManager } from '../../../../util/LinkManager';
import { undoable } from '../../../../util/UndoManager';
import { PinProps, PresBox } from '../../trails';
import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
import './Chart.scss';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
export interface HistogramProps {
- rootDoc: Doc;
+ Document: Doc;
layoutDoc: Doc;
axes: string[];
records: { [key: string]: any }[];
@@ -34,7 +34,7 @@ export interface HistogramProps {
}
@observer
-export class Histogram extends React.Component<HistogramProps> {
+export class Histogram extends ObservableReactComponent<HistogramProps> {
private _disposers: { [key: string]: IReactionDisposer } = {};
private _histogramRef: React.RefObject<HTMLDivElement> = React.createRef();
private _histogramSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@@ -46,46 +46,51 @@ export class Histogram extends React.Component<HistogramProps> {
private selectedData: any = undefined; // Selection of selected bar
private hoverOverData: any = undefined; // Selection of bar being hovered over
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get _tableDataIds() {
- return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ return !this.parentViz ? this._props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
}
// returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
@computed get _tableData() {
- return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ return !this.parentViz ? this._props.records : this._tableDataIds.map(rowId => this._props.records[rowId]);
}
// filters all data to just display selected data if brushed (created from an incoming link)
@computed get _histogramData() {
- if (this.props.axes.length < 1) return [];
- if (this.props.axes.length < 2) {
- var ax0 = this.props.axes[0];
- if (/\d/.test(this.props.records[0][ax0])) {
+ if (this._props.axes.length < 1) return [];
+ if (this._props.axes.length < 2) {
+ var ax0 = this._props.axes[0];
+ if (/\d/.test(this._props.records[0][ax0])) {
this.numericalXData = true;
}
- return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]] }));
+ return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]] }));
}
- var ax0 = this.props.axes[0];
- var ax1 = this.props.axes[1];
- if (/\d/.test(this.props.records[0][ax0])) {
+ var ax0 = this._props.axes[0];
+ var ax1 = this._props.axes[1];
+ if (/\d/.test(this._props.records[0][ax0])) {
this.numericalXData = true;
}
- if (/\d/.test(this.props.records[0][ax1])) {
+ if (/\d/.test(this._props.records[0][ax1])) {
this.numericalYData = true;
}
- return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]], [ax1]: record[this.props.axes[1]] }));
+ return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]], [ax1]: record[this._props.axes[1]] }));
}
@computed get defaultGraphTitle() {
- var ax0 = this.props.axes[0];
- var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
- if (this.props.axes.length < 2 || !ax1 || !/\d/.test(this.props.records[0][ax1]) || !this.numericalYData) {
+ var ax0 = this._props.axes[0];
+ var ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
+ if (this._props.axes.length < 2 || !ax1 || !/\d/.test(this._props.records[0][ax1]) || !this.numericalYData) {
return ax0 + ' Histogram';
} else return ax0 + ' by ' + ax1 + ' Histogram';
}
@computed get parentViz() {
- return DocCast(this.props.rootDoc.dataViz_parentViz);
- // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
- // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link
+ return DocCast(this._props.Document.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links
+ // .filter(link => link.link_anchor_1 == this._props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link
// .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
}
@@ -115,16 +120,16 @@ export class Histogram extends React.Component<HistogramProps> {
const anchor = Docs.Create.ConfigDocument({
title: 'histogram doc selection' + this._currSelected,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
return anchor;
};
@computed get height() {
- return this.props.height - this.props.margin.top - this.props.margin.bottom;
+ return this._props.height - this._props.margin.top - this._props.margin.bottom;
}
@computed get width() {
- return this.props.width - this.props.margin.left - this.props.margin.right;
+ return this._props.width - this._props.margin.left - this._props.margin.right;
}
// cleans data by converting numerical data to numbers and taking out empty cells
@@ -212,10 +217,10 @@ export class Histogram extends React.Component<HistogramProps> {
.select(this._histogramRef.current)
.append('svg')
.attr('class', 'graph')
- .attr('width', width + this.props.margin.right + this.props.margin.left)
- .attr('height', height + this.props.margin.top + this.props.margin.bottom)
+ .attr('width', width + this._props.margin.right + this._props.margin.left)
+ .attr('height', height + this._props.margin.top + this._props.margin.bottom)
.append('g')
- .attr('transform', 'translate(' + this.props.margin.left + ',' + this.props.margin.top + ')'));
+ .attr('transform', 'translate(' + this._props.margin.left + ',' + this._props.margin.top + ')'));
var x = d3
.scaleLinear()
.domain(this.numericalXData ? [startingPoint!, endingPoint!] : [0, numBins])
@@ -383,7 +388,7 @@ export class Histogram extends React.Component<HistogramProps> {
)
.attr('fill', d => {
var barColor;
- const barColors = StrListCast(this.props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
+ const barColors = StrListCast(this._props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
barColors.forEach(each => {
if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
else {
@@ -391,24 +396,24 @@ export class Histogram extends React.Component<HistogramProps> {
if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
}
});
- return barColor ? StrCast(barColor) : StrCast(this.props.layoutDoc.dataViz_histogram_defaultColor);
+ return barColor ? StrCast(barColor) : StrCast(this._props.layoutDoc.dataViz_histogram_defaultColor);
});
};
@action changeSelectedColor = (color: string) => {
this.curBarSelected.attr('fill', color);
- const barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ const barName = StrCast(this._currSelected[this._props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
- const barColors = Cast(this.props.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
+ const barColors = Cast(this._props.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1));
barColors.push(StrCast(barName + '::' + color));
};
@action eraseSelectedColor = () => {
- this.curBarSelected.attr('fill', this.props.layoutDoc.dataViz_histogram_defaultColor);
- const barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ this.curBarSelected.attr('fill', this._props.layoutDoc.dataViz_histogram_defaultColor);
+ const barName = StrCast(this._currSelected[this._props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
- const barColors = Cast(this.props.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
+ const barColors = Cast(this._props.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1));
};
@@ -417,7 +422,7 @@ export class Histogram extends React.Component<HistogramProps> {
if (svg)
svg.selectAll('rect').attr('fill', (d: any) => {
var barColor;
- const barColors = StrListCast(this.props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
+ const barColors = StrListCast(this._props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
barColors.forEach(each => {
if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
else {
@@ -425,7 +430,7 @@ export class Histogram extends React.Component<HistogramProps> {
if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
}
});
- return barColor ? StrCast(barColor) : StrCast(this.props.layoutDoc.dataViz_histogram_defaultColor);
+ return barColor ? StrCast(barColor) : StrCast(this._props.layoutDoc.dataViz_histogram_defaultColor);
});
};
@@ -434,14 +439,14 @@ export class Histogram extends React.Component<HistogramProps> {
this._histogramData;
var curSelectedBarName = '';
var titleAccessor: any = '';
- if (this.props.axes.length == 2) titleAccessor = 'dataViz_histogram_title' + this.props.axes[0] + '-' + this.props.axes[1];
- else if (this.props.axes.length > 0) titleAccessor = 'dataViz_histogram_title' + this.props.axes[0];
- if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
- if (!this.props.layoutDoc.dataViz_histogram_defaultColor) this.props.layoutDoc.dataViz_histogram_defaultColor = '#69b3a2';
- if (!this.props.layoutDoc.dataViz_histogram_barColors) this.props.layoutDoc.dataViz_histogram_barColors = new List<string>();
+ if (this._props.axes.length == 2) titleAccessor = 'dataViz_histogram_title' + this._props.axes[0] + '-' + this._props.axes[1];
+ else if (this._props.axes.length > 0) titleAccessor = 'dataViz_histogram_title' + this._props.axes[0];
+ if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
+ if (!this._props.layoutDoc.dataViz_histogram_defaultColor) this._props.layoutDoc.dataViz_histogram_defaultColor = '#69b3a2';
+ if (!this._props.layoutDoc.dataViz_histogram_barColors) this._props.layoutDoc.dataViz_histogram_barColors = new List<string>();
var selected = 'none';
if (this._currSelected) {
- curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ curSelectedBarName = StrCast(this._currSelected![this._props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
selected = '{ ';
Object.keys(this._currSelected).forEach(key =>
key //
@@ -451,17 +456,17 @@ export class Histogram extends React.Component<HistogramProps> {
selected = selected.substring(0, selected.length - 2) + ' }';
}
var selectedBarColor;
- var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
+ var barColors = StrListCast(this._props.layoutDoc.histogramBarColors).map(each => each.split('::'));
barColors.forEach(each => each[0] === curSelectedBarName && (selectedBarColor = each[1]));
if (this._histogramData.length > 0 || !this.parentViz) {
- return this.props.axes.length >= 1 ? (
+ return this._props.axes.length >= 1 ? (
<div className="chart-container">
<div className="graph-title">
<EditableText
- val={StrCast(this.props.layoutDoc[titleAccessor])}
+ val={StrCast(this._props.layoutDoc[titleAccessor])}
setVal={undoable(
- action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+ action(val => (this._props.layoutDoc[titleAccessor] = val as string)),
'Change Graph Title'
)}
color={'black'}
@@ -473,9 +478,9 @@ export class Histogram extends React.Component<HistogramProps> {
tooltip={'Change Default Bar Color'}
type={Type.SEC}
icon={<FaFillDrip />}
- selectedColor={StrCast(this.props.layoutDoc.dataViz_histogram_defaultColor)}
- setFinalColor={undoable(color => (this.props.layoutDoc.dataViz_histogram_defaultColor = color), 'Change Default Bar Color')}
- setSelectedColor={undoable(color => (this.props.layoutDoc.dataViz_histogram_defaultColor = color), 'Change Default Bar Color')}
+ selectedColor={StrCast(this._props.layoutDoc.dataViz_histogram_defaultColor)}
+ setFinalColor={undoable(color => (this._props.layoutDoc.dataViz_histogram_defaultColor = color), 'Change Default Bar Color')}
+ setSelectedColor={undoable(color => (this._props.layoutDoc.dataViz_histogram_defaultColor = color), 'Change Default Bar Color')}
size={Size.XSMALL}
/>
</div>
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 3de7a0c4a..abb95aa4c 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -1,6 +1,6 @@
import { EditableText, Size } from 'browndash-components';
import * as d3 from 'd3';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, NumListCast, StrListCast } from '../../../../../fields/Doc';
@@ -14,6 +14,7 @@ import { PinProps, PresBox } from '../../trails';
import { DataVizBox } from '../DataVizBox';
import { createLineGenerator, drawLine, minMaxRange, scaleCreatorNumerical, xAxisCreator, xGrid, yAxisCreator, yGrid } from '../utils/D3Utils';
import './Chart.scss';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
export interface DataPoint {
x: number;
@@ -23,7 +24,7 @@ export interface SelectedDataPoint extends DataPoint {
elem?: d3.Selection<d3.BaseType, unknown, SVGGElement, unknown>;
}
export interface LineChartProps {
- rootDoc: Doc;
+ Document: Doc;
layoutDoc: Doc;
axes: string[];
records: { [key: string]: any }[];
@@ -40,33 +41,38 @@ export interface LineChartProps {
}
@observer
-export class LineChart extends React.Component<LineChartProps> {
+export class LineChart extends ObservableReactComponent<LineChartProps> {
private _disposers: { [key: string]: IReactionDisposer } = {};
private _lineChartRef: React.RefObject<HTMLDivElement> = React.createRef();
private _lineChartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@observable _currSelected: SelectedDataPoint | undefined = undefined;
// TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates
+ constructor(props:any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get _tableDataIds() {
- return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ return !this.parentViz ? this._props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
}
// returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
@computed get _tableData() {
- return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ return !this.parentViz ? this._props.records : this._tableDataIds.map(rowId => this._props.records[rowId]);
}
@computed get _lineChartData() {
- var guids = StrListCast(this.props.layoutDoc.dataViz_rowIds);
- if (this.props.axes.length <= 1) return [];
- return this._tableData.map(record => ({ x: Number(record[this.props.axes[0]]), y: Number(record[this.props.axes[1]]) })).sort((a, b) => (a.x < b.x ? -1 : 1));
+ var guids = StrListCast(this._props.layoutDoc.dataViz_rowIds);
+ if (this._props.axes.length <= 1) return [];
+ return this._tableData.map(record => ({ x: Number(record[this._props.axes[0]]), y: Number(record[this._props.axes[1]]) })).sort((a, b) => (a.x < b.x ? -1 : 1));
}
@computed get graphTitle() {
- return this.props.axes[1] + ' vs. ' + this.props.axes[0] + ' Line Chart';
+ return this._props.axes[1] + ' vs. ' + this._props.axes[0] + ' Line Chart';
}
@computed get parentViz() {
- return DocCast(this.props.rootDoc.dataViz_parentViz);
- // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
+ return DocCast(this._props.Document.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links
// .filter(link => {
- // return link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz;
+ // return link.link_anchor_1 == this._props.Document.dataViz_parentViz;
// }) // get links where this chart doc is the target of the link
// .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
}
@@ -74,7 +80,7 @@ export class LineChart extends React.Component<LineChartProps> {
// return selected x and y axes
// otherwise, use the selection of whatever is linked to us
const incomingVizBox = DocumentManager.Instance.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox;
- const highlitedRowIds = NumListCast(incomingVizBox?.rootDoc?.dataViz_highlitedRows);
+ const highlitedRowIds = NumListCast(incomingVizBox?.layoutDoc?.dataViz_highlitedRows);
return this._tableData.filter((record, i) => highlitedRowIds.includes(this._tableDataIds[i])); // get all the datapoints they have selected field set by incoming anchor
}
@computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
@@ -94,7 +100,7 @@ export class LineChart extends React.Component<LineChartProps> {
{ fireImmediately: true }
);
this._disposers.annos = reaction(
- () => DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']),
+ () => DocListCast(this._props.dataDoc[this._props.fieldKey + '_annotations']),
annotations => {
// modify how d3 renders so that anything in this annotations list would be potentially highlighted in some way
// could be blue colored to make it look like anchor
@@ -114,7 +120,7 @@ export class LineChart extends React.Component<LineChartProps> {
// redraw annotations when the chart data has changed, or the local or inherited selection has changed
this.clearAnnotations();
selected && this.drawAnnotations(Number(selected.x), Number(selected.y), true);
- incomingHighlited?.forEach((record: any) => this.drawAnnotations(Number(record[this.props.axes[0]]), Number(record[this.props.axes[1]])));
+ incomingHighlited?.forEach((record: any) => this.drawAnnotations(Number(record[this._props.axes[0]]), Number(record[this._props.axes[1]])));
},
{ fireImmediately: true }
);
@@ -170,23 +176,23 @@ export class LineChart extends React.Component<LineChartProps> {
//
title: 'line doc selection' + this._currSelected?.x,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
anchor.config_dataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
return anchor;
};
@computed get height() {
- return this.props.height - this.props.margin.top - this.props.margin.bottom;
+ return this._props.height - this._props.margin.top - this._props.margin.bottom;
}
@computed get width() {
- return this.props.width - this.props.margin.left - this.props.margin.right;
+ return this._props.width - this._props.margin.left - this._props.margin.right;
}
@computed get defaultGraphTitle() {
- var ax0 = this.props.axes[0];
- var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
- if (this.props.axes.length < 2 || !/\d/.test(this.props.records[0][ax0]) || !ax1) {
+ var ax0 = this._props.axes[0];
+ var ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
+ if (this._props.axes.length < 2 || !/\d/.test(this._props.records[0][ax0]) || !ax1) {
return ax0 + ' Line Chart';
} else return ax1 + ' by ' + ax0 + ' Line Chart';
}
@@ -210,7 +216,7 @@ export class LineChart extends React.Component<LineChartProps> {
// TODO: nda - get rid of svg element in the list?
if (this._currSelected && this._currSelected.x == x && this._currSelected.y == y) this._currSelected = undefined;
else this._currSelected = x !== undefined && y !== undefined ? { x, y } : undefined;
- this.props.records.forEach(record => record[this.props.axes[0]] === x && record[this.props.axes[1]] === y && (record.selected = true));
+ this._props.records.forEach(record => record[this._props.axes[0]] === x && record[this._props.axes[1]] === y && (record.selected = true));
}
drawDataPoints(data: DataPoint[], idx: number, xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>) {
@@ -245,7 +251,7 @@ export class LineChart extends React.Component<LineChartProps> {
const yScale = scaleCreatorNumerical(0, yMax, height, 0);
// adding svg
- const margin = this.props.margin;
+ const margin = this._props.margin;
const svg = (this._lineChartSvg = d3
.select(this._lineChartRef.current)
.append('svg')
@@ -317,7 +323,7 @@ export class LineChart extends React.Component<LineChartProps> {
svg.append('text')
.attr('transform', 'translate(' + width / 2 + ' ,' + (height + 40) + ')')
.style('text-anchor', 'middle')
- .text(this.props.axes[0]);
+ .text(this._props.axes[0]);
svg.append('text')
.attr('transform', 'rotate(-90)' + ' ' + 'translate( 0, ' + -10 + ')')
.attr('x', -(height / 2))
@@ -325,7 +331,7 @@ export class LineChart extends React.Component<LineChartProps> {
.attr('height', 20)
.attr('width', 20)
.style('text-anchor', 'middle')
- .text(this.props.axes[1]);
+ .text(this._props.axes[1]);
};
private updateTooltip(
@@ -346,18 +352,18 @@ export class LineChart extends React.Component<LineChartProps> {
render() {
var titleAccessor: any = '';
- if (this.props.axes.length == 2) titleAccessor = 'dataViz_lineChart_title' + this.props.axes[0] + '-' + this.props.axes[1];
- else if (this.props.axes.length > 0) titleAccessor = 'dataViz_lineChart_title' + this.props.axes[0];
- if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
- const selectedPt = this._currSelected ? `{ ${this.props.axes[0]}: ${this._currSelected.x} ${this.props.axes[1]}: ${this._currSelected.y} }` : 'none';
+ if (this._props.axes.length == 2) titleAccessor = 'dataViz_lineChart_title' + this._props.axes[0] + '-' + this._props.axes[1];
+ else if (this._props.axes.length > 0) titleAccessor = 'dataViz_lineChart_title' + this._props.axes[0];
+ if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
+ const selectedPt = this._currSelected ? `{ ${this._props.axes[0]}: ${this._currSelected.x} ${this._props.axes[1]}: ${this._currSelected.y} }` : 'none';
if (this._lineChartData.length > 0 || !this.parentViz || this.parentViz.length == 0) {
- return this.props.axes.length >= 2 && /\d/.test(this.props.records[0][this.props.axes[0]]) && /\d/.test(this.props.records[0][this.props.axes[1]]) ? (
+ return this._props.axes.length >= 2 && /\d/.test(this._props.records[0][this._props.axes[0]]) && /\d/.test(this._props.records[0][this._props.axes[1]]) ? (
<div className="chart-container">
<div className="graph-title">
<EditableText
- val={StrCast(this.props.layoutDoc[titleAccessor])}
+ val={StrCast(this._props.layoutDoc[titleAccessor])}
setVal={undoable(
- action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+ action(val => (this._props.layoutDoc[titleAccessor] = val as string)),
'Change Graph Title'
)}
color={'black'}
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index 7303eb184..9e7265b26 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -1,6 +1,7 @@
+import { Checkbox } from '@mui/material';
import { ColorPicker, EditableText, Size, Type } from 'browndash-components';
import * as d3 from 'd3';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { FaFillDrip } from 'react-icons/fa';
@@ -12,10 +13,10 @@ import { Docs } from '../../../../documents/Documents';
import { undoable } from '../../../../util/UndoManager';
import { PinProps, PresBox } from '../../trails';
import './Chart.scss';
-import { Checkbox } from '@material-ui/core';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
export interface PieChartProps {
- rootDoc: Doc;
+ Document: Doc;
layoutDoc: Doc;
axes: string[];
records: { [key: string]: any }[];
@@ -32,7 +33,7 @@ export interface PieChartProps {
}
@observer
-export class PieChart extends React.Component<PieChartProps> {
+export class PieChart extends ObservableReactComponent<PieChartProps> {
private _disposers: { [key: string]: IReactionDisposer } = {};
private _piechartRef: React.RefObject<HTMLDivElement> = React.createRef();
private _piechartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@@ -41,44 +42,49 @@ export class PieChart extends React.Component<PieChartProps> {
private hoverOverData: any = undefined; // Selection of slice being hovered over
@observable _currSelected: any | undefined = undefined; // Object of selected slice
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get _tableDataIds() {
- return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ return !this.parentViz ? this._props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
}
// returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
@computed get _tableData() {
- return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ return !this.parentViz ? this._props.records : this._tableDataIds.map(rowId => this._props.records[rowId]);
}
// organized by specified number percentages/ratios if one column is selected and it contains numbers
// otherwise, assume data is organized by categories
@computed get byCategory() {
- return !/\d/.test(this.props.records[0][this.props.axes[0]]) || this.props.layoutDoc.dataViz_pie_asHistogram;
+ return !/\d/.test(this._props.records[0][this._props.axes[0]]) || this._props.layoutDoc.dataViz_pie_asHistogram;
}
// filters all data to just display selected data if brushed (created from an incoming link)
@computed get _pieChartData() {
- if (this.props.axes.length < 1) return [];
+ if (this._props.axes.length < 1) return [];
- const ax0 = this.props.axes[0];
- if (this.props.axes.length < 2) {
- return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]] }));
+ const ax0 = this._props.axes[0];
+ if (this._props.axes.length < 2) {
+ return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]] }));
}
- const ax1 = this.props.axes[1];
- return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]], [ax1]: record[this.props.axes[1]] }));
+ const ax1 = this._props.axes[1];
+ return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]], [ax1]: record[this._props.axes[1]] }));
}
@computed get defaultGraphTitle() {
- var ax0 = this.props.axes[0];
- var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
- if (this.props.axes.length < 2 || !/\d/.test(this.props.records[0][ax0]) || !ax1) {
+ var ax0 = this._props.axes[0];
+ var ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
+ if (this._props.axes.length < 2 || !/\d/.test(this._props.records[0][ax0]) || !ax1) {
return ax0 + ' Pie Chart';
}
return ax1 + ' by ' + ax0 + ' Pie Chart';
}
@computed get parentViz() {
- return DocCast(this.props.rootDoc.dataViz_parentViz);
- // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
- // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link
+ return DocCast(this._props.Document.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links
+ // .filter(link => link.link_anchor_1 == this._props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link
// .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
}
@@ -101,16 +107,16 @@ export class PieChart extends React.Component<PieChartProps> {
//
title: 'piechart doc selection' + this._currSelected,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
return anchor;
};
@computed get height() {
- return this.props.height - this.props.margin.top - this.props.margin.bottom;
+ return this._props.height - this._props.margin.top - this._props.margin.bottom;
}
@computed get width() {
- return this.props.width - this.props.margin.left - this.props.margin.right;
+ return this._props.width - this._props.margin.left - this._props.margin.right;
}
// cleans data by converting numerical data to numbers and taking out empty cells
@@ -181,7 +187,7 @@ export class PieChart extends React.Component<PieChartProps> {
var percentField = Object.keys(dataSet[0])[0];
var descriptionField = Object.keys(dataSet[0])[1]!;
- var radius = Math.min(width, height - this.props.margin.top - this.props.margin.bottom) / 2;
+ var radius = Math.min(width, height - this._props.margin.top - this._props.margin.bottom) / 2;
// converts data into Objects
var data = this.data(dataSet);
@@ -209,10 +215,10 @@ export class PieChart extends React.Component<PieChartProps> {
.select(this._piechartRef.current)
.append('svg')
.attr('class', 'graph')
- .attr('width', width + this.props.margin.right + this.props.margin.left)
- .attr('height', height + this.props.margin.top + this.props.margin.bottom)
+ .attr('width', width + this._props.margin.right + this._props.margin.left)
+ .attr('height', height + this._props.margin.top + this._props.margin.bottom)
.append('g'));
- let g = svg.append('g').attr('transform', 'translate(' + (width / 2 + this.props.margin.left) + ',' + height / 2 + ')');
+ let g = svg.append('g').attr('transform', 'translate(' + (width / 2 + this._props.margin.left) + ',' + height / 2 + ')');
var pie = d3.pie();
var arc = d3.arc().innerRadius(0).outerRadius(radius);
@@ -249,7 +255,7 @@ export class PieChart extends React.Component<PieChartProps> {
} catch (error) {}
possibleDataPointVals.push(dataPointVal);
});
- const sliceColors = StrListCast(this.props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
+ const sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
arcs.append('path')
.attr('fill', (d, i) => {
var dataPoint;
@@ -261,7 +267,7 @@ export class PieChart extends React.Component<PieChartProps> {
}
var sliceColor;
if (dataPoint) {
- const sliceTitle = dataPoint[this.props.axes[0]];
+ const sliceTitle = dataPoint[this._props.axes[0]];
const accessByName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\</g, '') : sliceTitle;
sliceColors.forEach(each => each[0] == accessByName && (sliceColor = each[1]));
}
@@ -277,6 +283,7 @@ export class PieChart extends React.Component<PieChartProps> {
return 'slice';
}
)
+ // @ts-ignore
.attr('d', arc)
.on('click', onPointClick)
.on('mouseover', onHover)
@@ -308,10 +315,10 @@ export class PieChart extends React.Component<PieChartProps> {
@action changeSelectedColor = (color: string) => {
this.curSliceSelected.attr('fill', color);
- const sliceTitle = this._currSelected[this.props.axes[0]];
+ const sliceTitle = this._currSelected[this._props.axes[0]];
const sliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\</g, '') : sliceTitle;
- const sliceColors = Cast(this.props.layoutDoc.dataViz_pie_sliceColors, listSpec('string'), null);
+ const sliceColors = Cast(this._props.layoutDoc.dataViz_pie_sliceColors, listSpec('string'), null);
sliceColors.map(each => {
if (each.split('::')[0] == sliceName) sliceColors.splice(sliceColors.indexOf(each), 1);
});
@@ -319,20 +326,20 @@ export class PieChart extends React.Component<PieChartProps> {
};
@action changeHistogramCheckBox = () => {
- this.props.layoutDoc.dataViz_pie_asHistogram = !this.props.layoutDoc.dataViz_pie_asHistogram;
+ this._props.layoutDoc.dataViz_pie_asHistogram = !this._props.layoutDoc.dataViz_pie_asHistogram;
this.drawChart(this._pieChartData, this.width, this.height);
};
render() {
var titleAccessor: any = '';
- if (this.props.axes.length == 2) titleAccessor = 'dataViz_pie_title' + this.props.axes[0] + '-' + this.props.axes[1];
- else if (this.props.axes.length > 0) titleAccessor = 'dataViz_pie_title' + this.props.axes[0];
- if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
- if (!this.props.layoutDoc.dataViz_pie_sliceColors) this.props.layoutDoc.dataViz_pie_sliceColors = new List<string>();
+ if (this._props.axes.length == 2) titleAccessor = 'dataViz_pie_title' + this._props.axes[0] + '-' + this._props.axes[1];
+ else if (this._props.axes.length > 0) titleAccessor = 'dataViz_pie_title' + this._props.axes[0];
+ if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
+ if (!this._props.layoutDoc.dataViz_pie_sliceColors) this._props.layoutDoc.dataViz_pie_sliceColors = new List<string>();
var selected: string;
var curSelectedSliceName = '';
if (this._currSelected) {
- const sliceTitle = this._currSelected[this.props.axes[0]];
+ const sliceTitle = this._currSelected[this._props.axes[0]];
curSelectedSliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\</g, '') : sliceTitle;
selected = '{ ';
Object.keys(this._currSelected).map(key => {
@@ -342,19 +349,19 @@ export class PieChart extends React.Component<PieChartProps> {
selected += ' }';
} else selected = 'none';
var selectedSliceColor;
- var sliceColors = StrListCast(this.props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
+ var sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
sliceColors.forEach(each => {
if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1];
});
if (this._pieChartData.length > 0 || !this.parentViz) {
- return this.props.axes.length >= 1 ? (
+ return this._props.axes.length >= 1 ? (
<div className="chart-container">
<div className="graph-title">
<EditableText
- val={StrCast(this.props.layoutDoc[titleAccessor])}
+ val={StrCast(this._props.layoutDoc[titleAccessor])}
setVal={undoable(
- action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+ action(val => (this._props.layoutDoc[titleAccessor] = val as string)),
'Change Graph Title'
)}
color={'black'}
@@ -362,9 +369,9 @@ export class PieChart extends React.Component<PieChartProps> {
fillWidth
/>
</div>
- {this.props.axes.length === 1 && /\d/.test(this.props.records[0][this.props.axes[0]]) ? (
- <div className={'asHistogram-checkBox'} style={{ width: this.props.width }}>
- <Checkbox color="primary" onChange={this.changeHistogramCheckBox} checked={this.props.layoutDoc.dataViz_pie_asHistogram as boolean} />
+ {this._props.axes.length === 1 && /\d/.test(this._props.records[0][this._props.axes[0]]) ? (
+ <div className={'asHistogram-checkBox'} style={{ width: this._props.width }}>
+ <Checkbox color="primary" onChange={this.changeHistogramCheckBox} checked={this._props.layoutDoc.dataViz_pie_asHistogram as boolean} />
Organize data as histogram
</div>
) : null}
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 147dfb182..f77c138df 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -1,5 +1,5 @@
import { Button, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, Field, NumListCast } from '../../../../../fields/Doc';
@@ -10,11 +10,11 @@ import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../../Utils';
import { DragManager } from '../../../../util/DragManager';
import { DocumentView } from '../../DocumentView';
import { DataVizView } from '../DataVizBox';
-import { DATA_VIZ_TABLE_ROW_HEIGHT } from '../../../global/globalCssVariables.scss';
import './Chart.scss';
-
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
+const { default: { DATA_VIZ_TABLE_ROW_HEIGHT } } = require('../../../global/globalCssVariables.module.scss'); // prettier-ignore
interface TableBoxProps {
- rootDoc: Doc;
+ Document: Doc;
layoutDoc: Doc;
records: { [key: string]: any }[];
selectAxes: (axes: string[]) => void;
@@ -31,13 +31,17 @@ interface TableBoxProps {
}
@observer
-export class TableBox extends React.Component<TableBoxProps> {
+export class TableBox extends ObservableReactComponent<TableBoxProps> {
_inputChangedDisposer?: IReactionDisposer;
_containerRef: HTMLDivElement | null = null;
@observable _scrollTop = -1;
@observable _tableHeight = 0;
@observable _tableContainerHeight = 0;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
componentDidMount() {
// if the tableData changes (ie., when records are selected by the parent (input) visulization),
@@ -49,17 +53,17 @@ export class TableBox extends React.Component<TableBoxProps> {
this._inputChangedDisposer?.();
}
@computed get _tableDataIds() {
- return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ return !this.parentViz ? this._props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
}
// returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
@computed get _tableData() {
- return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ return !this.parentViz ? this._props.records : this._tableDataIds.map(rowId => this._props.records[rowId]);
}
@computed get parentViz() {
- return DocCast(this.props.rootDoc.dataViz_parentViz);
- // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
- // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link
+ return DocCast(this._props.Document.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links
+ // .filter(link => link.link_anchor_1 == this._props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link
// .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
}
@@ -69,33 +73,35 @@ export class TableBox extends React.Component<TableBoxProps> {
// updates the 'dataViz_selectedRows' and 'dataViz_highlightedRows' fields to no longer include rows that aren't in the table
filterSelectedRowsDown = () => {
- const selected = NumListCast(this.props.layoutDoc.dataViz_selectedRows);
- this.props.layoutDoc.dataViz_selectedRows = new List<number>(selected.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data
- const highlighted = NumListCast(this.props.layoutDoc.dataViz_highlitedRows);
- this.props.layoutDoc.dataViz_highlitedRows = new List<number>(highlighted.filter(rowId => this._tableDataIds.includes(rowId))); // filters through highlighted to remove guids that were removed in the incoming data
+ const selected = NumListCast(this._props.layoutDoc.dataViz_selectedRows);
+ this._props.layoutDoc.dataViz_selectedRows = new List<number>(selected.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data
+ const highlighted = NumListCast(this._props.layoutDoc.dataViz_highlitedRows);
+ this._props.layoutDoc.dataViz_highlitedRows = new List<number>(highlighted.filter(rowId => this._tableDataIds.includes(rowId))); // filters through highlighted to remove guids that were removed in the incoming data
};
@computed get viewScale() {
- return this.props.docView?.()?.props.ScreenToLocalTransform().Scale || 1;
+ return this._props.docView?.()?._props.ScreenToLocalTransform().Scale || 1;
}
@computed get rowHeight() {
+ console.log('scale = ' + this.viewScale + ' table = ' + this._tableHeight + ' ids = ' + this._tableDataIds.length);
return (this.viewScale * this._tableHeight) / this._tableDataIds.length;
}
@computed get startID() {
return this.rowHeight ? Math.floor(this._scrollTop / this.rowHeight) : 0;
}
@computed get endID() {
+ console.log('start = ' + this.startID + ' container = ' + this._tableContainerHeight + ' scale = ' + this.viewScale + ' row = ' + this.rowHeight);
return Math.ceil(this.startID + (this._tableContainerHeight * this.viewScale) / (this.rowHeight || 1));
}
@action handleScroll = () => {
- if (!this.props.docView?.()?.ContentDiv?.hidden) {
+ if (!this._props.docView?.()?.ContentDiv?.hidden) {
this._scrollTop = this._containerRef?.scrollTop ?? 0;
}
};
@action
tableRowClick = (e: React.MouseEvent, rowId: number) => {
- const highlited = Cast(this.props.layoutDoc.dataViz_highlitedRows, listSpec('number'), null);
- const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('number'), null);
+ const highlited = Cast(this._props.layoutDoc.dataViz_highlitedRows, listSpec('number'), null);
+ const selected = Cast(this._props.layoutDoc.dataViz_selectedRows, listSpec('number'), null);
if (e.metaKey) {
// highlighting a row
if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1);
@@ -119,26 +125,26 @@ export class TableBox extends React.Component<TableBoxProps> {
e,
e => {
// dragging off a column to create a brushed DataVizBox
- const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!;
+ const sourceAnchorCreator = () => this._props.docView?.()!.Document!;
const targetCreator = (annotationOn: Doc | undefined) => {
- const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!);
+ const embedding = Doc.MakeEmbedding(this._props.docView?.()!.Document!);
embedding._dataViz = DataVizView.TABLE;
embedding._dataViz_axes = new List<string>([col, col]);
- embedding._dataViz_parentViz = this.props.rootDoc;
+ embedding._dataViz_parentViz = this._props.Document;
embedding.annotationOn = annotationOn;
- embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors);
- embedding.defaultHistogramColor = this.props.layoutDoc.defaultHistogramColor;
- embedding.pieSliceColors = Field.Copy(this.props.layoutDoc.pieSliceColors);
+ embedding.histogramBarColors = Field.Copy(this._props.layoutDoc.histogramBarColors);
+ embedding.defaultHistogramColor = this._props.layoutDoc.defaultHistogramColor;
+ embedding.pieSliceColors = Field.Copy(this._props.layoutDoc.pieSliceColors);
return embedding;
};
- if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
- DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
+ if (this._props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
+ DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this._props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
dragComplete: e => {
if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
e.linkDocument.link_displayLine = true;
e.linkDocument.link_matchEmbeddings = true;
e.linkDocument.link_displayArrow = true;
- // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
+ // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this._props.Document;
// e.annoDragData.linkSourceDoc.followLinkZoom = false;
}
},
@@ -149,11 +155,11 @@ export class TableBox extends React.Component<TableBoxProps> {
},
emptyFunction,
action(e => {
- const newAxes = this.props.axes;
+ const newAxes = this._props.axes;
if (newAxes.includes(col)) newAxes.splice(newAxes.indexOf(col), 1);
else if (newAxes.length > 1) newAxes[1] = col;
else newAxes.push(col);
- this.props.selectAxes(newAxes);
+ this._props.selectAxes(newAxes);
})
);
};
@@ -165,14 +171,14 @@ export class TableBox extends React.Component<TableBoxProps> {
className="tableBox"
tabIndex={0}
onKeyDown={e => {
- if (this.props.layoutDoc && e.key === 'a' && (e.ctrlKey || e.metaKey)) {
+ if (this._props.layoutDoc && e.key === 'a' && (e.ctrlKey || e.metaKey)) {
e.stopPropagation();
- this.props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds);
+ this._props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds);
}
}}>
<div className="selectAll-buttons">
- <Button onClick={action(() => (this.props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds)))} text="Select All" type={Type.SEC} color={'black'} />
- <Button onClick={action(() => (this.props.layoutDoc.dataViz_selectedRows = new List<number>()))} text="Deselect All" type={Type.SEC} color={'black'} />
+ <Button onClick={action(() => (this._props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds)))} text="Select All" type={Type.SEC} color={'black'} />
+ <Button onClick={action(() => (this._props.layoutDoc.dataViz_selectedRows = new List<number>()))} text="Deselect All" type={Type.SEC} color={'black'} />
</div>
<div
className={`tableBox-container ${this.columns[0]}`}
@@ -180,7 +186,7 @@ export class TableBox extends React.Component<TableBoxProps> {
onScroll={this.handleScroll}
ref={action((r: HTMLDivElement | null) => {
this._containerRef = r;
- if (!this.props.docView?.()?.ContentDiv?.hidden && r) {
+ if (!this._props.docView?.()?.ContentDiv?.hidden && r) {
this._tableContainerHeight = r.getBoundingClientRect().height ?? 0;
r.addEventListener(
'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
@@ -195,7 +201,7 @@ export class TableBox extends React.Component<TableBoxProps> {
<table
className="tableBox-table"
ref={action((r: HTMLTableElement | null) => {
- if (!this.props.docView?.()?.ContentDiv?.hidden && r) {
+ if (!this._props.docView?.()?.ContentDiv?.hidden && r) {
this._tableHeight = r?.getBoundingClientRect().height ?? 0;
}
})}>
@@ -206,8 +212,8 @@ export class TableBox extends React.Component<TableBoxProps> {
<th
key={this.columns.indexOf(col)}
style={{
- color: this.props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this.props.axes.lastElement() === col ? 'darkred' : undefined,
- background: this.props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this.props.axes.lastElement() === col ? '#Fbdbdb' : undefined,
+ color: this._props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this._props.axes.lastElement() === col ? 'darkred' : undefined,
+ background: this._props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this._props.axes.lastElement() === col ? '#Fbdbdb' : undefined,
fontWeight: 'bolder',
border: '3px solid black',
}}
@@ -226,13 +232,13 @@ export class TableBox extends React.Component<TableBoxProps> {
className={`tableBox-row ${this.columns[0]}`}
onClick={e => this.tableRowClick(e, rowId)}
style={{
- background: NumListCast(this.props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this.props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '',
+ background: NumListCast(this._props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this._props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '',
}}>
{this.columns.map(col => {
- const colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false;
+ const colSelected = this._props.axes.length > 1 ? this._props.axes[0] == col || this._props.axes[1] == col : this._props.axes.length > 0 ? this._props.axes[0] == col : false;
return (
<td key={this.columns.indexOf(col)} style={{ border: colSelected ? '3px solid black' : '1px solid black', fontWeight: colSelected ? 'bolder' : 'normal' }}>
- <div className="tableBox-cell">{this.props.records[rowId][col]}</div>
+ <div className="tableBox-cell">{this._props.records[rowId][col]}</div>
</td>
);
})}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 669d45ca5..e161b4c4c 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,24 +1,25 @@
-import { computed, trace } from 'mobx';
+import { computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
+import * as XRegExp from 'xregexp';
+import { OmitKeys, Without, emptyPath } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
-import { AclPrivate } from '../../../fields/DocSymbols';
+import { AclPrivate, DocData } from '../../../fields/DocSymbols';
import { ScriptField } from '../../../fields/ScriptField';
import { Cast, StrCast } from '../../../fields/Types';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
-import { emptyPath, OmitKeys, Without } from '../../../Utils';
-import { DirectoryImportBox } from '../../util/Import & Export/DirectoryImportBox';
+import { InkingStroke } from '../InkingStroke';
+import { ObservableReactComponent } from '../ObservableReactComponent';
import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { CollectionView } from '../collections/CollectionView';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { CollectionSchemaView } from '../collections/collectionSchema/CollectionSchemaView';
import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
-import { CollectionView } from '../collections/CollectionView';
-import { InkingStroke } from '../InkingStroke';
import { PresElementBox } from '../nodes/trails/PresElementBox';
import { SearchBox } from '../search/SearchBox';
import { DashWebRTCVideo } from '../webcam/DashWebRTCVideo';
import { YoutubeBox } from './../../apis/youtube/YoutubeBox';
import { AudioBox } from './AudioBox';
-import { ColorBox } from './ColorBox';
import { ComparisonBox } from './ComparisonBox';
import { DataVizBox } from './DataVizBox/DataVizBox';
import { DocumentViewProps } from './DocumentView';
@@ -26,27 +27,25 @@ import './DocumentView.scss';
import { EquationBox } from './EquationBox';
import { FieldView, FieldViewProps } from './FieldView';
import { FontIconBox } from './FontIconBox/FontIconBox';
-import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { FunctionPlotBox } from './FunctionPlotBox';
import { ImageBox } from './ImageBox';
-import { ImportElementBox } from './importBox/ImportElementBox';
import { KeyValueBox } from './KeyValueBox';
import { LabelBox } from './LabelBox';
import { LinkAnchorBox } from './LinkAnchorBox';
import { LinkBox } from './LinkBox';
import { LoadingBox } from './LoadingBox';
import { MapBox } from './MapBox/MapBox';
+import { MapPushpinBox } from './MapBox/MapPushpinBox';
import { PDFBox } from './PDFBox';
import { PhysicsSimulationBox } from './PhysicsBox/PhysicsSimulationBox';
import { RecordingBox } from './RecordingBox';
import { ScreenshotBox } from './ScreenshotBox';
import { ScriptingBox } from './ScriptingBox';
-import { PresBox } from './trails/PresBox';
import { VideoBox } from './VideoBox';
import { WebBox } from './WebBox';
-import React = require('react');
-import XRegExp = require('xregexp');
-import { MapPushpinBox } from './MapBox/MapPushpinBox';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { ImportElementBox } from './importBox/ImportElementBox';
+import { PresBox } from './trails/PresBox';
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
@@ -116,40 +115,38 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
}
@observer
-export class DocumentContentsView extends React.Component<
- DocumentViewProps & {
- setHeight?: (height: number) => void;
- layout_fieldKey: string;
- }
+export class DocumentContentsView extends ObservableReactComponent<
+ DocumentViewProps &
+ FieldViewProps & {
+ setHeight?: (height: number) => void;
+ layout_fieldKey: string;
+ }
> {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get layout(): string {
TraceMobx();
- if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
+ if (this._props.LayoutTemplateString) return this._props.LayoutTemplateString;
if (!this.layoutDoc) return '<p>awaiting layout</p>';
- if (this.props.layout_fieldKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString());
- const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layout_fieldKey ? this.props.layout_fieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')], 'string');
- if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString();
+ if (this._props.layout_fieldKey === 'layout_keyValue') return StrCast(this._props.Document.layout_keyValue, KeyValueBox.LayoutString());
+ const layout = Cast(this.layoutDoc[this.layoutDoc === this._props.Document && this._props.layout_fieldKey ? this._props.layout_fieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')], 'string');
+ if (layout === undefined) return this._props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString();
if (typeof layout === 'string') return layout;
return '<p>Loading layout</p>';
}
- componentDidUpdate(prevProps: Readonly<DocumentViewProps & { setHeight?: ((height: number) => void) | undefined; layout_fieldKey: string }>, prevState: Readonly<{}>, snapshot?: any): void {
- Object.keys(prevProps).forEach(pkey => (prevProps as any)[pkey] !== (this.props as any)[pkey] && console.log(pkey + ' ' + (prevProps as any)[pkey] + ' ' + (this.props as any)[pkey]));
- }
-
- get dataDoc() {
- const proto = this.props.DataDoc || Doc.GetProto(this.props.Document);
- return proto instanceof Promise ? undefined : proto;
- }
get layoutDoc() {
// bcz: replaced this with below : is it correct? change was made to accommodate passing fieldKey's from a layout script
- // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layout_fieldKey ? Cast(this.props.Document[this.props.layout_fieldKey], Doc, null) : undefined);
+ // const template: Doc = this._props.LayoutTemplate?.() || Doc.Layout(this._props.Document, this._props.layout_fieldKey ? Cast(this._props.Document[this._props.layout_fieldKey], Doc, null) : undefined);
const template: Doc =
- this.props.LayoutTemplate?.() ||
- (this.props.LayoutTemplateString && this.props.Document) ||
- (this.props.layout_fieldKey && StrCast(this.props.Document[this.props.layout_fieldKey]) && this.props.Document) ||
- Doc.Layout(this.props.Document, this.props.layout_fieldKey ? Cast(this.props.Document[this.props.layout_fieldKey], Doc, null) : undefined);
- return Doc.expandTemplateLayout(template, this.props.Document);
+ this._props.LayoutTemplate?.() ||
+ (this._props.LayoutTemplateString && this._props.Document) ||
+ (this._props.layout_fieldKey && StrCast(this._props.Document[this._props.layout_fieldKey]) && this._props.Document) ||
+ Doc.Layout(this._props.Document, this._props.layout_fieldKey ? Cast(this._props.Document[this._props.layout_fieldKey], Doc, null) : undefined);
+ return Doc.expandTemplateLayout(template, this._props.Document);
}
CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
@@ -163,23 +160,28 @@ export class DocumentContentsView extends React.Component<
'LayoutTemplate',
'dontCenter',
'contextMenuItems',
- 'onClick',
+ //'onClick', // don't need to omit this since it will be set
'onDoubleClick',
'onPointerDown',
'onPointerUp',
];
- const list = {
- ...OmitKeys(this.props, [...docOnlyProps], '').omit,
- Document: this.layoutDoc,
- DataDoc: this.dataDoc,
- onClick: onClick,
- onInput: onInput,
+ const templateDataDoc = this._props.TemplateDataDocument ?? (this.layoutDoc !== this._props.Document ? this._props.Document[DocData] : undefined);
+ const list: BindingProps & React.DetailedHTMLProps<React.HtmlHTMLAttributes<HTMLDivElement>, HTMLDivElement> = {
+ ...this._props,
+ Document: this.layoutDoc ?? this._props.Document,
+ TemplateDataDocument: templateDataDoc instanceof Promise ? undefined : templateDataDoc,
+ onClick: onClick as any as React.MouseEventHandler, // pass onClick script as if it were a real function -- it will be interpreted properly in the HTMLtag
+ onInput: onInput as any as React.FormEventHandler,
+ };
+ return {
+ props: {
+ ...OmitKeys(list, [...docOnlyProps], '').omit,
+ },
};
- return { props: list };
}
// componentWillUpdate(oldProps: any, newState: any) {
- // // console.log("willupdate", oldProps, this.props); // bcz: if you get a message saying something invalidated because reactive props changed, then this method allows you to figure out which prop changed
+ // // console.log("willupdate", oldProps, this._props); // bcz: if you get a message saying something invalidated because reactive props changed, then this method allows you to figure out which prop changed
// }
@computed get renderData() {
@@ -188,13 +190,13 @@ export class DocumentContentsView extends React.Component<
// replace code content with a script >{content}< as in <HTMLdiv>{this.title}</HTMLdiv>
const replacer = (match: any, prefix: string, expr: string, postfix: string, offset: any, string: any) => {
- return prefix + ((ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string) || '') + postfix;
+ return prefix + ((ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this._props.Document }).result as string) || '') + postfix;
};
layoutFrame = layoutFrame.replace(/(>[^{]*)[^=]\{([^.'][^<}]+)\}([^}]*<)/g, replacer);
// replace HTML<tag> with corresponding HTML tag as in: <HTMLdiv> becomes <HTMLtag Document={props.Document} htmltag='div'>
const replacer2 = (match: any, p1: string, offset: any, string: any) => {
- return `<HTMLtag Document={props.Document} scaling='${this.props.NativeDimScaling?.() || 1}' htmltag='${p1}'`;
+ return `<HTMLtag Document={props.Document} scaling='${this._props.NativeDimScaling?.() || 1}' htmltag='${p1}'`;
};
layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
@@ -226,7 +228,7 @@ export class DocumentContentsView extends React.Component<
TraceMobx();
const { bindings, layoutFrame } = this.renderData;
- return this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || GetEffectiveAcl(this.layoutDoc) === AclPrivate ? null : (
+ return this._props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || GetEffectiveAcl(this.layoutDoc) === AclPrivate ? null : (
<ObserverJsxParser
key={42}
blacklistedAttrs={emptyPath}
@@ -234,7 +236,6 @@ export class DocumentContentsView extends React.Component<
components={{
FormattedTextBox,
ImageBox,
- DirectoryImportBox,
FontIconBox,
LabelBox,
EquationBox,
@@ -254,7 +255,6 @@ export class DocumentContentsView extends React.Component<
PresElementBox,
SearchBox,
FunctionPlotBox,
- ColorBox,
DashWebRTCVideo,
LinkAnchorBox,
InkingStroke,
@@ -269,7 +269,7 @@ export class DocumentContentsView extends React.Component<
PhysicsSimulationBox,
SchemaRowBox,
ImportElementBox,
- // MapPushpinBox,
+ MapPushpinBox,
}}
bindings={bindings}
jsx={layoutFrame}
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index bccbd66e8..dfd610581 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -1,24 +1,34 @@
+import { Tooltip } from '@mui/material';
+import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { DocumentView } from './DocumentView';
-import { DocumentManager } from '../../util/DocumentManager';
-import { Transformer, ts } from '../../util/Scripting';
+import { factory } from 'typescript';
import { Field } from '../../../fields/Doc';
-import { Tooltip } from '@material-ui/core';
-import { action, observable } from 'mobx';
import { Id } from '../../../fields/FieldSymbols';
-import { factory } from 'typescript';
-import { LightboxView } from '../LightboxView';
+import { DocumentManager } from '../../util/DocumentManager';
+import { Transformer, ts } from '../../util/Scripting';
import { SettingsManager } from '../../util/SettingsManager';
+import { LightboxView } from '../LightboxView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import { DocumentView } from './DocumentView';
+interface DocumentIconProps {
+ view: DocumentView;
+ index: number;
+}
@observer
-export class DocumentIcon extends React.Component<{ view: DocumentView; index: number }> {
+export class DocumentIcon extends ObservableReactComponent<DocumentIconProps> {
@observable _hovered = false;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
static get DocViews() {
- return LightboxView.LightboxDoc ? DocumentManager.Instance.DocumentViews.filter(v => LightboxView.IsLightboxDocView(v.props.docViewPath())) : DocumentManager.Instance.DocumentViews;
+ return LightboxView.LightboxDoc ? DocumentManager.Instance.DocumentViews.filter(v => LightboxView.IsLightboxDocView(v._props.docViewPath())) : DocumentManager.Instance.DocumentViews;
}
render() {
- const view = this.props.view;
+ const view = this._props.view;
const { left, top, right, bottom } = view.getBounds() || { left: 0, top: 0, right: 0, bottom: 0 };
return (
@@ -33,8 +43,8 @@ export class DocumentIcon extends React.Component<{ view: DocumentView; index: n
background: SettingsManager.userBackgroundColor,
transform: `translate(${(left + right) / 2}px, ${top}px)`,
}}>
- <Tooltip title={<>{this.props.view.rootDoc.title}</>}>
- <p>d{this.props.index}</p>
+ <Tooltip title={<>{this._props.view.Document.title}</>}>
+ <p>d{this._props.index}</p>
</Tooltip>
</div>
);
@@ -59,7 +69,7 @@ export class DocumentIconContainer extends React.Component {
const match = node.text.match(/d([0-9]+)/);
if (match) {
const m = parseInt(match[1]);
- const doc = DocumentIcon.DocViews[m].rootDoc;
+ const doc = DocumentIcon.DocViews[m].Document;
usedDocuments.add(m);
return factory.createIdentifier(`idToDoc("${doc[Id]}")`);
}
@@ -74,7 +84,7 @@ export class DocumentIconContainer extends React.Component {
getVars() {
const docs = DocumentIcon.DocViews;
const capturedVariables: { [name: string]: Field } = {};
- usedDocuments.forEach(index => (capturedVariables[`d${index}`] = docs.length > index ? docs[index].props.Document : `d${index}`));
+ usedDocuments.forEach(index => (capturedVariables[`d${index}`] = docs.length > index ? docs[index].Document : `d${index}`));
return capturedVariables;
},
};
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index 6da0b73ba..b32b27e65 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.scss';
+@import '../global/globalCssVariables.module.scss';
.documentLinksButton-wrapper {
transform-origin: top left;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 50a7f5d7b..165057d21 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -1,22 +1,22 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
-import { action, computed, observable, runInAction } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
+import { StopEvent, emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
import { StrCast } from '../../../fields/Types';
-import { emptyFunction, returnFalse, setupMoveUpEvents, StopEvent } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { Hypothesis } from '../../util/HypothesisUtils';
import { LinkManager } from '../../util/LinkManager';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { UndoManager, undoBatch } from '../../util/UndoManager';
+import { ObservableReactComponent } from '../ObservableReactComponent';
import './DocumentLinksButton.scss';
import { DocumentView } from './DocumentView';
import { LinkDescriptionPopup } from './LinkDescriptionPopup';
import { TaskCompletionBox } from './TaskCompletedBox';
import { PinProps } from './trails';
-import React = require('react');
-import _ = require('lodash');
interface DocumentLinksButtonProps {
View: DocumentView;
@@ -29,25 +29,43 @@ interface DocumentLinksButtonProps {
scaling?: () => number; // how uch doc is scaled so that link buttons can invert it
hideCount?: () => boolean;
}
+
+export class DocButtonState {
+ @observable public StartLink: Doc | undefined = undefined; //origin's Doc, if defined
+ @observable public StartLinkView: DocumentView | undefined = undefined;
+ @observable public AnnotationId: string | undefined = undefined;
+ @observable public AnnotationUri: string | undefined = undefined;
+ @observable public LinkEditorDocView: DocumentView | undefined = undefined;
+ public static _instance: DocButtonState | undefined;
+ public static get Instance() {
+ return DocButtonState._instance ?? (DocButtonState._instance = new DocButtonState());
+ }
+ constructor() {
+ makeObservable(this);
+ }
+}
@observer
-export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
+export class DocumentLinksButton extends ObservableReactComponent<DocumentLinksButtonProps> {
private _linkButton = React.createRef<HTMLDivElement>();
- @observable public static StartLink: Doc | undefined; //origin's Doc, if defined
- @observable public static StartLinkView: DocumentView | undefined;
- @observable public static AnnotationId: string | undefined;
- @observable public static AnnotationUri: string | undefined;
- @observable public static LinkEditorDocView: DocumentView | undefined;
+ public static get StartLink() { return DocButtonState.Instance.StartLink; } // prettier-ignore
+ public static set StartLink(value) { runInAction(() => (DocButtonState.Instance.StartLink = value)); } // prettier-ignore
+ @observable public static StartLinkView: DocumentView | undefined = undefined;
+ @observable public static AnnotationId: string | undefined = undefined;
+ @observable public static AnnotationUri: string | undefined = undefined;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
- @action
@undoBatch
onLinkButtonMoved = (e: PointerEvent) => {
- if (this.props.InMenu && this.props.StartLink) {
+ if (this._props.InMenu && this._props.StartLink) {
if (this._linkButton.current !== null) {
const linkDrag = UndoManager.StartBatch('Drag Link');
- this.props.View &&
- DragManager.StartLinkDrag(this._linkButton.current, this.props.View, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
+ this._props.View &&
+ DragManager.StartLinkDrag(this._linkButton.current, this._props.View, this._props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
dragComplete: dropEv => {
- if (this.props.View && dropEv.linkDocument) {
+ if (this._props.View && dropEv.linkDocument) {
// dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
!dropEv.linkDocument.link_relationship && (Doc.GetProto(dropEv.linkDocument).link_relationship = 'hyperlink');
}
@@ -69,11 +87,11 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
this.onLinkButtonMoved,
emptyFunction,
action((e, doubleTap) => {
- doubleTap && DocumentView.showBackLinks(this.props.View.rootDoc);
+ doubleTap && DocumentView.showBackLinks(this._props.View.Document);
}),
undefined,
undefined,
- action(() => (DocumentLinksButton.LinkEditorDocView = this.props.View))
+ action(() => (DocButtonState.Instance.LinkEditorDocView = this._props.View))
);
};
@@ -84,33 +102,32 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
this.onLinkButtonMoved,
emptyFunction,
action((e, doubleTap) => {
- if (doubleTap && this.props.InMenu && this.props.StartLink) {
- //action(() => Doc.BrushDoc(this.props.View.Document));
- if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
+ if (doubleTap && this._props.InMenu && this._props.StartLink) {
+ //action(() => Doc.BrushDoc(this._props.View.Document));
+ if (DocumentLinksButton.StartLink === this._props.View.Document) {
DocumentLinksButton.StartLink = undefined;
DocumentLinksButton.StartLinkView = undefined;
} else {
- DocumentLinksButton.StartLink = this.props.View.props.Document;
- DocumentLinksButton.StartLinkView = this.props.View;
+ DocumentLinksButton.StartLink = this._props.View.Document;
+ DocumentLinksButton.StartLinkView = this._props.View;
}
}
})
);
};
- @action
@undoBatch
onLinkClick = (e: React.MouseEvent): void => {
- if (this.props.InMenu && this.props.StartLink) {
+ if (this._props.InMenu && this._props.StartLink) {
DocumentLinksButton.AnnotationId = undefined;
DocumentLinksButton.AnnotationUri = undefined;
- if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
+ if (DocumentLinksButton.StartLink === this._props.View.Document) {
DocumentLinksButton.StartLink = undefined;
DocumentLinksButton.StartLinkView = undefined;
} else {
//if this LinkButton's Document is undefined
- DocumentLinksButton.StartLink = this.props.View.props.Document;
- DocumentLinksButton.StartLinkView = this.props.View;
+ DocumentLinksButton.StartLink = this._props.View.Document;
+ DocumentLinksButton.StartLinkView = this._props.View;
}
}
};
@@ -121,7 +138,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
e,
returnFalse,
emptyFunction,
- action(e => DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View))
+ action(e => DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this._props.View.Document, true, this._props.View))
);
};
@@ -133,7 +150,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
DocumentLinksButton.StartLinkView = undefined;
DocumentLinksButton.AnnotationId = undefined;
DocumentLinksButton.AnnotationUri = undefined;
- //!this.props.StartLink
+ //!this._props.StartLink
} else if (startLink !== endLink) {
endLink = endLinkView?.docView?._componentView?.getAnchor?.(true, pinProps) || endLink;
startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.(true) || startLink;
@@ -190,8 +207,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
@computed get filteredLinks() {
const results = [] as Doc[];
- const filters = this.props.View.props.childFilters();
- Array.from(new Set<Doc>(this.props.View.allLinks)).forEach(link => {
+ const filters = this._props.View._props.childFilters();
+ Array.from(new Set<Doc>(this._props.View.allLinks)).forEach(link => {
if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.link_anchor_2 as Doc], filters, []).length || DocUtils.FilterDocs([link.link_anchor_1 as Doc], filters, []).length) {
results.push(link);
}
@@ -206,8 +223,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
*/
@computed get linkButtonInner() {
const btnDim = 30;
- const isActive = DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.StartLink;
- const scaling = Math.min(1, this.props.scaling?.() || 1);
+ const isActive = DocumentLinksButton.StartLink === this._props.View.Document && this._props.StartLink;
+ const scaling = Math.min(1, this._props.scaling?.() || 1);
const showLinkCount = (onHover?: boolean, offset?: boolean) => (
<div
className="documentLinksButton-showCount"
@@ -221,16 +238,16 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
<span style={{ width: '100%', display: 'inline-block', textAlign: 'center' }}>{Array.from(this.filteredLinks).length}</span>
</div>
);
- return this.props.ShowCount ? (
- showLinkCount(this.props.OnHover, this.props.Bottom)
+ return this._props.ShowCount ? (
+ showLinkCount(this._props.OnHover, this._props.Bottom)
) : (
<div className="documentLinksButton-menu">
- {this.props.StartLink ? ( //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
+ {this._props.StartLink ? ( //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
<div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton} onPointerDown={isActive ? StopEvent : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
<FontAwesomeIcon className="documentdecorations-icon" icon="link" />
</div>
) : null}
- {!this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? ( //if the origin node is not this node
+ {!this._props.StartLink && DocumentLinksButton.StartLink !== this._props.View.Document ? ( //if the origin node is not this node
<div className={'documentLinksButton-endLink'} ref={this._linkButton} onPointerDown={DocumentLinksButton.StartLink && this.completeLink}>
<FontAwesomeIcon className="documentdecorations-icon" icon="link" />
</div>
@@ -240,21 +257,21 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
}
render() {
- if (this.props.hideCount?.()) return null;
- const menuTitle = this.props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link';
+ if (this._props.hideCount?.()) return null;
+ const menuTitle = this._props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link';
const buttonTitle = 'Tap to view links; double tap to open link collection';
- const title = this.props.ShowCount ? buttonTitle : menuTitle;
+ const title = this._props.ShowCount ? buttonTitle : menuTitle;
//render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu
- return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? null : (
+ return !Array.from(this.filteredLinks).length && !this._props.AlwaysOn ? null : (
<div
className="documentLinksButton-wrapper"
style={{
- position: this.props.InMenu ? 'relative' : 'absolute',
+ position: this._props.InMenu ? 'relative' : 'absolute',
top: 0,
pointerEvents: 'none',
}}>
- {DocumentLinksButton.LinkEditorDocView ? this.linkButtonInner : <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>}
+ {DocButtonState.Instance.LinkEditorDocView ? this.linkButtonInner : <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>}
</div>
);
}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 406a1b8fb..c4dab16fb 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables';
+@import '../global/globalCssVariables.module.scss';
.documentView-effectsWrapper {
border-radius: inherit;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6e50a6b0f..ab413e6f2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,11 +1,13 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { Dropdown, DropdownType, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { IReactionDisposer, action, computed, makeObservable, 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 { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
+import * as React from 'react';
+import { Utils, emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick } from '../../../Utils';
import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
-import { AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols';
+import { AclPrivate, Animation, AudioPlay, DocViews } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -15,12 +17,11 @@ import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { AudioField } from '../../../fields/URLField';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
-import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
-import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { DocServer } from '../../DocServer';
-import { DocOptions, Docs, DocUtils, FInfo } from '../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { Networking } from '../../Network';
+import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocOptions, DocUtils, Docs, FInfo } from '../../documents/Documents';
import { DictationManager } from '../../util/DictationManager';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType } from '../../util/DragManager';
@@ -32,13 +33,14 @@ import { SettingsManager } from '../../util/SettingsManager';
import { SharingManager } from '../../util/SharingManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { UndoManager, undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from '../DocComponent';
import { EditableView } from '../EditableView';
import { GestureOverlay } from '../GestureOverlay';
import { LightboxView } from '../LightboxView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
import { StyleProp } from '../StyleProvider';
import { UndoStack } from '../UndoStack';
import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
@@ -46,12 +48,11 @@ import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView'
import { DocumentLinksButton } from './DocumentLinksButton';
import './DocumentView.scss';
import { FieldViewProps } from './FieldView';
-import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { KeyValueBox } from './KeyValueBox';
import { LinkAnchorBox } from './LinkAnchorBox';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { PresEffect, PresEffectDirection } from './trails';
import { PinProps, PresBox } from './trails/PresBox';
-import React = require('react');
const { Howl } = require('howler');
interface Window {
@@ -109,8 +110,10 @@ export interface DocFocusOptions {
easeFunc?: 'linear' | 'ease'; // transition method for scrolling
}
export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => Opt<number>;
-export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
+export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => any;
export interface DocComponentView {
+ fieldKey?: string;
+ annotationKey?: string;
updateIcon?: () => void; // updates the icon representation of the document
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;
@@ -119,10 +122,8 @@ export interface DocComponentView {
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)
- ignoreNativeDimScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
select?: (ctrlKey: boolean, shiftKey: boolean) => void;
focus?: (textAnchor: Doc, options: DocFocusOptions) => Opt<number>;
- menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
isAnyChildContentActive?: () => boolean; // is any child content of the document active
onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected
getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
@@ -136,11 +137,7 @@ export interface DocComponentView {
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)
- fieldKey?: string;
- annotationKey?: string;
- getTitle?: () => string;
+ infoUI?: () => JSX.Element | null;
getCenter?: (xf: Transform) => { 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 };
@@ -150,33 +147,28 @@ export interface DocComponentView {
}
// These props are passed to both FieldViews and DocumentViews
export interface DocumentViewSharedProps {
- fieldKey?: string; // only used by FieldViews but helpful here to allow styleProviders to access fieldKey of FieldViewProps. In priniciple, passing a fieldKey to a documentView could override or be the default fieldKey for fieldViews
- DocumentView?: () => DocumentView;
renderDepth: number;
Document: Doc;
- DataDoc?: Doc;
+ TemplateDataDocument?: Doc;
+ scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
+ DocumentView?: () => DocumentView;
+ CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
isGroupActive?: () => string | undefined; // is this document part of a group that is active
- suppressSetHeight?: boolean;
setContentView?: (view: DocComponentView) => any;
- CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
PanelWidth: () => number;
PanelHeight: () => number;
- shouldNotScale?: () => boolean;
docViewPath: () => DocumentView[];
- 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.
+ childFilters: () => string[];
+ childFiltersByRanges: () => string[];
styleProvider: Opt<StyleProviderFunc>;
setTitleFocus?: () => void;
focus: DocFocusFunc;
layout_fitWidth?: (doc: Doc) => boolean | undefined;
- childFilters: () => string[];
- childFiltersByRanges: () => string[];
searchFilterDocs: () => Doc[];
layout_showTitle?: () => string;
whenChildContentsActiveChanged: (isActive: boolean) => void;
- rootSelected: () => 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;
@@ -185,26 +177,27 @@ export interface DocumentViewSharedProps {
pinToPres: (document: Doc, pinProps: PinProps) => void;
ScreenToLocalTransform: () => Transform;
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
- dragAction?: dropActionType;
+ waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
+ defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
+ pointerEvents?: () => Opt<string>;
treeViewDoc?: Doc;
xPadding?: number;
yPadding?: number;
- dropAction?: dropActionType;
dontRegisterView?: boolean;
+ 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.
+ dropAction?: dropActionType;
+ dragAction?: dropActionType;
dragWhenActive?: boolean;
+ dontHideOnDrag?: boolean;
hideLinkButton?: boolean;
hideCaptions?: boolean;
ignoreAutoHeight?: boolean;
forceAutoHeight?: boolean;
+ suppressSetHeight?: boolean;
disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
- waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
- defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
- pointerEvents?: () => Opt<string>;
- scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
- createNewFilterDoc?: () => void;
- updateFilterDoc?: (doc: Doc) => void;
- dontHideOnDrag?: boolean;
}
// these props are specific to DocuentViews
@@ -218,12 +211,11 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
hideOpenButton?: boolean;
hideDeleteButton?: boolean;
hideLinkAnchors?: boolean;
- isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
- isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
contentPointerEvents?: 'none' | 'all' | undefined; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
- radialMenu?: String[];
LayoutTemplateString?: string;
dontCenter?: 'x' | 'y' | 'xy';
+ isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
+ isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
NativeWidth?: () => number;
NativeHeight?: () => number;
NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to FieldViewProps
@@ -241,8 +233,6 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
// these props are only available in DocumentViewIntenral
export interface DocumentViewInternalProps extends DocumentViewProps {
- NativeWidth: () => number;
- NativeHeight: () => number;
isSelected: () => boolean;
select: (ctrlPressed: boolean, shiftPress?: boolean) => void;
DocumentView: () => DocumentView;
@@ -264,6 +254,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
private _mainCont = React.createRef<HTMLDivElement>();
private _titleRef = React.createRef<EditableView>();
private _dropDisposer?: DragManager.DragDropDisposer;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
@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
@@ -273,7 +267,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
return this._animateScaleTime ?? 100;
}
public get displayName() {
- return 'DocumentViewInternal(' + this.props.Document.title + ')';
+ return 'DocumentViewInternal(' + this._props.Document.title + ')';
} // this makes mobx trace() statements more descriptive
public get ContentDiv() {
@@ -283,57 +277,51 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
return Doc.LayoutFieldKey(this.layoutDoc);
}
@computed get layout_showTitle() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.ShowTitle) as Opt<string>;
}
@computed get NativeDimScaling() {
- return this.props.NativeDimScaling?.() || 1;
+ return this._props.NativeDimScaling?.() || 1;
}
@computed get thumb() {
return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png');
}
@computed get opacity() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Opacity);
}
@computed get boxShadow() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow);
}
@computed get borderRounding() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BorderRounding);
}
@computed get widgetDecorations() {
TraceMobx();
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Decorations);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Decorations);
}
@computed get backgroundBoxColor() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box');
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor + ':box');
}
@computed get docContents() {
- return this.props.styleProvider?.(this.Document, this.props, StyleProp.DocContents);
+ return this._props.styleProvider?.(this.Document, this._props, StyleProp.DocContents);
}
@computed get headerMargin() {
- return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
+ return this._props?.styleProvider?.(this.layoutDoc, this._props, StyleProp.HeaderMargin) || 0;
}
@computed get layout_showCaption() {
- return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.ShowCaption) || 0;
+ return this._props?.styleProvider?.(this.layoutDoc, this._props, StyleProp.ShowCaption) || 0;
}
@computed get titleHeight() {
- return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
+ return this._props?.styleProvider?.(this.layoutDoc, this._props, StyleProp.TitleHeight) || 0;
}
- @observable _pointerEvents: 'none' | 'all' | 'visiblePainted' | undefined;
+ @observable _pointerEvents: 'none' | 'all' | 'visiblePainted' | undefined = undefined;
@computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
return this._pointerEvents;
}
@computed get finalLayoutKey() {
return StrCast(this.Document.layout_fieldKey, 'layout');
}
- @computed get nativeWidth() {
- return this.props.NativeWidth();
- }
- @computed get nativeHeight() {
- return this.props.NativeHeight();
- }
@computed get disableClickScriptFunc() {
- const onScriptDisable = this.props.onClickScriptDisable ?? this._componentView?.onClickScriptDisable?.() ?? this.layoutDoc.onClickScriptDisable;
+ const onScriptDisable = this._props.onClickScriptDisable ?? this._componentView?.onClickScriptDisable?.() ?? this.layoutDoc.onClickScriptDisable;
// prettier-ignore
return (
DocumentView.LongPress ||
@@ -342,49 +330,49 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
);
}
@computed get onClickHandler() {
- return this.props.onClick?.() ?? this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null));
+ return this._props.onClick?.() ?? this._props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null));
}
@computed get onDoubleClickHandler() {
- return this.props.onDoubleClick?.() ?? Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick;
+ return this._props.onDoubleClick?.() ?? Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick;
}
@computed get onPointerDownHandler() {
- return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown);
+ return this._props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown);
}
@computed get onPointerUpHandler() {
- return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp);
+ return this._props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp);
}
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;
+ runInAction(() => (this._mounted = true));
this.setupHandlers();
this._disposers.contentActive = reaction(
() => {
// true - if the document has been activated directly or indirectly (by having its children selected)
// false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive
// undefined - it is not active, but it should be responsive to actions that might activate it or its contents (eg clicking)
- return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none'
+ return this._props.isContentActive() === false || this._props.pointerEvents?.() === 'none'
? false
- : Doc.ActiveTool !== InkTool.None || SnappingManager.GetCanEmbed() || this.rootSelected() || this.Document.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive()
- ? true
- : undefined;
+ : Doc.ActiveTool !== InkTool.None || SnappingManager.CanEmbed || this.rootSelected() || this.Document.forceActive || this._componentView?.isAnyChildContentActive?.() || this._props.isContentActive()
+ ? true
+ : undefined;
},
active => (this._isContentActive = active),
{ fireImmediately: true }
);
this._disposers.pointerevents = reaction(
- () => this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents),
+ () => this._props.styleProvider?.(this.Document, this._props, StyleProp.PointerEvents),
pointerevents => (this._pointerEvents = pointerevents),
{ fireImmediately: true }
);
}
preDropFunc = (e: Event, de: DragManager.DropEvent) => {
const dropAction = this.layoutDoc.dropAction as dropActionType;
- if (de.complete.docDragData && this.isContentActive() && !this.props.treeViewDoc) {
+ if (de.complete.docDragData && this.isContentActive() && !this._props.treeViewDoc) {
dropAction && (de.complete.docDragData.dropAction = dropAction);
e.stopPropagation();
}
@@ -392,38 +380,38 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
setupHandlers() {
this.cleanupHandlers(false);
if (this._mainCont.current) {
- this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document, this.preDropFunc);
+ this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this._props.Document, this.preDropFunc);
}
}
- @action
+
cleanupHandlers(unbrush: boolean) {
this._dropDisposer?.();
- unbrush && Doc.UnBrushDoc(this.props.Document);
+ unbrush && Doc.UnBrushDoc(this._props.Document);
Object.values(this._disposers).forEach(disposer => disposer?.());
}
startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
if (this._mainCont.current) {
- const views = SelectionManager.Views().filter(dv => dv.docView?._mainCont.current);
- const selected = views.length > 1 && views.some(dv => dv.Document === this.Document) ? views : [this.props.DocumentView()];
+ const views = SelectionManager.Views.filter(dv => dv.docView?._mainCont.current);
+ const selected = views.length > 1 && views.some(dv => dv.Document === this.Document) ? views : [this._props.DocumentView()];
const dragData = new DragManager.DocumentDragData(selected.map(dv => dv.Document));
- const [left, top] = this.props.ScreenToLocalTransform().scale(this.NativeDimScaling).inverse().transformPoint(0, 0);
- dragData.offset = this.props
+ const [left, top] = this._props.ScreenToLocalTransform().scale(this.NativeDimScaling).inverse().transformPoint(0, 0);
+ dragData.offset = this._props
.ScreenToLocalTransform()
.scale(this.NativeDimScaling)
.transformDirection(x - left, y - top);
dragData.dropAction = dropAction;
- dragData.treeViewDoc = this.props.treeViewDoc;
- dragData.removeDocument = this.props.removeDocument;
- dragData.moveDocument = this.props.moveDocument;
- dragData.draggedViews = [this.props.DocumentView()];
- dragData.canEmbed = this.Document.dragAction ?? this.props.dragAction ? true : false;
+ dragData.treeViewDoc = this._props.treeViewDoc;
+ dragData.removeDocument = this._props.removeDocument;
+ dragData.moveDocument = this._props.moveDocument;
+ dragData.draggedViews = [this._props.DocumentView()];
+ dragData.canEmbed = this.Document.dragAction ?? this._props.dragAction ? true : false;
DragManager.StartDocumentDrag(
selected.map(dv => dv.docView!._mainCont.current!),
dragData,
x,
y,
- { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) }
+ { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this._props.dontHideOnDrag) }
); // this needs to happen after the drop event is processed.
}
}
@@ -446,28 +434,28 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
public static addDocTabFunc: (doc: Doc, location: OpenWhere) => boolean = returnFalse;
onClick = action((e: React.MouseEvent | React.PointerEvent) => {
- if (this.props.isGroupActive?.() === 'child' && !this.props.isDocumentActive?.()) return;
- if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
+ if (this._props.isGroupActive?.() === 'child' && !this._props.isDocumentActive?.()) return;
+ if (!this.Document.ignoreClick && this._props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
let stopPropagate = true;
let preventDefault = true;
- !this.layoutDoc._keepZWhenDragged && this.props.bringToFront(this.Document);
+ !this.layoutDoc._keepZWhenDragged && this._props.bringToFront(this.Document);
if (this._doubleTap) {
- const defaultDblclick = this.props.defaultDoubleClick?.() || this.Document.defaultDoubleClick;
+ const defaultDblclick = this._props.defaultDoubleClick?.() || this.Document.defaultDoubleClick;
if (this.onDoubleClickHandler?.script) {
const { clientX, clientY, shiftKey, altKey, ctrlKey } = e; // or we could call e.persist() to capture variables
// prettier-ignore
const func = () => this.onDoubleClickHandler.script.run( {
this: this.Document,
- scriptContext: this.props.scriptContext,
- documentView: this.props.DocumentView(),
+ scriptContext: this._props.scriptContext,
+ documentView: this._props.DocumentView(),
clientX, clientY, altKey, shiftKey, ctrlKey,
value: undefined,
}, console.log );
- UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
+ UndoManager.RunInBatch(() => (func().result?.select === true ? this._props.select(false) : ''), 'on double click');
} else if (!Doc.IsSystem(this.Document) && (defaultDblclick === undefined || defaultDblclick === 'default')) {
UndoManager.RunInBatch(() => LightboxView.Instance.AddDocTab(this.Document, OpenWhere.lightbox), 'double tap');
SelectionManager.DeselectAll();
- Doc.UnBrushDoc(this.props.Document);
+ Doc.UnBrushDoc(this._props.Document);
} else {
this._singleClickFunc?.();
}
@@ -484,13 +472,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
// e.g., if this document is part of a labeled 'lightbox' container, then documents will be shown in place
// instead of in the global lightbox
const oldFunc = DocumentViewInternal.addDocTabFunc;
- DocumentViewInternal.addDocTabFunc = this.props.addDocTab;
+ DocumentViewInternal.addDocTabFunc = this._props.addDocTab;
this.onClickHandler?.script.run(
{
this: this.Document,
_readOnly_: false,
- scriptContext: this.props.scriptContext,
- documentView: this.props.DocumentView(),
+ scriptContext: this._props.scriptContext,
+ documentView: this._props.DocumentView(),
clientX,
clientY,
shiftKey,
@@ -499,14 +487,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
},
console.log
).result?.select === true
- ? this.props.select(false)
+ ? this._props.select(false)
: '';
DocumentViewInternal.addDocTabFunc = oldFunc;
};
clickFunc = () => UndoManager.RunInBatch(func, 'click ' + this.Document.title);
} else {
// onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
- if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
+ if ((this.layoutDoc.onDragStart || this._props.TemplateDataDocument) && !(e.ctrlKey || e.button > 0)) {
stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
}
preventDefault = false;
@@ -514,10 +502,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const sendToBack = e.altKey;
this._singleClickFunc =
// prettier-ignore
- clickFunc ?? (() => (sendToBack ? this.props.DocumentView().props.bringToFront(this.Document, true) :
+ clickFunc ?? (() => (sendToBack ? this._props.DocumentView()._props.bringToFront(this.Document, true) :
this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ??
- this.props.select(e.ctrlKey||e.shiftKey, e.metaKey)));
- const waitFordblclick = this.props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick;
+ this._props.select(e.ctrlKey||e.shiftKey, e.metaKey)));
+ const waitFordblclick = this._props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick;
if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') {
this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
this._doubleClickTimeout = setTimeout(this._singleClickFunc, 300);
@@ -531,40 +519,39 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
});
- @action
onPointerDown = (e: React.PointerEvent): void => {
- if (this.props.isGroupActive?.() === 'child' && !this.props.isDocumentActive?.()) return;
+ if (this._props.isGroupActive?.() === 'child' && !this._props.isDocumentActive?.()) return;
this._longPressSelector = setTimeout(() => {
if (DocumentView.LongPress) {
if (this.Document.undoIgnoreFields) {
runInAction(() => (UndoStack.HideInline = !UndoStack.HideInline));
} else {
- this.props.select(false);
+ this._props.select(false);
}
}
}, 1000);
- if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this.props.DocumentView();
+ if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this._props.DocumentView();
this._downX = e.clientX;
this._downY = e.clientY;
this._downTime = Date.now();
- if ((Doc.ActiveTool === InkTool.None || this.props.addDocTab === returnFalse) && !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) {
+ if ((Doc.ActiveTool === InkTool.None || this._props.addDocTab === returnFalse) && !(this._props.TemplateDataDocument && !(e.ctrlKey || e.button > 0))) {
// click events stop here if the document is active and no modes are overriding it
// if this is part of a template, let the event go up to the template root unless right/ctrl clicking
if (
// prettier-ignore
- (this.props.isDocumentActive?.() || this.props.isContentActive?.()) &&
- !this.props.onBrowseClick?.() &&
+ (this._props.isDocumentActive?.() || this._props.isContentActive?.()) &&
+ !this._props.onBrowseClick?.() &&
!this.Document.ignoreClick &&
e.button === 0 &&
!Doc.IsInMyOverlay(this.layoutDoc)
) {
e.stopPropagation();
// don't preventDefault anymore. Goldenlayout, PDF text selection and RTF text selection all need it to go though
- //if (this.props.isSelected(true) && this.Document.type !== DocumentType.PDF && this.layoutDoc._type_collection !== CollectionViewType.Docking) e.preventDefault();
+ //if (this._props.isSelected(true) && this.Document.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.layoutDoc._dragWhenActive, this.props.dragWhenActive))) {
+ if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || BoolCast(this.layoutDoc._dragWhenActive, this._props.dragWhenActive))) {
document.addEventListener('pointermove', this.onPointerMove);
}
}
@@ -572,14 +559,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
};
- @action
onPointerMove = (e: PointerEvent): void => {
if (e.buttons !== 1 || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) return;
if (!Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
this.cleanupPointerEvents();
this._longPressSelector && clearTimeout(this._longPressSelector);
- this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'embed') || ((this.Document.dragAction || this.props.dragAction || undefined) as dropActionType));
+ this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'embed') || ((this.Document.dragAction || this._props.dragAction || undefined) as dropActionType));
}
};
@@ -588,7 +574,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
document.removeEventListener('pointerup', this.onPointerUp);
};
- @action
onPointerUp = (e: PointerEvent): void => {
this.cleanupPointerEvents();
this._longPressSelector && clearTimeout(this._longPressSelector);
@@ -603,7 +588,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
};
@undoBatch
- @action
toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => {
const hadOnClick = this.Document.onClick;
this.noOnClick();
@@ -611,8 +595,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this.Document.waitForDoubleClickToClick = hadOnClick ? undefined : 'never';
};
@undoBatch
- @action
- followLinkOnClick = (): void => {
+ followLinkOnClick = () => {
this.Document.ignoreClick = false;
this.Document.onClick = FollowLinkScript();
this.Document.followLinkToggle = false;
@@ -620,12 +603,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this.Document.followLinkLocation = undefined;
};
@undoBatch
- noOnClick = (): void => {
+ noOnClick = () => {
this.Document.ignoreClick = false;
this.Document.onClick = Doc.GetProto(this.Document).onClick = undefined;
};
- @undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document);
+ @undoBatch deleteClicked = () => this._props.removeDocument?.(this._props.Document);
@undoBatch setToggleDetail = () =>
(this.Document.onClick = ScriptField.MakeScript(
`toggleDetail(documentView, "${StrCast(this.Document.layout_fieldKey)
@@ -635,10 +618,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
));
@undoBatch
- @action
drop = (e: Event, de: DragManager.DropEvent) => {
- if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return false;
- if (this.props.Document === Doc.ActiveDashboard) {
+ if (this._props.dontRegisterView || this._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return false;
+ if (this._props.Document === Doc.ActiveDashboard) {
e.stopPropagation();
e.preventDefault();
alert(
@@ -660,7 +642,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.DocumentView().CollectionFreeFormView?.addDocument(de.complete.linkDocument);
+ this._props.DocumentView().CollectionFreeFormView?.addDocument(de.complete.linkDocument);
}
}
e.stopPropagation();
@@ -671,18 +653,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
};
@undoBatch
- @action
makeIntoPortal = () => {
- const portalLink = this.allLinks.find(d => d.link_anchor_1 === this.props.Document && d.link_relationship === 'portal to:portal from');
+ const portalLink = this.allLinks.find(d => d.link_anchor_1 === this._props.Document && d.link_relationship === 'portal to:portal from');
if (!portalLink) {
DocUtils.MakeLink(
- this.props.Document,
+ this._props.Document,
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]',
+ title: StrCast(this._props.Document.title) + ' [Portal]',
}),
{ link_relationship: 'portal to:portal from' }
);
@@ -700,7 +681,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const batch = UndoManager.StartBatch('importing');
Doc.importDocument(input.files[0]).then(doc => {
if (doc instanceof Doc) {
- this.props.addDocTab(doc, OpenWhere.addRight);
+ this._props.addDocTab(doc, OpenWhere.addRight);
batch.end();
}
});
@@ -709,12 +690,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
input.click();
};
- @action
onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
if (e && this.layoutDoc._layout_hideContextMenu && Doc.noviceMode) {
e.preventDefault();
e.stopPropagation();
- //!this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
+ //!this._props.isSelected(true) && SelectionManager.SelectView(this._props.DocumentView(), false);
}
// the touch onContextMenu is button 0, the pointer onContextMenu is button 2
if (e) {
@@ -736,7 +716,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (e && !(e.nativeEvent as any).dash) {
const onDisplay = () => {
- if (this.Document.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.
+ if (this.Document.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')) {
@@ -747,33 +727,33 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
return;
}
- const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
+ const customScripts = Cast(this._props.Document.contextMenuScripts, listSpec(ScriptField), []);
StrListCast(this.Document.contextMenuLabels).forEach((label, i) =>
- cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.Document, scriptContext: this.props.scriptContext }), icon: 'sticky-note' })
+ cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.Document, scriptContext: this._props.scriptContext }), icon: 'sticky-note' })
);
- this.props.contextMenuItems?.().forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.Document, scriptContext: this.props.scriptContext }), icon: item.icon as IconProp }));
+ this._props.contextMenuItems?.().forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.Document, scriptContext: this._props.scriptContext }), icon: item.icon as IconProp }));
- if (!this.props.Document.isFolder) {
- const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layout_fieldKey)], Doc, null);
+ if (!this._props.Document.isFolder) {
+ const templateDoc = Cast(this._props.Document[StrCast(this._props.Document.layout_fieldKey)], Doc, null);
const appearance = cm.findByDescription('Appearance...');
const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
- if (this.props.renderDepth === 0) {
+ if (this._props.renderDepth === 0) {
appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => LightboxView.Instance.SetLightboxDoc(this.Document), icon: 'external-link-alt' });
}
- appearanceItems.push({ description: 'Pin', event: () => this.props.pinToPres(this.Document, {}), icon: 'eye' });
- !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
+ appearanceItems.push({ description: 'Pin', event: () => this._props.pinToPres(this.Document, {}), 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: 'Appearance...', subitems: appearanceItems, icon: 'compass' });
if (!Doc.IsSystem(this.Document) && this.Document.type !== DocumentType.PRES && ![CollectionViewType.Docking, CollectionViewType.Tree].includes(this.Document._type_collection as any)) {
const existingOnClick = cm.findByDescription('OnClick...');
const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
- if (this.props.bringToFront !== emptyFunction) {
+ if (this._props.bringToFront !== emptyFunction) {
const zorders = cm.findByDescription('ZOrder...');
const zorderItems: ContextMenuProps[] = zorders && 'subitems' in zorders ? zorders.subitems : [];
- zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.Document, false)), icon: 'arrow-up' });
- zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.Document, true)), icon: 'arrow-down' });
+ zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront(dv.Document, false)), icon: 'arrow-up' });
+ zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront(dv.Document, true)), icon: 'arrow-down' });
zorderItems.push({
description: !this.layoutDoc._keepZDragged ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged',
event: undoBatch(action(() => (this.layoutDoc._keepZWhenDragged = !this.layoutDoc._keepZWhenDragged))),
@@ -785,14 +765,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
onClicks.push({ description: 'Enter Portal', event: this.makeIntoPortal, icon: 'window-restore' });
!Doc.noviceMode && onClicks.push({ description: 'Toggle Detail', event: this.setToggleDetail, icon: 'concierge-bell' });
- if (!this.props.treeViewDoc) {
+ if (!this._props.treeViewDoc) {
if (!this.Document.annotationOn) {
const options = cm.findByDescription('Options...');
const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
onClicks.push({ description: this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' });
- !Doc.noviceMode && onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
+ !Doc.noviceMode && onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this._props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
!existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
} else if (LinkManager.Links(this.Document).length) {
onClicks.push({ description: 'Restore On Click default', event: () => this.noOnClick(), icon: 'link' });
@@ -814,15 +794,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const moreItems = more && 'subitems' in more ? more.subitems : [];
if (!Doc.IsSystem(this.Document)) {
if (!Doc.noviceMode) {
- moreItems.push({ description: 'Make View of Metadata Field', event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: 'concierge-bell' });
+ moreItems.push({ description: 'Make View of Metadata Field', event: () => Doc.MakeMetadataFieldTemplate(this._props.Document, this._props.TemplateDataDocument), icon: 'concierge-bell' });
moreItems.push({ description: `${this.Document._chromeHidden ? 'Show' : 'Hide'} Chrome`, event: () => (this.Document._chromeHidden = !this.Document._chromeHidden), icon: 'project-diagram' });
- if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
- moreItems.push({ description: 'Export to Google Photos Album', event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: 'caret-square-right' });
- moreItems.push({ description: 'Tag Child Images via Google Photos', event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: 'caret-square-right' });
- moreItems.push({ description: 'Write Back Link to Album', event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: 'caret-square-right' });
+ if (Cast(Doc.GetProto(this._props.Document).data, listSpec(Doc))) {
+ moreItems.push({ description: 'Export to Google Photos Album', event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this._props.Document }).then(console.log), icon: 'caret-square-right' });
+ moreItems.push({ description: 'Tag Child Images via Google Photos', event: () => GooglePhotos.Query.TagChildImages(this._props.Document), icon: 'caret-square-right' });
+ moreItems.push({ description: 'Write Back Link to Album', event: () => GooglePhotos.Transactions.AddTextEnrichment(this._props.Document), icon: 'caret-square-right' });
}
- moreItems.push({ description: 'Copy ID', event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: 'fingerprint' });
+ moreItems.push({ description: 'Copy ID', event: () => Utils.CopyText(Doc.globalServerPath(this._props.Document)), icon: 'fingerprint' });
}
}
@@ -830,25 +810,25 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
const constantItems: ContextMenuProps[] = [];
if (!Doc.IsSystem(this.Document) && this.Document._type_collection !== CollectionViewType.Docking) {
- constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
- (this.Document._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
- if (this.props.removeDocument && Doc.ActiveDashboard !== this.props.Document) {
+ constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this._props.Document) });
+ (this.Document._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this._props.DocumentView()), icon: 'users' });
+ if (this._props.removeDocument && Doc.ActiveDashboard !== this._props.Document) {
// need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
}
}
- constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, OpenWhere.addRightKeyvalue), 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...');
const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
- !Doc.noviceMode && helpItems.push({ description: 'Text Shortcuts Ctrl+/', event: () => this.props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'keyboard' });
+ !Doc.noviceMode && helpItems.push({ description: 'Text Shortcuts Ctrl+/', event: () => this._props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'keyboard' });
!Doc.noviceMode && helpItems.push({ description: 'Print Document in Console', event: () => console.log(this.Document), icon: 'hand-point-right' });
!Doc.noviceMode && helpItems.push({ description: 'Print DataDoc in Console', event: () => console.log(this.dataDoc), icon: 'hand-point-right' });
let documentationDescription: string | undefined = undefined;
let documentationLink: string | undefined = undefined;
- switch (this.props.Document.type) {
+ switch (this._props.Document.type) {
case DocumentType.COL:
documentationDescription = 'See collection documentation';
documentationLink = 'https://brown-dash.github.io/Dash-Documentation/views/';
@@ -883,7 +863,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
break;
}
// Add link to help documentation
- if (!this.props.treeViewDoc && documentationDescription && documentationLink) {
+ if (!this._props.treeViewDoc && documentationDescription && documentationLink) {
helpItems.push({
description: documentationDescription,
event: () => window.open(documentationLink, '_blank'),
@@ -898,26 +878,26 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
};
@computed get _rootSelected() {
- return this.props.isSelected() || BoolCast(this.Document.rootDocument && this.props.rootSelected?.());
+ return this._props.isSelected() || BoolCast(this._props.TemplateDataDocument && this._props.rootSelected?.());
}
rootSelected = () => this._rootSelected;
- panelHeight = () => this.props.PanelHeight() - this.headerMargin;
- screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
+ panelHeight = () => this._props.PanelHeight() - this.headerMargin;
+ screenToLocal = () => this._props.ScreenToLocalTransform().translate(0, -this.headerMargin);
onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
- setHeight = (height: number) => (this.layoutDoc._height = height);
+ setHeight = (height: number) => !this._props.suppressSetHeight && (this.layoutDoc._height = height);
setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
- @observable _isContentActive: boolean | undefined;
+ @observable _isContentActive: boolean | undefined = undefined;
isContentActive = (): boolean | undefined => this._isContentActive;
- childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
+ childFilters = () => [...this._props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
/// disable pointer events on content when there's an enabled onClick script (but not the browse script) and the contents aren't forced active, or if contents are marked inactive
@computed get _contentPointerEvents() {
TraceMobx();
- return this.props.contentPointerEvents ??
+ return this._props.contentPointerEvents ??
((!this.disableClickScriptFunc && //
this.onClickHandler &&
- !this.props.onBrowseClick?.() &&
+ !this._props.onBrowseClick?.() &&
this.isContentActive() !== true) ||
this.isContentActive() === false)
? 'none'
@@ -926,8 +906,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
contentPointerEvents = () => this._contentPointerEvents;
@computed get contents() {
TraceMobx();
- const isInk = this.layoutDoc._layout_isSvg && !this.props.LayoutTemplateString;
- const noBackground = this.Document.isGroup && !this.props.LayoutTemplateString?.includes(KeyValueBox.name) && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
+ const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
+ const noBackground = this.Document.isGroup && !this._props.LayoutTemplateString?.includes(KeyValueBox.name) && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
return (
<div
className="documentView-contentsView"
@@ -937,18 +917,18 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}}>
<DocumentContentsView
key={1}
- {...this.props}
+ {...this._props}
+ fieldKey=""
pointerEvents={this.contentPointerEvents}
- docViewPath={this.props.viewPath}
+ docViewPath={this._props.viewPath}
setContentView={this.setContentView}
childFilters={this.childFilters}
PanelHeight={this.panelHeight}
- setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
+ setHeight={this.setHeight}
isContentActive={this.isContentActive}
ScreenToLocalTransform={this.screenToLocal}
rootSelected={this.rootSelected}
onClick={this.onClickFunc}
- focus={this.props.focus}
setTitleFocus={this.setTitleFocus}
layout_fieldKey={this.finalLayoutKey}
/>
@@ -957,8 +937,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
);
}
- anchorPanelWidth = () => this.props.PanelWidth() || 1;
- anchorPanelHeight = () => this.props.PanelHeight() || 1;
+ anchorPanelWidth = () => this._props.PanelWidth() || 1;
+ anchorPanelHeight = () => this._props.PanelHeight() || 1;
anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
// prettier-ignore
switch (property.split(':')[0]) {
@@ -966,11 +946,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
case StyleProp.PointerEvents: return 'none';
case StyleProp.Highlighting: return undefined;
case StyleProp.Opacity: {
- const filtered = DocUtils.FilterDocs(this.directLinks, this.props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines);
+ const filtered = DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines);
return filtered.some(link => link._link_displayArrow) ? 0 : undefined;
}
}
- return this.props.styleProvider?.(doc, props, property);
+ return this._props.styleProvider?.(doc, props, property);
};
// We need to use allrelatedLinks to get not just links to the document as a whole, but links to
// anchors that are not rendered as DocumentViews (marked as 'layout_unrendered' with their 'annotationOn' set to this document). e.g.,
@@ -996,15 +976,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get allLinkEndpoints() {
// the small blue dots that mark the endpoints of links
TraceMobx();
- if (this._componentView instanceof KeyValueBox || this.props.hideLinkAnchors || this.layoutDoc.layout_hideLinkAnchors || this.props.dontRegisterView || this.layoutDoc.layout_unrendered) return null;
- const filtered = DocUtils.FilterDocs(this.directLinks, this.props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines);
+ if (this._componentView instanceof KeyValueBox || this._props.hideLinkAnchors || this.layoutDoc.layout_hideLinkAnchors || this._props.dontRegisterView || this.layoutDoc.layout_unrendered) return null;
+ const filtered = DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines);
return filtered.map(link => (
<div className="documentView-anchorCont" key={link[Id]}>
<DocumentView
- {...this.props}
+ {...this._props}
isContentActive={returnFalse}
Document={link}
- docViewPath={this.props.viewPath}
+ docViewPath={this._props.viewPath}
PanelWidth={this.anchorPanelWidth}
PanelHeight={this.anchorPanelHeight}
dontRegisterView={false}
@@ -1094,7 +1074,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
};
- captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
+ captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => this._props?.styleProvider?.(doc, props, property + ':caption');
@observable _changingTitleField = false;
@observable _dropDownInnerWidth = 0;
fieldsDropdown = (inputOptions: string[], dropdownWidth: number, placeholder: string, onChange: (val: string | number) => void, onClose: () => void) => {
@@ -1138,12 +1118,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
<div
className="documentView-captionWrapper"
style={{
- pointerEvents: this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
+ pointerEvents: this.Document.ignoreClick ? 'none' : this.isContentActive() || this._props.isDocumentActive?.() ? 'all' : undefined,
background: StrCast(this.layoutDoc._backgroundColor, 'rgba(0,0,0,0.2)'),
color: lightOrDark(StrCast(this.layoutDoc._backgroundColor, 'black')),
}}>
<FormattedTextBox
- {...this.props}
+ {...this._props}
yPadding={10}
xPadding={10}
fieldKey={this.layout_showCaption}
@@ -1151,7 +1131,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
dontRegisterView={true}
noSidebar={true}
dontScale={true}
- renderDepth={this.props.renderDepth}
+ renderDepth={this._props.renderDepth}
isContentActive={this.isContentActive}
/>
</div>
@@ -1177,7 +1157,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
width: 100 - sidebarWidthPercent + '%',
color: background === 'transparent' ? SettingsManager.userColor : lightOrDark(background),
background,
- pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
+ pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this._props.isDocumentActive?.() ? 'all' : undefined,
}}>
{!dropdownWidth
? null
@@ -1188,7 +1168,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
action((field: string | number) => {
if (this.layoutDoc.layout_showTitle) {
this.layoutDoc._layout_showTitle = field;
- } else if (!this.props.layout_showTitle) {
+ } else if (!this._props.layout_showTitle) {
Doc.UserDoc().layout_showTitle = field;
}
this._changingTitleField = false;
@@ -1216,7 +1196,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (input?.startsWith('#')) {
if (this.layoutDoc.layout_showTitle) {
this.layoutDoc._layout_showTitle = input?.substring(1);
- } else if (!this.props.layout_showTitle) {
+ } else if (!this._props.layout_showTitle) {
Doc.UserDoc().layout_showTitle = input?.substring(1) ?? 'author_date';
}
} else if (showTitle && !showTitle.includes('Date') && showTitle !== 'author') {
@@ -1228,7 +1208,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
</div>
</div>
);
- return this.props.hideTitle || (!showTitle && !this.layout_showCaption) ? (
+ return this._props.hideTitle || (!showTitle && !this.layout_showCaption) ? (
this.contents
) : (
<div className="documentView-styleWrapper">
@@ -1284,31 +1264,31 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
default:
case PresEffect.None: return renderDoc;
- case PresEffect.Zoom: return <Zoom {...effectProps}>{renderDoc}</Zoom>;
- case PresEffect.Fade: return <Fade {...effectProps}>{renderDoc}</Fade>;
- case PresEffect.Flip: return <Flip {...effectProps}>{renderDoc}</Flip>;
- case PresEffect.Rotate: return <Rotate {...effectProps}>{renderDoc}</Rotate>;
- case PresEffect.Bounce: return <Bounce {...effectProps}>{renderDoc}</Bounce>;
- case PresEffect.Roll: return <Roll {...effectProps}>{renderDoc}</Roll>;
- case PresEffect.Lightspeed: return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>;
+ // case PresEffect.Zoom: return <Zoom {...effectProps}>{renderDoc}</Zoom>;
+ // case PresEffect.Fade: return <Fade {...effectProps}>{renderDoc}</Fade>;
+ // case PresEffect.Flip: return <Flip {...effectProps}>{renderDoc}</Flip>;
+ // case PresEffect.Rotate: return <Rotate {...effectProps}>{renderDoc}</Rotate>;
+ // case PresEffect.Bounce: return <Bounce {...effectProps}>{renderDoc}</Bounce>;
+ // case PresEffect.Roll: return <Roll {...effectProps}>{renderDoc}</Roll>;
+ // case PresEffect.Lightspeed: return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>;
}
}
@computed get highlighting() {
- return this.props.styleProvider?.(this.Document, this.props, StyleProp.Highlighting);
+ return this._props.styleProvider?.(this.Document, this._props, StyleProp.Highlighting);
}
@computed get borderPath() {
- return this.props.styleProvider?.(this.Document, this.props, StyleProp.BorderPath);
+ return this._props.styleProvider?.(this.Document, this._props, StyleProp.BorderPath);
}
render() {
TraceMobx();
const highlighting = this.highlighting;
const borderPath = this.borderPath;
const boxShadow =
- this.props.treeViewDoc || !highlighting
+ this._props.treeViewDoc || !highlighting
? this.boxShadow
: highlighting && this.borderRounding && highlighting.highlightStyle !== 'dashed'
- ? `0 0 0 ${highlighting.highlightIndex}px ${highlighting.highlightColor}`
- : this.boxShadow || (this.Document.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined);
+ ? `0 0 0 ${highlighting.highlightIndex}px ${highlighting.highlightColor}`
+ : this.boxShadow || (this.Document.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined);
const renderDoc = this.renderDoc({
borderRadius: this.borderRounding,
outline: highlighting && !this.borderRounding && !highlighting.highlightStroke ? `${highlighting.highlightColor} ${highlighting.highlightStyle} ${highlighting.highlightIndex}px` : 'solid 0px',
@@ -1324,8 +1304,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
onContextMenu={this.onContextMenu}
onPointerDown={this.onPointerDown}
onClick={this.onClick}
- onPointerEnter={e => (!SnappingManager.GetIsDragging() || SnappingManager.GetCanEmbed()) && Doc.BrushDoc(this.Document)}
- onPointerOver={e => (!SnappingManager.GetIsDragging() || SnappingManager.GetCanEmbed()) && Doc.BrushDoc(this.Document)}
+ onPointerEnter={e => (!SnappingManager.IsDragging || SnappingManager.CanEmbed) && Doc.BrushDoc(this.Document)}
+ onPointerOver={e => (!SnappingManager.IsDragging || SnappingManager.CanEmbed) && Doc.BrushDoc(this.Document)}
onPointerLeave={e => !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.Document)}
style={{
borderRadius: this.borderRounding,
@@ -1341,14 +1321,20 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
@observer
-export class DocumentView extends React.Component<DocumentViewProps> {
+export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
public static ROOT_DIV = 'documentView-effectsWrapper';
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@observable _selected = false;
public get SELECTED() {
return this._selected;
}
public set SELECTED(val) {
- this._selected = val;
+ runInAction(() => (this._selected = val));
}
@observable public static Interacting = false;
@observable public static LongPress = false;
@@ -1357,14 +1343,14 @@ export class DocumentView extends React.Component<DocumentViewProps> {
@computed public static get exploreMode() {
return () => (DocumentView.ExploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined);
}
- @observable public docView: DocumentViewInternal | undefined | null;
+ @observable public docView: DocumentViewInternal | undefined | null = undefined;
@observable public textHtmlOverlay: Opt<string>;
@observable public textHtmlOverlayTime: Opt<number>;
@observable private _isHovering = false;
public htmlOverlayEffect: Opt<Doc>;
public get displayName() {
- return 'DocumentView(' + this.props.Document?.title + ')';
+ return 'DocumentView(' + this._props.Document?.title + ')';
} // this makes mobx trace() statements more descriptive
public ContentRef = React.createRef<HTMLDivElement>();
public ViewTimer: NodeJS.Timeout | undefined; // timer for res
@@ -1385,11 +1371,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
};
public setViewTransition = (transProp: string, timeInMs: number, afterTrans?: () => void, dataTrans = false) => {
this.layoutDoc._viewTransition = `${transProp} ${timeInMs}ms`;
- if (dataTrans) this.rootDoc._dataTransition = `${transProp} ${timeInMs}ms`;
+ if (dataTrans) this.Document._dataTransition = `${transProp} ${timeInMs}ms`;
this.ViewTimer && clearTimeout(this.ViewTimer);
return (this.ViewTimer = setTimeout(() => {
this.layoutDoc._viewTransition = undefined;
- this.rootDoc._dataTransition = 'inherit';
+ this.Document._dataTransition = 'inherit';
afterTrans?.();
}, timeInMs + 10));
};
@@ -1422,13 +1408,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
get Document() {
- return this.props.Document;
+ return this._props.Document;
}
get topMost() {
- return this.props.renderDepth === 0;
- }
- get rootDoc() {
- return this.Document;
+ return this._props.renderDepth === 0;
}
get dataDoc() {
return this.docView?.dataDoc ?? this.Document;
@@ -1440,38 +1423,38 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.docView?._componentView;
}
get allLinks() {
- return (this.docView?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.rootDoc);
+ return (this.docView?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document);
}
get LayoutFieldKey() {
return this.docView?.LayoutFieldKey || 'layout';
}
@computed get layout_fitWidth() {
- return this.docView?._componentView?.layout_fitWidth?.() ?? this.props.layout_fitWidth?.(this.layoutDoc) ?? this.layoutDoc?.layout_fitWidth;
+ return this._props.layout_fitWidth?.(this.layoutDoc) ?? this.layoutDoc?.layout_fitWidth;
}
@computed get anchorViewDoc() {
- return this.props.LayoutTemplateString?.includes('link_anchor_2') ? DocCast(this.Document['link_anchor_2']) : this.props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(this.Document['link_anchor_1']) : undefined;
+ return this._props.LayoutTemplateString?.includes('link_anchor_2') ? DocCast(this.Document['link_anchor_2']) : this._props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(this.Document['link_anchor_1']) : undefined;
}
@computed get hideLinkButton() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkBtn + (this.isSelected() ? ':selected' : ''));
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.HideLinkBtn + (this.SELECTED ? ':selected' : ''));
}
- hideLinkCount = () => this.props.renderDepth === -1 || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton;
+ hideLinkCount = () => this._props.renderDepth === -1 || (this.SELECTED && this._props.renderDepth) || !this._isHovering || this.hideLinkButton;
@computed get linkCountView() {
return <DocumentLinksButton hideCount={this.hideLinkCount} View={this} scaling={this.scaleToScreenSpace} OnHover={true} Bottom={this.topMost} ShowCount={true} />;
}
@computed get docViewPath(): DocumentView[] {
- return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this];
+ return this._props.docViewPath ? [...this._props.docViewPath(), this] : [this];
}
@computed get layoutDoc() {
- return Doc.Layout(this.Document, this.props.LayoutTemplate?.());
+ return Doc.Layout(this.Document, this._props.LayoutTemplate?.());
}
@computed get nativeWidth() {
- return this.docView?._componentView?.ignoreNativeDimScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
+ return this._props.LayoutTemplateString?.includes(KeyValueBox.name) ? 0 : returnVal(this._props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this._props.TemplateDataDocument, !this.layout_fitWidth));
}
@computed get nativeHeight() {
- return this.docView?._componentView?.ignoreNativeDimScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
+ return this._props.LayoutTemplateString?.includes(KeyValueBox.name) ? 0 : returnVal(this._props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this._props.TemplateDataDocument, !this.layout_fitWidth));
}
@computed get shouldNotScale() {
- return this.props.shouldNotScale?.() || (this.layout_fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._type_collection as any);
+ return (this.layout_fitWidth && !this.nativeWidth) || this._props.LayoutTemplateString?.includes(KeyValueBox.name) || [CollectionViewType.Docking].includes(this.Document._type_collection as any);
}
@computed get effectiveNativeWidth() {
return this.shouldNotScale ? 0 : this.nativeWidth || NumCast(this.layoutDoc.width);
@@ -1482,57 +1465,57 @@ export class DocumentView extends React.Component<DocumentViewProps> {
@computed get nativeScaling() {
if (this.shouldNotScale) return 1;
const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0;
- if (this.layout_fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) {
- return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or layout_fitWidth
+ if (this.layout_fitWidth || this._props.PanelHeight() / (this.effectiveNativeHeight || 1) > this._props.PanelWidth() / (this.effectiveNativeWidth || 1)) {
+ return Math.max(minTextScale, this._props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or layout_fitWidth
}
- return Math.max(minTextScale, this.props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled
+ return Math.max(minTextScale, this._props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled
}
@computed get panelWidth() {
- return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth();
+ return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this._props.PanelWidth();
}
@computed get panelHeight() {
if (this.effectiveNativeHeight && (!this.layout_fitWidth || !this.layoutDoc.layout_reflowVertical)) {
- return Math.min(this.props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling);
+ return Math.min(this._props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling);
}
- return this.props.PanelHeight();
+ return this._props.PanelHeight();
}
@computed get Xshift() {
- return this.effectiveNativeWidth ? Math.max(0, (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2) : 0;
+ return this.effectiveNativeWidth ? Math.max(0, (this._props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2) : 0;
}
@computed get Yshift() {
return this.effectiveNativeWidth &&
this.effectiveNativeHeight &&
Math.abs(this.Xshift) < 0.001 &&
- (!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)
+ (!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;
}
@computed get centeringX() {
- return this.props.dontCenter?.includes('x') ? 0 : this.Xshift;
+ return this._props.dontCenter?.includes('x') ? 0 : this.Xshift;
}
@computed get centeringY() {
- return this.props.dontCenter?.includes('y') ? 0 : this.Yshift;
+ return this._props.dontCenter?.includes('y') ? 0 : this.Yshift;
}
@computed get CollectionFreeFormView() {
return this.CollectionFreeFormDocumentView?.CollectionFreeFormView;
}
@computed get CollectionFreeFormDocumentView() {
- return this.props.CollectionFreeFormDocumentView?.();
+ return this._props.CollectionFreeFormDocumentView?.();
}
- public toggleNativeDimensions = () => this.docView && this.Document.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
+ public toggleNativeDimensions = () => this.docView && this.Document.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())) {
+ if (!this.docView?.ContentDiv || this._props.treeViewDoc || Doc.AreProtosEqual(this._props.Document, Doc.UserDoc())) {
return undefined;
}
if (this.docView._componentView?.screenBounds?.()) {
return this.docView._componentView.screenBounds();
}
- const xf = this.docView.props.ScreenToLocalTransform().scale(this.nativeScaling).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)) {
+ if (this.docView._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
const docuBox = this.docView.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), center: undefined };
}
@@ -1555,18 +1538,17 @@ export class DocumentView extends React.Component<DocumentViewProps> {
const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null);
this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finalFinished);
this.Document.deiconifyLayout = undefined;
- this.props.bringToFront(this.Document);
+ this._props.bringToFront(this.Document);
}
}
@undoBatch
- @action
setCustomView = (custom: boolean, layout: string): void => {
- Doc.setNativeView(this.props.Document);
- custom && DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
+ Doc.setNativeView(this._props.Document);
+ custom && DocUtils.makeCustomViewClicked(this._props.Document, Docs.Create.StackingDocument, layout, undefined);
};
- @action
+
switchViews = (custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
- this.docView && (this.docView._animateScalingTo = 0.1); // shrink doc
+ runInAction(() => this.docView && (this.docView._animateScalingTo = 0.1)); // shrink doc
setTimeout(
action(() => {
if (useExistingLayout && custom && this.Document['layout_' + view]) {
@@ -1588,11 +1570,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
};
layout_fitWidthFunc = (doc: Doc) => BoolCast(this.layout_fitWidth);
- scaleToScreenSpace = () => (1 / (this.props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale;
+ scaleToScreenSpace = () => (1 / (this._props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale;
docViewPathFunc = () => this.docViewPath;
isSelected = () => this.SELECTED;
select = (extendSelection: boolean, focusSelection?: boolean) => {
- if (this.isSelected() && SelectionManager.Views().length > 1) SelectionManager.DeselectView(this);
+ if (this.SELECTED && SelectionManager.Views.length > 1) SelectionManager.DeselectView(this);
else {
SelectionManager.SelectView(this, extendSelection);
if (focusSelection) {
@@ -1612,14 +1594,13 @@ export class DocumentView extends React.Component<DocumentViewProps> {
NativeDimScaling = () => this.nativeScaling;
selfView = () => this;
screenToLocalTransform = () =>
- this.props
+ this._props
.ScreenToLocalTransform()
.translate(-this.centeringX, -this.centeringY)
.scale(1 / this.nativeScaling);
- @action
componentDidMount() {
- this.Document[DocViews].add(this);
+ runInAction(() => this.Document[DocViews].add(this));
this._disposers.updateContentsScript = reaction(() => ScriptCast(this.Document.updateContentsScript)?.script?.run({ this: this.Document }).result, emptyFunction);
this._disposers.height = reaction(
// increase max auto height if document has been resized to be greater than current max
@@ -1629,13 +1610,13 @@ export class DocumentView extends React.Component<DocumentViewProps> {
if (docMax && docMax < height) this.layoutDoc.layout_maxAutoHeight = height;
})
);
- !BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.AddView(this);
+ !BoolCast(this._props.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.AddView(this);
}
- @action
+
componentWillUnmount() {
this.Document[DocViews].delete(this);
Object.values(this._disposers).forEach(disposer => disposer?.());
- !BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
+ !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'.
@@ -1666,28 +1647,31 @@ export class DocumentView extends React.Component<DocumentViewProps> {
);
}
+ @computed get infoUI() {
+ return this.ComponentView?.infoUI?.();
+ }
+
render() {
TraceMobx();
- const xshift = Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined;
- const yshift = Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined;
+ const xshift = Math.abs(this.Xshift) <= 0.001 ? this._props.PanelWidth() : undefined;
+ const yshift = Math.abs(this.Yshift) <= 0.001 ? this._props.PanelHeight() : undefined;
return (
<div className="contentFittingDocumentView" onPointerEnter={action(() => (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))}>
- {!this.props.Document || !this.props.PanelWidth() ? null : (
+ {!this._props.Document || !this._props.PanelWidth() ? null : (
<div
className="contentFittingDocumentView-previewDoc"
ref={this.ContentRef}
style={{
- transition: 'inherit', // this.props.dataTransition,
+ transition: 'inherit', // this._props.dataTransition,
transform: `translate(${this.centeringX}px, ${this.centeringY}px)`,
- width: xshift ?? `${this.props.PanelWidth() - this.Xshift * 2}px`,
- height: this.props.forceAutoHeight ? undefined : yshift ?? (this.layout_fitWidth ? `${this.panelHeight}px` : `${(this.effectiveNativeHeight / this.effectiveNativeWidth) * this.props.PanelWidth()}px`),
+ width: xshift ?? `${this._props.PanelWidth() - this.Xshift * 2}px`,
+ height: this._props.forceAutoHeight ? undefined : yshift ?? (this.layout_fitWidth ? `${this.panelHeight}px` : `${(this.effectiveNativeHeight / this.effectiveNativeWidth) * this._props.PanelWidth()}px`),
}}>
<DocumentViewInternal
- {...this.props}
+ {...this._props}
DocumentView={this.selfView}
viewPath={this.docViewPathFunc}
- shouldNotScale={this.ShouldNotScale}
PanelWidth={this.PanelWidth}
PanelHeight={this.PanelHeight}
NativeWidth={this.NativeWidth}
@@ -1697,10 +1681,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
select={this.select}
layout_fitWidth={this.layout_fitWidthFunc}
ScreenToLocalTransform={this.screenToLocalTransform}
- focus={this.props.focus || emptyFunction}
+ focus={this._props.focus || emptyFunction}
ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
/>
{this.htmlOverlay}
+ {this.infoUI}
</div>
)}
diff --git a/src/client/views/nodes/EquationBox.scss b/src/client/views/nodes/EquationBox.scss
index f5871db22..5009ec7a7 100644
--- a/src/client/views/nodes/EquationBox.scss
+++ b/src/client/views/nodes/EquationBox.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.scss';
+@import '../global/globalCssVariables.module.scss';
.equationBox-cont {
transform-origin: center;
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index cd263f82a..ff92c701f 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -1,5 +1,4 @@
-import EquationEditor from 'equation-editor-react';
-import { action, reaction } from 'mobx';
+import { action, makeObservable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Id } from '../../../fields/FieldSymbols';
@@ -11,6 +10,7 @@ import { ViewBoxBaseComponent } from '../DocComponent';
import { LightboxView } from '../LightboxView';
import './EquationBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
+import EquationEditor from './formattedText/EquationEditor';
@observer
export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -19,10 +19,16 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
public static SelectOnLoad: string = '';
_ref: React.RefObject<EquationEditor> = React.createRef();
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
componentDidMount() {
- this.props.setContentView?.(this);
- if (EquationBox.SelectOnLoad === this.Document[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
- this.props.select(false);
+ this._props.setContentView?.(this);
+ if (EquationBox.SelectOnLoad === this.Document[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath()))) {
+ this._props.select(false);
this._ref.current!.mathField.focus();
this.dataDoc.text === 'x' && this._ref.current!.mathField.select();
@@ -38,7 +44,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
//{ fireImmediately: true }
);
reaction(
- () => this.props.isSelected(),
+ () => this._props.isSelected(),
selected => {
if (this._ref.current) {
if (selected) this._ref.current.element.current.children[0].addEventListener('keydown', this.keyPressed, true);
@@ -62,7 +68,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
y: NumCast(this.layoutDoc.y) + _height + 10,
});
EquationBox.SelectOnLoad = nextEq[Id];
- this.props.addDocument?.(nextEq);
+ this._props.addDocument?.(nextEq);
e.stopPropagation();
}
if (e.key === 'Tab') {
@@ -73,10 +79,10 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
_height: 300,
backgroundColor: 'white',
});
- this.props.addDocument?.(graph);
+ this._props.addDocument?.(graph);
e.stopPropagation();
}
- if (e.key === 'Backspace' && !this.dataDoc.text) this.props.removeDocument?.(this.Document);
+ if (e.key === 'Backspace' && !this.dataDoc.text) this._props.removeDocument?.(this.Document);
};
@undoBatch
onChange = (str: string) => (this.dataDoc.text = str);
@@ -100,7 +106,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
render() {
TraceMobx();
- const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
+ const scale = (this._props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
return (
<div
ref={r => this.updateSize()}
@@ -110,7 +116,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
transform: `scale(${scale})`,
width: 'fit-content', // `${100 / scale}%`,
height: `${100 / scale}%`,
- pointerEvents: !this.props.isSelected() ? 'none' : undefined,
+ pointerEvents: !this._props.isSelected() ? 'none' : undefined,
fontSize: StrCast(this.layoutDoc._text_fontSize),
}}
onKeyDown={e => e.stopPropagation()}>
diff --git a/src/client/views/nodes/FaceRectangle.tsx b/src/client/views/nodes/FaceRectangle.tsx
index 20afa4565..8d03bf57a 100644
--- a/src/client/views/nodes/FaceRectangle.tsx
+++ b/src/client/views/nodes/FaceRectangle.tsx
@@ -1,14 +1,14 @@
-import React = require("react");
-import { observer } from "mobx-react";
-import { observable, runInAction } from "mobx";
-import { RectangleTemplate } from "./FaceRectangles";
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import { observable, runInAction } from 'mobx';
+import { RectangleTemplate } from './FaceRectangles';
@observer
export default class FaceRectangle extends React.Component<{ rectangle: RectangleTemplate }> {
@observable private opacity = 0;
componentDidMount() {
- setTimeout(() => runInAction(() => this.opacity = 1), 500);
+ setTimeout(() => runInAction(() => (this.opacity = 1)), 500);
}
render() {
@@ -18,12 +18,11 @@ export default class FaceRectangle extends React.Component<{ rectangle: Rectangl
style={{
...rectangle.style,
opacity: this.opacity,
- transition: "1s ease opacity",
- position: "absolute",
- borderRadius: 5
+ transition: '1s ease opacity',
+ position: 'absolute',
+ borderRadius: 5,
}}
/>
);
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx
index 0d1e063af..26e720c0d 100644
--- a/src/client/views/nodes/FaceRectangles.tsx
+++ b/src/client/views/nodes/FaceRectangles.tsx
@@ -1,9 +1,9 @@
-import React = require("react");
-import { Doc, DocListCast } from "../../../fields/Doc";
-import { Cast, NumCast } from "../../../fields/Types";
-import { observer } from "mobx-react";
-import { Id } from "../../../fields/FieldSymbols";
-import FaceRectangle from "./FaceRectangle";
+import * as React from 'react';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Cast, NumCast } from '../../../fields/Types';
+import { observer } from 'mobx-react';
+import { Id } from '../../../fields/FieldSymbols';
+import FaceRectangle from './FaceRectangle';
interface FaceRectanglesProps {
document: Doc;
@@ -18,7 +18,6 @@ export interface RectangleTemplate {
@observer
export class FaceRectangles extends React.Component<FaceRectanglesProps> {
-
render() {
const faces = DocListCast(this.props.document.faces);
const templates: RectangleTemplate[] = faces.map(faceDoc => {
@@ -33,14 +32,15 @@ export class FaceRectangles extends React.Component<FaceRectanglesProps> {
} as React.CSSProperties;
return {
id: rectangle[Id],
- style: style
+ style: style,
};
});
return (
<div>
- {templates.map(rectangle => <FaceRectangle key={rectangle.id} rectangle={rectangle} />)}
+ {templates.map(rectangle => (
+ <FaceRectangle key={rectangle.id} rectangle={rectangle} />
+ ))}
</div>
);
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index f7f94c546..f4c5167a5 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import { DateField } from '../../../fields/DateField';
@@ -16,7 +16,6 @@ import { DocumentViewSharedProps } from './DocumentView';
export interface FieldViewProps extends DocumentViewSharedProps {
// FieldView specific props that are not part of DocumentView props
fieldKey: string;
- scrollOverflow?: boolean; // bcz: would like to think this can be avoided -- need to look at further
select: (isCtrlPressed: boolean) => void;
isContentActive: (outsideReaction?: boolean) => boolean | undefined;
diff --git a/src/client/views/nodes/FontIconBox/ButtonInterface.ts b/src/client/views/nodes/FontIconBox/ButtonInterface.ts
index 0aa2ac8e1..1c034bfbe 100644
--- a/src/client/views/nodes/FontIconBox/ButtonInterface.ts
+++ b/src/client/views/nodes/FontIconBox/ButtonInterface.ts
@@ -1,12 +1,12 @@
-import { Doc } from "../../../../fields/Doc";
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { ButtonType } from "./FontIconBox";
+import { Doc } from '../../../../fields/Doc';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { ButtonType } from './FontIconBox';
export interface IButtonProps {
type: string | ButtonType;
- rootDoc: Doc;
+ Document: Doc;
label: any;
icon: IconProp;
color: string;
backgroundColor: string;
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.scss b/src/client/views/nodes/FontIconBox/FontIconBox.scss
index 9d9fa26b0..db2ffa756 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables';
+@import '../../global/globalCssVariables.module.scss';
.menuButton {
height: 100%;
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index ba5370360..5a8665aaf 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -1,7 +1,7 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { action, computed, observable } from 'mobx';
+import { computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
@@ -13,7 +13,7 @@ import { SelectionManager } from '../../../util/SelectionManager';
import { SettingsManager } from '../../../util/SettingsManager';
import { undoable, UndoManager } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
-import { DocComponent } from '../../DocComponent';
+import { ViewBoxBaseComponent } from '../../DocComponent';
import { EditableView } from '../../EditableView';
import { SelectedDocView } from '../../selectedDoc';
import { StyleProp } from '../../StyleProvider';
@@ -41,10 +41,15 @@ export interface ButtonProps extends FieldViewProps {
type?: ButtonType;
}
@observer
-export class FontIconBox extends DocComponent<ButtonProps>() {
+export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(FontIconBox, fieldKey);
}
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
//
// This controls whether fontIconButtons will display labels under their icons or not
//
@@ -54,10 +59,11 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
public static set ShowIconLabels(show: boolean) {
Doc.UserDoc()._showLabel = show;
}
+
@observable noTooltip = false;
showTemplate = (): void => {
const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
- dragFactory && this.props.addDocTab(dragFactory, OpenWhere.addRight);
+ dragFactory && this._props.addDocTab(dragFactory, OpenWhere.addRight);
};
dragAsTemplate = (): void => {
this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
@@ -130,8 +136,8 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
type = 'slider';
break;
}
- const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, value, _readOnly_: value === undefined });
- const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+ const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, self: this.Document, value, _readOnly_: value === undefined });
+ const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
// Script for checking the outcome of the toggle
const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)));
@@ -157,7 +163,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
this,
e,
(e: PointerEvent) => {
- return ScriptCast(this.Document.onDragScript)?.script.run({ this: this.Document, value: { doc: value, e } }).result;
+ return ScriptCast(this.Document.onDragScript)?.script.run({ this: this.Document, self: this.Document, value: { doc: value, e } }).result;
},
emptyFunction,
emptyFunction
@@ -177,7 +183,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
let icon: IconProp = 'caret-down';
const isViewDropdown = script?.script.originalScript.startsWith('setView');
if (isViewDropdown) {
- const selected = SelectionManager.Docs();
+ const selected = SelectionManager.Docs;
if (selected.lastElement()) {
if (StrCast(selected.lastElement().type) === DocumentType.COL) {
text = StrCast(selected.lastElement()._type_collection);
@@ -205,7 +211,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
}
noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Carousel3D, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
} else {
- text = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result;
+ text = script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result;
// text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
getStyle = (val: string) => ({ fontFamily: val });
}
@@ -223,7 +229,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
return (
<Dropdown
selectedVal={text}
- setSelectedVal={undoable(value => script.script.run({ this: this.Document, value }), `dropdown select ${this.label}`)}
+ setSelectedVal={undoable(value => script.script.run({ this: this.Document, self: this.Document, value }), `dropdown select ${this.label}`)}
color={SettingsManager.userColor}
background={SettingsManager.userVariantColor}
type={Type.TERT}
@@ -246,18 +252,18 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
* Color button
*/
@computed get colorButton() {
- const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
- const curColor = this.colorScript?.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result ?? 'transparent';
+ const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
+ const curColor = this.colorScript?.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result ?? 'transparent';
const tooltip: string = StrCast(this.Document.toolTip);
return (
<ColorPicker
setSelectedColor={value => {
if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
- this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+ this.colorScript?.script.run({ this: this.Document, self: this.Document, value: value, _readOnly_: false });
}}
setFinalColor={value => {
- this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+ this.colorScript?.script.run({ this: this.Document, self: this.Document, value: value, _readOnly_: false });
this.colorBatch?.end();
this.colorBatch = undefined;
}}
@@ -277,9 +283,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
const tooltip: string = StrCast(this.Document.toolTip);
const script = ScriptCast(this.Document.onClick);
- const toggleStatus = script ? script.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result : false;
+ const toggleStatus = script ? script.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result : false;
// Colors
- const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+ const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
const items = DocListCast(this.dataDoc.data);
return (
<MultiToggle
@@ -308,10 +314,10 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
const tooltip = StrCast(this.Document.toolTip);
const script = ScriptCast(this.Document.onClick);
- const toggleStatus = script ? script.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result : false;
+ const toggleStatus = script ? script.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result : false;
// Colors
- const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
- const backgroundColor = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor);
+ const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
+ const backgroundColor = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor);
return (
<Toggle
@@ -324,7 +330,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
//background={SettingsManager.userBackgroundColor}
icon={this.Icon(color)!}
label={this.label}
- onPointerDown={() => script.script.run({ this: this.Document, value: !toggleStatus, _readOnly_: false })}
+ onPointerDown={() => script.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false })}
/>
);
}
@@ -333,8 +339,8 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
* Default
*/
@computed get defaultButton() {
- const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
- const backgroundColor = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor);
+ const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
+ const backgroundColor = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor);
const tooltip: string = StrCast(this.Document.toolTip);
return <IconButton tooltip={tooltip} icon={this.Icon(color)!} label={this.label} />;
@@ -344,9 +350,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
// Script for running the toggle
const script = ScriptCast(this.Document.script);
// Function to run the script
- const checkResult = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result;
+ const checkResult = script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result;
- const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ this: this.Document, value, _readOnly_: false }).result;
+ const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ this: this.Document, self: this.Document, value, _readOnly_: false }).result;
return <EditableText editing={false} setEditing={(editing: boolean) => {}} />;
@@ -354,14 +360,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
<div className="menuButton editableText">
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} />
<div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}>
- <EditableView GetValue={() => script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
+ <EditableView GetValue={() => script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
</div>
</div>
);
}
render() {
- const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+ const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
const tooltip = StrCast(this.Document.toolTip);
const scriptFunc = () => ScriptCast(this.Document.onClick)?.script.run({ this: this.Document, self: this.Document, _readOnly_: false });
const btnProps = { tooltip, icon: this.Icon(color)!, label: this.label };
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index daf6cd9a6..c26579e66 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -1,27 +1,19 @@
import functionPlot from 'function-plot';
-import { action, computed, reaction } from 'mobx';
+import { computed, makeObservable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
-import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
-import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { listSpec } from '../../../fields/Schema';
import { Cast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
-import { DocFocusOptions, DocumentView } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { PinProps, PresBox } from './trails';
-const EquationSchema = createSchema({});
-
-type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSchema]>;
-const EquationDocument = makeInterface(EquationSchema, documentSchema);
-
@observer
export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
@@ -31,23 +23,23 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
_plot: any;
_plotId = '';
_plotEle: any;
+
constructor(props: any) {
super(props);
+ makeObservable(this);
this._plotId = 'graph' + FunctionPlotBox.GraphCount++;
}
+
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
reaction(
() => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.layoutDoc.xRange, this.layoutDoc.yRange],
() => this.createGraph()
);
}
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
- const anchor = Docs.Create.ConfigDocument({
- //
- annotationOn: this.rootDoc,
- });
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.rootDoc);
+ const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.Document);
anchor.config_xRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
anchor.config_yRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
if (addAsAnnotation) this.addDocument(anchor);
@@ -55,8 +47,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
};
createGraph = (ele?: HTMLDivElement) => {
this._plotEle = ele || this._plotEle;
- const width = this.props.PanelWidth();
- const height = this.props.PanelHeight();
+ const width = this._props.PanelWidth();
+ const height = this._props.PanelHeight();
const fns = DocListCast(this.dataDoc.data).map(doc => StrCast(doc.text, 'x^2').replace(/\\frac\{(.*)\}\{(.*)\}/, '($1/$2)'));
try {
this._plotEle.children.length && this._plotEle.removeChild(this._plotEle.children[0]);
@@ -80,7 +72,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
@undoBatch
drop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.docDragData?.droppedDocuments.length) {
- const added = de.complete.docDragData.droppedDocuments.reduce((res, doc) => res && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, doc), true);
+ const added = de.complete.docDragData.droppedDocuments.reduce((res, doc) => res && Doc.AddDocToList(this.dataDoc, this._props.fieldKey, doc), true);
!added && e.preventDefault();
e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
return added;
@@ -105,14 +97,14 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
<div
ref={this.createDropTarget}
style={{
- pointerEvents: !this.props.isContentActive() ? 'all' : undefined,
- width: this.props.PanelWidth(),
- height: this.props.PanelHeight(),
+ pointerEvents: !this._props.isContentActive() ? 'all' : undefined,
+ width: this._props.PanelWidth(),
+ height: this._props.PanelHeight(),
}}>
{this.theGraph}
<div
style={{
- display: this.props.isSelected() ? 'none' : undefined,
+ display: this._props.isSelected() ? 'none' : undefined,
position: 'absolute',
width: '100%',
height: '100%',
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 19e393968..876f13370 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,25 +1,20 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { extname } from 'path';
+import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
-import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
-import { createSchema } from '../../../fields/Schema';
-import { ComputedField } from '../../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { DashColor, emptyFunction, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
-import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
-import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
-import { Networking } from '../../Network';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
@@ -31,22 +26,9 @@ import { MarqueeAnnotator } from '../MarqueeAnnotator';
import { AnchorMenu } from '../pdf/AnchorMenu';
import { StyleProp } from '../StyleProvider';
import { DocFocusOptions, OpenWhere } from './DocumentView';
-import { FaceRectangles } from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import './ImageBox.scss';
import { PinProps, PresBox } from './trails';
-import React = require('react');
-
-export const pageSchema = createSchema({
- googlePhotosUrl: 'string',
- googlePhotosTags: 'string',
-});
-const uploadIcons = {
- idle: 'downarrow.png',
- loading: 'loading.gif',
- success: 'greencheck.png',
- failure: 'redx.png',
-};
@observer
export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -54,10 +36,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return FieldView.LayoutString(ImageBox, fieldKey);
}
- @observable public static imageRootDoc: Doc | undefined;
+ @observable public static imageRootDoc: Doc | undefined = undefined;
@observable public static imageEditorOpen: boolean = false;
@observable public static imageEditorSource: string = '';
- @observable public static addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined;
+ @observable public static addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined = undefined;
@action public static setImageEditorOpen(open: boolean) {
ImageBox.imageEditorOpen = open;
}
@@ -72,16 +54,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
private _overlayIconRef = React.createRef<HTMLDivElement>();
private _marqueeref = React.createRef<MarqueeAnnotator>();
@observable _curSuffix = '';
- @observable _uploadIcon = uploadIcons.idle;
constructor(props: any) {
super(props);
- this.props.setContentView?.(this);
+ makeObservable(this);
+ this._props.setContentView?.(this);
}
protected createDropTarget = (ele: HTMLDivElement) => {
this._dropDisposer?.();
- ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
+ ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.Document));
};
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
@@ -89,27 +71,27 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const anchor =
visibleAnchor ??
Docs.Create.ConfigDocument({
- title: 'ImgAnchor:' + this.rootDoc.title,
+ title: 'ImgAnchor:' + this.Document.title,
config_panX: NumCast(this.layoutDoc._freeform_panX),
config_panY: NumCast(this.layoutDoc._freeform_panY),
config_viewScale: Cast(this.layoutDoc._freeform_scale, 'number', null),
- annotationOn: this.rootDoc,
+ annotationOn: this.Document,
});
if (anchor) {
if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
addAsAnnotation && this.addDocument(anchor);
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: visibleAnchor ? false : true } }, this.rootDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: visibleAnchor ? false : true } }, this.Document);
return anchor;
}
- return this.rootDoc;
+ return this.Document;
};
componentDidMount() {
this._disposers.sizer = reaction(
() => ({
- forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes,
- scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._freeform_scale, 1),
- selected: this.props.isSelected(),
+ forceFull: this._props.renderDepth < 1 || this.layoutDoc._showFullRes,
+ scrSize: (this._props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.layoutDoc._freeform_scale, 1),
+ selected: this._props.isSelected(),
}),
({ forceFull, scrSize, selected }) => (this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 ? '_l' : '_o'),
{ fireImmediately: true, delay: 1000 }
@@ -141,7 +123,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
@undoBatch
- @action
drop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.docDragData) {
let added: boolean | undefined = undefined;
@@ -152,7 +133,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
};
if (de.metaKey || targetIsBullseye(e.target as HTMLElement)) {
added = de.complete.docDragData.droppedDocuments.reduce((last: boolean, drop: Doc) => {
- this.rootDoc[this.fieldKey + '_usePath'] = 'alternate:hover';
+ this.layoutDoc[this.fieldKey + '_usePath'] = 'alternate:hover';
return last && Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
}, true);
} else if (de.altKey || !this.dataDoc[this.fieldKey]) {
@@ -177,13 +158,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@undoBatch
setNativeSize = action(() => {
- const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._freeform_scale, 1);
- const nscale = NumCast(this.props.PanelWidth()) / scaling;
+ const scaling = (this._props.DocumentView?.()._props.ScreenToLocalTransform().Scale || 1) / NumCast(this.layoutDoc._freeform_scale, 1);
+ const nscale = NumCast(this._props.PanelWidth()) / scaling;
const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
this.dataDoc[this.fieldKey + '_nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']) * nw;
this.dataDoc[this.fieldKey + '_nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) * nw;
- this.rootDoc._freeform_panX = nw * NumCast(this.rootDoc._freeform_panX);
- this.rootDoc._freeform_panY = nw * NumCast(this.rootDoc._freeform_panY);
+ this.layoutDoc._freeform_panX = nw * NumCast(this.layoutDoc._freeform_panX);
+ this.layoutDoc._freeform_panY = nw * NumCast(this.layoutDoc._freeform_panY);
this.dataDoc._freeform_panXMax = this.dataDoc._freeform_panXMax ? nw * NumCast(this.dataDoc._freeform_panXMax) : undefined;
this.dataDoc._freeform_panXMin = this.dataDoc._freeform_panXMin ? nw * NumCast(this.dataDoc._freeform_panXMin) : undefined;
this.dataDoc._freeform_panYMax = this.dataDoc._freeform_panYMax ? nw * NumCast(this.dataDoc._freeform_panYMax) : undefined;
@@ -206,28 +187,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (!region) return;
const cropping = Doc.MakeCopy(region, true);
Doc.GetProto(region).lockedPosition = true;
- Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
+ Doc.GetProto(region).title = 'region:' + this.Document.title;
Doc.GetProto(region).followLinkToggle = true;
this.addDocument(region);
const anchx = NumCast(cropping.x);
const anchy = NumCast(cropping.y);
const anchw = NumCast(cropping._width);
const anchh = NumCast(cropping._height);
- const viewScale = NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']) / anchw;
- cropping.title = 'crop: ' + this.rootDoc.title;
- cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
- cropping.y = NumCast(this.rootDoc.y);
- cropping._width = anchw * (this.props.NativeDimScaling?.() || 1);
- cropping._height = anchh * (this.props.NativeDimScaling?.() || 1);
+ const viewScale = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) / anchw;
+ cropping.title = 'crop: ' + this.Document.title;
+ cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width);
+ cropping.y = NumCast(this.Document.y);
+ cropping._width = anchw * (this._props.NativeDimScaling?.() || 1);
+ cropping._height = anchh * (this._props.NativeDimScaling?.() || 1);
cropping.onClick = undefined;
const croppingProto = Doc.GetProto(cropping);
croppingProto.annotationOn = undefined;
croppingProto.isDataDoc = true;
croppingProto.backgroundColor = undefined;
- croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
+ croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
croppingProto.type = DocumentType.IMG;
croppingProto.layout = ImageBox.LayoutString('data');
- croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
+ croppingProto.data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField);
croppingProto['data_nativeWidth'] = anchw;
croppingProto['data_nativeHeight'] = anchh;
croppingProto.freeform_scale = viewScale;
@@ -240,12 +221,12 @@ 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) + NumCast(this.rootDoc._width);
- cropping.y = NumCast(this.rootDoc.y);
- this.props.addDocTab(cropping, OpenWhere.inParent);
+ cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width);
+ cropping.y = NumCast(this.Document.y);
+ this._props.addDocTab(cropping, OpenWhere.inParent);
}
DocumentManager.Instance.AddViewRenderedCb(cropping, dv => setTimeout(() => (dv.ComponentView as ImageBox).setNativeSize(), 200));
- this.props.bringToFront(cropping);
+ this._props.bringToFront(cropping);
return cropping;
};
@@ -262,55 +243,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
event: action(() => {
ImageBox.setImageEditorOpen(true);
ImageBox.setImageEditorSource(this.choosePath(field.url));
- ImageBox.addDoc = this.props.addDocument;
- ImageBox.imageRootDoc = this.rootDoc;
+ ImageBox.addDoc = this._props.addDocument;
+ ImageBox.imageRootDoc = this.Document;
}),
icon: 'pencil-alt',
});
- if (!Doc.noviceMode) {
- funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' });
-
- const existingAnalyze = ContextMenu.Instance?.findByDescription('Analyzers...');
- const modes: ContextMenuProps[] = existingAnalyze && 'subitems' in existingAnalyze ? existingAnalyze.subitems : [];
- modes.push({ description: 'Generate Tags', event: this.generateMetadata, icon: 'tag' });
- modes.push({ description: 'Find Faces', event: this.extractFaces, icon: 'camera' });
- //modes.push({ description: "Recommend", event: this.extractText, icon: "brain" });
- !existingAnalyze && ContextMenu.Instance?.addItem({ description: 'Analyzers...', subitems: modes, icon: 'hand-point-right' });
- }
-
ContextMenu.Instance?.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
}
};
- extractFaces = () => {
- const converter = (results: any) => {
- return results.map((face: CognitiveServices.Image.Face) => Doc.Get.FromJson({ data: face, title: `Face: ${face.faceId}` })!);
- };
- this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + '-faces'], this.url, Service.Face, converter);
- };
-
- generateMetadata = (threshold: Confidence = Confidence.Excellent) => {
- const converter = (results: any) => {
- const tagDoc = new Doc();
- const tagsList = new List();
- results.tags.map((tag: Tag) => {
- tagsList.push(tag.name);
- const sanitized = tag.name.replace(' ', '_');
- tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
- });
- this.dataDoc[this.fieldKey + '-generatedTags'] = tagsList;
- tagDoc.title = 'Generated Tags Doc';
- tagDoc.confidence = threshold;
- return tagDoc;
- };
- this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + '-generatedTagsDoc'], this.url, Service.ComputerVision, converter);
- };
-
- @computed private get url() {
- const data = Cast(this.dataDoc[this.fieldKey], ImageField);
- return data ? data.url?.href : undefined;
- }
-
choosePath(url: URL) {
if (!url?.href) return '';
const lower = url.href.toLowerCase();
@@ -319,62 +260,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return `/assets/unknown-file-icon-hi.png`;
const ext = extname(url.href);
- return url.href.replace(ext, this._curSuffix + ext);
- }
-
- considerGooglePhotosLink = () => {
- const remoteUrl = StrCast(this.dataDoc.googlePhotosUrl); // bcz: StrCast or URLCast???
- return !remoteUrl ? null : <img draggable={false} style={{ transformOrigin: 'bottom right' }} id={'google-photos'} src={'/assets/google_photos.png'} onClick={() => window.open(remoteUrl)} />;
- };
-
- considerGooglePhotosTags = () => {
- const tags = this.dataDoc.googlePhotosTags;
- return !tags ? null : <img id={'google-tags'} src={'/assets/google_tags.png'} />;
- };
-
- getScrollHeight = () => (this.props.layout_fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._freeform_scale, 1) === NumCast(this.rootDoc._freeform_scaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
-
- @computed
- private get considerDownloadIcon() {
- const data = this.dataDoc[this.fieldKey];
- if (!(data instanceof ImageField)) {
- return null;
- }
- const primary = data.url?.href;
- if (primary.includes(window.location.origin)) {
- return null;
- }
- return (
- <img
- id={'upload-icon'}
- draggable={false}
- style={{ transformOrigin: 'bottom right' }}
- src={`/assets/${this._uploadIcon}`}
- onClick={async () => {
- const { dataDoc } = this;
- const { success, failure, idle, loading } = uploadIcons;
- runInAction(() => (this._uploadIcon = loading));
- const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [primary] });
- dataDoc[this.props.fieldKey + '-originalUrl'] = primary;
- let succeeded = true;
- let data: ImageField | undefined;
- try {
- data = new ImageField(accessPaths.agnostic.client);
- } catch {
- succeeded = false;
- }
- runInAction(() => (this._uploadIcon = succeeded ? success : failure));
- setTimeout(
- action(() => {
- this._uploadIcon = idle;
- data && (dataDoc[this.fieldKey] = data);
- }),
- 2000
- );
- }}
- />
- );
+ return url.href.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
}
+ getScrollHeight = () => (this._props.layout_fitWidth?.(this.Document) !== false && NumCast(this.layoutDoc._freeform_scale, 1) === NumCast(this.dataDoc._freeform_scaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
@computed get nativeSize() {
TraceMobx();
@@ -384,7 +272,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return { nativeWidth, nativeHeight, nativeOrientation };
}
@computed get overlayImageIcon() {
- const usePath = this.rootDoc[`_${this.fieldKey}_usePath`];
+ const usePath = this.layoutDoc[`_${this.fieldKey}_usePath`];
return (
<Tooltip
title={
@@ -405,9 +293,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div
className="imageBox-alternateDropTarget"
ref={this._overlayIconRef}
- onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
+ onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.layoutDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
style={{
- display: (this.props.isContentActive() !== false && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
+ display: (this._props.isContentActive() !== false && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
width: 'min(10%, 25px)',
height: 'min(10%, 25px)',
background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray',
@@ -430,11 +318,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return paths.length ? paths : [Utils.CorsProxy('https://cs.brown.edu/~bcz/noImage.png')];
}
+ @observable _error = '';
+
@observable _isHovering = false; // flag to switch between primary and alternate images on hover
@computed get content() {
TraceMobx();
- const backColor = DashColor(this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor));
+ const backColor = DashColor(this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor));
const backAlpha = backColor.red() === 0 && backColor.green() === 0 && backColor.blue() === 0 ? backColor.alpha() : 1;
const srcpath = this.layoutDoc.hideImage ? '' : this.paths[0];
const fadepath = this.layoutDoc.hideImage ? '' : this.paths.lastElement();
@@ -452,21 +342,18 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
transformOrigin = 'right top';
transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
}
- const usePath = this.rootDoc[`_${this.fieldKey}_usePath`];
+ const usePath = this.layoutDoc[`_${this.fieldKey}_usePath`];
return (
<div className="imageBox-cont" onPointerEnter={action(() => (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))} key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
<div className="imageBox-fader" style={{ opacity: backAlpha }}>
- <img key="paths" src={srcpath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
+ <img key="paths" src={srcpath} style={{ transform, transformOrigin }} onError={action(e => (this._error = e.toString()))} draggable={false} width={nativeWidth} />
{fadepath === srcpath ? null : (
<div className={`imageBox-fadeBlocker${(this._isHovering && usePath === 'alternate:hover') || usePath === 'alternate' ? '-hover' : ''}`} style={{ transition: StrCast(this.layoutDoc.viewTransition, 'opacity 1000ms') }}>
<img className="imageBox-fadeaway" key="fadeaway" src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
</div>
)}
</div>
- {!Doc.noviceMode && this.considerDownloadIcon}
- {this.considerGooglePhotosLink()}
- <FaceRectangles document={this.dataDoc} color={'#0000FF'} backgroundColor={'#0000FF'} />
{this.overlayImageIcon}
</div>
);
@@ -477,11 +364,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@computed get annotationLayer() {
TraceMobx();
- return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
+ return <div className="imageBox-annotationLayer" style={{ height: this._props.PanelHeight() }} ref={this._annotationLayer} />;
}
- screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this.props.ScreenToLocalTransform().Scale);
+ screenToLocalTransform = () => this._props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this._props.ScreenToLocalTransform().Scale);
marqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._freeform_scale, 1) <= NumCast(this.rootDoc.freeform_scaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+ if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
setupMoveUpEvents(
this,
e,
@@ -500,7 +387,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
finishMarquee = () => {
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
this._marqueeref.current?.onTerminateSelection();
- this.props.select(false);
+ this._props.select(false);
};
focus = (anchor: Doc, options: DocFocusOptions) => (anchor.type === DocumentType.CONFIG ? undefined : this._ffref.current?.focus(anchor, options));
@@ -508,8 +395,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
savedAnnotations = () => this._savedAnnotations;
render() {
TraceMobx();
- const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
- const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this.props.NativeDimScaling?.() || 1)}px` : borderRad;
+ const borderRad = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BorderRounding);
+ const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this._props.NativeDimScaling?.() || 1)}px` : borderRad;
return (
<div
className="imageBox"
@@ -525,25 +412,25 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
})}
style={{
- width: this.props.PanelWidth() ? undefined : `100%`,
- height: this.props.PanelWidth() ? undefined : `100%`,
+ width: this._props.PanelWidth() ? undefined : `100%`,
+ height: this._props.PanelWidth() ? undefined : `100%`,
pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
borderRadius,
- overflow: this.layoutDoc.layout_fitWidth || this.props.layout_fitWidth?.(this.rootDoc) ? 'auto' : undefined,
+ overflow: this.layoutDoc.layout_fitWidth || this._props.layout_fitWidth?.(this.Document) ? 'auto' : undefined,
}}>
<CollectionFreeFormView
ref={this._ffref}
- {...this.props}
+ {...this._props}
setContentView={emptyFunction}
NativeWidth={returnZero}
NativeHeight={returnZero}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
fieldKey={this.annotationKey}
- styleProvider={this.props.styleProvider}
+ styleProvider={this._props.styleProvider}
isAnnotationOverlay={true}
annotationLayerHostsContent={true}
- PanelWidth={this.props.PanelWidth}
- PanelHeight={this.props.PanelHeight}
+ PanelWidth={this._props.PanelWidth}
+ PanelHeight={this._props.PanelHeight}
ScreenToLocalTransform={this.screenToLocalTransform}
select={emptyFunction}
focus={this.focus}
@@ -564,8 +451,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
scrollTop={0}
annotationLayerScrollTop={0}
scaling={returnOne}
- annotationLayerScaling={this.props.NativeDimScaling}
- docView={this.props.DocumentView!}
+ annotationLayerScaling={this._props.NativeDimScaling}
+ docView={this._props.DocumentView!}
addDocument={this.addDocument}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotations}
diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss
index ffcba4981..a44f614b2 100644
--- a/src/client/views/nodes/KeyValueBox.scss
+++ b/src/client/views/nodes/KeyValueBox.scss
@@ -1,7 +1,7 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables.module.scss';
.keyValueBox-cont {
overflow-y: scroll;
- width:100%;
+ width: 100%;
height: 100%;
background-color: $white;
border: 1px solid $medium-gray;
@@ -15,45 +15,45 @@
}
$header-height: 30px;
.keyValueBox-tbody {
- width:100%;
- height:100%;
+ width: 100%;
+ height: 100%;
position: absolute;
overflow-y: scroll;
}
.keyValueBox-key {
display: inline-block;
- height:100%;
- width:50%;
+ height: 100%;
+ width: 50%;
text-align: center;
}
.keyValueBox-fields {
display: inline-block;
- height:100%;
- width:50%;
+ height: 100%;
+ width: 50%;
text-align: center;
}
.keyValueBox-table {
position: absolute;
- width:100%;
- height:100%;
+ width: 100%;
+ height: 100%;
border-collapse: collapse;
}
.keyValueBox-td-key {
- display:inline-block;
- height:30px;
+ display: inline-block;
+ height: 30px;
}
.keyValueBox-td-value {
- display:inline-block;
- height:30px;
+ display: inline-block;
+ height: 30px;
}
.keyValueBox-valueRow {
- width:100%;
- height:30px;
+ width: 100%;
+ height: 30px;
display: inline-block;
}
.keyValueBox-header {
- width:100%;
+ width: 100%;
position: relative;
display: inline-block;
background: $medium-gray;
@@ -74,8 +74,8 @@ $header-height: 30px;
.keyValueBox-evenRow {
position: relative;
display: flex;
- width:100%;
- height:$header-height;
+ width: 100%;
+ height: $header-height;
background: $white;
.formattedTextBox-cont {
background: $white;
@@ -86,10 +86,10 @@ $header-height: 30px;
position: relative;
}
}
-.keyValueBox-dividerDraggerThumb{
+.keyValueBox-dividerDraggerThumb {
position: relative;
width: 4px;
- float: left;
+ float: left;
height: 30px;
width: 5px;
z-index: 20;
@@ -99,10 +99,10 @@ $header-height: 30px;
background: black;
pointer-events: all;
}
-.keyValueBox-dividerDragger{
- position: relative;
+.keyValueBox-dividerDragger {
+ position: relative;
width: 100%;
- float: left;
+ float: left;
height: 37px;
z-index: 20;
right: 0;
@@ -114,10 +114,10 @@ $header-height: 30px;
.keyValueBox-oddRow {
position: relative;
display: flex;
- width:100%;
- height:30px;
+ width: 100%;
+ height: 30px;
background: $light-gray;
.formattedTextBox-cont {
background: $light-gray;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 95d2a2667..73fdc3a23 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -1,26 +1,27 @@
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
+import { returnAlways, returnTrue } from '../../../Utils';
import { Doc, Field, FieldResult } from '../../../fields/Doc';
import { List } from '../../../fields/List';
import { RichTextField } from '../../../fields/RichTextField';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { DocCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
-import { returnAll, returnAlways, returnTrue } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { SetupDrag } from '../../util/DragManager';
-import { CompiledScript, CompileScript, ScriptOptions } from '../../util/Scripting';
+import { CompileScript, CompiledScript, ScriptOptions } from '../../util/Scripting';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
+import { ObservableReactComponent } from '../ObservableReactComponent';
import { DocumentIconContainer } from './DocumentIcon';
import { OpenWhere } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
-import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { ImageBox } from './ImageBox';
import './KeyValueBox.scss';
import { KeyValuePair } from './KeyValuePair';
-import React = require('react');
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
export type KVPScript = {
script: CompiledScript;
@@ -28,10 +29,14 @@ export type KVPScript = {
onDelegate: boolean;
};
@observer
-export class KeyValueBox extends React.Component<FieldViewProps> {
+export class KeyValueBox extends ObservableReactComponent<FieldViewProps> {
public static LayoutString() {
return FieldView.LayoutString(KeyValueBox, 'data');
}
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
private _mainCont = React.createRef<HTMLDivElement>();
private _keyHeader = React.createRef<HTMLTableHeaderCellElement>();
@@ -39,19 +44,18 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
private _valInput = React.createRef<HTMLInputElement>();
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
}
- ignoreNativeDimScaling = returnTrue;
+ isKeyValueBox = returnTrue;
able = returnAlways;
layout_fitWidth = returnTrue;
- overridePointerEvents = returnAll;
onClickScriptDisable = returnAlways;
@observable private rows: KeyValuePair[] = [];
@observable _splitPercentage = 50;
get fieldDocToLayout() {
- return this.props.fieldKey ? DocCast(this.props.Document[this.props.fieldKey], DocCast(this.props.Document)) : this.props.Document;
+ return this._props.fieldKey ? DocCast(this._props.Document[this._props.fieldKey], DocCast(this._props.Document)) : this._props.Document;
}
@action
@@ -110,7 +114,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
onPointerDown = (e: React.PointerEvent): void => {
- if (e.buttons === 1 && this.props.isSelected()) {
+ if (e.buttons === 1 && this._props.isSelected()) {
e.stopPropagation();
}
};
@@ -156,8 +160,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
rows.push(
<KeyValuePair
doc={realDoc}
- addDocTab={this.props.addDocTab}
- PanelWidth={this.props.PanelWidth}
+ addDocTab={this._props.addDocTab}
+ PanelWidth={this._props.PanelWidth}
PanelHeight={this.rowHeight}
ref={(function () {
let oldEl: KeyValuePair | undefined;
@@ -221,19 +225,19 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
getFieldView = () => {
const rows = this.rows.filter(row => row.isChecked);
if (rows.length > 1) {
- const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document).title}`, _chromeHidden: true });
+ const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this._props.Document).title}`, _chromeHidden: true });
for (const row of rows) {
- const field = this.createFieldView(DocCast(this.props.Document), row);
+ const field = this.createFieldView(DocCast(this._props.Document), row);
field && Doc.AddDocToList(parent, 'data', field);
row.uncheck();
}
return parent;
}
- return rows.length ? this.createFieldView(DocCast(this.props.Document), rows.lastElement()) : undefined;
+ return rows.length ? this.createFieldView(DocCast(this._props.Document), rows.lastElement()) : undefined;
};
createFieldView = (templateDoc: Doc, row: KeyValuePair) => {
- const metaKey = row.props.keyName;
+ const metaKey = row._props.keyName;
const fieldTemplate = Doc.IsDelegateField(templateDoc, metaKey) ? Doc.MakeDelegate(templateDoc) : Doc.MakeEmbedding(templateDoc);
fieldTemplate.title = metaKey;
fieldTemplate.layout_fitWidth = true;
@@ -279,8 +283,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
openItems.push({
description: 'Default Perspective',
event: () => {
- this.props.addDocTab(this.props.Document, OpenWhere.close);
- this.props.addDocTab(this.fieldDocToLayout, OpenWhere.addRight);
+ this._props.addDocTab(this._props.Document, OpenWhere.close);
+ this._props.addDocTab(this.fieldDocToLayout, OpenWhere.addRight);
},
icon: 'image',
});
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index 57d36932e..46ea9c18e 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables';
+@import '../global/globalCssVariables.module.scss';
.keyValuePair-td-key {
display: inline-block;
@@ -26,6 +26,7 @@
position: relative;
overflow: auto;
display: inline;
+ align-self: center;
}
}
}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 6f99b7c28..fd8d8ef56 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,21 +1,23 @@
-import { action, observable } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
-import { Doc, Field } from '../../../fields/Doc';
+import { ObservableGroupMap } from 'mobx-utils';
+import * as React from 'react';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../Utils';
+import { Doc, Field } from '../../../fields/Doc';
+import { DocCast } from '../../../fields/Types';
+import { DocumentOptions, FInfo } from '../../documents/Documents';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { EditableView } from '../EditableView';
import { DefaultStyleProvider } from '../StyleProvider';
-import { OpenWhere, OpenWhereMod } from './DocumentView';
-import { FieldView, FieldViewProps } from './FieldView';
+import { OpenWhere } from './DocumentView';
+import { FieldViewProps } from './FieldView';
import { KeyValueBox } from './KeyValueBox';
import './KeyValueBox.scss';
import './KeyValuePair.scss';
-import React = require('react');
-import { DocCast } from '../../../fields/Types';
-import { Tooltip } from '@material-ui/core';
-import { DocumentOptions, FInfo } from '../../documents/Documents';
+import { ObservableReactComponent } from '../ObservableReactComponent';
// Represents one row in a key value plane
@@ -29,10 +31,15 @@ export interface KeyValuePairProps {
addDocTab: (doc: Doc, where: OpenWhere) => boolean;
}
@observer
-export class KeyValuePair extends React.Component<KeyValuePairProps> {
+export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
@observable private isPointerOver = false;
@observable public isChecked = false;
private checkbox = React.createRef<HTMLInputElement>();
+ constructor(props:any) {
+ super(props);
+ makeObservable(this);
+ }
+
@action
handleCheck = (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -46,26 +53,24 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
};
onContextMenu = (e: React.MouseEvent) => {
- const value = this.props.doc[this.props.keyName];
+ const value = this._props.doc[this._props.keyName];
if (value instanceof Doc) {
e.stopPropagation();
e.preventDefault();
- ContextMenu.Instance.addItem({ description: 'Open Fields', event: () => this.props.addDocTab(value, OpenWhere.addRightKeyvalue), 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);
}
};
render() {
const props: FieldViewProps = {
- Document: this.props.doc,
- DataDoc: this.props.doc,
+ Document: this._props.doc,
childFilters: returnEmptyFilter,
childFiltersByRanges: returnEmptyFilter,
searchFilterDocs: returnEmptyDoclist,
styleProvider: DefaultStyleProvider,
docViewPath: returnEmptyDoclist,
- fieldKey: this.props.keyName,
- rootSelected: returnFalse,
+ fieldKey: this._props.keyName,
isSelected: returnFalse,
setHeight: returnFalse,
select: emptyFunction,
@@ -75,12 +80,11 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
whenChildContentsActiveChanged: emptyFunction,
ScreenToLocalTransform: Transform.Identity,
focus: emptyFunction,
- PanelWidth: this.props.PanelWidth,
- PanelHeight: this.props.PanelHeight,
+ PanelWidth: this._props.PanelWidth,
+ PanelHeight: this._props.PanelHeight,
addDocTab: returnFalse,
pinToPres: returnZero,
};
- const contents = <FieldView {...props} />;
// let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
let protoCount = 0;
let doc: Doc | undefined = props.Document;
@@ -97,8 +101,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
const hover = { transition: '0.3s ease opacity', opacity: this.isPointerOver || this.isChecked ? 1 : 0 };
return (
- <tr className={this.props.rowStyle} onPointerEnter={action(() => (this.isPointerOver = true))} onPointerLeave={action(() => (this.isPointerOver = false))}>
- <td className="keyValuePair-td-key" style={{ width: `${this.props.keyWidth}%` }}>
+ <tr className={this._props.rowStyle} onPointerEnter={action(() => (this.isPointerOver = true))} onPointerLeave={action(() => (this.isPointerOver = false))}>
+ <td className="keyValuePair-td-key" style={{ width: `${this._props.keyWidth}%` }}>
<div className="keyValuePair-td-key-container">
<button
style={hover}
@@ -120,9 +124,9 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
</Tooltip>
</div>
</td>
- <td className="keyValuePair-td-value" style={{ width: `${100 - this.props.keyWidth}%` }} onContextMenu={this.onContextMenu}>
+ <td className="keyValuePair-td-value" style={{ width: `${100 - this._props.keyWidth}%` }} onContextMenu={this.onContextMenu}>
<div className="keyValuePair-td-value-container">
- <EditableView contents={contents} GetValue={() => Field.toKeyValueString(props.Document, props.fieldKey)} SetValue={(value: string) => KeyValueBox.SetField(props.Document, props.fieldKey, value)} />
+ <EditableView contents={undefined} fieldContents={props} GetValue={() => Field.toKeyValueString(props.Document, props.fieldKey)} SetValue={(value: string) => KeyValueBox.SetField(props.Document, props.fieldKey, value)} />
</div>
</td>
</tr>
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 0d7b2b0a4..934bce448 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -1,10 +1,10 @@
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../../fields/Doc';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
-import { Cast, StrCast, NumCast, BoolCast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
@@ -29,21 +29,27 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
}
private dropDisposer?: DragManager.DragDropDisposer;
private _timeout: any;
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
}
componentWillUnMount() {
this._timeout && clearTimeout(this._timeout);
}
- getTitle() {
- return this.dataDoc.title_custom ? StrCast(this.Document.title) : this.props.label ? this.props.label : typeof this.dataDoc[this.fieldKey] === 'string' ? StrCast(this.dataDoc[this.fieldKey]) : StrCast(this.Document.title);
+ @computed get Title() {
+ return this.dataDoc.title_custom ? StrCast(this.Document.title) : this._props.label ? this._props.label : typeof this.dataDoc[this.fieldKey] === 'string' ? StrCast(this.dataDoc[this.fieldKey]) : StrCast(this.Document.title);
}
protected createDropTarget = (ele: HTMLDivElement) => {
this.dropDisposer?.();
if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document);
+ this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.Document);
}
};
@@ -66,7 +72,6 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
};
@undoBatch
- @action
drop = (e: Event, de: DragManager.DropEvent) => {
const docDragData = de.complete.docDragData;
const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []);
@@ -81,10 +86,25 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
@observable _mouseOver = false;
@computed get hoverColor() {
- return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor);
+ return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor);
}
- fitTextToBox = (r: any) => {
+ fitTextToBox = (
+ r: any
+ ):
+ | NodeJS.Timeout
+ | {
+ rotateText: null;
+ fontSizeFactor: number;
+ minimumFontSize: number;
+ maximumFontSize: number;
+ limitingDimension: string;
+ horizontalAlign: string;
+ verticalAlign: string;
+ textAlign: string;
+ singleLine: boolean;
+ whiteSpace: string;
+ } => {
const singleLine = BoolCast(this.layoutDoc._singleLine, true);
const params = {
rotateText: null,
@@ -119,7 +139,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []);
const missingParams = params?.filter(p => !this.paramsDoc[p]);
params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ...
- const label = this.getTitle();
+ const label = this.Title;
return (
<div
className="labelBox-outerDiv"
@@ -127,7 +147,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
onMouseOver={action(() => (this._mouseOver = true))}
ref={this.createDropTarget}
onContextMenu={this.specificContextMenu}
- style={{ boxShadow: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow) }}>
+ style={{ boxShadow: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow) }}>
<div
className="labelBox-mainButton"
style={{
@@ -141,11 +161,11 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
paddingRight: NumCast(this.layoutDoc._xPadding),
paddingTop: NumCast(this.layoutDoc._yPadding),
paddingBottom: NumCast(this.layoutDoc._yPadding),
- width: this.props.PanelWidth(),
- height: this.props.PanelHeight(),
- whiteSpace: typeof boxParams !== 'number' && boxParams.singleLine ? 'pre' : 'pre-wrap',
+ width: this._props.PanelWidth(),
+ height: this._props.PanelHeight(),
+ whiteSpace: 'singleLine' in boxParams && boxParams.singleLine ? 'pre' : 'pre-wrap',
}}>
- <span style={{ width: typeof boxParams !== 'number' && boxParams.singleLine ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}>
+ <span style={{ width: 'singleLine' in boxParams ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}>
{label.startsWith('#') ? null : label.replace(/([^a-zA-Z])/g, '$1\u200b')}
</span>
</div>
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index cf157c3a9..b86ba72a0 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -1,9 +1,9 @@
-import { action, computed, observable } from 'mobx';
-import { observer } from 'mobx-react';
+import { action, computed, makeObservable } from 'mobx';
+import * as React from 'react';
+import { Utils, emptyFunction, setupMoveUpEvents } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
import { NumCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
import { LinkFollower } from '../../util/LinkFollower';
import { SelectionManager } from '../../util/SelectionManager';
@@ -11,11 +11,8 @@ import { ViewBoxBaseComponent } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
import { FieldView, FieldViewProps } from './FieldView';
import './LinkAnchorBox.scss';
-import { LinkDocPreview } from './LinkDocPreview';
-import React = require('react');
-import globalCssVariables = require('../global/globalCssVariables.scss');
-
-@observer
+import { LinkInfo } from './LinkDocPreview';
+const { default: { MEDIUM_GRAY }, } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(LinkAnchorBox, fieldKey);
@@ -25,21 +22,24 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
_ref = React.createRef<HTMLDivElement>();
_isOpen = false;
_timeout: NodeJS.Timeout | undefined;
- @observable _x = 0;
- @observable _y = 0;
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
}
@computed get linkSource() {
- return this.props.docViewPath()[this.props.docViewPath().length - 2].Document; // this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource);
+ return this._props.docViewPath()[this._props.docViewPath().length - 2].Document; // this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.LinkSource);
}
onPointerDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => {
if (doubleTap) LinkFollower.FollowLink(this.Document, this.linkSource, false);
- else this.props.select(false);
+ else this._props.select(false);
});
};
onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => {
@@ -67,26 +67,26 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
render() {
TraceMobx();
- const small = this.props.PanelWidth() <= 1; // this happens when rendered in a treeView
+ const small = this._props.PanelWidth() <= 1; // this happens when rendered in a treeView
const x = NumCast(this.layoutDoc[this.fieldKey + '_x'], 100);
const y = NumCast(this.layoutDoc[this.fieldKey + '_y'], 100);
- const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ':anchor');
+ const background = this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.BackgroundColor + ':anchor');
const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1';
const anchorScale = !this.dataDoc[this.fieldKey + '_useSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
- const selView = SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_1')
+ const selView = SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_1')
? 'link_anchor_1'
- : SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_2')
- ? 'link_anchor_2'
- : '';
+ : SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_2')
+ ? 'link_anchor_2'
+ : '';
return (
<div
ref={this._ref}
title={targetTitle}
className={`linkAnchorBox-cont${small ? '-small' : ''}`}
onPointerEnter={e =>
- LinkDocPreview.SetLinkInfo({
- docProps: this.props,
+ LinkInfo.SetLinkInfo({
+ docProps: this._props,
linkSrc: this.linkSource,
linkDoc: this.Document,
showHeader: true,
@@ -97,7 +97,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
onPointerDown={this.onPointerDown}
onContextMenu={this.specificContextMenu}
style={{
- border: selView && this.dataDoc[selView] === this.dataDoc[this.fieldKey] ? `solid ${globalCssVariables.MEDIUM_GRAY} 2px` : undefined,
+ border: selView && this.dataDoc[selView] === this.dataDoc[this.fieldKey] ? `solid ${MEDIUM_GRAY} 2px` : undefined,
background,
left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 30cd65cb4..134f2e14a 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,7 +1,7 @@
-import React = require('react');
import { Bezier } from 'bezier-js';
-import { computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { Id } from '../../../fields/FieldSymbols';
import { DocCast, NumCast, StrCast } from '../../../fields/Types';
import { aggregateBounds, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils';
@@ -20,23 +20,28 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
return FieldView.LayoutString(LinkBox, fieldKey);
}
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
onClickScriptDisable = returnAlways;
@computed get anchor1() {
const anchor1 = DocCast(this.dataDoc.link_anchor_1);
const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1;
- return DocumentManager.Instance.getDocumentView(anchor_1, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement());
+ return DocumentManager.Instance.getDocumentView(anchor_1, this._props.docViewPath()[this._props.docViewPath().length - 2]); // this._props.docViewPath().lastElement());
}
@computed get anchor2() {
const anchor2 = DocCast(this.dataDoc.link_anchor_2);
const anchor_2 = anchor2?.layout_unrendered ? DocCast(anchor2.annotationOn) : anchor2;
- return DocumentManager.Instance.getDocumentView(anchor_2, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement());
+ 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.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(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 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(NumCast(this.anchor1.Document._width), NumCast(this.anchor1.Document._height)) };
+ const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(this.anchor2.Document._width), NumCast(this.anchor2.Document._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]);
@@ -54,19 +59,19 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
disposer: IReactionDisposer | undefined;
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
this.disposer = reaction(
() => {
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?.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(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 parxf = this._props.docViewPath()[this._props.docViewPath().length - 2].ComponentView as CollectionFreeFormView;
+ 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(NumCast(a.Document._width), NumCast(a.Document._height)) };
+ const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(b.Document._width), NumCast(b.Document._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]) };
@@ -103,10 +108,10 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
componentWillUnmount(): void {
this.disposer?.();
}
- @observable renderProps: { lx: number; rx: number; ty: number; by: number; pts: number[][] } | undefined;
+ @observable renderProps: { lx: number; rx: number; ty: number; by: number; pts: number[][] } | undefined = undefined;
render() {
if (this.renderProps) {
- const highlight = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Highlighting);
+ const highlight = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Highlighting);
const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : undefined;
const bez = new Bezier(this.renderProps.pts.map(p => ({ x: p[0], y: p[1] })));
@@ -131,7 +136,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
<path
className="collectionfreeformlinkview-linkLine"
style={{
- pointerEvents: this.props.pointerEvents?.() === 'none' ? 'none' : 'visibleStroke', //
+ pointerEvents: this._props.pointerEvents?.() === 'none' ? 'none' : 'visibleStroke', //
stroke: highlightColor ?? 'lightblue',
strokeDasharray,
strokeWidth,
@@ -142,7 +147,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
/>
<text
filter={`url(#${this.Document[Id] + 'background'})`}
- style={{ pointerEvents: this.props.pointerEvents?.() === 'none' ? 'none' : 'all', textAnchor: 'middle', fontSize: '12', stroke: 'black' }}
+ style={{ pointerEvents: this._props.pointerEvents?.() === 'none' ? 'none' : 'all', textAnchor: 'middle', fontSize: '12', stroke: 'black' }}
x={text.x - lx}
y={text.y - ty}>
<tspan>&nbsp;</tspan>
@@ -154,14 +159,14 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
}
return (
- <div className={`linkBox-container${this.props.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
+ <div className={`linkBox-container${this._props.isContentActive() ? '-interactive' : ''}`} style={{ background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) }}>
<ComparisonBox
- {...this.props}
+ {...this._props}
fieldKey="link_anchor"
setHeight={emptyFunction}
dontRegisterView={true}
- renderDepth={this.props.renderDepth + 1}
- isContentActive={this.props.isContentActive}
+ renderDepth={this._props.renderDepth + 1}
+ isContentActive={this._props.isContentActive}
addDocument={returnFalse}
removeDocument={returnFalse}
moveDocument={returnFalse}
diff --git a/src/client/views/nodes/LinkDescriptionPopup.scss b/src/client/views/nodes/LinkDescriptionPopup.scss
index a8db5d360..104301656 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.scss
+++ b/src/client/views/nodes/LinkDescriptionPopup.scss
@@ -1,7 +1,6 @@
-@import "../global/globalCssVariables.scss";
+@import '../global/globalCssVariables.module.scss';
.linkDescriptionPopup {
-
display: flex;
flex-direction: row;
justify-content: center;
@@ -26,7 +25,6 @@
}
.linkDescriptionPopup-btn {
-
float: right;
justify-content: center;
vertical-align: middle;
@@ -53,6 +51,4 @@
color: black;
}
}
-
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index c45045a8a..32300d60a 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../../fields/Doc';
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 7e4f1da8e..d0a9f10b4 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
-import { action, computed, observable } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import wiki from 'wikijs';
import { Doc, Opt } from '../../../fields/Doc';
@@ -18,7 +18,27 @@ import { SettingsManager } from '../../util/SettingsManager';
import { Transform } from '../../util/Transform';
import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView';
import './LinkDocPreview.scss';
-import React = require('react');
+import * as React from 'react';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+
+export class LinkInfo {
+ private static _instance: Opt<LinkInfo>;
+ constructor() {
+ LinkInfo._instance = this;
+ makeObservable(this);
+ }
+ @observable public LinkInfo: Opt<LinkDocPreviewProps> = undefined;
+
+ public static get Instance() {
+ return LinkInfo._instance ?? new LinkInfo();
+ }
+ public static Clear() {
+ runInAction(() => LinkInfo.Instance && (LinkInfo.Instance.LinkInfo = undefined));
+ }
+ public static SetLinkInfo(info?: LinkDocPreviewProps) {
+ runInAction(() => LinkInfo.Instance && (LinkInfo.Instance.LinkInfo = info));
+ }
+}
interface LinkDocPreviewProps {
linkDoc?: Doc;
@@ -30,36 +50,25 @@ interface LinkDocPreviewProps {
noPreview?: boolean;
}
@observer
-export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
- @action public static Clear() {
- LinkDocPreview.LinkInfo = undefined;
- }
- @action public static SetLinkInfo(info?: LinkDocPreviewProps) {
- LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info);
- }
-
- static _instance: Opt<LinkDocPreview>;
-
+export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps> {
_infoRef = React.createRef<HTMLDivElement>();
_linkDocRef = React.createRef<HTMLDivElement>();
- @observable public static LinkInfo: Opt<LinkDocPreviewProps>;
- @observable _targetDoc: Opt<Doc>;
- @observable _markerTargetDoc: Opt<Doc>;
- @observable _linkDoc: Opt<Doc>;
- @observable _linkSrc: Opt<Doc>;
+ @observable _targetDoc: Opt<Doc> = undefined;
+ @observable _markerTargetDoc: Opt<Doc> = undefined;
+ @observable _linkDoc: Opt<Doc> = undefined;
+ @observable _linkSrc: Opt<Doc> = undefined;
@observable _toolTipText = '';
@observable _hrefInd = 0;
-
constructor(props: any) {
super(props);
- LinkDocPreview._instance = this;
+ makeObservable(this);
}
@action
init() {
- var linkTarget = this.props.linkDoc;
- this._linkSrc = this.props.linkSrc;
- this._linkDoc = this.props.linkDoc;
+ var linkTarget = this._props.linkDoc;
+ this._linkSrc = this._props.linkSrc;
+ this._linkDoc = this._props.linkDoc;
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) {
@@ -72,28 +81,28 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
this._toolTipText = '';
this.updateHref();
}
- componentDidUpdate(props: any) {
- if (props.linkSrc !== this.props.linkSrc || props.linkDoc !== this.props.linkDoc || props.hrefs !== this.props.hrefs) this.init();
+ componentDidUpdate(prevProps: Readonly<LinkDocPreviewProps>) {
+ super.componentDidUpdate(prevProps);
+ if (prevProps.linkSrc !== this._props.linkSrc || prevProps.linkDoc !== this._props.linkDoc || prevProps.hrefs !== this._props.hrefs) this.init();
}
componentDidMount() {
this.init();
document.addEventListener('pointerdown', this.onPointerDown, true);
}
- @action
componentWillUnmount() {
- LinkDocPreview.SetLinkInfo(undefined);
+ LinkInfo.Clear();
document.removeEventListener('pointerdown', this.onPointerDown, true);
}
onPointerDown = (e: PointerEvent) => {
- !this._linkDocRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
+ !this._linkDocRef.current?.contains(e.target as any) && LinkInfo.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
};
@action
updateHref() {
- if (this.props.hrefs?.length) {
- const href = this.props.hrefs[this._hrefInd];
+ if (this._props.hrefs?.length) {
+ const href = this._props.hrefs[this._hrefInd];
if (href.indexOf(Doc.localServerPath()) !== 0) {
// link to a web page URL -- try to show a preview
if (href.startsWith('https://en.wikipedia.org/wiki/')) {
@@ -123,7 +132,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
this._markerTargetDoc = linkTarget;
this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
}
- if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
+ if (LinkInfo.Instance?.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
}
})
);
@@ -143,9 +152,9 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
action(() => {
LinkManager.currentLink = this._linkDoc;
LinkManager.currentLinkAnchor = this._linkSrc;
- this.props.docProps.DocumentView?.().select(false);
- if ((SettingsManager.propertiesWidth ?? 0) < 100) {
- SettingsManager.propertiesWidth = 250;
+ this._props.docProps.DocumentView?.().select(false);
+ if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) {
+ SettingsManager.Instance.propertiesWidth = 250;
}
})
);
@@ -157,7 +166,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
returnFalse,
emptyFunction,
action(() => {
- const nextHrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1);
+ const nextHrefInd = (this._hrefInd + 1) % (this._props.hrefs?.length || 1);
if (nextHrefInd !== this._hrefInd) {
this._linkDoc = undefined;
this._hrefInd = nextHrefInd;
@@ -168,19 +177,19 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
};
followLink = () => {
- LinkDocPreview.Clear();
+ LinkInfo.Clear();
if (this._linkDoc && this._linkSrc) {
LinkFollower.FollowLink(this._linkDoc, this._linkSrc, false);
- } else if (this.props.hrefs?.length) {
+ } else if (this._props.hrefs?.length) {
const webDoc =
- Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ??
- Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true });
+ Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this._props.hrefs[0]).keys()).lastElement() ??
+ Docs.Create.WebDocument(this._props.hrefs[0], { title: this._props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true });
DocumentManager.Instance.showDocument(webDoc, {
openLocation: OpenWhere.lightbox,
willPan: true,
zoomTime: 500,
});
- //this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
+ //this._props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
}
};
@@ -216,7 +225,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
</div>
<div className="linkDocPreview-buttonBar" style={{ float: 'right' }}>
<Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top">
- <div className="linkDocPreview-button" style={{ background: (this.props.hrefs?.length || 0) <= 1 ? 'gray' : 'green' }} onPointerDown={this.nextHref}>
+ <div className="linkDocPreview-button" style={{ background: (this._props.hrefs?.length || 0) <= 1 ? 'gray' : 'green' }} onPointerDown={this.nextHref}>
<FontAwesomeIcon className="linkDocPreview-fa-icon" icon="chevron-right" color="white" size="sm" />
</div>
</Tooltip>
@@ -228,7 +237,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
@computed get docPreview() {
return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : (
<div className="linkDocPreview-inner">
- {!this.props.showHeader ? null : this.previewHeader}
+ {!this._props.showHeader ? null : this.previewHeader}
<div
className="linkDocPreview-preview-wrapper"
onPointerDown={e =>
@@ -238,7 +247,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
(e, down, delta) => {
if (Math.abs(e.clientX - down[0]) + Math.abs(e.clientY - down[1]) > 100) {
DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
- LinkDocPreview.Clear();
+ LinkInfo.Clear();
return true;
}
return false;
@@ -256,12 +265,11 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
<DocumentView
ref={r => {
const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
- targetanchor && this._targetDoc !== targetanchor && r?.props.focus?.(targetanchor, {});
+ targetanchor && this._targetDoc !== targetanchor && r?._props.focus?.(targetanchor, {});
}}
Document={this._targetDoc!}
moveDocument={returnFalse}
- rootSelected={returnFalse}
- styleProvider={this.props.docProps?.styleProvider}
+ styleProvider={this._props.docProps?.styleProvider}
docViewPath={returnEmptyDoclist}
ScreenToLocalTransform={Transform.Identity}
isDocumentActive={returnFalse}
@@ -300,7 +308,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
className="linkDocPreview"
ref={this._linkDocRef}
onPointerDown={this.followLinkPointerDown}
- style={{ borderColor: SettingsManager.userColor, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+ style={{ borderColor: SettingsManager.userColor, left: this._props.location[0], top: this._props.location[1], width: this.width() + borders, height: this.height() + borders + (this._props.showHeader ? 37 : 0) }}>
{this.docPreview}
</div>
);
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
index 4bb0f14d2..27d73a585 100644
--- a/src/client/views/nodes/LoadingBox.tsx
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -6,6 +6,7 @@ import { Doc } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { StrCast } from '../../../fields/Types';
import { Networking } from '../../Network';
+import { DocumentManager } from '../../util/DocumentManager';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { FieldView, FieldViewProps } from './FieldView';
import './LoadingBox.scss';
@@ -36,49 +37,47 @@ 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);
+ if (DocumentManager.Instance.CurrentlyLoading) {
+ const index = DocumentManager.Instance.CurrentlyLoading.indexOf(doc);
+ runInAction(() => index !== -1 && DocumentManager.Instance.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);
+ if (DocumentManager.Instance.CurrentlyLoading.indexOf(doc) === -1) {
+ runInAction(() => DocumentManager.Instance.CurrentlyLoading.push(doc));
}
}
_timer: any;
@observable progress = '';
componentDidMount() {
- if (!LoadingBox.CurrentlyLoading?.includes(this.rootDoc)) {
- this.rootDoc.loadingError = 'Upload interrupted, please try again';
+ if (!DocumentManager.Instance.CurrentlyLoading?.includes(this.Document)) {
+ this.Document.loadingError = 'Upload interrupted, please try again';
} else {
const updateFunc = async () => {
- const result = await Networking.QueryYoutubeProgress(StrCast(this.rootDoc[Id])); // We use the guid of the overwriteDoc to track file uploads.
+ const result = await Networking.QueryYoutubeProgress(StrCast(this.Document[Id])); // We use the guid of the overwriteDoc to track file uploads.
runInAction(() => (this.progress = result.progress));
- !this.rootDoc.loadingError && (this._timer = setTimeout(updateFunc, 1000));
+ !this.Document.loadingError && this._timer && (this._timer = setTimeout(updateFunc, 1000));
};
this._timer = setTimeout(updateFunc, 1000);
}
}
componentWillUnmount() {
clearTimeout(this._timer);
+ this._timer = undefined;
}
render() {
return (
- <div className="loadingBoxContainer" style={{ background: !this.rootDoc.loadingError ? '' : 'red' }}>
+ <div className="loadingBoxContainer" style={{ background: !this.Document.loadingError ? '' : 'red' }}>
<div className="loadingBox-textContainer">
- <span className="loadingBox-title">{StrCast(this.rootDoc.title)}</span>
- <p className="loadingBox-headerText">{StrCast(this.rootDoc.loadingError, 'Loading ' + (this.progress.replace('[download]', '') || '(can take several minutes)'))}</p>
- {this.rootDoc.loadingError ? null : (
+ <span className="loadingBox-title">{StrCast(this.Document.title)}</span>
+ <p className="loadingBox-headerText">{StrCast(this.Document.loadingError, 'Loading ' + (this.progress.replace('[download]', '') || '(can take several minutes)'))}</p>
+ {this.Document.loadingError ? null : (
<div className="loadingBox-spinner">
<ReactLoading type="spinningBubbles" color="blue" height={100} width={100} />
</div>
diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts
index 11b335a96..a5cff4668 100644
--- a/src/client/views/nodes/MapBox/AnimationUtility.ts
+++ b/src/client/views/nodes/MapBox/AnimationUtility.ts
@@ -1,12 +1,13 @@
-import mapboxgl from "mapbox-gl";
-import { MercatorCoordinate } from "mapbox-gl";
-import { MapRef } from "react-map-gl";
+import mapboxgl from 'mapbox-gl';
+import { MercatorCoordinate } from 'mapbox-gl';
+import { MapRef } from 'react-map-gl';
import * as React from 'react';
-import * as d3 from "d3";
+import * as d3 from 'd3';
import * as turf from '@turf/turf';
-import { Position } from "@turf/turf";
-import { Feature, GeoJsonProperties, Geometry } from "geojson";
-import { action, computed, observable, runInAction } from "mobx";
+import { Position } from '@turf/turf';
+import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson';
+import { observer } from 'mobx-react';
+import { action, computed, observable, runInAction } from 'mobx';
export enum AnimationStatus {
START = 'start',
@@ -21,15 +22,15 @@ export enum AnimationSpeed {
}
export class AnimationUtility {
- private SMOOTH_FACTOR = 0.95
- private ROUTE_COORDINATES: Position[] = [];
+ private SMOOTH_FACTOR = 0.95;
+ private ROUTE_COORDINATES: Position[] = [];
@observable
private PATH: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>;
-
+
private PATH_DISTANCE: number;
private FLY_IN_START_PITCH = 40;
- private FIRST_LNG_LAT: {lng: number, lat: number};
+ private FIRST_LNG_LAT: { lng: number; lat: number };
private START_ALTITUDE = 3_000_000;
private MAP_REF: MapRef | null;
@@ -37,27 +38,27 @@ export class AnimationUtility {
@observable private animationSpeed: AnimationSpeed;
@observable
- private previousLngLat: {lng: number, lat: number};
+ private previousLngLat: { lng: number; lat: number };
private previousAltitude: number | null = null;
private previousPitch: number | null = null;
-
+
private currentStreetViewBearing: number = 0;
private terrainDisplayed: boolean;
@computed get flyInEndBearing() {
- return this.isStreetViewAnimation ?
- this.calculateBearing(
- {
- lng: this.ROUTE_COORDINATES[0][0],
- lat: this.ROUTE_COORDINATES[0][1]
- },
- {
- lng: this.ROUTE_COORDINATES[1][0],
- lat: this.ROUTE_COORDINATES[1][1]
- }
- )
+ return this.isStreetViewAnimation
+ ? this.calculateBearing(
+ {
+ lng: this.ROUTE_COORDINATES[0][0],
+ lat: this.ROUTE_COORDINATES[0][1],
+ },
+ {
+ lng: this.ROUTE_COORDINATES[1][0],
+ lat: this.ROUTE_COORDINATES[1][1],
+ }
+ )
: -20;
}
@@ -67,15 +68,14 @@ export class AnimationUtility {
const coords: mapboxgl.LngLatLike = [this.previousLngLat.lng, this.previousLngLat.lat];
// console.log('MAP REF: ', this.MAP_REF)
// console.log("current elevation: ", this.MAP_REF?.queryTerrainElevation(coords));
- let altitude = (this.MAP_REF ? (this.MAP_REF.queryTerrainElevation(coords) ?? 0) : 0);
- if (altitude === 0){
- altitude+=50;
+ let altitude = this.MAP_REF ? this.MAP_REF.queryTerrainElevation(coords) ?? 0 : 0;
+ if (altitude === 0) {
+ altitude += 50;
}
- if (this.previousAltitude){
+ if (this.previousAltitude) {
return this.lerp(altitude, this.previousAltitude, 0.02);
}
return altitude;
-
}
@computed get flyInStartBearing() {
@@ -96,16 +96,15 @@ export class AnimationUtility {
const horizontalDistance = 500;
let pitch;
- if (heightAboveGround >= 0){
- pitch = (90- Math.atan(heightAboveGround/horizontalDistance) * (180/Math.PI));
- }
- else {
+ if (heightAboveGround >= 0) {
+ pitch = 90 - Math.atan(heightAboveGround / horizontalDistance) * (180 / Math.PI);
+ } else {
pitch = 80;
}
console.log(Math.max(50, Math.min(pitch, 85)));
- if (this.previousPitch){
+ if (this.previousPitch) {
return this.lerp(Math.max(50, Math.min(pitch, 85)), this.previousPitch, 0.02);
}
return Math.max(50, Math.min(pitch, 85));
@@ -138,7 +137,7 @@ export class AnimationUtility {
const normalizedDistance = Math.min(1, (this.PATH_DISTANCE - MIN_DISTANCE) / (MAX_DISTANCE - MIN_DISTANCE));
const easedDistance = d3.easeExpOut(Math.min(normalizedDistance, 1));
- switch (this.animationSpeed){
+ switch (this.animationSpeed) {
case AnimationSpeed.SLOW:
scalingFactor = 250;
break;
@@ -148,13 +147,13 @@ export class AnimationUtility {
case AnimationSpeed.FAST:
scalingFactor = 85;
break;
- default:
+ default:
scalingFactor = 150;
break;
}
- const duration = Math.min(MAX_DURATION, (easedDistance * MAX_DISTANCE) * (this.isStreetViewAnimation ? scalingFactor*1.12 : scalingFactor));
-
+ const duration = Math.min(MAX_DURATION, easedDistance * MAX_DISTANCE * (this.isStreetViewAnimation ? scalingFactor * 1.12 : scalingFactor));
+
return duration;
}
@@ -163,26 +162,18 @@ export class AnimationUtility {
// calculate new flyToDuration and animationDuration
this.animationSpeed = speed;
}
-
+
@action
public updateIsStreetViewAnimation(isStreetViewAnimation: boolean) {
this.isStreetViewAnimation = isStreetViewAnimation;
}
- @action
+ @action
public setPath = (path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>) => {
this.PATH = path;
- }
+ };
-
- constructor(
- firstLngLat: {lng: number, lat: number},
- routeCoordinates: Position[],
- isStreetViewAnimation: boolean,
- animationSpeed: AnimationSpeed,
- terrainDisplayed: boolean,
- mapRef: MapRef | null
- ) {
+ constructor(firstLngLat: { lng: number; lat: number }, routeCoordinates: Position[], isStreetViewAnimation: boolean, animationSpeed: AnimationSpeed, terrainDisplayed: boolean, mapRef: MapRef | null) {
this.FIRST_LNG_LAT = firstLngLat;
this.previousLngLat = firstLngLat;
this.isStreetViewAnimation = isStreetViewAnimation;
@@ -196,11 +187,11 @@ export class AnimationUtility {
const bearing = this.calculateBearing(
{
lng: routeCoordinates[0][0],
- lat: routeCoordinates[0][1]
+ lat: routeCoordinates[0][1],
},
{
lng: routeCoordinates[1][0],
- lat: routeCoordinates[1][1]
+ lat: routeCoordinates[1][1],
}
);
this.currentStreetViewBearing = bearing;
@@ -216,295 +207,242 @@ export class AnimationUtility {
currentAnimationPhase,
updateAnimationPhase,
updateFrameId,
- }: {
- map: MapRef,
+ }: {
+ map: MapRef;
// path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>,
// startBearing: number,
// startAltitude: number,
// pitch: number,
- currentAnimationPhase: number,
- updateAnimationPhase: (
- newAnimationPhase: number,
- ) => void,
+ currentAnimationPhase: number;
+ updateAnimationPhase: (newAnimationPhase: number) => void;
updateFrameId: (newFrameId: number) => void;
+ }) => {
+ return new Promise<void>(async resolve => {
+ let startTime: number | null = null;
+
+ const frame = async (currentTime: number) => {
+ if (!startTime) startTime = currentTime;
+ const elapsedSinceLastFrame = currentTime - startTime;
+ const phaseIncrement = elapsedSinceLastFrame / this.animationDuration;
+ const animationPhase = currentAnimationPhase + phaseIncrement;
+
+ // when the duration is complete, resolve the promise and stop iterating
+ if (animationPhase > 1) {
+ resolve();
+ return;
+ }
- }) => {
- return new Promise<void>(async (resolve) => {
- let startTime: number | null = null;
-
- const frame = async (currentTime: number) => {
- if (!startTime) startTime = currentTime;
- const elapsedSinceLastFrame = currentTime - startTime;
- const phaseIncrement = elapsedSinceLastFrame / this.animationDuration;
- const animationPhase = currentAnimationPhase + phaseIncrement;
-
- // when the duration is complete, resolve the promise and stop iterating
- if (animationPhase > 1) {
- resolve();
- return;
- }
+ // calculate the distance along the path based on the animationPhase
+ const alongPath = turf.along(this.PATH, this.PATH_DISTANCE * animationPhase).geometry.coordinates;
+
+ const lngLat = {
+ lng: alongPath[0],
+ lat: alongPath[1],
+ };
+
+ let bearing: number;
+ if (this.isStreetViewAnimation) {
+ bearing = this.lerp(this.currentStreetViewBearing, this.calculateBearing(this.previousLngLat, lngLat), 0.032);
+ this.currentStreetViewBearing = bearing;
+ // bearing = this.calculateBearing(this.previousLngLat, lngLat); // TODO: Calculate actual bearing
+ } else {
+ // slowly rotate the map at a constant rate
+ bearing = this.flyInEndBearing - animationPhase * 200.0;
+ // bearing = startBearing - animationPhase * 200.0;
+ }
+
+ runInAction(() => {
+ this.previousLngLat = lngLat;
+ });
+
+ updateAnimationPhase(animationPhase);
-
- // calculate the distance along the path based on the animationPhase
- const alongPath = turf.along(this.PATH, this.PATH_DISTANCE * animationPhase).geometry
- .coordinates;
-
- const lngLat = {
- lng: alongPath[0],
- lat: alongPath[1],
+ // compute corrected camera ground position, so that he leading edge of the path is in view
+ var correctedPosition = this.computeCameraPosition(
+ this.isStreetViewAnimation,
+ this.currentPitch,
+ bearing,
+ lngLat,
+ this.currentAnimationAltitude,
+ true // smooth
+ );
+
+ // set the pitch and bearing of the camera
+ const camera = map.getFreeCameraOptions();
+ camera.setPitchBearing(this.currentPitch, bearing);
+
+ // set the position and altitude of the camera
+ camera.position = MercatorCoordinate.fromLngLat(correctedPosition, this.currentAnimationAltitude);
+
+ // apply the new camera options
+ map.setFreeCameraOptions(camera);
+
+ this.previousAltitude = this.currentAnimationAltitude;
+ this.previousPitch = this.previousPitch;
+
+ // repeat!
+ const innerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(innerFrameId);
};
-
- let bearing: number;
- if (this.isStreetViewAnimation){
- bearing = this.lerp(
- this.currentStreetViewBearing,
- this.calculateBearing(this.previousLngLat, lngLat),
- 0.032
- );
- this.currentStreetViewBearing = bearing;
- // bearing = this.calculateBearing(this.previousLngLat, lngLat); // TODO: Calculate actual bearing
- } else {
- // slowly rotate the map at a constant rate
- bearing = this.flyInEndBearing - animationPhase * 200.0;
- // bearing = startBearing - animationPhase * 200.0;
- }
- runInAction(() => {
- this.previousLngLat = lngLat;
- })
-
- updateAnimationPhase(animationPhase);
-
- // compute corrected camera ground position, so that he leading edge of the path is in view
- var correctedPosition = this.computeCameraPosition(
- this.isStreetViewAnimation,
- this.currentPitch,
- bearing,
- lngLat,
- this.currentAnimationAltitude,
- true // smooth
- );
-
- // set the pitch and bearing of the camera
- const camera = map.getFreeCameraOptions();
- camera.setPitchBearing(this.currentPitch, bearing);
-
-
- // set the position and altitude of the camera
- camera.position = MercatorCoordinate.fromLngLat(
- correctedPosition,
- this.currentAnimationAltitude
- );
-
-
- // apply the new camera options
- map.setFreeCameraOptions(camera);
-
- this.previousAltitude = this.currentAnimationAltitude;
- this.previousPitch = this.previousPitch;
-
- // repeat!
- const innerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(innerFrameId);
- };
-
- const outerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(outerFrameId);
+ const outerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(outerFrameId);
});
- };
+ };
- public flyInAndRotate = async ({
- map,
- updateFrameId
- }:
- {
- map: MapRef,
- updateFrameId: (newFrameId: number) => void
- }
- ) => {
- return new Promise<{bearing: number, altitude: number}>(async (resolve) => {
- let start: number | null;
-
- var currentAltitude;
- var currentBearing;
- var currentPitch;
-
- // the animation frame will run as many times as necessary until the duration has been reached
- const frame = async (time: number) => {
- if (!start) {
- start = time;
- }
-
- // otherwise, use the current time to determine how far along in the duration we are
- let animationPhase = (time - start) / this.flyToDuration;
-
- // because the phase calculation is imprecise, the final zoom can vary
- // if it ended up greater than 1, set it to 1 so that we get the exact endAltitude that was requested
- if (animationPhase > 1) {
- animationPhase = 1;
- }
-
- currentAltitude = this.START_ALTITUDE + (this.flyInEndAltitude - this.START_ALTITUDE) * d3.easeCubicOut(animationPhase)
- // rotate the camera between startBearing and endBearing
- currentBearing = this.flyInStartBearing + (this.flyInEndBearing - this.flyInStartBearing) * d3.easeCubicOut(animationPhase)
-
- currentPitch = this.FLY_IN_START_PITCH + (this.flyInEndPitch - this.FLY_IN_START_PITCH) * d3.easeCubicOut(animationPhase)
-
- // compute corrected camera ground position, so the start of the path is always in view
- var correctedPosition = this.computeCameraPosition(
- false,
- currentPitch,
- currentBearing,
- this.FIRST_LNG_LAT,
- currentAltitude
- );
-
- // set the pitch and bearing of the camera
- const camera = map.getFreeCameraOptions();
- camera.setPitchBearing(currentPitch, currentBearing);
-
- // set the position and altitude of the camera
- camera.position = MercatorCoordinate.fromLngLat(
- correctedPosition,
- currentAltitude
- );
-
- // apply the new camera options
- map.setFreeCameraOptions(camera);
-
- // when the animationPhase is done, resolve the promise so the parent function can move on to the next step in the sequence
- if (animationPhase === 1) {
- resolve({
- bearing: currentBearing,
- altitude: currentAltitude,
- });
-
- // return so there are no further iterations of this frame
- return;
- }
-
- const innerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(innerFrameId);
-
- };
-
- const outerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(outerFrameId);
+ public flyInAndRotate = async ({ map, updateFrameId }: { map: MapRef; updateFrameId: (newFrameId: number) => void }) => {
+ return new Promise<{ bearing: number; altitude: number }>(async resolve => {
+ let start: number | null;
+
+ var currentAltitude;
+ var currentBearing;
+ var currentPitch;
+
+ // the animation frame will run as many times as necessary until the duration has been reached
+ const frame = async (time: number) => {
+ if (!start) {
+ start = time;
+ }
+
+ // otherwise, use the current time to determine how far along in the duration we are
+ let animationPhase = (time - start) / this.flyToDuration;
+
+ // because the phase calculation is imprecise, the final zoom can vary
+ // if it ended up greater than 1, set it to 1 so that we get the exact endAltitude that was requested
+ if (animationPhase > 1) {
+ animationPhase = 1;
+ }
+
+ currentAltitude = this.START_ALTITUDE + (this.flyInEndAltitude - this.START_ALTITUDE) * d3.easeCubicOut(animationPhase);
+ // rotate the camera between startBearing and endBearing
+ currentBearing = this.flyInStartBearing + (this.flyInEndBearing - this.flyInStartBearing) * d3.easeCubicOut(animationPhase);
+
+ currentPitch = this.FLY_IN_START_PITCH + (this.flyInEndPitch - this.FLY_IN_START_PITCH) * d3.easeCubicOut(animationPhase);
+
+ // compute corrected camera ground position, so the start of the path is always in view
+ var correctedPosition = this.computeCameraPosition(false, currentPitch, currentBearing, this.FIRST_LNG_LAT, currentAltitude);
+
+ // set the pitch and bearing of the camera
+ const camera = map.getFreeCameraOptions();
+ camera.setPitchBearing(currentPitch, currentBearing);
+
+ // set the position and altitude of the camera
+ camera.position = MercatorCoordinate.fromLngLat(correctedPosition, currentAltitude);
+
+ // apply the new camera options
+ map.setFreeCameraOptions(camera);
+
+ // when the animationPhase is done, resolve the promise so the parent function can move on to the next step in the sequence
+ if (animationPhase === 1) {
+ resolve({
+ bearing: currentBearing,
+ altitude: currentAltitude,
+ });
+
+ // return so there are no further iterations of this frame
+ return;
+ }
+
+ const innerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(innerFrameId);
+ };
+
+ const outerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(outerFrameId);
});
- };
+ };
- previousCameraPosition: {lng: number, lat: number} | null = null;
+ previousCameraPosition: { lng: number; lat: number } | null = null;
- lerp = (start: number, end: number, amt: number) => {
+ lerp = (start: number, end: number, amt: number) => {
return (1 - amt) * start + amt * end;
- }
-
- computeCameraPosition = (
- isStreetViewAnimation: boolean,
- pitch: number,
- bearing: number,
- targetPosition: {lng: number, lat: number},
- altitude: number,
- smooth = false
- ) => {
+ };
+
+ computeCameraPosition = (isStreetViewAnimation: boolean, pitch: number, bearing: number, targetPosition: { lng: number; lat: number }, altitude: number, smooth = false) => {
const bearingInRadian = (bearing * Math.PI) / 180;
- const pitchInRadian = ((90 - pitch)* Math.PI) / 180;
+ const pitchInRadian = ((90 - pitch) * Math.PI) / 180;
let correctedLng = targetPosition.lng;
let correctedLat = targetPosition.lat;
if (!isStreetViewAnimation) {
- const lngDiff =
- ((altitude / Math.tan(pitchInRadian)) *
- Math.sin(-bearingInRadian)) /
- 70000; // ~70km/degree longitude
- const latDiff =
- ((altitude / Math.tan(pitchInRadian)) *
- Math.cos(-bearingInRadian)) /
- 110000 // 110km/degree latitude
+ const lngDiff = ((altitude / Math.tan(pitchInRadian)) * Math.sin(-bearingInRadian)) / 70000; // ~70km/degree longitude
+ const latDiff = ((altitude / Math.tan(pitchInRadian)) * Math.cos(-bearingInRadian)) / 110000; // 110km/degree latitude
correctedLng = targetPosition.lng + lngDiff;
correctedLat = targetPosition.lat - latDiff;
-
}
-
+
const newCameraPosition = {
- lng: correctedLng,
- lat: correctedLat
+ lng: correctedLng,
+ lat: correctedLat,
};
-
+
if (smooth) {
- if (this.previousCameraPosition) {
- newCameraPosition.lng = this.lerp(newCameraPosition.lng, this.previousCameraPosition.lng, this.SMOOTH_FACTOR);
- newCameraPosition.lat = this.lerp(newCameraPosition.lat, this.previousCameraPosition.lat, this.SMOOTH_FACTOR);
- }
+ if (this.previousCameraPosition) {
+ newCameraPosition.lng = this.lerp(newCameraPosition.lng, this.previousCameraPosition.lng, this.SMOOTH_FACTOR);
+ newCameraPosition.lat = this.lerp(newCameraPosition.lat, this.previousCameraPosition.lat, this.SMOOTH_FACTOR);
+ }
}
-
- this.previousCameraPosition = newCameraPosition
-
+
+ this.previousCameraPosition = newCameraPosition;
+
return newCameraPosition;
- };
-
- public static createGeoJSONCircle = (center: number[], radiusInKm: number, points = 64): Feature<Geometry, GeoJsonProperties>=> {
+ };
+
+ public static createGeoJSONCircle = (center: number[], radiusInKm: number, points = 64): Feature<Geometry, GeoJsonProperties> => {
const coords = {
- latitude: center[1],
- longitude: center[0],
+ latitude: center[1],
+ longitude: center[0],
};
const km = radiusInKm;
const ret = [];
- const distanceX = km / (111.320 * Math.cos((coords.latitude * Math.PI) / 180));
+ const distanceX = km / (111.32 * Math.cos((coords.latitude * Math.PI) / 180));
const distanceY = km / 110.574;
let theta;
let x;
let y;
for (let i = 0; i < points; i += 1) {
- theta = (i / points) * (2 * Math.PI);
- x = distanceX * Math.cos(theta);
- y = distanceY * Math.sin(theta);
- ret.push([coords.longitude + x, coords.latitude + y]);
+ theta = (i / points) * (2 * Math.PI);
+ x = distanceX * Math.cos(theta);
+ y = distanceY * Math.sin(theta);
+ ret.push([coords.longitude + x, coords.latitude + y]);
}
ret.push(ret[0]);
return {
- type: 'Feature',
- geometry: {
- type: 'Polygon',
- coordinates: [ret],
- },
- properties: {}
+ type: 'Feature',
+ geometry: {
+ type: 'Polygon',
+ coordinates: [ret],
+ },
+ properties: {},
};
- }
+ };
- private calculateBearing(
- from: { lng: number; lat: number },
- to: { lng: number; lat: number }
- ): number {
+ private calculateBearing(from: { lng: number; lat: number }, to: { lng: number; lat: number }): number {
const lon1 = from.lng;
const lat1 = from.lat;
const lon2 = to.lng;
const lat2 = to.lat;
-
+
const lon1Rad = (lon1 * Math.PI) / 180;
const lon2Rad = (lon2 * Math.PI) / 180;
const lat1Rad = (lat1 * Math.PI) / 180;
const lat2Rad = (lat2 * Math.PI) / 180;
-
+
const y = Math.sin(lon2Rad - lon1Rad) * Math.cos(lat2Rad);
- const x =
- Math.cos(lat1Rad) * Math.sin(lat2Rad) -
- Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(lon2Rad - lon1Rad);
-
- let bearing = Math.atan2(y,x);
-
+ const x = Math.cos(lat1Rad) * Math.sin(lat2Rad) - Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(lon2Rad - lon1Rad);
+
+ let bearing = Math.atan2(y, x);
+
// Convert bearing from radians to degrees
bearing = (bearing * 180) / Math.PI;
-
+
// Ensure the bearing is within [0, 360)
if (bearing < 0) {
- bearing += 360;
+ bearing += 360;
}
-
- return bearing;
- }
-
-} \ No newline at end of file
+ return bearing;
+ }
+}
diff --git a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
index bf4028f01..f9607becf 100644
--- a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
@@ -1,15 +1,15 @@
-import React = require('react');
-import { observer } from "mobx-react";
-import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
-import { IReactionDisposer, ObservableMap, reaction } from "mobx";
-import { Doc, Opt } from "../../../../fields/Doc";
-import { returnFalse, unimplementedFunction } from "../../../../Utils";
-import { NumCast, StrCast } from "../../../../fields/Types";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { IconButton } from "browndash-components";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { SettingsManager } from "../../../util/SettingsManager";
-import { IconLookup, faAdd, faCalendarDays, faRoute } from "@fortawesome/free-solid-svg-icons";
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { IReactionDisposer, ObservableMap, reaction } from 'mobx';
+import { Doc, Opt } from '../../../../fields/Doc';
+import { returnFalse, unimplementedFunction } from '../../../../Utils';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { IconButton } from 'browndash-components';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { IconLookup, faAdd, faCalendarDays, faRoute } from '@fortawesome/free-solid-svg-icons';
@observer
export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -32,9 +32,9 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
private title: string | undefined = undefined;
- public setPinDoc(pinDoc: Doc){
- this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`) ;
- console.log("Title: ", this.title)
+ public setPinDoc(pinDoc: Doc) {
+ this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`);
+ console.log('Title: ', this.title);
}
public get Active() {
@@ -54,7 +54,7 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
this._disposer = reaction(
- () => SelectionManager.Views().slice(),
+ () => SelectionManager.Views.slice(),
sel => DirectionsAnchorMenu.Instance.fadeOut(true)
);
}
@@ -95,43 +95,28 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
render() {
const buttons = (
- <div className='directions-menu-buttons' style={{display: 'flex'}}>
+ <div className="directions-menu-buttons" style={{ display: 'flex' }}>
<IconButton
tooltip="Add route" //
onPointerDown={this.Delete}
icon={<FontAwesomeIcon icon={faAdd as IconLookup} />}
color={SettingsManager.userColor}
/>
-
-
- <IconButton
- tooltip='Animate route'
- onPointerDown={this.Delete} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faRoute as IconLookup}/>}
- color={SettingsManager.userColor}
- />
- <IconButton
- tooltip='Add to calendar'
- onPointerDown={this.Delete} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup}/>}
- color={SettingsManager.userColor}
- />
+
+ <IconButton tooltip="Animate route" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faRoute as IconLookup} />} color={SettingsManager.userColor} />
+ <IconButton tooltip="Add to calendar" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />
</div>
- )
+ );
return this.getElement(
- <div ref={DirectionsAnchorMenu.top} style={{ height: 'max-content' , width: '100%', display: 'flex', flexDirection: 'column' }}>
+ <div ref={DirectionsAnchorMenu.top} style={{ height: 'max-content', width: '100%', display: 'flex', flexDirection: 'column' }}>
<div>{this.title}</div>
- <div className='direction-inputs' style={{display: 'flex', flexDirection: 'column'}}>
- <input
- placeholder="Origin"
- />
- <input
- placeholder="Destination"
- />
+ <div className="direction-inputs" style={{ display: 'flex', flexDirection: 'column' }}>
+ <input placeholder="Origin" />
+ <input placeholder="Destination" />
</div>
{buttons}
</div>
- )
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index f4e24d9c1..b1fb3368c 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,6 +1,6 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IReactionDisposer, ObservableMap, action, observable, reaction, runInAction } from 'mobx';
+import * as React from 'react';
+import { IReactionDisposer, ObservableMap, action, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, NumListCast, Opt } from '../../../../fields/Doc';
import { returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../../Utils';
@@ -11,29 +11,14 @@ import { Button, IconButton } from 'browndash-components';
import { SettingsManager } from '../../../util/SettingsManager';
import './MapAnchorMenu.scss';
import { NumCast, StrCast } from '../../../../fields/Types';
-import {
- IconLookup,
- faDiamondTurnRight,
- faCalendarDays,
- faEdit,
- faAdd,
- faRoute,
- faArrowLeft,
- faLocationDot,
- faArrowDown,
- faCar,
- faBicycle,
- faPersonWalking,
- faUpload,
- faArrowsRotate,
- } from '@fortawesome/free-solid-svg-icons';
+import { IconLookup, faDiamondTurnRight, faCalendarDays, faEdit, faAdd, faRoute, faArrowLeft, faLocationDot, faArrowDown, faCar, faBicycle, faPersonWalking, faUpload, faArrowsRotate } from '@fortawesome/free-solid-svg-icons';
import { DirectionsAnchorMenu } from './DirectionsAnchorMenu';
import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material';
import { MapboxApiUtility, TransportationType } from './MapboxApiUtility';
import { MapBox } from './MapBox';
import { List } from '../../../../fields/List';
import { MarkerIcons } from './MarkerIcons';
-import { CirclePicker, ColorState } from 'react-color';
+import { CirclePicker, ColorResult } from 'react-color';
import { Position } from 'geojson';
type MapAnchorMenuType = 'standard' | 'routeCreation' | 'calendar' | 'customize' | 'route';
@@ -75,8 +60,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@action
public setMenuType = (menuType: MapAnchorMenuType) => {
this.menuType = menuType;
- }
-
+ };
private allMapPinDocs: Doc[] = [];
@@ -86,31 +70,30 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
private title: string | undefined = undefined;
-
- public setPinDoc(pinDoc: Doc){
+ public setPinDoc(pinDoc: Doc) {
this.pinDoc = pinDoc;
- this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`) ;
+ this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`);
}
- public setRouteDoc(routeDoc: Doc){
+ public setRouteDoc(routeDoc: Doc) {
this.routeDoc = routeDoc;
- this.title = StrCast(routeDoc.title ?? 'Map route')
+ this.title = StrCast(routeDoc.title ?? 'Map route');
}
public setAllMapboxPins(pinDocs: Doc[]) {
this.allMapPinDocs = pinDocs;
pinDocs.forEach((p, idx) => {
console.log(`Pin ${idx}: ${p.title}`);
- })
- }
+ });
+ }
public get Active() {
return this._left > 0;
}
- constructor(props: Readonly<{}>) {
+ constructor(props: any) {
super(props);
-
+ makeObservable(this);
MapAnchorMenu.Instance = this;
MapAnchorMenu.Instance._canFade = false;
}
@@ -125,7 +108,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
this._disposer = reaction(
- () => SelectionManager.Views().slice(),
+ () => SelectionManager.Views.slice(),
sel => MapAnchorMenu.Instance.fadeOut(true)
);
}
@@ -164,55 +147,52 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
// return this.top
// }
-
-
@action
DirectionsClick = () => {
this.menuType = 'routeCreation';
- }
+ };
@action
CustomizeClick = () => {
this.currentRouteInfoMap = undefined;
this.menuType = 'customize';
- }
+ };
@action
BackClick = () => {
this.currentRouteInfoMap = undefined;
this.menuType = 'standard';
- }
+ };
@action
TriggerFileInputClick = () => {
if (this._fileInputRef) {
this._fileInputRef.current?.click(); // Trigger the file input click event
- }
- }
+ }
+ };
- @action
- onMarkerColorChange = (color: ColorState) => {
- if (this.pinDoc){
+ @action
+ onMarkerColorChange = (color: ColorResult) => {
+ if (this.pinDoc) {
this.pinDoc.markerColor = color.hex;
}
- }
+ };
revertToOriginalMarker = () => {
if (this.pinDoc) {
- this.pinDoc.markerType = "MAP_PIN";
- this.pinDoc.markerColor = "#ff5722";
+ this.pinDoc.markerType = 'MAP_PIN';
+ this.pinDoc.markerColor = '#ff5722';
}
- }
+ };
onMarkerIconChange = (iconKey: string) => {
if (this.pinDoc) {
this.pinDoc.markerType = iconKey;
}
- }
-
+ };
@observable
- destinationFeatures: any[] = []
+ destinationFeatures: any[] = [];
@observable
destinationSelected: boolean = false;
@@ -220,7 +200,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable
selectedDestinationFeature: any = undefined;
- @observable
+ @observable
createPinForDestination: boolean = true;
@observable
@@ -231,98 +211,85 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@action
handleTransportationTypeChange = (newType: TransportationType) => {
- if (newType !== this.selectedTransportationType){
+ if (newType !== this.selectedTransportationType) {
this.selectedTransportationType = newType;
this.DisplayRoute(this.currentRouteInfoMap, newType);
}
-
- }
+ };
@action
handleSelectedDestinationFeature = (destinationFeature: any) => {
this.selectedDestinationFeature = destinationFeature;
- }
+ };
@action
toggleCreatePinForDestinationCheckbox = () => {
this.createPinForDestination = !this.createPinForDestination;
- }
+ };
@action
handleDestinationSearchChange = async (searchText: string) => {
if (this.selectedDestinationFeature !== undefined) this.selectedDestinationFeature = undefined;
const features = await MapboxApiUtility.forwardGeocodeForFeatures(searchText);
- if (features){
+ if (features) {
runInAction(() => {
this.destinationFeatures = features;
-
- })
+ });
}
- }
+ };
getRoutes = async (destinationFeature: any) => {
const currentPinLong: number = NumCast(this.pinDoc?.longitude);
const currentPinLat: number = NumCast(this.pinDoc?.latitude);
- if (currentPinLong && currentPinLat && destinationFeature.center){
+ if (currentPinLong && currentPinLat && destinationFeature.center) {
const routeInfoMap = await MapboxApiUtility.getDirections([currentPinLong, currentPinLat], destinationFeature.center);
if (routeInfoMap) {
runInAction(() => {
this.currentRouteInfoMap = routeInfoMap;
- })
+ });
this.DisplayRoute(routeInfoMap, 'driving');
}
}
- // get route menu, set it equal to here
- // create a temporary route
+ // get route menu, set it equal to here
+ // create a temporary route
// create pin if createPinForDestination was clicked
- }
+ };
HandleAddRouteClick = () => {
- if (this.currentRouteInfoMap && this.selectedTransportationType && this.selectedDestinationFeature){
+ if (this.currentRouteInfoMap && this.selectedTransportationType && this.selectedDestinationFeature) {
const coordinates = this.currentRouteInfoMap[this.selectedTransportationType].coordinates;
console.log(coordinates);
console.log(this.selectedDestinationFeature);
- this.AddNewRouteToMap(coordinates, this.title ?? "", this.selectedDestinationFeature, this.createPinForDestination);
+ this.AddNewRouteToMap(coordinates, this.title ?? '', this.selectedDestinationFeature, this.createPinForDestination);
this.HideRoute();
}
- }
+ };
getMarkerIcon = (): JSX.Element | undefined => {
- if (this.pinDoc){
+ if (this.pinDoc) {
const markerType = StrCast(this.pinDoc.markerType);
const markerColor = StrCast(this.pinDoc.markerColor);
return MarkerIcons.getFontAwesomeIcon(markerType, '2x', markerColor);
}
return undefined;
- }
-
+ };
render() {
const buttons = (
- <div className='menu-buttons' style={{display: 'flex'}}>
- {this.menuType === 'standard' &&
+ <div className="menu-buttons" style={{ display: 'flex' }}>
+ {this.menuType === 'standard' && (
<>
<IconButton
- tooltip="Delete Pin" //
- onPointerDown={this.Delete}
- icon={<FontAwesomeIcon icon="trash-alt" />}
- color={SettingsManager.userColor}
- />
- <IconButton
- tooltip='Get directions'
- onPointerDown={this.DirectionsClick} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faDiamondTurnRight as IconLookup}/>}
- color={SettingsManager.userColor}
- />
- <IconButton
- tooltip='Add to calendar'
- onPointerDown={this.Delete} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup}/>}
+ tooltip="Delete Pin" //
+ onPointerDown={this.Delete}
+ icon={<FontAwesomeIcon icon="trash-alt" />}
color={SettingsManager.userColor}
/>
+ <IconButton tooltip="Get directions" onPointerDown={this.DirectionsClick} /**TODO: fix */ icon={<FontAwesomeIcon icon={faDiamondTurnRight as IconLookup} />} color={SettingsManager.userColor} />
+ <IconButton tooltip="Add to calendar" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />
<div ref={this._commentRef}>
<IconButton
tooltip="Link Note to Pin" //
@@ -331,12 +298,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
</div>
- <IconButton
- tooltip="Customize pin"
- onPointerDown={this.CustomizeClick}
- icon={<FontAwesomeIcon icon={faEdit as IconLookup}/>}
- color={SettingsManager.userColor}
- />
+ <IconButton tooltip="Customize pin" onPointerDown={this.CustomizeClick} icon={<FontAwesomeIcon icon={faEdit as IconLookup} />} color={SettingsManager.userColor} />
<IconButton
tooltip="Center on pin" //
onPointerDown={this.Center}
@@ -344,8 +306,8 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
</>
- }
- {this.menuType === 'routeCreation' &&
+ )}
+ {this.menuType === 'routeCreation' && (
<>
<IconButton
tooltip="Go back" //
@@ -360,8 +322,8 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
</>
- }
- {this.menuType === 'route' &&
+ )}
+ {this.menuType === 'route' && (
<>
<IconButton
tooltip="Delete Route" //
@@ -369,12 +331,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
icon={<FontAwesomeIcon icon="trash-alt" />}
color={SettingsManager.userColor}
/>
- <IconButton
- tooltip='Animate route'
- onPointerDown={() => this.OpenAnimationPanel(this.routeDoc)} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faRoute as IconLookup}/>}
- color={SettingsManager.userColor}
- />
+ <IconButton tooltip="Animate route" onPointerDown={() => this.OpenAnimationPanel(this.routeDoc)} /**TODO: fix */ icon={<FontAwesomeIcon icon={faRoute as IconLookup} />} color={SettingsManager.userColor} />
<div ref={this._commentRef}>
<IconButton
tooltip="Link Note to Pin" //
@@ -383,17 +340,10 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
</div>
- <IconButton
- tooltip='Add to calendar'
- onPointerDown={this.Delete} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup}/>}
- color={SettingsManager.userColor}
- />
-
+ <IconButton tooltip="Add to calendar" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />
</>
-
- }
- {this.menuType === 'customize' &&
+ )}
+ {this.menuType === 'customize' && (
<>
<IconButton
tooltip="Go back" //
@@ -408,9 +358,8 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
</>
- }
-
-
+ )}
+
{/* {this.IsTargetToggler !== returnFalse && (
<Toggle
tooltip={'Make target visibility toggle on click'}
@@ -432,68 +381,37 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
// )
return this.getElement(
- <div
- ref={MapAnchorMenu.top}
- className='map-anchor-menu-container'>
- {this.menuType === 'standard' &&
- <div>{this.title}</div>
- }
- {this.menuType === 'routeCreation' &&
- <div className='direction-inputs' style={{display: 'flex', flexDirection: 'column'}}>
- <TextField
- fullWidth
- disabled
- value={this.title}
- />
- <FontAwesomeIcon icon={faArrowDown as IconLookup} size='xs'/>
+ <div ref={MapAnchorMenu.top} className="map-anchor-menu-container">
+ {this.menuType === 'standard' && <div>{this.title}</div>}
+ {this.menuType === 'routeCreation' && (
+ <div className="direction-inputs" style={{ display: 'flex', flexDirection: 'column' }}>
+ <TextField fullWidth disabled value={this.title} />
+ <FontAwesomeIcon icon={faArrowDown as IconLookup} size="xs" />
<Autocomplete
fullWidth
id="route-destination-searcher"
-
- onInputChange={(e, searchText) => this.handleDestinationSearchChange(searchText)}
- onChange={(e, feature, reason) => {
- if (reason === 'clear'){
+ onInputChange={(e: any, searchText: any) => this.handleDestinationSearchChange(searchText)}
+ onChange={(e: any, feature: any, reason: any) => {
+ if (reason === 'clear') {
this.handleSelectedDestinationFeature(undefined);
- } else if (reason === 'selectOption'){
+ } else if (reason === 'selectOption') {
this.handleSelectedDestinationFeature(feature);
}
}}
- options={this.destinationFeatures
- .filter(feature => feature.place_name)
- .map(feature => feature)}
- getOptionLabel={(feature) => feature.place_name}
- renderInput={(params) => (
- <TextField
- {...params}
- placeholder='Enter a destination'
- />
- )}
+ options={this.destinationFeatures.filter(feature => feature.place_name).map(feature => feature)}
+ getOptionLabel={(feature: any) => feature.place_name}
+ renderInput={(params: any) => <TextField {...params} placeholder="Enter a destination" />}
/>
- {this.selectedDestinationFeature &&
+ {this.selectedDestinationFeature && (
<>
- {!this.allMapPinDocs.some(pinDoc => pinDoc.title === this.selectedDestinationFeature.place_name) &&
- <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '5px'}}>
- <FormControlLabel
- label='Create pin for destination?'
- control={
- <Checkbox
- color='success'
- checked={this.createPinForDestination}
- onChange={this.toggleCreatePinForDestinationCheckbox}
- />
- }
- />
- </div>
- }
+ {!this.allMapPinDocs.some(pinDoc => pinDoc.title === this.selectedDestinationFeature.place_name) && (
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '5px' }}>
+ <FormControlLabel label="Create pin for destination?" control={<Checkbox color="success" checked={this.createPinForDestination} onChange={this.toggleCreatePinForDestinationCheckbox} />} />
+ </div>
+ )}
</>
-
-
- }
- <button
- id='get-routes-button'
- disabled={this.selectedDestinationFeature ? false : true}
- onClick={() => this.getRoutes(this.selectedDestinationFeature)}
- >
+ )}
+ <button id="get-routes-button" disabled={this.selectedDestinationFeature ? false : true} onClick={() => this.getRoutes(this.selectedDestinationFeature)}>
Get routes
</button>
@@ -501,74 +419,58 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
placeholder="Origin"
/> */}
</div>
- }
- {this.currentRouteInfoMap &&
- <div className='current-route-info-container'>
- <div className='transportation-icons-container'>
+ )}
+ {this.currentRouteInfoMap && (
+ <div className="current-route-info-container">
+ <div className="transportation-icons-container">
<IconButton
- tooltip="Driving route"
+ tooltip="Driving route"
onPointerDown={() => this.handleTransportationTypeChange('driving')}
- icon={<FontAwesomeIcon icon={faCar as IconLookup}/>}
- color={this.selectedTransportationType === 'driving' ? 'lightblue': 'grey'}
+ icon={<FontAwesomeIcon icon={faCar as IconLookup} />}
+ color={this.selectedTransportationType === 'driving' ? 'lightblue' : 'grey'}
/>
<IconButton
- tooltip="Cycling route"
+ tooltip="Cycling route"
onPointerDown={() => this.handleTransportationTypeChange('cycling')}
- icon={<FontAwesomeIcon icon={faBicycle as IconLookup}/>}
- color={this.selectedTransportationType === 'cycling' ? 'lightblue': 'grey'}
+ icon={<FontAwesomeIcon icon={faBicycle as IconLookup} />}
+ color={this.selectedTransportationType === 'cycling' ? 'lightblue' : 'grey'}
/>
<IconButton
- tooltip="Walking route"
+ tooltip="Walking route"
onPointerDown={() => this.handleTransportationTypeChange('walking')}
- icon={<FontAwesomeIcon icon={faPersonWalking as IconLookup}/>}
- color={this.selectedTransportationType === 'walking' ? 'lightblue': 'grey'}
+ icon={<FontAwesomeIcon icon={faPersonWalking as IconLookup} />}
+ color={this.selectedTransportationType === 'walking' ? 'lightblue' : 'grey'}
/>
</div>
- <div className='selected-route-details-container'>
+ <div className="selected-route-details-container">
<div>Duration: {this.currentRouteInfoMap[this.selectedTransportationType].duration}</div>
<div>Distance: {this.currentRouteInfoMap[this.selectedTransportationType].distance}</div>
</div>
</div>
-
-
- }
- {this.menuType === 'customize' &&
- <div className='customized-marker-container'>
- <div className='current-marker-container'>
- <div>Current Marker: </div>
- <div>
- {this.getMarkerIcon()}
+ )}
+ {this.menuType === 'customize' && (
+ <div className="customized-marker-container">
+ <div className="current-marker-container">
+ <div>Current Marker: </div>
+ <div>{this.getMarkerIcon()}</div>
</div>
+ <div className="color-picker-container" style={{ marginBottom: '10px' }}>
+ <CirclePicker circleSize={15} circleSpacing={7} width="100%" onChange={color => this.onMarkerColorChange(color)} />
+ </div>
+ <div className="all-markers-container">
+ {Object.keys(MarkerIcons.FAMarkerIconsMap).map(iconKey => (
+ <div key={iconKey} className="marker-icon">
+ <IconButton onPointerDown={() => this.onMarkerIconChange(iconKey)} icon={MarkerIcons.getFontAwesomeIcon(iconKey, '1x', 'white')} />
+ </div>
+ ))}
+ </div>
+ <div style={{ width: '100%', height: '3px', color: 'white' }}></div>
</div>
- <div className='color-picker-container' style={{marginBottom: '10px'}}>
- <CirclePicker
- circleSize={15}
- circleSpacing={7}
- width='100%'
- onChange={(color) => this.onMarkerColorChange(color)}
- />
- </div>
- <div className='all-markers-container'>
- {Object.keys(MarkerIcons.FAMarkerIconsMap).map((iconKey) => (
- <div key={iconKey} className='marker-icon'>
- <IconButton
- onPointerDown={() => this.onMarkerIconChange(iconKey)}
- icon={MarkerIcons.getFontAwesomeIcon(iconKey, '1x', 'white')}
- />
- </div>
- ))}
- </div>
- <div style={{width: '100%', height:'3px', color: 'white'}}></div>
- </div>
- }
- {this.menuType === 'route' && this.routeDoc &&
- <div>
- {StrCast(this.routeDoc.title)}
- </div>
-
- }
+ )}
+ {this.menuType === 'route' && this.routeDoc && <div>{StrCast(this.routeDoc.title)}</div>}
{buttons}
- </div>
- , true);
+ </div>,
+ true
+ );
}
}
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index e25261729..434e02b27 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables.scss';
+@import '../../global/globalCssVariables.module.scss';
.mapBox {
width: 100%;
height: 100%;
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 6bad7d724..fdd8285d5 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -1,597 +1,597 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
-import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { Docs } from '../../../documents/Documents';
-import { DragManager } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { UndoManager } from '../../../util/UndoManager';
-import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
-import { Colors } from '../../global/globalEnums';
-import { AnchorMenu } from '../../pdf/AnchorMenu';
-import { Annotation } from '../../pdf/Annotation';
-import { SidebarAnnos } from '../../SidebarAnnos';
-import { FieldView, FieldViewProps } from '../FieldView';
-import { PinProps } from '../trails';
-import './MapBox2.scss';
-import { MapBoxInfoWindow } from './MapBoxInfoWindow';
-
-/**
- * MapBox2 architecture:
- * Main component: MapBox2.tsx
- * Supporting Components: SidebarAnnos, CollectionStackingView
- *
- * MapBox2 is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
- * The main body of MapBox2 uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
- * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
- * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
- * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
- * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
- */
-
-// const _global = (window /* browser */ || global /* node */) as any;
-
-const mapContainerStyle = {
- height: '100%',
-};
-
-const defaultCenter = {
- lat: 42.360081,
- lng: -71.058884,
-};
-
-const mapOptions = {
- fullscreenControl: false,
-};
-
-const apiKey = process.env.GOOGLE_MAPS;
-
-const script = document.createElement('script');
-script.defer = true;
-script.async = true;
-script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
-console.log(script.src);
-document.head.appendChild(script);
-
-/**
- * Consider integrating later: allows for drawing, circling, making shapes on map
- */
-// const drawingManager = new window.google.maps.drawing.DrawingManager({
-// drawingControl: true,
-// drawingControlOptions: {
-// position: google.maps.ControlPosition.TOP_RIGHT,
-// drawingModes: [
-// google.maps.drawing.OverlayType.MARKER,
-// // currently we are not supporting the following drawing mode on map, a thought for future development
-// google.maps.drawing.OverlayType.CIRCLE,
-// google.maps.drawing.OverlayType.POLYLINE,
-// ],
-// },
-// });
-
-// options for searchbox in Google Maps Places Autocomplete API
-const options = {
- fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields
- strictBounds: false,
- types: ['establishment'], // type pf places, subject of change according to user need
-} as google.maps.places.AutocompleteOptions;
-
-@observer
-export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
- private _dropDisposer?: DragManager.DragDropDisposer;
- private _disposers: { [name: string]: IReactionDisposer } = {};
- private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
- @observable private _overlayAnnoInfo: Opt<Doc>;
- showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(MapBox2, fieldKey);
- }
- public get SidebarKey() {
- return this.fieldKey + '_sidebar';
- }
- private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
- @computed get inlineTextAnnotations() {
- return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
- }
-
- @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
- @observable private selectedPlace: Doc | undefined;
- @observable private markerMap: { [id: string]: google.maps.Marker } = {};
- @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
- @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);
- @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
- @computed get allSidebarDocs() {
- return DocListCast(this.dataDoc[this.SidebarKey]);
- }
- @computed get allMapMarkers() {
- return DocListCast(this.dataDoc[this.annotationKey]);
- }
- @observable private toggleAddMarker = false;
-
- @observable _showSidebar = false;
- @computed get SidebarShown() {
- return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
- }
-
- static _canAnnotate = true;
- static _hadSelection: boolean = false;
- private _sidebarRef = React.createRef<SidebarAnnos>();
- private _ref: React.RefObject<HTMLDivElement> = React.createRef();
-
- componentDidMount() {
- this.props.setContentView?.(this);
- }
-
- @action
- private setSearchBox = (searchBox: any) => {
- this.searchBox = searchBox;
- };
-
- // iterate allMarkers to size, center, and zoom map to contain all markers
- private fitBounds = (map: google.maps.Map) => {
- const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
- const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
- !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
- };
-
- /**
- * Custom control for add marker button
- * @param controlDiv
- * @param map
- */
- private CenterControl = () => {
- const controlDiv = document.createElement('div');
- controlDiv.className = 'MapBox2-addMarker';
- // Set CSS for the control border.
- const controlUI = document.createElement('div');
- controlUI.style.backgroundColor = '#fff';
- controlUI.style.borderRadius = '3px';
- controlUI.style.cursor = 'pointer';
- controlUI.style.marginTop = '10px';
- controlUI.style.borderRadius = '4px';
- controlUI.style.marginBottom = '22px';
- controlUI.style.textAlign = 'center';
- controlUI.style.position = 'absolute';
- controlUI.style.width = '32px';
- controlUI.style.height = '32px';
- controlUI.title = 'Click to toggle marker mode. In marker mode, click on map to place a marker.';
-
- const plIcon = document.createElement('img');
- plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png';
- plIcon.style.color = 'rgb(25,25,25)';
- plIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
- plIcon.style.fontSize = '16px';
- plIcon.style.lineHeight = '32px';
- plIcon.style.left = '18';
- plIcon.style.top = '15';
- plIcon.style.position = 'absolute';
- plIcon.width = 14;
- plIcon.height = 14;
- plIcon.innerHTML = 'Add';
- controlUI.appendChild(plIcon);
-
- // Set CSS for the control interior.
- const markerIcon = document.createElement('img');
- markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png';
- markerIcon.style.color = 'rgb(25,25,25)';
- markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
- markerIcon.style.fontSize = '16px';
- markerIcon.style.lineHeight = '32px';
- markerIcon.style.left = '-2';
- markerIcon.style.top = '1';
- markerIcon.width = 30;
- markerIcon.height = 30;
- markerIcon.style.position = 'absolute';
- markerIcon.innerHTML = 'Add';
- controlUI.appendChild(markerIcon);
-
- // Setup the click event listeners
- controlUI.addEventListener('click', () => {
- if (this.toggleAddMarker === true) {
- this.toggleAddMarker = false;
- console.log('add marker button status:' + this.toggleAddMarker);
- controlUI.style.backgroundColor = '#fff';
- markerIcon.style.color = 'rgb(25,25,25)';
- } else {
- this.toggleAddMarker = true;
- console.log('add marker button status:' + this.toggleAddMarker);
- controlUI.style.backgroundColor = '#4476f7';
- markerIcon.style.color = 'rgb(255,255,255)';
- }
- });
- controlDiv.appendChild(controlUI);
- return controlDiv;
- };
-
- /**
- * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
- * @param position - the LatLng position where the marker is placed
- * @param map
- */
- @action
- private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
- const marker = new google.maps.Marker({
- position: position,
- map: map,
- });
- map.panTo(position);
- const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
- this.addDocument(mapMarker, this.annotationKey);
- };
-
- _loadPending = true;
- /**
- * store a reference to google map instance
- * setup the drawing manager on the top right corner of map
- * fit map bounds to contain all markers
- * @param map
- */
- @action
- private loadHandler = (map: google.maps.Map) => {
- this._map = map;
- this._loadPending = true;
- const centerControlDiv = this.CenterControl();
- map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
- //drawingManager.setMap(map);
- // if (navigator.geolocation) {
- // navigator.geolocation.getCurrentPosition(
- // (position: Position) => {
- // const pos = {
- // lat: position.coords.latitude,
- // lng: position.coords.longitude,
- // };
- // this._map.setCenter(pos);
- // }
- // );
- // } else {
- // alert("Your geolocation is not supported by browser.")
- // };
- map.setZoom(NumCast(this.dataDoc.map_zoom, 2.5));
- map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
- setTimeout(() => {
- if (this._loadPending && this._map.getBounds()) {
- this._loadPending = false;
- this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
- }
- }, 250);
- // listener to addmarker event
- this._map.addListener('click', (e: MouseEvent) => {
- if (this.toggleAddMarker === true) {
- this.placeMarker((e as any).latLng, map);
- }
- });
- };
-
- @action
- centered = () => {
- if (this._loadPending && this._map.getBounds()) {
- this._loadPending = false;
- this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
- }
- this.dataDoc.mapLat = this._map.getCenter()?.lat();
- this.dataDoc.mapLng = this._map.getCenter()?.lng();
- };
-
- @action
- zoomChanged = () => {
- if (this._loadPending && this._map.getBounds()) {
- this._loadPending = false;
- this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
- }
- this.dataDoc.map_zoom = this._map.getZoom();
- };
-
- /**
- * Load and render all map markers
- * @param marker
- * @param place
- */
- @action
- private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
- place[Id] ? (this.markerMap[place[Id]] = marker) : null;
- };
-
- /**
- * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
- * @param e
- * @param place
- */
- @action
- private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
- // set which place was clicked
- this.selectedPlace = place;
- place.infoWindowOpen = true;
- };
-
- /**
- * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
- * @param doc
- * @param sidebarKey
- * @returns
- */
- sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
- console.log('print all sidebar Docs');
- if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
- const docs = doc instanceof Doc ? [doc] : doc;
- docs.forEach(doc => {
- if (doc.lat !== undefined && doc.lng !== undefined) {
- const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
- if (existingMarker) {
- Doc.AddDocToList(existingMarker, 'data', doc);
- } else {
- const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
- this.addDocument(marker, this.annotationKey);
- }
- }
- }); //add to annotation list
-
- return this.addDocument(doc, sidebarKey); // add to sidebar list
- };
-
- /**
- * Removing documents from the sidebar
- * @param doc
- * @param sidebarKey
- * @returns
- */
- sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
- if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
- const docs = doc instanceof Doc ? [doc] : doc;
- return this.removeDocument(doc, sidebarKey);
- };
-
- /**
- * Toggle sidebar onclick the tiny comment button on the top right corner
- * @param e
- */
- sidebarBtnDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(
- this,
- e,
- (e, down, delta) =>
- runInAction(() => {
- const localDelta = this.props
- .ScreenToLocalTransform()
- .scale(this.props.NativeDimScaling?.() || 1)
- .transformDirection(delta[0], delta[1]);
- const fullWidth = NumCast(this.layoutDoc._width);
- const mapWidth = fullWidth - this.sidebarWidth();
- if (this.sidebarWidth() + localDelta[0] > 0) {
- this._showSidebar = true;
- this.layoutDoc._width = fullWidth + localDelta[0];
- this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
- } else {
- this._showSidebar = false;
- this.layoutDoc._width = mapWidth;
- this.layoutDoc._layout_sidebarWidthPercent = '0%';
- }
- return false;
- }),
- emptyFunction,
- () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
- );
- };
-
- sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
- @computed get layout_sidebarWidthPercent() {
- return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
- }
- @computed get sidebarColor() {
- return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
- }
-
- /**
- * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
- * add a customized temporary marker on the map
- */
- @action
- private handlePlaceChanged = () => {
- const place = this.searchBox.getPlace();
-
- if (!place.geometry || !place.geometry.location) {
- // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
- window.alert("No details available for input: '" + place.name + "'");
- return;
- }
-
- // zoom in on the location of the search result
- if (place.geometry.viewport) {
- this._map.fitBounds(place.geometry.viewport);
- } else {
- this._map.setCenter(place.geometry.location);
- this._map.setZoom(17);
- }
-
- // customize icon => customized icon for the nature of the location selected
- const icon = {
- url: place.icon as string,
- size: new google.maps.Size(71, 71),
- origin: new google.maps.Point(0, 0),
- anchor: new google.maps.Point(17, 34),
- scaledSize: new google.maps.Size(25, 25),
- };
-
- // put temporary cutomized marker on searched location
- this.searchMarkers.forEach(marker => {
- marker.setMap(null);
- });
- this.searchMarkers = [];
- this.searchMarkers.push(
- new window.google.maps.Marker({
- map: this._map,
- icon,
- title: place.name,
- position: place.geometry.location,
- })
- );
- };
-
- /**
- * Handles toggle of sidebar on click the little comment button
- */
- @computed get sidebarHandle() {
- return (
- <div
- className="MapBox2-overlayButton-sidebar"
- key="sidebar"
- title="Toggle Sidebar"
- style={{
- display: !this.props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
- backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
- }}
- onPointerDown={this.sidebarBtnDown}>
- <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
- </div>
- );
- }
-
- // TODO: Adding highlight box layer to Maps
- @action
- toggleSidebar = () => {
- //1.2 * w * ? = .2 * w .2/1.2
- const prevWidth = this.sidebarWidth();
- this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
- this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
- };
-
- sidebarDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
- };
- sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
- const bounds = this._ref.current!.getBoundingClientRect();
- this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
- this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
- e.preventDefault();
- return false;
- };
-
- setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
-
- addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
- return this.addDocument(doc, annotationKey);
- };
-
- pointerEvents = () => {
- return this.props.isContentActive() === false ? 'none' : this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
- };
- @computed get annotationLayer() {
- return (
- <div className="MapBox2-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
- {this.inlineTextAnnotations
- .sort((a, b) => NumCast(a.y) - NumCast(b.y))
- .map(anno => (
- <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
- ))}
- </div>
- );
- }
-
- getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
-
- /**
- * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
- * @returns
- */
- private renderMarkers = () => {
- return this.allMapMarkers.map(place => (
- <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
- ));
- };
-
- // TODO: auto center on select a document in the sidebar
- private handleMapCenter = (map: google.maps.Map) => {
- // console.log("print the selected views in selectionManager:")
- // if (SelectionManager.Views().lastElement()) {
- // console.log(SelectionManager.Views().lastElement());
- // }
- };
-
- 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.TransparentBackgroundFilter];
- opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
- infoWidth = () => this.props.PanelWidth() / 5;
- infoHeight = () => this.props.PanelHeight() / 5;
- anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
- savedAnnotations = () => this._savedAnnotations;
-
- get MicrosoftMaps() {
- return (window as any).Microsoft.Maps;
- }
- render() {
- const renderAnnotations = (childFilters?: () => string[]) => null;
- return (
- <div className="MapBox2" ref={this._ref}>
- <div
- className="MapBox2-wrapper"
- onWheel={e => e.stopPropagation()}
- onPointerDown={async e => {
- e.button === 0 && !e.ctrlKey && e.stopPropagation();
- }}
- style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
- <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
- {renderAnnotations(this.opaqueFilter)}
- {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
- {this.annotationLayer}
-
- <div>
- <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
- <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
- <input className="MapBox2-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
- </Autocomplete>
-
- {this.renderMarkers()}
- {this.allMapMarkers
- .filter(marker => marker.infoWindowOpen)
- .map(marker => (
- <MapBoxInfoWindow
- key={marker[Id]}
- {...this.props}
- setContentView={emptyFunction}
- place={marker}
- markerMap={this.markerMap}
- PanelWidth={this.infoWidth}
- PanelHeight={this.infoHeight}
- moveDocument={this.moveDocument}
- isAnyChildContentActive={this.isAnyChildContentActive}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- />
- ))}
- {/* {this.handleMapCenter(this._map)} */}
- </GoogleMap>
- </div>
- </div>
- {/* </LoadScript > */}
- <div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
- <SidebarAnnos
- ref={this._sidebarRef}
- {...this.props}
- fieldKey={this.fieldKey}
- rootDoc={this.rootDoc}
- layoutDoc={this.layoutDoc}
- dataDoc={this.dataDoc}
- usePanelWidth={true}
- showSidebar={this.SidebarShown}
- nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- PanelWidth={this.sidebarWidth}
- sidebarAddDocument={this.sidebarAddDocument}
- moveDocument={this.moveDocument}
- removeDocument={this.sidebarRemoveDocument}
- />
- </div>
- {this.sidebarHandle}
- </div>
- );
- }
-}
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
+// import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
+// import { observer } from 'mobx-react';
+// import * as React from 'react';
+// import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
+// import { Id } from '../../../../fields/FieldSymbols';
+// import { NumCast, StrCast } from '../../../../fields/Types';
+// import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
+// import { Docs } from '../../../documents/Documents';
+// import { DragManager } from '../../../util/DragManager';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { UndoManager } from '../../../util/UndoManager';
+// import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
+// import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
+// import { Colors } from '../../global/globalEnums';
+// import { AnchorMenu } from '../../pdf/AnchorMenu';
+// import { Annotation } from '../../pdf/Annotation';
+// import { SidebarAnnos } from '../../SidebarAnnos';
+// import { FieldView, FieldViewProps } from '../FieldView';
+// import { PinProps } from '../trails';
+// import './MapBox2.scss';
+// import { MapBoxInfoWindow } from './MapBoxInfoWindow';
+
+// /**
+// * MapBox2 architecture:
+// * Main component: MapBox2.tsx
+// * Supporting Components: SidebarAnnos, CollectionStackingView
+// *
+// * MapBox2 is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
+// * The main body of MapBox2 uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
+// * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
+// * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
+// * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
+// * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
+// */
+
+// // const _global = (window /* browser */ || global /* node */) as any;
+
+// const mapContainerStyle = {
+// height: '100%',
+// };
+
+// const defaultCenter = {
+// lat: 42.360081,
+// lng: -71.058884,
+// };
+
+// const mapOptions = {
+// fullscreenControl: false,
+// };
+
+// const apiKey = process.env.GOOGLE_MAPS;
+
+// const script = document.createElement('script');
+// script.defer = true;
+// script.async = true;
+// script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
+// console.log(script.src);
+// document.head.appendChild(script);
+
+// /**
+// * Consider integrating later: allows for drawing, circling, making shapes on map
+// */
+// // const drawingManager = new window.google.maps.drawing.DrawingManager({
+// // drawingControl: true,
+// // drawingControlOptions: {
+// // position: google.maps.ControlPosition.TOP_RIGHT,
+// // drawingModes: [
+// // google.maps.drawing.OverlayType.MARKER,
+// // // currently we are not supporting the following drawing mode on map, a thought for future development
+// // google.maps.drawing.OverlayType.CIRCLE,
+// // google.maps.drawing.OverlayType.POLYLINE,
+// // ],
+// // },
+// // });
+
+// // options for searchbox in Google Maps Places Autocomplete API
+// const options = {
+// fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields
+// strictBounds: false,
+// types: ['establishment'], // type pf places, subject of change according to user need
+// } as google.maps.places.AutocompleteOptions;
+
+// @observer
+// export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
+// private _dropDisposer?: DragManager.DragDropDisposer;
+// private _disposers: { [name: string]: IReactionDisposer } = {};
+// private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+// @observable private _overlayAnnoInfo: Opt<Doc>;
+// showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
+// public static LayoutString(fieldKey: string) {
+// return FieldView.LayoutString(MapBox2, fieldKey);
+// }
+// public get SidebarKey() {
+// return this.fieldKey + '_sidebar';
+// }
+// private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
+// @computed get inlineTextAnnotations() {
+// return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
+// }
+
+// @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
+// @observable private selectedPlace: Doc | undefined = undefined;
+// @observable private markerMap: { [id: string]: google.maps.Marker } = {};
+// @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
+// @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);
+// @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+// @computed get allSidebarDocs() {
+// return DocListCast(this.dataDoc[this.SidebarKey]);
+// }
+// @computed get allMapMarkers() {
+// return DocListCast(this.dataDoc[this.annotationKey]);
+// }
+// @observable private toggleAddMarker = false;
+
+// @observable _showSidebar = false;
+// @computed get SidebarShown() {
+// return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
+// }
+
+// static _canAnnotate = true;
+// static _hadSelection: boolean = false;
+// private _sidebarRef = React.createRef<SidebarAnnos>();
+// private _ref: React.RefObject<HTMLDivElement> = React.createRef();
+
+// componentDidMount() {
+// this.props.setContentView?.(this);
+// }
+
+// @action
+// private setSearchBox = (searchBox: any) => {
+// this.searchBox = searchBox;
+// };
+
+// // iterate allMarkers to size, center, and zoom map to contain all markers
+// private fitBounds = (map: google.maps.Map) => {
+// const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
+// const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
+// !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
+// };
+
+// /**
+// * Custom control for add marker button
+// * @param controlDiv
+// * @param map
+// */
+// private CenterControl = () => {
+// const controlDiv = document.createElement('div');
+// controlDiv.className = 'MapBox2-addMarker';
+// // Set CSS for the control border.
+// const controlUI = document.createElement('div');
+// controlUI.style.backgroundColor = '#fff';
+// controlUI.style.borderRadius = '3px';
+// controlUI.style.cursor = 'pointer';
+// controlUI.style.marginTop = '10px';
+// controlUI.style.borderRadius = '4px';
+// controlUI.style.marginBottom = '22px';
+// controlUI.style.textAlign = 'center';
+// controlUI.style.position = 'absolute';
+// controlUI.style.width = '32px';
+// controlUI.style.height = '32px';
+// controlUI.title = 'Click to toggle marker mode. In marker mode, click on map to place a marker.';
+
+// const plIcon = document.createElement('img');
+// plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png';
+// plIcon.style.color = 'rgb(25,25,25)';
+// plIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+// plIcon.style.fontSize = '16px';
+// plIcon.style.lineHeight = '32px';
+// plIcon.style.left = '18';
+// plIcon.style.top = '15';
+// plIcon.style.position = 'absolute';
+// plIcon.width = 14;
+// plIcon.height = 14;
+// plIcon.innerHTML = 'Add';
+// controlUI.appendChild(plIcon);
+
+// // Set CSS for the control interior.
+// const markerIcon = document.createElement('img');
+// markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png';
+// markerIcon.style.color = 'rgb(25,25,25)';
+// markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+// markerIcon.style.fontSize = '16px';
+// markerIcon.style.lineHeight = '32px';
+// markerIcon.style.left = '-2';
+// markerIcon.style.top = '1';
+// markerIcon.width = 30;
+// markerIcon.height = 30;
+// markerIcon.style.position = 'absolute';
+// markerIcon.innerHTML = 'Add';
+// controlUI.appendChild(markerIcon);
+
+// // Setup the click event listeners
+// controlUI.addEventListener('click', () => {
+// if (this.toggleAddMarker === true) {
+// this.toggleAddMarker = false;
+// console.log('add marker button status:' + this.toggleAddMarker);
+// controlUI.style.backgroundColor = '#fff';
+// markerIcon.style.color = 'rgb(25,25,25)';
+// } else {
+// this.toggleAddMarker = true;
+// console.log('add marker button status:' + this.toggleAddMarker);
+// controlUI.style.backgroundColor = '#4476f7';
+// markerIcon.style.color = 'rgb(255,255,255)';
+// }
+// });
+// controlDiv.appendChild(controlUI);
+// return controlDiv;
+// };
+
+// /**
+// * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
+// * @param position - the LatLng position where the marker is placed
+// * @param map
+// */
+// @action
+// private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
+// const marker = new google.maps.Marker({
+// position: position,
+// map: map,
+// });
+// map.panTo(position);
+// const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
+// this.addDocument(mapMarker, this.annotationKey);
+// };
+
+// _loadPending = true;
+// /**
+// * store a reference to google map instance
+// * setup the drawing manager on the top right corner of map
+// * fit map bounds to contain all markers
+// * @param map
+// */
+// @action
+// private loadHandler = (map: google.maps.Map) => {
+// this._map = map;
+// this._loadPending = true;
+// const centerControlDiv = this.CenterControl();
+// map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
+// //drawingManager.setMap(map);
+// // if (navigator.geolocation) {
+// // navigator.geolocation.getCurrentPosition(
+// // (position: Position) => {
+// // const pos = {
+// // lat: position.coords.latitude,
+// // lng: position.coords.longitude,
+// // };
+// // this._map.setCenter(pos);
+// // }
+// // );
+// // } else {
+// // alert("Your geolocation is not supported by browser.")
+// // };
+// map.setZoom(NumCast(this.dataDoc.map_zoom, 2.5));
+// map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
+// setTimeout(() => {
+// if (this._loadPending && this._map.getBounds()) {
+// this._loadPending = false;
+// this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
+// }
+// }, 250);
+// // listener to addmarker event
+// this._map.addListener('click', (e: MouseEvent) => {
+// if (this.toggleAddMarker === true) {
+// this.placeMarker((e as any).latLng, map);
+// }
+// });
+// };
+
+// @action
+// centered = () => {
+// if (this._loadPending && this._map.getBounds()) {
+// this._loadPending = false;
+// this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
+// }
+// this.dataDoc.mapLat = this._map.getCenter()?.lat();
+// this.dataDoc.mapLng = this._map.getCenter()?.lng();
+// };
+
+// @action
+// zoomChanged = () => {
+// if (this._loadPending && this._map.getBounds()) {
+// this._loadPending = false;
+// this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
+// }
+// this.dataDoc.map_zoom = this._map.getZoom();
+// };
+
+// /**
+// * Load and render all map markers
+// * @param marker
+// * @param place
+// */
+// @action
+// private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
+// place[Id] ? (this.markerMap[place[Id]] = marker) : null;
+// };
+
+// /**
+// * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
+// * @param e
+// * @param place
+// */
+// @action
+// private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
+// // set which place was clicked
+// this.selectedPlace = place;
+// place.infoWindowOpen = true;
+// };
+
+// /**
+// * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
+// * @param doc
+// * @param sidebarKey
+// * @returns
+// */
+// sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
+// console.log('print all sidebar Docs');
+// if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
+// const docs = doc instanceof Doc ? [doc] : doc;
+// docs.forEach(doc => {
+// if (doc.lat !== undefined && doc.lng !== undefined) {
+// const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
+// if (existingMarker) {
+// Doc.AddDocToList(existingMarker, 'data', doc);
+// } else {
+// const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
+// this.addDocument(marker, this.annotationKey);
+// }
+// }
+// }); //add to annotation list
+
+// return this.addDocument(doc, sidebarKey); // add to sidebar list
+// };
+
+// /**
+// * Removing documents from the sidebar
+// * @param doc
+// * @param sidebarKey
+// * @returns
+// */
+// sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
+// if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
+// const docs = doc instanceof Doc ? [doc] : doc;
+// return this.removeDocument(doc, sidebarKey);
+// };
+
+// /**
+// * Toggle sidebar onclick the tiny comment button on the top right corner
+// * @param e
+// */
+// sidebarBtnDown = (e: React.PointerEvent) => {
+// setupMoveUpEvents(
+// this,
+// e,
+// (e, down, delta) =>
+// runInAction(() => {
+// const localDelta = this.props
+// .ScreenToLocalTransform()
+// .scale(this.props.NativeDimScaling?.() || 1)
+// .transformDirection(delta[0], delta[1]);
+// const fullWidth = NumCast(this.layoutDoc._width);
+// const mapWidth = fullWidth - this.sidebarWidth();
+// if (this.sidebarWidth() + localDelta[0] > 0) {
+// this._showSidebar = true;
+// this.layoutDoc._width = fullWidth + localDelta[0];
+// this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+// } else {
+// this._showSidebar = false;
+// this.layoutDoc._width = mapWidth;
+// this.layoutDoc._layout_sidebarWidthPercent = '0%';
+// }
+// return false;
+// }),
+// emptyFunction,
+// () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
+// );
+// };
+
+// sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+// @computed get layout_sidebarWidthPercent() {
+// return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
+// }
+// @computed get sidebarColor() {
+// return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
+// }
+
+// /**
+// * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
+// * add a customized temporary marker on the map
+// */
+// @action
+// private handlePlaceChanged = () => {
+// const place = this.searchBox.getPlace();
+
+// if (!place.geometry || !place.geometry.location) {
+// // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
+// window.alert("No details available for input: '" + place.name + "'");
+// return;
+// }
+
+// // zoom in on the location of the search result
+// if (place.geometry.viewport) {
+// this._map.fitBounds(place.geometry.viewport);
+// } else {
+// this._map.setCenter(place.geometry.location);
+// this._map.setZoom(17);
+// }
+
+// // customize icon => customized icon for the nature of the location selected
+// const icon = {
+// url: place.icon as string,
+// size: new google.maps.Size(71, 71),
+// origin: new google.maps.Point(0, 0),
+// anchor: new google.maps.Point(17, 34),
+// scaledSize: new google.maps.Size(25, 25),
+// };
+
+// // put temporary cutomized marker on searched location
+// this.searchMarkers.forEach(marker => {
+// marker.setMap(null);
+// });
+// this.searchMarkers = [];
+// this.searchMarkers.push(
+// new window.google.maps.Marker({
+// map: this._map,
+// icon,
+// title: place.name,
+// position: place.geometry.location,
+// })
+// );
+// };
+
+// /**
+// * Handles toggle of sidebar on click the little comment button
+// */
+// @computed get sidebarHandle() {
+// return (
+// <div
+// className="MapBox2-overlayButton-sidebar"
+// key="sidebar"
+// title="Toggle Sidebar"
+// style={{
+// display: !this.props.isContentActive() ? 'none' : undefined,
+// top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
+// backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+// }}
+// onPointerDown={this.sidebarBtnDown}>
+// <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
+// </div>
+// );
+// }
+
+// // TODO: Adding highlight box layer to Maps
+// @action
+// toggleSidebar = () => {
+// //1.2 * w * ? = .2 * w .2/1.2
+// const prevWidth = this.sidebarWidth();
+// this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
+// this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+// };
+
+// sidebarDown = (e: React.PointerEvent) => {
+// setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
+// };
+// sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
+// const bounds = this._ref.current!.getBoundingClientRect();
+// this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+// this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
+// e.preventDefault();
+// return false;
+// };
+
+// setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
+
+// addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
+// return this.addDocument(doc, annotationKey);
+// };
+
+// pointerEvents = () => {
+// return this.props.isContentActive() === false ? 'none' : this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.IsDragging ? undefined : 'none';
+// };
+// @computed get annotationLayer() {
+// return (
+// <div className="MapBox2-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+// {this.inlineTextAnnotations
+// .sort((a, b) => NumCast(a.y) - NumCast(b.y))
+// .map(anno => (
+// <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
+// ))}
+// </div>
+// );
+// }
+
+// getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.Document;
+
+// /**
+// * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
+// * @returns
+// */
+// private renderMarkers = () => {
+// return this.allMapMarkers.map(place => (
+// <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
+// ));
+// };
+
+// // TODO: auto center on select a document in the sidebar
+// private handleMapCenter = (map: google.maps.Map) => {
+// // console.log("print the selected views in selectionManager:")
+// // if (SelectionManager.Views.lastElement()) {
+// // console.log(SelectionManager.Views.lastElement());
+// // }
+// };
+
+// 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.TransparentBackgroundFilter];
+// opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
+// infoWidth = () => this.props.PanelWidth() / 5;
+// infoHeight = () => this.props.PanelHeight() / 5;
+// anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
+// savedAnnotations = () => this._savedAnnotations;
+
+// get MicrosoftMaps() {
+// return (window as any).Microsoft.Maps;
+// }
+// render() {
+// const renderAnnotations = (childFilters?: () => string[]) => null;
+// return (
+// <div className="MapBox2" ref={this._ref}>
+// <div
+// className="MapBox2-wrapper"
+// onWheel={e => e.stopPropagation()}
+// onPointerDown={async e => {
+// e.button === 0 && !e.ctrlKey && e.stopPropagation();
+// }}
+// style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+// <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
+// {renderAnnotations(this.opaqueFilter)}
+// {SnappingManager.IsDragging ? null : renderAnnotations()}
+// {this.annotationLayer}
+
+// <div>
+// <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
+// <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
+// <input className="MapBox2-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
+// </Autocomplete>
+
+// {this.renderMarkers()}
+// {this.allMapMarkers
+// .filter(marker => marker.infoWindowOpen)
+// .map(marker => (
+// <MapBoxInfoWindow
+// key={marker[Id]}
+// {...this.props}
+// setContentView={emptyFunction}
+// place={marker}
+// markerMap={this.markerMap}
+// PanelWidth={this.infoWidth}
+// PanelHeight={this.infoHeight}
+// moveDocument={this.moveDocument}
+// isAnyChildContentActive={this.isAnyChildContentActive}
+// whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+// />
+// ))}
+// {/* {this.handleMapCenter(this._map)} */}
+// </GoogleMap>
+// </div>
+// </div>
+// {/* </LoadScript > */}
+// <div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+// <SidebarAnnos
+// ref={this._sidebarRef}
+// {...this.props}
+// fieldKey={this.fieldKey}
+// Document={this.Document}
+// layoutDoc={this.layoutDoc}
+// dataDoc={this.dataDoc}
+// usePanelWidth={true}
+// showSidebar={this.SidebarShown}
+// nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
+// whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+// PanelWidth={this.sidebarWidth}
+// sidebarAddDocument={this.sidebarAddDocument}
+// moveDocument={this.moveDocument}
+// removeDocument={this.sidebarRemoveDocument}
+// />
+// </div>
+// {this.sidebarHandle}
+// </div>
+// );
+// }
+// }
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 66c47d131..a9c6ba22c 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -1,96 +1,95 @@
-import { InfoWindow } from '@react-google-maps/api';
-import { action } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { emptyFunction, returnAll, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils';
-import { Docs } from '../../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
-import { CollectionNoteTakingView } from '../../collections/CollectionNoteTakingView';
-import { CollectionStackingView } from '../../collections/CollectionStackingView';
-import { ViewBoxAnnotatableProps } from '../../DocComponent';
-import { FieldViewProps } from '../FieldView';
-import { FormattedTextBox } from '../formattedText/FormattedTextBox';
-import './MapBox.scss';
+// import { InfoWindow } from '@react-google-maps/api';
+// import { action } from 'mobx';
+// import { observer } from 'mobx-react';
+// import * as React from 'react';
+// import { Doc } from '../../../../fields/Doc';
+// import { Id } from '../../../../fields/FieldSymbols';
+// import { emptyFunction, returnAll, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils';
+// import { Docs } from '../../../documents/Documents';
+// import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+// import { CollectionNoteTakingView } from '../../collections/CollectionNoteTakingView';
+// import { CollectionStackingView } from '../../collections/CollectionStackingView';
+// import { ViewBoxAnnotatableProps } from '../../DocComponent';
+// import { FieldViewProps } from '../FieldView';
+// import { FormattedTextBox } from '../formattedText/FormattedTextBox';
+// import './MapBox.scss';
-interface MapBoxInfoWindowProps {
- place: Doc;
- renderDepth: number;
- markerMap: { [id: string]: google.maps.Marker };
- isAnyChildContentActive: () => boolean;
-}
-@observer
-export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps> {
- @action
- private handleInfoWindowClose = () => {
- if (this.props.place.infoWindowOpen) {
- this.props.place.infoWindowOpen = false;
- }
- this.props.place.infoWindowOpen = false;
- };
+// interface MapBoxInfoWindowProps {
+// place: Doc;
+// renderDepth: number;
+// markerMap: { [id: string]: google.maps.Marker };
+// isAnyChildContentActive: () => boolean;
+// }
+// @observer
+// export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps> {
+// @action
+// private handleInfoWindowClose = () => {
+// if (this.props.place.infoWindowOpen) {
+// this.props.place.infoWindowOpen = false;
+// }
+// this.props.place.infoWindowOpen = false;
+// };
- addNoteClick = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
- const newBox = Docs.Create.TextDocument('Note', { _layout_autoHeight: true });
- FormattedTextBox.SelectOnLoad = newBox[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
- Doc.AddDocToList(this.props.place, 'data', newBox);
- this._stack?.scrollToBottom();
- e.stopPropagation();
- e.preventDefault();
- });
- };
+// addNoteClick = (e: React.PointerEvent) => {
+// setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
+// const newDoc = Docs.Create.TextDocument('Note', { _layout_autoHeight: true });
+// FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+// Doc.AddDocToList(this.props.place, 'data', newDoc);
+// this._stack?.scrollToBottom();
+// e.stopPropagation();
+// e.preventDefault();
+// });
+// };
- _stack: CollectionStackingView | CollectionNoteTakingView | null | undefined;
- childLayoutFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
- addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
- removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
- render() {
- return (
- <InfoWindow
- // anchor={this.props.markerMap[this.props.place[Id]]}
- onCloseClick={this.handleInfoWindowClose}>
- <div className="mapbox-infowindow">
- <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
- <CollectionStackingView
- ref={r => (this._stack = r)}
- {...this.props}
- setContentView={emptyFunction}
- Document={this.props.place}
- DataDoc={undefined}
- fieldKey="data"
- NativeWidth={returnZero}
- NativeHeight={returnZero}
- childFilters={returnEmptyFilter}
- setHeight={emptyFunction}
- isAnnotationOverlay={false}
- select={emptyFunction}
- NativeDimScaling={returnOne}
- isContentActive={returnTrue}
- chromeHidden={true}
- rootSelected={returnFalse}
- childHideResizeHandles={returnTrue}
- childHideDecorationTitle={returnTrue}
- childLayoutFitWidth={this.childLayoutFitWidth}
- // childDocumentsActive={returnFalse}
- removeDocument={this.removeDoc}
- addDocument={this.addDoc}
- renderDepth={this.props.renderDepth + 1}
- type_collection={CollectionViewType.Stacking}
- pointerEvents={returnAll}
- />
- </div>
- <hr />
- <div
- onPointerDown={this.addNoteClick}
- onClick={e => {
- e.stopPropagation();
- e.preventDefault();
- }}>
- Add Note
- </div>
- </div>
- </InfoWindow>
- );
- }
-}
+// _stack: CollectionStackingView | CollectionNoteTakingView | null | undefined;
+// childLayoutFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
+// addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
+// removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
+// render() {
+// return (
+// <InfoWindow
+// // anchor={this.props.markerMap[this.props.place[Id]]}
+// onCloseClick={this.handleInfoWindowClose}>
+// <div className="mapbox-infowindow">
+// <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
+// <CollectionStackingView
+// ref={r => (this._stack = r)}
+// {...this.props}
+// setContentView={emptyFunction}
+// Document={this.props.place}
+// TemplateDataDocument={undefined}
+// fieldKey="data"
+// NativeWidth={returnZero}
+// NativeHeight={returnZero}
+// childFilters={returnEmptyFilter}
+// setHeight={emptyFunction}
+// isAnnotationOverlay={false}
+// select={emptyFunction}
+// NativeDimScaling={returnOne}
+// isContentActive={returnTrue}
+// chromeHidden={true}
+// childHideResizeHandles={true}
+// childHideDecorationTitle={true}
+// childLayoutFitWidth={this.childLayoutFitWidth}
+// // childDocumentsActive={returnFalse}
+// removeDocument={this.removeDoc}
+// addDocument={this.addDoc}
+// renderDepth={this.props.renderDepth + 1}
+// type_collection={CollectionViewType.Stacking}
+// pointerEvents={returnAll}
+// />
+// </div>
+// <hr />
+// <div
+// onPointerDown={this.addNoteClick}
+// onClick={e => {
+// e.stopPropagation();
+// e.preventDefault();
+// }}>
+// Add Note
+// </div>
+// </div>
+// </InfoWindow>
+// );
+// }
+// }
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
index 56f0a49b8..8760c8600 100644
--- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -1,15 +1,11 @@
-import { observer } from 'mobx-react';
-// import { SettingsManager } from '../../../util/SettingsManager';
+import * as React from 'react';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from '../FieldView';
-import React = require('react');
-import { computed } from 'mobx';
import { MapBox } from './MapBox';
/**
* Map Pushpin doc class
*/
-@observer
export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(MapPushpinBox, fieldKey);
@@ -21,11 +17,11 @@ export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.mapBoxView.deletePushpin(this.Document);
}
- @computed get mapBoxView() {
- return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as MapBox;
+ get mapBoxView() {
+ return this.props.DocumentView?.()?._props.docViewPath().lastElement()?.ComponentView as MapBox;
}
- @computed get mapBox() {
- return this.props.DocumentView?.().props.docViewPath().lastElement()?.Document;
+ get mapBox() {
+ return this.props.DocumentView?.()._props.docViewPath().lastElement()?.Document;
}
render() {
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index a6182991d..6a14427c0 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -43,7 +43,7 @@ import { MapProvider, Map as MapboxMap } from 'react-map-gl';
* A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
*/
-const mapboxApiKey = "pk.eyJ1IjoiemF1bHRhdmFuZ2FyIiwiYSI6ImNsbnc2eHJpbTA1ZTUyam85aGx4Z2FhbGwifQ.2Kqw9mk-9wAAg9kmHmKzcg";
+const mapboxApiKey = 'pk.eyJ1IjoiemF1bHRhdmFuZ2FyIiwiYSI6ImNsbnc2eHJpbTA1ZTUyam85aGx4Z2FhbGwifQ.2Kqw9mk-9wAAg9kmHmKzcg';
const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/ and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
/**
@@ -198,7 +198,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
title="Toggle Sidebar"
style={{
display: !this.props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
+ top: StrCast(this.Document._layout_showTitle) === 'title' ? 20 : 5,
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
}}
onPointerDown={this.sidebarBtnDown}>
@@ -230,8 +230,8 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
});
const targetCreator = (annotationOn: Doc | undefined) => {
- const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
- FormattedTextBox.SelectOnLoad = target[Id];
+ const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
+ FormattedTextBox.SetSelectOnLoad(target);
return target;
};
const docView = this.props.DocumentView?.();
@@ -324,7 +324,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
};
@observable
- bingSearchBarContents: any = this.rootDoc.map; // For Bing Maps: The contents of the Bing search bar (string)
+ bingSearchBarContents: any = this.Document.map; // For Bing Maps: The contents of the Bing search bar (string)
geoDataRequestOptions = {
entityType: 'PopulatedPlace',
@@ -357,9 +357,9 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
deselectPin = () => {
if (this.selectedPin) {
// Removes filter
- Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
- Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
- Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
+ Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'remove');
+ Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'remove');
+ Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
const temp = this.selectedPin;
if (!this._unmounting) {
@@ -372,7 +372,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
}
this.map_docToPinMap.set(temp, newpin);
this.selectedPin = undefined;
- this.bingSearchBarContents = this.rootDoc.map;
+ this.bingSearchBarContents = this.Document.map;
}
};
@@ -389,9 +389,9 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
this.selectedPin = pinDoc;
this.bingSearchBarContents = pinDoc.map;
- // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
- // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
- Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
+ // Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'match');
+ // Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'match');
+ Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
this.recolorPin(this.selectedPin, 'green');
@@ -460,8 +460,8 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps, existingPin?: Doc) => {
/// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER
const anchor = Docs.Create.ConfigDocument({
- title: 'MapAnchor:' + this.rootDoc.title,
- text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location',
+ title: 'MapAnchor:' + this.Document.title,
+ text: StrCast(this.selectedPin?.map) || StrCast(this.Document.map) || 'map location',
config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude),
config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude),
config_map_zoom: NumCast(this.dataDoc.map_zoom),
@@ -469,15 +469,15 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
config_map: StrCast((existingPin ?? this.selectedPin)?.map) || StrCast(this.dataDoc.map),
layout_unrendered: true,
mapPin: existingPin ?? this.selectedPin,
- annotationOn: this.rootDoc,
+ annotationOn: this.Document,
});
if (anchor) {
if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
addAsAnnotation && this.addDocument(anchor);
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.Document);
return anchor;
}
- return this.rootDoc;
+ return this.Document;
};
map_docToPinMap = new Map<Doc, any>();
@@ -524,9 +524,9 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
deleteSelectedPin = undoable(() => {
if (this.selectedPin) {
// Removes filter
- Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
- Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
- Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
+ Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'remove');
+ Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'remove');
+ Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
this.removePushpin(this.selectedPin);
}
@@ -614,7 +614,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));
this._disposers.mapLocation = reaction(
- () => this.rootDoc.map,
+ () => this.Document.map,
mapLoc => (this.bingSearchBarContents = mapLoc),
{ fireImmediately: true }
);
@@ -639,7 +639,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
);
this._disposers.location = reaction(
- () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.map_zoom, mapType: this.rootDoc.map_type }),
+ () => ({ lat: this.Document.latitude, lng: this.Document.longitude, zoom: this.Document.map_zoom, mapType: this.Document.map_type }),
locationObject => {
// if (this._bingMap.current)
try {
@@ -690,7 +690,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
}
},
e => {
- const createPin = () => this.createPushpin(this.rootDoc.latitude, this.rootDoc.longitude, this.rootDoc.map);
+ const createPin = () => this.createPushpin(this.Document.latitude, this.Document.longitude, this.Document.map);
if (this.bingSearchBarContents) {
this.bingSearch().then(createPin);
} else createPin();
@@ -706,7 +706,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
render() {
// bcz: no idea what's going on here, but bings maps have some kind of bug
// such that we need to delay rendering a second map on startup until the first map is rendered.
- this.rootDoc[DocCss];
+ this.Document[DocCss];
if (MapBoxContainer._rerenderDelay) {
// prettier-ignore
this._rerenderTimeout = this._rerenderTimeout ??
@@ -715,7 +715,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
MapBoxContainer._rerenderDelay = 0;
}
this._rerenderTimeout = undefined;
- this.rootDoc[DocCss] = this.rootDoc[DocCss] + 1;
+ this.Document[DocCss] = this.Document[DocCss] + 1;
}), MapBoxContainer._rerenderDelay);
return null;
}
@@ -732,7 +732,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
<div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
{renderAnnotations(this.opaqueFilter)}
- {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
+ {SnappingManager.IsDragging ? null : renderAnnotations()}
<div className="mapBox-searchbar">
<EditableText
@@ -758,15 +758,10 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
</div>
</div>
<MapProvider>
- <MapboxMap
- id="mabox-map"
- mapStyle={`mapbox://styles/mapbox/streets-v9`}
- mapboxAccessToken={mapboxApiKey}
- />
+ <MapboxMap id="mabox-map" mapStyle={`mapbox://styles/mapbox/streets-v9`} mapboxAccessToken={mapboxApiKey} />
</MapProvider>
-
-
-{/*
+
+ {/*
<BingMapsReact
onMapReady={this.bingMapReady} //
bingMapsKey={bingApiKey}
@@ -786,7 +781,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
{...this.props}
renderDepth={this.props.renderDepth + 1}
Document={pushpin}
- DataDoc={undefined}
PanelWidth={returnOne}
PanelHeight={returnOne}
NativeWidth={returnOne}
@@ -824,7 +818,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
ref={this._sidebarRef}
{...this.props}
fieldKey={this.fieldKey}
- rootDoc={this.rootDoc}
+ Document={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
usePanelWidth={true}
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 8a68f9647..0f5e25a0c 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -1,4 +1,4 @@
-@import "../global/globalCssVariables.scss";
+@import '../global/globalCssVariables.module.scss';
.pdfBox,
.pdfBox-interactive {
@@ -38,7 +38,7 @@
box-shadow: $standard-box-shadow;
transition: 0.2s;
- &:hover{
+ &:hover {
filter: brightness(0.85);
}
}
@@ -51,7 +51,8 @@
left: 5px;
top: 5px;
- .pdfBox-fwdBtn, .pdfBox-backBtn {
+ .pdfBox-fwdBtn,
+ .pdfBox-backBtn {
background: #121721;
height: 25px;
width: 25px;
@@ -119,7 +120,6 @@
background: none;
}
-
.pdfBox-settingsCont {
position: absolute;
right: 0;
@@ -194,7 +194,7 @@
justify-content: center;
align-items: center;
overflow: hidden;
- transition: left .5s;
+ transition: left 0.5s;
pointer-events: all;
.pdfBox-searchBar {
@@ -204,7 +204,6 @@
}
}
-
.pdfBox-title-outer {
width: 100%;
height: 100%;
@@ -269,7 +268,6 @@
// CSS adjusted for mobile devices
@media only screen and (max-device-width: 480px) {
-
.pdfBox .pdfBox-ui .pdfBox-settingsCont .pdfBox-settingsButton,
.pdfBox-interactive .pdfBox-ui .pdfBox-settingsCont .pdfBox-settingsButton {
height: 60px;
@@ -288,15 +286,11 @@
}
}
-
-
.pdfBox .pdfBox-ui .pdfBox-settingsCont .pdfBox-settingsFlyout,
.pdfBox-interactive .pdfBox-ui .pdfBox-settingsCont .pdfBox-settingsFlyout {
font-size: 30px;
}
-
-
.pdfBox .pdfBox-ui .pdfBox-overlayCont,
.pdfBox-interactive .pdfBox-ui .pdfBox-overlayCont {
height: 60px;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 1035116d5..733febd2d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,8 +1,9 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as Pdfjs from 'pdfjs-dist';
import 'pdfjs-dist/web/pdf_viewer.css';
+import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
@@ -31,7 +32,6 @@ import { FieldView, FieldViewProps } from './FieldView';
import { ImageBox } from './ImageBox';
import './PDFBox.scss';
import { PinProps, PresBox } from './trails';
-import React = require('react');
@observer
export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -52,7 +52,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable private _pageControls = false;
@computed get pdfUrl() {
- return Cast(this.dataDoc[this.props.fieldKey], PdfField);
+ return Cast(this.dataDoc[this._props.fieldKey], PdfField);
}
@computed get pdfThumb() {
return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url;
@@ -60,6 +60,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
constructor(props: any) {
super(props);
+ makeObservable(this);
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 = NumCast(this.Document._width) * (nh / nw));
@@ -97,11 +98,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (!region) return;
const cropping = Doc.MakeCopy(region, true);
Doc.GetProto(region).lockedPosition = true;
- Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
+ Doc.GetProto(region).title = 'region:' + this.Document.title;
Doc.GetProto(region).followLinkToggle = true;
this.addDocument(region);
- const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
+ const docViewContent = this._props.docViewPath().lastElement().ContentDiv!;
const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
newDiv.style.width = NumCast(this.layoutDoc._width).toString();
newDiv.style.height = NumCast(this.layoutDoc._height).toString();
@@ -110,19 +111,19 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const anchx = NumCast(cropping.x);
const anchy = NumCast(cropping.y);
- const anchw = NumCast(cropping._width) * (this.props.NativeDimScaling?.() || 1);
- const anchh = NumCast(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);
- cropping.y = NumCast(this.rootDoc.y);
+ cropping.title = 'crop: ' + this.Document.title;
+ cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width);
+ cropping.y = NumCast(this.Document.y);
cropping._width = anchw;
cropping._height = anchh;
cropping.onClick = undefined;
const croppingProto = Doc.GetProto(cropping);
croppingProto.annotationOn = undefined;
croppingProto.isDataDoc = true;
- croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
+ croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
croppingProto.type = DocumentType.IMG;
croppingProto.layout = ImageBox.LayoutString('data');
croppingProto.data = new ImageField(Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png'));
@@ -131,7 +132,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (addCrop) {
DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
}
- this.props.bringToFront(cropping);
+ this._props.bringToFront(cropping);
CreateImage(
'',
@@ -139,8 +140,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
htmlString,
anchw,
anchh,
- (NumCast(region.y) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']),
- (NumCast(region.x) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']),
+ (NumCast(region.y) * this._props.PanelWidth()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']),
+ (NumCast(region.x) * this._props.PanelWidth()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']),
4
)
.then((data_url: any) => {
@@ -162,7 +163,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
updateIcon = () => {
// currently we render pdf icons as text labels
- const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
+ const docViewContent = this._props.docViewPath().lastElement().ContentDiv!;
const filename = this.layoutDoc[Id] + '-icon' + new Date().getTime();
this._pdfViewer?._mainCont.current &&
CollectionFreeFormView.UpdateIcon(
@@ -170,10 +171,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
docViewContent,
NumCast(this.layoutDoc._width),
NumCast(this.layoutDoc._height),
- this.props.PanelWidth(),
- this.props.PanelHeight(),
+ this._props.PanelWidth(),
+ this._props.PanelHeight(),
NumCast(this.layoutDoc._layout_scrollTop),
- NumCast(this.rootDoc[this.fieldKey + '_nativeHeight'], 1),
+ NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], 1),
true,
this.layoutDoc[Id] + '-icon',
(iconFile: string, nativeWidth: number, nativeHeight: number) => {
@@ -190,33 +191,33 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
Object.values(this._disposers).forEach(disposer => disposer?.());
}
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
this._disposers.select = reaction(
- () => this.props.isSelected(),
+ () => this._props.isSelected(),
() => {
document.removeEventListener('keydown', this.onKeyDown);
- this.props.isSelected() && document.addEventListener('keydown', this.onKeyDown);
+ this._props.isSelected() && document.addEventListener('keydown', this.onKeyDown);
},
{ fireImmediately: true }
);
this._disposers.scroll = reaction(
- () => this.rootDoc.layout_scrollTop,
+ () => this.layoutDoc.layout_scrollTop,
() => {
- if (!(ComputedField.WithoutComputed(() => FieldValue(this.props.Document[this.SidebarKey + '_panY'])) instanceof ComputedField)) {
- this.props.Document[this.SidebarKey + '_panY'] = ComputedField.MakeFunction('this.layout_scrollTop');
+ if (!(ComputedField.WithoutComputed(() => FieldValue(this.Document[this.SidebarKey + '_panY'])) instanceof ComputedField)) {
+ this.Document[this.SidebarKey + '_panY'] = ComputedField.MakeFunction('this.layout_scrollTop');
}
- this.props.Document[this.SidebarKey + '_freeform_scale'] = 1;
- this.props.Document[this.SidebarKey + '_freeform_panX'] = 0;
+ this.layoutDoc[this.SidebarKey + '_freeform_scale'] = 1;
+ this.layoutDoc[this.SidebarKey + '_freeform_panX'] = 0;
}
);
}
sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
- if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
+ if (DocListCast(this.Document[this._props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
this.toggleSidebar(false);
return true;
}
- return this.props.addDocTab(doc, where);
+ return this._props.addDocTab(doc, where);
};
focus = (anchor: Doc, options: DocFocusOptions) => {
this._initialScrollTarget = anchor;
@@ -236,12 +237,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
const docAnchor = () =>
Docs.Create.ConfigDocument({
- title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._layout_scrollTop)?.toFixed(0)),
- annotationOn: this.rootDoc,
+ title: StrCast(this.Document.title + '@' + NumCast(this.layoutDoc._layout_scrollTop)?.toFixed(0)),
+ annotationOn: this.Document,
});
const visibleAnchor = this._pdfViewer?._getAnchor?.(this._pdfViewer.savedAnnotations(), true);
const anchor = visibleAnchor ?? docAnchor();
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.Document);
anchor.text = ele?.textContent ?? '';
anchor.text_html = ele?.innerHTML;
addAsAnnotation && this.addDocument(anchor);
@@ -251,7 +252,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
loaded = (nw: number, nh: number, np: number) => {
- this.dataDoc[this.props.fieldKey + '_numPages'] = np;
+ 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 = NumCast(this.layoutDoc._width) / (Doc.NativeAspect(this.dataDoc) || 1);
@@ -276,7 +277,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return true;
};
public forwardPage = () => {
- this.Document._layout_curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + '_numPages']), (NumCast(this.Document._layout_curPage) || 1) + 1);
+ this.Document._layout_curPage = Math.min(NumCast(this.dataDoc[this._props.fieldKey + '_numPages']), (NumCast(this.Document._layout_curPage) || 1) + 1);
return true;
};
public gotoPage = (p: number) => (this.Document._layout_curPage = p);
@@ -300,7 +301,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
setPdfViewer = (pdfViewer: PDFViewer) => {
this._pdfViewer = pdfViewer;
- const docView = this.props.DocumentView?.();
+ const docView = this._props.DocumentView?.();
if (this._initialScrollTarget && docView) {
this.focus(this._initialScrollTarget, { instant: true });
this._initialScrollTarget = undefined;
@@ -321,13 +322,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this,
e,
(e, down, delta) => {
- const localDelta = this.props
+ const localDelta = this._props
.ScreenToLocalTransform()
- .scale(this.props.NativeDimScaling?.() || 1)
+ .scale(this._props.NativeDimScaling?.() || 1)
.transformDirection(delta[0], delta[1]);
const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
- const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
+ 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 = NumCast(this.layoutDoc._width) + localDelta[0]);
@@ -372,12 +373,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
);
const searchTitle = `${!this._searching ? 'Open' : 'Close'} Search Bar`;
const curPage = NumCast(this.Document._layout_curPage) || 1;
- return !this.props.isContentActive() || this._pdfViewer?.isAnnotating ? null : (
+ return !this._props.isContentActive() || this._pdfViewer?.isAnnotating ? null : (
<div
className="pdfBox-ui"
onKeyDown={e => ([KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true)}
onPointerDown={e => e.stopPropagation()}
- style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}>
+ style={{ display: this._props.isContentActive() ? 'flex' : 'none' }}>
<div className="pdfBox-overlayCont" onPointerDown={e => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
<button className="pdfBox-overlayButton" title={searchTitle} />
<input
@@ -431,10 +432,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (!this.SidebarShown) return 0;
if (this._previewWidth) return PDFBox.sidebarResizerWidth + PDFBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
- return PDFBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
+ return PDFBox.sidebarResizerWidth + nativeDiff * (this._props.NativeDimScaling?.() || 1);
};
@undoBatch
- toggleSidebarType = () => (this.rootDoc[this.SidebarKey + '_type_collection'] = this.rootDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform);
+ toggleSidebarType = () => (this.dataDoc[this.SidebarKey + '_type_collection'] = this.dataDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform);
specificContextMenu = (e: React.MouseEvent): void => {
const cm = ContextMenu.Instance;
const options = cm.findByDescription('Options...');
@@ -450,11 +451,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
@computed get renderTitleBox() {
- const classname = 'pdfBox' + (this.props.isContentActive() ? '-interactive' : '');
+ const classname = 'pdfBox' + (this._props.isContentActive() ? '-interactive' : '');
return (
<div className={classname}>
<div className="pdfBox-title-outer">
- <strong className="pdfBox-title">{StrCast(this.props.Document.title)}</strong>
+ <strong className="pdfBox-title">{StrCast(this.Document.title)}</strong>
</div>
</div>
);
@@ -472,8 +473,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
key="sidebar"
title="Toggle Sidebar"
style={{
- display: !this.props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
+ display: !this._props.isContentActive() ? 'none' : undefined,
+ top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
}}
onPointerDown={e => this.sidebarBtnDown(e, true)}>
@@ -489,27 +490,27 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const pdfNativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
const nativeWidth = NumCast(this.layoutDoc.nativeWidth, pdfNativeWidth);
const pdfRatio = pdfNativeWidth / nativeWidth;
- return (pdfRatio * this.props.PanelWidth()) / pdfNativeWidth;
+ return (pdfRatio * this._props.PanelWidth()) / pdfNativeWidth;
}
@computed get sidebarNativeWidth() {
return this.sidebarWidth() / this.pdfScale;
}
@computed get sidebarNativeHeight() {
- return this.props.PanelHeight() / this.pdfScale;
+ return this._props.PanelHeight() / this.pdfScale;
}
sidebarNativeWidthFunc = () => this.sidebarNativeWidth;
sidebarNativeHeightFunc = () => this.sidebarNativeHeight;
sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey);
sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.SidebarKey);
- sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate((this.sidebarWidth() - this.props.PanelWidth()) / this.pdfScale, 0);
+ sidebarScreenToLocal = () => this._props.ScreenToLocalTransform().translate((this.sidebarWidth() - this._props.PanelWidth()) / this.pdfScale, 0);
@computed get sidebarCollection() {
const renderComponent = (tag: string) => {
const ComponentTag = tag === CollectionViewType.Freeform ? CollectionFreeFormView : CollectionStackingView;
return ComponentTag === CollectionStackingView ? (
<SidebarAnnos
ref={this._sidebarRef}
- {...this.props}
- rootDoc={this.rootDoc}
+ {...this._props}
+ Document={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
setHeight={emptyFunction}
@@ -521,13 +522,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
removeDocument={this.removeDocument}
/>
) : (
- <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}>
+ <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this._props.DocumentView?.()!, false), true)}>
<ComponentTag
- {...this.props}
+ {...this._props}
setContentView={emptyFunction} // override setContentView to do nothing
NativeWidth={this.sidebarNativeWidthFunc}
NativeHeight={this.sidebarNativeHeightFunc}
- PanelHeight={this.props.PanelHeight}
+ PanelHeight={this._props.PanelHeight}
PanelWidth={this.sidebarWidth}
xPadding={0}
yPadding={0}
@@ -541,7 +542,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
moveDocument={this.sidebarMoveDocument}
addDocument={this.sidebarAddDocument}
ScreenToLocalTransform={this.sidebarScreenToLocal}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
noSidebar={true}
fieldKey={this.SidebarKey}
/>
@@ -557,13 +558,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@computed get renderPdfView() {
TraceMobx();
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
- const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
+ const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
return !this._pdf ? null : (
<div
className="pdfBox"
onContextMenu={this.specificContextMenu}
style={{
- height: this.props.Document._layout_scrollTop && !this.Document._layout_fitWidth && window.screen.width > 600 ? (NumCast(this.Document._height) * this.props.PanelWidth()) / NumCast(this.Document._width) : undefined,
+ height: this.Document._layout_scrollTop && !this.Document._layout_fitWidth && window.screen.width > 600 ? (NumCast(this.Document._height) * this._props.PanelWidth()) / NumCast(this.Document._width) : undefined,
}}>
<div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
<div
@@ -576,7 +577,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
top: 0,
}}>
<PDFViewer
- {...this.props}
+ {...this._props}
sidebarAddDoc={this.sidebarAddDocument}
addDocTab={this.sidebarAddDocTab}
layoutDoc={this.layoutDoc}
@@ -594,7 +595,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
crop={this.crop}
/>
</div>
- <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.props.PanelWidth()}%` }}>{this.sidebarCollection}</div>
+ <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this._props.PanelWidth()}%` }}>{this.sidebarCollection}</div>
{this.settingsPanel()}
</div>
);
@@ -604,8 +605,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
static pdfpromise = new Map<string, Promise<Pdfjs.PDFDocumentProxy>>();
render() {
TraceMobx();
- const pdfView = this.renderPdfView;
-
+ const pdfView = !this._pdf ? null : this.renderPdfView;
const href = this.pdfUrl?.url.href;
if (!pdfView && href) {
if (PDFBox.pdfcache.get(href)) setTimeout(action(() => (this._pdf = PDFBox.pdfcache.get(href))));
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
index cd1ff17dd..e75b1ab6f 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
@@ -15,11 +15,11 @@ import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from './../FieldView';
import './PhysicsSimulationBox.scss';
import InputField from './PhysicsSimulationInputField';
-import * as questions from './PhysicsSimulationQuestions.json';
-import * as tutorials from './PhysicsSimulationTutorial.json';
+import questions from './PhysicsSimulationQuestions.json';
+import tutorials from './PhysicsSimulationTutorial.json';
import Wall from './PhysicsSimulationWall';
import Weight from './PhysicsSimulationWeight';
-import React = require('react');
+import * as React from 'react';
interface IWallProps {
length: number;
@@ -202,7 +202,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
];
}
- componentDidUpdate() {
+ componentDidUpdate(prevProps: Readonly<React.PropsWithChildren<FieldViewProps>>) {
+ super.componentDidUpdate(prevProps);
if (this.xMax !== this.props.PanelWidth() * 0.6 || this.yMax != this.props.PanelHeight()) {
this.xMax = this.props.PanelWidth() * 0.6;
this.yMax = this.props.PanelHeight();
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx
index d595a499e..c704863f5 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx
@@ -1,6 +1,6 @@
import { TextField, InputAdornment } from '@mui/material';
import { Doc } from '../../../../fields/Doc';
-import React = require('react');
+import * as React from 'react';
import TaskAltIcon from '@mui/icons-material/TaskAlt';
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
import { isNumber } from 'lodash';
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx
index 8cc1d0fbf..696352296 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx
@@ -1,34 +1,33 @@
-import React = require('react');
+import * as React from 'react';
export interface Force {
- magnitude: number;
- directionInDegrees: number;
+ magnitude: number;
+ directionInDegrees: number;
}
export interface IWallProps {
- length: number;
- xPos: number;
- yPos: number;
- angleInDegrees: number;
+ length: number;
+ xPos: number;
+ yPos: number;
+ angleInDegrees: number;
}
export default class Wall extends React.Component<IWallProps> {
+ constructor(props: any) {
+ super(props);
+ }
- constructor(props: any) {
- super(props)
- }
+ wallStyle = {
+ width: this.props.angleInDegrees == 0 ? this.props.length + '%' : '5px',
+ height: this.props.angleInDegrees == 0 ? '5px' : this.props.length + '%',
+ position: 'absolute' as 'absolute',
+ left: this.props.xPos + '%',
+ top: this.props.yPos + '%',
+ backgroundColor: '#6c7b8b',
+ margin: 0,
+ padding: 0,
+ };
- wallStyle = {
- width: this.props.angleInDegrees == 0 ? this.props.length + "%" : "5px",
- height: this.props.angleInDegrees == 0 ? "5px" : this.props.length + "%",
- position: "absolute" as "absolute",
- left: this.props.xPos + "%",
- top: this.props.yPos + "%",
- backgroundColor: "#6c7b8b",
- margin: 0,
- padding: 0,
- };
-
- render () {
- return (<div style={this.wallStyle}></div>);
- }
-};
+ render() {
+ return <div style={this.wallStyle}></div>;
+ }
+}
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
index 2165c8ba9..f5077a07e 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
@@ -1,7 +1,7 @@
-import { computed, IReactionDisposer, reaction } from 'mobx';
+import { computed, IReactionDisposer, makeObservable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import './PhysicsSimulationBox.scss';
-import React = require('react');
+import * as React from 'react';
interface IWallProps {
length: number;
@@ -93,6 +93,7 @@ interface IState {
export default class Weight extends React.Component<IWeightProps, IState> {
constructor(props: any) {
super(props);
+ makeObservable(this);
this.state = {
angleLabel: 0,
clickPositionX: 0,
diff --git a/src/client/views/nodes/RadialMenu.scss b/src/client/views/nodes/RadialMenu.scss
index 312b51013..9cd1ee23a 100644
--- a/src/client/views/nodes/RadialMenu.scss
+++ b/src/client/views/nodes/RadialMenu.scss
@@ -1,4 +1,4 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables.module.scss';
.radialMenu-cont {
position: absolute;
@@ -26,7 +26,7 @@
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
- transition: all .1s;
+ transition: all 0.1s;
border-style: none;
white-space: nowrap;
font-size: 13px;
@@ -34,8 +34,7 @@
text-transform: uppercase;
}
-s
-.radialMenu-itemSelected {
+s .radialMenu-itemSelected {
border-style: none;
}
@@ -50,8 +49,8 @@ s
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
- transition: all .1s;
- border-width: .11px;
+ transition: all 0.1s;
+ border-width: 0.11px;
border-style: none;
border-color: $medium-gray; // rgb(187, 186, 186);
// padding: 10px 0px 10px 0px;
@@ -62,9 +61,8 @@ s
padding-left: 5px;
}
-
.radialMenu-description {
margin-left: 5px;
text-align: left;
display: inline; //need this?
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx
index 7f0956e51..061a46f03 100644
--- a/src/client/views/nodes/RadialMenu.tsx
+++ b/src/client/views/nodes/RadialMenu.tsx
@@ -1,17 +1,17 @@
-import React = require("react");
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import "./RadialMenu.scss";
-import { RadialMenuItem, RadialMenuProps } from "./RadialMenuItem";
+import * as React from 'react';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import './RadialMenu.scss';
+import { RadialMenuItem, RadialMenuProps } from './RadialMenuItem';
@observer
export class RadialMenu extends React.Component {
static Instance: RadialMenu;
static readonly buffer = 20;
- constructor(props: Readonly<{}>) {
+ constructor(props: any) {
super(props);
-
+ makeObservable(this);
RadialMenu.Instance = this;
}
@@ -23,11 +23,10 @@ export class RadialMenu extends React.Component {
public used: boolean = false;
-
catchTouch = (te: React.TouchEvent) => {
te.stopPropagation();
te.preventDefault();
- }
+ };
@action
onPointerDown = (e: PointerEvent) => {
@@ -35,8 +34,8 @@ export class RadialMenu extends React.Component {
this._mouseX = e.clientX;
this._mouseY = e.clientY;
this.used = false;
- document.addEventListener("pointermove", this.onPointerMove);
- }
+ document.addEventListener('pointermove', this.onPointerMove);
+ };
@observable
private _closest: number = -1;
@@ -60,11 +59,10 @@ export class RadialMenu extends React.Component {
}
}
this._closest = closest;
- }
- else {
+ } else {
this._closest = -1;
}
- }
+ };
@action
onPointerUp = (e: PointerEvent) => {
this.used = true;
@@ -75,43 +73,41 @@ export class RadialMenu extends React.Component {
this._shouldDisplay = false;
}
this._shouldDisplay && (this._display = true);
- document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener('pointermove', this.onPointerMove);
if (this._closest !== -1 && this._items?.length > this._closest) {
this._items[this._closest].event();
}
- }
+ };
componentWillUnmount() {
- document.removeEventListener("pointerdown", this.onPointerDown);
+ document.removeEventListener('pointerdown', this.onPointerDown);
- document.removeEventListener("pointerup", this.onPointerUp);
+ document.removeEventListener('pointerup', this.onPointerUp);
this._reactionDisposer && this._reactionDisposer();
}
@action
componentDidMount = () => {
- document.addEventListener("pointerdown", this.onPointerDown);
- document.addEventListener("pointerup", this.onPointerUp);
+ document.addEventListener('pointerdown', this.onPointerDown);
+ document.addEventListener('pointerup', this.onPointerUp);
this.previewcircle();
this._reactionDisposer = reaction(
() => this._shouldDisplay,
- () => this._shouldDisplay && !this._mouseDown && runInAction(() => this._display = true)
+ () => this._shouldDisplay && !this._mouseDown && runInAction(() => (this._display = true))
);
- }
+ };
componentDidUpdate = () => {
this.previewcircle();
- }
+ };
@observable private _pageX: number = 0;
@observable private _pageY: number = 0;
@observable _display: boolean = false;
@observable private _yRelativeToTop: boolean = true;
-
@observable private _width: number = 0;
@observable private _height: number = 0;
-
getItems() {
return this._items;
}
@@ -133,7 +129,7 @@ export class RadialMenu extends React.Component {
this._mouseX = x;
this._mouseY = y;
this._shouldDisplay = true;
- }
+ };
// @computed
// get pageX() {
// const x = this._pageX;
@@ -168,7 +164,7 @@ export class RadialMenu extends React.Component {
this.clearItems();
this._display = false;
this._shouldDisplay = false;
- }
+ };
@action
openMenu = (x: number, y: number) => {
@@ -176,56 +172,52 @@ export class RadialMenu extends React.Component {
this._pageY = y;
this._shouldDisplay;
this._display = true;
- }
+ };
@action
clearItems() {
this._items = [];
}
-
previewcircle() {
- if (document.getElementById("newCanvas") !== null) {
- const c: any = document.getElementById("newCanvas");
+ if (document.getElementById('newCanvas') !== null) {
+ const c: any = document.getElementById('newCanvas');
if (c.getContext) {
- const ctx = c.getContext("2d");
+ const ctx = c.getContext('2d');
ctx.beginPath();
ctx.arc(150, 150, 50, 0, 2 * Math.PI);
- ctx.fillStyle = "white";
+ ctx.fillStyle = 'white';
ctx.fill();
- ctx.font = "12px Arial";
- ctx.fillStyle = "black";
- ctx.textAlign = "center";
- let description = "";
+ ctx.font = '12px Arial';
+ ctx.fillStyle = 'black';
+ ctx.textAlign = 'center';
+ let description = '';
if (this._closest !== -1) {
description = this._items[this._closest].description;
}
if (description.length > 15) {
description = description.slice(0, 12);
- description += "...";
+ description += '...';
}
ctx.fillText(description, 150, 150, 90);
}
}
}
-
render() {
if (!this._display) {
return null;
}
- const style = this._yRelativeToTop ? { left: this._pageX - 130, top: this._pageY - 130 } :
- { left: this._pageX - 130, top: this._pageY - 130 };
+ const style = this._yRelativeToTop ? { left: this._pageX - 130, top: this._pageY - 130 } : { left: this._pageX - 130, top: this._pageY - 130 };
return (
-
<div className="radialMenu-cont" onTouchStart={this.catchTouch} style={style}>
- <canvas id="newCanvas" style={{ position: "absolute" }} height="300" width="300"> Your browser does not support the HTML5 canvas tag.</canvas>
+ <canvas id="newCanvas" style={{ position: 'absolute' }} height="300" width="300">
+ {' '}
+ Your browser does not support the HTML5 canvas tag.
+ </canvas>
{this.menuItems}
</div>
-
);
}
-
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx
index 8876b4879..c931202f1 100644
--- a/src/client/views/nodes/RadialMenuItem.tsx
+++ b/src/client/views/nodes/RadialMenuItem.tsx
@@ -1,8 +1,8 @@
-import React = require("react");
+import * as React from 'react';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { observer } from "mobx-react";
-import { UndoManager } from "../../util/UndoManager";
+import { observer } from 'mobx-react';
+import { UndoManager } from '../../util/UndoManager';
export interface RadialMenuProps {
description: string;
@@ -15,17 +15,15 @@ export interface RadialMenuProps {
selected: number;
}
-
@observer
export class RadialMenuItem extends React.Component<RadialMenuProps> {
-
componentDidMount = () => {
this.setcircle();
- }
+ };
componentDidUpdate = () => {
this.setcircle();
- }
+ };
handleEvent = async (e: React.PointerEvent) => {
this.props.closeMenu && this.props.closeMenu();
@@ -35,38 +33,36 @@ export class RadialMenuItem extends React.Component<RadialMenuProps> {
}
await this.props.event({ x: e.clientX, y: e.clientY });
batch && batch.end();
- }
-
+ };
setcircle() {
let circlemin = 0;
let circlemax = 1;
- this.props.min ? circlemin = this.props.min : null;
- this.props.max ? circlemax = this.props.max : null;
- if (document.getElementById("myCanvas") !== null) {
- const c: any = document.getElementById("myCanvas");
- let color = "white";
+ this.props.min ? (circlemin = this.props.min) : null;
+ this.props.max ? (circlemax = this.props.max) : null;
+ if (document.getElementById('myCanvas') !== null) {
+ const c: any = document.getElementById('myCanvas');
+ let color = 'white';
switch (circlemin % 3) {
case 1:
- color = "#c2c2c5";
+ color = '#c2c2c5';
break;
case 0:
- color = "#f1efeb";
+ color = '#f1efeb';
break;
case 2:
- color = "lightgray";
+ color = 'lightgray';
break;
}
if (circlemax % 3 === 1 && circlemin === circlemax - 1) {
- color = "#c2c2c5";
+ color = '#c2c2c5';
}
if (this.props.selected === this.props.min) {
- color = "#808080";
-
+ color = '#808080';
}
if (c.getContext) {
- const ctx = c.getContext("2d");
+ const ctx = c.getContext('2d');
ctx.beginPath();
ctx.arc(150, 150, 150, (circlemin / circlemax) * 2 * Math.PI, ((circlemin + 1) / circlemax) * 2 * Math.PI);
ctx.arc(150, 150, 50, ((circlemin + 1) / circlemax) * 2 * Math.PI, (circlemin / circlemax) * 2 * Math.PI, true);
@@ -79,35 +75,36 @@ export class RadialMenuItem extends React.Component<RadialMenuProps> {
calculatorx() {
let circlemin = 0;
let circlemax = 1;
- this.props.min ? circlemin = this.props.min : null;
- this.props.max ? circlemax = this.props.max : null;
- const avg = ((circlemin / circlemax) + ((circlemin + 1) / circlemax)) / 2;
+ this.props.min ? (circlemin = this.props.min) : null;
+ this.props.max ? (circlemax = this.props.max) : null;
+ const avg = (circlemin / circlemax + (circlemin + 1) / circlemax) / 2;
const degrees = 360 * avg;
- const x = 100 * Math.cos(degrees * Math.PI / 180);
- const y = -125 * Math.sin(degrees * Math.PI / 180);
+ const x = 100 * Math.cos((degrees * Math.PI) / 180);
+ const y = -125 * Math.sin((degrees * Math.PI) / 180);
return x;
}
calculatory() {
-
let circlemin = 0;
let circlemax = 1;
- this.props.min ? circlemin = this.props.min : null;
- this.props.max ? circlemax = this.props.max : null;
- const avg = ((circlemin / circlemax) + ((circlemin + 1) / circlemax)) / 2;
+ this.props.min ? (circlemin = this.props.min) : null;
+ this.props.max ? (circlemax = this.props.max) : null;
+ const avg = (circlemin / circlemax + (circlemin + 1) / circlemax) / 2;
const degrees = 360 * avg;
- const x = 125 * Math.cos(degrees * Math.PI / 180);
- const y = -100 * Math.sin(degrees * Math.PI / 180);
+ const x = 125 * Math.cos((degrees * Math.PI) / 180);
+ const y = -100 * Math.sin((degrees * Math.PI) / 180);
return y;
}
-
render() {
return (
- <div className={"radialMenu-item" + (this.props.selected ? " radialMenu-itemSelected" : "")} onPointerUp={this.handleEvent}>
- <canvas id="myCanvas" height="300" width="300"> Your browser does not support the HTML5 canvas tag.</canvas>
- <FontAwesomeIcon icon={this.props.icon} size="3x" style={{ position: "absolute", left: this.calculatorx() + 150 - 19, top: this.calculatory() + 150 - 19 }} />
+ <div className={'radialMenu-item' + (this.props.selected ? ' radialMenu-itemSelected' : '')} onPointerUp={this.handleEvent}>
+ <canvas id="myCanvas" height="300" width="300">
+ {' '}
+ Your browser does not support the HTML5 canvas tag.
+ </canvas>
+ <FontAwesomeIcon icon={this.props.icon} size="3x" style={{ position: 'absolute', left: this.calculatorx() + 150 - 19, top: this.calculatory() + 150 - 19 }} />
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 224bc6f67..d5d31b407 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,7 +1,7 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
// import { Canvas } from '@react-three/fiber';
-import { computed, observable, runInAction } from 'mobx';
+import { computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
// import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
import { DateField } from '../../../fields/DateField';
@@ -37,13 +37,13 @@ declare class MediaRecorder {
// setRaised: (r: { coord: Vector2, off: Vector3 }[]) => void;
// x: number;
// y: number;
-// rootDoc: Doc;
+// doc: Doc;
// color: string;
// }
// @observer
// export class VideoTile extends React.Component<VideoTileProps> {
-// @observable _videoRef: HTMLVideoElement | undefined;
+// @observable _videoRef: HTMLVideoElement | undefined = undefined;
// _mesh: any = undefined;
// render() {
@@ -69,8 +69,8 @@ declare class MediaRecorder {
// const normals = new Float32Array(quad_normals);
// const uvs = new Float32Array(quad_uvs); // Each vertex has one uv coordinate for texture mapping
// const indices = new Uint32Array(quad_indices); // Use the four vertices to draw the two triangles that make up the square.
-// const popOut = () => NumCast(this.props.rootDoc.popOut);
-// const popOff = () => NumCast(this.props.rootDoc.popOff);
+// const popOut = () => NumCast(this.props.Document.popOut);
+// const popOff = () => NumCast(this.props.Document.popOff);
// return (
// <mesh key={`mesh${topLeft[0]}${topLeft[1]}`} onClick={action(async e => {
// this.props.setRaised([
@@ -123,18 +123,12 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
constructor(props: any) {
super(props);
- if (this.rootDoc.videoWall) {
- this.rootDoc.nativeWidth = undefined;
- this.rootDoc.nativeHeight = undefined;
- this.layoutDoc.popOff = 0;
- this.layoutDoc.popOut = 1;
- } else {
- this.setupDictation();
- }
+ makeObservable(this);
+ this.setupDictation();
}
getAnchor = (addAsAnnotation: boolean) => {
const startTime = Cast(this.layoutDoc._layout_currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
- return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc;
+ return CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.Document;
};
videoLoad = () => {
@@ -151,7 +145,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
componentDidMount() {
this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
- // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
+ // this.layoutDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
// ({ width, height }) => {
// if (this._camera) {
// const angle = -Math.abs(1 - width / height);
@@ -173,7 +167,6 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
};
@computed get content() {
- if (this.rootDoc.videoWall) return null;
return (
<video
className={'videoBox-content'}
@@ -181,7 +174,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
ref={r => {
this._videoRef = r;
setTimeout(() => {
- if (this.rootDoc.mediaState === media_state.PendingRecording && this._videoRef) {
+ if (this.layoutDoc.mediaState === media_state.PendingRecording && this._videoRef) {
this.toggleRecording();
}
}, 100);
@@ -202,12 +195,12 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
// @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
// @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
@computed get threed() {
- // if (this.rootDoc.videoWall) {
+ // if (this.layoutDoc.videoWall) {
// const screens: any[] = [];
// const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
// let count = 0;
// numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
- // <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
+ // <VideoTile doc={this.layoutDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
// return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
// this._camera = props.camera;
// props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
@@ -250,7 +243,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
this.dataDoc[this.fieldKey + '_presentation'] = JSON.stringify(presCopy);
}
TrackMovements.Instance.finish();
- const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
+ const file = new File(vid_chunks, `${this.Document[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
const [{ result }] = await Networking.UploadFilesToServer({ file });
this.dataDoc[this.fieldKey + '_duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
if (!(result instanceof Error)) {
@@ -279,13 +272,13 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
const ind = DocUtils.ActiveRecordings.indexOf(this);
ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1);
- CaptureManager.Instance.open(this.rootDoc);
+ CaptureManager.Instance.open(this.Document);
}
};
setupDictation = () => {
if (this.dataDoc[this.fieldKey + '_dictation']) return;
- const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
+ const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.Document.x), NumCast(this.Document.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
const textField = Doc.LayoutFieldKey(dictationText);
dictationText._layout_autoHeight = false;
const dictationTextProto = Doc.GetProto(dictationText);
diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss
index 8d76f2b1d..9789da55a 100644
--- a/src/client/views/nodes/ScriptingBox.scss
+++ b/src/client/views/nodes/ScriptingBox.scss
@@ -32,7 +32,7 @@
.scriptingBox-wrapper {
width: 100%;
height: 100%;
- max-height: calc(100%-30px);
+ max-height: calc(100% - 30px);
display: flex;
flex-direction: row;
overflow: auto;
@@ -42,7 +42,8 @@
overflow: hidden;
}
- .scriptingBox-textArea, .scriptingBox-textArea-inputs {
+ .scriptingBox-textArea,
+ .scriptingBox-textArea-inputs {
flex: 70;
height: 100%;
max-width: 95%;
@@ -110,7 +111,7 @@
cursor: pointer;
}
- .rta__entity>* {
+ .rta__entity > * {
padding-left: 4px;
padding-right: 4px;
}
@@ -125,7 +126,7 @@
.scriptingBox-textArea-inputs {
max-width: 100%;
height: 40%;
- width: 100%;
+ width: 100%;
resize: none;
}
.scriptingBox-textArea-script {
@@ -194,8 +195,8 @@
.scriptingBox-errorMessage {
overflow: auto;
- background: "red";
- background-color: "red";
+ background: 'red';
+ background-color: 'red';
height: 45px;
}
@@ -221,4 +222,4 @@
width: 25%;
}
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index a51a83b26..7e7eaee45 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,5 +1,5 @@
let ReactTextareaAutocomplete = require('@webscopeio/react-textarea-autocomplete').default;
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc } from '../../../fields/Doc';
@@ -60,8 +60,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
constructor(props: any) {
super(props);
+ makeObservable(this);
if (!this.compileParams.length) {
- const params = ScriptCast(this.rootDoc[this.props.fieldKey])?.script.options.params as { [key: string]: any };
+ const params = ScriptCast(this.dataDoc[this.props.fieldKey])?.script.options.params as { [key: string]: any };
if (params) {
this.compileParams = Array.from(Object.keys(params))
.filter(p => !p.startsWith('_'))
@@ -78,30 +79,30 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
return this.compileParams.map(p => p.split(':')[1].trim());
}
@computed({ keepAlive: true }) get rawScript() {
- return ScriptCast(this.rootDoc[this.fieldKey])?.script.originalScript ?? '';
+ return ScriptCast(this.dataDoc[this.fieldKey])?.script.originalScript ?? '';
}
@computed({ keepAlive: true }) get functionName() {
- return StrCast(this.rootDoc[this.props.fieldKey + '-functionName'], '');
+ return StrCast(this.dataDoc[this.fieldKey + '-functionName'], '');
}
@computed({ keepAlive: true }) get functionDescription() {
- return StrCast(this.rootDoc[this.props.fieldKey + '-functionDescription'], '');
+ return StrCast(this.dataDoc[this.fieldKey + '-functionDescription'], '');
}
@computed({ keepAlive: true }) get compileParams() {
- return Cast(this.rootDoc[this.props.fieldKey + '-params'], listSpec('string'), []);
+ return Cast(this.dataDoc[this.fieldKey + '-params'], listSpec('string'), []);
}
set rawScript(value) {
- Doc.SetInPlace(this.rootDoc, this.props.fieldKey, new ScriptField(undefined, undefined, value), true);
+ this.dataDoc[this.fieldKey] = new ScriptField(undefined, undefined, value);
}
set functionName(value) {
- Doc.SetInPlace(this.rootDoc, this.props.fieldKey + '-functionName', value, true);
+ this.dataDoc[this.fieldKey + '-functionName'] = value;
}
set functionDescription(value) {
- Doc.SetInPlace(this.rootDoc, this.props.fieldKey + '-functionDescription', value, true);
+ this.dataDoc[this.fieldKey + '-functionDescription'] = value;
}
set compileParams(value) {
- Doc.SetInPlace(this.rootDoc, this.props.fieldKey + '-params', new List<string>(value), true);
+ this.dataDoc[this.fieldKey + '-params'] = new List<string>(value);
}
getValue(result: any, descrip: boolean) {
@@ -165,9 +166,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
// only included in buttons, transforms scripting UI to a button
@action
- onFinish = () => {
- this.rootDoc.layout_fieldKey = 'layout';
- };
+ onFinish = () => (this.layoutDoc.layout_fieldKey = 'layout');
// displays error message
@action
@@ -189,7 +188,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
params,
typecheck: false,
});
- Doc.SetInPlace(this.rootDoc, this.fieldKey, result.compiled ? new ScriptField(result, undefined, this.rawText) : undefined, true);
+ this.dataDoc[this.fieldKey] = result.compiled ? new ScriptField(result, undefined, this.rawText) : undefined;
this.onError(result.compiled ? undefined : result.errors);
return result.compiled;
};
@@ -199,9 +198,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
onRun = () => {
if (this.onCompile()) {
const bindings: { [name: string]: any } = {};
- this.paramsNames.forEach(key => (bindings[key] = this.rootDoc[key]));
+ this.paramsNames.forEach(key => (bindings[key] = this.dataDoc[key]));
// binds vars so user doesnt have to refer to everything as this.<var>
- ScriptCast(this.rootDoc[this.fieldKey], null)?.script.run({ ...bindings, self: this.rootDoc, this: this.layoutDoc }, this.onError);
+ ScriptCast(this.dataDoc[this.fieldKey], null)?.script.run({ ...bindings, this: this.Document }, this.onError);
}
};
@@ -270,7 +269,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
@action
onDrop = (e: Event, de: DragManager.DropEvent, fieldKey: string) => {
if (de.complete.docDragData) {
- de.complete.docDragData.droppedDocuments.forEach(doc => Doc.SetInPlace(this.rootDoc, fieldKey, doc, true));
+ de.complete.docDragData.droppedDocuments.forEach(doc => (this.dataDoc[fieldKey] = doc));
e.stopPropagation();
return true;
}
@@ -280,7 +279,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
// deletes a param from all areas in which it is stored
@action
onDelete = (num: number) => {
- Doc.SetInPlace(this.rootDoc, this.paramsNames[num], undefined, true);
+ this.dataDoc[this.paramsNames[num]] = undefined;
this.compileParams.splice(num, 1);
return true;
};
@@ -290,13 +289,13 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
viewChanged = (e: React.ChangeEvent, name: string) => {
//@ts-ignore
const val = e.target.selectedOptions[0].value;
- Doc.SetInPlace(this.rootDoc, name, val[0] === 'S' ? val.substring(1) : val[0] === 'N' ? parseInt(val.substring(1)) : val.substring(1) === 'true', true);
+ this.dataDoc[name] = val[0] === 'S' ? val.substring(1) : val[0] === 'N' ? parseInt(val.substring(1)) : val.substring(1) === 'true';
};
// creates a copy of the script document
onCopy = () => {
- const copy = Doc.MakeCopy(this.rootDoc, true);
- copy.x = NumCast(this.dataDoc.x) + NumCast(this.dataDoc._width);
+ const copy = Doc.MakeCopy(this.Document, true);
+ copy.x = NumCast(this.Document.x) + NumCast(this.dataDoc._width);
this.props.addDocument?.(copy);
};
@@ -346,8 +345,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
maxHeight={72}
height={35}
fontSize={14}
- contents={StrCast(DocCast(this.rootDoc[parameter])?.title, 'undefined')}
- GetValue={() => StrCast(DocCast(this.rootDoc[parameter])?.title, 'undefined')}
+ contents={StrCast(DocCast(this.dataDoc[parameter])?.title, 'undefined')}
+ GetValue={() => StrCast(DocCast(this.dataDoc[parameter])?.title, 'undefined')}
SetValue={action((value: string) => {
const script = CompileScript(value, {
addReturn: true,
@@ -357,7 +356,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
const results = script.compiled && script.run();
if (results && results.success) {
this._errorMessage = '';
- Doc.SetInPlace(this.rootDoc, parameter, results.result, true);
+ this.dataDoc[parameter] = results.result;
return true;
}
this._errorMessage = 'invalid document';
@@ -370,7 +369,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
// rendering when a string's value can be set in applied UI
renderBasicType(parameter: string, isNum: boolean) {
- const strVal = isNum ? NumCast(this.rootDoc[parameter]).toString() : StrCast(this.rootDoc[parameter]);
+ const strVal = isNum ? NumCast(this.dataDoc[parameter]).toString() : StrCast(this.dataDoc[parameter]);
return (
<div className="scriptingBox-paramInputs" style={{ overflowY: 'hidden' }}>
<EditableView
@@ -384,7 +383,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
const setValue = isNum ? parseInt(value) : value;
if (setValue !== undefined && setValue !== ' ') {
this._errorMessage = '';
- Doc.SetInPlace(this.rootDoc, parameter, setValue, true);
+ this.dataDoc[parameter] = setValue;
return true;
}
this._errorMessage = 'invalid input';
@@ -405,7 +404,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
className="scriptingBox-viewPicker"
onPointerDown={e => e.stopPropagation()}
onChange={e => this.viewChanged(e, parameter)}
- value={typeof this.rootDoc[parameter] === 'string' ? 'S' + StrCast(this.rootDoc[parameter]) : typeof this.rootDoc[parameter] === 'number' ? 'N' + NumCast(this.rootDoc[parameter]) : 'B' + BoolCast(this.rootDoc[parameter])}>
+ value={typeof this.dataDoc[parameter] === 'string' ? 'S' + StrCast(this.dataDoc[parameter]) : typeof this.dataDoc[parameter] === 'number' ? 'N' + NumCast(this.dataDoc[parameter]) : 'B' + BoolCast(this.dataDoc[parameter])}>
{types.map((type, i) => (
<option key={i} className="scriptingBox-viewOption" value={(typeof type === 'string' ? 'S' : typeof type === 'number' ? 'N' : 'B') + type}>
{' '}
@@ -703,7 +702,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
// toolbar (with compile and apply buttons) for scripting UI
renderScriptingTools() {
- const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? '-finish' : '');
+ const buttonStyle = 'scriptingBox-button' + (StrCast(this.Document.layout_fieldKey).startsWith('layout_on') ? '-finish' : '');
return (
<div className="scriptingBox-toolbar">
<button
@@ -731,7 +730,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
Save
</button>
- {!StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? null : ( // onClick, onChecked, etc need a Finish button to return to their default layout
+ {!StrCast(this.Document.layout_fieldKey).startsWith('layout_on') ? null : ( // onClick, onChecked, etc need a Finish button to return to their default layout
<button
className={buttonStyle}
onPointerDown={e => {
@@ -777,7 +776,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
// toolbar (with edit and run buttons and error message) for params UI
renderTools(toolSet: string, func: () => void) {
- const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? '-finish' : '');
+ const buttonStyle = 'scriptingBox-button' + (StrCast(this.Document.layout_fieldKey).startsWith('layout_on') ? '-finish' : '');
return (
<div className="scriptingBox-toolbar">
<button
@@ -796,7 +795,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
}}>
{toolSet}
</button>
- {!StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? null : (
+ {!StrCast(this.Document.layout_fieldKey).startsWith('layout_on') ? null : (
<button
className={buttonStyle}
onPointerDown={e => {
diff --git a/src/client/views/nodes/TaskCompletedBox.tsx b/src/client/views/nodes/TaskCompletedBox.tsx
index 2a3dd8d2d..9aab8c5a9 100644
--- a/src/client/views/nodes/TaskCompletedBox.tsx
+++ b/src/client/views/nodes/TaskCompletedBox.tsx
@@ -1,13 +1,11 @@
-import React = require("react");
-import { observer } from "mobx-react";
-import "./TaskCompletedBox.scss";
-import { observable, action } from "mobx";
-import { Fade } from "@material-ui/core";
-
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import './TaskCompletedBox.scss';
+import { observable, action } from 'mobx';
+import { Fade } from '@mui/material';
@observer
export class TaskCompletionBox extends React.Component<{}> {
-
@observable public static taskCompleted: boolean = false;
@observable public static popupX: number = 500;
@observable public static popupY: number = 150;
@@ -16,15 +14,20 @@ export class TaskCompletionBox extends React.Component<{}> {
@action
public static toggleTaskCompleted = () => {
TaskCompletionBox.taskCompleted = !TaskCompletionBox.taskCompleted;
- }
+ };
render() {
- return <Fade in={TaskCompletionBox.taskCompleted}>
- <div className="taskCompletedBox-fade"
- style={{
- left: TaskCompletionBox.popupX ? TaskCompletionBox.popupX : 500,
- top: TaskCompletionBox.popupY ? TaskCompletionBox.popupY : 150,
- }}>{TaskCompletionBox.textDisplayed}</div>
- </Fade>;
+ return (
+ <Fade in={TaskCompletionBox.taskCompleted}>
+ <div
+ className="taskCompletedBox-fade"
+ style={{
+ left: TaskCompletionBox.popupX ? TaskCompletionBox.popupX : 500,
+ top: TaskCompletionBox.popupY ? TaskCompletionBox.popupY : 150,
+ }}>
+ {TaskCompletionBox.textDisplayed}
+ </div>
+ </Fade>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index f90c19050..ae923ad60 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.scss';
+@import '../global/globalCssVariables.module.scss';
.mini-viewer {
cursor: grab;
@@ -172,7 +172,11 @@
top: 90%;
transform: translate(-50%, -50%);
width: 80%;
- transition: top 0s, width 0s, opacity 0.3s, visibility 0.3s;
+ transition:
+ top 0s,
+ width 0s,
+ opacity 0.3s,
+ visibility 0.3s;
}
}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index f87d94784..df73dffe4 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,8 +1,8 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx';
import { observer } from 'mobx-react';
import { basename } from 'path';
+import * as React from 'react';
import { Doc, StrListCast } from '../../../fields/Doc';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -33,7 +33,6 @@ import { FieldView, FieldViewProps } from './FieldView';
import { RecordingBox } from './RecordingBox';
import { PinProps, PresBox } from './trails';
import './VideoBox.scss';
-const path = require('path');
/**
* VideoBox
@@ -52,7 +51,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(VideoBox, fieldKey);
}
-
static _youtubeIframeCounter: number = 0;
static heightPercent = 80; // height of video relative to videoBox when timeline is open
static numThumbnails = 20;
@@ -70,6 +68,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
private _playRegionTimer: any = null; // timeout for playback
private _controlsFadeTimer: any = null; // timeout for controls fade
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ this._props.setContentView?.(this);
+ }
+
@observable _stackedTimeline: any; // CollectionStackedTimeline ref
@observable static _nativeControls: boolean; // default html controls
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@@ -96,13 +100,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@observable rawDuration: number = 0;
@computed get youtubeVideoId() {
- const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+ const field = Cast(this.dataDoc[this._props.fieldKey], VideoField);
return field && field.url.href.indexOf('youtube') !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split('/')) : '';
}
// returns the path of the audio file
@computed get audiopath() {
- const field = Cast(this.props.Document[this.props.fieldKey + '_audio'], AudioField, null);
+ const field = Cast(this.Document[this._props.fieldKey + '_audio'], AudioField, null);
const vfield = Cast(this.dataDoc[this.fieldKey], VideoField, null);
return field?.url.href ?? vfield?.url.href ?? '';
}
@@ -125,7 +129,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
componentDidMount() {
this.unmounting = false;
- this.props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
+ this._props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
if (this.youtubeVideoId) {
const youtubeaspect = 400 / 315;
const nativeWidth = Doc.NativeWidth(this.layoutDoc);
@@ -162,7 +166,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()
+ this._props.isSelected()
) {
switch (e.key) {
case 'ArrowLeft':
@@ -267,7 +271,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this.player && this._contentRef && this._contentRef.requestFullscreen();
}
try {
- this._youtubePlayer && this.props.addDocTab(this.rootDoc, OpenWhere.add);
+ this._youtubePlayer && this._props.addDocTab(this.Document, OpenWhere.add);
} catch (e) {
console.log('Video FullScreen Exception:', e);
}
@@ -328,15 +332,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
title: (this.layoutDoc._layout_currentTimecode || 0).toString(),
onClick: FollowLinkScript(),
});
- this.props.addDocument?.(b);
- DocUtils.MakeLink(b, this.rootDoc, { link_relationship: 'video snapshot' });
+ this._props.addDocument?.(b);
+ DocUtils.MakeLink(b, this.Document, { link_relationship: 'video snapshot' });
Networking.PostToServer('/youtubeScreenshot', {
id: this.youtubeVideoId,
timecode: this.layoutDoc._layout_currentTimecode,
}).then(response => {
const resolved = response?.accessPaths?.agnostic?.client;
if (resolved) {
- this.props.removeDocument?.(b);
+ this._props.removeDocument?.(b);
this.createSnapshotLink(resolved);
}
});
@@ -344,7 +348,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
//convert to desired file format
const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
// if you want to preview the captured image,
- const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
+ const retitled = StrCast(this.Document.title).replace(/[ -\.:]/g, '');
const encodedFilename = encodeURIComponent('snapshot' + retitled + '_' + (this.layoutDoc._layout_currentTimecode || 0).toString().replace(/\./, '_'));
const filename = basename(encodedFilename);
Utils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createSnapshotLink)(returnedFilename, downX, downY));
@@ -377,7 +381,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
});
Doc.SetNativeWidth(Doc.GetProto(imageSnapshot), Doc.NativeWidth(this.layoutDoc));
Doc.SetNativeHeight(Doc.GetProto(imageSnapshot), Doc.NativeHeight(this.layoutDoc));
- this.props.addDocument?.(imageSnapshot);
+ this._props.addDocument?.(imageSnapshot);
const link = DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { link_relationship: 'video snapshot' });
link && (Doc.GetProto(link.link_anchor_2 as Doc).timecodeToHide = NumCast((link.link_anchor_2 as Doc).timecodeToShow) + 3);
setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, 'move', true));
@@ -388,9 +392,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation);
if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent';
const anchor = addAsAnnotation
- ? CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc
- : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.rootDoc });
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc);
+ ? CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.Document
+ : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.Document });
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.Document);
return anchor;
};
@@ -437,7 +441,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
canvas.height = 100;
canvas.width = 100;
canvas.getContext('2d')?.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, 100, 100);
- const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
+ const retitled = StrCast(this.Document.title).replace(/[ -\.:]/g, '');
const encodedFilename = encodeURIComponent('thumbnail' + retitled + '_' + video.currentTime.toString().replace(/\./, '_'));
thumbnailPromises?.push(Utils.convertDataUri(canvas.toDataURL(), basename(encodedFilename), true));
const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1);
@@ -492,13 +496,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// context menu
specificContextMenu = (e: React.MouseEvent): void => {
- const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+ const field = Cast(this.dataDoc[this._props.fieldKey], VideoField);
if (field) {
const url = field.url.href;
const subitems: ContextMenuProps[] = [];
subitems.push({ description: 'Full Screen', event: this.FullScreen, icon: 'expand' });
subitems.push({ description: 'Take Snapshot', event: this.Snapshot, icon: 'expand-arrows-alt' });
- this.rootDoc.type === DocumentType.SCREENSHOT &&
+ this.Document.type === DocumentType.SCREENSHOT &&
subitems.push({
description: 'Screen Capture',
event: async () => {
@@ -532,7 +536,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
event: () => {
this.dataDoc.layout = RecordingBox.LayoutString(this.fieldKey);
// delete assoicated video data
- this.dataDoc[this.props.fieldKey] = '';
+ this.dataDoc[this._props.fieldKey] = '';
this.dataDoc[this.fieldKey + '_duration'] = '';
// delete assoicated presentation data
this.dataDoc[this.fieldKey + '_presentation'] = '';
@@ -550,7 +554,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// renders the video and audio
@computed get content() {
const field = Cast(this.dataDoc[this.fieldKey], VideoField);
- const interactive = Doc.ActiveTool !== InkTool.None || !this.props.isSelected() ? '' : '-interactive';
+ const interactive = Doc.ActiveTool !== InkTool.None || !this._props.isSelected() ? '' : '-interactive';
const classname = 'videoBox-content' + (this._fullScreen ? '-fullScreen' : '') + interactive;
const opacity = this._scrubbing ? 0.3 : this._controlsVisible ? 1 : 0;
return !field ? (
@@ -575,7 +579,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
key="video"
autoPlay={this._screenCapture}
ref={this.setVideoRef}
- style={this._fullScreen ? this.fullScreenSize() : this.isCropped ? { width: 'max-content', height: 'max-content', transform: `scale(${1 / NumCast(this.rootDoc._freeform_scale)})`, transformOrigin: 'top left' } : {}}
+ style={this._fullScreen ? this.fullScreenSize() : this.isCropped ? { width: 'max-content', height: 'max-content', transform: `scale(${1 / NumCast(this.layoutDoc._freeform_scale)})`, transformOrigin: 'top left' } : {}}
onCanPlay={this.videoLoad}
controls={VideoBox._nativeControls}
onPlay={() => this.Play()}
@@ -586,7 +590,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
Not supported.
</video>
{!this.audiopath || this.audiopath === field.url.href ? null : (
- <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? '-interactive' : ''}`}>
+ <audio ref={this.setAudioRef} className={`audiobox-control${this._props.isContentActive() ? '-interactive' : ''}`}>
<source src={this.audiopath} type="audio/mpeg" />
Not supported.
</audio>
@@ -626,7 +630,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() && !SnappingManager.GetIsDragging() && !DocumentView.Interacting,
+ () => Doc.ActiveTool === InkTool.None && this._props.isSelected() && !SnappingManager.IsDragging && !DocumentView.Interacting,
interactive => (iframe.style.pointerEvents = interactive ? 'all' : 'none'),
{ fireImmediately: true }
);
@@ -636,8 +640,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
(YT as any)?.ready(() => {
this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
events: {
- onReady: this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
- onStateChange: this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
+ onReady: this._props.dontRegisterView ? undefined : onYoutubePlayerReady,
+ onStateChange: this._props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
},
});
});
@@ -677,9 +681,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
e,
action(encodeURIComponent => {
this._clicking = false;
- if (this.props.isContentActive()) {
- // const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY);
- // this.layoutDoc._layout_timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100));
+ if (this._props.isContentActive()) {
+ // const local = this._props.ScreenToLocalTransform().scale(this._props.scaling?.() || 1).transformPoint(e.clientX, e.clientY);
+ // this.layoutDoc._layout_timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this._props.PanelHeight() * 100));
this.layoutDoc._layout_timelineHeightPercent = 80;
}
@@ -693,15 +697,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
500
);
},
- this.props.isContentActive(),
- this.props.isContentActive()
+ this._props.isContentActive(),
+ this._props.isContentActive()
);
};
// removes from currently playing display
@action
removeCurrentlyPlaying = () => {
- const docView = this.props.DocumentView?.();
+ const docView = this._props.DocumentView?.();
if (CollectionStackedTimeline.CurrentlyPlaying && docView) {
const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView);
index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
@@ -710,7 +714,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// adds doc to currently playing display
@action
addCurrentlyPlaying = () => {
- const docView = this.props.DocumentView?.();
+ const docView = this._props.DocumentView?.();
if (!CollectionStackedTimeline.CurrentlyPlaying) {
CollectionStackedTimeline.CurrentlyPlaying = [];
}
@@ -861,7 +865,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// starts marquee selection
marqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
+ if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) === 1 && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
setupMoveUpEvents(
this,
e,
@@ -882,31 +886,31 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action
finishMarquee = () => {
this._marqueeref.current?.onTerminateSelection();
- this.props.select(true);
+ this._props.select(true);
};
- timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
+ timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this._props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
timelineScreenToLocal = () =>
- this.props
+ this._props
.ScreenToLocalTransform()
.scale(this.scaling())
- .translate(0, (-this.heightPercent / 100) * this.props.PanelHeight());
+ .translate(0, (-this.heightPercent / 100) * this._props.PanelHeight());
setPlayheadTime = (time: number) => (this.player!.currentTime = this.layoutDoc._layout_currentTimecode = time);
- timelineHeight = () => (this.props.PanelHeight() * (100 - this.heightPercent)) / 100;
+ timelineHeight = () => (this._props.PanelHeight() * (100 - this.heightPercent)) / 100;
playing = () => this._playing;
- scaling = () => this.props.NativeDimScaling?.() || 1;
+ scaling = () => this._props.NativeDimScaling?.() || 1;
- panelWidth = () => (this.props.PanelWidth() * this.heightPercent) / 100;
- panelHeight = () => (this.layoutDoc._layout_fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100);
+ panelWidth = () => (this._props.PanelWidth() * this.heightPercent) / 100;
+ panelHeight = () => (this.layoutDoc._layout_fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.dataDoc) || 1) : (this._props.PanelHeight() * this.heightPercent) / 100);
screenToLocalTransform = () => {
- const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
- return this.props
+ const offset = (this._props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
+ return this._props
.ScreenToLocalTransform()
.translate(-offset, 0)
.scale(100 / this.heightPercent);
@@ -918,10 +922,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// renders video controls
componentUI = (boundsLeft: number, boundsTop: number) => {
- const xf = this.props.ScreenToLocalTransform().inverse();
- const height = this.props.PanelHeight();
+ const xf = this._props.ScreenToLocalTransform().inverse();
+ const height = this._props.PanelHeight();
const vidHeight = (height * this.heightPercent) / 100 / this.scaling();
- const vidWidth = this.props.PanelWidth() / this.scaling();
+ const vidWidth = this._props.PanelWidth() / this.scaling();
const uiHeight = 25;
const uiMargin = 10;
const yBot = xf.transformPoint(0, vidHeight)[1];
@@ -936,7 +940,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div
className="videoBox-ui"
style={{
- transform: `rotate(${this.props.ScreenToLocalTransform().inverse().RotateDeg}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,
@@ -957,13 +961,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%`, display: this.heightPercent === 100 ? 'none' : '' }}>
<CollectionStackedTimeline
ref={action((r: any) => (this._stackedTimeline = r))}
- {...this.props}
+ {...this._props}
dataFieldKey={this.fieldKey}
fieldKey={this.annotationKey}
dictationKey={this.fieldKey + '_dictation'}
mediaPath={this.audiopath}
thumbnails={this.thumbnails}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
startTag={'_timecodeToShow' /* videoStart */}
endTag={'_timecodeToHide' /* videoEnd */}
bringToFront={emptyFunction}
@@ -999,7 +1003,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const cropping = Doc.MakeCopy(region, true);
Doc.GetProto(region).backgroundColor = 'transparent';
Doc.GetProto(region).lockedPosition = true;
- Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
+ Doc.GetProto(region).title = 'region:' + this.Document.title;
Doc.GetProto(region).followLinkToggle = true;
region._timecodeToHide = NumCast(region._timecodeToShow) + 0.0001;
this.addDocument(region);
@@ -1007,22 +1011,22 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const anchy = NumCast(cropping.y);
const anchw = NumCast(cropping._width);
const anchh = NumCast(cropping._height);
- const viewScale = NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']) / anchw;
- cropping.title = 'crop: ' + this.rootDoc.title;
- cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
- cropping.y = NumCast(this.rootDoc.y);
- cropping._width = anchw * (this.props.NativeDimScaling?.() || 1);
- cropping._height = anchh * (this.props.NativeDimScaling?.() || 1);
+ const viewScale = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) / anchw;
+ cropping.title = 'crop: ' + this.Document.title;
+ cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width);
+ cropping.y = NumCast(this.Document.y);
+ cropping._width = anchw * (this._props.NativeDimScaling?.() || 1);
+ cropping._height = anchh * (this._props.NativeDimScaling?.() || 1);
cropping.timecodeToHide = undefined;
cropping.timecodeToShow = undefined;
cropping.onClick = undefined;
const croppingProto = Doc.GetProto(cropping);
croppingProto.annotationOn = undefined;
croppingProto.isDataDoc = true;
- croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
+ croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
croppingProto.type = DocumentType.VID;
croppingProto.layout = VideoBox.LayoutString('data');
- croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
+ croppingProto.data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField);
croppingProto['data_nativeWidth'] = anchw;
croppingProto['data_nativeHeight'] = anchh;
croppingProto.videoCrop = true;
@@ -1038,12 +1042,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (addCrop) {
DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
}
- this.props.bringToFront(cropping);
+ this._props.bringToFront(cropping);
return cropping;
};
savedAnnotations = () => this._savedAnnotations;
render() {
- const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
+ const borderRad = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BorderRounding);
const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / this.scaling()}px` : borderRad;
return (
<div
@@ -1053,7 +1057,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
style={{
pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
borderRadius,
- overflow: this.props.docViewPath?.().slice(-1)[0].layout_fitWidth ? 'auto' : undefined,
+ overflow: this._props.docViewPath?.().slice(-1)[0].layout_fitWidth ? 'auto' : undefined,
}}>
<div className="videoBox-viewer" onPointerDown={this.marqueeDown}>
<div
@@ -1063,19 +1067,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
width: this.panelWidth(),
height: this.panelHeight(),
top: 0,
- left: (this.props.PanelWidth() - this.panelWidth()) / 2,
+ left: (this._props.PanelWidth() - this.panelWidth()) / 2,
}}>
<CollectionFreeFormView
- {...this.props}
+ {...this._props}
setContentView={emptyFunction}
NativeWidth={returnZero}
NativeHeight={returnZero}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
fieldKey={this.annotationKey}
isAnnotationOverlay={true}
annotationLayerHostsContent={true}
- PanelWidth={this.props.PanelWidth}
- PanelHeight={this.props.PanelHeight}
+ PanelWidth={this._props.PanelWidth}
+ PanelHeight={this._props.PanelHeight}
isAnyChildContentActive={returnFalse}
ScreenToLocalTransform={this.screenToLocalTransform}
childFilters={this.timelineDocFilter}
@@ -1093,12 +1097,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{!this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
ref={this._marqueeref}
- Document={this.rootDoc}
+ Document={this.Document}
scrollTop={0}
annotationLayerScrollTop={0}
scaling={returnOne}
- annotationLayerScaling={this.props.NativeDimScaling}
- docView={this.props.DocumentView!}
+ annotationLayerScaling={this._props.NativeDimScaling}
+ docView={this._props.DocumentView!}
containerOffset={this.marqueeOffset}
addDocument={this.addDocWithTimecode}
finishMarquee={this.finishMarquee}
@@ -1116,7 +1120,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
@computed get UIButtons() {
- const bounds = this.props.docViewPath().lastElement().getBounds();
+ const bounds = this._props.docViewPath().lastElement().getBounds();
const width = (bounds?.right || 0) - (bounds?.left || 0);
const curTime = NumCast(this.layoutDoc._layout_currentTimecode);
return (
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 511c91da0..a1686adaf 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.scss';
+@import '../global/globalCssVariables.module.scss';
.webBox {
height: 100%;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 1e6d92327..57045e2af 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,6 +1,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import * as WebRequest from 'web-request';
import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
@@ -33,10 +34,9 @@ import { SidebarAnnos } from '../SidebarAnnos';
import { StyleProp } from '../StyleProvider';
import { DocComponentView, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
-import { LinkDocPreview } from './LinkDocPreview';
+import { LinkInfo } from './LinkDocPreview';
import { PinProps, PresBox } from './trails';
import './WebBox.scss';
-import React = require('react');
const { CreateImage } = require('./WebBoxRenderer');
const _global = (window /* browser */ || global) /* node */ as any;
const htmlToText = require('html-to-text');
@@ -67,7 +67,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable private _searching: boolean = false;
@observable private _showSidebar = false;
@observable private _webPageHasBeenRendered = false;
- @observable private _marqueeing: number[] | undefined;
+ @observable private _marqueeing: number[] | undefined = undefined;
get marqueeing() {
return this._marqueeing;
}
@@ -95,12 +95,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return this.allAnnotations.filter(a => a.text_inlineAnnotations);
}
@computed get webField() {
- return Cast(this.Document[this.props.fieldKey], WebField)?.url;
+ return Cast(this.Document[this._props.fieldKey], WebField)?.url;
}
constructor(props: any) {
super(props);
- runInAction(() => (this._webUrl = this._url)); // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
+ makeObservable(this);
+ this._webUrl = this._url; // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
}
@action
@@ -139,7 +140,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (!this._iframe) return;
const scrollTop = NumCast(this.layoutDoc._layout_scrollTop);
const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
- const nativeHeight = (nativeWidth * this.props.PanelHeight()) / this.props.PanelWidth();
+ const nativeHeight = (nativeWidth * this._props.PanelHeight()) / this._props.PanelWidth();
var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
if (!htmlString) {
htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
@@ -170,8 +171,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
});
};
- async componentDidMount() {
- this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document. this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
+ componentDidMount() {
+ this._props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document. this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
runInAction(() => {
this._annotationKeySuffix = () => (this._urlHash ? this._urlHash + '_' : '') + 'annotations';
@@ -200,8 +201,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
() => this.layoutDoc._layout_autoHeight,
layout_autoHeight => {
if (layout_autoHeight) {
- this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']);
- this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
+ this.layoutDoc._nativeHeight = NumCast(this.Document[this._props.fieldKey + '_nativeHeight']);
+ this._props.setHeight?.(NumCast(this.Document[this._props.fieldKey + '_nativeHeight']) * (this._props.NativeDimScaling?.() || 1));
}
}
);
@@ -218,10 +219,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
} // else it's an HTMLfield
} else if (this.webField && !this.dataDoc.text) {
- const result = await WebRequest.get(Utils.CorsProxy(this.webField.href));
- if (result) {
- this.dataDoc.text = htmlToText.fromString(result.content);
- }
+ WebRequest.get(Utils.CorsProxy(this.webField.href)) //
+ .then(result => result && (this.dataDoc.text = htmlToText.convert(result.content)));
}
this._disposers.scrollReaction = reaction(
@@ -236,7 +235,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
{ fireImmediately: true }
);
}
- @action componentWillUnmount() {
+ componentWillUnmount() {
this._iframetimeout && clearTimeout(this._iframetimeout);
this._iframetimeout = undefined;
Object.values(this._disposers).forEach(disposer => disposer?.());
@@ -251,7 +250,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
if (this._mainCont.current && selRange) {
- if (this.dataDoc[this.props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this.props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`;
+ if (this.dataDoc[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);
@@ -259,7 +258,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (rect && rect.width !== this._mainCont.current.clientWidth) {
const annoBox = document.createElement('div');
annoBox.className = 'marqueeAnnotator-annotationBox';
- const scale = this._url ? 1 : this.props.ScreenToLocalTransform().Scale;
+ const scale = this._url ? 1 : this._props.ScreenToLocalTransform().Scale;
// transforms the positions from screen onto the pdf div
annoBox.style.top = ((rect.top - mainrect.translateY) * scale + (this._url ? this._mainCont.current.scrollTop : NumCast(this.layoutDoc.layout_scrollTop))).toString();
annoBox.style.left = ((rect.left - mainrect.translateX) * scale).toString();
@@ -282,7 +281,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
focus = (anchor: Doc, options: DocFocusOptions) => {
if (anchor !== this.Document && this._outerRef.current) {
- const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
+ const windowHeight = this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
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) {
@@ -298,8 +297,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
getView = (doc: Doc) => {
- if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this.props.DocumentView?.()));
- if (this.Document.layout_fieldKey === 'layout_icon') this.props.DocumentView?.().iconify();
+ if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this._props.DocumentView?.()));
+ if (this.Document.layout_fieldKey === 'layout_icon') this._props.DocumentView?.().iconify();
const webUrl = WebCast(doc.config_data)?.url;
if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href);
if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
@@ -307,11 +306,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
- if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
+ if (DocListCast(this.Document[this._props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
this.toggleSidebar(false);
return true;
}
- return this.props.addDocTab(doc, where);
+ return this._props.addDocTab(doc, where);
};
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
let ele: Opt<HTMLDivElement> = undefined;
@@ -344,10 +343,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
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.
+ 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) {
const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
- const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
+ const scale = (this._props.NativeDimScaling?.() || 1) * mainContBounds.scale;
const sel = this._iframe.contentWindow.getSelection();
if (sel) {
this._selectionText = sel.toString();
@@ -355,7 +354,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale);
// 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.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
}
}
@@ -393,23 +392,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._textAnnotationCreator = () => this.createTextAnnotation(sel, selRange);
(!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.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
}
};
@action
iframeDown = (e: PointerEvent) => {
- this.props.select(false);
+ this._props.select(false);
const locpt = {
- x: (e.clientX / NumCast(this.Document.nativeWidth)) * this.props.PanelWidth(),
- y: ((e.clientY - NumCast(this.layoutDoc.layout_scrollTop))/ NumCast(this.Document.nativeHeight)) * this.props.PanelHeight() }; // prettier-ignore
- const scrclick = this.props.DocumentView?.().props.ScreenToLocalTransform().inverse().transformPoint(locpt.x, locpt.y)!;
- const scrcent = this.props
+ x: (e.clientX / NumCast(this.Document.nativeWidth)) * this._props.PanelWidth(),
+ y: ((e.clientY - NumCast(this.layoutDoc.layout_scrollTop))/ NumCast(this.Document.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()
+ ._props.ScreenToLocalTransform()
.inverse()
.transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2)!;
- const theclickoff = Utils.rotPt(scrclick[0] - scrcent[0], scrclick[1] - scrcent[1], -this.props.ScreenToLocalTransform().Rotate);
+ 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);
const word = getWordAtPoint(e.target, e.clientX, e.clientY);
@@ -489,7 +488,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._scrollHeight = Math.max(this._scrollHeight, iframeContent.body.scrollHeight || 0);
if (this._scrollHeight) {
this.Document.nativeHeight = Math.min(NumCast(this.Document.nativeHeight), this._scrollHeight);
- this.layoutDoc.height = Math.min(NumCast(this.layoutDoc._height), (NumCast(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.Document.nativeHeight)) / NumCast(this.Document.nativeWidth));
}
};
const swidth = Math.max(NumCast(this.layoutDoc.nativeWidth), iframeContent.body.scrollWidth || 0);
@@ -564,7 +563,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
clearStyleSheetRules(WebBox.webStyleSheet);
this._scrollTimer = undefined;
const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
- if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
+ if (!LinkInfo.Instance?.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath()))) {
this.layoutDoc.thumb = undefined;
this.layoutDoc.thumbScrollTop = undefined;
this.layoutDoc.thumbNativeWidth = undefined;
@@ -608,8 +607,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
back = (checkAvailable?: boolean) => {
- const future = Cast(this.rootDoc[this.fieldKey + '_future'], listSpec('string'));
- const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []);
+ const future = Cast(this.dataDoc[this.fieldKey + '_future'], listSpec('string'));
+ const history = Cast(this.dataDoc[this.fieldKey + '_history'], listSpec('string'), []);
if (checkAvailable) return history.length;
runInAction(() => {
if (history.length) {
@@ -677,7 +676,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (Field.toString(data) === this._url) return false;
this._scrollHeight = 0;
const oldUrl = this._url;
- const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []);
+ const history = Cast(this.dataDoc[this.fieldKey + '_history'], listSpec('string'), []);
const weburl = new WebField(Field.toString(data));
this.dataDoc[this.fieldKey + '_future'] = new List<string>([]);
this.dataDoc[this.fieldKey + '_history'] = new List<string>([...(history || []), oldUrl]);
@@ -709,7 +708,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
funcs.push({
description: (!this.layoutDoc.layout_reflowHorizontal ? 'Force' : 'Prevent') + ' Reflow',
event: () => {
- const nw = !this.layoutDoc.layout_reflowHorizontal ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
+ 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);
@@ -734,7 +733,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
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)) {
+ if (!e.altKey && e.button === 0 && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
setupMoveUpEvents(
this,
e,
@@ -770,7 +769,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
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);
+ this._props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
});
}
}
@@ -779,7 +778,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable lighttext = false;
@computed get urlContent() {
- if (this.props.ScreenToLocalTransform().Scale > 25) return <div></div>;
+ if (this._props.ScreenToLocalTransform().Scale > 25) return <div></div>;
setTimeout(
action(() => {
if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
@@ -788,7 +787,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._webPageHasBeenRendered = true;
})
);
- const field = this.dataDoc[this.props.fieldKey];
+ const field = this.dataDoc[this._props.fieldKey];
if (field instanceof HtmlField) {
return (
<span
@@ -845,14 +844,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
e,
action((e, down, delta) => {
this._draggingSidebar = true;
- const localDelta = this.props
+ const localDelta = this._props
.ScreenToLocalTransform()
- .scale(this.props.NativeDimScaling?.() || 1)
+ .scale(this._props.NativeDimScaling?.() || 1)
.transformDirection(delta[0], delta[1]);
const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight']);
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
- const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
+ const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this._props.NativeDimScaling?.() || 1)) / nativeWidth;
if (ratio >= 1) {
this.layoutDoc.nativeWidth = nativeWidth * ratio;
this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
@@ -916,7 +915,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
});
@action
onZoomWheel = (e: React.WheelEvent) => {
- if (this.props.isContentActive(true)) {
+ if (this._props.isContentActive(true)) {
e.stopPropagation();
}
};
@@ -924,14 +923,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (!this.SidebarShown) return 0;
if (this._previewWidth) return WebBox.sidebarResizerWidth + WebBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
- return WebBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
+ return WebBox.sidebarResizerWidth + nativeDiff * (this._props.NativeDimScaling?.() || 1);
};
_innerCollectionView: CollectionFreeFormView | undefined;
zoomScaling = () => this._innerCollectionView?.zoomScaling() ?? 1;
setInnerContent = (component: DocComponentView) => (this._innerCollectionView = component as CollectionFreeFormView);
@computed get content() {
- const interactive = this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
+ const interactive = this._props.isContentActive() && this._props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
return (
<div
className={'webBox-cont' + (interactive ? '-interactive' : '')}
@@ -959,7 +958,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} 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>
);
@@ -969,13 +968,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
renderAnnotations = (childFilters: () => string[]) => (
<CollectionFreeFormView
- {...this.props}
+ {...this._props}
setContentView={this.setInnerContent}
NativeWidth={returnZero}
NativeHeight={returnZero}
originTopLeft={false}
isAnnotationOverlayScrollable={true}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
isAnnotationOverlay={true}
fieldKey={this.annotationKey}
setPreviewCursor={this.setPreviewCursor}
@@ -1004,11 +1003,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@computed get renderTransparentAnnotations() {
return this.renderAnnotations(this.transparentFilter);
}
- childPointerEvents = () => (this.props.isContentActive() ? 'all' : undefined);
+ childPointerEvents = () => (this._props.isContentActive() ? 'all' : undefined);
@computed get webpage() {
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
- const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
- const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
+ const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this._props.pointerEvents?.() as any);
+ const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
return (
<div
className="webBox-outerContent"
@@ -1021,9 +1020,9 @@ 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 > this.props.PanelHeight() && 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>
+ <div style={{ display: SnappingManager.CanEmbed ? 'none' : undefined, mixBlendMode: 'multiply' }}>{this.renderTransparentAnnotations}</div>
{this.renderOpaqueAnnotations}
{this.annotationLayer}
</div>
@@ -1033,7 +1032,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@computed get searchUI() {
return (
- <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}>
+ <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this._props.isContentActive() ? 'flex' : 'none' }}>
<div className="webBox-overlayCont" onPointerDown={e => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
<button className="webBox-overlayButton" title={'search'} />
<input
@@ -1067,36 +1066,36 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
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));
+ 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.TransparentBackgroundFilter];
- opaqueFilter = () => [...this.props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.OpaqueBackgroundFilter])];
+ transparentFilter = () => [...this._props.childFilters(), Utils.TransparentBackgroundFilter];
+ opaqueFilter = () => [...this._props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.CanEmbed ? [] : [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';
}
- return this.props.styleProvider?.(doc, props, property);
+ return this._props.styleProvider?.(doc, props, property);
};
pointerEvents = () =>
- !this._draggingSidebar && this.props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown()
+ !this._draggingSidebar && this._props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown()
? 'all' //
: 'none';
- annotationPointerEvents = () => (this.props.isContentActive() && (SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
+ annotationPointerEvents = () => (this._props.isContentActive() && (SnappingManager.IsDragging || 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);
- const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
+ const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this._props.pointerEvents?.() as any);
+ const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
return (
<div
className="webBox"
ref={this._mainCont}
style={{
pointerEvents: this.pointerEvents(), //
- position: SnappingManager.GetIsDragging() ? 'absolute' : undefined,
+ position: SnappingManager.IsDragging ? 'absolute' : undefined,
}}>
- <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }} />
+ <div className="webBox-background" style={{ backgroundColor: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) }} />
<div
className="webBox-container"
style={{
@@ -1115,9 +1114,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
anchorMenuClick={this.anchorMenuClick}
scrollTop={NumCast(this.layoutDoc.layout_scrollTop)}
annotationLayerScrollTop={0}
- scaling={this.props.NativeDimScaling}
+ scaling={this._props.NativeDimScaling}
addDocument={this.addDocumentWrapper}
- docView={this.props.DocumentView!}
+ docView={this._props.DocumentView!}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotationsCreator}
selectionText={this.selectionText}
@@ -1135,25 +1134,25 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}}
onPointerDown={e => this.sidebarBtnDown(e, false)}
/>
- <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.props.PanelWidth()}%` }}>
+ <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this._props.PanelWidth()}%` }}>
<SidebarAnnos
ref={this._sidebarRef}
- {...this.props}
+ {...this._props}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
fieldKey={this.fieldKey + '_' + this._urlHash}
- rootDoc={this.rootDoc}
+ Document={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
setHeight={emptyFunction}
- nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.NativeDimScaling?.() || 1)}
+ nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this._props.NativeDimScaling?.() || 1)}
showSidebar={this.SidebarShown}
sidebarAddDocument={this.sidebarAddDocument}
moveDocument={this.moveDocument}
removeDocument={this.removeDocument}
/>
</div>
- {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.sidebarHandle}
- {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.searchUI}
+ {!this._props.isContentActive() || SnappingManager.IsDragging ? null : this.sidebarHandle}
+ {!this._props.isContentActive() || SnappingManager.IsDragging ? null : this.searchUI}
</div>
);
}
diff --git a/src/client/views/nodes/audio/AudioWaveform.scss b/src/client/views/nodes/audio/AudioWaveform.scss
new file mode 100644
index 000000000..6cbd1759a
--- /dev/null
+++ b/src/client/views/nodes/audio/AudioWaveform.scss
@@ -0,0 +1,17 @@
+.audioWaveform {
+ position: relative;
+ width: 100%;
+ height: 200%;
+ overflow: hidden;
+ z-index: -1000;
+ bottom: 0;
+ pointer-events: none;
+ div {
+ height: 100% !important;
+ width: 100% !important;
+ }
+ canvas {
+ height: 100% !important;
+ width: 100% !important;
+ }
+}
diff --git a/src/client/views/nodes/audio/AudioWaveform.tsx b/src/client/views/nodes/audio/AudioWaveform.tsx
new file mode 100644
index 000000000..01392c4a5
--- /dev/null
+++ b/src/client/views/nodes/audio/AudioWaveform.tsx
@@ -0,0 +1,122 @@
+import axios from 'axios';
+import { computed, IReactionDisposer, makeObservable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, NumListCast } from '../../../../fields/Doc';
+import { List } from '../../../../fields/List';
+import { listSpec } from '../../../../fields/Schema';
+import { Cast } from '../../../../fields/Types';
+import { numberRange } from '../../../../Utils';
+import { Colors } from './../../global/globalEnums';
+import './AudioWaveform.scss';
+import { WaveCanvas } from './WaveCanvas';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+
+/**
+ * AudioWaveform
+ *
+ * Used in CollectionStackedTimeline to render a canvas with a visual of an audio waveform for AudioBox and VideoBox documents.
+ * Uses react-audio-waveform package.
+ * Bins the audio data into audioBuckets which are passed to package to render the lines.
+ * Calculates new buckets each time a new zoom factor or new set of trim bounds is created and stores it in a field on the layout doc with a title indicating the bounds and zoom for that list (see audioBucketField)
+ */
+
+export interface AudioWaveformProps {
+ duration: number; // length of media clip
+ rawDuration: number; // length of underlying media data
+ mediaPath: string;
+ layoutDoc: Doc;
+ clipStart: number;
+ clipEnd: number;
+ zoomFactor: number;
+ PanelHeight: number;
+ PanelWidth: number;
+ fieldKey: string;
+ progress?: number;
+}
+
+@observer
+export class AudioWaveform extends ObservableReactComponent<AudioWaveformProps> {
+ public static NUMBER_OF_BUCKETS = 100; // number of buckets data is divided into to draw waveform lines
+ _disposer: IReactionDisposer | undefined;
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
+ get waveHeight() {
+ return Math.max(50, this._props.PanelHeight);
+ }
+
+ get clipStart() {
+ return this._props.clipStart;
+ }
+ get clipEnd() {
+ return this._props.clipEnd;
+ }
+ get zoomFactor() {
+ return this._props.zoomFactor;
+ }
+
+ @computed get audioBuckets() {
+ return NumListCast(this._props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor)]);
+ }
+
+ audioBucketField = (start: number, end: number, zoomFactor: number) => this._props.fieldKey + '_audioBuckets/' + '/' + start.toFixed(2).replace('.', '_') + '/' + end.toFixed(2).replace('.', '_') + '/' + zoomFactor * 10;
+
+ componentWillUnmount() {
+ this._disposer?.();
+ }
+
+ componentDidMount() {
+ this._disposer = reaction(
+ () => ({ clipStart: this.clipStart, clipEnd: this.clipEnd, fieldKey: this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor), zoomFactor: this._props.zoomFactor }),
+ ({ clipStart, clipEnd, fieldKey, zoomFactor }) => {
+ if (!this._props.layoutDoc[fieldKey] && this._props.layoutDoc.layout_fieldKey != 'layout_icon') {
+ // setting these values here serves as a "lock" to prevent multiple attempts to create the waveform at nerly the same time.
+ const waveform = Cast(this._props.layoutDoc[this.audioBucketField(0, this._props.rawDuration, 1)], listSpec('number'));
+ this._props.layoutDoc[fieldKey] = waveform && new List<number>(waveform.slice((clipStart / this._props.rawDuration) * waveform.length, (clipEnd / this._props.rawDuration) * waveform.length));
+ setTimeout(() => this.createWaveformBuckets(fieldKey, clipStart, clipEnd, zoomFactor));
+ }
+ },
+ { fireImmediately: true }
+ );
+ }
+
+ // decodes the audio file into peaks for generating the waveform
+ createWaveformBuckets = (fieldKey: string, clipStart: number, clipEnd: number, zoomFactor: number) => {
+ axios({ url: this._props.mediaPath, responseType: 'arraybuffer' }).then(response =>
+ new window.AudioContext().decodeAudioData(response.data, buffer => {
+ const rawDecodedAudioData = buffer.getChannelData(0);
+ const startInd = clipStart / this._props.rawDuration;
+ const endInd = clipEnd / this._props.rawDuration;
+ const decodedAudioData = rawDecodedAudioData.slice(Math.floor(startInd * rawDecodedAudioData.length), Math.floor(endInd * rawDecodedAudioData.length));
+ const numBuckets = Math.floor(AudioWaveform.NUMBER_OF_BUCKETS * zoomFactor);
+
+ const bucketDataSize = Math.floor(decodedAudioData.length / numBuckets);
+ const brange = Array.from(Array(bucketDataSize));
+ const bucketList = numberRange(numBuckets).map((i: number) => brange.reduce((p, x, j) => Math.abs(Math.max(p, decodedAudioData[i * bucketDataSize + j])), 0) / 2);
+ this._props.layoutDoc[fieldKey] = new List<number>(bucketList);
+ })
+ );
+ };
+
+ render() {
+ return (
+ <div className="audioWaveform">
+ <WaveCanvas
+ color={Colors.LIGHT_GRAY}
+ progressColor={Colors.MEDIUM_BLUE_ALT}
+ progress={this._props.progress ?? 1}
+ barWidth={200 / this.audioBuckets.length}
+ //gradientColors={this.props.gradientColors}
+ peaks={this.audioBuckets}
+ width={(this._props.PanelWidth ?? 0) * window.devicePixelRatio}
+ height={this.waveHeight * window.devicePixelRatio}
+ pixelRatio={window.devicePixelRatio}
+ />
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/nodes/audio/WaveCanvas.tsx b/src/client/views/nodes/audio/WaveCanvas.tsx
new file mode 100644
index 000000000..d3f5669a2
--- /dev/null
+++ b/src/client/views/nodes/audio/WaveCanvas.tsx
@@ -0,0 +1,100 @@
+import React from 'react';
+
+interface WaveCanvasProps {
+ barWidth: number;
+ color: string;
+ progress: number;
+ progressColor: string;
+ gradientColors?: { stopPosition: number; color: string }[]; // stopPosition between 0 and 1
+ peaks: number[];
+ width: number;
+ height: number;
+ pixelRatio: number;
+}
+
+export class WaveCanvas extends React.Component<WaveCanvasProps> {
+ // If the first value of peaks is negative, addToIndices will be 1
+ posPeaks = (peaks: number[], addToIndices: number) => peaks.filter((_, index) => (index + addToIndices) % 2 == 0);
+
+ drawBars = (waveCanvasCtx: CanvasRenderingContext2D, width: number, halfH: number, peaks: number[]) => {
+ // Bar wave draws the bottom only as a reflection of the top,
+ // so we don't need negative values
+ const posPeaks = peaks.some(val => val < 0) ? this.posPeaks(peaks, peaks[0] < 0 ? 1 : 0) : peaks;
+
+ // A half-pixel offset makes lines crisp
+ const $ = 0.5 / this.props.pixelRatio;
+ const bar = this.props.barWidth * this.props.pixelRatio;
+ const gap = Math.max(this.props.pixelRatio, 2);
+
+ const max = Math.max(...posPeaks);
+ const scale = posPeaks.length / width;
+
+ for (let i = 0; i < width; i += bar + gap) {
+ if (i > width * this.props.progress) waveCanvasCtx.fillStyle = this.props.color;
+
+ const h = Math.round((posPeaks[Math.floor(i * scale)] / max) * halfH) || 1;
+
+ waveCanvasCtx.fillRect(i + $, halfH - h, bar + $, h * 2);
+ }
+ };
+
+ addNegPeaks = (peaks: number[]) =>
+ peaks.reduce((reflectedPeaks, peak) => reflectedPeaks.push(peak, -peak) ? reflectedPeaks:[],
+ [] as number[]); // prettier-ignore
+
+ drawWaves = (waveCanvasCtx: CanvasRenderingContext2D, width: number, halfH: number, peaks: number[]) => {
+ const allPeaks = peaks.some(val => val < 0) ? peaks : this.addNegPeaks(peaks); // add negative peaks to arrays without negative peaks
+
+ // A half-pixel offset makes lines crisp
+ const $ = 0.5 / this.props.pixelRatio;
+ const length = ~~(allPeaks.length / 2); // ~~ is Math.floor for positive numbers.
+
+ const scale = width / length;
+ const absmax = Math.max(...allPeaks.map(peak => Math.abs(peak)));
+
+ waveCanvasCtx.beginPath();
+ waveCanvasCtx.moveTo($, halfH);
+
+ for (var i = 0; i < length; i++) {
+ var h = Math.round((allPeaks[2 * i] / absmax) * halfH);
+ waveCanvasCtx.lineTo(i * scale + $, halfH - h);
+ }
+
+ // Draw the bottom edge going backwards, to make a single closed hull to fill.
+ for (var i = length - 1; i >= 0; i--) {
+ var h = Math.round((allPeaks[2 * i + 1] / absmax) * halfH);
+ waveCanvasCtx.lineTo(i * scale + $, halfH - h);
+ }
+
+ waveCanvasCtx.fill();
+
+ // Always draw a median line
+ waveCanvasCtx.fillRect(0, halfH - $, width, $);
+ };
+
+ updateSize = (width: number, height: number, peaks: number[], waveCanvasCtx: CanvasRenderingContext2D) => {
+ const displayWidth = Math.round(width / this.props.pixelRatio);
+ const displayHeight = Math.round(height / this.props.pixelRatio);
+ waveCanvasCtx.canvas.width = width;
+ waveCanvasCtx.canvas.height = height;
+ waveCanvasCtx.canvas.style.width = `${displayWidth}px`;
+ waveCanvasCtx.canvas.style.height = `${displayHeight}px`;
+
+ waveCanvasCtx.clearRect(0, 0, width, height);
+
+ const gradient = this.props.gradientColors && waveCanvasCtx.createLinearGradient(0, 0, width, 0);
+ gradient && this.props.gradientColors?.forEach(color => gradient.addColorStop(color.stopPosition, color.color));
+ waveCanvasCtx.fillStyle = gradient ?? this.props.progressColor;
+
+ const waveDrawer = this.props.barWidth ? this.drawBars : this.drawWaves;
+ waveDrawer(waveCanvasCtx, width, height / 2, peaks);
+ };
+
+ render() {
+ return this.props.peaks ? (
+ <div style={{ position: 'relative', width: '100%', height: '100%', cursor: 'pointer' }}>
+ <canvas ref={instance => (ctx => ctx && this.updateSize(this.props.width, this.props.height, this.props.peaks, ctx))(instance?.getContext('2d'))} />
+ </div>
+ ) : null;
+ }
+}
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index aa269d8d6..b7d2a24c2 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -2,7 +2,7 @@ import { TextSelection } from 'prosemirror-state';
import * as ReactDOM from 'react-dom/client';
import { Doc } from '../../../../fields/Doc';
import { DocServer } from '../../../DocServer';
-import React = require('react');
+import * as React from 'react';
// creates an inline comment in a note when '>>' is typed.
// the comment sits on the right side of the note and vertically aligns with its anchor in the text.
@@ -54,7 +54,7 @@ interface IDashDocCommentViewInternal {
}
export class DashDocCommentViewInternal extends React.Component<IDashDocCommentViewInternal> {
- constructor(props: IDashDocCommentViewInternal) {
+ constructor(props: any) {
super(props);
this.onPointerLeaveCollapsed = this.onPointerLeaveCollapsed.bind(this);
this.onPointerEnterCollapsed = this.onPointerEnterCollapsed.bind(this);
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index e8b9a98b7..6332b200d 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -11,7 +11,7 @@ import { Docs, DocUtils } from '../../../documents/Documents';
import { Transform } from '../../../util/Transform';
import { DocFocusOptions, DocumentView } from '../DocumentView';
import { FormattedTextBox } from './FormattedTextBox';
-import React = require('react');
+import * as React from 'react';
export class DashDocView {
dom: HTMLSpanElement; // container for label and value
@@ -82,7 +82,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
_spanRef = React.createRef<HTMLDivElement>();
_disposers: { [name: string]: IReactionDisposer } = {};
_textBox: FormattedTextBox;
- @observable _dashDoc: Doc | undefined;
+ @observable _dashDoc: Doc | undefined = undefined;
@observable _finalLayout: any;
@observable _width: number = 0;
@observable _height: number = 0;
@@ -202,7 +202,6 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
<DocumentView
Document={this._finalLayout}
addDocument={returnFalse}
- rootSelected={returnFalse} //{this._textBox.props.isSelected}
removeDocument={this.removeDoc}
isDocumentActive={returnFalse}
isContentActive={this.isContentActive}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index ad315acc8..3426ba1a7 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables';
+@import '../../global/globalCssVariables.module.scss';
.dashFieldView {
position: relative;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 130ac40c8..555b752f0 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,23 +1,24 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { Doc } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { Cast, StrCast } from '../../../../fields/Types';
+import { Cast } from '../../../../fields/Types';
import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { CollectionViewType } from '../../../documents/DocumentTypes';
+import { Transform } from '../../../util/Transform';
import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableCell';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
import { OpenWhere } from '../DocumentView';
import './DashFieldView.scss';
import { FormattedTextBox } from './FormattedTextBox';
-import React = require('react');
-import { Transform } from '../../../util/Transform';
export class DashFieldView {
dom: HTMLDivElement; // container for label and value
@@ -25,7 +26,7 @@ export class DashFieldView {
node: any;
tbox: FormattedTextBox;
- unclickable = () => !this.tbox.props.isSelected() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview);
+ unclickable = () => !this.tbox._props.isSelected() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview);
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
this.node = node;
this.tbox = tbox;
@@ -70,10 +71,10 @@ export class DashFieldView {
} catch {}
});
}
- @action deselectNode() {
+ deselectNode() {
this.dom.classList.remove('ProseMirror-selectednode');
}
- @action selectNode() {
+ selectNode() {
this.dom.classList.add('ProseMirror-selectednode');
}
}
@@ -92,25 +93,27 @@ interface IDashFieldViewInternal {
}
@observer
-export class DashFieldViewInternal extends React.Component<IDashFieldViewInternal> {
+export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldViewInternal> {
_reactionDisposer: IReactionDisposer | undefined;
_textBoxDoc: Doc;
_fieldKey: string;
_fieldStringRef = React.createRef<HTMLSpanElement>();
- @observable _dashDoc: Doc | undefined;
+ @observable _dashDoc: Doc | undefined = undefined;
@observable _expanded = false;
constructor(props: IDashFieldViewInternal) {
super(props);
- this._fieldKey = this.props.fieldKey;
- this._textBoxDoc = this.props.tbox.props.Document;
+ makeObservable(this);
+ this._fieldKey = this._props.fieldKey;
+ this._textBoxDoc = this._props.tbox.Document;
- if (this.props.docId) {
- DocServer.GetRefField(this.props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
+ if (this._props.docId) {
+ DocServer.GetRefField(this._props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
} else {
- this._dashDoc = this.props.tbox.Document;
+ this._dashDoc = this._props.tbox.Document;
}
}
+
componentWillUnmount() {
this._reactionDisposer?.();
}
@@ -119,18 +122,18 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
// set the display of the field's value (checkbox for booleans, span of text for strings)
@computed get fieldValueContent() {
return !this._dashDoc ? null : (
- <div onClick={action(e => (this._expanded = !this.props.editable ? !this._expanded : true))} style={{ fontSize: 'smaller', width: this.props.hideKey ? this.props.tbox.props.PanelWidth() - 20 : undefined }}>
+ <div onClick={action(e => (this._expanded = !this._props.editable ? !this._expanded : true))} style={{ fontSize: 'smaller', width: this._props.hideKey ? this._props.tbox._props.PanelWidth() - 20 : undefined }}>
<SchemaTableCell
Document={this._dashDoc}
col={0}
deselectCell={emptyFunction}
selectCell={emptyFunction}
- maxWidth={this.props.hideKey ? undefined : this.return100}
- columnWidth={this.props.hideKey ? () => this.props.tbox.props.PanelWidth() - 20 : returnZero}
+ maxWidth={this._props.hideKey ? undefined : this.return100}
+ columnWidth={this._props.hideKey ? () => this._props.tbox._props.PanelWidth() - 20 : returnZero}
selectedCell={() => [this._dashDoc!, 0]}
fieldKey={this._fieldKey}
rowHeight={returnZero}
- isRowActive={() => this._expanded && this.props.editable}
+ isRowActive={() => this._expanded && this._props.editable}
padding={0}
getFinfo={emptyFunction}
setColumnValues={returnFalse}
@@ -145,9 +148,9 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
}
createPivotForField = (e: React.MouseEvent) => {
- let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement();
+ let container = this._props.tbox._props.DocumentView?.()._props.docViewPath().lastElement();
if (container) {
- const embedding = Doc.MakeEmbedding(container.rootDoc);
+ const embedding = Doc.MakeEmbedding(container.Document);
embedding._type_collection = CollectionViewType.Time;
const colHdrKey = '_' + container.LayoutFieldKey + '_columnHeaders';
let list = Cast(embedding[colHdrKey], listSpec(SchemaHeaderField));
@@ -157,7 +160,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, '#f1efeb'));
list.map(c => c.heading).indexOf('text') === -1 && list.push(new SchemaHeaderField('text', '#f1efeb'));
embedding._pivotField = this._fieldKey.startsWith('#') ? 'tags' : this._fieldKey;
- this.props.tbox.props.addDocTab(embedding, OpenWhere.addRight);
+ this._props.tbox._props.addDocTab(embedding, OpenWhere.addRight);
}
};
@@ -175,17 +178,17 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
<div
className="dashFieldView"
style={{
- width: this.props.width,
- height: this.props.height,
- pointerEvents: this.props.tbox.props.isSelected() || this.props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
+ width: this._props.width,
+ height: this._props.height,
+ pointerEvents: this._props.tbox._props.isSelected() || this._props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
}}>
- {this.props.hideKey ? null : (
+ {this._props.hideKey ? null : (
<span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
{this._fieldKey}
</span>
)}
- {this.props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
+ {this._props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
</div>
);
}
@@ -198,7 +201,7 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
super(props);
DashFieldViewMenu.Instance = this;
}
- @action
+
showFields = (e: React.MouseEvent) => {
DashFieldViewMenu.createFieldView(e);
DashFieldViewMenu.Instance.fadeOut(true);
diff --git a/src/client/views/nodes/formattedText/EquationEditor.scss b/src/client/views/nodes/formattedText/EquationEditor.scss
new file mode 100644
index 000000000..b0c17e56e
--- /dev/null
+++ b/src/client/views/nodes/formattedText/EquationEditor.scss
@@ -0,0 +1,468 @@
+// using this import, we get runtime errors when trying to load the specified font-faces
+// so we copy the .css and remove the @font-face imports
+
+// @import 'mathquill/build/mathquill.css'
+/*
+ * MathQuill v0.10.1 http://mathquill.com
+ * by Han, Jeanine, and Mary maintainers@mathquill.com
+ *
+ * This Source Code Form is subject to the terms of the
+ * Mozilla Public License, v. 2.0. If a copy of the MPL
+ * was not distributed with this file, You can obtain
+ * one at http://mozilla.org/MPL/2.0/.
+ */
+// @font-face {
+// font-family: Symbola;
+// src: url(font/Symbola.eot);
+// src:
+// local('Symbola Regular'),
+// local('Symbola'),
+// url(font/Symbola.woff2) format('woff2'),
+// url(font/Symbola.woff) format('woff'),
+// url(font/Symbola.ttf) format('truetype'),
+// url(font/Symbola.otf) format('opentype'),
+// url(font/Symbola.svg#Symbola) format('svg');
+// }
+.mq-editable-field {
+ display: -moz-inline-box;
+ display: inline-block;
+}
+.mq-editable-field .mq-cursor {
+ border-left: 1px solid black;
+ margin-left: -1px;
+ position: relative;
+ z-index: 1;
+ padding: 0;
+ display: -moz-inline-box;
+ display: inline-block;
+}
+.mq-editable-field .mq-cursor.mq-blink {
+ visibility: hidden;
+}
+.mq-editable-field,
+.mq-math-mode .mq-editable-field {
+ border: 1px solid gray;
+}
+.mq-editable-field.mq-focused,
+.mq-math-mode .mq-editable-field.mq-focused {
+ -webkit-box-shadow:
+ #8bd 0 0 1px 2px,
+ inset #6ae 0 0 2px 0;
+ -moz-box-shadow:
+ #8bd 0 0 1px 2px,
+ inset #6ae 0 0 2px 0;
+ box-shadow:
+ #8bd 0 0 1px 2px,
+ inset #6ae 0 0 2px 0;
+ border-color: #709ac0;
+ border-radius: 1px;
+}
+.mq-math-mode .mq-editable-field {
+ margin: 1px;
+}
+.mq-editable-field .mq-latex-command-input {
+ color: inherit;
+ font-family: 'Courier New', monospace;
+ border: 1px solid gray;
+ padding-right: 1px;
+ margin-right: 1px;
+ margin-left: 2px;
+}
+.mq-editable-field .mq-latex-command-input.mq-empty {
+ background: transparent;
+}
+.mq-editable-field .mq-latex-command-input.mq-hasCursor {
+ border-color: ActiveBorder;
+}
+.mq-editable-field.mq-empty:after,
+.mq-editable-field.mq-text-mode:after,
+.mq-math-mode .mq-empty:after {
+ visibility: hidden;
+ content: 'c';
+}
+.mq-editable-field .mq-cursor:only-child:after,
+.mq-editable-field .mq-textarea + .mq-cursor:last-child:after {
+ visibility: hidden;
+ content: 'c';
+}
+.mq-editable-field .mq-text-mode .mq-cursor:only-child:after {
+ content: '';
+}
+.mq-editable-field.mq-text-mode {
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+.mq-root-block,
+.mq-math-mode .mq-root-block {
+ display: -moz-inline-box;
+ display: inline-block;
+ width: 100%;
+ padding: 2px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ white-space: nowrap;
+ overflow: hidden;
+ vertical-align: middle;
+}
+.mq-math-mode {
+ font-variant: normal;
+ font-weight: normal;
+ font-style: normal;
+ font-size: 115%;
+ line-height: 1;
+ display: -moz-inline-box;
+ display: inline-block;
+}
+.mq-math-mode .mq-non-leaf,
+.mq-math-mode .mq-scaled {
+ display: -moz-inline-box;
+ display: inline-block;
+}
+.mq-math-mode var,
+.mq-math-mode .mq-text-mode,
+.mq-math-mode .mq-nonSymbola {
+ font-family: 'Times New Roman', Symbola, serif;
+ line-height: 0.9;
+}
+.mq-math-mode * {
+ font-size: inherit;
+ line-height: inherit;
+ margin: 0;
+ padding: 0;
+ border-color: black;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ box-sizing: border-box;
+}
+.mq-math-mode .mq-empty {
+ background: #ccc;
+}
+.mq-math-mode .mq-empty.mq-root-block {
+ background: transparent;
+}
+.mq-math-mode.mq-empty {
+ background: transparent;
+}
+.mq-math-mode .mq-text-mode {
+ display: inline-block;
+}
+.mq-math-mode .mq-text-mode.mq-hasCursor {
+ box-shadow: inset darkgray 0 0.1em 0.2em;
+ padding: 0 0.1em;
+ margin: 0 -0.1em;
+ min-width: 1ex;
+}
+.mq-math-mode .mq-font {
+ font:
+ 1em 'Times New Roman',
+ Symbola,
+ serif;
+}
+.mq-math-mode .mq-font * {
+ font-family: inherit;
+ font-style: inherit;
+}
+.mq-math-mode b,
+.mq-math-mode b.mq-font {
+ font-weight: bolder;
+}
+.mq-math-mode var,
+.mq-math-mode i,
+.mq-math-mode i.mq-font {
+ font-style: italic;
+}
+.mq-math-mode var.mq-f {
+ margin-right: 0.2em;
+ margin-left: 0.1em;
+}
+.mq-math-mode .mq-roman var.mq-f {
+ margin: 0;
+}
+.mq-math-mode big {
+ font-size: 200%;
+}
+.mq-math-mode .mq-int > big {
+ display: inline-block;
+ -webkit-transform: scaleX(0.7);
+ -moz-transform: scaleX(0.7);
+ -ms-transform: scaleX(0.7);
+ -o-transform: scaleX(0.7);
+ transform: scaleX(0.7);
+ vertical-align: -0.16em;
+}
+.mq-math-mode .mq-int > .mq-supsub {
+ font-size: 80%;
+ vertical-align: -1.1em;
+ padding-right: 0.2em;
+}
+.mq-math-mode .mq-int > .mq-supsub > .mq-sup > .mq-sup-inner {
+ vertical-align: 1.3em;
+}
+.mq-math-mode .mq-int > .mq-supsub > .mq-sub {
+ margin-left: -0.35em;
+}
+.mq-math-mode .mq-roman {
+ font-style: normal;
+}
+.mq-math-mode .mq-sans-serif {
+ font-family: sans-serif, Symbola, serif;
+}
+.mq-math-mode .mq-monospace {
+ font-family: monospace, Symbola, serif;
+}
+.mq-math-mode .mq-overline {
+ border-top: 1px solid black;
+ margin-top: 1px;
+}
+.mq-math-mode .mq-underline {
+ border-bottom: 1px solid black;
+ margin-bottom: 1px;
+}
+.mq-math-mode .mq-binary-operator {
+ padding: 0 0.2em;
+ display: -moz-inline-box;
+ display: inline-block;
+}
+.mq-math-mode .mq-supsub {
+ text-align: left;
+ font-size: 90%;
+ vertical-align: -0.5em;
+}
+.mq-math-mode .mq-supsub.mq-sup-only {
+ vertical-align: 0.5em;
+}
+.mq-math-mode .mq-supsub.mq-sup-only .mq-sup {
+ display: inline-block;
+ vertical-align: text-bottom;
+}
+.mq-math-mode .mq-supsub .mq-sup {
+ display: block;
+}
+.mq-math-mode .mq-supsub .mq-sub {
+ display: block;
+ float: left;
+}
+.mq-math-mode .mq-supsub .mq-binary-operator {
+ padding: 0 0.1em;
+}
+.mq-math-mode .mq-supsub .mq-fraction {
+ font-size: 70%;
+}
+.mq-math-mode sup.mq-nthroot {
+ font-size: 80%;
+ vertical-align: 0.8em;
+ margin-right: -0.6em;
+ margin-left: 0.2em;
+ min-width: 0.5em;
+}
+.mq-math-mode .mq-paren {
+ padding: 0 0.1em;
+ vertical-align: top;
+ -webkit-transform-origin: center 0.06em;
+ -moz-transform-origin: center 0.06em;
+ -ms-transform-origin: center 0.06em;
+ -o-transform-origin: center 0.06em;
+ transform-origin: center 0.06em;
+}
+.mq-math-mode .mq-paren.mq-ghost {
+ color: silver;
+}
+.mq-math-mode .mq-paren + span {
+ margin-top: 0.1em;
+ margin-bottom: 0.1em;
+}
+.mq-math-mode .mq-array {
+ vertical-align: middle;
+ text-align: center;
+}
+.mq-math-mode .mq-array > span {
+ display: block;
+}
+.mq-math-mode .mq-operator-name {
+ font-family: Symbola, 'Times New Roman', serif;
+ line-height: 0.9;
+ font-style: normal;
+}
+.mq-math-mode var.mq-operator-name.mq-first {
+ padding-left: 0.2em;
+}
+.mq-math-mode var.mq-operator-name.mq-last,
+.mq-math-mode .mq-supsub.mq-after-operator-name {
+ padding-right: 0.2em;
+}
+.mq-math-mode .mq-fraction {
+ font-size: 90%;
+ text-align: center;
+ vertical-align: -0.4em;
+ padding: 0 0.2em;
+}
+.mq-math-mode .mq-fraction,
+.mq-math-mode .mq-large-operator,
+.mq-math-mode x:-moz-any-link {
+ display: -moz-groupbox;
+}
+.mq-math-mode .mq-fraction,
+.mq-math-mode .mq-large-operator,
+.mq-math-mode x:-moz-any-link,
+.mq-math-mode x:default {
+ display: inline-block;
+}
+.mq-math-mode .mq-numerator,
+.mq-math-mode .mq-denominator {
+ display: block;
+}
+.mq-math-mode .mq-numerator {
+ padding: 0 0.1em;
+}
+.mq-math-mode .mq-denominator {
+ border-top: 1px solid;
+ float: right;
+ width: 100%;
+ padding: 0.1em;
+}
+.mq-math-mode .mq-sqrt-prefix {
+ padding-top: 0;
+ position: relative;
+ top: 0.1em;
+ vertical-align: top;
+ -webkit-transform-origin: top;
+ -moz-transform-origin: top;
+ -ms-transform-origin: top;
+ -o-transform-origin: top;
+ transform-origin: top;
+}
+.mq-math-mode .mq-sqrt-stem {
+ border-top: 1px solid;
+ margin-top: 1px;
+ padding-left: 0.15em;
+ padding-right: 0.2em;
+ margin-right: 0.1em;
+ padding-top: 1px;
+}
+.mq-math-mode .mq-vector-prefix {
+ display: block;
+ text-align: center;
+ line-height: 0.25em;
+ margin-bottom: -0.1em;
+ font-size: 0.75em;
+}
+.mq-math-mode .mq-vector-stem {
+ display: block;
+}
+.mq-math-mode .mq-large-operator {
+ vertical-align: -0.2em;
+ padding: 0.2em;
+ text-align: center;
+}
+.mq-math-mode .mq-large-operator .mq-from,
+.mq-math-mode .mq-large-operator big,
+.mq-math-mode .mq-large-operator .mq-to {
+ display: block;
+}
+.mq-math-mode .mq-large-operator .mq-from,
+.mq-math-mode .mq-large-operator .mq-to {
+ font-size: 80%;
+}
+.mq-math-mode .mq-large-operator .mq-from {
+ float: right;
+ /* take out of normal flow to manipulate baseline */
+ width: 100%;
+}
+.mq-math-mode,
+.mq-math-mode .mq-editable-field {
+ cursor: text;
+ font-family: Symbola, 'Times New Roman', serif;
+}
+.mq-math-mode .mq-overarrow {
+ border-top: 1px solid black;
+ margin-top: 1px;
+ padding-top: 0.2em;
+}
+.mq-math-mode .mq-overarrow:before {
+ display: block;
+ position: relative;
+ top: -0.34em;
+ font-size: 0.5em;
+ line-height: 0em;
+ content: '\27A4';
+ text-align: right;
+}
+.mq-math-mode .mq-overarrow.mq-arrow-left:before {
+ -moz-transform: scaleX(-1);
+ -o-transform: scaleX(-1);
+ -webkit-transform: scaleX(-1);
+ transform: scaleX(-1);
+ filter: FlipH;
+ -ms-filter: 'FlipH';
+}
+.mq-math-mode .mq-selection,
+.mq-editable-field .mq-selection,
+.mq-math-mode .mq-selection .mq-non-leaf,
+.mq-editable-field .mq-selection .mq-non-leaf,
+.mq-math-mode .mq-selection .mq-scaled,
+.mq-editable-field .mq-selection .mq-scaled {
+ background: #b4d5fe !important;
+ background: Highlight !important;
+ color: HighlightText;
+ border-color: HighlightText;
+}
+.mq-math-mode .mq-selection .mq-matrixed,
+.mq-editable-field .mq-selection .mq-matrixed {
+ background: #39f !important;
+}
+.mq-math-mode .mq-selection .mq-matrixed-container,
+.mq-editable-field .mq-selection .mq-matrixed-container {
+ filter: progid:DXImageTransform.Microsoft.Chroma(color='#3399FF') !important;
+}
+.mq-math-mode .mq-selection.mq-blur,
+.mq-editable-field .mq-selection.mq-blur,
+.mq-math-mode .mq-selection.mq-blur .mq-non-leaf,
+.mq-editable-field .mq-selection.mq-blur .mq-non-leaf,
+.mq-math-mode .mq-selection.mq-blur .mq-scaled,
+.mq-editable-field .mq-selection.mq-blur .mq-scaled,
+.mq-math-mode .mq-selection.mq-blur .mq-matrixed,
+.mq-editable-field .mq-selection.mq-blur .mq-matrixed {
+ background: #d4d4d4 !important;
+ color: black;
+ border-color: black;
+}
+.mq-math-mode .mq-selection.mq-blur .mq-matrixed-container,
+.mq-editable-field .mq-selection.mq-blur .mq-matrixed-container {
+ filter: progid:DXImageTransform.Microsoft.Chroma(color='#D4D4D4') !important;
+}
+.mq-editable-field .mq-textarea,
+.mq-math-mode .mq-textarea {
+ position: relative;
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ user-select: text;
+}
+.mq-editable-field .mq-textarea *,
+.mq-math-mode .mq-textarea *,
+.mq-editable-field .mq-selectable,
+.mq-math-mode .mq-selectable {
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ user-select: text;
+ position: absolute;
+ clip: rect(1em 1em 1em 1em);
+ -webkit-transform: scale(0);
+ -moz-transform: scale(0);
+ -ms-transform: scale(0);
+ -o-transform: scale(0);
+ transform: scale(0);
+ resize: none;
+ width: 1px;
+ height: 1px;
+}
+.mq-math-mode .mq-matrixed {
+ background: white;
+ display: -moz-inline-box;
+ display: inline-block;
+}
+.mq-math-mode .mq-matrixed-container {
+ filter: progid:DXImageTransform.Microsoft.Chroma(color='white');
+ margin-top: -0.1em;
+}
diff --git a/src/client/views/nodes/formattedText/EquationEditor.tsx b/src/client/views/nodes/formattedText/EquationEditor.tsx
new file mode 100644
index 000000000..b4102e08e
--- /dev/null
+++ b/src/client/views/nodes/formattedText/EquationEditor.tsx
@@ -0,0 +1,87 @@
+import React, { Component, createRef } from 'react';
+
+// Import JQuery, required for the functioning of the equation editor
+import $ from 'jquery';
+
+import './EquationEditor.scss';
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
+// @ts-ignore
+window.jQuery = $;
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
+// @ts-ignore
+require('mathquill/build/mathquill');
+
+(window as any).MathQuill = (window as any).MathQuill.getInterface(1);
+
+type EquationEditorProps = {
+ onChange(latex: string): void;
+ value: string;
+ spaceBehavesLikeTab?: boolean;
+ autoCommands: string;
+ autoOperatorNames: string;
+ onEnter?(): void;
+};
+
+/**
+ * @typedef {EquationEditorProps} props
+ * @prop {Function} onChange Triggered when content of the equation editor changes
+ * @prop {string} value Content of the equation handler
+ * @prop {boolean}[false] spaceBehavesLikeTab Whether spacebar should simulate tab behavior
+ * @prop {string} autoCommands List of commands for which you only have to type the name of the
+ * command with a \ in front of it. Examples: pi theta rho sum
+ * @prop {string} autoOperatorNames List of operators for which you only have to type the name of the
+ * operator with a \ in front of it. Examples: sin cos tan
+ * @prop {Function} onEnter Triggered when enter is pressed in the equation editor
+ * @extends {Component<EquationEditorProps>}
+ */
+class EquationEditor extends Component<EquationEditorProps> {
+ element: any;
+ mathField: any;
+ ignoreEditEvents: number;
+
+ // Element needs to be in the class format and thus requires a constructor. The steps that are run
+ // in the constructor is to make sure that React can succesfully communicate with the equation
+ // editor.
+ constructor(props: any) {
+ super(props);
+
+ this.element = createRef();
+ this.mathField = null;
+
+ // MathJax apparently fire 2 edit events on startup.
+ this.ignoreEditEvents = 2;
+ }
+
+ componentDidMount() {
+ const { onChange, value, spaceBehavesLikeTab, autoCommands, autoOperatorNames, onEnter } = this.props;
+
+ const config = {
+ handlers: {
+ edit: () => {
+ if (this.ignoreEditEvents > 0) {
+ this.ignoreEditEvents -= 1;
+ return;
+ }
+ if (this.mathField.latex() !== value) {
+ onChange(this.mathField.latex());
+ }
+ },
+ enter: onEnter,
+ },
+ spaceBehavesLikeTab,
+ autoCommands,
+ autoOperatorNames,
+ };
+
+ this.mathField = (window as any).MathQuill.MathField(this.element.current, config);
+ this.mathField.latex(value || '');
+ }
+
+ render() {
+ return <span ref={this.element} style={{ border: '0px', boxShadow: 'None' }} />;
+ }
+}
+
+export default EquationEditor;
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 5e62d94c2..331ed1980 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -1,4 +1,4 @@
-import EquationEditor from 'equation-editor-react';
+import EquationEditor from './EquationEditor';
import { IReactionDisposer, trace } from 'mobx';
import { observer } from 'mobx-react';
import { TextSelection } from 'prosemirror-state';
@@ -7,7 +7,8 @@ import { Doc } from '../../../../fields/Doc';
import { StrCast } from '../../../../fields/Types';
import './DashFieldView.scss';
import { FormattedTextBox } from './FormattedTextBox';
-import React = require('react');
+import * as React from 'react';
+import { AnyArray } from 'mongoose';
export class EquationView {
dom: HTMLDivElement; // container for label and value
@@ -63,7 +64,7 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal>
_fieldKey: string;
_ref: React.RefObject<EquationEditor> = React.createRef();
- constructor(props: IEquationViewInternal) {
+ constructor(props: any) {
super(props);
this._fieldKey = this.props.fieldKey;
this._textBoxDoc = this.props.tbox.props.Document;
@@ -101,7 +102,7 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal>
<EquationEditor
ref={this._ref}
value={StrCast(this._textBoxDoc[this._fieldKey], 'y=')}
- onChange={str => (this._textBoxDoc[this._fieldKey] = str)}
+ onChange={(str: any) => (this._textBoxDoc[this._fieldKey] = str)}
autoCommands="pi theta sqrt sum prod alpha beta gamma rho"
autoOperatorNames="sin cos tan"
spaceBehavesLikeTab={true}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 717efd5a9..03ff0436b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables';
+@import '../../global/globalCssVariables.module.scss';
.ProseMirror {
width: 100%;
@@ -592,7 +592,7 @@ footnote::before {
}
@media only screen and (max-width: 1000px) {
- @import '../../global/globalCssVariables';
+ @import '../../global/globalCssVariables.module.scss';
.ProseMirror {
width: 100%;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 31c0545d4..ad2fab8b0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,9 +1,8 @@
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 { Tooltip } from '@mui/material';
import { isEqual } from 'lodash';
-import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { baseKeymap, selectAll } from 'prosemirror-commands';
import { history } from 'prosemirror-history';
@@ -56,7 +55,7 @@ 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';
+import { LinkInfo } from '../LinkDocPreview';
import { PinProps, PresBox } from '../trails';
import { DashDocCommentView } from './DashDocCommentView';
import { DashDocView } from './DashDocView';
@@ -71,10 +70,8 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from './RichTextRules';
import { schema } from './schema_rts';
import { SummaryView } from './SummaryView';
-import applyDevTools = require('prosemirror-dev-tools');
-import React = require('react');
-// setting up cors-anywhere server address
-const translate = setCORS('http://cors-anywhere.herokuapp.com/');
+// import * as applyDevTools from 'prosemirror-dev-tools';
+import * as React from 'react';
export const GoogleRef = 'googleDocId';
type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@@ -132,7 +129,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
@computed get noSidebar() {
- return this.props.docViewPath().lastElement()?.props.hideDecorationTitle || this.props.noSidebar || this.Document._layout_noSidebar;
+ return this._props.docViewPath().lastElement()?._props.hideDecorationTitle || this._props.noSidebar || this.Document._layout_noSidebar;
}
@computed get layout_sidebarWidthPercent() {
return this._showSidebar ? '20%' : StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
@@ -141,19 +138,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.fieldKey + '_backgroundColor'], '#e4e4e4'));
}
@computed get layout_autoHeight() {
- return (this.props.forceAutoHeight || this.layoutDoc._layout_autoHeight) && !this.props.ignoreAutoHeight;
+ return (this._props.forceAutoHeight || this.layoutDoc._layout_autoHeight) && !this._props.ignoreAutoHeight;
}
@computed get textHeight() {
- return NumCast(this.rootDoc[this.fieldKey + '_height']);
+ return NumCast(this.dataDoc[this.fieldKey + '_height']);
}
@computed get scrollHeight() {
- return NumCast(this.rootDoc[this.fieldKey + '_scrollHeight']);
+ return NumCast(this.dataDoc[this.fieldKey + '_scrollHeight']);
}
@computed get sidebarHeight() {
- return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + '_height']);
+ return !this.sidebarWidth() ? 0 : NumCast(this.dataDoc[this.SidebarKey + '_height']);
}
@computed get titleHeight() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.HeaderMargin) || 0;
}
@computed get layout_autoHeightMargins() {
return this.titleHeight + NumCast(this.layoutDoc._layout_autoHeightMargins);
@@ -165,8 +162,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
!this.dataDoc[`${this.fieldKey}_recordingSource`] && (this.dataDoc.mediaState = value ? media_state.Recording : undefined);
}
@computed get config() {
- this._keymap = buildKeymap(schema, this.props);
- this._rules = new RichTextRules(this.rootDoc, this);
+ this._keymap = buildKeymap(schema, this._props);
+ this._rules = new RichTextRules(this.Document, this);
return {
schema,
plugins: [
@@ -190,7 +187,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
private gptRes: string = '';
public static PasteOnLoad: ClipboardEvent | undefined;
- public static SelectOnLoad = '';
+ private static SelectOnLoad: Doc | undefined;
+ public static SetSelectOnLoad(doc: Doc) {
+ FormattedTextBox.SelectOnLoad = doc;
+ }
public static DontSelectInitialText = false; // whether initial text should be selected or not
public static SelectOnLoadChar = '';
public static IsFragment(html: string) {
@@ -210,6 +210,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
constructor(props: any) {
super(props);
+ makeObservable(this);
FormattedTextBox.Instance = this;
this._recordingStart = Date.now();
}
@@ -310,15 +311,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
e.preventDefault();
e.stopPropagation();
const targetCreator = (annotationOn?: Doc) => {
- const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn);
- FormattedTextBox.SelectOnLoad = target[Id];
+ const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn);
+ FormattedTextBox.SetSelectOnLoad(target);
return target;
};
- DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), () => this.getAnchor(true), targetCreator), e.pageX, e.pageY);
+ 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.rootSelected() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
+ this._props.rootSelected?.() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
let ele: Opt<HTMLDivElement> = undefined;
try {
const contents = window.getSelection()?.getRangeAt(0).cloneContents();
@@ -339,7 +340,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const newText = state.doc.textBetween(0, state.doc.content.size, ' \n');
const newJson = JSON.stringify(state.toJSON());
const prevData = Cast(this.layoutDoc[this.fieldKey], RichTextField, null); // the actual text in the text box
- const templateData = this.rootDoc !== this.layoutDoc ? prevData : undefined; // the default text stored in a layout template
+ const templateData = this.Document !== this.layoutDoc ? prevData : undefined; // the default text stored in a layout template
const protoData = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
const effectiveAcl = GetEffectiveAcl(dataDoc);
@@ -361,11 +362,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
if ((!prevData && !protoData) || newText || (!newText && !templateData)) {
// if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
- if ((this._finishingLink || this.props.isContentActive()) && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
+ if ((this._finishingLink || this._props.isContentActive()) && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
const numstring = NumCast(dataDoc[this.fieldKey], null);
dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : new RichTextField(newJson, newText);
dataDoc[this.fieldKey + '_noTemplate'] = true; // mark the data field as being split from the template if it has been edited
- textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText });
+ textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.Document, text: newText });
unchanged = false;
}
} else {
@@ -373,7 +374,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
dataDoc[this.fieldKey] = undefined;
this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((protoData || prevData).Data)));
dataDoc[this.fieldKey + '_noTemplate'] = undefined; // mark the data field as not being split from any template it might have
- ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText });
+ ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, text: newText });
unchanged = false;
}
this._applyingChange = '';
@@ -390,7 +391,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this._editorView.updateState(EditorState.fromJSON(this.config, json));
}
}
- if (window.getSelection()?.isCollapsed && this.props.rootSelected()) {
+ if (window.getSelection()?.isCollapsed && this._props.rootSelected?.()) {
AnchorMenu.Instance.fadeOut(true);
}
}
@@ -438,7 +439,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
autoLink = () => {
const newAutoLinks = new Set<Doc>();
- const oldAutoLinks = LinkManager.Links(this.props.Document).filter(link => link.link_relationship === LinkManager.AutoKeywords);
+ const oldAutoLinks = LinkManager.Links(this._props.Document).filter(link => link.link_relationship === LinkManager.AutoKeywords);
if (this._editorView?.state.doc.textContent) {
const isNodeSel = this._editorView.state.selection instanceof NodeSelection;
const f = this._editorView.state.selection.from;
@@ -450,17 +451,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
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);
}
- oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
+ oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(LinkManager.Instance.deleteLink);
};
updateTitle = () => {
const title = StrCast(this.dataDoc.title, Cast(this.dataDoc.title, RichTextField, null)?.Text);
if (
- !this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
+ !this._props.dontRegisterView && // (this._props.Document.isTemplateForField === "text" || !this._props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
(title.startsWith('-') || title.startsWith('@')) &&
this._editorView &&
!this.dataDoc.title_custom &&
- (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === 'text')
+ (Doc.LayoutFieldKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
) {
let node = this._editorView.state.doc;
while (node.firstChild && node.firstChild.type.name !== 'text') node = node.firstChild;
@@ -471,7 +472,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.AddToMyPublished(this.rootDoc);
+ Doc.AddToMyPublished(this.Document);
}
}
}
@@ -480,7 +481,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
// creates links between terms in a document and published documents (myPublishedDocs) that have titles starting with an '@'
hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set<Doc>) => {
const editorView = this._editorView;
- if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) {
+ if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.Document)) {
const autoLinkTerm = StrCast(target.title).replace(/^@/, '');
var alink: Doc | undefined;
this.findInNode(editorView, editorView.state.doc, autoLinkTerm).forEach(sel => {
@@ -491,15 +492,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
alink =
alink ??
- (LinkManager.Links(this.rootDoc).find(
+ (LinkManager.Links(this.Document).find(
link =>
- Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && //
+ Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.Document) && //
Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)
) ||
- DocUtils.MakeLink(this.rootDoc, target, { link_relationship: LinkManager.AutoKeywords })!);
+ DocUtils.MakeLink(this.Document, 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] }];
+ 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' });
tr = tr.addMark(pos, pos + node.nodeSize, link);
@@ -568,7 +569,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
@undoBatch
- @action
drop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.annoDragData) {
de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true);
@@ -584,7 +584,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
// replace text contents when dragging with Alt
if (de.altKey) {
const fieldKey = Doc.LayoutFieldKey(draggedDoc);
- if (draggedDoc[fieldKey] instanceof RichTextField && !Doc.AreProtosEqual(draggedDoc, this.props.Document)) {
+ if (draggedDoc[fieldKey] instanceof RichTextField && !Doc.AreProtosEqual(draggedDoc, this._props.Document)) {
Doc.GetProto(this.dataDoc)[this.fieldKey] = Field.Copy(draggedDoc[fieldKey]);
}
@@ -602,7 +602,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
if (added) {
draggedDoc._freeform_fitContentsToBox = true;
- Doc.SetContainer(draggedDoc, this.rootDoc);
+ Doc.SetContainer(draggedDoc, this.Document);
const view = this._editorView!;
try {
const pos = view.posAtCoords({ left: de.x, top: de.y })?.pos;
@@ -742,9 +742,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
);
};
sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
- const localDelta = this.props
+ const localDelta = this._props
.ScreenToLocalTransform()
- .scale(this.props.NativeDimScaling?.() || 1)
+ .scale(this._props.NativeDimScaling?.() || 1)
.transformDirection(delta[0], delta[1]);
const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100;
const width = NumCast(this.layoutDoc._width) + localDelta[0];
@@ -758,15 +758,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
deleteAnnotation = (anchor: Doc) => {
const batch = UndoManager.StartBatch('delete link');
LinkManager.Instance.deleteLink(LinkManager.Links(anchor)[0]);
- // const docAnnotations = DocListCast(this.props.dataDoc[this.fieldKey]);
- // this.props.dataDoc[this.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion));
+ // const docAnnotations = DocListCast(this._props.dataDoc[this.fieldKey]);
+ // this._props.dataDoc[this.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion));
// AnchorMenu.Instance.fadeOut(true);
- this.props.select(false);
+ this._props.select(false);
setTimeout(batch.end); // wait for reaction to remove link from document
};
@undoBatch
- pinToPres = (anchor: Doc) => this.props.pinToPres(anchor, {});
+ pinToPres = (anchor: Doc) => this._props.pinToPres(anchor, {});
@undoBatch
makeTargetToggle = (anchor: Doc) => (anchor.followLinkToggle = !anchor.followLinkToggle);
@@ -776,7 +776,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const trail = DocCast(anchor.presentationTrail);
if (trail) {
Doc.ActivePresentation = trail;
- this.props.addDocTab(trail, OpenWhere.replaceRight);
+ this._props.addDocTab(trail, OpenWhere.replaceRight);
}
};
@@ -794,7 +794,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
?.trim()
.split(' ')
.filter(h => h);
- const anchorDoc = Array.from(hrefs).lastElement().replace(Doc.localServerPath(), '').split('?')[0];
+ const anchorDoc = Array.from(hrefs ?? [])
+ .lastElement()
+ .replace(Doc.localServerPath(), '')
+ .split('?')[0];
const deleteMarkups = undoBatch(() => {
const sel = editor.state.selection;
editor.dispatch(editor.state.tr.removeMark(sel.from, sel.to, editor.state.schema.marks.linkAnchor));
@@ -823,7 +826,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
changeItems.push({
description: 'plain',
event: undoBatch(() => {
- Doc.setNativeView(this.rootDoc);
+ Doc.setNativeView(this.Document);
this.layoutDoc.layout_autoHeightMargins = undefined;
}),
icon: 'eye',
@@ -832,8 +835,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
description: 'metadata',
event: undoBatch(() => {
this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout;
- this.rootDoc.layout_fieldKey = 'layout_meta';
- setTimeout(() => (this.rootDoc._headerHeight = this.rootDoc._layout_autoHeightMargins = 50), 50);
+ this.Document.layout_fieldKey = 'layout_meta';
+ setTimeout(() => (this.layoutDoc._headerHeight = this.layoutDoc._layout_autoHeightMargins = 50), 50);
}),
icon: 'eye',
});
@@ -844,8 +847,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
description: StrCast(note.title),
event: undoBatch(() => {
this.layoutDoc.layout_autoHeightMargins = undefined;
- Doc.setNativeView(this.rootDoc);
- DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
+ Doc.setNativeView(this.Document);
+ DocUtils.makeCustomViewClicked(this.Document, Docs.Create.TreeDocument, StrCast(note.title), note);
}),
icon: icon,
});
@@ -884,40 +887,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
!Doc.noviceMode &&
appearanceItems.push({
description: 'Broadcast Message',
- event: () => DocServer.GetRefField('rtfProto').then(proto => proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)),
+ event: () => DocServer.GetRefField('rtfProto').then(proto => proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text)),
icon: 'expand-arrows-alt',
});
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" });
!Doc.noviceMode &&
appearanceItems.push({
description: 'Make Default Layout',
event: () => {
if (!this.layoutDoc.isTemplateDoc) {
- const title = StrCast(this.rootDoc.title);
- this.rootDoc.title = 'text';
- MakeTemplate(this.rootDoc, true, title);
- } else if (!this.rootDoc.isTemplateDoc) {
- const title = StrCast(this.rootDoc.title);
- this.rootDoc.title = 'text';
- this.rootDoc.layout = this.layoutDoc.layout as string;
- this.rootDoc.title = this.layoutDoc.isTemplateForField as string;
- this.rootDoc.isTemplateDoc = false;
- this.rootDoc.isTemplateForField = '';
- this.rootDoc.layout_fieldKey = 'layout';
- MakeTemplate(this.rootDoc, true, title);
- setTimeout(() => {
- this.rootDoc._layout_autoHeight = this.layoutDoc._layout_autoHeight; // layout_autoHeight, width and height
- this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template
- this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root. This should probably apply to all '_' fields
- this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, 'string', null);
- this.rootDoc.backgroundColor = Cast(this.layoutDoc.backgroundColor, 'string', null);
- }, 10);
+ const title = StrCast(this.Document.title);
+ this.Document.title = 'text';
+ MakeTemplate(this.Document, true, title);
}
- Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
- Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', this.rootDoc);
+ Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.Document);
+ Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', this.Document);
},
icon: 'eye',
});
@@ -927,7 +913,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const optionItems = options && 'subitems' in options ? options.subitems : [];
optionItems.push({ description: `Generate Dall-E Image`, event: () => this.generateImage(), icon: 'star' });
optionItems.push({ description: `Ask GPT-3`, event: () => this.askGPT(), icon: 'lightbulb' });
- this.props.renderDepth &&
+ this._props.renderDepth &&
optionItems.push({
description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns',
event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR),
@@ -971,8 +957,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
generateImage = async () => {
GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
- GPTPopup.Instance?.setImgTargetDoc(this.rootDoc);
- GPTPopup.Instance.addToCollection = this.props.addDocument;
+ GPTPopup.Instance?.setImgTargetDoc(this.Document);
+ GPTPopup.Instance.addToCollection = this._props.addDocument;
GPTPopup.Instance.setImgDesc((this.dataDoc.text as RichTextField)?.Text);
GPTPopup.Instance.generateImage();
};
@@ -1071,13 +1057,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
anchor.presentation_zoomText = true;
return anchor;
}
- return anchorDoc ?? this.rootDoc;
+ return anchorDoc ?? this.Document;
}
- return anchorDoc ?? this.rootDoc;
+ return anchorDoc ?? this.Document;
}
getView = async (doc: Doc) => {
- if (DocListCast(this.rootDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
+ if (DocListCast(this.dataDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
!this.SidebarShown && this.toggleSidebar(false);
setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc));
}
@@ -1141,7 +1127,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
setTimeout(() => clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000));
return focusSpeed;
} else {
- return this.props.focus(this.rootDoc, options);
+ return this._props.focus(this.Document, options);
}
}
};
@@ -1150,15 +1136,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
// Since we also monitor all component height changes, this will update the document's height.
resetNativeHeight = (scrollHeight: number) => {
const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight);
- this.rootDoc[this.fieldKey + '_height'] = scrollHeight;
+ this.dataDoc[this.fieldKey + '_height'] = scrollHeight;
if (nh) this.layoutDoc._nativeHeight = scrollHeight;
};
@computed get contentScaling() {
- return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.NativeDimScaling?.() || 1 : 1;
+ return Doc.NativeAspect(this.Document, this.dataDoc, false) ? this._props.NativeDimScaling?.() || 1 : 1;
}
componentDidMount() {
- !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+ !this._props.dontSelectOnLoad && this._props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
this._cachedLinks = LinkManager.Links(this.Document);
this._disposers.breakupDictation = reaction(() => Doc.RecordingEvent, this.breakupDictation);
this._disposers.layout_autoHeight = reaction(
@@ -1171,7 +1157,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
{ fireImmediately: true }
);
this._disposers.width = reaction(
- () => this.props.PanelWidth(),
+ () => this._props.PanelWidth(),
width => this.tryUpdateScrollHeight()
);
this._disposers.scrollHeight = reaction(
@@ -1185,13 +1171,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
({ sidebarHeight, textHeight, layout_autoHeight, marginsHeight }) => {
const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight));
if (
- (!Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') || this.props.isSelected()) && //
+ (!Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') || this._props.isSelected()) && //
layout_autoHeight &&
newHeight &&
- newHeight !== this.rootDoc.height &&
- !this.props.dontRegisterView
+ newHeight !== this.layoutDoc.height &&
+ !this._props.dontRegisterView
) {
- this.props.setHeight?.(newHeight);
+ this._props.setHeight?.(newHeight);
}
},
{ fireImmediately: !Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') }
@@ -1233,7 +1219,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
);
this._disposers.pullDoc = reaction(
- () => this.props.Document[Pulls],
+ () => this._props.Document[Pulls],
() => {
if (!DocumentButtonBar.hasPulledHack) {
DocumentButtonBar.hasPulledHack = true;
@@ -1242,7 +1228,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
);
this._disposers.pushDoc = reaction(
- () => this.props.Document[Pushes],
+ () => this._props.Document[Pushes],
() => {
if (!DocumentButtonBar.hasPushedHack) {
DocumentButtonBar.hasPushedHack = true;
@@ -1252,13 +1238,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
);
this._disposers.search = reaction(
- () => Doc.IsSearchMatch(this.rootDoc),
+ () => Doc.IsSearchMatch(this.Document),
search => (search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms()),
- { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false }
+ { fireImmediately: Doc.IsSearchMatchUnmemoized(this.Document) ? true : false }
);
this._disposers.selected = reaction(
- () => this.props.rootSelected(),
+ () => this._props.rootSelected?.(),
action(selected => {
//selected && setTimeout(() => this.prepareForTyping());
if (FormattedTextBox._globalHighlights.has('Bold Text')) {
@@ -1268,14 +1254,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
}
if (this._editorView && selected) {
- RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props, this.layoutDoc);
+ RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this._props, this.layoutDoc);
setTimeout(this.autoLink, 20);
}
}),
{ fireImmediately: true }
);
- if (!this.props.dontRegisterView) {
+ if (!this._props.dontRegisterView) {
this._disposers.record = reaction(
() => this._recordingDictation,
() => {
@@ -1290,7 +1276,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this._disposers.scroll = reaction(
() => NumCast(this.layoutDoc._layout_scrollTop),
pos => {
- if (!this._ignoreScroll && this._scrollRef.current && !this.props.dontSelectOnLoad) {
+ if (!this._ignoreScroll && this._scrollRef.current && !this._props.dontSelectOnLoad) {
const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition);
const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
const durationSecStr = viewTrans.match(/([0-9.]*)s/);
@@ -1322,7 +1308,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (this._editorView && reference) {
const content = await RichTextUtils.GoogleDocs.Export(this._editorView.state);
const response = await GoogleApiClientUtils.Docs.write({ reference, content, mode });
- response && (this.dataDoc[GoogleRef] = response.documentId);
+ response?.documentId && (this.dataDoc[GoogleRef] = response.documentId);
const pushSuccess = response !== undefined && !('errors' in response);
dataDoc.googleDocUnchanged = pushSuccess;
DocumentButtonBar.Instance.startPushOutcome(pushSuccess);
@@ -1420,7 +1406,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const dashField = view.state.schema.nodes.paragraph.create({}, [
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]}` }],
+ allAnchors: [{ href: `/doc/${this.Document[Id]}`, title: this.Document.title, anchorId: `${this.Document[Id]}` }],
title: `from: ${DocCast(pdfAnchor.embedContainer).title}`,
noPreview: true,
docref: false,
@@ -1430,7 +1416,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
]),
]);
- const link = DocUtils.MakeLink(pdfAnchor, this.rootDoc, { link_relationship: 'PDF pasted' });
+ const link = DocUtils.MakeLink(pdfAnchor, this.Document, { link_relationship: 'PDF pasted' });
if (link) {
view.dispatch(view.state.tr.replaceSelectionWith(dashField, false).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste'));
}
@@ -1453,8 +1439,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const self = this;
return new Plugin({
view(newView) {
- runInAction(() => self.props.rootSelected() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView));
- return new RichTextMenuPlugin({ editorProps: this.props });
+ runInAction(() => self._props.rootSelected?.() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView));
+ return new RichTextMenuPlugin({ editorProps: this._props });
},
});
}
@@ -1476,7 +1462,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const botOff = docPos.bottom > viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined;
if (((topOff && Math.abs(Math.trunc(topOff)) > 0) || (botOff && Math.abs(Math.trunc(botOff)) > 0)) && scrollRef) {
const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE);
- const scrollPos = scrollRef.scrollTop + shift * self.props.ScreenToLocalTransform().Scale;
+ const scrollPos = scrollRef.scrollTop + shift * self._props.ScreenToLocalTransform().Scale;
if (this._focusSpeed !== undefined) {
scrollPos && (this._scrollStopper = smoothScroll(this._focusSpeed, scrollRef, scrollPos, 'ease', this._scrollStopper));
} else {
@@ -1528,11 +1514,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
(this._editorView as any).TextView = this;
}
- const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()));
- if (this._editorView && selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
+ const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.Document, FormattedTextBox.SelectOnLoad) && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath()));
+ if (this._editorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
const selLoadChar = FormattedTextBox.SelectOnLoadChar;
- FormattedTextBox.SelectOnLoad = '';
- this.props.select(false);
+ FormattedTextBox.SelectOnLoad = undefined;
+ this._props.select(false);
if (selLoadChar) {
const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
@@ -1548,7 +1534,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
}
selectOnLoad && this._editorView!.focus();
- if (this.props.isContentActive()) this.prepareForTyping();
+ if (this._props.isContentActive()) this.prepareForTyping();
if (this._editorView) {
const tr = this._editorView.state.tr;
const { from, to } = tr.selection;
@@ -1571,15 +1557,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
- ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
- ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
+ ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontFamily) })] : []),
+ ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize) })] : []),
...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
...[schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })],
];
this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks));
};
- @action
componentWillUnmount() {
if (this._recordingDictation) {
this._recordingDictation = !this._recordingDictation;
@@ -1612,7 +1597,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const func = () => {
const docView = DocumentManager.Instance.getDocumentView(audiodoc);
if (!docView) {
- this.props.addDocTab(audiodoc, OpenWhere.addBottom);
+ this._props.addDocTab(audiodoc, OpenWhere.addBottom);
setTimeout(func);
} else docView.ComponentView?.playFrom?.(timecode, Cast(anchor.timecodeToHide, 'number', null)); // bcz: would be nice to find the next audio tag in the doc and play until that
};
@@ -1628,7 +1613,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this._downTime = Date.now();
this._hadDownFocus = this.ProseRef?.children[0].className.includes('focused') ?? false;
FormattedTextBoxComment.textBox = this;
- if (e.button === 0 && (this.props.rootSelected() || this.props.rootSelected()) && !e.altKey && !e.ctrlKey && !e.metaKey) {
+ if (e.button === 0 && this._props.rootSelected?.() && !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
@@ -1650,7 +1635,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
if (!state || !editor || !this.ProseRef?.children[0].className.includes('-focused')) return;
if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
- else if (this.props.isContentActive(true)) {
+ else if (this._props.isContentActive(true)) {
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
let xpos = pcords?.pos || 0;
while (xpos > 0 && !state.doc.resolve(xpos).node()?.isTextblock) {
@@ -1665,7 +1650,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@action
onDoubleClick = (e: React.MouseEvent): void => {
FormattedTextBoxComment.textBox = this;
- if (e.button === 0 && this.props.rootSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
+ if (e.button === 0 && this._props.rootSelected?.() && !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
@@ -1676,7 +1661,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
FormattedTextBoxComment.Hide();
- if (e.buttons === 1 && this.props.rootSelected() && !e.altKey) {
+ if (e.buttons === 1 && this._props.rootSelected?.() && !e.altKey) {
e.stopPropagation();
}
};
@@ -1687,14 +1672,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
@action
onFocused = (e: React.FocusEvent): void => {
- console.log('FOCUSED = ' + this.layoutDoc.title + ' ' + this.props.rootSelected());
//applyDevTools.applyDevTools(this._editorView);
- this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props, this.layoutDoc);
+ this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this._props, this.layoutDoc);
e.stopPropagation();
};
onClick = (e: React.MouseEvent): void => {
- if (!this.props.isContentActive()) return;
+ if (!this._props.isContentActive()) return;
if ((e.nativeEvent as any).handledByInnerReactInstance) {
e.stopPropagation();
return;
@@ -1723,7 +1707,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.rootSelected()) {
+ if (this._props.rootSelected?.()) {
// 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);
@@ -1737,7 +1721,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.rootSelected()) {
+ if (clickPos && olistPos && this._props.rootSelected?.()) {
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;
@@ -1765,7 +1749,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
}
startUndoTypingBatch() {
- !this._undoTyping && (this._undoTyping = UndoManager.StartBatch('text edits on ' + this.rootDoc.title));
+ !this._undoTyping && (this._undoTyping = UndoManager.StartBatch('text edits on ' + this.Document.title));
}
public endUndoTypingBatch() {
this._undoTyping?.end();
@@ -1774,7 +1758,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@action
onBlur = (e: any) => {
- console.log('BLUREd = ' + this.layoutDoc.title + ' ' + this.props.rootSelected());
if (this.ProseRef?.children[0] !== e.nativeEvent.target) return;
if (!(this.EditorView?.state.selection instanceof NodeSelection) || this.EditorView.state.selection.node.type !== this.EditorView.state.schema.nodes.footnote) {
const stordMarks = this._editorView?.state.storedMarks?.slice();
@@ -1787,7 +1770,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
tr && this._editorView.dispatch(tr);
}
}
- if (RichTextMenu.Instance?.view === this._editorView && !this.props.rootSelected()) {
+ if (RichTextMenu.Instance?.view === this._editorView && !this._props.rootSelected?.()) {
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
}
FormattedTextBox._hadSelection = window.getSelection()?.toString() !== '';
@@ -1798,29 +1781,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const state = this._editorView!.state;
const curText = state.doc.textBetween(0, state.doc.content.size, ' \n');
- if (this.layoutDoc[this.SidebarKey + '_type_collection'] === 'translation' && !this.fieldKey.includes('translation') && curText.endsWith(' ') && curText !== this._lastText) {
- try {
- translate(curText, { from: 'en', to: 'es' }).then((result1: any) => {
- setTimeout(
- () =>
- translate(result1.text, { from: 'es', to: 'en' }).then((result: any) => {
- const tb = this._sidebarTagRef.current as FormattedTextBox;
- tb._editorView?.dispatch(tb._editorView!.state.tr.insertText(result1.text + '\r\n\r\n' + result.text));
- }),
- 1000
- );
- });
- } catch (e: any) {
- console.log(e.message);
- }
- this._lastText = curText;
- }
- if (StrCast(this.rootDoc.title).startsWith('@') && !this.dataDoc.title_custom) {
+ if (StrCast(this.Document.title).startsWith('@') && !this.dataDoc.title_custom) {
UndoManager.RunInBatch(() => {
this.dataDoc.title_custom = true;
this.dataDoc.layout_showTitle = 'title';
const tr = this._editorView!.state.tr;
- this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.rootDoc.title).length + 2))).deleteSelection());
+ this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.Document.title).length + 2))).deleteSelection());
}, 'titler');
}
};
@@ -1829,7 +1795,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if ((e.altKey || e.ctrlKey) && e.key === 't') {
e.preventDefault();
e.stopPropagation();
- this.props.setTitleFocus?.();
+ this._props.setTitleFocus?.();
return;
}
const state = this._editorView!.state;
@@ -1846,7 +1812,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
let stopPropagation = true;
for (var i = state.selection.from; i <= state.selection.to; i++) {
const node = state.doc.resolve(i);
- if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
+ if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.Document))) {
e.preventDefault();
}
}
@@ -1869,7 +1835,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
case ' ':
if (e.code !== 'Space') {
- [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
+ [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.Document)) &&
this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
}
break;
@@ -1882,8 +1848,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
e.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash.
};
onScroll = (e: React.UIEvent) => {
- if (!LinkDocPreview.LinkInfo && this._scrollRef.current) {
- if (!this.props.dontSelectOnLoad) {
+ if (!LinkInfo.Instance?.LinkInfo && this._scrollRef.current) {
+ if (!this._props.dontSelectOnLoad) {
this._ignoreScroll = true;
this.layoutDoc._layout_scrollTop = this._scrollRef.current.scrollTop;
this._ignoreScroll = false;
@@ -1893,9 +1859,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
};
tryUpdateScrollHeight = () => {
- const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
+ const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yPadding || 0);
const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
- if (children && !SnappingManager.GetIsDragging()) {
+ if (children && !SnappingManager.IsDragging) {
const toNum = (val: string) => Number(val.replace('px', '').replace('auto', '0'));
const toHgt = (node: Element) => {
const { height, marginTop, marginBottom } = getComputedStyle(node);
@@ -1903,11 +1869,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + toHgt(child), margins);
const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.layout_maxAutoHeight, proseHeight), proseHeight);
- if (this.props.setHeight && scrollHeight && !this.props.dontRegisterView) {
+ if (this._props.setHeight && scrollHeight && !this._props.dontRegisterView) {
// if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
- const setScrollHeight = () => (this.rootDoc[this.fieldKey + '_scrollHeight'] = scrollHeight);
+ const setScrollHeight = () => (this.dataDoc[this.fieldKey + '_scrollHeight'] = scrollHeight);
- if (this.rootDoc === this.layoutDoc || this.layoutDoc.resolvedDataDoc) {
+ if (this.Document === this.layoutDoc || this.layoutDoc.resolvedDataDoc) {
setScrollHeight();
} else {
setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived...
@@ -1915,21 +1881,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
}
};
- fitContentsToBox = () => BoolCast(this.props.Document._freeform_fitContentsToBox);
- sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
+ fitContentsToBox = () => BoolCast(this._props.Document._freeform_fitContentsToBox);
+ sidebarContentScaling = () => (this._props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => {
if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
return this.addDocument(doc, sidebarKey);
};
sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey);
sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.SidebarKey);
- setSidebarHeight = (height: number) => (this.rootDoc[this.SidebarKey + '_height'] = height);
- sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+ setSidebarHeight = (height: number) => (this.dataDoc[this.SidebarKey + '_height'] = height);
+ sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this._props.PanelWidth();
sidebarScreenToLocal = () =>
- this.props
+ this._props
.ScreenToLocalTransform()
- .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.NativeDimScaling?.() || 1), 0)
- .scale(1 / NumCast(this.layoutDoc._freeform_scale, 1) / (this.props.NativeDimScaling?.() || 1));
+ .translate(-(this._props.PanelWidth() - this.sidebarWidth()) / (this._props.NativeDimScaling?.() || 1), 0)
+ .scale(1 / NumCast(this.layoutDoc._freeform_scale, 1) / (this._props.NativeDimScaling?.() || 1));
@computed get audioHandle() {
return !this._recordingDictation ? null : (
@@ -1952,9 +1918,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
TraceMobx();
const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
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' : ''));
+ const backgroundColor = !annotated ? (this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK) : this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.WidgetColor + (annotated ? ':annotated' : ''));
- return !annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? null : (
+ return !annotated && (!this._props.isContentActive() || SnappingManager.IsDragging || Doc.ActiveTool !== InkTool.None) ? null : (
<div
className="formattedTextBox-sidebar-handle"
onPointerDown={this.sidebarDown}
@@ -1969,12 +1935,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
@computed get sidebarCollection() {
const renderComponent = (tag: string) => {
- const ComponentTag = tag === CollectionViewType.Freeform ? CollectionFreeFormView : tag === CollectionViewType.Tree ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView;
+ const ComponentTag: any = tag === CollectionViewType.Freeform ? CollectionFreeFormView : tag === CollectionViewType.Tree ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView;
return ComponentTag === CollectionStackingView ? (
<SidebarAnnos
ref={this._sidebarRef}
- {...this.props}
- rootDoc={this.rootDoc}
+ {...this._props}
+ Document={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
usePanelWidth={true}
@@ -1990,14 +1956,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
setHeight={this.setSidebarHeight}
/>
) : (
- <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}>
+ <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this._props.DocumentView?.()!, false), true)}>
<ComponentTag
- {...this.props}
+ {...this._props}
ref={this._sidebarTagRef as any}
setContentView={emptyFunction}
NativeWidth={returnZero}
NativeHeight={returnZero}
- PanelHeight={this.props.PanelHeight}
+ PanelHeight={this._props.PanelHeight}
PanelWidth={this.sidebarWidth}
xPadding={0}
yPadding={0}
@@ -2011,7 +1977,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
moveDocument={this.sidebarMoveDocument}
addDocument={this.sidebarAddDocument}
ScreenToLocalTransform={this.sidebarScreenToLocal}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
setHeight={this.setSidebarHeight}
fitContentsToBox={this.fitContentsToBox}
noSidebar={true}
@@ -2029,12 +1995,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
cycleAlternateText = () => {
if (this.layoutDoc._layout_enableAltContentUI) {
- const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`];
- this.rootDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
+ const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
+ this.layoutDoc[`_${this._props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
}
};
@computed get overlayAlternateIcon() {
- const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`];
+ const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
return (
<Tooltip
title={
@@ -2056,7 +2022,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
className="formattedTextBox-alternateButton"
onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => this.cycleAlternateText())}
style={{
- display: this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'flex' : 'none',
+ display: this._props.isContentActive() && !SnappingManager.IsDragging ? 'flex' : 'none',
background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray',
color: usePath === undefined ? 'black' : 'white',
}}>
@@ -2065,14 +2031,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
</Tooltip>
);
}
- @computed get fieldKey() {
- const usePath = StrCast(this.rootDoc[`${this.props.fieldKey}_usePath`]);
- return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering || this.props.isContentActive()) ? `_${usePath.replace(':hover', '')}` : '');
+ get fieldKey() {
+ const usePath = StrCast(this.layoutDoc[`${this._props.fieldKey}_usePath`]);
+ return this._props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering || this._props.isContentActive()) ? `_${usePath.replace(':hover', '')}` : '');
}
@observable _isHovering = false;
onPassiveWheel = (e: WheelEvent) => {
if (e.clientX > this.ProseRef!.getBoundingClientRect().right) {
- if (this.rootDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform) {
+ if (this.dataDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform) {
// if the scrolled freeform is a child of the sidebar component, we need to let the event go through
// so react can let the freeform view handle it. We prevent default to stop any containing views from scrolling
e.preventDefault();
@@ -2081,14 +2047,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
// if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
- if (this.props.isContentActive()) {
- const scale = this.props.NativeDimScaling?.() || 1;
- const styleFromLayoutString = Doc.styleFromLayoutString(this.Document, this.props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
+ if (this._props.isContentActive()) {
+ const scale = this._props.NativeDimScaling?.() || 1;
+ const styleFromLayoutString = Doc.styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
const height = Number(styleFromLayoutString.height?.replace('px', ''));
// prevent default if selected || child is active but this doc isn't scrollable
if (
- (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil((height ? height : this.props.PanelHeight()) / scale) && //
- (this.props.rootSelected() || this.isAnyChildContentActive())
+ (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil((height ? height : this._props.PanelHeight()) / scale) && //
+ (this._props.rootSelected?.() || this.isAnyChildContentActive())
) {
e.preventDefault();
}
@@ -2097,25 +2063,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
_oldWheel: any;
@computed get fontColor() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
}
@computed get fontSize() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize);
}
@computed get fontFamily() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontFamily);
}
@computed get fontWeight() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontWeight);
}
render() {
TraceMobx();
- const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
+ const scale = (this._props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
const rounded = StrCast(this.layoutDoc.layout_borderRounding) === '100%' ? '-rounded' : '';
- setTimeout(() => !this.props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
- const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
- const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
- const styleFromLayoutString = Doc.styleFromLayoutString(this.Document, this.props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
+ setTimeout(() => !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
+ const paddingX = NumCast(this.layoutDoc._xMargin, this._props.xPadding || 0);
+ const paddingY = NumCast(this.layoutDoc._yMargin, this._props.yPadding || 0);
+ const styleFromLayoutString = Doc.styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
return styleFromLayoutString?.height === '0px' ? null : (
<div
className="formattedTextBox"
@@ -2127,7 +2093,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
}}
style={{
- ...(this.props.dontScale
+ ...(this._props.dontScale
? {}
: {
transform: `scale(${scale})`,
@@ -2146,9 +2112,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
className="formattedTextBox-cont"
ref={this._ref}
style={{
- cursor: this.props.isContentActive() ? 'text' : undefined,
- height: this.props.height ? 'max-content' : undefined,
- pointerEvents: Doc.ActiveTool === InkTool.None && !this.props.onBrowseClick?.() ? undefined : 'none',
+ cursor: this._props.isContentActive() ? 'text' : undefined,
+ height: this._props.height ? 'max-content' : undefined,
+ pointerEvents: Doc.ActiveTool === InkTool.None && !this._props.onBrowseClick?.() ? undefined : 'none',
}}
onContextMenu={this.specificContextMenu}
onKeyDown={this.onKeyDown}
@@ -2163,7 +2129,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
className="formattedTextBox-outer"
ref={this._scrollRef}
style={{
- width: this.props.dontSelectOnLoad || this.noSidebar ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
+ width: this._props.dontSelectOnLoad || this.noSidebar ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
}}
onScroll={this.onScroll}
@@ -2180,8 +2146,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}}
/>
</div>
- {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
- {this.noSidebar || this.Document._layout_noSidebar || this.props.dontSelectOnLoad || this.Document._createDocOnCR || this.layoutDoc._chromeHidden ? null : this.sidebarHandle}
+ {this.noSidebar || this._props.dontSelectOnLoad || !this.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
+ {this.noSidebar || this.Document._layout_noSidebar || this._props.dontSelectOnLoad || this.Document._createDocOnCR || this.layoutDoc._chromeHidden ? null : this.sidebarHandle}
{this.audioHandle}
{this.layoutDoc._layout_enableAltContentUI && !this.layoutDoc._chromeHidden ? this.overlayAlternateIcon : null}
</div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index e7ca26d5c..be8736525 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -3,7 +3,7 @@ import { EditorState, NodeSelection } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { Doc } from '../../../../fields/Doc';
import { DocServer } from '../../../DocServer';
-import { LinkDocPreview } from '../LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from '../LinkDocPreview';
import { FormattedTextBox } from './FormattedTextBox';
import './FormattedTextBoxComment.scss';
import { schema } from './schema_rts';
@@ -133,9 +133,9 @@ export class FormattedTextBoxComment {
const naft = findEndOfMark(state.selection.$from, view, findLinkMark) || nbef;
//nbef &&
naft &&
- LinkDocPreview.SetLinkInfo({
+ LinkInfo.SetLinkInfo({
docProps: textBox.props,
- linkSrc: textBox.rootDoc,
+ linkSrc: textBox.Document,
linkDoc: linkDoc ? (DocServer.GetCachedRefField(linkDoc) as Doc) : undefined,
location: (pos => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))),
hrefs,
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index ec11079b4..08bad2d57 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -47,7 +47,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
}
const canEdit = (state: any) => {
- switch (GetEffectiveAcl(props.DataDoc)) {
+ switch (GetEffectiveAcl(props.TemplateDataDocument)) {
case AclAugment:
const prevNode = state.selection.$cursor.nodeBefore;
const prevUser = !prevNode ? Doc.CurrentUserEmail : prevNode.marks[prevNode.marks.length - 1].attrs.userid;
@@ -334,7 +334,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
//Command to create a blank space
bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => {
- if (props.DataDoc && GetEffectiveAcl(props.DataDoc) != AclEdit && GetEffectiveAcl(props.DataDoc) != AclAugment && GetEffectiveAcl(props.DataDoc) != AclAdmin) return true;
+ if (props.TemplateDataDocument && GetEffectiveAcl(props.TemplateDataDocument) != AclEdit && GetEffectiveAcl(props.TemplateDataDocument) != AclAugment && GetEffectiveAcl(props.TemplateDataDocument) != AclAdmin) return true;
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
dispatch(splitMetadata(marks, state.tr));
return false;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss
index 8afa0f6b5..d6ed5ebee 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.scss
+++ b/src/client/views/nodes/formattedText/RichTextMenu.scss
@@ -1,4 +1,4 @@
-@import "../../global/globalCssVariables";
+@import '../../global/globalCssVariables.module.scss';
.button-dropdown-wrapper {
position: relative;
@@ -55,7 +55,6 @@
input {
color: black;
}
-
}
.richTextMenu {
@@ -68,12 +67,12 @@
font-size: 12px;
height: 100%;
margin-right: 3px;
-
+
&:focus,
&:hover {
background-color: black;
}
-
+
&::-ms-expand {
color: white;
}
@@ -126,7 +125,7 @@
display: flex;
justify-content: space-between;
- >div {
+ > div {
display: flex;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 7f9bfe753..4881070fd 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,13 +1,13 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { lift, wrapIn } from 'prosemirror-commands';
import { Mark, MarkType, Node as ProsNode, ResolvedPos } from 'prosemirror-model';
import { wrapInList } from 'prosemirror-schema-list';
import { EditorState, NodeSelection, TextSelection } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
+import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
import { numberRange } from '../../../../Utils';
@@ -16,6 +16,7 @@ import { LinkManager } from '../../../util/LinkManager';
import { SelectionManager } from '../../../util/SelectionManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
import { EquationBox } from '../EquationBox';
import { FieldViewProps } from '../FieldView';
import { FormattedTextBox } from './FormattedTextBox';
@@ -63,14 +64,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private showLinkDropdown: boolean = false;
_reaction: IReactionDisposer | undefined;
- constructor(props: Readonly<{}>) {
+ constructor(props: AntimodeMenuProps) {
super(props);
- runInAction(() => {
- RichTextMenu.Instance = this;
- this.updateMenu(undefined, undefined, props, this.layoutDoc);
- this._canFade = false;
- this.Pinned = true;
- });
+ makeObservable(this);
+ RichTextMenu.Instance = this;
+ this.updateMenu(undefined, undefined, props, this.layoutDoc);
+ this._canFade = false;
+ this.Pinned = true;
}
@computed get noAutoLink() {
@@ -109,7 +109,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
_disposer: IReactionDisposer | undefined;
componentDidMount() {
this._disposer = reaction(
- () => SelectionManager.Views().slice(),
+ () => SelectionManager.Views.slice(),
views => this.updateMenu(undefined, undefined, undefined, undefined)
);
}
@@ -186,7 +186,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// finds font sizes and families in selection
getActiveAlignment() {
- if (this.view && this.TextView?.props.rootSelected()) {
+ if (this.view && this.TextView?.props.rootSelected?.()) {
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) {
@@ -199,7 +199,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// finds font sizes and families in selection
getActiveListStyle() {
- if (this.view && this.TextView?.props.rootSelected()) {
+ if (this.view && this.TextView?.props.rootSelected?.()) {
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) {
@@ -219,7 +219,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.rootSelected()) {
+ if (this.view && this.TextView?.props.rootSelected?.()) {
const state = this.view.state;
const pos = this.view.state.selection.$from;
const marks: Mark[] = [...(state.storedMarks ?? [])];
@@ -238,8 +238,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
m.type === state.schema.marks.pFontSize && activeSizes.add(m.attrs.fontSize);
m.type === state.schema.marks.marker && activeHighlights.add(String(m.attrs.highlight));
});
- } else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) {
- SelectionManager.Views().forEach(dv => StrCast(dv.rootDoc._text_fontSize) && activeSizes.add(StrCast(dv.rootDoc._text_fontSize)));
+ } else if (SelectionManager.Views.some(dv => dv.ComponentView instanceof EquationBox)) {
+ SelectionManager.Views.forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize)));
}
return { activeFamilies: Array.from(activeFamilies), activeSizes: Array.from(activeSizes), activeColors: Array.from(activeColors), activeHighlights: Array.from(activeHighlights) };
}
@@ -254,7 +254,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.rootSelected()) return activeMarks;
+ if (!this.view || !this.TextView?.props.rootSelected?.()) 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);
@@ -358,8 +358,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
this.view.focus();
}
- } else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) {
- SelectionManager.Views().forEach(dv => (dv.rootDoc._text_fontSize = fontSize));
+ } else if (SelectionManager.Views.some(dv => dv.ComponentView instanceof EquationBox)) {
+ SelectionManager.Views.forEach(dv => (dv.Document._text_fontSize = fontSize));
} else Doc.UserDoc().fontSize = fontSize;
this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
};
@@ -447,7 +447,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.layoutDoc && (this.layoutDoc.layout_centered = !this.layoutDoc.layout_centered);
};
align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => {
- if (this.TextView?.props.rootSelected()) {
+ if (this.TextView?.props.rootSelected?.()) {
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)) {
@@ -642,7 +642,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
if (linkDoc instanceof Doc) {
const link_anchor_1 = await Cast(linkDoc.link_anchor_1, Doc);
const link_anchor_2 = await Cast(linkDoc.link_anchor_2, Doc);
- const currentDoc = SelectionManager.Docs().lastElement();
+ const currentDoc = SelectionManager.Docs.lastElement();
if (currentDoc && link_anchor_1 && link_anchor_2) {
if (Doc.AreProtosEqual(currentDoc, link_anchor_1)) {
return StrCast(link_anchor_2.title);
@@ -669,7 +669,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
};
@undoBatch
- @action
deleteLink = () => {
if (this.view) {
const linkAnchor = this.view.state.selection.$from.nodeAfter?.marks.find(m => m.type === this.view!.state.schema.marks.linkAnchor);
@@ -775,11 +774,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// <div className="collectionMenu-divider" key="divider 3" />
// {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
// this.activeFontSize = val;
- // SelectionManager.Views().map(dv => dv.props.Document._text_fontSize = val);
+ // SelectionManager.Views.map(dv => dv.props.Document._text_fontSize = val);
// })),
// this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => {
// this.activeFontFamily = val;
- // SelectionManager.Views().map(dv => dv.props.Document._text_fontFamily = val);
+ // SelectionManager.Views.map(dv => dv.props.Document._text_fontFamily = val);
// })),
// <div className="collectionMenu-divider" key="divider 4" />,
// this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
@@ -812,10 +811,15 @@ interface ButtonDropdownProps {
}
@observer
-export class ButtonDropdown extends React.Component<ButtonDropdownProps> {
+export class ButtonDropdown extends ObservableReactComponent<ButtonDropdownProps> {
@observable private showDropdown: boolean = false;
private ref: HTMLDivElement | null = null;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
componentDidMount() {
document.addEventListener('pointerdown', this.onBlur);
}
@@ -850,22 +854,22 @@ export class ButtonDropdown extends React.Component<ButtonDropdownProps> {
render() {
return (
<div className="button-dropdown-wrapper" ref={node => (this.ref = node)}>
- {!this.props.pdf ? (
- <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.props.openDropdownOnButton ? this.onDropdownClick : undefined}>
- {this.props.button}
- <div style={{ marginTop: '-8.5', position: 'relative' }} onPointerDown={!this.props.openDropdownOnButton ? this.onDropdownClick : undefined}>
+ {!this._props.pdf ? (
+ <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this._props.openDropdownOnButton ? this.onDropdownClick : undefined}>
+ {this._props.button}
+ <div style={{ marginTop: '-8.5', position: 'relative' }} onPointerDown={!this._props.openDropdownOnButton ? this.onDropdownClick : undefined}>
<FontAwesomeIcon icon="caret-down" size="sm" />
</div>
</div>
) : (
<>
- {this.props.button}
+ {this._props.button}
<button className="dropdown-button antimodeMenu-button" key="antimodebutton" onPointerDown={this.onDropdownClick}>
<FontAwesomeIcon icon="caret-down" size="sm" />
</button>
</>
)}
- {this.showDropdown ? this.props.dropdownContent : null}
+ {this.showDropdown ? this._props.dropdownContent : null}
</div>
);
}
diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index 3355e4529..7ec296ed2 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -1,7 +1,7 @@
import { TextSelection } from 'prosemirror-state';
import { Fragment, Node, Slice } from 'prosemirror-model';
import * as ReactDOM from 'react-dom/client';
-import React = require('react');
+import * as React from 'react';
// an elidable textblock that collapses when its '<-' is clicked and expands when its '...' anchor is clicked.
// this node actively edits prosemirror (as opposed to just changing how things are rendered) and thus doesn't
diff --git a/src/client/views/nodes/formattedText/TooltipTextMenu.scss b/src/client/views/nodes/formattedText/TooltipTextMenu.scss
index 8c4d77da9..87320943d 100644
--- a/src/client/views/nodes/formattedText/TooltipTextMenu.scss
+++ b/src/client/views/nodes/formattedText/TooltipTextMenu.scss
@@ -1,9 +1,9 @@
-@import "../views/global/globalCssVariables";
+@import '../views/global/globalCssVariables.module.scss';
.ProseMirror-menu-dropdown-wrap {
display: inline-block;
position: relative;
}
-
+
.ProseMirror-menu-dropdown {
vertical-align: 1px;
cursor: pointer;
@@ -17,11 +17,11 @@
margin-right: 4px;
&:after {
- content: "";
+ content: '';
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid currentColor;
- opacity: .6;
+ opacity: 0.6;
position: absolute;
right: 4px;
top: calc(50% - 2px);
@@ -33,7 +33,7 @@
margin-right: -4px;
}
-.ProseMirror-menu-dropdown-menu,
+.ProseMirror-menu-dropdown-menu,
.ProseMirror-menu-submenu {
font-size: 12px;
background: white;
@@ -55,19 +55,18 @@
}
}
-
.ProseMirror-menu-submenu-label:after {
- content: "";
+ content: '';
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-left: 4px solid currentColor;
- opacity: .6;
+ opacity: 0.6;
position: absolute;
right: 4px;
top: calc(50% - 4px);
}
-
- .ProseMirror-icon {
+
+.ProseMirror-icon {
display: inline-block;
// line-height: .8;
// vertical-align: -2px; /* Compensate for padding */
@@ -79,14 +78,14 @@
}
svg {
- fill:white;
+ fill: white;
height: 1em;
}
span {
vertical-align: text-top;
- }
- }
+ }
+}
.wrapper {
position: absolute;
@@ -99,10 +98,10 @@
background: #323232;
border-radius: 6px;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
-
}
-.tooltipMenu, .basic-tools {
+.tooltipMenu,
+.basic-tools {
z-index: 3000;
pointer-events: all;
padding: 3px;
@@ -111,17 +110,17 @@
align-items: center;
.ProseMirror-example-setup-style hr {
- padding: 2px 10px;
- border: none;
- margin: 1em 0;
+ padding: 2px 10px;
+ border: none;
+ margin: 1em 0;
}
-
+
.ProseMirror-example-setup-style hr:after {
- content: "";
- display: block;
- height: 1px;
- background-color: silver;
- line-height: 2px;
+ content: '';
+ display: block;
+ height: 1px;
+ background-color: silver;
+ line-height: 2px;
}
}
@@ -142,7 +141,7 @@
}
}
- &> * {
+ & > * {
margin-top: 50%;
margin-left: 50%;
transform: translate(-50%, -50%);
@@ -168,7 +167,7 @@
background-color: black;
}
- &> * {
+ & > * {
margin-top: 50%;
margin-left: 50%;
transform: translate(-50%, -50%);
@@ -208,18 +207,17 @@
margin-top: 13px;
}
- .font-size-indicator {
- font-size: 12px;
- padding-right: 0px;
- }
- .summarize{
- color: white;
- height: 20px;
- text-align: center;
- }
-
-
-.brush{
+.font-size-indicator {
+ font-size: 12px;
+ padding-right: 0px;
+}
+.summarize {
+ color: white;
+ height: 20px;
+ text-align: center;
+}
+
+.brush {
display: inline-block;
width: 1em;
height: 1em;
@@ -229,7 +227,7 @@
margin-right: 15px;
}
-.brush-active{
+.brush-active {
display: inline-block;
width: 1em;
height: 1em;
@@ -269,7 +267,6 @@
}
.buttonSettings-dropdown {
-
&.ProseMirror-menu-dropdown {
width: 10px;
height: 25px;
@@ -301,7 +298,7 @@
padding: 3px;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
- .ProseMirror-menu-dropdown-item{
+ .ProseMirror-menu-dropdown-item {
cursor: default;
&:last-child {
@@ -312,7 +309,8 @@
background-color: #323232;
}
- .button-setting, .button-setting-disabled {
+ .button-setting,
+ .button-setting-disabled {
padding: 2px;
border-radius: 2px;
}
@@ -328,25 +326,23 @@
}
input {
- color: black;
- border: none;
- border-radius: 1px;
- padding: 3px;
+ color: black;
+ border: none;
+ border-radius: 1px;
+ padding: 3px;
}
button {
- padding: 6px;
- background-color: #323232;
- border: 1px solid black;
- border-radius: 1px;
+ padding: 6px;
+ background-color: #323232;
+ border: 1px solid black;
+ border-radius: 1px;
&:hover {
- background-color: black;
+ background-color: black;
}
}
}
-
-
}
}
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 4faef26e2..a342285b0 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { DOMOutputSpec, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from 'prosemirror-model';
import { Doc } from '../../../../fields/Doc';
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 4cd2cee52..d023020e1 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { DOMOutputSpec, Node, NodeSpec } from 'prosemirror-model';
import { listItem, orderedList } from 'prosemirror-schema-list';
import { ParagraphNodeSpec, toParagraphDOM, getParagraphNodeAttrs } from './ParagraphNodeSpec';
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
index 2b0927148..87e1b69c3 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
@@ -21,7 +21,7 @@ import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollection
import { CursorData, ImageDimensions, Point } from './generativeFillUtils/generativeFillInterfaces';
import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler';
import { PointerHandler } from './generativeFillUtils/PointerHandler';
-import React = require('react');
+import * as React from 'react';
enum BrushStyle {
ADD,
@@ -438,7 +438,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
// disable once edited has been clicked (doesn't make sense to change after first edit)
disabled={edited}
checked={isNewCollection}
- onChange={e => {
+ onChange={() => {
setIsNewCollection(prev => !prev);
}}
/>
@@ -513,7 +513,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
defaultValue={150}
size="small"
valueLabelDisplay="auto"
- onChange={(e, val) => {
+ onChange={(e: any, val: any) => {
setCursorData(prev => ({ ...prev, width: val as number }));
}}
/>
@@ -571,7 +571,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
<div>
<TextField
value={input}
- onChange={e => setInput(e.target.value)}
+ onChange={(e: any) => setInput(e.target.value)}
disabled={isBrushing}
type="text"
label="Prompt"
diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
index 10eca358e..185ba2280 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
@@ -1,5 +1,5 @@
import './GenerativeFillButtons.scss';
-import React = require('react');
+import * as React from 'react';
import ReactLoading from 'react-loading';
import { activeColor } from './generativeFillUtils/generativeFillConstants';
import { Button, IconButton, Type } from 'browndash-components';
diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx
index 170626cd2..9dc0c5180 100644
--- a/src/client/views/nodes/importBox/ImportElementBox.tsx
+++ b/src/client/views/nodes/importBox/ImportElementBox.tsx
@@ -7,7 +7,7 @@ import { ViewBoxBaseComponent } from '../../DocComponent';
import { DefaultStyleProvider } from '../../StyleProvider';
import { DocumentView, DocumentViewInternal } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
-import React = require('react');
+import * as React from 'react';
@observer
export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -24,7 +24,6 @@ export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
LayoutTemplateString={undefined}
Document={this.Document}
isContentActive={returnFalse}
- DataDoc={undefined}
addDocument={returnFalse}
ScreenToLocalTransform={this.screenToLocalXf}
hideResizeHandles={true}
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index 0b51813a5..3b34a1f90 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables';
+@import '../../global/globalCssVariables.module.scss';
.presBox-cont {
cursor: auto;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index ec0acf36e..17993d88e 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,7 +1,7 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast, FieldResult, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
import { Animation } from '../../../../fields/DocSymbols';
@@ -36,9 +36,6 @@ import { FieldView, FieldViewProps } from '../FieldView';
import { ScriptingBox } from '../ScriptingBox';
import './PresBox.scss';
import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
-import _ = require('lodash');
-const { Howl } = require('howler');
-
export interface pinDataTypes {
scrollable?: boolean;
dataviz?: number[];
@@ -75,6 +72,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
constructor(props: any) {
super(props);
+ makeObservable(this);
if (!PresBox.navigateToDocScript) {
PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(this.presentation_targetDoc, self)')!;
}
@@ -142,7 +140,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return false;
}
@computed get selectedDocumentView() {
- if (SelectionManager.Views().length) return SelectionManager.Views()[0];
+ if (SelectionManager.Views.length) return SelectionManager.Views[0];
if (this.selectedArray.size) return DocumentManager.Instance.getDocumentView(this.Document);
}
@computed get isPres() {
@@ -156,7 +154,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
addToSelectedArray = action((doc: Doc) => this.selectedArray.add(doc));
removeFromSelectedArray = action((doc: Doc) => this.selectedArray.delete(doc));
- @action
componentWillUnmount() {
this._unmounting = true;
if (this._presTimer) clearTimeout(this._presTimer);
@@ -187,11 +184,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
},
{ fireImmediately: true }
);
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
this._unmounting = false;
this.turnOffEdit(true);
this._disposers.selection = reaction(
- () => SelectionManager.Views().slice(),
+ () => SelectionManager.Views.slice(),
views => (!PresBox.Instance || views.some(view => view.Document === this.Document)) && this.updateCurrentPresentation(),
{ fireImmediately: true }
);
@@ -603,7 +600,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const dv = DocumentManager.Instance.getDocumentView(bestTarget);
if (dv) {
changed = true;
- const computedScale = NumCast(activeItem.config_zoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height);
+ 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, 2500);
}
@@ -748,7 +745,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const dragViewCache = Array.from(this._dragArray);
const eleViewCache = Array.from(this._eleArray);
const resetSelection = action(() => {
- if (!this.props.isSelected()) {
+ if (!this._props.isSelected()) {
const presDocView = DocumentManager.Instance.getDocumentView(this.Document);
if (presDocView) SelectionManager.SelectView(presDocView, false);
this.clearSelectedArray();
@@ -982,9 +979,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
enterMinimize = () => {
this.updateCurrentPresentation(this.Document);
clearTimeout(this._presTimer);
- const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
- this.props.removeDocument?.(this.layoutDoc);
- return PresBox.OpenPresMinimized(this.Document, [pt[0] + (this.props.PanelWidth() - 250), pt[1] + 10]);
+ const pt = this._props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ this._props.removeDocument?.(this.layoutDoc);
+ return PresBox.OpenPresMinimized(this.Document, [pt[0] + (this._props.PanelWidth() - 250), pt[1] + 10]);
};
exitMinimize = () => {
if (Doc.IsInMyOverlay(this.layoutDoc)) {
@@ -1052,7 +1049,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return StrCast(activeItem.presentation_movement);
});
- whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isChildActive = isActive)));
+ whenChildContentsActiveChanged = action((isActive: boolean) => this._props.whenChildContentsActiveChanged((this._isChildActive = isActive)));
// For dragging documents into the presentation trail
addDocumentFilter = (docs: Doc[]) => {
docs.forEach((doc, i) => {
@@ -1063,7 +1060,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;
- this.props.pinToPres(audio, { audioRange: true });
+ this._props.pinToPres(audio, { audioRange: true });
setTimeout(() => this.removeDocument(doc), 0);
return false;
}
@@ -1079,8 +1076,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
childLayoutTemplate = () => Docs.Create.PresElementBoxDocument();
removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.Document, this.fieldKey, doc);
- getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
- panelHeight = () => this.props.PanelHeight() - 40;
+ getTransform = () => this._props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
+ panelHeight = () => this._props.PanelHeight() - 40;
/**
* For sorting the array so that the order is maintained when it is dropped.
*/
@@ -1313,7 +1310,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
} else if (doc.config_pinView && presCollection === tagDoc && dv) {
// Case B: Document is presPinView and is presCollection
const scale = 1 / NumCast(doc.config_viewScale);
- const viewBounds = NumListCast(doc.config_viewBounds, [0, 0, dv.props.PanelWidth(), dv.props.PanelHeight()]);
+ const viewBounds = NumListCast(doc.config_viewBounds, [0, 0, dv._props.PanelWidth(), dv._props.PanelHeight()]);
const height = (viewBounds[3] - viewBounds[1]) * scale;
const width = (viewBounds[2] - viewBounds[0]) * scale;
const indWidth = width / 10;
@@ -1433,46 +1430,39 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
updateMovement = action((movement: PresMovement, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presentation_movement = movement)));
@undoBatch
- @action
updateHideBefore = (activeItem: Doc) => {
activeItem.presentation_hideBefore = !activeItem.presentation_hideBefore;
this.selectedArray.forEach(doc => (doc.presentation_hideBefore = activeItem.presentation_hideBefore));
};
@undoBatch
- @action
updateHide = (activeItem: Doc) => {
activeItem.presentation_hide = !activeItem.presentation_hide;
this.selectedArray.forEach(doc => (doc.presentation_hide = activeItem.presentation_hide));
};
@undoBatch
- @action
updateHideAfter = (activeItem: Doc) => {
activeItem.presentation_hideAfter = !activeItem.presentation_hideAfter;
this.selectedArray.forEach(doc => (doc.presentation_hideAfter = activeItem.presentation_hideAfter));
};
@undoBatch
- @action
updateOpenDoc = (activeItem: Doc) => {
activeItem.presentation_openInLightbox = !activeItem.presentation_openInLightbox;
this.selectedArray.forEach(doc => (doc.presentation_openInLightbox = activeItem.presentation_openInLightbox));
};
@undoBatch
- @action
updateEaseFunc = (activeItem: Doc) => {
activeItem.presEaseFunc = activeItem.presEaseFunc === 'linear' ? 'ease' : 'linear';
this.selectedArray.forEach(doc => (doc.presEaseFunc = activeItem.presEaseFunc));
};
@undoBatch
- @action
updateEffectDirection = (effect: PresEffectDirection, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presentation_effectDirection = effect));
@undoBatch
- @action
updateEffect = (effect: PresEffect, bullet: boolean, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (bullet ? (doc.presBulletEffect = effect) : (doc.presentation_effect = effect)));
static _sliderBatch: any;
@@ -1505,7 +1495,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
@undoBatch
- @action
applyTo = (array: Doc[]) => {
this.updateMovement(this.activeItem.presentation_movement as PresMovement, true);
this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true);
@@ -2219,10 +2208,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const config_data = Cast(this.Document.data, listSpec(Doc));
if (data && config_data) {
data.push(doc);
- this.props.pinToPres(doc, {});
+ this._props.pinToPres(doc, {});
this.gotoDocument(this.childDocs.length, this.activeItem);
} else {
- this.props.addDocTab(doc, OpenWhere.addRight);
+ this._props.addDocTab(doc, OpenWhere.addRight);
}
}
};
@@ -2283,15 +2272,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed
get toolbarWidth(): number {
- return this.props.PanelWidth();
+ return this._props.PanelWidth();
}
@action
- toggleProperties = () => (SettingsManager.propertiesWidth = SettingsManager.propertiesWidth > 0 ? 0 : 250);
+ toggleProperties = () => (SettingsManager.Instance.propertiesWidth = SettingsManager.Instance.propertiesWidth > 0 ? 0 : 250);
@computed get toolbar() {
- const propIcon = SettingsManager.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
- const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
+ const propIcon = SettingsManager.Instance.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
+ const propTitle = SettingsManager.Instance.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
const mode = StrCast(this.Document._type_collection) as CollectionViewType;
const isMini: boolean = this.toolbarWidth <= 100;
const activeColor = SettingsManager.userVariantColor;
@@ -2320,7 +2309,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</Tooltip>
<Tooltip title={<div className="dash-tooltip">{propTitle}</div>}>
<div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
- <FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: SettingsManager.propertiesWidth > 0 ? activeColor : inactiveColor }} />
+ <FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: SettingsManager.Instance.propertiesWidth > 0 ? activeColor : inactiveColor }} />
</div>
</Tooltip>
</>
@@ -2338,9 +2327,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const mode = StrCast(this.Document._type_collection) as CollectionViewType;
const isMini: boolean = this.toolbarWidth <= 100;
return (
- <div
- className={`presBox-buttons${Doc.IsInMyOverlay(this.Document) ? ' inOverlay' : ''}`}
- style={{ background: Doc.ActivePresentation === this.Document ? Colors.LIGHT_BLUE : undefined, display: !this.Document._chromeHidden ? 'none' : undefined }}>
+ <div className={`presBox-buttons${Doc.IsInMyOverlay(this.Document) ? ' inOverlay' : ''}`} style={{ background: Doc.ActivePresentation === this.Document ? Colors.LIGHT_BLUE : undefined }}>
{isMini ? null : (
<select className="presBox-viewPicker" style={{ display: this.layoutDoc.presentation_status === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
<option onPointerDown={StopEvent} value={CollectionViewType.Stacking}>
@@ -2368,7 +2355,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
})}>
<FontAwesomeIcon icon={'play-circle'} />
- <div style={{ display: this.props.PanelWidth() > 200 ? 'inline-flex' : 'none' }}>&nbsp; Present</div>
+ <div style={{ display: this._props.PanelWidth() > 200 ? 'inline-flex' : 'none' }}>&nbsp; Present</div>
</div>
{mode === CollectionViewType.Carousel3D || isMini ? null : (
<div
@@ -2478,12 +2465,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<b>1</b>
</div>
</Tooltip>
- <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: inOverlay || this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
+ <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: inOverlay || this._props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
{inOverlay ? '' : 'Slide'} {this.itemIndex + 1}
{this.activeItem?.presentation_indexed !== undefined ? `(${this.activeItem.presentation_indexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length}
</div>
<div className="presPanel-divider"></div>
- {this.props.PanelWidth() > 250 ? (
+ {this._props.PanelWidth() > 250 ? (
<div
className="presPanel-button-text"
onClick={undoBatch(
@@ -2529,7 +2516,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
@undoBatch
- @action
exitClicked = () => {
this.layoutDoc.presentation_status = this._exitTrail?.() ?? this.exitMinimize();
clearTimeout(this._presTimer);
@@ -2572,7 +2558,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.itemIndex === this.childDocs.length - 1 &&
(this.activeItem.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
const presStart = !this.layoutDoc.presLoop && this.itemIndex === 0;
- return this.props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player
+ return this._props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player
<div className="miniPres" onClick={e => e.stopPropagation()} onPointerEnter={action(e => (this._forceKeyEvents = true))}>
<div
className="presPanelOverlay"
@@ -2622,8 +2608,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="Slide">
{mode !== CollectionViewType.Invalid ? (
<CollectionView
- {...this.props}
- PanelWidth={this.props.PanelWidth}
+ {...this._props}
+ PanelWidth={this._props.PanelWidth}
PanelHeight={this.panelHeight}
childIgnoreNativeSize={true}
moveDocument={returnFalse}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 6a8edee0d..ec5d090dd 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -1,7 +1,8 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
@@ -24,7 +25,6 @@ import { StyleProp } from '../../StyleProvider';
import { PresBox } from './PresBox';
import './PresElementBox.scss';
import { PresMovement } from './PresEnums';
-import React = require('react');
/**
* This class models the view a document added to presentation will have in the presentation.
* It involves some functionality for its buttons and options.
@@ -41,11 +41,16 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
readonly expandViewHeight = 100;
readonly collapsedHeight = 35;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@observable _dragging = false;
// the presentation view that renders this slide
@computed get presBoxView() {
- return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as PresBox;
+ return this._props.DocumentView?.()?._props.docViewPath().lastElement()?.ComponentView as PresBox;
}
// the presentation view document that renders this slide
@@ -56,7 +61,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
// Since this node is being rendered with a template, this method retrieves
// the actual slide being rendered from the auto-generated rendering template
@computed get slideDoc() {
- return Cast(this.Document.rootDocument, Doc, null) || this.props.Document;
+ return this._props.TemplateDataDocument ?? this.Document;
}
// this is the document in the workspaces that is targeted by the slide
@@ -91,9 +96,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
presExpandDocumentClick = () => (this.slideDoc.presentation_expandInlineButton = !this.slideDoc.presentation_expandInlineButton);
embedHeight = () => this.collapsedHeight + this.expandViewHeight;
- embedWidth = () => this.props.PanelWidth() / 2;
+ embedWidth = () => this._props.PanelWidth() / 2;
styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
- return property === StyleProp.Opacity ? 1 : this.props.styleProvider?.(doc, props, property);
+ return property === StyleProp.Opacity ? 1 : this._props.styleProvider?.(doc, props, property);
};
/**
* The function that is responsible for rendering a preview or not for this
@@ -104,23 +109,21 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="presItem-embedded" style={{ height: this.embedHeight(), width: '50%' }}>
<DocumentView
Document={PresBox.targetRenderedDoc(this.slideDoc)}
- DataDoc={undefined} //this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
PanelWidth={this.embedWidth}
PanelHeight={this.embedHeight}
- isContentActive={this.props.isContentActive}
+ isContentActive={this._props.isContentActive}
styleProvider={this.styleProvider}
hideLinkButton={true}
ScreenToLocalTransform={Transform.Identity}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
docViewPath={returnEmptyDoclist}
- childFilters={this.props.childFilters}
- childFiltersByRanges={this.props.childFiltersByRanges}
- searchFilterDocs={this.props.searchFilterDocs}
- rootSelected={returnFalse}
+ childFilters={this._props.childFilters}
+ childFiltersByRanges={this._props.childFiltersByRanges}
+ searchFilterDocs={this._props.searchFilterDocs}
addDocument={returnFalse}
removeDocument={returnFalse}
fitContentsToBox={returnTrue}
- moveDocument={this.props.moveDocument!}
+ moveDocument={this._props.moveDocument!}
focus={emptyFunction}
whenChildContentsActiveChanged={returnFalse}
addDocTab={returnFalse}
@@ -193,8 +196,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
const dragArray = this.presBoxView?._dragArray ?? [];
const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []);
if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.slideDoc);
- dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.props.DocumentView?.()?.props.treeViewDoc;
- dragData.moveDocument = this.props.moveDocument;
+ dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this._props.DocumentView?.()?._props.treeViewDoc;
+ dragData.moveDocument = this._props.moveDocument;
const dragItem: HTMLElement[] = [];
const classesToRestore = new Map<HTMLElement, string>();
if (dragArray.length === 1) {
@@ -267,8 +270,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
toggleProperties = () => {
- if (SettingsManager.propertiesWidth < 5) {
- SettingsManager.propertiesWidth = 250;
+ if (SettingsManager.Instance.propertiesWidth < 5) {
+ SettingsManager.Instance.propertiesWidth = 250;
}
};
@@ -278,7 +281,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) {
this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1;
}
- this.props.removeDocument?.(this.slideDoc);
+ this._props.removeDocument?.(this.slideDoc);
this.presBoxView?.removeFromSelectedArray(this.slideDoc);
this.removeAllRecordingInOverlay();
}),
@@ -301,7 +304,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
* @param activeItem
*/
@undoBatch
- @action
updateCapturedContainerLayout = (presTargetDoc: Doc, activeItem: Doc) => {
const targetDoc = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
activeItem.config_x = NumCast(targetDoc.x);
@@ -391,7 +393,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
@undoBatch
- @action
lfg = (e: React.MouseEvent) => {
e.stopPropagation();
// TODO: fix this bug
@@ -406,7 +407,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
get toolbarWidth(): number {
const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox);
const width = NumCast(this.presBox?._width);
- return presBoxDocView ? presBoxDocView.props.PanelWidth() : width ? width : 300;
+ return presBoxDocView ? presBoxDocView._props.PanelWidth() : width ? width : 300;
}
@computed get presButtons() {
@@ -455,8 +456,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
{!activeItem.presentation_groupWithUp
? 'Not grouped with previous slide (click to group)'
: activeItem.presentation_groupWithUp === 1
- ? 'Run simultaneously with previous slide (click again to run after)'
- : 'Run after previous slide (click to ungroup from previous)'}
+ ? 'Run simultaneously with previous slide (click again to run after)'
+ : 'Run after previous slide (click to ungroup from previous)'}
</div>
}>
<div
@@ -532,10 +533,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
style={{
backgroundColor: presColorBool ? (isSelected ? 'rgba(250,250,250,0.3)' : 'transparent') : isSelected ? Colors.LIGHT_BLUE : 'transparent',
opacity: this._dragging ? 0.3 : 1,
- paddingLeft: NumCast(this.layoutDoc._xPadding, this.props.xPadding),
- paddingRight: NumCast(this.layoutDoc._xPadding, this.props.xPadding),
- paddingTop: NumCast(this.layoutDoc._yPadding, this.props.yPadding),
- paddingBottom: NumCast(this.layoutDoc._yPadding, this.props.yPadding),
+ paddingLeft: NumCast(this.layoutDoc._xPadding, this._props.xPadding),
+ paddingRight: NumCast(this.layoutDoc._xPadding, this._props.xPadding),
+ paddingTop: NumCast(this.layoutDoc._yPadding, this._props.yPadding),
+ paddingBottom: NumCast(this.layoutDoc._yPadding, this._props.yPadding),
}}
onDoubleClick={action(e => {
this.toggleProperties();
@@ -554,7 +555,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
className={`presItem-slide ${isCurrent ? 'active' : ''}${this.slideDoc.runProcess ? ' testingv2' : ''}`}
style={{
display: 'infline-block',
- backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+ backgroundColor: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor),
//layout_boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined,
border: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? presBoxColor + ' solid 2.5px' : undefined) : undefined,
}}>